Skip to content

Scope, validated regime, and known limitations

This page states, in one place, what aeolus1d is validated for, what it is not, and what it is not qualified for — so you can decide whether to trust a number before you rely on it. It is deliberately conservative. The detailed, reproducible evidence behind every claim here lives in the validation dossier; this page is the honest summary and the caveats.

aeolus1d is a 1D compressible-Euler pipe-network solver with engine, powertrain, and aftertreatment modelling. It is pre-1.0, alpha-to-beta software (PyPA Development Status :: 4 – Beta, since 0.4.0; it was 3 – Alpha before). We do not claim general "parity" with GT-POWER / AVL / Ricardo WAVE as a product. Where the term "parity" appears in project docs it means a specific, auditable thing — validated competence on a named workflow at a stated tolerance, reproducible from a checked-in case — never feature-count parity with a 30-year funded commercial code.

Verification — are the equations solved correctly?

Dataset-independent checks that the numerics converge at their design rates:

  • Spatial order (flux): HLLC + MUSCL/MP5 checked against the analytic Sod solution (tests/validation/test_sod_against_analytical.py) and an observed-order test on a smooth density wave (MUSCL ≥ 1.8; tests/integration/test_smooth_advection.py). The order is a lower bound away from smooth extrema, where a TVD limiter must clip. The order is also verified per field, not just density — a running-solver Richardson self-convergence on a smooth pulse where ρ, u, and p all vary independently recovers order 2 for each of density, velocity, and pressure (measured ≈1.98 / 1.94 / 2.01; tests/integration/test_running_order_multivar.py).
  • Classic high-order / strong-shock benchmarks: the literature reference problems the basic tubes can't exercise — Shu-Osher (MP5 must preserve the post-shock entropy-wave amplitude a dissipative scheme erases; observed ~3.8× first-order), Woodward-Colella interacting blast waves (density and pressure stay strictly positive through the violent collision; the ρ≈6 spike forms at x≈0.78), and the Abgrall moving γ-interface (see the variable-γ caveat under Known limitations). bench/{shu_osher,woodward_colella,abgrall_interface}.py, gated by tests/integration/test_euler_verification_benchmarks.py.
  • Species mass-flux scheme-consistency (telescoping invariant): a passive scalar must advect on the same per-face mass flux the density was advanced with, recovered by telescoping the scheme's own mass residual (\(\dot m_i = \dot m_0 - \Delta x \sum_{j<i} R_{j,0}\)) from a boundary-face anchor. A consistent anchor telescopes exactly to the scheme's true right-boundary flux; the check pins \(|\dot m_N - F_N| < 10^{-9}\) across all seven flux/EOS variants (first_order, MUSCL and MP5 each in scalar-γ / variable-γ / real-gas-EOS), and a teeth-test confirms the pre-#719 first-order anchor violated it by \(\mathcal O(1)\) (tests/integration/test_species_scheme_consistency.py). This is a structural-consistency check, not a convergence-rate one — it guarantees a uniform composition stays uniform across sharp gradients on every scheme.
  • Temporal order (each integrator): a per-rk self-convergence study pins the formal order of all seven time integrators — IE ≈ 1, SSP-RK2 / BDF2 ≈ 2, SSP-RK3 ≈ 3, RK4 ≈ 4, RK5 / Radau-IIA(3) ≈ 5 (tests/integration/test_temporal_order.py).
  • Source-term discretisation (MMS): Method-of-Manufactured-Solutions proofs that the source closures converge at the formal order (2 for MUSCL) and do not without the source. The base energy-source path (tests/integration/test_mms_source.py) plus area variation, wall friction, and wall heat (tests/integration/test_mms_closures.py) — each re-derives the closure by hand so that a wrong-signed / wrong-coefficient / mis-coupled closure leaves an O(1) residual that fails to converge (asserted by a load-bearing control per closure). The catalyst reaction source is stiff, operator-split chemistry — its integrator order is verified separately in test_temporal_order.py, not by a spatial MMS.
  • Regression net: a golden-snapshot suite (tests/regression/) freezes the final state of canonical cases and fails on any silent physics drift ≳ 1e-4.

