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

Classes

struct  BoundaryWaterImport
 
class  ConservativeMixing
 
class  FvQualityTransport
 
struct  FvTransportWater
 
struct  MixedScalar
 
struct  MixingScalar
 
struct  MixingWater
 
class  QualitySolver
 
struct  WaterTransfer
 

Functions

void bookNegativeSourceClamp (SimulationContext &ctx, int node, int p, double shortfall)
 Book one pollutant-row clamp: count it and un-book the shortfall from the external-extraction ledger row.
 
void bookNegativeAgeClamp (SimulationContext &ctx, int node)
 Age-row clamp: counted, not ledgered (no age row until A2c) and not warned per-clamp (see bookNegativeSourceClamp's note).
 
void bookNegativeCellSourceClamp (SimulationContext &ctx, int conduit, double shortfall)
 P1.4: book one ARD cell-source clamp. Counted, NOT ledgered — and the reason is structural, not an omission.
 
void summarizeNegativeSourceClamps (SimulationContext &ctx)
 End-of-run summary (pushed once by SWMMEngine::end when anything clamped; gates read ctx.warnings, so .rpt ordering is not load-bearing).
 
bool linkTakesUpstreamValue (const SimulationContext &ctx, int link)
 Test whether a link has no mixed conduit water pool.
 
double conduitMixingInflow (const SimulationContext &ctx, int link, double dt)
 Calculate a conduit quality mixing inflow from accepted hydraulic balance.
 
bool linkIsDry (const SimulationContext &ctx, int link)
 Test the accepted conduit water/depth effective-zero thresholds.
 
bool nodeIsReactor (const SimulationContext &ctx, int node)
 Select storage or retained-water node mixing.
 
double storageEvapFactor (const SimulationContext &ctx, int node)
 Calculate storage concentration increase from non-LID evaporation.
 
void updateUntreatedStorageHrt (SimulationContext &ctx, double dt)
 Advance residence time only for untreated storage nodes under active native reactions.
 
bool nodeIsDry (const SimulationContext &ctx, int node, double q_in)
 Test node effective dryness only when mixing inflow is negligible.
 
void applyNodeTreatment (SimulationContext &ctx, int j, double dt, double q_raw, const double *cin)
 Resolve one node's compiled co-treatment laws and book accepted pollutant removal.
 

Variables

constexpr double ZERO_VOLUME = 0.0353147
 1 liter in ft3
 
constexpr double ZERO_DEPTH = 0.003281
 
constexpr double LEGACY_ZERO = 1.0e-10
 

Function Documentation

◆ applyNodeTreatment()

void openswmm::quality::applyNodeTreatment ( SimulationContext & ctx,
int j,
double dt,
double q_raw,
const double * cin )

Resolve one node's compiled co-treatment laws and book accepted pollutant removal.

One node's [TREATMENT] application, against the CALLER's inflow figures — the seam the LEGACY pass and the LARD MIX share.

Parameters
[in,out]ctxCompiled treatment rows and shared cin/removal scratch, node concentrations/HRT and reacted mass ledger. Requires all configured node/pollutant/subtype arrays prepared.
[in]jValid nonnegative native node index (node in declaration); absent treatment mask returns.
[in]dtAccepted routing duration s; positive finite required for ordinary HRT/mass bookkeeping.
[in]q_rawCaller-complete inflow ft3/s (q_in_cfs in declaration); caller selects its assembly source.
[in]cinBorrowed np inflow concentrations, null supplies zeros; may equal treat.cin.data().
Note
Uses display flow/depth/area, average native volume ft3, HRT hours and dt seconds for expression variables. Resolves R references recursively with cycle sentinel; invalid/cyclic references become0, no explicit warning/error result. Removal clamps0..1, concentration result cannot increase node; zero inflow suppresses removal-typed expression. Updates storage HRT and positive net lost-mass ledger; shared scratch overwritten. No water/clock advance. Temporary vectors/evaluation can throw after HRT or earlier pollutant changes; exclusive context/compiled-table lifecycle, no rollback.

Evaluates the node's compiled expressions (removal- and concentration-typed, with co-treatment resolution and cycle detection), rewrites nodes.conc for every treated pollutant and books the removed mass into qual_routing_reacted. The inflow concentration and rate come from the CALLER because the two engines hold them in different places: LEGACY accumulates qual_mass_in/qual_vol_in, while LARD's node inflow lives in its solver-internal drain accumulators — reading the LEGACY arrays under LARD made an R-typed expression a literal no-op (cin read 0, and cOut = (c_in > 0) ? … : c_node kept the untreated value; P2.3's first draft shipped exactly that).

  • Argument q_in_cfs: Inflow rate feeding the node this interval, ft³/s.
  • Argument cin: Per-pollutant inflow concentration, length np.
  • Argument ctx (in,out): Prepared node concentrations/HRT, compiled treatment and shared scratch/ledgers.
  • Argument node (in): Valid native node ID; source definition calls it j.
  • Argument dt (in): Accepted positive finite routing interval s.
  • Argument q_in_cfs (in): Complete caller inflow ft3/s; source definition calls it q_raw.
    Note
    cin may be null (zeros) or alias treatment cin scratch; other borrowed inputs remain stable. Exclusive once-per-interval treatment; no hydraulic or clock change.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ bookNegativeAgeClamp()

void openswmm::quality::bookNegativeAgeClamp ( SimulationContext & ctx,
int node )
inline

Age-row clamp: counted, not ledgered (no age row until A2c) and not warned per-clamp (see bookNegativeSourceClamp's note).

Count an age-row extraction clamp and retain first diagnostic element.

Parameters
[in,out]ctxMutable shared negative-source diagnostic counters; no physical age ledger here.
[in]nodeZero-based diagnostic element identity, not dereferenced.
Note
Counts every call, does not itself clamp any state, book age mass or validate input. Requires serialized/shared-counter ownership; no allocation/physics/clock change.
Here is the caller graph for this function:

◆ bookNegativeCellSourceClamp()

void openswmm::quality::bookNegativeCellSourceClamp ( SimulationContext & ctx,
int conduit,
double shortfall )
inline

P1.4: book one ARD cell-source clamp. Counted, NOT ledgered — and the reason is structural, not an omission.

The node seam un-books its shortfall from qual_routing_ex_in because the loaders booked the signed request there in advance. Cell sources have no such row to correct:

  1. [TRANSPORT_SOURCES] rows resolve to MSX species rows only — ArdEngine.cpp pushes np + src_msx[i], so a cell source can never name a pollutant; and
  2. MSX species have no mass-balance row at all. The ARD component writes to ctx.mass_balance nowhere.

So a per-pollutant row for this would be permanently zero, and inventing an MSX row here would be a third self-consistent balance (lesson 147) bolted on inside a defect fix. Giving MSX species real ledger rows (sizing the qual_* vectors by np + nm) is a deliberate, separate round — until it lands, the delivered mass reaches qual_routing_final through the end-of-run inventory sweep and therefore surfaces as CONTINUITY ERROR rather than as a labelled term. Recorded, not hidden.

Parameters
conduitmesh conduit row of the first clamp (diagnostic only).
shortfallthe UNMET extraction (positive, internal mass units).
[in,out]ctxMutable shared extraction count/shortfall and first-element diagnostics only.
Note
Caller supplies finite positive unmet native extraction and already performs its actual physical/native receipt booking. This helper does not repair any mass ledger or remove inventory. Counts every call; requires serialized diagnostic ownership, allocation-free/no clock mutation.

◆ bookNegativeSourceClamp()

void openswmm::quality::bookNegativeSourceClamp ( SimulationContext & ctx,
int node,
int p,
double shortfall )
inline

Book one pollutant-row clamp: count it and un-book the shortfall from the external-extraction ledger row.

Note
NO per-clamp warning. There was one until 2026-08-29; it fired on every correct extraction deck, because during fill every cell is near-empty and any extraction clamps trivially (measured: 108 clamps on a deck extracting 40 % of its inflow). A notice that fires on ordinary models is one users learn to ignore — which destroys it on the models where it matters (lesson 148). The end-of-run summary carries the count and the unmet mass; that is the diagnostic.
Parameters
shortfallthe UNMET extraction (positive, internal mass units).
[in,out]ctxNative extraction statistics and pollutant external-inflow balance, mutated once per actual caller clamp; shared stats/ledger require serial access or external synchronization.
[in]nodeZero-based diagnostic element identity, not dereferenced; first clamp retained.
[in]pPollutant ledger index; out-of-range skips ledger correction but still counts event.
Note
shortfall must be finite positive unmet internal mass from an already-booked signed external request; helper does not validate, remove physical mass, clamp inventory or advance time. Adds it back to qual_routing_ex_in, increments counts and records first element. Allocation-free.
Here is the caller graph for this function:

◆ conduitMixingInflow()

double openswmm::quality::conduitMixingInflow ( const SimulationContext & ctx,
int link,
double dt )

Calculate a conduit quality mixing inflow from accepted hydraulic balance.

Parameters
[in]ctxStable covered link/type/subtype hydraulic arrays.
[in]linkValid nonnegative native link index, unchecked.
[in]dtAccepted interval s; nonpositive omits volume correction. Positive finite duration required for ordinary routing.
Returns
Nonnegative ft3/s: absolute KW upstream flow or link flow; DYNWAVE/FV add (new volume
  • barrel seep/evap losses - old volume)/dt for dt>0.
Note
Read-only and allocation-free; no generic finite/physical validation.

The mixing inflow rate a conduit sees over one step (legacy findLinkQual): KW's accepted upstream flow, or |Link.newFlow| for single-rate models, plus — under a single-rate dynamic model only — the volume the conduit gained together with what it lost to seepage and evaporation, the sum clamped at zero.

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

◆ linkIsDry()

bool openswmm::quality::linkIsDry ( const SimulationContext & ctx,
int link )

Test the accepted conduit water/depth effective-zero thresholds.

Parameters
[in]ctxStable covered link/type/subtype hydraulic arrays.
[in]linkValid nonnegative native link index, unchecked.
Returns
True for volume<.0353147ft3 OR depth<=.003281ft; no mutation.
Note
Read-only and allocation-free; no generic finite/physical validation.

Legacy findLinkQual's closing test: the link ends the step essentially empty — under a litre of water OR under a millimetre of depth.

Here is the caller graph for this function:

◆ linkTakesUpstreamValue()

bool openswmm::quality::linkTakesUpstreamValue ( const SimulationContext & ctx,
int link )

Test whether a link has no mixed conduit water pool.

Parameters
[in]ctxStable covered link/type/subtype hydraulic arrays.
[in]linkValid nonnegative native link index, unchecked.
Returns
True for non-conduit type or DUMMY section; no flow/depth check.
Note
Read-only and allocation-free; no generic finite/physical validation.

Legacy findLinkQual's opening test: a non-conduit link — pump, orifice, weir, outlet — or a conduit with a DUMMY cross-section holds no water, so it takes its upstream node's value outright, with no mixing, no evaporation factor and no reaction.

Here is the caller graph for this function:

◆ nodeIsDry()

bool openswmm::quality::nodeIsDry ( const SimulationContext & ctx,
int node,
double q_in )

Test node effective dryness only when mixing inflow is negligible.

Parameters
[in]ctxStable node/type/subtype old/current hydraulic arrays; paired storage loss and LID states covered.
[in]nodeValid nonnegative native node ID; storageEvapFactor returns1 when type extent absent.
[in]q_inMixing inflow rate ft3/s, not interval volume; negative meets the rate test.
Returns
True when volume<=.0353147ft3 OR depth<=.003281ft, AND q_in<=1e-10ft3/s.
Note
Read-only/no allocation; no geometry refresh, physical change or clock update.

Legacy findStorageQual's closing test: the node ends the step essentially empty AND is taking nothing in. Both halves matter — a dry node still receiving flow keeps its mix.

Here is the caller graph for this function:

◆ nodeIsReactor()

bool openswmm::quality::nodeIsReactor ( const SimulationContext & ctx,
int node )

Select storage or retained-water node mixing.

Parameters
[in]ctxStable node/type/subtype old/current hydraulic arrays; paired storage loss and LID states covered.
[in]nodeValid nonnegative native node ID; storageEvapFactor returns1 when type extent absent.
Returns
True for STORAGE type regardless of dryness, or old volume>.0353147ft3.
Note
Read-only/no allocation; no geometry refresh, physical change or clock update.

Legacy qualrout_execute's node dispatch: true when the node routes as a mixed reactor (findStorageQual), false when it is pure flow-through (findNodeQual). Note the asymmetry — the STORAGE half tests the node's TYPE, so a storage unit never drops to flow-through however empty it gets.

Here is the caller graph for this function:

◆ storageEvapFactor()

double openswmm::quality::storageEvapFactor ( const SimulationContext & ctx,
int node )

Calculate storage concentration increase from non-LID evaporation.

Parameters
[in]ctxStable node/type/subtype old/current hydraulic arrays; paired storage loss and LID states covered.
[in]nodeValid nonnegative native node ID; storageEvapFactor returns1 when type extent absent.
Returns
1 plus max(0,total evaporation minus native LID evaporation)/old volume when old volume>one litre; otherwise1.
Note
Read-only/no allocation; no geometry refresh, physical change or clock update.

Legacy findStorageQual's evaporation concentration factor 1 + vEvap/v1 for a storage node (1 for any other node). Shared by the pollutant reactor and the reaction-species mirror (review B1).

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

◆ summarizeNegativeSourceClamps()

void openswmm::quality::summarizeNegativeSourceClamps ( SimulationContext & ctx)
inline

End-of-run summary (pushed once by SWMMEngine::end when anything clamped; gates read ctx.warnings, so .rpt ordering is not load-bearing).

Append the accumulated extraction-clamp summary when any pollutant/native or age event occurred.

Parameters
[in,out]ctxReads counters/first element and appends owned message to warnings.
Note
No events returns. Does not clear counts or suppress repeated calls, so owner invokes once at run end. Message includes internal mass shortfall, age event count and zero-based diagnostic identity. Allocation may throw; exclusive warning sink/stable counters, no physics/clock advance.

◆ updateUntreatedStorageHrt()

void openswmm::quality::updateUntreatedStorageHrt ( SimulationContext & ctx,
double dt )

Advance residence time only for untreated storage nodes under active native reactions.

Parameters
[in,out]ctxPrepared storage node HRT/old volume and hydraulic inflow; updates covered HRT seconds.
[in]dtPositive finite accepted interval s; nonpositive or inactive legacy reactions returns.
Note
Skips treated nodes (their treatment path advances HRT); essentially empty old volume resets0, otherwise max((old HRT+dt)*old volume/(old volume+abs(inflow)*dt),0). Missing inflow uses0. No quality/water ledger or clock mutation, allocation or finite repair; exclusive once-per-interval state.

Residence time (nodes.hrt, s) at storage nodes WITHOUT a treatment expression, by legacy updateHRT on the hydraulic inflow. Treated nodes are advanced by applyNodeTreatment. No-op without a reactions component (review A6).

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

Variable Documentation

◆ LEGACY_ZERO

double openswmm::quality::LEGACY_ZERO = 1.0e-10
constexpr

Legacy's effective-zero flow (ZERO in src/legacy/engine/consts.h). The mixing kernels test the inflow RATE against this, not against 0.0. Published here because the water-age, heat and MSX transport mirrors run the same kernels and must not drift from them.

◆ ZERO_DEPTH

double openswmm::quality::ZERO_DEPTH = 0.003281
constexpr

1 mm in ft

◆ ZERO_VOLUME

double openswmm::quality::ZERO_VOLUME = 0.0353147
constexpr

1 liter in ft3