OpenSWMM Engine  6.0.0-alpha.4
Data-oriented, plugin-extensible SWMM Engine (6.0.0-alpha.4)
Loading...
Searching...
No Matches
Chapter 9: Water Age and Heat Transport

9.1 Introduction

Water age and temperature are not pollutants, but both are scalar fields carried by the same water, mixed by the same junctions and advected by the same flows. OpenSWMM therefore transports them through the same machinery that carries pollutants and reaction species, as reserved species — rows on the transport state that no [POLLUTANTS] declaration creates and that obey slightly different laws.

Treating them as reserved rows rather than as separate subsystems is what lets a single mixing formula serve all of them: when two flows meet, the volume-weighted mean is the correct answer for a concentration, for an age and for a temperature alike. What differs is not the mixing but the source terms and the constraints, and those are stated explicitly below rather than left to fall out of the code.

Both are enabled by an [OPTIONS] key and are inert otherwise:

Key Reserved species Internal unit Reported unit
WATER_AGE __WATER_AGE__ seconds hours
HEAT_TRANSPORT __TEMPERATURE__ °C °C

9.2 Water Age

9.2.1 Definition and aging

Water age is the time elapsed since a parcel of water entered the system. Every element's age advances by exactly the routing step before transport is applied:

\[a(t + \Delta t) = a(t) + \Delta t\]

and then mixes by volume weight wherever flows combine, exactly as a concentration does:

\[a_{mix} = \frac{\sum_i a_i V_i}{\sum_i V_i}\]

Age therefore has no decay and no reaction. It grows without bound in water that never leaves, which is the intended reading: a storage unit whose age climbs steadily is a storage unit that is not turning over.

9.2.2 Sources

[WATER_AGE_SOURCES] sets the age carried by water entering the system, by scope. The common cases are:

  • INITIAL_STATE — the age assigned to water already in the network at \(t = 0\). Setting this to zero models a system flushed at the start; setting it to a nonzero value models one that has been standing.
  • Per-pathway sources — the age of water arriving by dry-weather flow, groundwater, RDII, external inflow, and the remaining loader pathways.

Water entering with age zero is fresh; water entering with a nonzero age is treated as having already spent that long in transit elsewhere, which is how a boundary at the edge of a modelled area represents the network beyond it.

With [OPTIONS] OUTFALL_BACKFLOW_QUALITY ZERO (§7.5), water that re-enters through an outfall during reverse flow arrives at age zero — the outfall is a fresh boundary — whereas the default LAST lets the held boundary water keep aging one-for-one and re-import that age on every reversal.

Example. A collection system fed by an upstream trunk that is not modelled, whose travel time is known to be about four hours:

[WATER_AGE_SOURCES]
INITIAL_STATE GLOBAL 0.0
EXTERNAL_INFLOW GLOBAL 4.0
DWF GLOBAL 0.0

Ages here are in hours, the reported unit. Water arriving through [INFLOWS] is treated as four hours old on arrival; dry-weather flow enters fresh; the network starts empty of aged water.

9.2.3 State and reporting

Age is held in seconds internally and reported in hours, which is the scale users read. The distinction matters when reading the output file directly rather than through the reporting layer.

A dry element keeps aging in state while its reported age is masked. Those are deliberately separate: the state must keep advancing because the element may refill and the water in it has genuinely been there, while reporting an age for an element holding no water would be meaningless. A restart therefore carries the aged state, not the masked report.

9.3 Heat Transport

9.3.1 Temperature as a transported scalar

Temperature is carried as a reserved species and mixed by volume weight, which is the correct mixing rule when the specific heat of water is taken as constant across the modelled range — mixing enthalpies then reduces to mixing temperatures:

\[T_{mix} = \frac{\sum_i T_i V_i}{\sum_i V_i}\]

Unlike age, temperature does not advance with time by itself, does not decay, and is not constrained to be non-negative: water below 0 °C is a legitimate model state, and the non-negativity clamp applied to concentrations is deliberately not applied to this row.

Inlet temperatures are set per source by [HEAT_SOURCES], with a global value for each of the seven water sources and node-scope overrides where a particular inflow point differs:

[HEAT_SOURCES]
;;source scope [name] degC
RAINFALL GLOBAL 12.0
DWF GLOBAL 18.5
GW GLOBAL 11.0
INITIAL_STATE GLOBAL 15.0
DWF NODE J12 22.0

Temperatures are °C and must lie between −50 and 100. A source with no row takes 20 °C. NODE-scope overrides apply to DWF and EXTERNAL_INFLOW; other sources take their global value.