The GCI machinery ships in aeolus1d.validation — grid_convergence_index / mesh_convergence_study (Roache / ASME V&V-20 three-grid apparent order + Richardson extrapolation + a numerical-uncertainty band), and ScalarComparison carries u_num / u_data (RSS'd into validation_uncertainty, with a within_validation_uncertainty verdict). It is now run end-to-end on real suites via avl_compare_common.gci_mesh_study (run any AVL case's headline scalar at three mesh densities). Two contrasting results, both on committed data:

  • DOC light-off T50 (C02001) — mesh-converged. A 25/50/100-cell study gives GCI ≈ 0.07 % (u_num ≈ ±0.3 K); the reported U_val ≈ ±5 K is dominated by the digitization term u_data, and the model–AVL error (~2 K) sits within U_val. run_validation.py --gci reports it; a @slow test gates that u_num stays small.
  • DPF regen peak substrate T (C02006) — mesh-converged, cleanly. A 15/25/40-cell study gives peak substrate T 662.1 → 662.8 → 663.2 K with observed order p = 1.0, in the asymptotic range (GCI ≈ 0.13 %, u_num ≈ ±0.8 K) — so the regen thermal peak is a trustworthy number (run_validation.py --gci; slow ~12 min).
  • SCR With-NO NO-conversion (C02002) — NOT mesh-converged. The same study gives NO conversion 21.5 % → 12.6 % → 7.2 % at 25 / 50 / 100 cells: GCI ≈ 140 % (u_num ≈ ±10 conversion-points) with an unphysical negative Richardson h→0 value. The coarse-mesh agreement is a numerical-diffusion artefact, so this quantity is reported, not quoted as validated (run_validation.py --gci; slow ~10 min).

Pending (tracked): rolling the --gci pattern out to the remaining quoted numbers (the flux + area/friction/heat source paths are now MMS-covered). Treat any single quoted number as mesh-specific unless it carries a U_val — as the SCR case shows, a value without its mesh can be a pure diffusion artefact.

When GCI does not apply. For the calibrated lumped-closure aftertreatment cases (DOC Voltz light-off, SCR Temkin storage, C02006 DPF/CSF regen), a grid-convergence index on the discretisation uncertainty u_num is misleadingly small and not the honest error bar: per ASME V&V-20 the total validation uncertainty u_val = √(u_num² + u_input² + u_D²) is dominated by the model-form/data term u_D (the lumped rate constants were fitted to the AVL trace), not by mesh. These cases are therefore labelled GCI-not-applicable — calibrated in their pass_criteria.toml, and GCI is reserved for the handful of numbers actually quoted in the docs where u_num is the meaningful term.

Validation — does it agree with reality?

The reference type matters as much as the number. We distinguish three:

Workflow Status Reference type Honest verdict
Engine breathing / manifold tuning (SI) breathing verified; quantitative match re-cal owed mixed — mendeley_ethanol_water is experiment; ricardo_ktm is code-to-code (reclassified 2026-08-30: its reference is Ricardo WAVE's own OEM baseline, not the chassis dyno — see that suite's README) The valve-coupling breathing physics is verified correct + conservation-pinned (ADR-0008, now default). The published-data quantitative match was calibrated against the legacy coupling and is mid-re-establishment: Ricardo-WAVE KTM 510 cc is 7/10 with the recommended --throttle --wall-heat invocation, re-measured 2026-08-30 on 9222699d (per-point table in that suite's README; the 6/10 recorded on 2026-06-09 stood here until then, and which point flipped is unrecoverable because that record kept only the aggregate) — 3/10 bare; was 8/10 legacy same-invocation; originally re-validated 2026-06-09: the single-point-calibrated idealised cam over-admits air across RPM (every point over-predicts torque), a per-RPM re-cal owed, gated on proprietary OEM cam data. Mendeley ethanol/water is 39/60 — the honest score: the earlier 44/60 (2026-06-09) was flattered by a knock-integral bug that silently retarded combustion 10° on this scaffold, bisected 2026-08-15 to the 2026-07-01 fix 643fc3fb (closure in BACKLOG_HISTORY.md; fixed-fuel suite, so the orifice coupling is ~1 %/near-neutral, no re-tuning) — the residual concentrates at 40–50 % water, where the measured gap is combustion deterioration under water dilution; the earlier "charge-cooling / constant-γ" diagnosis was implemented, measured, and retracted 2026-08-15 (charge cooling moves the prediction the wrong way — see the suite README). (An earlier note that the data was "local-only / not re-runnable in CI" was wrong; operating_points.csv is committed and runs in CI.)
Acoustics / NVH validated analytic + code-to-code transmission-loss vs analytic + a GT-Suite TMM cross-check. Not yet experiment-validated.
Aftertreatment (DOC / SCR / TWC) partly bench-validated experiment (open synthetic-gas-bench) + code-to-code vs AVL Boost digitized traces Corrected 2026-08-30. This row said "Not validated against lab / synthetic-gas-bench data", which had been false since heliyon_twc_co_oxidation landed: that suite reproduces the measured Table-11 CO-step OSC discharge durations of an open-access (CC BY) Heliyon 2024 synthetic-gas-bench study across three ageing states — and is predictive across two of them, since Omega_OSC_brnk is calibrated on the fresh state only while mild and severe follow from the paper's independent Table-13 site-density ratio. ornl_passive_scr adds measured NO→NH₃ selectivity-vs-λ (ORNL CLEERS 2014 / SAE 2016-01-0934) at trend level. Both are CI-gated and pass. What remains true: several rate constants are calibrated to AVL rather than predictive, doc_propylene_lightoff is code-to-literature (a T50 band, not a dataset), and the SCR NO-conversion mesh-calibration caveat below still stands. See docs/validation-dossier.md
Turbo matching consistency check self-consistency shaft power-balance + map fidelity — the code agrees with itself, not with experiment.
Electrified drive cycle consistency check self-consistency energy bookkeeping — again a self-check, not external validation.

See the validation dossier for the per-operating-point numbers, the checked-in case behind each row, and how to reproduce them.

Blind-predictable vs structurally-blocked gates

A gate is only worth aiming a blind (un-tuned) prediction at if the physics it needs is actually present. We do not claim un-tuned predictive closure of any gate that is structurally missing physics; for those, the honest target is a calibrated match, labelled as such. Each v0.3.0-era gate is classified:

Gate Blind-predictable today? Why
W1 breathing physics (mass conservation across the valve) Yes — verified Conservation-pinned by test_cylinder_valve_conservation.py (ADR-0008); no tuning involved.
W1 KTM / Mendeley quantitative match No — calibrated Single-point-calibrated cases; a blind per-RPM match needs OEM cam geometry (KTM) or charge-cooling physics (high-water Mendeley) not in the shipped case.
W2 turbo matching n/a — self-consistency Conservation/​map-fidelity check, not a prediction of external data.
W3 C02006 co_conv_cat No — CO calibration gap The CSF CO/HC oxidation kinetics do exist (aeolus1d.closures.filter.csf_into) and are active on the Cat brick: c3h6_conv_cat matches AVL's Cat trace within ~16 % (gated at 30 %), which disproves the earlier "no CSF kinetics / structural" claim. Only co_conv_cat remains off (~77 % mean) — aeolus1d under-converts CO because the mass-transfer cap that keeps the QS steady-energy exotherm stable also caps CO conversion at AVL's lower band. A CO-oxidation calibration/stability gap, not missing physics; recalibration is deferred (pass_criteria.toml FOLLOWUP).
W3 C02006 Non-Cat channels (T, soot, Δp, conv) partly — calibrated The passive brick is calibrated to the AVL Non-Cat trace (cake-Darcy, A_o2 ×4); the soot burn-down is bi-stable (cliff vs MT-limited plateau) until an MT-limited rate cap lands.
EPW diesel breathing / IMEP_High / PFP Yes — near-exact With the orifice coupling (ADR-0008), vol-eff ~74 % and IMEP_High/PFP land within a few % at the anchor points; the blind 2nd-operating-point T_EXH check no longer generalises.
↳ CORRECTED 2026-09-10 T_EXH: no The row above read "one blind 2nd-operating-point T_EXH check generalises (−5.6 % / −1.8 %)" until this date. Those figures predate 2026-09-06. test_blind_operating_point_T_EXH_generalizes is now xfail(strict=True) at −9.91 % against a 6 % gate (AVL 605.4 °C vs aeolus1d 545.44 °C), re-measured 2026-09-07 and reproducing the 2026-09-06 run to 0.05 °C — deterministic, so the drift is real. Two of the three calibration anchors are also outside the gated 40 K band (−47.09 K and −43.09 K). A refit was measured and REFUSED: the two blind ops want h = 57.1 and 196.3, a 109.9 % spread against a 25 % bound pre-registered in BACKLOG.md before any number existed. The breathing / IMEP_High / PFP claims in the row above are unaffected — only T_EXH is withdrawn.
EPW diesel BMEP / NOx No — residual gaps Pumping-loop residual leaves BMEP ~30 % low. The two former NOx blockers have since landed — the Zeldovich post-processor no longer raises (it escalates BDF→LSODA→Radau→equilibrium-envelope, aeolus1d.analysis.nox_post) and an opt-in dissociation-aware burnt-zone-T cap ([cylinder.combustion] burnt_dissociation) tames the constant-γ peak-T — but NOx itself is still uncalibrated/unvalidated. Neither residual is a conservation bug.
W4 acoustics Yes — analytic Reproduces the closed-form resonance + GT TMM in the plane-wave regime; not an external-experiment claim.
W5 electrified n/a — self-consistency Energy-bookkeeping check, not a prediction.

The coupled DOC + DPF + SCR aftertreatment train remains a named v0.3.0 gate that is not yet closed (single-component aftertreatment is demonstrative; the coupled train is unvalidated).

Known limitations / not validated / known-wrong

Do not rely on aeolus1d for these without independent verification:

  • The caloric model is a constant by default, and drifts at engine temperatures. Two separate paths, both measured 2026-08-01:

Ideal-gas path (203 of the 212 bundled cases). A single scalar gamma. For N2 against NASA-7, gamma falls from 1.4005 at 300 K to 1.3139 at 1500 K, so a constant 1.4 is 6.6 % high there. The consequence is larger than that sounds: pressure is recovered as p = (gamma - 1) rho e, and the error in gamma - 1 reaches 11 % at 800 K, 22 % at 1200 K and 31 % at 1800 K. aeolus1d already has the fix — [pipe.species] per_cell_gamma = true evaluates gamma per cell from NASA-7 at the cell's own temperature, cutting the 1500 K error from 6.6 % to 0.9 %. It is off by default and used by three bundled cases, and it requires a [pipe.species] block: a single-gas pipe with no species tracking cannot use it at all.

The EPW diesel validation suite is one of the three: since 2026-08-10 its exhaust pipes ship per_cell_gamma = true, after an A/B measured on one commit came back net-better in both anchored and predictive mode. It is paired with a re-fitted anchored-mode plenum h*A (550 -> 377 W/K) and the pair must move together — see the suite README. The DD13 suite still declares no species columns, so its exhaust-side numbers carry this error. See tests/integration/test_caloric_model_accuracy.py.

Until 2026-08-01 simply adding the columns did not help on an engine, which is why no engine case had them. A cylinder routed only its fuel column; every other column fell back to the pipe's zero-gradient ghost (which mirrors the runner back at itself) and, under the orifice valve model, blowback mass entered the runner with no species credit at all. The runner's composition was therefore diluted toward zero, and a zero-sum composition makes mixture.gamma return exactly 1.0 — which zeroes the pressure and collapses the HLLC fan into a ZeroDivisionError. The cylinder now carries a general composition (next entry), so the columns can be added; doing so on the validated diesel suites is a separate exercise, because it changes their exhaust-side results.

  • The cylinder's composition is a well-mixed single zone with complete-combustion products. Since 2026-08-01 a cylinder keeps a mass inventory of every non-fuel species column its ports touch, exchanges it with the runners through both valve couplings, and converts burnt fuel into stoichiometric CO2 + H2O with the matching O2 debit (aeolus1d.chemistry.fuel_stoichiometry, driven by the fuel's H/C atom ratio). What that does not model: no CO, no soot, no NO formed in the cylinder, no dissociation, and no burnt/unburnt zone split — the charge is one well-mixed volume, so the exhaust composition is a cycle-averaged product mixture rather than a resolved blowdown history. Rich operation is not modelled either: when the tracked O2 runs out the conversion scales back and reports the shortfall in Cylinder.last_products_unconverted instead of forming CO. The inventory is advanced by an operator-split forward-Euler step on the stage-mean port rate — exact for SSP-RK2, first-order for SSP-RK3. Inert on any case whose pipes carry no non-fuel column, which is every bundled engine case. See examples/91_cylinder_species_transport.toml.

