Skip to content

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 is 0 and 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).