![]() |
OpenSWMM Engine
6.0.0-alpha.4
Data-oriented, plugin-extensible SWMM Engine (6.0.0-alpha.4)
|
Knobs for FLOW_ROUTING FV.
More...
#include <FvOptions.hpp>
Public Attributes | |
| double | cell_length = 0.0 |
| int | min_cells = 4 |
| int | pressure_closure = 0 |
| int | unsteady_friction = 0 |
| 0 = NONE (default, bit-inert), 1 = VITKOVSKY. | |
| double | uf_k3 = 0.015 |
| Brunone-type k3 coefficient; consumed only when unsteady_friction != 0. | |
| double | cfl = 0.5 |
| RiemannSolver | riemann = RiemannSolver::HLLC |
| int | order = 1 |
| 1 | 2 (MUSCL-Hancock) | |
| Limiter | limiter = Limiter::MINMOD |
| ScalarScheme | scalar_scheme = ScalarScheme::MUSCL |
| TimeIntegration | time_integration = TimeIntegration::EULER |
| double | slot_celerity = 100.0 |
| bool | pressurized_implicit = false |
| StructureCoupling | structure_coupling = StructureCoupling::SUBSTEP |
| Backend | backend = Backend::AUTO |
| long | min_parallel_cells = 20000 |
| bool | compaction = true |
| bool | lts = true |
| bool | node_feedback_dt = false |
| int | lts_max_tiers = 6 |
| Maximum LTS tier count (tier k advances at 2^k·dt₀). 6 ⇒ 64× spread. | |
| int | cfl_census_interval = 1 |
| double | dispersion = 0.0 |
Knobs for FLOW_ROUTING FV.
Length-dimensioned members (cell_length, slot_celerity) are stored in INTERNAL units (feet, ft/s) — OptionsHandler converts from the project's display units on parse, exactly as HEAD_TOLERANCE is handled for DW.
| Backend openswmm::fv::FvOptions::backend = Backend::AUTO |
| double openswmm::fv::FvOptions::cell_length = 0.0 |
Target Δx. 0 ⇒ no length target, and each conduit gets min_cells.
0 ⇒ each conduit is cut into max(min_cells, ceil(L/cell_length)) cells. Internal units (ft).
| double openswmm::fv::FvOptions::cfl = 0.5 |
| int openswmm::fv::FvOptions::cfl_census_interval = 1 |
Recompute the global CFL min-reduction every k substeps instead of every substep. 1 = every substep (exact).
INERT BEFORE 2026-08-20. The accepted-substep tail wrote the step it had just taken into the census cache and forced the countdown to zero, so the pre-step census ran every substep for every value of k. The step taken and the Courant bound are now separate quantities (dt_cache_ vs dt_census_) and the countdown survives; a retry — the post-step census proving the bound inadmissible — still resets it. The default of 1 is bit-identical across the change.
NOT YET SWEPT. k > 1 throttles only the PRE-step census; the post-step acceptance census still runs every substep and is the safety net that makes skipping the pre-step one defensible at all. The ceiling on this option is therefore ~2× on census cost, not k×. Measure before raising the default (Phase 1, FV1D_PERF_PLAN_REVISED_2026-08-20.md).
| bool openswmm::fv::FvOptions::compaction = true |
Dry/inactive work-list compaction. Results-transparent by contract: a compacted run must reproduce the non-compacted run bit-for-bit on the same backend (§6.10).
| double openswmm::fv::FvOptions::dispersion = 0.0 |
Longitudinal dispersion coefficient (ft²/s). 0 disables the parabolic term entirely (advection only). Treated implicitly per D-FV1 so the Δx²/(2·D_L) constraint never binds.
| Limiter openswmm::fv::FvOptions::limiter = Limiter::MINMOD |
| bool openswmm::fv::FvOptions::lts = true |
Local time stepping — stiff cells (short Δx, pressurized) substep at their own dt while the rest advance at the macro step. When tiering finds nothing to separate the solver falls through to the global-dt path bit-for-bit, so this is on by default (plan §3.3).
| int openswmm::fv::FvOptions::lts_max_tiers = 6 |
Maximum LTS tier count (tier k advances at 2^k·dt₀). 6 ⇒ 64× spread.
| int openswmm::fv::FvOptions::min_cells = 4 |
Floor on cells per conduit. A FLOOR, not a FINE-mode detail: it applies with or without a cell_length target.
Four, not one. A conduit meshed as a single cell has no interior gradient of its own and presents an artificial bed step of half its fall at every manhole (§8.3), so it under-conveys and backs water up. Measured on Example1 — mean absolute peak-flow deviation from DYNWAVE, and wall-clock relative to one cell:
cells 1 2 4 8 deviation 37.1 % 25.7 % 15.3 % 7.6 % worst -75.8 % -58.1 % -43.2 % -22.6 % time 1.0x 1.4x 2.2x 5.4x
Four is the knee: it more than halves the one-cell error for about twice the cost, and convergence past it is slower than its price. One cell was the default and should not have been — it is a known-wrong setting, not a cheap-but-approximate one. Raise this (or set cell_length) where peak flows or in-conduit profiles matter.
| long openswmm::fv::FvOptions::min_parallel_cells = 20000 |
Small-problem gate: below this cell count AUTO stays on the CPU because per-kernel launch overhead dominates. Carried over from the 2D marcher's measured recalibration (plan §2 guardrails).
| bool openswmm::fv::FvOptions::node_feedback_dt = false |
Bound the explicit step by the ALGEBRAIC JUNCTION feedback limit. Opt-in: correct but costly (see the wall-time note below).
An algebraic junction has no volume state, so it carries no storage bound and was exempted from the step census on both paths. But its head is solved from the instantaneous flux balance and then handed to the incident cells as a ghost, so the neighbours integrate against a boundary state that can travel a long way inside one step. That feedback is explicit and has its own limit (algebraicNodeStableDt):
tau = min_i(0.5*dx_i*T_i) / sum_j(T_j*c_j)
min in the numerator, not sum: every incident conduit pushes flux (the sum), but the solved head must resolve on the TIGHTEST incident storage. Summing – what legacy DW does for its own node continuity – makes the bound LOOSER at exactly the junctions that need it (a 12 ft barrel meeting a 3 ft one gives a 2123 ft effective length against a 250 ft cell). Gated on the junction being pressurized, so open-channel networks pay nothing.
Relative to the cell bound this is 1/(2n) * (T_min/T_max) – purely geometric: 4x for two equal barrels, 32x across a 16:1 area step. Those are the factors the EPA QA decks were measured to need, and they are CELERITY-INVARIANT (T ~ 1/c^2 cancels), which is why lowering FV_SLOT_CELERITY suppresses the symptom without fixing the bound.
The constraint is applied to the junction's INCIDENT CELLS in assignTiers, not to dt_node: a plain junction integrates no state and is pinned to its finest incident cell, so routing it through dt_node discards the tier while still dragging dt0 down. On the cells it is an ordinary local CFL number and tiering localises it normally.
Measured at stock FV_CFL 0.5 with LTS on (zigzag = total variation / range of the worst link, against dynamic wave): test5 37.95 -> 4.97 (DW 5.08), peak Q 1.06x -> 1.01x, 8.5 s -> 121 s test2 52.01 -> 2.99 (DW 6.87), peak Q 11.9x -> 0.99x, 1.0 s -> 9.7 s It also closes an accuracy hole unrelated to oscillation: on the culvert fixture the un-tiered path floods 0.885 acre-ft against DW's 0.003, and 0.001 with this on.
Default OFF because of that 10-14x wall cost on pressurized networks. The cost is the bound doing its job – a stiff junction genuinely needs a small step – but it is the user's call whether to pay it. It does NOT disable tiering (an earlier revision did): LTS still localises the cost, and turning LTS off instead costs 376 s / 38 s on the same decks.
| int openswmm::fv::FvOptions::order = 1 |
1 | 2 (MUSCL-Hancock)
| int openswmm::fv::FvOptions::pressure_closure = 0 |
[OPTIONS] FV_PRESSURE_CLOSURE SLOT|TPA. 0 = SLOT (default, parity-preserving), 1 = TPA (Vasconcelos/Wright/Roe 2006): a per-cell regime flag extends the slot line to ΔA < 0 (sub-atmospheric full-pipe flow), with venting governed by atmosphere contact. FV_SLOT_CELERITY doubles as the TPA acoustic celerity a (same physical dial). Plan: plans/TPA_TWO_COMPONENT_PRESSURE_PLAN.md + MIXED_FLOW §2.2.
| bool openswmm::fv::FvOptions::pressurized_implicit = false |
Integrate the acoustic/slot pair implicitly on the pressurized subset (slot program R2a, Strategy E). Above the taper band the closure is linear in head, so cells at/above band entry solve an SPD head system per substep (Thomas on chains, Jacobi-CG past folded junctions) whose back-substituted face discharges overwrite f_mass_ — a pure flux predictor: conservation, rollback and hot start are untouched, and a run that never pressurizes is bit-identical with the option on.
With it, pressurized cells are advection-bound in the step census — FV_SLOT_CELERITY leaves the dt law entirely, making the slot width a pure accuracy parameter (a narrow slot no longer costs runtime). Default OFF while the R2 gates land; the R4 default flip is a separate, deliberate commit.
| RiemannSolver openswmm::fv::FvOptions::riemann = RiemannSolver::HLLC |
| ScalarScheme openswmm::fv::FvOptions::scalar_scheme = ScalarScheme::MUSCL |
| double openswmm::fv::FvOptions::slot_celerity = 100.0 |
Pressurized wave speed (ft/s internal). Sets the Preissmann slot top width through T_slot = g·A_full/c², so it is a direct accuracy/cost dial: physical acoustic speeds would crush the global CFL step. Default 100 ft/s is the same order DW's SLOT surcharge method produces.
| StructureCoupling openswmm::fv::FvOptions::structure_coupling = StructureCoupling::SUBSTEP |
| TimeIntegration openswmm::fv::FvOptions::time_integration = TimeIntegration::EULER |
| double openswmm::fv::FvOptions::uf_k3 = 0.015 |
Brunone-type k3 coefficient; consumed only when unsteady_friction != 0.
| int openswmm::fv::FvOptions::unsteady_friction = 0 |
0 = NONE (default, bit-inert), 1 = VITKOVSKY.