OpenSWMM Engine  6.0.0-alpha.4
Data-oriented, plugin-extensible SWMM Engine (6.0.0-alpha.4)
Loading...
Searching...
No Matches
openswmm::transport Namespace Reference

Namespaces

namespace  fvkernels
 
namespace  heat
 

Classes

class  ArdEngine
 
struct  Cell
 
struct  ClassEnables
 Allocation-free class enables for one domain — what the engines size from. n_msx is the reactions component's species count when MSX is on for the domain; msx_has_wall tells ARD / 2D that the component declares WALL species (their own fallback / warning). More...
 
struct  Donors
 
struct  Matrix
 
class  ReactionIntegrator
 
struct  RowLayout
 
struct  RxEvalEnv
 Evaluation environment: pre-gathered pointers, no lookups. More...
 
struct  RxExprSpan
 Compiled expression = span into the shared pool. len 0 ⇒ no expression. More...
 
struct  RxStepReport
 
struct  RxSymbols
 
struct  RxToken
 
class  RxWorkspace
 
class  Tokenizer
 Stateless multi-delimiter tokenizer for SWMM input lines. More...
 

Enumerations

enum class  Domain : int {
  RUNOFF = 0 ,
  GROUNDWATER = 1 ,
  NETWORK_1D = 2 ,
  SURFACE_2D = 3 ,
  COUNT = 4
}
 Rows of the matrix. Values are the C API's SWMM_TRANSPORT_DOMAIN_* codes. More...
 
enum class  SpeciesClass : int {
  POLLUTANTS = 0 ,
  MSX = 1 ,
  AGE = 2 ,
  TEMPERATURE = 3 ,
  COUNT = 4
}
 Columns of the matrix. Values are the C API's SWMM_TRANSPORT_CLASS_* codes. More...
 
enum class  CellState : int {
  ENABLED = 0 ,
  DISABLED_BY_USER = 1 ,
  UNAVAILABLE = 2
}
 One cell's state. Values are the C API's SWMM_TRANSPORT_* state codes. More...
 
enum class  RxHydVar
 

Functions

void warnIfFvDispersionKeyIgnored (SimulationContext &ctx)
 
bool ardBoundariesNeedExternalVolumes (const SimulationContext &ctx)
 
void resolveArdTransportRows (SimulationContext &ctx, std::vector< std::string > &errors)
 
void registerArdComponent ()
 
void registerHeatComponent ()
 
void routeLegacyHeat (SimulationContext &ctx, double dt)
 
double lidLayerHeatCapacity (const SimulationContext &ctx, const lid::LIDGroupSoA &g, std::size_t unit, int layer) noexcept
 Heat capacity of one layer, J/m²/K over the layer's footprint.
 
void initLidLayerTemperature (SimulationContext &ctx, const lid::LIDSolver &solver)
 
void setLidInflowTemperature (SimulationContext &ctx, int type_index, int unit, int subcatch, double rain_rate, double q_imperv, double q_perv, double q_runon, double lid_area)
 
void routeLidLayerTemperature (SimulationContext &ctx, const lid::LIDSolver &solver, double dt)
 
void addRunonTemperature (SimulationContext &ctx, int donor_sc, int receiver_sc, double q)
 Accumulate run-on temperature from donor_sc onto receiver_sc.
 
void addRunonTemperatureAt (SimulationContext &ctx, int receiver_sc, double q, double temp_c)
 Accumulate run-on at an explicitly supplied temperature.
 
void routeSubcatchmentTemperature (SimulationContext &ctx, const runoff::RunoffSoA &soa, double dt)
 Advance ponded subarea temperatures one runoff step.
 
Donors donorsFor (lid::LIDType t, bool has_soil)
 
void layerVolumes (const lid::LIDGroupSoA &g, std::size_t ui, double(&v)[kNL])
 
std::vector< int > buildOffsets (const lid::LIDSolver &solver)
 Flat index of each per-type group's first unit; back() == n_units.
 
void routeLegacyMsx (SimulationContext &ctx, double dt)
 
bool ardReactionsActive (const SimulationContext &ctx)
 
bool ardHasWallSpecies (const SimulationContext &ctx)
 
void reactArdStage (SimulationContext &ctx, double dt, double *cell_phi, const double *cell_a, const double *cell_dx, int n_cells, double *node_mass, const double *node_vol, int n_nodes, int n_pollut, int ns_total, double min_store_vol, int temp_row=-1)
 The E4 reaction stage over one routing step.
 
std::string compileReactionExpression (const std::string &src, const RxSymbols &symbols, std::vector< RxToken > &pool, RxExprSpan &out, int &error_col)
 Compile src into the shared pool.
 
double evalReactionExpression (const std::vector< RxToken > &pool, const RxExprSpan &span, const RxEvalEnv &env) noexcept
 Tier-1 evaluation: one pass, fixed-depth stack, allocation-free.
 
int reactionFunctionCount ()
 Number of built-in functions.
 
bool reactionFunctionInfo (int idx, const char **name, int *arity)
 Name + arity of function idx; false when out of range.
 
int reactionHydVarCount ()
 Number of built-in hydraulic variables.
 
