Skip to content

Explicit network step

The default network driver (advance_network): an SSP-RK2 or SSP-RK3 step of every pipe, junction volume and closure state at the acoustic CFL step, with the operator-split sources applied once after the last stage. strang=True routes the same entry point to the Strang-split reacting integrator, the third chart. The contracts of every function below are in their docstrings (the standard is Function contracts and flow charts).

One step

What enters: the built pipes, junctions and far boundary conditions, t_end, the CFL and the optional Δt controls (dt_fixed, dt_hrr_fraction, dt_heat_fraction). The junctions' neck pipes join the pipe list once, at the start (network_pipes).

flowchart TD
    IN["pipes, junctions, far BCs, t_end, cfl, rk, dt knobs"] --> S{"strang?"}
    S -- yes --> STR["_advance_network_strang_impl (third chart)"]
    S -- no --> B["_ExplicitNetwork.build: necks added, packed first-order pipes, SoA rows, orifice cylinders tagged"]
    B --> RK{"_check_network_rk"}
    RK -- "not ssp_rk2 / ssp_rk3" --> X1["ValueError"]
    RK --> T{"t < t_end?"}
    T -- "no" --> OUT["t reached; every state advanced in place; clock.t = t"]
    T -- "budget spent short of t_end" --> X2["RuntimeError: max_steps exhausted"]
    T -- yes --> LC{"poll_live_control"}
    LC -- stop --> OUT
    LC --> G["refresh_network_gamma, then fill_ghosts (far BCs and ports)"]
    G --> DT{"step_dt: packed / per-pipe CFL step, or dt_fixed; HRR cap; wall-heat cap"}
    DT -- "NaN or 1e300-sentinel step on any pipe" --> X3["_raise_nonfinite_network_dt: RuntimeError naming the pipe (cell diagnostic for a lone rho <= 0 or p <= 0)"]
    DT -- "dt <= 0 or non-finite, before the t_end clamp" --> X4["assert_advancing_dt: NonAdvancingStepError"]
    DT --> SN["snapshot: start-of-step copies"]
    SN --> E1["euler_stage: stage_rates at weight 1, forward Euler of every state; soot, housing and port u_n advance here only"]
    E1 --> K{"rk"}
    K -- ssp_rk2 --> S2["ssp_stage rk2_stage2 at t + dt"]
    K -- ssp_rk3 --> S3["ssp_stage rk3_stage2 at t + dt, then rk3_stage3 at t + dt/2 (weight 4)"]
    S2 --> P["post_rk: regen species fold, CSF, urea spray at t, fuel step"]
    S3 --> P
    P --> TH["advance_thermal_couplings: coolant and walls"]
    TH --> H["hrr_next_dt_limit (when dt_hrr_fraction > 0); t += dt"]
    H --> T

One stage's rates

stage_rates is the same for every stage. The order matters: the species flux is recovered from the mass row of the residual, so every source that lands on that row (the DPF trap, the valve) comes after it.

flowchart TD
    R0["residuals"] --> SOA["_soa_into: compiled MUSCL rows"]
    R0 --> PK["_packed_into: packed first-order rows"]
    R0 --> PP["_pipe_residual: the rest, closure cached per pipe"]
    SOA -- "kernel fault" --> XR["the pipe re-run alone: _bad_residual_state_error names it; no single pipe -> RuntimeError on the buffers"]
    PK -- "kernel fault" --> XR
    PP -- "ZeroDivisionError / FloatingPointError" --> XR
    SOA --> CL["catalyst, DPF cake, regen and substrate-wall sources onto R[:, 1:3]"]
    PK --> CL
    PP --> CL
    CL --> SP["_species_rate: ghost pads (feeds), telescoped face flux anchored on _left_boundary_mass_flux, advection, reaction, DPF trap (debits R[:, 0])"]
    SP --> J["_accumulate_at_weight: junction volume rates at the stage weight"]
    J --> V["_valve_sources_into: cylinder valve mass, momentum, energy and species off the runner cell"]
    V --> OUT["_StageRates"]

The Strang-split step

advance_network(..., strang=True): T(Δt/2) ∘ C(Δt) ∘ T(Δt/2), where T is the plain loop above with the reaction and the doser off, sub-cycled at the acoustic step, and C the stiff per-cell chemistry. With chem_every_n > 1 it is a Lie super-cycle: T(Δt) every step, C over the accumulated window every n-th step.

flowchart TD
    IN["as above, plus chem_substeps, chem_every_n, chem_interval"] --> V{"chem_every_n >= 1 and chem_substeps >= 1?"}
    V -- no --> X1["ValueError"]
    V --> T{"t < t_end?"}
    T -- "budget spent short of t_end" --> X2["RuntimeError"]
    T -- no --> OUT["t reached; clock.t = t"]
    T -- yes --> C{"_strang_step_cfl: live control and CFL ramp"}
    C -- "stop" --> FL["pending super-cycle chemistry reacted, then stop"]
    FL --> OUT
    C --> DT{"fill_network_ghosts, then _strang_step_dt"}
    DT -- "non-finite pipe step, dt <= 0" --> X3["RuntimeError / NonAdvancingStepError"]
    DT --> TR["_StrangTransport with the run's clock: each sub-call's BCs see the run time"]
    TR --> N{"chem_every_n == 1?"}
    N -- yes --> SYM["_strang_symmetric_step: T(dt/2), _strang_chem_burst(dt), T(dt/2)"]
    N -- no --> SC["_strang_supercycle_step: T(dt); C over the window on the n-th step, the last step, or the interval gate"]
    SYM --> END["_strang_end_of_step: coolant and walls once, urea at the true t"]
    SC --> END
    END --> H["hrr_next_dt_limit; t += dt"]
    H --> T

