Skip to content

Quasi-steady drivers

The low-Mach driver for aftertreatment networks (solver = "quasi_steady"). Acoustics are dropped: each macro step solves a steady flow field, then advances the slow states (gas energy, species, soot, cake, catalyst and wall temperatures, surface chemistry) over a step set by the heat-release rate, the advective CFL and the boundary schedules. The contracts of every function below are in their docstrings (the standard is Function contracts and flow charts).

One run

What enters: the built pipes, junctions and far boundary conditions (each far BC carries a qs_kind tag from its factory), t_end, and the QSTransientConfig the case runner assembles from [case].

flowchart TD
    IN["pipes, junctions, tagged far BCs, t_end, QSTransientConfig"] --> RJ{"_refuse_unsupported_junctions"}
    RJ -- "cylinder, EHR junction, neck port" --> X1["ValueError naming the component"]
    RJ --> WL["_warn_if_qs_loop_without_loop_flow: warns on a looped network without loop-flow"]
    WL --> ST{"_QSTransientRun.start"}
    ST -- "real-gas pipe (_qs_refuse_real_gas)" --> X2["NotImplementedError naming the pipe"]
    ST -- "open pipe end, far BC without a QS tag (_resolve_pipes)" --> X3["NotImplementedError"]
    ST --> T{"t < t_end?"}
    T -- "no, or a live-control stop" --> OUT["final time; pipes, junction volumes, walls and coolant advanced in place"]
    T -- "max_steps spent" --> X4["RuntimeError"]
    T -- yes --> NET["advance_network_quasi_steady: steady flow at the current T and soot"]
    NET --> DT{"_qs_macro_dt: min of HRR cap, dt_max, advective CFL; clamp to t_end and the next BC knot"}
    DT -- "non-finite velocity, non-advancing step" --> X5["RuntimeError naming the pipe"]
    DT --> SLOW["_qs_advance_slow_states: ghosts, urea spray, then _qs_advance_pipe per pipe"]
    SLOW --> CHEM{"_qs_chemistry_substep: fire this step?"}
    CHEM -- "every n-th step, last step, interval, or a settle gate open" --> SURF["_qs_surface_chem_step over the accumulated dt, gas T frozen"]
    SURF -- "coverage over-drawn outside [0, 1]" --> X6["RuntimeError naming the coverage"]
    SURF --> HRR["lagged HRR cap for the next step; thermal couplings; t += dt"]
    CHEM -- "not yet" --> HRR
    HRR --> T

The steady flow field

advance_network_quasi_steady couples one SIMPLE solve per pipe through the junction pressures, then writes the result back.

flowchart TD
    A["pipes, junctions, far BCs, t_now, cached pqs and Picard state"] --> R["_refresh_pipes: schedules, T, p0, cake drag, gamma, Sutherland mu"]
    R --> ANC{"junctions but no far-pressure anchor?"}
    ANC -- yes --> Y1["NotImplementedError: the plenum pressures float"]
    ANC -- no --> S{"junction solver"}
    S -- "no junction" --> E["_qs_evaluate: each pipe once"]
    S -- "qs_junction_loop_flow" --> LF["_qs_solve_loop_flow -> loop_flow_core"]
    S -- "qs_junction_coupled_newton" --> CN["_qs_solve_coupled_newton"]
    S -- default --> SC["_qs_solve_secant"]
    LF -- "distinct references, mass-flow outlet" --> Y2["NotImplementedError"]
    LF -- "continuity inconsistent, junction unreachable, not converged" --> Y3["RuntimeError"]
    CN -- "not converged in picard_max_iter" --> Y3
    SC -- "not converged in picard_max_iter" --> Y3
    E --> P["_solve_one_pipe"]
    CN --> P
    SC --> P
    P -- "mass-flow inlet" --> M["solve_quasi_steady_single_pipe"]
    P -- "pressure at both ends" --> PD["solve_quasi_steady_pressure_driven: root on mdot, zero flow when p_in <= p_out"]
    P -- "mass-flow BC on a RIGHT end (_p_out_of)" --> Y4["ValueError"]
    M -- "empty pipe, K_extra or A_face of the wrong length, negative end face" --> Y5["ValueError"]
    LF --> WB{"_write_back"}
    M --> WB
    PD --> WB
    WB -- "a pipe with a reverse steady flow" --> Y6["NotImplementedError naming the pipe"]
    WB --> OUT["pipe U rebuilt at the frozen T, _qs_mdot_face published, junction volumes set to (p, mixed T)"]

Two things this chart does not show, both recorded in the contracts: the SIMPLE and root-finder converged flags are not read by _solve_one_pipe (the junction solver's own convergence test is the only gate), and a real-gas pipe reaches the flow solve only through a direct call, since the transient driver refuses it first.

Functions

  • src/aeolus1d/solver/quasi_steady_network.py::advance_network_quasi_steady_transient
  • src/aeolus1d/solver/quasi_steady_network.py::_refuse_unsupported_junctions
  • src/aeolus1d/solver/quasi_steady_network.py::_warn_if_qs_loop_without_loop_flow
  • src/aeolus1d/solver/quasi_steady_network.py::_QSTransientRun.start
  • src/aeolus1d/solver/quasi_steady_network.py::_qs_refuse_real_gas
  • src/aeolus1d/solver/quasi_steady_network.py::_resolve_pipes
  • src/aeolus1d/solver/quasi_steady_network.py::_qs_macro_dt
  • src/aeolus1d/solver/quasi_steady_network.py::_qs_advance_slow_states
  • src/aeolus1d/solver/quasi_steady_network.py::_qs_advance_pipe
  • src/aeolus1d/solver/quasi_steady_network.py::_qs_chemistry_substep
  • src/aeolus1d/solver/quasi_steady_network.py::_qs_surface_chem_step
  • src/aeolus1d/solver/quasi_steady_network.py::advance_network_quasi_steady
  • src/aeolus1d/solver/quasi_steady_network.py::_refresh_pipes
  • src/aeolus1d/solver/quasi_steady_network.py::_qs_evaluate
  • src/aeolus1d/solver/quasi_steady_network.py::_qs_solve_loop_flow
  • src/aeolus1d/solver/quasi_steady_network.py::_qs_solve_coupled_newton
  • src/aeolus1d/solver/quasi_steady_network.py::_qs_solve_secant
  • src/aeolus1d/solver/quasi_steady_network.py::_solve_one_pipe
  • src/aeolus1d/solver/quasi_steady_network.py::_p_out_of
  • src/aeolus1d/solver/quasi_steady_network.py::_write_back
  • src/aeolus1d/solver/loop_flow_steady.py::loop_flow_core
  • src/aeolus1d/solver/quasi_steady.py::solve_quasi_steady_single_pipe
  • src/aeolus1d/solver/quasi_steady.py::solve_quasi_steady_pressure_driven