bool reactionHydVarInfo (int idx, const char **name, const char **description)
 Name + one-line description of hydraulic variable idx.
 
void ensureMsxState (SimulationContext &ctx)
 
void reactSpeciesBlock (SimulationContext &ctx, bool tank, double dt, double *species_block, const double *pollut, double hrt_seconds, double temp_c)
 
bool legacyReactionsActive (const SimulationContext &ctx)
 
void reactLegacyNodes (SimulationContext &ctx, double dt)
 
void reactLegacyLinks (SimulationContext &ctx, double dt)
 
void warnIfLegacyBindingBypassed (SimulationContext &ctx)
 
const std::vector< std::string > & reactionSectionTags ()
 
void applyReactionSections (SimulationContext &ctx, const components::ComponentConfigSections &config, std::vector< std::string > &errors)
 
void applyEmbeddedReactionSections (SimulationContext &ctx, bool external_component_registered, std::vector< std::string > &errors)
 
void registerReactionsComponent ()
 
bool recompileReactionSystem (SimulationContext &ctx, std::vector< std::string > &errors)
 
std::string serializeReactionSystem (const SimulationContext &ctx)
 
std::string serializeWaterAgeConfig (const SimulationContext &ctx)
 
void registerWaterAgeComponent ()
 
void routeLegacyAge (SimulationContext &ctx, double dt)
 
void initLidLayerAge (SimulationContext &ctx, const lid::LIDSolver &solver)
 
void setLidInflowAge (SimulationContext &ctx, int type_index, int unit, int subcatch, double rain_rate, double q_imperv, double q_perv, double q_runon, double lid_area)
 
void routeLidLayerAge (SimulationContext &ctx, const lid::LIDSolver &solver, double dt)
 Advance the per-layer ages one runoff step, after LIDSolver::execute.
 
void addRunonAge (SimulationContext &ctx, int donor_sc, int receiver_sc, double q)
 
void routeSubcatchmentAge (SimulationContext &ctx, const runoff::RunoffSoA &soa, double dt)
 
double arrivingPrecipRate (const SimulationContext &ctx, std::size_t ui, int subarea) noexcept
 Net precipitation actually reaching one subarea this step [ft/s].
 
double arrivingMeltFraction (const SimulationContext &ctx, std::size_t ui, int subarea) noexcept
 Fraction of the arriving water that is MELTWATER, in [0, 1].
 
double arrivingPrecipTemperature (const SimulationContext &ctx, std::size_t ui, int subarea) noexcept
 Temperature of the water arriving at one subarea [°C] (S2).
 
double arrivingPrecipAge (const SimulationContext &ctx, std::size_t ui, int subarea) noexcept
 Water age of the water arriving at one subarea [seconds] (S2b).
 
double initialAgeSecondsFor (const SimulationContext &ctx, bool is_link, int idx, double fallback)
 Per-element initial age (SECONDS) for one node/link, falling back to fallback (normally the INITIAL_STATE global) when no [INITIAL_QUALITY] row targets the element. Later rows win.
 
double initialTempFor (const SimulationContext &ctx, bool is_link, int idx, double fallback)
 Per-element initial temperature (degC) for one node/link, falling back to fallback (normally the INITIAL_STATE global).
 
void applyInitialAgeOverrides (SimulationContext &ctx)
 Apply WATER_AGE rows onto water_age_state.node_age/link_age (hours -> seconds). No-op when a hotstart state is loaded (D-IQ7) or the arrays are unsized.
 
void applyInitialTempOverrides (SimulationContext &ctx)
 Apply TEMPERATURE rows onto heat_state.node_temp/link_temp (degC). No-op on unsized arrays.
 
bool initialQualityHasMsxRow (const SimulationContext &ctx, bool is_link, int elem_idx, int species)
 
std::vector< std::string > mirrorInitialQualityMsxRows (SimulationContext &ctx)
 
ClassEnables network1DEnables (const SimulationContext &ctx) noexcept
 1D network enables: LEGACY / ARD / LARD all size from this.
 
ClassEnables surface2DEnables (const SimulationContext &ctx) noexcept
 
RowLayout canonicalRows (const SimulationContext &ctx, const ClassEnables &e)
 
Matrix resolve (const SimulationContext &ctx)
 Build the full matrix (states + reasons). Pure: never writes warnings.
 
const char * domainName (Domain d) noexcept
 
const char * speciesClassName (SpeciesClass c) noexcept
 
const char * cellStateName (CellState s) noexcept
 
std::string formatReportBlock (const Matrix &m)
 

Variables

constexpr int kNL = LidLayerSpeciesState::kLayerCount
 
constexpr int kExternal = -1
 -1 means "external to the unit"; -2 means "this type has no such layer".
 
constexpr int kAbsent = -2
 
constexpr int kSurf = static_cast<int>(LidLayer::SURFACE)
 
constexpr int kPave = static_cast<int>(LidLayer::PAVEMENT)
 
constexpr int kSoil = static_cast<int>(LidLayer::SOIL)
 
constexpr int kStor = static_cast<int>(LidLayer::STORAGE)
 