The same stage-mean treatment applies to a plenum's fuel and species inventories (Junction, EHRJunction). Until 2026-08-01 both integrated the last RK stage's rate, which lost 6.6e-05 (N2) / 1.5e-04 (O2) / 6.7e-05 (fuel) of the tracked mass over 4 ms on a closed network — see tests/integration/test_plenum_species_conservation.py. Volume-side inventories are the recurring home of this mistake, so any new one should use species_coupling.StageRateAccumulator rather than caching a single rate.

Real-gas EOS path. The scalar cv has the same weakness (N2: 741 J/(kg K) at 300 K rising to 946 at 1500 K, so a constant tuned near ambient is 27 % low). Since 2026-08-01 [pipe.eos] accepts an optional cv_poly / cv_T_min / cv_T_max polynomial; omitting it keeps the constant and is bit-identical. Note the narrow reach: ideal-gas pipes never take the EOS residual (has_real_gas is eos_kind != EOS_IDEAL), so cv_poly only affects pipes configured with redlich_kwong / peng_robinson / tabulated — 6 bundled cases, none of the validated suites.

  • Choosing an EOS: cubics have an accuracy ceiling at high density. Measured against NIST Chemistry WebBook nitrogen (tests/integration/test_eos_vs_nist_reference.py), max relative error:
pressure @200 K sound speed @200 K pressure @300 K
ideal 19.1 % — 0.52 %
redlich_kwong 0.80 % 6.96 % 1.55 %
peng_robinson 4.85 % 9.53 % 1.67 %
tabulated (CoolProp) 0.037 % 0.025 % 0.037 %

Three things worth taking from that. Near the critical region a cubic is a large win over ideal (0.8 % vs 19 %). In the near-ideal regime the cubics are slightly worse than ideal, which is also cheaper — "real gas" does not mean "more accurate everywhere". And the residual sound-speed error of the cubics is the p-v-T form at liquid-like density, not the caloric model: it grows with density and no cv(T) can remove it. When density accuracy matters, use kind = "tabulated".

  • A non-ideal EOS cannot be combined with species transport, and is now refused at load time (2026-09-15). Pipe.eos_params is one array for the whole pipe — one R_gas, one Tc, one Pc, one omega — so a pipe whose composition varies cell to cell is evaluated with the wrong substance everywhere but one cell. Measured with Peng-Robinson in molar units and van der Waals one-fluid mixing (k_ij = 0), pressure at fixed (T, ρ):
condition error today if only R were per-cell real-gas worth
exhaust, 700 K, 0.75 kg/m³ 36.4 % 0.05 % 0.05 %
EGR cooler, 400 K, 1.6 kg/m³ 36.5 % 0.42 % 0.25 %
wet exhaust H₂O/N₂, 700 K 55.0 % 0.21 % 0.26 %
dense CO₂/N₂, 320 K, 50 kg/m³ 43.8 % 17.96 % 13.99 %

