Skip to content

ADR-0004 — Implicit-upwind transport for quasi-steady species

Status: Accepted

Context

The quasi-steady (QS) solver removes the acoustic-CFL floor so a long transient (e.g. a 6000 s aftertreatment ads/des run) fast-forwards at a macro step set by qs_cfl_adv, which is routinely > 1. The QS species transport originally advanced the gas-species mass fractions with an explicit forward-Euler upwind update. Forward-Euler upwind advection is only stable for CFL ≤ 1, so at the QS macro step it went unstable and blew up the tracer fields — any past QS run at qs_cfl_adv > 1 has suspect species output.

Decision

QS species transport uses an unconditionally-stable implicit-upwind march (_qs_implicit_species / _qs_implicit_species_kernel in src/aeolus1d/solver/quasi_steady_network.py) — a single downstream sweep analogous to the QS steady-energy march, solving the implicit upwind update rather than stepping it explicitly. This is gated by species_implicit_upwind (default on for the QS driver).

Consequences

  • QS species are stable at the large macro steps the QS driver exists to take; the tracer blow-up at qs_cfl_adv > 1 is fixed.
  • Implicit upwind is first-order and adds numerical diffusion at large CFL — acceptable for the slowly-varying QS composition fields it transports, but it means a QS species profile is not a high-accuracy advection result (consistent with the QS driver's whole premise).
  • Provenance caveat worth remembering: results from before this fix, run under the QS driver at qs_cfl_adv > 1, cannot be trusted for species/tracer channels and should be re-run.

References

  • src/aeolus1d/solver/quasi_steady_network.py:650 — _qs_implicit_species (kernel _qs_implicit_species_kernel at :720).
  • species_implicit_upwind ([case] knob) — see the update note below.

Update 2026-07: The QS driver now applies the implicit-upwind species march unconditionally (_qs_implicit_species is called directly in the QS advance loop; the flag is never read on the QS path), so the decision above still holds but is no longer gated per case. The species_implicit_upwind [case] knob (schema default False) today governs a different path: whether the implicit-Euler / Radau driver reuses this same backward-Euler march in place of its explicit, acoustic-CFL-bound species sub-stepping (src/aeolus1d/solver/implicit_network.py). The QS transport decision recorded here is unchanged.