constexpr int kSubIMPERV0 = 0
 
constexpr int kSubIMPERV1 = 1
 
constexpr int kSubPERV = 2
 
constexpr double kMeltwaterTempC = 0.0
 
constexpr int kRxMaxStackDepth
 

Enumeration Type Documentation

◆ CellState

enum class openswmm::transport::CellState : int
strong

One cell's state. Values are the C API's SWMM_TRANSPORT_* state codes.

Enumerator
ENABLED 

rows are carried in this domain

DISABLED_BY_USER 

a key turned it off — reason names the key

UNAVAILABLE 

nothing to carry, or the solver lacks it — reason says which

◆ Domain

enum class openswmm::transport::Domain : int
strong

Rows of the matrix. Values are the C API's SWMM_TRANSPORT_DOMAIN_* codes.

Enumerator
RUNOFF 

subcatchment runoff + LID

GROUNDWATER 

legacy per-subcatchment aquifers

NETWORK_1D 

LEGACY / EULERIAN_ARD / LAGRANGIAN routing.

SURFACE_2D 

the 2D mesh

COUNT 

◆ RxHydVar

enum class openswmm::RxHydVar : int
strong

Hydraulic variables available in reaction expressions (reactions plan §2; engines populate the evaluation environment at R6). Order is the ABI of the evaluator's hydvar array.

◆ SpeciesClass

enum class openswmm::transport::SpeciesClass : int
strong

Columns of the matrix. Values are the C API's SWMM_TRANSPORT_CLASS_* codes.

Enumerator
POLLUTANTS 

[POLLUTANTS]

MSX 

reactions component species

AGE 

WATER_AGE

TEMPERATURE 

TEMPERATURE

COUNT 

Function Documentation

◆ addRunonAge()

void openswmm::transport::addRunonAge ( SimulationContext & ctx,
int donor_sc,
int receiver_sc,
double q )

Accumulate run-on age-volume from a donor subcatchment to its receiver. Called from the run-on assembly beside the flow it mirrors.

◆ addRunonTemperature()

void openswmm::transport::addRunonTemperature ( SimulationContext & ctx,
int donor_sc,
int receiver_sc,
double q )

Accumulate run-on temperature from donor_sc onto receiver_sc.

The RATE convention every loader uses: donors add q · T, the consumer divides by the total rate. Mirrors addRunonAge, and must be called at every contributor to runon_inflow — the subcatchment cascade, the LID underdrain return, and the outfall return. A3 filled this numerator from one of the three and divided by all three, which produced arriving water younger than anything entering the model. The temperature analogue of that defect is quieter and worse: a missing contributor pulls the arriving temperature toward 0 °C, which is a plausible number.

Parameters
qVolumetric flow rate, ft³/s.
Here is the call graph for this function:

◆ addRunonTemperatureAt()

void openswmm::transport::addRunonTemperatureAt ( SimulationContext & ctx,
int receiver_sc,
double q,
double temp_c )

Accumulate run-on at an explicitly supplied temperature.

For contributors whose donor is not another subcatchment: the outfall return (which carries heat_state.node_temp) and, from H5b, the LID underdrain. Adds to the numerator and the rate together — the pairing is why this is one function and not two array writes at each call site.

Parameters
qVolumetric flow rate, ft³/s.
temp_cTemperature of that flow, °C.
Here is the caller graph for this function:

◆ applyEmbeddedReactionSections()

void openswmm::transport::applyEmbeddedReactionSections ( SimulationContext & ctx,
bool external_component_registered,
std::vector< std::string > & errors )

Embedded fallback (called from SWMMEngine::open after component resolution): if [REACTION_*] sections were found in the legacy .inp, apply them with a style warning — unless an external reactions component was registered, in which case the external file wins wholesale and the embedded sections are reported ignored.

Here is the call graph for this function:

◆ applyInitialAgeOverrides()

void openswmm::transport::applyInitialAgeOverrides ( SimulationContext & ctx)
inline

Apply WATER_AGE rows onto water_age_state.node_age/link_age (hours -> seconds). No-op when a hotstart state is loaded (D-IQ7) or the arrays are unsized.

Here is the caller graph for this function:

◆ applyInitialTempOverrides()

void openswmm::transport::applyInitialTempOverrides ( SimulationContext & ctx)
inline

Apply TEMPERATURE rows onto heat_state.node_temp/link_temp (degC). No-op on unsized arrays.

Here is the caller graph for this function:

◆ applyReactionSections()

void openswmm::transport::applyReactionSections ( SimulationContext & ctx,
const components::ComponentConfigSections & config,
std::vector< std::string > & errors )

Parse + validate a set of reaction sections into ctx.reactions and the species registry. Shared by the component apply hook (external file) and the embedded fallback. Diagnostics are appended to errors (fatal).

Here is the call graph for this function:
Here is the caller graph for this function:

◆ ardBoundariesNeedExternalVolumes()

bool openswmm::transport::ardBoundariesNeedExternalVolumes ( const SimulationContext & ctx)