This sharpens the "real gas is not more accurate everywhere" point above into a rule you can apply. Compare the last two columns: at every condition an engine runs, the real-gas correction is worth 0.05–0.26 % while a wrong composition costs 36–55 % — so on a compositionally graded exhaust duct a cubic EOS is not merely unhelpful, it is two orders of magnitude the wrong thing to spend accuracy on. Use kind = "ideal" there: its p = (γ−1)ρe is composition-correct already, because gamma_pad is built per cell from the mixture's own cp and R_mix.

Lifting the restriction needs per-cell EOS parameters — eos_params (P,) → (N, P) across the 28 @njit kernels that take a params array, plus k_ij as a new input surface. Filed in BACKLOG.md with this measurement. Note that per-cell R alone does not do it: it is a ~99 % win only in the regime where the cubic buys nothing, and leaves 18 % in the dense regime where the cubic is the point.

  • Diesel exhaust temperature is no longer ~10× high — that was an artefact of the under-breathing valve bug, closed by the orifice coupling (ADR-0008). With the corrected default + a physical exhaust wall-heat coefficient, EPW T_EXH is no longer 10x high. CORRECTED 2026-09-10: this bullet read "lands within ~±13 % mid-range and a blind 2nd-operating-point check generalises (−5.6 % / −1.8 %)" until this date, on figures that predate 2026-09-06. Re-measured, the blind op reads −9.91 % against a 6 % gate and the test is xfail(strict=True); in predictive mode — the mode a user without the vendor CSV actually runs — T_EXH is report-only and NOT VALIDATED, reading −44.9 / −113.4 / −80.9 K at the 1200 / 1500 / 1800 anchors (measured 2026-09-07; −38.6 / −109.0 / −73.2 K re-measured 2026-10-11, --ops 3 predictive on main 56dad407, with the 1500 rpm op not plateau-certified). Treat diesel T_EXH as indicative. The residual diesel gaps are the pumping loop (BMEP ~30 % low) and NOx (a frozen constant-γ burnt-zone back-solve over-predicts peak T at high load, inflating the Zeldovich estimate — mitigated but not closed by the opt-in [cylinder.combustion] burnt_dissociation cap and the two-zone gamma_burnt / gamma_unburnt path); treat diesel NOx and BMEP as indicative, not validated.
  • EPW diesel: cyclic convergence / gas-exchange under-convergence is the open driver. The EPW steady suite runs a short settle protocol (N_CYCLES_SETTLE + 1 = 2 cycles, last cycle read) — adequate for the mechanical outputs since the orifice coupling (breathing converges by ~cycle 4–5, trapped mass matching AVL MF_IA to ~10 %, vol-eff ~74 %), but the gas-exchange loop remains under-converged: IMEP_Low plateaus several bar too negative (−1.7 / −3.3 / −4.4 bar at the anchors vs AVL ≈ −0.5), which is precisely the residual behind the ~30 % low BMEP above. Quoted EPW numbers are protocol-specific until a cycle-convergence detector + converged-state wall-heat re-tune land (tracked in BACKLOG.md).
  • Quasi-steady solver (solver = "quasi_steady") operating limits. The QS SIMPLE low-Mach mode is validated on DPF/filter aftertreatment pipes (the C02006 keystone) — not across all pipe kinds and regimes:
    • Catalyst pipes can crash the QS flow solve. A DOC-style catalyst pipe (two-phase catalyst + Darcy-Forchheimer porous + DOC oxidation kinetics) can drive the SIMPLE solve to a degenerate outlet state — a ZeroDivisionError in the pressure-outlet kernel with ρ/p ≈ 0 at the outlet cell. Only DPF/filter pipes are QS-validated today.
    • Strong exotherms can crash the QS SIMPLE kernel. A high-temperature regeneration runaway (peak inlet ≳ 740 K combined with heavy soot loading) drives the SIMPLE solve to a ZeroDivisionError as a cell's density → 0 when the steady-energy temperature spikes. Gentle passive regen (C02006, ≤ 693 K) is stable; high-soot active-regen drive cycles can crash until the kernel guards the singular regime.
    • DPF cake soot-drag is absent from the QS momentum equation — only the pipe's porous Darcy-Forchheimer drag is carried, so QS pressure drop under heavy cake loading reads low.
    • No mixed engine + aftertreatment multi-rate network — QS is whole-network: an engine-breathing section cannot run compressible while the aftertreatment section runs QS in the same case.
    • Species mass conservation under a fast light-off (transient). Reaction products absent from the inlet feed are correctly flushed at steady state (ΣY ≈ 1; the inflow-ghost fix, ADR-0009), but a sharp gas-phase light-off shows a bounded transient ΣY dip (≈ 0.96) before recovering to 1.0 — an operator-split transient. QS validation metrics are steady-state, so this is a numerical note, not a result caveat.
  • Coupled aftertreatment train. A cross-coupled DOC + DPF + SCR drive-cycle is a named v0.3.0 release gate that is not yet closed. Single-component aftertreatment is demonstrative (above); the coupled train is not validated.
  • SCR NO-conversion is mesh-calibrated, not mesh-converged. The AVL C02002 With-NO conversion matches AVL (30.4 % vs 30.6 %) only at its 50-cell calibration mesh; refining monotonically lowers it (45 % @ 25 → 30 % @ 50 → 19 % @ 100, change not shrinking — numerical diffusion on a coarse mesh over-predicts conversion). The converged value is lower than AVL's, so the agreement is a calibration artefact. See the C02002 suite README.
  • Any temperature-critical claim outside the validated SI-breathing regime (light-off prediction, wall/solid temperatures, heat rejection) should be treated as indicative until a per-quantity uncertainty is published.
  • Real-gas (PR/RK/tabulated) and network acoustic eigenfrequencies are exercised by tests but not validated against external data.
  • Variable-γ pressure equilibrium — default recovery is Karni-consistent (convergent). The per-cell-γ HLLC (use_per_cell_gamma) recovers each cell's temperature self-consistently from the conserved internal energy (fixed point on T = (γ(T)-1)·e/R) — the gamma_T_from_energy path, now the default. Per-cell γ is opt-in per pipe via [pipe.species] per_cell_gamma = true (the recovery mode is the companion gamma_T_from_energy flag, default true); see examples/87_multicomponent_variable_gamma_shock_tube.toml. With it, an advected γ-jump interface at uniform p, u stays near pressure equilibrium: the moving-contact oscillation on an N₂|CO₂ contact is <0.1 % pressure / sub-% velocity and converges under mesh refinement (p-error ≈1.3e-3 → 2.4e-4 from N=100 → 800) — i.e. genuine truncation error, not a consistency defect. Setting gamma_T_from_energy=False restores the legacy scalar-mean-γ T recovery, which exhibits the classic Karni (1994) failure mode: a mesh-independent ~1.3 % / ~3.3 % oscillation (bounded, no blow-up). For uniform composition the two recoveries are algebraically identical (e = p/((γ−1)ρ)), so scalar-γ and uniform-mixture results — including every golden snapshot — are bit-identical either way; only sharp multicomponent γ-contrasts shift. Pinned both ways by test_euler_verification_benchmarks.py::test_abgrall_interface_*. A fully quasi-conservative (Abgrall 1996) flux that would drive the residual to exact round-off is deliberately not pursued: it requires advecting 1/(γ−1) as an independent, non-thermodynamic field, which is incompatible with aeolus1d's physical γ = mixture_γ(T,Y) wherever composition mixes or temperature changes (i.e. every reacting cell) — the standard pressure-equilibrium-vs- thermodynamic-consistency trade-off, resolved here in favour of thermodynamic consistency with a convergent residual.
  • Calibration vs prediction: a coefficient tuned to reproduce one dataset is calibration, not predictive validation. Aftertreatment rate constants and some wall-heat coefficients are calibrated; the KTM flow_nu result (no tuned heat-transfer coefficient) is the predictive gold standard.
  • Bit-reproducibility is scoped to x86-64. (Stated here 2026-08-29; the decision itself dates from 2026-07-31.) The golden regression net asserts bit-identity under AEOLUS_FASTMATH=0, and that guarantee is claimed on x86-64 only. Cross-architecture equality was never attainable for the chemistry and EOS rows — libm exp / log differ by ~1 ulp between architectures — so it was scoped out rather than pursued. aeolus1d is expected to run correctly elsewhere; what does not carry across architectures is the bit-for-bit reproduction of a stored snapshot. The decision was recorded in MATURITY.md (R1) and docs/ci-runner-runbook.md, neither of which a user reads — so the limitation R1 was closed on the strength of being "stated" was, until this entry, not stated anywhere in the user-facing documentation.