Transport alone — advection and mixing, with no flux modules enabled — is the default and is often enough: it answers "where does the warm water go?" A model that additionally needs "how does it warm or cool on the way?" enables one or more of the flux modules below.

9.3.2 Meteorological forcing

The flux modules are driven by the model's existing climate data, not by a heat-specific meteorology section. Air temperature, wind speed and relative humidity come from [TEMPERATURE], [WINDSPEED] and the humidity series already used by evaporation and snowmelt, which means a model that has been calibrated for hydrology is already carrying most of what heat transport needs.

Forcing Source Native unit Used as
Air temperature \(T_a\) [TEMPERATURE] °F °C
Wind speed \(w\) [WINDSPEED] mph m/s
Relative humidity \(RH\) climate series % fraction
Incoming shortwave \(J_{in}\) [RADIATIVE_FLUXES] / computed W/m² W/m²
Cloud fraction \(C\) [CLOUD_COVER] 0–1 0–1

The unit conversions are worth stating because they are a common source of confusion when reading intermediate values: the climate state stores air temperature in °F and wind in mph for historical reasons, while every heat formulation is written in SI. The conversion happens in exactly one place so the two cannot drift apart.

9.3.2a The Physical Constants

Five [OPTIONS] keys (Section: [OPTIONS]) set the constants every flux module shares. They are model-wide by design — a water density that varied by reach would be two descriptions of one fluid — and their defaults are the CSH reference's Table 4.1 values.

Key Symbol Default Where it enters
WATER_DENSITY \(\rho_w\) 1000 kg/m³ The latent flux \(J_e = \rho_w L_v E\) (§9.3.4); the heat capacity \(\rho_w c_p V\) the relaxation of §9.3.9 divides by; the water-side capacity and the hyporheic advective conductance of the bed zone (§9.3.11); LID layer conduction (§9.3.10)
WATER_SPECIFIC_HEAT_CAPACITY \(c_p\) 4184 J/kg/°C With \(\rho_w\) in every capacity and conductance above
WIND_FUNC_COEFF_A \(a\) 1.505e-8 The wind function \(f(w) = a + b\,w\) in the evaporation rate \(E = f(w)\,(e_s(T_w) - e_a)\) (§9.3.4); Dunne and Leopold (1978)
WIND_FUNC_COEFF_B \(b\) 1.6e-8 As above; \(w\) is wind speed in m/s, floored at zero
PRESSURE_RATIO \(P_a / P\) 1.0 The Bowen ratio \(B = \gamma\,(P_a/P)\,(T_w - T_a)/(e_w - e_a)\) of §9.3.5, an elevation correction; 1 at sea level

WATER_DENSITY and the specific heat were deliberately absent from the first heat phase: with no flux modules enabled they cancel identically and no gate could observe them. They became load-bearing with surface exchange, where they set the weight of a surface flux against advected heat.

9.3.3 The surface energy balance

With [HEAT_FLUXES] SURFACE_EXCHANGE ON and/or RADIATIVE_EXCHANGE ON, the free surface exchanges heat with the atmosphere. The modules use a consistent sign convention throughout:

Positive flux is heat leaving the water.

The net flux out of the surface is the sum of the enabled modules' terms:

\[J_{net} = \underbrace{J_e + J_h}_{\text{surface exchange}} + \underbrace{J_{br} - J_{sn} - J_{an} - J_{lc}}_{\text{radiative exchange}}\]

where \(J_e\) is latent, \(J_h\) sensible, \(J_{br}\) back longwave radiated by the water, \(J_{sn}\) net shortwave absorbed, \(J_{an}\) atmospheric longwave absorbed, and \(J_{lc}\) land-cover longwave absorbed. Three radiative terms warm the water and one cools it, which is why only \(J_{br}\) carries a positive sign in the sum.

The two modules are independent. A model may enable surface exchange without radiation (evaporative cooling only), radiation without surface exchange (a covered channel), or both.

Figure 9-1 collects the terms of (the balance above) with the bed-zone exchange of §9.3.11, grouped by the module that enables each.

Figure 9-1

Figure 9-1 The heat budget of a water body: the six surface terms of the net flux by module, and the bed zone's conduction and hyporheic exchange

9.3.4 Latent heat — evaporation and condensation

Latent exchange follows the mass-transfer formulation given for surface-water quality modelling by Martin and McCutcheon (1999). Saturation vapour pressure over water uses the Magnus form, in kPa:

\[e_s(T) = 0.61275\,\exp\!\left(\frac{17.27\,T}{237.3 + T}\right)\]

The air's actual vapour pressure follows from relative humidity evaluated at the air temperature, while the water surface is saturated at the water temperature:

\[e_a = \frac{RH}{100}\,e_s(T_a), \qquad e_w = e_s(T_w)\]

The evaporative mass flux is the vapour-pressure deficit driven by a linear wind function:

\[E = f(w)\,(e_w - e_a), \qquad f(w) = a + b\,w\]

with \(E\) in m/s and \(f(w)\) in m/s/kPa. The coefficients \(a\) and \(b\) are the calibration handles of this module. The latent heat flux is then

\[J_e = \rho_w\,L_v(T_w)\,E, \qquad L_v(T) = 1000\,(2499 - 2.36\,T)\ \text{J/kg}\]

** \(E\) is signed.** When \(e_a > e_w\) — humid air over cold water — the deficit is negative, \(E\) is negative, and the term becomes condensation warming the water rather than evaporation cooling it. The formulation is not clamped to evaporation only, because condensation onto a cold surface is a real process and suppressing it would bias the balance in one direction.

9.3.5 Sensible heat — the Bowen ratio

Sensible exchange is obtained from the latent flux through the Bowen ratio rather than by an independent transfer coefficient, which is the standard closure and keeps the two terms consistent by construction:

\[B_r = \gamma\,\frac{p}{p_0}\,\frac{T_w - T_a}{e_w - e_a}, \qquad J_h = B_r\,J_e\]

where \(\gamma\) is the Bowen coefficient (kPa/°C) and \(p/p_0\) the ratio of site to sea-level pressure.

The ratio has a removable singularity: as the vapour-pressure deficit approaches zero the quotient diverges, but the product \(B_r J_e\) remains finite because \(J_e\) carries the same deficit as a factor. The implementation returns \(B_r = 0\) when \(|e_w - e_a| < 10^{-12}\), which is the correct value of the limiting product and not an error suppression.

9.3.6 Radiative exchange

Four terms are computed when RADIATIVE_EXCHANGE ON. All longwave terms use the Stefan–Boltzmann law with absolute temperature.

Net absorbed shortwave. Incident shortwave, reduced by surface reflection and by shading:

\[J_{sn} = (1 - R_s)\,J_{in}\,\max(0,\,1 - f_s)\]

The \(\max\) guard means an over-unity shade factor shades completely rather than inverting the sign into negative insolation.

Back longwave radiated by the water:

\[J_{br} = \varepsilon_w\,\sigma\,T_w^4\]

Atmospheric longwave absorbed. Clear-sky atmospheric emissivity uses the Brunt (1932) form, with vapour pressure in pascals:

\[\varepsilon_a = A_a + 0.0027\,\sqrt{1000\,e_a}\]

\[J_{an} = \sigma\,T_a^4\,\varepsilon_a\,(1 - R_L)\,f_{sky}\]

where \(R_L\) is the longwave reflectance of the water surface and \(f_{sky}\) the sky-view fraction.

Land-cover longwave absorbed. The complement of the sky view is occupied by vegetation or structures, which radiate toward the water:

\[J_{lc} = \varepsilon_{lc}\,\sigma\,(1 - f_{sky})\,T_a^4\]

Note
This term uses air temperature as the land-cover temperature. A canopy is usually warmer than the air during the day, so this understates daytime longwave gain from the surroundings. It is a recorded, deliberate simplification: carrying a separate land-cover temperature state would require a canopy energy balance of its own.

9.3.7 Shortwave forcing

[RADIATIVE_FLUXES] SHORTWAVE selects where \(J_{in}\) comes from. The three spellings are mutually exclusive:

Spelling Source
SHORTWAVE GLOBAL <W/m²> A constant
SHORTWAVE GLOBAL TIMESERIES <name> A measured record, interpolated
SHORTWAVE GLOBAL COMPUTED Solar position and a clear-sky model

Under COMPUTED, solar position is evaluated from the Fourier-series representation of Spencer (1971) — declination, equation of time, and hour angle — giving the solar zenith angle \(\theta_z\). Relative optical air mass then follows Kasten and Young (1989):

\[m = \frac{1}{\cos\theta_z + 0.50572\,(96.07995 - \theta_z)^{-1.6364}}\]

with \(\theta_z\) in degrees. This form is used rather than the simple \(\sec\theta_z\) because the secant diverges at the horizon, where a low-sun model spends a meaningful part of every day.

Station pressure is derived from site elevation by the standard-atmosphere relation

\[p = p_0\,(1 - 2.25577 \times 10^{-5}\,z)^{5.25588}\]