True when the external-load loaders must run even though the model has no pollutants: a [TRANSPORT_BOUNDARIES] row injects qual_vol_in * concentration, and qual_vol_in is accumulated by those loaders. Without this an MSX-ONLY model — the nh2cl shape, and the one E5a exists to enable — assembles no inflow volume and its boundary delivers exactly nothing.

◆ ardHasWallSpecies()

bool openswmm::transport::ardHasWallSpecies ( const SimulationContext & ctx)

True when any declared MSX species is WALL — unsupported under the ARD engine (no transport semantics for attached species yet); the engine falls back to LEGACY, whose R4 binding runs them element-locally.

◆ ardReactionsActive()

bool openswmm::transport::ardReactionsActive ( const SimulationContext & ctx)

True when the reactions component is active (configured + compiled) — the ARD engine's condition for carrying MSX species on the mesh.

Here is the caller graph for this function:

◆ arrivingMeltFraction()

double openswmm::transport::arrivingMeltFraction ( const SimulationContext & ctx,
std::size_t ui,
int subarea )
noexcept

Fraction of the arriving water that is MELTWATER, in [0, 1].

Under a pack the arriving water is two different waters: meltwater, and rain that reached the ground through the snow-free fraction. arrivingPrecipRate returns their sum, and a sum cannot say what either is worth — this is the split.

Returns 0 wherever there is no pack, IGNORE_SNOWMELT is on, or nothing arrived, so a caller can blend unconditionally and a bare deck is bit-identical to its pre-S2 behaviour.

Here is the caller graph for this function:

◆ arrivingPrecipAge()

double openswmm::transport::arrivingPrecipAge ( const SimulationContext & ctx,
std::size_t ui,
int subarea )
noexcept

Water age of the water arriving at one subarea [seconds] (S2b).

The age analogue of arrivingPrecipTemperature, and it uses the same arrivingMeltFraction — not an equivalent expression, the same call. Under a pack the arriving water is meltwater carrying the pack's residence time plus rain that reached the ground through the snow-free fraction carrying the configured WaterAgeSource::RAINFALL age:

a = (1 − f)·a_rain + f·a_pack

If the two tracks ever computed f separately they could drift, and the drift would be invisible: both answers stay inside their brackets, and only a deck comparing arriving age against arriving temperature could see it. One call is what makes that unrepresentable.

Where a_pack comes from
snow_melt_age_*, published beside snow_melt_* under the identical area blend. It is NOT the age of the water still in the pack — a pack that empties this step publishes the age its water HAD, which the remaining-water age cannot express because there is none.
The no-pack answer
Returns the configured RAINFALL age wherever there is no pack, IGNORE_SNOWMELT is on, nothing arrived, or the pack published no melt — so a caller blends unconditionally and a bare deck is bit-identical to its pre-S2b behaviour.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ arrivingPrecipRate()

double openswmm::transport::arrivingPrecipRate ( const SimulationContext & ctx,
std::size_t ui,
int subarea )
noexcept

Net precipitation actually reaching one subarea this step [ft/s].

This is the mixing volume, and it is not always the rainfall.

     `ctx.subcatches.rainfall` is set once, at `Runoff.cpp:294-295`,
     to `rain + SNOWFALL`, and never updated. On a subcatchment with a
     snowpack the runoff solver does not use it: it uses
     `snow_net_imperv` for IMPERV0/IMPERV1 and `snow_net_perv` for
     PERV (`Runoff.cpp:548-552`), each built as
     `imelt + rainfall·(1 − asc)` — **melt, plus the rain falling on
     the snow-free fraction** (`SWMMEngine.cpp:1608-1616`).

     Reading the rainfall field on such a deck is wrong twice over:
     - **snowfall is counted as arriving liquid**, so water being
       stored in the pack is mixed into the surface as though it had
       landed, and
     - **snowmelt is not counted at all**, so the water that genuinely
       arrives is invisible to the mixing volume and the surface ages
       as if nothing arrived.

     The second of those is A3's own net-gain failure (lessons 64, 68)
     reappearing through a different field: the mixing volume is again
     not the arriving volume. They do not cancel — they are displaced
     by the whole residence time of the pack, which is precisely the
     quantity a snow-aware age model exists to measure.
The -1.0 sentinel is the whole guard
snow_net_* initialise to -1.0 (SubcatchData.hpp:642), and the solver's own test is >= 0.0 (Runoff.cpp:551-552). A subcatchment with a pack but no melt this step publishes 0.0, which is a real rate; only the negative sentinel means "no pack, use the gage". This function mirrors that test exactly rather than inventing its own.
IGNORE_SNOWMELT
Falls back to the gage value, matching Runoff.cpp:548's !ctx.options.ignore_snow_melt and legacy subcatch.c:784.
Parameters
subareaOne of kSubIMPERV0 / kSubIMPERV1 / kSubPERV.
Here is the caller graph for this function:

◆ arrivingPrecipTemperature()

double openswmm::transport::arrivingPrecipTemperature ( const SimulationContext & ctx,
std::size_t ui,
int subarea )
noexcept

Temperature of the water arriving at one subarea [°C] (S2).

