Issue 008 · September 18, 2026

Where the flood water goes: keeping the books between a pipe network and the street

A 1D storm sewer model has a line in its report called flooding loss. Water climbs out of a manhole, the model writes down the volume, and the water leaves the calculation. Put a 2D ground surface on top and that water has to go somewhere real, then come back in through the next grate. Every cubic foot now crosses the rim twice, and you need two ledgers that agree. Here is the bookkeeping on a test model, and the bug that broke the engine's ledger by 667 percent while the surface ledger balanced to four decimals.

TL;DR. EPA's sample Site Drainage model with every pipe narrowed 2.5 times floods 0.040 MG, about 5,350 ft³, on its own. Coupled to a 2D surface in StormSewer (free, open source, 0.10.0 this week), the surface receives 5,360 ft³ when the engine and the surface step together and 5,229 ft³ when the surface runs after the engine has finished. With the manhole lids open, 1,454 ft³ drains back in, against the engine's own 0.011 MG (about 1,470 ft³) of external inflow. The surface balance closes to 0.0000%. Two things decide whether the books close: where the rim is, which is not always the depth you typed, and never asking a node for water it does not hold.

The water a 1D model throws away

With ponding off, EPA SWMM caps the head at a node's rim and books anything above it as flooding. The report gives you a volume and a duration per node, and the continuity table subtracts it from the system as a loss. That is honest bookkeeping for a pipe model. It says "this much left the pipes". It does not say where the water went, whether it ran down the gutter to the next inlet, or whether it ponded on somebody's slab.

The test case is the Site Drainage sample that ships with EPA SWMM 5.2, with every conduit narrowed by a factor of 2.5 so it surcharges. The ground surface is a DEM interpolated through the manhole rims. On its own, the engine reports a flooding loss of 0.040 MG. That is 5,347 ft³, and because the report prints three decimals of a million gallons, it is good to about ±67 ft³.

Two ways to couple

Iterative. Run the engine to the end. Take the flooding at each node from the results file and put it on the 2D grid. Route it overland. Whatever flows into an open inlet goes back to the engine as an inflow time series, and you run the engine again. It works with any engine you have installed, because it only reads and writes files.

Tight. Step the engine and the surface together. Every exchange interval, both models stop at the same time. The engine says what each node is spilling and how full it is, the surface says how deep the water is over each inlet, and the exchange is settled before either one moves on. StormSewer does this through EPA's own swmm5.dll, unmodified, driven one step at a time.

The exchange

Capture at an inlet or an open manhole is a weir over the opening's perimeter $P$ while the water is shallow, capped by an orifice through its clear area $A$ once it is deep:

$$Q_{weir} = c\,\tfrac{2}{3}\sqrt{2g}\;P\,d^{3/2} \qquad Q_{orifice} = c\,A\sqrt{2g\,\Delta h} \qquad Q = \min(Q_{weir},\,Q_{orifice})$$

With $c = 0.6$, the weir constant $c\,\tfrac{2}{3}\sqrt{2g}$ is $3.21$ in US units. A 2 ft round lid has $P = 6.28$ ft and $A = 3.14$ ft². The volume $Q\,\Delta t$ is taken off the surface cell now, limited to what the cell holds, and that same volume goes to the node over the next interval. Capture conserves mass by construction, because the same number is subtracted on one side and added on the other.

Spill is the other direction, and it is where the engine gets a vote. When the engine reports flooding at a node, that water has already left its books, so it goes onto the surface exactly as reported, and nothing is pushed back into the engine.

The ledger

QuantityEngine aloneCoupledDifference
Water leaving the network, tight5,347 ft³5,360 ft³+0.2%
Water leaving the network, iterative5,347 ft³5,229 ft³−2.2%
Water returned through open lids, tight1,470 ft³1,454 ft³−1.1%
Surface mass balance—0.0000%

Tight coupling lands inside the report's own rounding on both lines. The engine said 0.040 MG left and 0.011 MG came back as external inflow, and the surface saw the same water leave and return. Iterative coupling is 118 ft³ short, which is more than the rounding explains. It reads the flooding back from the saved results instead of stepping with the engine, and I have not yet pinned down which part of that loses the water. It stays an open item until I have.

The rim is not the depth you typed

Every spill and every capture is measured against the rim, so the rim had better be the engine's rim. In SWMM that is not simply invert plus the MaxDepth you entered. When the engine validates the network, it raises each node's full depth to the crown of every conduit that connects to it (EPA SWMM 5.2, link.c). Storage units are left alone. A junction entered with MaxDepth 0, which the Site Drainage sample does at every junction, gets a rim at the top of its highest pipe. A junction entered 6 inches shallower than its biggest pipe's crown gets the crown.

Use the typed depth and your 2D model's manholes sit below the engine's. Water shows up on the street while the engine still thinks it is in the pipe, and your profile plot disagrees with the engine's at every structure where the two differ.

The bug that balanced perfectly

An early build of the tight coupling got the rim wrong. It compared the node head with the DEM ground elevation instead of the rim. With the ground at the inverts of those MaxDepth 0 junctions, every pipe carrying any flow looked surcharged, and the coupling pulled that "surcharge" out of the node as a negative inflow with no limit. The surface received exactly what was taken, so the 2D mass balance closed perfectly. The engine had been told to give up water it did not hold, and its flow-routing continuity error came back as −667%.

The fix is two rules. First, a node spills only when the head is above the engine's rim and above the water already on the ground. Second, the withdrawal in any one interval is capped at half the volume the engine reports the node holding. A test now runs that exact case against the real engine and fails the build unless the run is clean.

That is the lesson worth keeping. A coupled model has two ledgers, and each can close perfectly while the other is wrecked. Before you believe a flood map, read the pipe model's continuity table from the same run, not just the surface's.

What this does not prove

These are benchmark and self-consistency checks: the engine's own totals, still water staying still, Manning uniform flow, and the Ritter dam break. None of that is a real storm on a real site with high-water marks. The equations and the numbers each test asserts are in chapter 20b of the manual. If you have a site with a surveyed flood extent or high-water marks from a known storm, and the model to go with it, I would like to run it and publish the comparison, good or bad.

Check both ledgers yourself

StormSewer 0.10.0 opens your existing SWMM .inp files, runs them on EPA's engine, and adds 2D overland flow with 1D-2D coupling. It is free, it is GPL-licensed, and every window is documented. HydroComplete follows the same rule on the hydrology side: every formula on the page, with the numbers that went into it.

— Michael Flynn, PE
Next issue: automated watershed delineation, checked against three USGS gauges — and the snap-distance setting that quietly moves your outlet onto the wrong stream.

More from the Hydraulic Notebook

← Previous Issue 007 — Two programs, one pipe, and a 2.5 percent disagreement

← Issue 006 Your low-flow orifice is a weir half the time

← Issue 004 Manning's tells you Q. CFD tells you the rest

See also: Manning's equation calculator on pe-calc · Rational method calculator

Browse the full archive →

Get next issue in your inbox

One issue per month. Real design problems, real math. Free, unsubscribe in one click.

Free. Unsubscribe anytime. Privacy.