Python assembly: aftertreatment and the quasi-steady driver¶
Part of the pure-Python assembly ladder. Aftertreatment is where the driver choice starts to matter as much as the model, so this page is half about bricks and half about which timeloop you hand them to.
A monolith is a pipe with drag and heat¶
A catalyst or filter substrate is a porous pipe. The two closures that make it
one are porous (the pressure drop through the channels) and heat (the
substrate exchanging with the gas):
from aeolus1d.io.schema import HeatSpec, PipeSpec, PorousSpec, UniformInit
brick = PipeSpec(
id="duct", length=0.2, n_cells=24, area=1.0e-3,
init=UniformInit(p=1.0e5, T=800.0, u=20.0),
porous=PorousSpec(kind="darcy_forchheimer", alpha=5.0e3, beta=2.0e2),
heat=HeatSpec(kind="constant_twall", h=300.0, T_wall=400.0, D_h=0.02),
)
alpha is the Darcy (viscous, linear in u) coefficient and beta the
Forchheimer (inertial, quadratic) one. Chemistry, soot loading, wash-coat
storage and regeneration ride on top through CatalystSpec / FilterSpec —
the closures reference is the field-by-field list,
and the aftertreatment thermal reference
covers the substrate/cake thermal blocks.
Why you would change driver¶
The explicit timeloop resolves acoustics, so its step is bounded by the acoustic CFL — microseconds. An aftertreatment system's interesting timescales are thermal and chemical — milliseconds to minutes. The quasi-steady driver drops the momentum time-derivative: it re-solves a steady flow field each macro step, then marches the slow states (species, energy, soot, cake and catalyst temperature) forward. That buys roughly two orders of magnitude in step size on a drive cycle.
from aeolus1d.io.build import build_network
from aeolus1d.solver.quasi_steady_network import (
QSTransientConfig, advance_network_quasi_steady_transient,
)
pipes, junctions, far = build_network(case_of(brick, t_end=0.5))
t = advance_network_quasi_steady_transient(
list(pipes.values()), junctions, far, t_end=0.5,
config=QSTransientConfig(dt_max=5.0e-3, dt_init=1.0e-4))
Note the shape: the network is built the same way — build_network does
not know or care which driver you will hand it to. Only the final call differs.
The two drivers do not give the same answer, and the gap is worth knowing¶
On the brick above, run to steady state under each:
t_end = 0.5 s QS = 493.961 K explicit = 474.067 K gap = 19.895 K
t_end = 1.0 s QS = 493.961 K explicit = 474.067 K gap = 19.895 K
Identical at both end times, so both are converged and this is a real model difference rather than one run being caught mid-transient — worth checking before drawing any conclusion from a driver comparison.
19.9 K is large, and it is mostly the porous drag. The cross-driver parity
harness (tests/integration/test_driver_steady_parity.py) measures the same
comparison on a duct with the heat closure only and finds 1.96 K. Adding
Darcy–Forchheimer drag widens it by an order of magnitude, because the QS
momentum balance has no momentum-flux term: on a brick dropping tens of kPa,
that omission is no longer small. The same harness pins the extreme case — QS
reports exactly 0.0 Pa of Rayleigh pressure rise where the compressible
drivers report about −100 Pa.
So: the quasi-steady driver is the right tool for a drive cycle and the wrong one for a like-for-like pressure comparison. Choose it for the timescale, not for the speed alone, and if you switch drivers mid-project, re-baseline rather than assuming continuity.
Real-gas pipes are refused, not silently approximated
The QS energy march is ideal-gas-caloric throughout (cv = R/(γ−1)), while
its flow solve inverts the real EOS for density. Running a real-gas pipe
would mix the two inconsistently, so the driver raises NotImplementedError
naming the pipe and telling you to use the explicit or implicit driver
instead. That refusal is deliberate — see
ADR-0004 for the
surrounding design.
Where to go next¶
| Next | Page |
|---|---|
| The species columns these bricks consume | Species and chemistry |
| Every catalyst / filter / regen field | Aftertreatment thermal & catalytic blocks |
| A full chain as a TOML recipe | Recipe: Aftertreatment chain |
| Which driver does what | Runtime data flow |