Meltwater is at 0 °C essentially by definition — that is what melting means — so the arriving temperature is the melt fraction blended against the configured HeatSource::RAINFALL value:

T = (1 − f)·T_rain + f·0

On a winter deck this is not a small approximation to make: it is the difference between a stream fed by snowmelt and one fed by rain. Unlike the age half, it needs no pack state at all — the freezing point is not something the pack has to remember.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ buildOffsets()

std::vector< int > openswmm::transport::buildOffsets ( const lid::LIDSolver & solver)

Flat index of each per-type group's first unit; back() == n_units.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ canonicalRows()

RowLayout openswmm::transport::canonicalRows ( const SimulationContext & ctx,
const ClassEnables & e )
Here is the call graph for this function:
Here is the caller graph for this function:

◆ cellStateName()

const char * openswmm::transport::cellStateName ( CellState s)
noexcept

◆ compileReactionExpression()

std::string openswmm::transport::compileReactionExpression ( const std::string & src,
const RxSymbols & symbols,
std::vector< RxToken > & pool,
RxExprSpan & out,
int & error_col )

Compile src into the shared pool.

Parameters
error_col[out] 1-based column of the offending token on failure.
Returns
empty on success, else the diagnostic ("undefined identifier 'x'", "expression too deep", "term 'T' references later term", …).
Here is the caller graph for this function:

◆ domainName()

const char * openswmm::transport::domainName ( Domain d)
noexcept
Here is the caller graph for this function:

◆ donorsFor()

Donors openswmm::transport::donorsFor ( lid::LIDType t,
bool has_soil )
Here is the caller graph for this function:

◆ ensureMsxState()

void openswmm::transport::ensureMsxState ( SimulationContext & ctx)

Lazily size + seed the MSX element state; warn once about the R4b transport limitation when any RATE MSX species exists in either scope (LEGACY only — under LARD the species ride the segments as of L3).

L3: lazily size + seed the MSX element state (GLOBAL fill + [REACTION_QUALITY] overrides). Shared by the LEGACY dispatch and the LARD engine's init — one seeding spelling for both engines.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ evalReactionExpression()

double openswmm::transport::evalReactionExpression ( const std::vector< RxToken > & pool,
const RxExprSpan & span,
const RxEvalEnv & env )
noexcept

Tier-1 evaluation: one pass, fixed-depth stack, allocation-free.

Here is the caller graph for this function:

◆ formatReportBlock()

std::string openswmm::transport::formatReportBlock ( const Matrix & m)

The .rpt block: a fixed-width table, one line per domain, one column per class, each cell "on(n)" / "off:KEY" / "n/a:reason". Ends with '
'.

Here is the call graph for this function:

◆ initialAgeSecondsFor()

double openswmm::transport::initialAgeSecondsFor ( const SimulationContext & ctx,
bool is_link,
int idx,
double fallback )
inline

Per-element initial age (SECONDS) for one node/link, falling back to fallback (normally the INITIAL_STATE global) when no [INITIAL_QUALITY] row targets the element. Later rows win.

◆ initialQualityHasMsxRow()

bool openswmm::transport::initialQualityHasMsxRow ( const SimulationContext & ctx,
bool is_link,
int elem_idx,
int species )

True when ctx.initial_quality carries an MSX row for (is_link, elem, species) — the ReactionsWriter's "already written elsewhere" test.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ initialTempFor()

double openswmm::transport::initialTempFor ( const SimulationContext & ctx,
bool is_link,
int idx,
double fallback )
inline

Per-element initial temperature (degC) for one node/link, falling back to fallback (normally the INITIAL_STATE global).

Here is the caller graph for this function:

◆ initLidLayerAge()

void openswmm::transport::initLidLayerAge ( SimulationContext & ctx,
const lid::LIDSolver & solver )

Size the per-layer block against the model's LID units and seed the layers that already hold water at t = 0. Idempotent; safe to call once the LID manager has built its groups.

Here is the call graph for this function:

◆ initLidLayerTemperature()

void openswmm::transport::initLidLayerTemperature ( SimulationContext & ctx,
const lid::LIDSolver & solver )

Size the shared layer block if needed and seed the TEMPERATURE row from HeatSource::INITIAL_STATE. Independent of initLidLayerAge: either may run first, neither wipes the other (LidLayerSpeciesState::ensureSized).

Here is the call graph for this function:

◆ layerVolumes()

void openswmm::transport::layerVolumes ( const lid::LIDGroupSoA & g,
std::size_t ui,
double(&) v[kNL] )

Void-weighted water held in each layer, as a depth per unit area (ft). The four terms are exactly those LIDSolver::storedVolume() sums, so a layer's age is weighted by the same water the mass balance counts.

Void-weighted water held in each layer, as a depth per unit area (ft). The four terms are exactly those LIDSolver::storedVolume() sums.

Here is the caller graph for this function:

◆ legacyReactionsActive()

bool openswmm::transport::legacyReactionsActive ( const SimulationContext & ctx)

True when the reactions component is active (configured + compiled) — the QualitySolver's branch condition.

Here is the caller graph for this function:

◆ lidLayerHeatCapacity()

