One discharge per pipe, or a mesh inside it
Chapter 3 of the OpenSWMM hydraulics manual says plainly that more powerful techniques exist — shock-capturing finite volume among them — and that SWMM 5 continues to use EXTRAN’s node-link approach anyway. On the develop branch there is now a second answer sitting beside the first: FLOW_ROUTING FV, a Godunov-type explicit finite-volume solver, 5,476 lines of C++ under src/engine/hydraulics/fv/ and 1,355 lines of reference manual behind it.
It supplements dynamic wave analysis rather than replacing it. Dynamic wave stays the default and stays the right choice for design storms, continuous simulation and planning. The finite-volume solver exists for the three places where the node-link formulation is structurally, not incidentally, limited.
Conservation
The dynamic wave momentum equation is written in non-conservative form. Volume is tracked well but not exactly, and routing continuity error is a diagnostic you are expected to watch. In conservation form, the flux leaving one control volume is the number entering the next. Measured here: 7×10−16 relative volume change over 491 substeps.
In-conduit resolution
With one discharge per conduit, a backwater profile or a bore inside a pipe has nowhere to live. The mesh subdivides each conduit — four cells by default — so the flow field within a reach is resolved.
Discontinuities
A pressurization front, a hydraulic jump, a transcritical transition: these are genuine discontinuities whose speed is set by Rankine–Hugoniot, which only a conservative scheme reproduces. Dynamic wave handles transcritical flow by suppressing it, through inertial damping and normal-flow limiting.
What was measured to build this page
The kernels below were ported to JavaScript from the branch and run against the same analytic cases the repository’s own test suite uses. Every number on this page came out of that run, not out of the documentation.
51 gates green The suite runs on load and checks these numbers, the physics identities and every embedded code block against its source. Open the console to see it; the count above is asserted by the last gate, which counts itself.
Ported: areaOfDepth, widthOfDepth, i1OfDepth, depthOfArea, celerity, waveSpeeds, physicalFlux, riemannFlux, speciesFlux, frictionUpdate, localLossUpdate, faceCflDt, positivityScale, the hydrostatic reconstruction of faceSide, the Audusse corrections and side signs of computeFaceFlux / updateCells, and the donor-side rule of limitPositivity. The engine’s tabulated cross-section evaluator is replaced by analytic geometry; everything else follows the source line for line.
What this is not
It is not a benchmark, and it is not the engine. The C++ solver couples to nodes, structures, culvert inlet control, flap gates, scalar transport, local time stepping and a device backend — none of which is reproduced here. What runs in this page is the scheme: closure, reconstruction, flux, sources, update, on a single reach with reflecting ends. Where a number came from the manual rather than from that run, it says so.
It is also a reading of one commit. develop moves; check the SHA in the Pipeline & code tab before quoting anything here.
Section
A(h), T(h) and the celerity that sets your time step
The slot opens over the band through φ(s) = s²(3 − 2s), which is C¹ at both ends. A discontinuous dA/dh at the crown would produce spurious reflections there and corrupt the Riemann solver’s wave-speed estimates; the taper is what prevents both. The area is the exact integral of the width, so A and T stay a consistent pair through the transition.
Why the inverse is built from the forward closure
The solver carries A and needs h. It does not use SWMM’s Y-tables to get there. SWMM’s geometry tables are independent tabulations — A_Circ gives area from depth, Y_Circ gives depth from area — and they round-trip only to table resolution, measured at 0.016 ft on a 3-ft pipe near the crown. Harmless for dynamic wave, which never composes them. Fatal here: cells at equal free surface but different bed elevation would reconstruct different surfaces, and still water would fail on every partly-full pipe.
Left cell
Right cell
The face
Reconstruction (8-8, 8-9)
Flux (8-11, 8-20 to 8-23)
The wave fan the estimates bound
SL and SR are the Davis (1988) estimates when both sides are wet. A dry side carries no celerity of its own, so the wet side’s rarefaction-tail speed v ∓ 2c bounds the front — the standard dry-bed estimate that keeps the computed front speed correct. No entropy fix is applied anywhere: the Davis estimates already bound the full fan, so a sonic rarefaction cannot collapse onto a single-state flux. S* is not used by the hydraulics at all — it selects the upwind side for an advected scalar, which is the only place a contact wave exists in this system.
Profile
The Courant census — and the ghost that dominates it
Local time stepping
A face fires at the finer of its two sides and books ±F·Δt into both incident volumes’ accumulators; a volume drains what has accumulated when it fires. What leaves a fine cell is bit-for-bit what arrives in its coarse neighbour. Faces of tier ≤ ctz(s) fire at base substep s; volumes of tier ≤ ctz(s+1) close — at the end of their windows, so the flux they drain and the sources they integrate cover the same span.
Steps are re-checked after they are taken
A cell can cross the crown inside a substep, taking its celerity from the free-surface value to the slot value. A step sized on the pre-step state is then far too large, precisely when the model is doing something interesting. So the solver re-runs the census on the state the substep produced and accepts the step only if the new stable limit is at least half the step just taken.
On rejection the full prognostic state is rolled back. Every input to that decision is solver state, so the retry sequence is deterministic — the same model re-run takes the same steps in the same order. Tiers are graded: no face may span more than one tier level, which is the standard admissibility condition and what makes a filling bore cross a length transition correctly.
One global substep, in order
Under FV_TIME_INTEGRATION RK2 steps 5–12 run twice at the same Δt and the two results are averaged, through volume at nodes — the operator is averaged rather than split, so the semi-implicit friction and the positivity limiter stay inside each stage where their stability arguments hold. Under local time stepping the same face, cell and node passes are issued per tier from the macro-cycle schedule.
Provenance
The minimum
[OPTIONS] FLOW_ROUTING FV
That is a complete, valid finite-volume run. Every conduit gets four cells, the slot is sized for a 100 ft/s pressurized celerity, the Courant number is 0.5, local time stepping and work-list compaction are on, and the node coupling is semi-implicit. Nothing else is required of the file.
The four that decide whether it is any good
| Key | Default | Why you would touch it |
|---|---|---|
| FV_CELL_LENGTH | 0 | The one to set. 0 means no Δx target, so every conduit gets exactly FV_MIN_CELLS cells regardless of how long it is. Set a real Δx whenever peak flows or in-conduit profiles matter. |
| FV_MIN_CELLS | 4 | A floor, applied with or without a Δx target. At four cells the manual measures 15.3 % mean peak attenuation on Example1, against 37.1 % at one cell and 7.6 % at eight. The cause is geometric rather than diffusive — a single cell puts the conduit’s bed at its mid-point elevation, an artificial step of half the fall at every manhole — so FV_ORDER 2 will not rescue an unresolved mesh. |
| FV_SLOT_CELERITY | 100 | Sets the slot width through Tslot = g·Afull/c², capped at 5 % of the section width. A direct accuracy/cost dial: the explicit step is bounded by Δx/(|v| + c), so a physical acoustic celerity of 1000+ ft/s would collapse it. The default is the same order SURCHARGE_METHOD SLOT produces under dynamic wave. |
| FV_CFL | 0.5 | The Courant number α. Rarely needs moving. |
units FV_CELL_LENGTH and FV_SLOT_CELERITY are read in project display units — the parser converts to internal feet on the way in, exactly as HEAD_TOLERANCE is handled for dynamic wave.
Mesh & cost planner
What that mesh costs you
The rest, grouped
Scheme
FV_ORDER (1), FV_LIMITER (MINMOD), FV_TIME_INTEGRATION (EULER), FV_RIEMANN (HLLC).
Order 2 is safe to leave on: it reconstructs (η, v) rather than (h, Q), so a flat surface has exactly zero slope and it degenerates to first order at rest. Measured here on Ritter it nearly halves the error, 3.295 % to 1.765 %, and changes still water by nothing at all. FV_RIEMANN affects transport only — hydraulics are bit-identical either way.
Node coupling
FV_NODE_COUPLING (SEMI_IMPLICIT), FV_NODE_PICARD (1), FV_NODE_CELL_COUPLING (NO), FV_NODE_DT (STABILITY).
Leave these alone unless you are chasing performance — and the manual argues against doing so. FV_NODE_DT NONE is 24–134× faster and degrades L1 depth error by factors of 3 to 49 on the frictional and transcritical SWASHES cases. Extra Picard sweeps do not substitute for the bound: they converge the node’s continuity equation at the step it is handed, which is a separate question from that step being small enough.
Performance
FV_LTS (YES), FV_LTS_MAX_TIERS (6), FV_COMPACTION (YES), FV_CFL_CENSUS_INTERVAL (1), FV_BACKEND (AUTO), FV_MIN_PARALLEL_CELLS (20000), FV_STRUCTURE_COUPLING (SUBSTEP).
Both FV_LTS and FV_COMPACTION are results-transparent by contract: where tiering finds nothing to separate the solver reproduces FV_LTS NO to the last bit, and every face compaction skips is one the full evaluation would give an exactly zero flux.
Transport
FV_SCALAR_SCHEME (MUSCL), FV_DISPERSION (0).
FV_DISPERSION is accepted but inert, and a non-zero value warns at open, until finite-volume transport is connected. Do not read a number there as doing anything yet.
What the model itself has to supply
None of this is an option key, and all of it bites at initialization rather than at run time.
- Every conduit needs real cross-section geometry. A control volume requires a section; a conduit whose full depth or area is zero is rejected at initialization rather than silently routed.
- No conduit loop closed entirely by virtual junctions. Such a cycle has no boundary and cannot be meshed. An ordinary junction breaks it and is accepted.
- Hot starts are not interchangeable between routing models. A dynamic wave hot start projects onto the mesh with a warning; the reverse discards in-conduit structure.
What it inherits from parameters you have already set
The solver meshes the Courant-lengthened mod_length, and uses the same adjusted bed slope and roughness, so LENGTHENING_STEP acts as a Δx floor for short pipes exactly as it acts as a length floor for dynamic wave.
It matters more here than you would expect. A junction, outfall or divider is a linear reservoir of area Vfull/yfull — that is, the MIN_SURFAREA floor unless a pump wet well overrode it. As an effective length As/T that is a few feet against a conduit Δx of several hundred, which is why the manhole rather than the pipe sets the explicit substep, and why the node coupling is semi-implicit by default.
The solver substeps internally to fill each routing step, so ROUTING_STEP is a reporting and forcing cadence rather than a stability constraint. Link discharge is published as the length-weighted time mean over the step, not an end-of-step sample, because a sample aliases badly against hundreds of substeps.
Manning’s n, entrance and exit loss coefficients, SURCHARGE_DEPTH, ponded area, storage curves, outfall types (free / normal / fixed / tidal / time series), culvert codes in [XSECTIONS] with FHWA inlet control, flap gates, force-main friction (Hazen–Williams or Darcy–Weisbach, chosen exactly as dynamic wave chooses), evaporation and seepage, lateral inflows, pumps, orifices, weirs, outlets, control rules, and [VIRTUAL_JUNCTIONS]. A model floods and ponds the same way under both solvers.
One portability warning
The FV_ keys are inert rather than rejected under KINWAVE, STEADY and DYNWAVE, so switching FLOW_ROUTING never invalidates the file within OpenSWMM.
Handing that same file to stock EPA SWMM 5.2.4 is a different matter. verified separately 5.2.4 dies with a fatal ERROR 205 on the SWMM6 [OPTIONS] keywords, and the same should be expected of FV_ — though that specific case has not been run. Keep a stock-compatible copy if the file has to travel.
Build an [OPTIONS] block
Tick the keys you want to state explicitly. Defaults are shown; anything unticked is left to the engine.
Findings ledger
Everything measured or caught while building this page. Verified means it came out of a run of the ported kernels; discrepancy means two parts of the repository disagree and the source was taken as the truth.
Choosing between them
Measured in the manual on the EPA reference site drainage model (Example1.inp, 30 h, 5 s routing step), each finite-volume configuration against the dynamic wave run of the same file. These are the manual’s numbers, not this page’s.
| Dynamic wave | FV, 1 cell | FV, default (4) | FV, Δx = 50 ft | FV, Δx = 20 ft | |
|---|---|---|---|---|---|
| Routing continuity error | 0.026 % | 0.000 % | 0.000 % | 0.000 % | 0.000 % |
| Mean absolute peak-flow deviation | — | 37.1 % | 15.3 % | 12.8 % | 7.2 % |
| Wall-clock, relative | 1× | ~7× | ~15× | ~12× | ~34× |
The deviation row is a consistency check, not an error. Dynamic wave routing is not the reference truth, and part of the 37 % at one cell is its own departure from the correct answer. The cause is geometric rather than diffusive — a cell-centred scheme puts a single cell’s bed at the conduit’s mid-point elevation, presenting an artificial bed step of half the conduit’s fall at every manhole — so second-order reconstruction does not remove it. Refine the mesh instead.
The ratio does not improve with network size. Across uniform and graded reaches of 50 / 500 / 2000 conduits the manual reports 117 / 252 / 189× on uniform reaches and 39 / 69 / 49× on graded ones — flat to erratic, so the cost should not be expected to amortize on larger models. The graded ratios are three to four times better not because the explicit solver got faster but because the implicit solver’s own step collapses there, from 5.00 s to 1.14 s. Steep, graded, stiff networks are where the trade is most favourable.
Limitations, as the manual states them
- Sub-atmospheric pressure cannot be represented. The slot closure is monotone — head below the crown means a free surface — so negative pipe pressures have no representation. Air-phase effects are out of scope. Same fidelity limit the dynamic wave slot carries.
- Junction momentum is not conserved, by choice. At a general junction one-dimensional momentum conservation is ill-posed because momentum is a vector and the pipe axes do not align. The standard stagnation-volume closure applies: velocity zero in the node, hydrostatic pressure, one shared piezometric head. Mass is conserved exactly.
- A conduit loop closed entirely by virtual junctions cannot be meshed, because such a cycle has no boundary. Ordinary junctions break the cycle and are accepted.
- Every conduit must carry real cross-section geometry. A conduit whose full depth or area is zero is rejected at initialization rather than silently routed.
- Hot start files are not interchangeable between routing models. A dynamic wave hot start projects onto the mesh with a warning; the reverse discards in-conduit structure.