![]() |
OpenSWMM Engine
6.0.0-alpha.4
Data-oriented, plugin-extensible SWMM Engine (6.0.0-alpha.4)
|
Implicit acoustic/slot head update on the pressurized subset (slot program R2a, Strategy E). More...
#include <cstdint>#include <vector>#include "FvKernels.hpp"#include "INetworkSolver.hpp"#include "NetworkMeshData.hpp"Go to the source code of this file.
Classes | |
| struct | openswmm::fv::PressurizedView |
| class | openswmm::fv::PressurizedHeadSolver |
Namespaces | |
| namespace | openswmm |
| namespace | openswmm::fv |
Implicit acoustic/slot head update on the pressurized subset (slot program R2a, Strategy E).
Above the taper band the Preissmann-slot closure is linear in head (A = a_crown + t_slot·(h − y_full)), so the acoustic pair — slot storage T·∂H/∂t against the pressure gradient g·A·∂H/∂x — is a LINEAR diffusion system in H that can be integrated implicitly (θ = 1), unconditionally stably. Everything else stays exactly the explicit Godunov scheme: membership is a pure function of the instantaneous state (h ≥ y_crown, band entry — no flags, no memory) — EXCEPT under the TPA closure (issue #156), where membership IS the regime flag (physical air-pathway history, cleared on cold start and hotstart restore; see cellPressurized below — the SLOT-closure path above is untouched). The flag is fixed within a substep, so the SPD structure is unchanged either way — and the solve is a FLUX PREDICTOR whose back-substituted face discharges overwrite f_mass_, the single array both the cell update and the node update read — so conservation, rollback, hot start and reporting are structurally untouched.
Per implicit face (α = 1/(1 + Δt·γ_f), friction semi-implicit as everywhere else in the scheme):
Q_f^{n+1} = α·Q*_f − C_f·(H_b − H_a), C_f = α·Δt·g·Â_f/L_f
and eliminating Q from each pressurized cell's continuity gives
(T_i·Δx_i/Δt)·H_i + Σ_f C_f·(H_i − H_nbr) = RHS_i
— symmetric, diagonally dominant with strictly positive cell diagonals: SPD per pressurized component. Chains solve by the Thomas algorithm; components with a degree-≥3 folded junction by Jacobi-preconditioned CG.
Face taxonomy (re-derived every substep; memoryless under SLOT, flag-consistent under TPA via cellPressurized/ghostPressurized): FULL — both sides pressurized (cell–cell, or cell–node with a pressurized ghost). The Casulli-form flux above; at steady state it reduces to ΔH/L = −S_f exactly, which is what makes full-bore head loss independent of the slot width (gate G1). DELTA — exactly one side pressurized: the TRANSITION face. Stays FULLY explicit — Godunov mass and momentum flux, and the full explicit census bound. Bores live here, and a filling front advanced at the implicit Δt moves one cell per substep regardless of physics (measured: arrival 65 s / 345 s at c = 150 / 660 against the explicit 115 s). Keeping the front explicit costs dt only WHILE a front exists; a fully pressurized network has no transition faces and runs advection-bound, which is where the R2 runtime win lives. (Giving standing fronts their own fine tier is R2b's job.) (none) — gated, culvert-capped and closed-end faces are never implicit: their laws stand and enter the pressurized rows as explicit constants.
Pressurized ALGEBRAIC junctions are folded in as unknown rows (zero storage — today's algebraic convention — plus the face conductances), because lagging their heads would re-create the ghost-feedback stiffness this solver exists to delete. Fixed-head nodes and storage/demoted nodes enter as Dirichlet data at their current heads. A folded row whose solved head violates the node's physical ceiling (rim + surcharge depth) or floor (invert) is demoted to a Dirichlet row at the clamp and the affected components re-solved once — the flux imbalance then lands in the node's carry ledger, where settleAlgebraicNode already books flooding and ponding exactly as before.