double openswmm::transport::lidLayerHeatCapacity ( const SimulationContext & ctx,
const lid::LIDGroupSoA & g,
std::size_t unit,
int layer )
noexcept

Heat capacity of one layer, J/m²/K over the layer's footprint.

Exported so a gate can sum a column's heat content and assert conservation WITHOUT restating the water/matrix mixture — a second copy of it in the test would make the gate agree with itself rather than with the code. Zero for an absent layer.

Here is the call graph for this function:

◆ mirrorInitialQualityMsxRows()

std::vector< std::string > openswmm::transport::mirrorInitialQualityMsxRows ( SimulationContext & ctx)

Mirror every MSX-kind [INITIAL_QUALITY] row into ctx.reactions.init_elem_* (upsert; a conflicting .rxn row is reported). Returns the diagnostics (empty on success).

Here is the call graph for this function:
Here is the caller graph for this function:

◆ network1DEnables()

ClassEnables openswmm::transport::network1DEnables ( const SimulationContext & ctx)
noexcept

1D network enables: LEGACY / ARD / LARD all size from this.

Here is the caller graph for this function:

◆ reactArdStage()

void openswmm::transport::reactArdStage ( SimulationContext & ctx,
double dt,
double * cell_phi,
const double * cell_a,
const double * cell_dx,
int n_cells,
double * node_mass,
const double * node_vol,
int n_nodes,
int n_pollut,
int ns_total,
double min_store_vol,
int temp_row = -1 )

The E4 reaction stage over one routing step.

