Mass balance#
Note
Engine: OpenSWMM 6 — refactored. Documents
openswmm.engine.MassBalance.
The MassBalance class exposes the engine’s continuity
diagnostics:
Continuity error — percentage discrepancy in the runoff, routing, and quality budgets.
Component totals — per-component breakdown of inflow / outflow / storage.
Numerical health — peak Courant number for the routing solver.
Use this class at end-of-simulation to verify the run was numerically well-behaved before trusting downstream analysis. A runoff or routing continuity error larger than ±2% almost always indicates a configuration or numerical-stability issue.
Reference: openswmm_massbalance.h.
Class signature#
class MassBalance:
def __init__(self, solver: Solver) -> None: ...
Key methods#
Continuity errors (percent)#
Method |
Returns |
|---|---|
|
Surface runoff continuity error. |
|
Pipe-network routing continuity error. |
|
Quality continuity error for pollutant |
Component totals#
Method |
Returns |
|---|---|
|
Cumulative runoff component (precip / evap / infil / runoff). |
|
Cumulative routing component (DWF / WW / GW / RDII / external / flooding / outflow / evap / seep / storage). |
|
All routing component totals as a dict. |
The component selectors are RunoffTotal and
RoutingTotal enums.
Numerical health#
Method |
Returns |
|---|---|
|
Peak Courant number observed during routing. |
|
Cumulative pollutant |
|
Cumulative pollutant |
End-to-end example#
from openswmm.engine import Solver, MassBalance, EngineState
with Solver("model.inp", "model.rpt", "model.out") as s:
while s.state == EngineState.RUNNING:
if s.step() != 0:
break
mb = MassBalance(s)
print(f" runoff continuity error = {mb.get_runoff_continuity_error():+.4f}%")
print(f" routing continuity error = {mb.get_routing_continuity_error():+.4f}%")
print(f" peak Courant number = {mb.get_max_courant():.3f}")
if abs(mb.get_routing_continuity_error()) > 2.0:
print(" WARNING: routing continuity error > ±2% — review inputs.")
Common recipes#
Itemise routing totals#
stats = mb.get_routing_stats()
for component, value in sorted(stats.items()):
print(f" {component:<28} {value:>14.3f}")
Pollutant continuity sweep#
from openswmm.engine import Pollutants
polls = Pollutants(s)
for i in range(polls.count()):
err = mb.get_quality_continuity_error(i)
seep = mb.get_quality_seep_loss(i)
evap = mb.get_quality_evap_loss(i)
print(f" {polls.get_id(i):<10} err={err:+.4f}% seep={seep:.3f} evap={evap:.3f}")
Numerical-stability check (CI gate)#
def assert_continuity_ok(s):
mb = MassBalance(s)
assert abs(mb.get_runoff_continuity_error()) < 0.5
assert abs(mb.get_routing_continuity_error()) < 1.0
assert mb.get_max_courant() < 5.0
EngineState requirements & exceptions#
Method |
Required state |
Notes |
|---|---|---|
all accessors |
|
Mid-run values are valid but reflect the partial run; final
values are only meaningful after |
quality continuity / loss |
|
Raises |
The MassBalance constructor itself never fails — it just
binds to a Solver. Errors surface from the accessors.
See also#
Statistics — per-element peak / cumulative statistics (peak depth, surcharge time, etc.).
Running a simulation — Solver —
Solver.report()writes a human-readable summary that includes these continuity errors.