Experimental / research seams (not part of the supported surface)

These ship in-tree as research scaffolding. They are not validated, not covered by the regression net, and may change or be removed without a deprecation cycle:

  • the ml extra (scikit-learn / torch) and the learned-closure NPZ seams (py_junction_bc = "learned_npz", turbo_map = "learned_npz");
  • the PINN closure adapter (aeolus1d.closures.pinn, Python-API only);
  • the ISAT in-situ tabulation prototype (aeolus1d.closures.isat_chem).

The full aftertreatment closure set (DOC / DPF / SCR / SCRF / TWC) is demonstrative / calibrated as noted above — usable for exploration, not for regulatory or certification work.

Deprecation & stability policy

What you may rely on staying stable, and how it changes when it must.

The supported surface is:

  • the aeolus1d CLI — command names, their documented flags, and exit codes (CLI reference);
  • the TOML case format — every field documented in run settings and the closures reference, versioned by the schema_version marker (the first key of the [case] block) with an explicit migration path (ADR-0007);
  • the documented Python API — exactly the symbols rendered under API reference (driving the solver, runtime objects, the closure registry). Undocumented modules, _-prefixed names, and internal kernels are not part of it and may change at any time;
  • the closed closure / component registry names a case file selects by string (aeolus1d.closures.registry).