Functions

  • src/aeolus1d/solver/network_timeloop.py::advance_network
  • src/aeolus1d/solver/solver_ops.py::network_pipes
  • src/aeolus1d/solver/network_timeloop.py::_ExplicitNetwork.build
  • src/aeolus1d/solver/network_timeloop.py::_soa_residual_rows
  • src/aeolus1d/solver/network_timeloop.py::_check_network_rk
  • src/aeolus1d/solver/solver_ops.py::poll_live_control@3
  • src/aeolus1d/solver/solver_ops.py::refresh_network_gamma
  • src/aeolus1d/solver/network_timeloop.py::_ExplicitNetwork.fill_ghosts
  • src/aeolus1d/bc/base.py::fill_network_ghosts
  • src/aeolus1d/solver/network_timeloop.py::_ExplicitNetwork.step_dt
  • src/aeolus1d/solver/timeloop.py::pipe_min_dt_dispatch
  • src/aeolus1d/solver/network_timeloop.py::_heat_transfer_dt_limit
  • src/aeolus1d/solver/network_timeloop.py::_raise_nonfinite_network_dt
  • src/aeolus1d/solver/solver_ops.py::assert_advancing_dt
  • src/aeolus1d/solver/network_timeloop.py::_ExplicitNetwork.snapshot
  • src/aeolus1d/solver/network_timeloop.py::_snapshot_rhoY
  • src/aeolus1d/solver/network_timeloop.py::_ExplicitNetwork.euler_stage
  • src/aeolus1d/solver/network_timeloop.py::_ExplicitNetwork.ssp_stage
  • src/aeolus1d/solver/network_timeloop.py::_ssp_mix
  • src/aeolus1d/solver/network_timeloop.py::_combine_port_u_n
  • src/aeolus1d/solver/network_timeloop.py::_advance_port_u_n
  • src/aeolus1d/solver/network_timeloop.py::advance_shaft_housing
  • src/aeolus1d/solver/network_timeloop.py::_clip_T_cake_max
  • src/aeolus1d/solver/network_timeloop.py::_ExplicitNetwork.post_rk
  • src/aeolus1d/closures/filter.py::regen_species_fold
  • src/aeolus1d/closures/filter.py::apply_csf_transient
  • src/aeolus1d/solver/solver_ops.py::apply_urea_spray_to_pipe
  • src/aeolus1d/solver/solver_ops.py::advance_thermal_couplings
  • src/aeolus1d/solver/solver_ops.py::hrr_next_dt_limit
  • src/aeolus1d/solver/network_timeloop.py::_ExplicitNetwork.stage_rates
  • src/aeolus1d/solver/network_timeloop.py::_ExplicitNetwork.residuals
  • src/aeolus1d/solver/network_timeloop.py::_ExplicitNetwork._soa_into
  • src/aeolus1d/solver/network_timeloop.py::_ExplicitNetwork._packed_into
  • src/aeolus1d/solver/network_timeloop.py::_pipe_residual
  • src/aeolus1d/solver/network_timeloop.py::_build_pipe_residual_fn
  • src/aeolus1d/solver/timeloop.py::make_pipe_residual_fn
  • src/aeolus1d/solver/network_timeloop.py::_bad_residual_state_error
  • src/aeolus1d/closures/catalyst.py::catalyst_rates_into
  • src/aeolus1d/closures/filter.py::cake_into
  • src/aeolus1d/closures/filter.py::regen_into
  • src/aeolus1d/closures/filter.py::substrate_wall_into
  • src/aeolus1d/solver/network_timeloop.py::_species_rate
  • src/aeolus1d/solver/network_timeloop.py::_species_ghost_pad_primary
  • src/aeolus1d/solver/network_timeloop.py::_species_ghost_pad_extras
  • src/aeolus1d/solver/network_timeloop.py::_inflow_ghost_extras
  • src/aeolus1d/solver/network_timeloop.py::_species_flux_needs_telescoping
  • src/aeolus1d/solver/network_timeloop.py::_telescoped_face_mass_flux
  • src/aeolus1d/solver/network_timeloop.py::_telescoped_face_mass_flux_quasi1d
  • src/aeolus1d/solver/network_timeloop.py::_left_boundary_mass_flux
  • src/aeolus1d/solver/network_timeloop.py::_species_advection_rates
  • src/aeolus1d/solver/network_timeloop.py::_overlay_species_reaction
  • src/aeolus1d/solver/network_timeloop.py::_species_reaction_temperature
  • src/aeolus1d/solver/network_timeloop.py::_overlay_filter_trap
  • src/aeolus1d/solver/network_timeloop.py::_accumulate_at_weight
  • src/aeolus1d/solver/network_timeloop.py::_valve_sources_into
  • src/aeolus1d/solver/network_timeloop.py::_valve_species_into
  • src/aeolus1d/solver/network_timeloop.py::_advance_network_strang_impl
  • src/aeolus1d/solver/network_timeloop.py::_strang_step_cfl
  • src/aeolus1d/solver/network_timeloop.py::_strang_step_dt
  • src/aeolus1d/solver/solver_ops.py::HRRSnapshot.take
  • src/aeolus1d/solver/network_timeloop.py::_StrangTransport.advance
  • src/aeolus1d/solver/network_timeloop.py::_OffsetClock.t@2
  • src/aeolus1d/solver/network_timeloop.py::_transport_max_steps
  • src/aeolus1d/solver/network_timeloop.py::_strang_symmetric_step
  • src/aeolus1d/solver/network_timeloop.py::_strang_supercycle_step
  • src/aeolus1d/solver/network_timeloop.py::_strang_chem_burst
  • src/aeolus1d/solver/chemistry_ops.py::chemistry_step_on_pipes
  • src/aeolus1d/solver/network_timeloop.py::_strang_end_of_step