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

Plan H6b — bed conduction, deep-ground conduction, and hyporheic exchange, integrated SIMULTANEOUSLY with the surface fluxes. More...

Include dependency graph for BedExchange.hpp:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Classes

struct  openswmm::transport::heat::BedCoupling
 One element's bed coupling, fully reduced to SI conductances. More...
 
struct  openswmm::transport::heat::PairStep
 Simultaneous temperature increments for the coupled pair, °C. More...
 
struct  openswmm::transport::heat::SolutePairStep
 Simultaneous concentration increments for the coupled pair. More...
 

Namespaces

namespace  openswmm
 
namespace  openswmm::transport
 
namespace  openswmm::transport::heat
 

Functions

PairStep openswmm::transport::heat::relaxPair (const BedCoupling &g, double t_w, double t_b, double j0, double j1, double h, double area_m2, double dt) noexcept
 One exact step of the coupled (water, bed) pair.
 
SolutePairStep openswmm::transport::heat::exchangePair (double c_w, double c_b, double vol_w, double vol_b, double q_exch, double dt) noexcept
 One exact step of the two-body solute exchange.
 
BedCoupling openswmm::transport::heat::bedCouplingForLink (const SimulationContext &ctx, int link, double vol_ft3, double t_gr) noexcept
 Bed conductances for link j from the deck configuration and the link's current hydraulics.
 
BedCoupling openswmm::transport::heat::bedCouplingFromContact (const SimulationContext &ctx, const SedimentConfig &sc, double bed_m2, double vol_ft3, double t_gr) noexcept
 The material half of the coupling, geometry already folded in.
 
double openswmm::transport::heat::groundTempFor (const SimulationContext &ctx, const SedimentConfig &sc, double t_gr_global) noexcept
 
double openswmm::transport::heat::bedExchangeQ (const SedimentConfig &cfg, double bed_m2) noexcept
 
double openswmm::transport::heat::linkBedAreaM2 (const SimulationContext &ctx, int link) noexcept
 
double openswmm::transport::heat::linkFreeSurfaceFt2 (const SimulationContext &ctx, int link) noexcept
 
double openswmm::transport::heat::groundTemperature (SimulationContext &ctx) noexcept
 
bool openswmm::transport::heat::bedExchangeEnabled (const SimulationContext &ctx) noexcept
 True when [HEAT_FLUXES] SEDIMENT_EXCHANGE is on and heat transport is.
 
void openswmm::transport::heat::seedBedTemperature (SimulationContext &ctx)
 Size and seed the bed temperature array, idempotently.
 
void openswmm::transport::heat::applyBedSoluteExchange (SimulationContext &ctx, double *link_conc, int n_arr, int offset, int n_total, double dt)
 Step every conduit's bed/channel solute exchange for one dt.
 

Detailed Description

Plan H6b — bed conduction, deep-ground conduction, and hyporheic exchange, integrated SIMULTANEOUSLY with the surface fluxes.

Why this is not "one more term in netFluxOut"
HeatFluxes.hpp predicted that H6b would be exactly that. It cannot be, for two independent reasons, and both were found by trying:
  1. netFluxOut returns W/m² at the FREE SURFACE. Bed exchange acts on the wetted perimeter, which is a different area — and for a closed conduit the free-surface area is zero while the bed area is not. Summing them would multiply the bed flux by the wrong area, and would switch it off entirely in a full pipe.
  2. The bed is a SECOND STATE VARIABLE. relaxT relaxes one body toward a fixed equilibrium. Two bodies exchanging with each other do not have a fixed equilibrium to relax toward: each one's target moves as the other responds. Treating the bed as a constant- temperature reservoir inside netFluxOut would make a thin bed behave like an infinite heat sink, which is the opposite of what a thin bed does.

So H6b adds a coupled stepper rather than a term. D-H5e's rule still holds and is why the stepper is coupled rather than sequential: relaxations do not commute, so the surface families and the bed exchange are solved in ONE step, not one after the other. Doing it sequentially would reintroduce precisely the defect D-H5e removed, one phase after it was removed.

The system
With ‘J(T_w) ~ J0 + J’(T_w - T_w0)linearized exactly asrelaxT already linearizes it: @code C_w dT_w/dt = -A_s J(T_w) + G_wb (T_b - T_w) C_b dT_b/dt = G_wb (T_w - T_b) + G_bg (T_gr - T_b) @endcode a 2x2 linear systemdu/dt = M u + csolved by its exact matrix exponential,u(dt) = dt * phi1(M dt) * c,phi1(z) = expm1(z)/z`.

Both eigenvalues of M are real and non-positive — the discriminant is (a11-a22)^2 + 4 a12 a21 with a12, a21 >= 0, so it can never be negative and the pair can never oscillate. The step therefore cannot overshoot at any dt, which is the property D-H5d bought for the single body and which would have been lost by any explicit coupling.

Conservation, and the gate that checks it
Adding the two equations, G_wb cancels identically: C_w dT_w/dt + C_b dT_b/dt = -A_s J + G_bg (T_gr - T_b). Whatever the water loses to the bed, the bed gains. The exchange is therefore conservative BY CONSTRUCTION, not by a bookkeeping step that could be forgotten, and BedExchangeConservesEnergy asserts the identity rather than a tolerance on a ledger.
Solutes
The solute pair is the same system minus the surface flux and minus the ground term — HTSComponent leaves its ground solute exchange commented out (element.cpp:161) and parity keeps it out. Two bodies exchanging with nothing else has a closed form that needs no matrix: the total mass is invariant and the DIFFERENCE decays exponentially. exchangePair uses that form, so conservation is STRUCTURAL — there is no separate bookkeeping step that could be omitted.

It is not bit-exact, and the first draft of this file claimed it was. vol_w * dc_w evaluates vol_w * (vol_b * share) while vol_b * dc_b evaluates vol_b * (vol_w * share) — the same factors in a different association, which round differently once share saturates at large dt. The residual is relative round-off, ~1e-16, and the gate asserts that bound rather than equality. Measured, not assumed: a driver over dt in {1, 600, 7200, 1e6} agreed bit for bit on the first three and differed on the fourth.

See also
plans/transport/HEAT_TRANSPORT_PLAN.md §2.3, §6.5 D-H6b
HTSComponent/src/element.cpp:121-200 (the reference)
Author
Caleb Buahin caleb.nosp@m..bua.nosp@m.hin@g.nosp@m.mail.nosp@m..com
License\n Apache-2.0