and clear-sky global horizontal irradiance from the Bird and Hulstrom (1981) model, which resolves direct-beam and diffuse components through separate transmittances for Rayleigh scattering, ozone, mixed gases, water vapour and aerosols. Site parameters — latitude, longitude, elevation, turbidity, precipitable water, ozone — are set in [SOLAR_RADIATION].

[SOLAR_RADIATION]
LATITUDE 41.88
LONGITUDE -87.63
TIMEZONE -6
ELEVATION 181.0

Night is represented by \(J_{in} = 0\) rather than by a special case, so no branch distinguishes it. A negative internal sentinel, not zero, marks "no resolved value, fall back to the configured constant" — because zero is a legal night-time irradiance and must not read as unset.

9.3.8 Cloud cover

[CLOUD_COVER] supplies a cloud fraction \(C \in [0,1]\), constant or as a time series. Cloud attenuates both radiative directions, and the two corrections are different functions:

Shortwave, following Kasten and Czeplak (1980):

\[J_{in} \leftarrow J_{in}\,\max\!\left(0,\ 1 - k\,C^{\,n}\right)\]

Longwave, following Bolz — cloud raises the effective atmospheric emissivity:

\[\varepsilon_a \leftarrow \varepsilon_a\,(1 + k_{lw}\,C^2)\]

Clouds therefore reduce incoming solar radiation while increasing downward longwave. Applying cloud to only one direction — the tempting simplification — would bias the net balance systematically, which is why both corrections are present and why the cloud fraction feeds two separate paths.

Both factors return a literal 1.0 when \(C \le 0\), so a model with no cloud configuration produces bit-identical results to one built before cloud existed. This is stronger than "equal within rounding" and is deliberate.

9.3.9 Applying the flux to a water body

Given \(J_{net}(T_w)\) over a surface area \(A\) and volume \(V\), the naive explicit update is

\[\Delta T_{expl} = -\,\frac{J_{net}(T_0)\,A\,\Delta t}{\rho_w c_p V}\]

which is accurate for small steps but can overshoot — a shallow, fast- cooling element can be driven past equilibrium and oscillate, or leave the physical range entirely, when the routing step is long relative to the thermal response time.

OpenSWMM therefore applies the flux with a linearized semi-implicit relaxation. The flux is probed at \(T_0\) and at \(T_0 + h\) to obtain its local slope,

\[J' = \frac{J(T_0 + h) - J(T_0)}{h}, \qquad k = \frac{A\,J'}{\rho_w c_p V}\]