Parameters
cell_phiARD cell state, species-major [s * n_cells + c]; rows 0..n_pollut-1 are pollutants, rows n_pollut.. are the MSX species in ReactionData order.
cell_aCell areas (read-only; dry cells still react — their carried concentration evolves like LEGACY's, and holds negligible mass).
cell_dxCell lengths (read-only; E5b — with cell_a they give the water volume each kdecay concentration change acts on, so the removed pollutant mass books into the qual_routing_reacted ledger row).
node_massNode-store species MASS [nd * ns_total + s].
node_volNode-store water volume [nd].
n_pollutPollutant row count (may be 0 — MSX-only model).
ns_totalTotal species rows (n_pollut + rx.n_species() when the component is active, else n_pollut).
min_store_volBelow this volume a store has no meaningful concentration: MSX tank integration is skipped there (mass-form kdecay still applies).
Here is the call graph for this function:
Here is the caller graph for this function:

◆ reactionFunctionCount()

int openswmm::transport::reactionFunctionCount ( )

Number of built-in functions.

Here is the caller graph for this function:

◆ reactionFunctionInfo()

bool openswmm::transport::reactionFunctionInfo ( int idx,
const char ** name,
int * arity )

Name + arity of function idx; false when out of range.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ reactionHydVarCount()

int openswmm::transport::reactionHydVarCount ( )

Number of built-in hydraulic variables.

Here is the caller graph for this function:

◆ reactionHydVarInfo()

bool openswmm::transport::reactionHydVarInfo ( int idx,
const char ** name,
const char ** description )

Name + one-line description of hydraulic variable idx.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ reactionSectionTags()

const std::vector< std::string > & openswmm::transport::reactionSectionTags ( )

The 12 [REACTION_*] tags (upper-case, no brackets) — shared by the embedded-fallback handlers and the config parser.

Here is the caller graph for this function:

◆ reactLegacyLinks()

void openswmm::transport::reactLegacyLinks ( SimulationContext & ctx,
double dt )

Link-side step: exact-exponential pollutant decay + MSX pipe-scope integration. Runs AFTER updateLinkQuality() (whose internal linear decay is zeroed when reactions are active), so the reacted concentrations are not overwritten by the mixing pass.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ reactLegacyNodes()

void openswmm::transport::reactLegacyNodes ( SimulationContext & ctx,
double dt )

Node-side step: exact-exponential pollutant decay + MSX tank-scope integration. Runs where applyDecay() ran (before updateLinkQuality).

Here is the call graph for this function:
Here is the caller graph for this function:

◆ reactSpeciesBlock()

void openswmm::transport::reactSpeciesBlock ( SimulationContext & ctx,
bool tank,
double dt,
double * species_block,
const double * pollut,
double hrt_seconds,
double temp_c = std::numeric_limits< double >::quiet_NaN() )

L3: integrate ONE species block in place over dt through the shared integrator. pollut may be null (no pollutant context); tank selects tank-scope expressions; hrt_seconds feeds RxHydVar::HRT and temp_c feeds RxHydVar::TEMP (NaN ⇒ the [REACTION_OPTIONS] TEMPERATURE constant). On an integrator failure the block is left unchanged and the once-per-run failure warning fires — the reactElements contract.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ recompileReactionSystem()

bool openswmm::transport::recompileReactionSystem ( SimulationContext & ctx,
std::vector< std::string > & errors )

Recompile every reaction expression (terms, pipes, tanks) into a fresh flat token pool from the sources in ctx.reactions (E-C2: the one compile path, shared by the file apply and the CRUD API's eager validation — D-RC4, no second compiler). Returns true and sets rx.compiled on success; on failure appends diagnostics and leaves rx.compiled false (the pool/spans hold the partial result — callers roll back or clear).

Here is the call graph for this function:
Here is the caller graph for this function:

◆ registerArdComponent()

void openswmm::transport::registerArdComponent ( )

Register org.hydrocouple.openswmm.transport.ard with the process-component registry (idempotent; called from SWMMEngine::open before resolution, next to registerReactionsComponent).

Here is the call graph for this function:

◆ registerHeatComponent()

void openswmm::transport::registerHeatComponent ( )

Register org.hydrocouple.openswmm.heat with the process-component registry (idempotent; called from SWMMEngine::open before resolution).

Here is the call graph for this function:

◆ registerReactionsComponent()

void openswmm::transport::registerReactionsComponent ( )

Register org.hydrocouple.openswmm.reactions with the process-component registry (idempotent; called from SWMMEngine::open before resolution).

Here is the call graph for this function:

◆ registerWaterAgeComponent()

void openswmm::transport::registerWaterAgeComponent ( )

Register org.hydrocouple.openswmm.waterage with the process-component registry (idempotent; called from SWMMEngine::open before resolution).

Here is the call graph for this function:

◆ resolve()

Matrix openswmm::transport::resolve ( const SimulationContext & ctx)

Build the full matrix (states + reasons). Pure: never writes warnings.

Here is the call graph for this function:

◆ resolveArdTransportRows()

void openswmm::transport::resolveArdTransportRows ( SimulationContext & ctx,
std::vector< std::string > & errors )

E5a: resolve the raw [TRANSPORT_BOUNDARIES]/[TRANSPORT_SOURCES] rows to node/link/species/timeseries indices. Called from SWMMEngine::open AFTER all process components (and the embedded reactions fallback) have applied, because the rows name MSX species and the reactions component may apply before or after transport.ard in file order. Pushes fatal diagnostics into errors; pollutant species are refused (their loading surface is the legacy pathways). Source VALUE rates convert from species mass/s to internal conc·ft³/s here (kLitersPerFt3).

Here is the call graph for this function:

◆ routeLegacyAge()

void openswmm::transport::routeLegacyAge ( SimulationContext & ctx,
double dt )

One LEGACY age routing step (call at the END of QualitySolver::execute, after qual_vol_in is fully accumulated). No-op when WATER_AGE is off.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ routeLegacyHeat()

void openswmm::transport::routeLegacyHeat ( SimulationContext & ctx,
double dt )

One LEGACY temperature routing step (call at the END of QualitySolver::execute, after qual_vol_in is fully accumulated). No-op when HEAT_TRANSPORT is off.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ routeLegacyMsx()

void openswmm::transport::routeLegacyMsx ( SimulationContext & ctx,
double dt )

Advect the MSX element state one routing step on the LEGACY CSTR path. No-op unless a reaction system with at least one species is configured.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ routeLidLayerAge()

void openswmm::transport::routeLidLayerAge ( SimulationContext & ctx,
const lid::LIDSolver & solver,
double dt )

Advance the per-layer ages one runoff step, after LIDSolver::execute.

Here is the call graph for this function:

◆ routeLidLayerTemperature()

void openswmm::transport::routeLidLayerTemperature ( SimulationContext & ctx,
const lid::LIDSolver & solver,
double dt )

Advance every LID column one runoff step: advective mixing per layer, then one coupled thermal solve over the stack. Publishes the underdrain temperature, retiring HeatSource::RAINFALL's "(and LID drains until H5)" marker.

< holds or receives WATER → advection

< has THERMAL MASS → conduction, policy

Here is the call graph for this function:

◆ routeSubcatchmentAge()

void openswmm::transport::routeSubcatchmentAge ( SimulationContext & ctx,
const runoff::RunoffSoA & soa,
double dt )

One runoff-step update of the subarea ages and the published runoff age. Call immediately AFTER the runoff solver has stepped, so the depths are this step's. No-op when WATER_AGE is off.

Here is the call graph for this function:

◆ routeSubcatchmentTemperature()

void openswmm::transport::routeSubcatchmentTemperature ( SimulationContext & ctx,
const runoff::RunoffSoA & soa,
double dt )

Advance ponded subarea temperatures one runoff step.

Per subarea, in order: apply the surface energy balance to the water that was already there, mix in what arrived by GROSS inflow volume, then publish the subcatchment's runoff temperature as the volume-weighted mean of the subareas holding water. Outflow leaves at the subarea's own temperature, so only the inflow is needed — the same complete-mix argument A3 arrived at after its net-gain estimate proved to be a 6.3× defect.

Parameters
soaThe solver's own SoA. ctx.subcatches.ponded_depth is declared but written by nobody; reading it would give zeros.
dtRunoff timestep, seconds.
Here is the call graph for this function:

◆ serializeReactionSystem()

std::string openswmm::transport::serializeReactionSystem ( const SimulationContext & ctx)

Canonical .rxn text for the current reaction system (empty system -> a header comment plus an empty [REACTION_SPECIES] shell is NOT emitted; the result is an empty string when nothing is configured).

Here is the call graph for this function:
Here is the caller graph for this function:

◆ serializeWaterAgeConfig()

std::string openswmm::transport::serializeWaterAgeConfig ( const SimulationContext & ctx)

Render [WATER_AGE_SOURCES] from the live config in FILE units (HOURS, shortest-exact) — the single spelling shared by the ComponentConfigSave hook and swmm_water_age_save (IO3c), the same sharing the reactions component has with serializeReactionSystem. Empty when the component never applied (save DECLINES → the carry-alongside copy runs).

Here is the call graph for this function:
Here is the caller graph for this function:

◆ setLidInflowAge()

void openswmm::transport::setLidInflowAge ( SimulationContext & ctx,
int type_index,
int unit,
int subcatch,
double rain_rate,
double q_imperv,
double q_perv,
double q_runon,
double lid_area )

Flow-weighted age of the water arriving at each LID unit this step, stored into ctx.lid_layer_state.inflow_value. Called where LIDGroupSoA::inflow is assembled, because that is the only place the contributing rates (rain, captured impervious and pervious runoff, whole-subcatchment run-on) exist together.

◆ setLidInflowTemperature()

void openswmm::transport::setLidInflowTemperature ( SimulationContext & ctx,
int type_index,
int unit,
int subcatch,
double rain_rate,
double q_imperv,
double q_perv,
double q_runon,
double lid_area )

Temperature of the water arriving at one unit this step, flow-weighted over the same four rates setLidInflowAge weights. Mirrors it exactly, including its hard-won choice to take the SUBCATCHMENT's published runoff value rather than reconstructing a mean from the subarea rows.

◆ speciesClassName()

const char * openswmm::transport::speciesClassName ( SpeciesClass c)
noexcept
Here is the caller graph for this function:

◆ surface2DEnables()

ClassEnables openswmm::transport::surface2DEnables ( const SimulationContext & ctx)
noexcept

2D surface enables: the 1D rule plus the [2D_OPTIONS] TRANSPORT_* keys. Age and temperature are NOT gated by IGNORE_QUALITY here (pre-E2 rule: "IGNORE_QUALITY turns off the pollutant and MSX rows only").

Here is the caller graph for this function:

◆ warnIfFvDispersionKeyIgnored()

void openswmm::transport::warnIfFvDispersionKeyIgnored ( SimulationContext & ctx)

Warn when [OPTIONS] FV_DISPERSION is set under QUALITY_SOLVER EULERIAN_ARD. The component's own bypass warnings only cover models that HAVE a transport.ard file; this is the reverse case — the user spelled dispersion the familiar way, selected the engine that now supports it, and got nothing. FV_DISPERSION reaches no solver today (WARN 105 says so under FLOW_ROUTING FV, and nothing says it under DYNWAVE), so E3's arrival is what makes the silence misleading rather than merely inert.

◆ warnIfLegacyBindingBypassed()

void openswmm::transport::warnIfLegacyBindingBypassed ( SimulationContext & ctx)

Warn (once, at open) when a reactions component is configured but no engine will run it this simulation. As of E4/R6 the only remaining bypass is IGNORE_QUALITY — EULERIAN_ARD now runs its own binding (ReactionArdBinding) with MSX species transported on the mesh. Without this a user who wrote a .rxn file gets a run in which nothing reacted and nothing said so.

Here is the call graph for this function:

Variable Documentation

◆ kAbsent

int openswmm::transport::kAbsent = -2
constexpr

◆ kExternal

int openswmm::transport::kExternal = -1
constexpr

-1 means "external to the unit"; -2 means "this type has no such layer".

◆ kMeltwaterTempC

double openswmm::transport::kMeltwaterTempC = 0.0
inlineconstexpr

The temperature meltwater leaves a pack at, °C. Zero, and named rather than written as a literal so a gate can assert against the constant the code uses instead of against a number a reader hopes it uses.

◆ kNL

int openswmm::transport::kNL = LidLayerSpeciesState::kLayerCount
constexpr

◆ kPave

int openswmm::transport::kPave = static_cast<int>(LidLayer::PAVEMENT)
constexpr

◆ kRxMaxStackDepth

int openswmm::kRxMaxStackDepth
inlineconstexpr

Maximum operand-stack depth; the compiler rejects deeper expressions so the evaluator's fixed stack can never overflow.

◆ kSoil

int openswmm::transport::kSoil = static_cast<int>(LidLayer::SOIL)
constexpr

◆ kStor

int openswmm::transport::kStor = static_cast<int>(LidLayer::STORAGE)
constexpr

◆ kSubIMPERV0

int openswmm::transport::kSubIMPERV0 = 0
constexpr

Subarea ordering shared by SubArea (A3) and HeatSubArea (H5a). Both enums are deliberately separate types; this is the ordinal they agree on.

◆ kSubIMPERV1

int openswmm::transport::kSubIMPERV1 = 1
constexpr

◆ kSubPERV

int openswmm::transport::kSubPERV = 2
constexpr

◆ kSurf

int openswmm::transport::kSurf = static_cast<int>(LidLayer::SURFACE)
constexpr