ADR-0009 — Inflow-ghost composition for species mass conservation¶
Status: Accepted
Context¶
The gas-species transport builds a boundary ghost cell to supply the upwind
value at each pipe end. At a boundary with a prescribed species inflow (a
mass / velocity / stagnation inlet, or a junction coupling — anything that sets
Pipe.species_inflow_{left,right}), the ghost originally defaulted to a
zero-gradient copy of the edge cell, with the listed feed species then
overwritten by their scheduled values. Species the feed omits kept the
zero-gradient value.
That is wrong for a reaction product the feed does not list. Consider a
DOC/TWC oxidation brick fed C₃H₆ + O₂ + N₂ but not CO₂/H₂O: the products are
created on the brick, not fed. With a zero-gradient ghost the inlet ghost
mirrors cell 0, so at the inflow face the advective in-flux ṁ·Y₀ exactly
cancels the out-flux ṁ·Y₀ — the product can never be flushed. Chemistry
injects product every step with zero advective removal, so its mass fraction
grows without bound and ΣY drifts above 1 (mass non-conservation). Continuity
(ṁ = ρuA) stays correct and the mole-fraction sampler renormalises, so the
corruption is invisible in conversion metrics: the propylene light-off brick
reached ΣY ≈ 1.92 with outlet CO₂ ~145× the stoichiometric value while its
light-off T50 still looked plausible (found 2026-06-21 via the new
doc_propylene_lightoff plot).
This was pre-existing and latent — every shipped Brinkmeier validation case is OSC/SCR-focused (gas-phase oxidation barely fires), or feeds the full composition (the heliyon OSC case lists all species, so its ghost was already correct). The C₃H₆-oxidation channel was the first case to drive a gas-phase oxidation hard enough to expose it.
Decision¶
At a boundary with a prescribed species inflow, build the ghost from the
feed via a single shared helper _inflow_ghost_extras
(src/aeolus1d/solver/network_timeloop.py):
- Complete feed — the specified mass fractions (primary + listed extras)
sum to
≳ 1(_FEED_COMPLETE_THRESHOLD = 0.5). This is the full incoming composition: species the feed omits are absent from the incoming gas, so their ghost is0and products created on the brick are flushed at the inflow face. - Partial feed — the specified fractions sum to
≪ 1(a dilute-tracer pipe whose bulk carrier gas is not in the species set / feed). The case relies on the pipe's own composition for the unspecified remainder, so the omitted species keep their zero-gradient edge ghost (legacy behaviour).
The helper is applied identically by both species-transport code paths:
_species_rate (the explicit and implicit-Newton driver advection) and
_qs_implicit_species (the quasi-steady + implicit-Newton march). The primary
species was always set to its feed value, so it is unchanged.
Consequences¶
- Reaction products absent from a complete feed are advected out instead of accumulating; ΣY stays ≈ 1 at steady state and outlet products are stoichiometric (propylene light-off outlet CO₂ now ~1416 ppm, was ~206,000).
- Bit-identical for fully-listed feeds (heliyon OSC ΣY = 1.00000) and for partial trickle feeds (passive-SCR underfloor, feed Σ ≈ 0.005). It changes — and corrects — cases that feed a near-complete composition but omit the products (ornl passive-SCR, DOC propylene); all reacting-validation gates still pass after the change.
- The complete/partial split is a heuristic threshold (0.5). It cleanly separates the cases that exist (trickle feeds ≪ 0.5, real feeds ≈ 1.0); a hypothetical future feed summing to ~0.5–0.9 that intends carrier-fallback would be misclassified as complete. Documented as a known edge.
- Under the QS driver a fast light-off shows a bounded transient ΣY dip (≈ 0.96) that recovers to exactly 1.0 at steady state — an operator-split transient, acceptable for a steady-state-targeting solver (all validation metrics are steady).
References¶
src/aeolus1d/solver/network_timeloop.py—_inflow_ghost_extras,_FEED_COMPLETE_THRESHOLD,_species_rate.src/aeolus1d/solver/quasi_steady_network.py—_qs_implicit_species.tests/unit/test_inflow_ghost_extras.py,tests/integration/test_qs_species_inflow_conservation.py— regression.- Related: ADR-0004 (QS species CFL stability — orthogonal: this ADR is about boundary conservation).