OpenSWMM Engine  6.0.0-alpha.4
Data-oriented, plugin-extensible SWMM Engine (6.0.0-alpha.4)
Loading...
Searching...
No Matches
HeatWatershed.hpp File Reference

Phase H5a — temperature on subcatchment surfaces. More...

This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Namespaces

namespace  openswmm
 
namespace  openswmm::runoff
 
namespace  openswmm::transport
 

Functions

void openswmm::transport::addRunonTemperature (SimulationContext &ctx, int donor_sc, int receiver_sc, double q)
 Accumulate run-on temperature from donor_sc onto receiver_sc.
 
void openswmm::transport::addRunonTemperatureAt (SimulationContext &ctx, int receiver_sc, double q, double temp_c)
 Accumulate run-on at an explicitly supplied temperature.
 
void openswmm::transport::routeSubcatchmentTemperature (SimulationContext &ctx, const runoff::RunoffSoA &soa, double dt)
 Advance ponded subarea temperatures one runoff step.
 

Detailed Description

Phase H5a — temperature on subcatchment surfaces.

This is A3's WaterAgeWatershed with the same complete-mix bookkeeping and three deliberate differences, which are the whole content of the file:

  1. No aging term. Age gains +dt every step because that is what age IS. Temperature gains nothing from the passage of time; it changes only by mixing and by flux.
  2. No zero floor. WaterAgeWatershed clamps with std::max(a, 0.0) because a negative age is meaningless. A negative temperature is ordinary — this is the same difference HeatLegacy makes against WaterAgeLegacy.
  3. A dry subarea is not zero. A4 writes 0 for a layer holding no water ("no water, no age"); 0 °C is a real temperature, so the answer is the deck's DryTempPolicy (plan D-H5c).
Why the surface energy balance is here and not in H6
The H5 plan line scoped "temperature states" but its verify criterion was a runoff temperature equilibration test, which advection alone cannot produce: without atmospheric exchange a ponded subarea holds the rain temperature forever. D-H5a resolved that by pulling the balance forward. The flux modules themselves are H2's and H3's, unchanged — only the binding is new.
The exchange area, and the trap in it
Heat crosses the free surface exactly where evaporation does (H2's precedent). On a subcatchment that surface is the ponded subarea: RunoffSoA::area × frac[k].

RunoffSoA::area is not ctx.subcatches.area, and the difference is bigger than it looks. Runoff.cpp:197-199 builds it as ctx.subcatches.area / ucf_area − total_lid_area_ft2: the SoA row is ft², the context row is in the deck's user area units — acres in US customary, a factor of 43560. The LID footprint is the second, far smaller correction. Substituting ctx.subcatches.area here was measured at 14.34 °C against 12.95 °C on a LID deck, with the ponded volume ledger collapsing from 27342 ft³ to 0.78 ft³.

Note what does NOT go wrong: a temperature is intensive, and the exchange area cancels exactly against the thermal mass in relaxT (it enters only through k = A·J′/(ρ cp V), and A / V ≡ 1 / depth_prev), so the flux term alone cannot see the substitution. It survives only through runon_depth_rate = runon_rate / area, which converts a run-on flow into the depth rate that weights it against rainfall. That is the one place the number has to be right, and it is not where the double-counting argument would look.

Clocks
This runs on the RUNOFF clock, immediately after runoff_.execute, because that is when the depths it reads are current. H2's node and link bindings run on the ROUTING clock inside routeLegacyHeat. Both are correct; the dt each passes is its own, and conflating them would scale every flux by the wrong interval.
See also
plans/transport/HEAT_TRANSPORT_PLAN.md §6 H5a, §6.1 D-H5a/D-H5c
src/engine/transport/components/WaterAgeModule/WaterAgeWatershed.hpp
Author
Caleb Buahin caleb.nosp@m..bua.nosp@m.hin@g.nosp@m.mail.nosp@m..com
License\n Apache-2.0