and the update becomes an exponential relaxation toward the equilibrium temperature \(T_{eq} = T_0 - J_0/J'\):

\[\Delta T = \frac{J_0}{J'}\left(e^{-k\,\Delta t} - 1\right)\]

Because \(J\) increases with \(T_w\) over the physical range (a warmer surface radiates and evaporates more), \(J' > 0\) and \(k > 0\), so the step cannot overshoot \(T_{eq}\) regardless of \(\Delta t\): it approaches it asymptotically. When \(J' \le 0\) — outside the physical regime, or where the probe is dominated by rounding — the scheme falls back to the explicit form rather than dividing by a slope it does not trust.

9.3.10 LID layer conduction

With [HEAT_FLUXES] LAYER_CONDUCTION ON, vertical conduction is solved between the layers of an LID control — surface, pavement, soil and storage — so a bioretention or permeable-pavement column has a thermal profile rather than a single mixed temperature.

Each layer's effective conductivity is the volume-weighted mean of its water and solid fractions, with \(\theta\) the water content:

\[k_{eff} = \theta\,k_w + (1 - \theta)\,k_s\]

Defaults are \(k_w = 0.606\) W/m/K for water and \(k_s = 2.6\) W/m/K for the solid matrix, with a matrix density of 1970 kg/m³ and specific heat 2758 J/kg/K. A wet layer therefore conducts differently from a dry one, which is the point: a saturated storage layer couples to the soil above it far more strongly than an empty one.

Conductance across the interface between adjacent layers is the series resistance of their half-thicknesses:

\[U_{i,i+1} = \left(\frac{d_i}{2 k_i} + \frac{d_{i+1}}{2 k_{i+1}}\right)^{-1}\]

and the resulting one-dimensional system is solved implicitly by a tridiagonal (Thomas) solve, so a thin surface film — which has a very short thermal time constant — does not force the whole model onto a short step.

A layer that is dry but present still participates in conduction: it has a temperature and a thermal mass even with no water in it, and removing it from the system would break the column into disconnected pieces.

9.3.11 The bed zone — conduction and hyporheic exchange

A buried conduit exchanges heat with more than its free surface. Its wetted perimeter is in contact with the pipe wall and the sediment beyond it, that sediment conducts to the deeper ground, and in an unlined or permeable channel water itself moves between the channel and the pore space beneath it. [HEAT_FLUXES] SEDIMENT_EXCHANGE ON adds all three.

The formulation follows the transient-storage zone of HydroCouple's HTSComponent, whose lineage is the stream transient-storage literature (Bencala and Walters, 1983; Runkel, 1998). The zone is a second body: a bed of thickness \(Y_{bed}\) beneath each conduit, with its own temperature \(T_b\) and its own thermal mass, exchanging with the water above and with a deep-ground boundary below.

Three conductances. Writing \(A_{bed}\) for the contact area and \(\rho_s c_s\) for the volumetric heat capacity of the saturated sediment:

\[G_{cond} = \frac{\alpha_{sed}\,\rho_s c_s\, A_{bed}}{Y_{bed}}\]

\[G_{adv} = \rho_w c_p\, v_{hyp}\, A_{bed}\]

\[G_{bg} = \frac{\alpha_{sed}\,\rho_s c_s\, A_{bed}}{Y_{gr}}\]

where \(\alpha_{sed}\) is the bed thermal diffusivity (m²/s), \(v_{hyp}\) the hyporheic exchange velocity across the interface (m/s), and \(Y_{gr}\) the depth to the deep-ground boundary. The grouping \(\alpha\rho c\) has units of W/m/K and is an effective thermal conductivity; it is written this way rather than as a conductivity \(k\) because the diffusivity is the parameter the deck supplies.

Conduction and hyporheic advection both move heat between the same two bodies and are therefore summed into a single water–bed conductance, \(G_{wb} = G_{cond} + G_{adv}\). The pair of bodies then obeys

\[C_w \frac{dT_w}{dt} = -A_s J(T_w) + G_{wb}(T_b - T_w)\]

\[C_b \frac{dT_b}{dt} = G_{wb}(T_w - T_b) + G_{bg}(T_{gr} - T_b)\]

with \(C_w = \rho_w c_p V_w\) and \(C_b = \rho_s c_s A_{bed} Y_{bed}\).

The exchange is conservative by construction. Adding the two equations, \(G_{wb}\) cancels identically and leaves \(C_w \dot T_w + C_b \dot T_b = -A_s J + G_{bg}(T_{gr} - T_b)\): whatever the water loses to the bed, the bed gains. The ground term is the only true source or sink in the pair, which is why a bed that is well insulated from the deep ground eventually equilibrates with the water rather than draining it.

Integration. The two bodies are stepped simultaneously, not one after the other. Each body's equilibrium moves as the other responds, so there is no fixed target for a single-body relaxation (§9.3.9) to relax toward, and stepping them in sequence makes the answer depend on which went last. The linearized 2×2 system is advanced by its exact matrix exponential. Both eigenvalues are real and non-positive — the discriminant is \((a_{11}-a_{22})^2 + 4a_{12}a_{21}\) with non-negative off-diagonals — so the pair can neither oscillate nor overshoot at any step length.

Contact area is the wetted perimeter, \(P = A/R\), times length times barrels. This is a deliberate departure from the open-channel reference, which uses top width: the two coincide in a wide channel, but for a surcharged circular pipe the top width is zero while the contact area is at its maximum. Using top width would switch the module off exactly where a buried pipe conducts most. The consequence worth knowing is that a closed conduit, which has no free surface and therefore no latent, sensible or radiative exchange at all, still exchanges with its bed — for much of a buried network this is the only heat transfer there is.

Solutes. The same zone acts as transient storage for pollutants and multi-species reactants. The exchange discharge is

\[Q_{exch} = \frac{D_{sed} A_{bed}}{Y_{bed}} + v_{hyp} A_{bed}\]

and the channel/bed concentration pair is advanced in total-and-difference coordinates: the total mass is invariant and the difference decays as \(\exp(-\mu \Delta t)\) with \(\mu = Q_{exch}(1/V_w + 1/V_b)\). Solving in those coordinates makes mass conservation structural rather than a bookkeeping step. This is what produces the long concentration tail characteristic of a stream with a storage zone: a tracer slug leaves mass behind in the bed and that mass returns slowly after the peak has passed. There is deliberately no bed-to-ground solute exchange, matching the reference.

Parameters ([SEDIMENT_EXCHANGE], all GLOBAL scope):

Key Symbol Default Meaning
THERMAL_DIFFUSIVITY \(\alpha_{sed}\) 1.0e-6 Bed thermal diffusivity, m²/s
SOLUTE_DIFFUSIVITY \(D_{sed}\) 1.0e-9 Bed effective solute diffusivity, m²/s
BED_THICKNESS \(Y_{bed}\) 0.20 Bed layer thickness, m
GROUND_DEPTH \(Y_{gr}\) 2.0 Depth to the deep-ground boundary, m
GROUND_TEMPERATURE \(T_{gr}\) 12.0 Deep-ground temperature, °C (or TIMESERIES)
HYPORHEIC_VELOCITY \(v_{hyp}\) 0.0 Exchange velocity across the interface, m/s
SEDIMENT_DENSITY \(\rho_s\) 1670 Bed bulk density, kg/m³
SEDIMENT_SPECIFIC_HEAT \(c_s\) 1807 Bed specific heat, J/kg/K
INITIAL_TEMPERATURE — \(T_{gr}\) Initial bed temperature, °C

The default sediment properties describe streambed sediment and are deliberately different from the LAYER_CONDUCTION defaults of §9.3.10, which describe a bioretention soil column. They are different materials and a single pair of numbers cannot stand for both.

Every length, diffusivity and material property is refused at zero or below rather than clamped, because each is a divisor or a capacity: a silently repaired zero produces either an infinite conductance or a massless bed that tracks the water exactly, both of which look like a working model. SOLUTE_DIFFUSIVITY and HYPORHEIC_VELOCITY accept zero, each selecting a meaningful configuration (conduction-only transport, and conduction-only exchange respectively).

GROUND_TEMPERATURE has a default but the engine warns when a deck leaves it unstated: for a buried conduit it is frequently the largest term in the balance, so the default is unlikely to be what was meant.

A worked bed configuration — a buried sewer in cool ground with modest hyporheic exchange:

[HEAT_FLUXES]
SEDIMENT_EXCHANGE ON
[SEDIMENT_EXCHANGE]
GROUND_TEMPERATURE GLOBAL 11.5
GROUND_DEPTH GLOBAL 2.0
BED_THICKNESS GLOBAL 0.25
THERMAL_DIFFUSIVITY GLOBAL 1.1e-6
HYPORHEIC_VELOCITY GLOBAL 5.0e-6
SOLUTE_DIFFUSIVITY GLOBAL 1.0e-9

9.3.12 Parameter reference

The six sections of the heat component, with every key, its symbol, its default and its scope. Rows are <key> <scope> [name] <value>; the scope token is required on every row of every section. GLOBAL is always accepted; where a key takes TAG name, LINK name or NODE name, the per-element value overrides the global for that element (Chapter 11 §11.4).

[HEAT_SOURCES] (Component section: [HEAT_SOURCES]) — inlet temperature per water source, °C, in \([-50, 100]\); default 20 °C for a source with no row.

Source Scope Default Meaning
RAINFALL GLOBAL 20.0 Rain on subcatchments and on the 2D mesh
DWF GLOBAL, NODE 20.0 Dry-weather flow
GW GLOBAL 20.0 Groundwater inflow from the legacy aquifers
RDII GLOBAL 20.0 Rainfall-derived inflow and infiltration
EXTERNAL_INFLOW GLOBAL, NODE 20.0 [INFLOWS]
IFACE GLOBAL 20.0 Interface-file inflow
INITIAL_STATE GLOBAL 20.0 Water in the network, and on the mesh, at \(t = 0\)

TIMESERIES temperatures, SUBCATCH scope and EDGE_BC scope are refused with the phase that delivers them.

[HEAT_FLUXES] (Component section: [HEAT_FLUXES]) — module toggles, one row per module, ON or OFF (YES and NO accepted); no scope token.

Key Default Meaning
SURFACE_EXCHANGE OFF Latent and sensible exchange (§9.3.4, §9.3.5)
RADIATIVE_EXCHANGE OFF The four radiative terms (§9.3.6)
LAYER_CONDUCTION OFF LID layer conduction (§9.3.10)
SEDIMENT_EXCHANGE OFF The bed zone (§9.3.11)
DRY_ELEMENT_TEMPERATURE HOLD HOLD, AIR or DEFAULT: what a dry element reports — the last wet value, the air temperature, or 20 °C

[RADIATIVE_FLUXES] (Component section: [RADIATIVE_FLUXES])

Key Symbol Default Scope Meaning
SHORTWAVE \(J_{in}\) 0.0 GLOBAL only Incoming solar, W/m², as a constant; or TIMESERIES name; or COMPUTED (§9.3.7). The three spellings are mutually exclusive
ALBEDO \(R_s\) 0.0 GLOBAL, TAG, LINK, NODE Shortwave reflectance of the water surface, in \([0,1]\)
SHADE_FACTOR \(f_s\) 0.0 GLOBAL, TAG, LINK, NODE Fraction of insolation blocked; 0 = unshaded
SKY_VIEW \(f_{sky}\) 1.0 GLOBAL, TAG, LINK, NODE Fraction of the hemisphere that is sky
EMISS_WATER \(\varepsilon_w\) 0.97 GLOBAL, TAG, LINK, NODE Longwave emissivity of water
EMISS_LANDCOVER \(\varepsilon_{lc}\) 0.97 GLOBAL, TAG, LINK, NODE Longwave emissivity of the surroundings
LANDCOVER_TEMPERATURE \(T_{lc}\) air temperature GLOBAL, TAG, LINK, NODE Temperature of the surroundings radiating to the water, °C; unset means "use the air temperature"
ATM_EMISS_COEFF \(A_a\) 0.5 GLOBAL only Brunt clear-sky coefficient, in \([0,1]\)
ATM_LW_REFLECTION \(R_L\) 0.03 GLOBAL only Longwave reflectance of the surface, in \([0,1]\)

Fractions are refused outside \([0,1]\) rather than clamped — a 97 typed for an emissivity of 0.97 would otherwise scale every longwave term by a hundred.

[SOLAR_RADIATION] (Component section: [SOLAR_RADIATION]) — site geometry and the Bird–Hulstrom atmosphere, read only under SHORTWAVE GLOBAL COMPUTED; every row is GLOBAL.

Key Symbol Default Meaning
LATITUDE \(\phi\) required Degrees, +N, in \([-90, 90]\); an error if absent under COMPUTED. Not taken from the [TEMPERATURE] snowmelt line
LONGITUDE \(\lambda\) required Degrees, +E, in \([-180, 180]\); an error if absent under COMPUTED
TIMEZONE — 0 Hours from UTC, in \([-14, 14]\); absent means the deck clock is UTC, and the engine warns, because a wrong offset shifts the whole diurnal curve
ELEVATION \(z\) 0.0 Site elevation, m, in \([-500, 9000]\)
TURBIDITY_380 \(\tau_{380}\) 0.30 Aerosol optical depth at 380 nm
TURBIDITY_500 \(\tau_{500}\) 0.20 Aerosol optical depth at 500 nm
PRECIP_WATER \(w_p\) 1.42 Precipitable water vapour, cm
OZONE \(O_3\) 0.34 Ozone column, cm at NTP
GROUND_ALBEDO \(\rho_g\) 0.20 Surface albedo for the sky–ground reflection term, in \([0,1]\)

A [SOLAR_RADIATION] section in a model whose SHORTWAVE is not COMPUTED is harmless and warned about: the coordinates are read by nothing.

[CLOUD_COVER] (Component section: [CLOUD_COVER]) — every row is GLOBAL.

Key Symbol Default Meaning
FRACTION \(C\) 0.0 Cloud fraction in \([0,1]\), or TIMESERIES name; the series path clamps at run time, the constant is refused outside the range
SW_ATTEN_K \(k\) 0.75 Kasten–Czeplak shortwave attenuation coefficient
SW_ATTEN_N \(n\) 3.4 Kasten–Czeplak exponent
LW_CLOUD_K \(k_{lw}\) 0.17 Bolz longwave cloud coefficient

Coefficients without a FRACTION row leave the cloud fraction at zero, where both corrections are the identity; the engine says so. A [CLOUD_COVER] applied on top of a measured SHORTWAVE TIMESERIES attenuates the clouds twice and is warned about.

[SEDIMENT_EXCHANGE] (Component section: [SEDIMENT_EXCHANGE]) — the bed zone of §9.3.11, whose table already lists these keys. Scopes: GLOBAL, TAG and LINK for every attribute but INITIAL_TEMPERATURE, which is GLOBAL only; NODE is refused because the bed exists beneath conduits only.

Key Symbol Default Meaning
THERMAL_DIFFUSIVITY \(\alpha_{sed}\) 1.0e-6 Bed thermal diffusivity, m²/s; must be positive
SOLUTE_DIFFUSIVITY \(D_{sed}\) 1.0e-9 Bed effective solute diffusivity, m²/s; zero selects advection-only exchange
BED_THICKNESS \(Y_{bed}\) 0.20 Bed layer thickness, m; positive
GROUND_DEPTH \(Y_{gr}\) 2.0 Depth to the deep-ground boundary, m; positive
GROUND_TEMPERATURE \(T_{gr}\) 12.0 Deep-ground temperature, °C, or GLOBAL TIMESERIES name; the engine warns when it is left at the default
HYPORHEIC_VELOCITY \(v_{hyp}\) 0.0 Exchange velocity across the bed interface, m/s; non-negative, zero is conduction only
SEDIMENT_DENSITY \(\rho_s\) 1670 Bed bulk density, kg/m³; positive
SEDIMENT_SPECIFIC_HEAT \(c_s\) 1807 Bed specific heat, J/kg/K; positive
INITIAL_TEMPERATURE \(T_{bed,0}\) \(T_{gr}\) Initial bed temperature, °C; the ground temperature when unset

9.3.13 A worked configuration

A shaded urban stream reach with computed solar forcing, partial cloud, and both exchange modules active:

[PROCESS_COMPONENTS]
org.hydrocouple.openswmm.heat config="model.heat"
;; model.heat
[HEAT_SOURCES]
DWF GLOBAL 18.5
EXTERNAL_INFLOW GLOBAL 14.0
INITIAL_STATE GLOBAL 15.0
[HEAT_FLUXES]
SURFACE_EXCHANGE ON
RADIATIVE_EXCHANGE ON
[RADIATIVE_FLUXES]
SHORTWAVE GLOBAL COMPUTED
ALBEDO GLOBAL 0.06
SHADE_FACTOR GLOBAL 0.65
SKY_VIEW GLOBAL 0.35
EMISS_WATER GLOBAL 0.97
[SOLAR_RADIATION]
LATITUDE GLOBAL 41.88
LONGITUDE GLOBAL -87.63
TIMEZONE GLOBAL -6
ELEVATION GLOBAL 181.0
[CLOUD_COVER]
FRACTION GLOBAL 0.4

The [HEAT_FLUXES] rows are the one exception: they are <module> ON|OFF with no scope token, and the listing already has them right. With the per-element scopes of Chapter 11 §11.4 the same reach could shade one conduit differently — SHADE_FACTOR LINK C12 0.95 — while the rest of the network keeps the global value.

Note that SHADE_FACTOR 0.65 and SKY_VIEW 0.35 are consistent with one another — riparian canopy blocking roughly two-thirds of the sky both shades the shortwave and replaces sky longwave with canopy longwave. They are independent keys, and setting one without the other is a common way to produce a reach that is shaded from the sun but still radiating to an open sky.

9.4 Engine Coverage

Both reserved species are carried by all three transport engines:

LEGACY EULERIAN_ARD LAGRANGIAN
Water age yes yes yes
Temperature yes yes yes
Dispersion of either n/a yes yes
Surface + radiative fluxes yes yes yes
Bed zone (§9.3.11) per link per cell per link

The bed zone is resolved differently by engine. Under EULERIAN_ARD each mesh cell has its own bed slice, so a bed beneath a long conduit resolves the same along-length variation the water above it does. Under LEGACY and LAGRANGIAN the bed is one well-mixed body per conduit; the Lagrangian engine solves the exchange against the volume-weighted parcel mean and applies the resulting increment uniformly, which preserves the along-link profile the parcels carry.

Where dispersion is active, temperature and age are dispersed with the same coefficient as the solutes. In shear-dominated open-channel flow the dispersion coefficient is a property of the velocity field rather than of the transported quantity (§7.3.3), so applying a separate coefficient to heat would be less physical, not more conservative.

Temperature is exempt from three behaviours the other rows have, and each exemption is deliberate:

  • No aging. The \(+\Delta t\) advance is the age row's law alone.
  • No decay. First-order decay applies to pollutants, not to temperature.
  • No non-negativity clamp. Sub-zero °C is a legal state — 0 is not special for temperature the way it is for a mass, so the floor that keeps concentrations non-negative is not applied to the temperature row in any engine.

9.5 Implementation

Water age is implemented in src/engine/transport/components/WaterAgeModule/, heat in src/engine/transport/components/HeatModule/ (HeatComponent.cpp for the configuration, HeatLid.cpp for layer conduction), and the flux modules in HeatFluxModules/ — SurfaceExchange for the latent and sensible terms and the relaxation stepper, RadiativeExchange for the four radiative terms, SolarRadiation for the Spencer, Kasten–Young and Bird–Hulstrom computations, and BedExchange for the bed zone's conductances, its coupled two-body stepper and the two-body solute exchange (BedZoneData.hpp holds its configuration and state). Both reserved rows are allocated by the transport engines' species-row layout: ArdEngine.cpp for the Eulerian mesh, and rowLayout() in LagrangianSolver.hpp for the segment store.

C API surfaces are declared in openswmm_waterage.h and openswmm_heat.h; the transport-engine configuration itself is in openswmm_transport.h.