Everything under Experimental / research seams above is explicitly outside this surface and may change or vanish without notice.

Pre-1.0 (current). aeolus1d is 0.x; under SemVer, 0.x grants no API-stability guarantee. In practice we hold a stronger line than that minimum:

  • Breaking changes to the supported surface happen only between minor versions (0.2 → 0.3), never in a patch (0.3.0 → 0.3.1), and every one is called out in docs/changelog.md.
  • Where a change can be made backward-compatible, it is. A renamed or moved public symbol keeps a working alias that emits a DeprecationWarning for at least one minor cycle before removal; the warning names the replacement. A removed CLI flag is accepted-and-warned for the same window.
  • The TOML case format never breaks silently. An older [case].schema_version is either auto-migrated on load or rejected with a clear, actionable error — it is never mis-parsed against the current schema (ADR-0007). A field's meaning does not change under a fixed schema_version. (An unversioned deck is treated as version 1.)

How a deprecation is surfaced — all three, so it is impossible to miss:

  1. a runtime DeprecationWarning at the call/parse site, naming the replacement;
  2. a Deprecated entry in docs/changelog.md for the release that introduces it, and again for the release that removes it;
  3. a note in the affected symbol's / field's documentation.

Post-1.0 (intended). Once 1.0 ships, aeolus1d follows SemVer strictly: breaking changes to the supported surface only on a major bump, and a deprecated name warns for at least one minor release before removal in the next major. The experimental-seam carve-out persists — those never enter the compatibility contract until they are explicitly promoted (documented + regression-covered).

Fitness for use

aeolus1d is provided for research and engineering exploration. It is not qualified for safety-critical, certification, regulatory, or homologation use, and carries no warranty (it is MIT-licensed — see the LICENSE file at the repository root). You are responsible for independently verifying any result you depend on. If you find a case where a documented-validated workflow disagrees with experiment, please open an issue — that is exactly the feedback the validation dossier exists to absorb.