How to set up an aftertreatment chain¶
An aftertreatment chain is a series of pipes downstream of the
exhaust manifold, each carrying a different reactive closure:
DOC for HC / CO oxidation, DPF for soot trap + regen, SCR for NOx
reduction with urea injection. In aeolus1d each element is a single
pipe. The reaction kinetics live in that pipe's [pipe.species]
block (the reaction kind), while the thermal / mechanical model
of the brick lives in a [pipe.catalyst] (gas + monolith-wall
heat) and/or [pipe.filter] (soot trap + cake) sub-block — the
chain is just the connection topology.
This tutorial wires a four-element chain (DOC → DPF → SCR → tail pipe) and shows where to set the kinetics knobs that ship today.
What you'll build¶
[exhaust manifold] → [DOC] → [DPF] → [SCR] → [tail pipe] → outlet BC
↑
urea injector (in-pipe source)
Each element is a pipe; the joins are junctions (kind =
"passthrough" for a simple in-line join, or kind = "plenum"
for a volume that can also split flow). The inlet feed for the
chain is the engine's exhaust manifold — for this tutorial we'll
replace it with a transient_mass_flow_inlet BC so the chain can
be exercised in isolation.
Step 1 — geometry + species¶
[case]
name = "aftertreatment_chain"
t_end = 0.1
cfl = 0.4
[gas]
gamma = 1.33 # exhaust mixture, hotter than dry air
R_gas = 290.0
[[pipe]]
id = "doc_brick"
length = 0.12
n_cells = 24
diameter = 0.10
[pipe.init]
p = 1.05e5
T = 700.0
u = 10.0
[pipe.species]
name = "HC" # primary column — the DOC's oxidised reactant
Y_init = 5.0e-4
[[pipe.species.extras]]
name = "CO"
Y_init = 2.0e-3
Species columns are declared per pipe: a single primary
(name + Y_init) plus zero or more [[pipe.species.extras]]
tables, each with its own name and Y_init. Columns are matched
by name as gas crosses junctions, so a species only needs to
appear on the pipes that carry it.
Each reactive brick expects a specific primary species — HC for
the DOC (doc_oxidation), NH3 for the SCR (nh3_scr_kinetics) —
so the bricks do not share one identical species block. Give
each pipe the columns its own reaction reads; the bundled examples
(46_doc.toml, 42_scr_urea_injector.toml) show the exact set for
each.
Step 2 — DOC closure¶
The DOC is a two_phase_heat monolith (gas + solid-wall thermal
mass) whose oxidation kinetics are set on [pipe.species] via
reaction = "doc_oxidation" — a simultaneous HC + CO Arrhenius
light-off (CO lights first, ~150 °C; HC ~250 °C):
[pipe.catalyst]
kind = "two_phase_heat" # gas + monolith wall thermal
rho_s_cs = 5.0e5 # ρ_s·c_s of the washcoat [J/(m³·K)]
a_v = 4000.0 # specific surface area [m²/m³]
h = 150.0 # gas–solid heat transfer [W/(m²·K)]
T_solid_init = 700.0
[pipe.species]
name = "HC"
Y_init = 5.0e-4
reaction = "doc_oxidation"
A_hc_ox = 1.0e9 # HC branch (~250 °C light-off on Pt/Pd)
Ea_hc_ox = 1.0e5
LHV_hc = 43.5e6
A_co_ox = 5.0e9 # CO branch (~150 °C)
Ea_co_ox = 7.5e4
LHV_co = 1.01e7
[[pipe.species.extras]]
name = "CO"
Y_init = 2.0e-3
Instead of the explicit (rho_s_cs, a_v, h) datasheet you can
give a monolith geometry spec and let aeolus1d resolve the thermal
parameters from cell density and substrate — set cpsi, ofa and
substrate (e.g. "cordierite") under kind =
"two_phase_heat_var_nu". Supplying both forms at once is an error.
See 74_twc.toml for the geometry form.
For a full elementary surface mechanism (rather than the bundled
lumped kinetics) use kind = "mechanism" with mechanism_path
pointing at a CHEMKIN .inp or Cantera .yaml/.yml file on disk
plus an optional surface_init table of initial coverages. There
is no aeolus1d://… pseudo-URI — mechanism_path is a real file
path resolved against the process working directory.
Step 3 — DPF closure¶
The DPF is a [pipe.filter] of kind diesel_particulate_filter:
a trap that pulls a PM species column out of the gas, a cake
Darcy-resistance pressure drop, and regeneration that burns the
stored soot. A plain DPF needs only the filter block — passive
(NO₂-driven) and active (O₂-driven) regen are both sub-paths of
[pipe.filter.regen], not a separate catalyst:
[pipe.species]
name = "CO"
Y_init = 1.0e-3
[[pipe.species.extras]]
name = "O2"
Y_init = 0.10
[[pipe.species.extras]]
name = "NO2"
Y_init = 1.0e-4
[[pipe.species.extras]]
name = "PM1"
Y_init = 1.0e-3 # particulate column captured by the trap
[pipe.filter]
kind = "diesel_particulate_filter"
[pipe.filter.trap]
pm_species_name = "PM1" # which column to trap
filtration_efficiency = 0.95 # fraction of incoming PM captured
soot_loading_init = 3.0e-3 # pre-loaded cake [kg/m³]
[pipe.filter.cake]
resistance = 1.0e7 # cake Darcy resistance [Pa·s·m²/kg]
mu_ref = 3.0e-5 # gas viscosity [Pa·s]
[pipe.filter.regen]
A_o2 = 1.0e8 # active O₂-pathway pre-exponential
Ea_o2 = 1.5e5
A_no2 = 5.5e3 # passive NO₂-pathway pre-exponential
Ea_no2 = 1.0e5
[pipe.filter.thermal]
Omega_cake = 1.5e6 # ρ_s·c_p,s of soot cake [J/(m³·K)]
h_eff = 1.0e4 # volumetric cake↔gas heat transfer [W/(m³·K)]
T_cake_init = 700.0
The DPF integrates soot loading per-cell over the run; the GUI's
plot dock surfaces the filter/soot_loading and filter/T_cake
time series when the run is traced.
To put an SCR washcoat on the filter (an SCRF), add a
[pipe.catalyst] block and an nh3_scr_kinetics reaction to the
same pipe — see 43_scrf.toml.
Step 4 — SCR closure + urea injector¶
The SCR is again a two_phase_heat brick; its deNOx kinetics —
NH₃ adsorption/desorption (Temkin coverage dependence via
alpha_des), NH₃ oxidation, and standard SCR — are set through
reaction = "nh3_scr_kinetics":
[pipe.catalyst]
kind = "two_phase_heat"
rho_s_cs = 4.0e5
a_v = 3500.0
h = 120.0
T_solid_init = 600.0
[pipe.species]
name = "NH3"
Y_init = 0.0
reaction = "nh3_scr_kinetics"
A_ads = 1.0e5
Ea_ads = 40.0e3
A_des = 1.0e11
Ea_des = 120.0e3
alpha_des = 0.2 # Temkin coverage dependence (Cu-zeolite-like)
A_ox = 1.0e8
Ea_ox = 80.0e3
A_scr = 5.0e8
Ea_scr = 60.0e3
W_cat = 2.0 # NH₃ storage capacity
theta_init = 0.0
[[pipe.species.extras]]
name = "NO"
Y_init = 6.0e-4
[[pipe.species.extras]]
name = "O2"
Y_init = 0.10
[pipe.urea]
kind = "urea_spray"
x_frac = 0.1 # inject at 10% of pipe length
n_spray_cells = 1
uws_conc = 0.325 # 32.5% AdBlue
mdot_t = [0.0, 1.0]
mdot_v = [0.0, 1.5e-5] # kg UWS/s ramp
eta_conv = 1.0
The [pipe.urea] block adds the urea-to-NH₃ decomposition source
(CO(NH₂)₂ + H₂O → 2 NH₃ + CO₂, ≈0.567 kg NH₃ per kg urea) to the
named cell range. The SCR reaction then consumes the stored NH₃
against gas-phase NO to drive standard SCR. For a downstream
ammonia-slip brick use reaction = "nh3_slip_catalyst" (see
45_two_stage_scr.toml).
Step 5 — connect the chain¶
# Junction between DOC and DPF.
[[junction]]
id = "doc_to_dpf"
kind = "plenum"
volume = 1.0e-4
[junction.init]
p = 1.05e5
T = 700.0
[[junction.ports]]
pipe = "doc_brick"
end = "right"
area = 7.85e-3
[[junction.ports]]
pipe = "dpf_brick"
end = "left"
area = 7.85e-3
For a plain in-line join you can use kind = "passthrough"
instead (it takes no volume/init) — that is what the bundled
DOC / SCR examples use. Repeat for dpf_to_scr and scr_to_tail.
Then a transient_mass_flow_inlet BC on the inlet end of the DOC
(replaced by the engine's exhaust manifold in the real case) and a
pressure_outlet on the tail-pipe right end.
Step 6 — run + verify¶
aeolus1d run /tmp/aftertreatment_chain.toml --out /tmp/at.h5 \
--trace-dt 5e-4 --info-every-n 200
GUI inspection checklist:
- DOC: select the pipe, plot the
T(x)profile — should show light-off (temperature rise downstream) under hot inlet. - DPF: select the pipe, plot
filter/soot_loading— should rise (loading) or drain (regen) depending on the balance of PM intake and regen temperature. - SCR: plot
Y_NOmid-brick — should drop once urea injection fills the NH₃ storage.
Reference¶
examples/46_doc.toml— minimal DOC standalone.examples/41_dpf_regen.toml— DPF with active + passive regen.examples/42_scr_urea_injector.toml— SCR with urea injector.examples/43_scrf.toml— SCR-coated filter (catalyst + filter on one pipe).examples/45_two_stage_scr.toml— Cu-zeolite + V₂O₅ slip two-bed SCR.examples/38_engine_turbo_scr.toml— engine + turbo feeding an SCR brick.- See Closures — reference table for the full catalyst / filter / urea / species kind list.
- For DPF heater control (electrical regen assist), see
examples/53_dpf_heater_ctrl.toml. ```