OpenSWMM Engine  6.0.0-alpha.3
Data-oriented, plugin-extensible SWMM Engine (6.0.0-alpha.3)
Loading...
Searching...
No Matches
openswmm::twoD Namespace Reference

Namespaces

namespace  gpu
 

Classes

struct  ActiveSetData
 
struct  BoundaryData
 SoA storage for per-edge boundary conditions. More...
 
struct  CouplingPoint
 Descriptor for a single coupling point between 2D and 1D. More...
 
class  HypreAmgPreconditioner
 BoomerAMG preconditioner over the 2D diffusion Newton matrix. More...
 
struct  InertialEdges
 Canonical unique interior-edge layout + per-cell incidence for the local-inertial scheme. More...
 
class  ISurfaceSolver
 Abstract time integrator for the 2D surface-routing ODE system. More...
 
struct  MeshData
 SoA storage for 2D triangular mesh geometry and topology. More...
 
struct  PendingBoundaryRow
 Per-row buffer for [2D_BOUNDARY_CONDITIONS] parse output. More...
 
struct  PendingEdgeConveyanceRow
 Per-row buffer for [2D_EDGE_CONVEYANCE] parse output (§11A). More...
 
class  RainfallInterpolator
 Builds and applies static rainfall-interpolation weights for the mesh. More...
 
struct  SolverOptions2D
 Configuration for the 2D surface routing CVODE solver. More...
 
class  SurfaceJacobian
 Assembles M = I − γ·J (diffusion stencil) in CSR for AMG. More...
 
class  SurfaceRouter2D
 Top-level orchestrator for the 2D surface routing module. More...
 
struct  SurfaceStateData
 

Enumerations

enum class  BoundaryType : int8_t {
  WALL = 0 ,
  NORMAL_FLOW = 1 ,
  SPECIFIED_STAGE = 2 ,
  SPECIFIED_FLOW = 3 ,
  RATING_CURVE = 4
}
 Boundary condition types for 2D mesh edges. More...
 
enum class  LinearSolverType : int8_t {
  GMRES = 0 ,
  BICGSTAB = 1 ,
  TFQMR = 2
}
 Krylov linear solver selector for the BDF + Newton + Krylov stack. More...
 
enum class  PreconditionerType : int8_t {
  NONE = 0 ,
  JACOBI = 1 ,
  ILU = 2 ,
  AMG = 3
}
 Preconditioner selector for the Krylov inner solver. More...
 
enum class  IntegratorType : int8_t {
  CVODE = 0 ,
  ARKODE = 1
}
 Time-integrator selector for the 2D surface ODE. More...
 
enum class  MomentumType : int8_t {
  DW = 0 ,
  INERTIAL = 1
}
 Surface-momentum closure for the 2D flux. More...
 
enum class  RainfallMode : int8_t {
  NATURAL_NEIGHBOUR = 0 ,
  SYSTEM = 1 ,
  NONE = 2
}
 How raingage rainfall is mapped onto the 2D mesh cells. More...
 

Functions

double computeNodeCouplingQ (const CouplingPoint &cp, const MeshData &mesh, const SurfaceStateData &state, const NodeData &nodes, const SolverOptions2D &opts) noexcept
 Live node-coupling orifice flux for ONE non-outfall coupling point.
 
void scatterCouplingToYdot (const MeshData &mesh, const SurfaceStateData &state, const CouplingPoint &cp, double Q, double *ydot) noexcept
 Scatter a signed volumetric exchange Q (m³/s) directly onto the cell derivatives ydot[] of the 2D volume ODE (for the live-RHS path).
 
std::vector< CouplingPointbuildCouplingPoints (const MeshData &mesh, const SimulationContext &ctx)
 Build the list of coupling points from mesh coupling maps.
 
void computeCouplingExchange (const std::vector< CouplingPoint > &cps, const MeshData &mesh, SurfaceStateData &state, SimulationContext &ctx, const SolverOptions2D &opts, double dt)
 Compute exchange flows at all coupling points and inject into forcing API.
 
void updateOutfallBoundaries (const std::vector< CouplingPoint > &cps, const MeshData &mesh, const SurfaceStateData &state, SimulationContext &ctx, const SolverOptions2D &opts)
 Update outfall boundary depths from 2D surface heads.
 
int transferOutfallDischarges (const std::vector< CouplingPoint > &cps, const MeshData &mesh, SurfaceStateData &state, const SimulationContext &ctx, const SolverOptions2D &opts, double dt, std::unordered_map< int, double > &applied_q)
 Transfer outfall discharges into 2D coupling cells.
 
std::vector< PendingBoundaryRowcollectBCRows (const std::vector< PendingBoundaryRow > *pending, const BoundaryData *boundary, bool drained)
 Collect [2D_BOUNDARY_CONDITIONS]-shaped rows for serialization.
 
std::vector< PendingEdgeConveyanceRowcollectConveyanceRows (const std::vector< PendingEdgeConveyanceRow > *pending, const MeshData *mesh, bool drained)
 Collect [2D_EDGE_CONVEYANCE]-shaped rows for serialization.
 
std::string parse2DOptionsLine (const std::vector< std::string > &tokens, SolverOptions2D &opts)
 Parse a single line from the [2D_OPTIONS] section.
 
bool is2DOptionKey (const std::string &key)
 True when key (case-insensitive) is a [2D_OPTIONS] parameter accepted by parse2DOptionsLine.
 
std::string format2DOptionValue (const SolverOptions2D &opts, const std::string &key)
 Format the current value of a [2D_OPTIONS] parameter as the string token parse2DOptionsLine accepts (round-trip safe).
 
std::string parse2DVertexLine (const std::vector< std::string > &tokens, MeshData &mesh)
 Parse a single line from the [2D_VERTICES] section.
 
std::string parse2DTriangleLine (const std::vector< std::string > &tokens, MeshData &mesh)
 Parse a single line from the [2D_TRIANGLES] section.
 
std::string parse2DVertexNodeMapLine (const std::vector< std::string > &tokens, MeshData &mesh)
 Parse a single line from the [2D_VERTEX_NODE_MAP] section.
 
std::string parse2DTriangleNodeMapLine (const std::vector< std::string > &tokens, MeshData &mesh)
 Parse a single line from the [2D_TRIANGLE_NODE_MAP] section.
 
std::string parse2DBoundaryConditionsLine (const std::vector< std::string > &tokens, std::vector< SurfaceRouter2D::PendingBoundaryRow > &pending_rows)
 V-E3 — parse a single line from the [2D_BOUNDARY_CONDITIONS] section into a SurfaceRouter2D::PendingBoundaryRow appended to pending_rows.
 
std::string parse2DEdgeConveyanceLine (const std::vector< std::string > &tokens, std::vector< SurfaceRouter2D::PendingEdgeConveyanceRow > &pending_rows)
 §11A — parse a single [2D_EDGE_CONVEYANCE] line.
 
void register2DSections (MeshData &mesh, SolverOptions2D &options, std::vector< SurfaceRouter2D::PendingBoundaryRow > &pending_bc_rows, std::vector< SurfaceRouter2D::PendingEdgeConveyanceRow > &pending_ec_rows, input::SectionRegistry &registry)
 Register all 2D input section handlers with the section registry.
 
std::string load2DMeshExternalFile (MeshData &mesh, SolverOptions2D &opts, std::vector< SurfaceRouter2D::PendingBoundaryRow > &pending_bc_rows, std::vector< SurfaceRouter2D::PendingEdgeConveyanceRow > &pending_ec_rows, const std::string &mesh_file, const std::string &inp_base_dir)
 Load 2D mesh sections from an external file.
 
void prescan2DUnitsHeader (const std::string &inp_path, SolverOptions2D &opts)
 Scan inp_path for a ;; UNITS: <value> comment header and set opts.mesh_units_si to true when the value names metres.
 
void buildMeshTopology (MeshData &mesh)
 Build mesh topology and precompute geometry from raw vertex/triangle data.
 
std::string validateMesh (const MeshData &mesh)
 Validate mesh data for consistency.
 
void recomputeVertexZDependents (MeshData &mesh, int vidx)
 Recompute Z-derived per-triangle / per-edge geometry for triangles incident to a vertex whose Z just changed.
 
void buildVertexStencils (MeshData &mesh)
 Build pseudo-Laplacian reconstruction stencils for all vertices.
 
void reconstructVertexHeads (const MeshData &mesh, SurfaceStateData &state, int nthreads=1)
 Reconstruct head values at vertices from cell-centred heads.
 
void seedInactiveState (const MeshData &mesh, SurfaceStateData &state, const SolverOptions2D &opts)
 
void rebuildActiveSet (const MeshData &mesh, SurfaceStateData &state, const BoundaryData *boundary, const std::vector< CouplingPoint > *coupling_pts, const SolverOptions2D &opts, ActiveSetData &as, bool live_coupling)
 
bool activeSetBreached (const MeshData &mesh, const SurfaceStateData &state, const ActiveSetData &as)
 
void computeUnlimitedGradients (const MeshData &mesh, SurfaceStateData &state, int nthreads=1)
 Compute unlimited gradients for all triangles via Green-Gauss theorem.
 
void computeLimitedGradients (const MeshData &mesh, SurfaceStateData &state, double epsilon, int nthreads=1)
 Apply Jawahar-Kamath slope limiter (Eq. [23]–[24]).
 
void computeEdgeFluxes (const MeshData &mesh, SurfaceStateData &state, const SolverOptions2D &opts)
 Compute edge fluxes for all triangles.
 
void assembleRHS (const MeshData &mesh, const SurfaceStateData &state, const SolverOptions2D &opts, double *ydot)
 Assemble the RHS of the ODE system: dψ/dt for each triangle.
 
void assembleImplicitRHS (const MeshData &mesh, const SurfaceStateData &state, const SolverOptions2D &opts, double *ydot)
 Implicit half of the IMEX split: the flux divergence only (ARKODE F_I).
 
void assembleExplicitRHS (const MeshData &mesh, const SurfaceStateData &state, const SolverOptions2D &opts, const double *y, double *ydot)
 Explicit half of the IMEX split: source/sink forcing (ARKODE F_E).
 
void computeCellContinuity (const MeshData &mesh, SurfaceStateData &state, const SolverOptions2D &opts, double dt)
 Compute the per-cell continuity residual (local mass-balance check).
 
void computeFaceVelocity (const MeshData &mesh, SurfaceStateData &state, const SolverOptions2D &opts)
 Reconstruct cell-centred velocity (vx, vy) from edge fluxes (RT0).
 
double evapSink (double rate, double depth, double dry_depth) noexcept
 Depth-limited evaporation sink rate (m/s) for one cell.
 
std::unique_ptr< ISurfaceSolvermakeSurfaceSolver (const SolverOptions2D &opts, std::string *chosen=nullptr, int n_cells=0)
 Construct the 2D surface solver per the runtime backend policy.
 

Enumeration Type Documentation

◆ BoundaryType

enum class openswmm::twoD::BoundaryType : int8_t
strong

Boundary condition types for 2D mesh edges.

SPECIFIED_FLOW (3) and RATING_CURVE (4) added per GUI plan §V V-E4 / V-E5. Storage + C API only at this revision — the FV-SWE flux integration for non-Wall BCs is deferred to a follow-up slice (V-E-FLUX). Today the solver treats every boundary edge as Wall regardless of type (see SurfaceFluxCalculator::computeEdgeFluxes line 131).

Enumerator
WALL 

Zero-flux wall (default)

NORMAL_FLOW 

Manning outflow using bed slope.

SPECIFIED_STAGE 

Prescribed water surface elevation (constant or TS)

SPECIFIED_FLOW 

Prescribed discharge per metre of edge (constant or TS)

RATING_CURVE 

Stage → flow lookup (curve registry index)

◆ IntegratorType

enum class openswmm::twoD::IntegratorType : int8_t
strong

Time-integrator selector for the 2D surface ODE.

CVODE is the default fully-implicit BDF integrator (CvodeSurfaceSolver). ARKODE selects the ARKStep additive-Runge–Kutta IMEX integrator (ArkodeSurfaceSolver) — the diffusion flux is integrated implicitly while the non-stiff source forcing is explicit. See docs/IMEX_LOCAL_INERTIAL_IMPLEMENTATION_PLAN.md. Orthogonal to the serial/omp/gpu backend selector; ARKODE is CPU-only. The env var OPENSWMM_2D_INTEGRATOR (cvode|arkode) overrides this field.

Enumerator
CVODE 

Default: fully-implicit BDF (CvodeSurfaceSolver).

ARKODE 

ARKStep IMEX additive-RK (ArkodeSurfaceSolver).

◆ LinearSolverType

enum class openswmm::twoD::LinearSolverType : int8_t
strong

Krylov linear solver selector for the BDF + Newton + Krylov stack.

Phase 1 wires GMRES only; BICGSTAB and TFQMR are kept as enum values to preserve the input-file parsing surface and to mark slots reserved for possible Phase 2 work, but selecting them today triggers a clear runtime error in CvodeSurfaceSolver::initialize().

GMRES is the canonical choice for the elliptic-flavoured diffusive-wave Jacobian and pairs cleanly with multigrid preconditioners (the Phase 2 BoomerAMG path); the other two Krylov methods would only earn their keep for problem classes we do not currently solve.

Enumerator
GMRES 

Phase 1: WIRED (SUNLinSol_SPGMR).

BICGSTAB 

Reserved; rejected at initialize() in Phase 1.

TFQMR 

Reserved; rejected at initialize() in Phase 1.

◆ MomentumType

enum class openswmm::twoD::MomentumType : int8_t
strong

Surface-momentum closure for the 2D flux.

DW (default) is the Manning diffusive wave (no inertia; state = cell volume only). INERTIAL adds the LISFLOOD-FP local-inertial momentum: a prognostic per-edge discharge q with implicit gravity + friction, integrated by the ARKStep IMEX solver. See docs/IMEX_LOCAL_INERTIAL_IMPLEMENTATION_PLAN.md §2. Only honored by ArkodeSurfaceSolver; env OPENSWMM_2D_MOMENTUM (dw|inertial) overrides this field.

Enumerator
DW 

Manning diffusive wave (default).

INERTIAL 

Local-inertial (LISFLOOD-FP) with per-edge q.

◆ PreconditionerType

enum class openswmm::twoD::PreconditionerType : int8_t
strong

Preconditioner selector for the Krylov inner solver.

NONE (no preconditioning) and JACOBI (per-cell diagonal approximation rebuilt each Jacobian refresh) are always available. AMG (hypre BoomerAMG) is wired only when the engine is built with OPENSWMM_WITH_HYPRE; selecting it otherwise triggers a clear runtime error in the solver's initialize(). ILU remains a reserved-but-rejected slot.

Tier rationale (see also the Phase 1/2 discussion in docs/2D_KNOWN_STIFFNESS_ISSUE.md):

  • NONE : baseline; useful for measuring how much the Jacobi heuristic actually buys at a given mesh size.
  • JACOBI : O(n) setup, O(n) apply, embarrassingly parallel. Effective while the Newton matrix M = I − γJ is diagonally dominant; expected to scale acceptably to ~10k–50k cells.
  • ILU : O(nnz) setup + apply via KLU. Better convergence per Krylov iteration than JACOBI but still asymptotically non-scalable on this elliptic operator. Not implemented.
  • AMG : O(n) setup amortised, near-constant Krylov iterations regardless of mesh size. The only scalable option past ~100k cells. Requires hypre/BoomerAMG. Not yet wired.
Enumerator
NONE 

WIRED (no preconditioning).

JACOBI 

WIRED (diagonal heuristic).

ILU 

Reserved; rejected at initialize().

AMG 

WIRED when built with OPENSWMM_WITH_HYPRE (BoomerAMG).

◆ RainfallMode

enum class openswmm::twoD::RainfallMode : int8_t
strong

How raingage rainfall is mapped onto the 2D mesh cells.

NATURAL_NEIGHBOUR (default) spatially interpolates the located raingages onto every cell centroid — natural-neighbour (Laplace) weights inside the convex hull of the gages, inverse-distance (power 2) extrapolation outside it. The weights are precomputed once in SurfaceRouter2D::initialize() (gage positions are static for a run) and applied each step as a sparse weighted sum.

SYSTEM applies one uniform value to all cells: the arithmetic mean of every gage's current rainfall. It is also the automatic fallback when no gage has a map location (no [SYMBOLS] coordinate), since interpolation is then undefined.

Parsed from [2D_OPTIONS] RAINFALL_MODE; env OPENSWMM_2D_RAINFALL_MODE (natural|system) overrides at initialize().

Enumerator
NATURAL_NEIGHBOUR 

Default: spatial interpolation across all gages.

SYSTEM 

Uniform = mean of all gages.

NONE 

No rain on the mesh. Use when subcatchments already capture the rainfall (runoff → nodes) — rain-on-mesh would double-count the same storm.

Function Documentation

◆ activeSetBreached()

bool openswmm::twoD::activeSetBreached ( const MeshData & mesh,
const SurfaceStateData & state,
const ActiveSetData & as )

Post-advance safety check: true if any OUTER-ring cell got wet (volume > eps·A) — the front crossed the whole halo within one window and the advance must be discarded and redone with a wider halo.

◆ assembleExplicitRHS()

void openswmm::twoD::assembleExplicitRHS ( const MeshData & mesh,
const SurfaceStateData & state,
const SolverOptions2D & opts,
const double * y,
double * ydot )

Explicit half of the IMEX split: source/sink forcing (ARKODE F_E).

Writes ydot_i = A_i·(rainfall_i + coupling_flux_i − evapSink_i). Cell depth is reconstructed locally from the stage volume y (depth = max(V,0)/A) so the callback is free of side effects on the shared state arrays the implicit half and the preconditioner read.

Parameters
meshMesh geometry (tri_area).
stateSurface state (reads rainfall, coupling_flux, evap_rate).
optsSolver options (dry_depth, num_threads).
yStage cell volumes (m³), one per triangle.
ydotOutput: explicit dV/dt per triangle (size = n_triangles).
Here is the call graph for this function:

◆ assembleImplicitRHS()

void openswmm::twoD::assembleImplicitRHS ( const MeshData & mesh,
const SurfaceStateData & state,
const SolverOptions2D & opts,
double * ydot )

Implicit half of the IMEX split: the flux divergence only (ARKODE F_I).

Writes ydot_i = Σ_e edge_flux[i·3+e] — the stiff parabolic diffusion operator that drives the implicit solve. The source/sink forcing is handled separately by assembleExplicitRHS; together they reproduce assembleRHS exactly. Requires state.edge_flux to be current (computeEdgeFluxes already called this stage).

Parameters
meshMesh geometry.
stateSurface state (reads edge_flux).
optsSolver options (num_threads).
ydotOutput: implicit dV/dt per triangle (size = n_triangles).
Here is the call graph for this function:

◆ assembleRHS()

void openswmm::twoD::assembleRHS ( const MeshData & mesh,
const SurfaceStateData & state,
const SolverOptions2D & opts,
double * ydot )

Assemble the RHS of the ODE system: dψ/dt for each triangle.

Combines edge fluxes, rainfall, evaporation, and coupling fluxes into the net rate of change of depth for each cell: dψ_i/dt = (1/A_i) Σ_j F_j + rainfall_i + coupling_flux_i − evapSink(evap_rate_i, ψ_i, dry_depth)

Parameters
meshMesh geometry.
stateSurface state.
optsSolver options (dry_depth for the evaporation ramp).
ydotOutput: dψ/dt for each triangle (size = n_triangles).
Here is the call graph for this function:

◆ buildCouplingPoints()

std::vector< CouplingPoint > openswmm::twoD::buildCouplingPoints ( const MeshData & mesh,
const SimulationContext & ctx )

Build the list of coupling points from mesh coupling maps.

Resolves vertex/triangle → node mappings into CouplingPoint descriptors. Must be called after node names are resolved to indices.

Parameters
meshMesh data with coupling maps populated.
ctxSimulation context (for node type and outfall queries).
Returns
Vector of coupling points.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ buildMeshTopology()

void openswmm::twoD::buildMeshTopology ( MeshData & mesh)

Build mesh topology and precompute geometry from raw vertex/triangle data.

Must be called after parsing is complete and before solver initialization. Populates: tri_nbr*, tri_area, tri_c*, edge_length, edge_n*, edge_m*.

Parameters
meshThe mesh data with vx/vy/vz and tri_v0/v1/v2 already populated.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ buildVertexStencils()

void openswmm::twoD::buildVertexStencils ( MeshData & mesh)

Build pseudo-Laplacian reconstruction stencils for all vertices.

For each vertex, collects all triangles sharing that vertex, computes moments (I_xx, I_yy, I_xy, R_x, R_y), Lagrange multipliers (λ_x, λ_y), and weights. Stores results in mesh.vert_stencil_ptr/idx/wt (CSR format).

Parameters
meshThe mesh (must have topology already built).
Here is the call graph for this function:
Here is the caller graph for this function:

◆ collectBCRows()

std::vector< PendingBoundaryRow > openswmm::twoD::collectBCRows ( const std::vector< PendingBoundaryRow > * pending,
const BoundaryData * boundary,
bool drained )
inline

Collect [2D_BOUNDARY_CONDITIONS]-shaped rows for serialization.

Parameters
pendingParse-time pending rows (may be null/empty).
boundaryDrained per-edge BC storage (may be null/unsized).
drainedSolverOptions2D::pending_rows_drained — true once initialize() moved the pending rows into boundary.
Returns
Authored rows; empty when every edge is a default WALL.
Note
Source selection: BEFORE the drain the pending rows are the only (and authoritative) source. AFTER the drain, BoundaryData is the live state — post-initialize API mutations edit it, so it wins — and the optional GROUP labels (not representable in BoundaryData) are re-attached from the retained pending rows by (tri, edge). NORMAL_FLOW's authored slope sentinel (0.0 = auto) is preserved: nothing writes the computed slope back into edge_bed_slope.
Here is the call graph for this function:

◆ collectConveyanceRows()

std::vector< PendingEdgeConveyanceRow > openswmm::twoD::collectConveyanceRows ( const std::vector< PendingEdgeConveyanceRow > * pending,
const MeshData * mesh,
bool drained )
inline

Collect [2D_EDGE_CONVEYANCE]-shaped rows for serialization.

Parameters
pendingParse-time pending rows (may be null/empty).
meshMesh whose edge_conveyance slots hold the drained values (may be null).
drainedSolverOptions2D::pending_rows_drained — true once initialize() moved the pending rows into the mesh slots.
Returns
Authored rows; empty when every edge is at the default 1.0.
Note
Source selection mirrors collectBCRows: pending rows before the drain (the mesh slots are still all-1.0 defaults then); the mesh slots after it (they are the live state API mutations edit, and reconstruction is lossless — pair + value). The walk derives the vertex pair for local edge e of triangle t as v[(e+1)%3], v[(e+2)%3] (the drain convention in SurfaceRouter2D::initialize), and de-duplicates the interior-edge mirror so each undirected edge is emitted once with v_from < v_to.
Here is the call graph for this function:

◆ computeCellContinuity()

void openswmm::twoD::computeCellContinuity ( const MeshData & mesh,
SurfaceStateData & state,
const SolverOptions2D & opts,
double dt )

Compute the per-cell continuity residual (local mass-balance check).

Evaluates the discrete semi-discrete balance for each cell: residual_i = (ψ_i − ψ_old_i)·A_i/dt − ( Σ_e F_e + (rainfall_i + coupling_flux_i − evapSink_i)·A_i ) where F_e = edge_flux[i·3+e] is the inflow-positive volumetric edge flux (m³/s). A perfectly conservative step yields ~0 (first-order diagnostic, not the solver's internal error). Reads old_depth, depth, edge_flux, rainfall, evap_rate, coupling_flux; writes cell_continuity_err (m³/s).

Call AFTER the solver advance, with old_depth holding the start-of-step depths (i.e. after save_state() but before the next save_state()).

Parameters
meshMesh geometry (tri_area).
stateSurface state (writes cell_continuity_err).
optsSolver options (dry_depth for the evaporation ramp).
dtStep over which old_depth→depth evolved (s).
Here is the call graph for this function:

◆ computeCouplingExchange()

void openswmm::twoD::computeCouplingExchange ( const std::vector< CouplingPoint > & cps,
const MeshData & mesh,
SurfaceStateData & state,
SimulationContext & ctx,
const SolverOptions2D & opts,
double dt )

Compute exchange flows at all coupling points and inject into forcing API.

For each coupling point:

  1. Computes head difference Δh = h_2d - h_swmm
  2. Applies orifice equation: Q = Cd * A * sign(Δh) * sqrt(2g|Δh|)
  3. Handles outfall boundary feedback and flap gates
  4. Suppresses ponding at coupled nodes
  5. Injects Q into forcing API as lateral inflow (ADD, RESET)
  6. Records coupling flux back into 2D state
Parameters
cpsCoupling points.
meshMesh data.
state2D surface state.
ctxSimulation context (node heads, forcing API, mass balance).
opts2D solver options (uses dry_depth as the wet/dry threshold).
dtCurrent SWMM routing timestep (s).

◆ computeEdgeFluxes()

void openswmm::twoD::computeEdgeFluxes ( const MeshData & mesh,
SurfaceStateData & state,
const SolverOptions2D & opts )

Compute edge fluxes for all triangles.

For each edge, reconstructs head at the edge from the upstream cell using the limited gradient, computes diffusive conductance, and evaluates the normal flux. Boundary edges use zero-flux (wall) condition.

Parameters
meshMesh geometry.
stateSurface state (reads depth, head, limited gradients; writes edge_flux).
optsSolver options (dry_depth).
Here is the call graph for this function:
Here is the caller graph for this function:

◆ computeFaceVelocity()

void openswmm::twoD::computeFaceVelocity ( const MeshData & mesh,
SurfaceStateData & state,
const SolverOptions2D & opts )

Reconstruct cell-centred velocity (vx, vy) from edge fluxes (RT0).

For each wet cell, solves the 3×2 least-squares system N·q ≈ b in closed form via the normal equations (NᵀN)·q = Nᵀb, where each row of N is the outward edge normal and b_e = edge_flux_e / edge_length_e is the depth-integrated normal speed (m²/s). The resulting specific-discharge vector is divided by cell depth to give velocity (m/s). Dry cells (depth < dry_depth) get zero velocity. Mirrors the GUI RT0 reconstruction (swmm2dresultslayer.cpp applyCurrentFlux_) without its scene-space Y-flip.

Parameters
meshMesh geometry (edge normals, lengths).
stateSurface state (reads edge_flux, depth; writes face_vx/face_vy).
optsSolver options (dry_depth).
Here is the call graph for this function:

◆ computeLimitedGradients()

void openswmm::twoD::computeLimitedGradients ( const MeshData & mesh,
SurfaceStateData & state,
double epsilon,
int nthreads = 1 )

Apply Jawahar-Kamath slope limiter (Eq. [23]–[24]).

Computes continuously differentiable limited gradients from the unlimited gradients of a cell and its neighbours.

Parameters
meshMesh geometry (for neighbour lookup).
stateSurface state (reads grad_hx/hy, writes grad_hx_lim/hy_lim).
epsilonLimiter epsilon (small positive, typically 1e-6).
nthreadsOpenMP thread count for the per-cell loop (1 = serial).
Here is the call graph for this function:
Here is the caller graph for this function:

◆ computeNodeCouplingQ()

double openswmm::twoD::computeNodeCouplingQ ( const CouplingPoint & cp,
const MeshData & mesh,
const SurfaceStateData & state,
const NodeData & nodes,
const SolverOptions2D & opts )
noexcept

Live node-coupling orifice flux for ONE non-outfall coupling point.

Evaluates the bidirectional capped-pipe orifice exchange Q (m³/s; > 0 drains 2D → 1D, < 0 spills 1D → 2D) from the CURRENT 2D state (head/depth/vert_head, reconstructed live inside the CVODE RHS) against the 1D node head, which is frozen for the duration of a 2D advance() window. Unlike computeCouplingExchange (which pre-computes a HELD flux per window and caps it by available volume / dt to stop a held drain overshooting), this is the continuous form for use inside the RHS: the orifice + capped-pipe gate + the wet/dry Hermite ramp on the LIVE source-side depth make Q self-limit smoothly as the cell drains, so CVODE integrates the stiff coupling implicitly and stably across a large macro-window — no discrete avail/dt cap needed.

Booking/conservation is handled by the caller integrating ∫Q dt (a per-point accumulator carried in the augmented state vector) over the window.

◆ computeUnlimitedGradients()

void openswmm::twoD::computeUnlimitedGradients ( const MeshData & mesh,
SurfaceStateData & state,
int nthreads = 1 )

Compute unlimited gradients for all triangles via Green-Gauss theorem.

For each triangle, the gradient is the area-weighted average of edge contributions: ∇h_i = (1/A_i) Σ_j h_edge_j * n_j * ξ_j

Parameters
meshMesh geometry.
stateSurface state (reads head[], writes grad_hx[], grad_hy[]).
nthreadsOpenMP thread count for the per-cell loop (1 = serial).
Here is the call graph for this function:
Here is the caller graph for this function:

◆ evapSink()

double openswmm::twoD::evapSink ( double rate,
double depth,
double dry_depth )
inlinenoexcept

Depth-limited evaporation sink rate (m/s) for one cell.

Reuses the cubic Hermite wet/dry ramp applied to edge fluxes: the full demand rate applies for depth ≥ dry_depth and shuts off smoothly (C¹) as the cell dries, so evaporation can never drive a depth negative. Negative rates are treated as zero (no condensation source — use rainfall).

Parameters
rateEvaporation demand rate (m/s).
depthCurrent cell depth (m).
dry_depthDry-depth threshold (m).
Returns
Effective sink rate (m/s, ≥ 0).
Here is the caller graph for this function:

◆ format2DOptionValue()

std::string openswmm::twoD::format2DOptionValue ( const SolverOptions2D & opts,
const std::string & key )

Format the current value of a [2D_OPTIONS] parameter as the string token parse2DOptionsLine accepts (round-trip safe).

Parameters
optsSolver options to read.
keyParameter name (case-insensitive).
Returns
The value token, or an empty string for unknown keys (and for an unset OUTPUT_FILE, whose value token is optional).
Here is the caller graph for this function:

◆ is2DOptionKey()

bool openswmm::twoD::is2DOptionKey ( const std::string & key)

True when key (case-insensitive) is a [2D_OPTIONS] parameter accepted by parse2DOptionsLine.

Used by the swmm_options_get_ext / swmm_options_set_ext C API to route these keys to the live SolverOptions2D (via SimulationContext::twod_io) instead of the generic ext_options map — that routing is what makes GUI/API edits of 2D options reach the solver and persist through the InpWriter [2D_OPTIONS] emission and the GeoPackage 2D_* option keys.

Here is the caller graph for this function:

◆ load2DMeshExternalFile()

std::string openswmm::twoD::load2DMeshExternalFile ( MeshData & mesh,
SolverOptions2D & opts,
std::vector< SurfaceRouter2D::PendingBoundaryRow > & pending_bc_rows,
std::vector< SurfaceRouter2D::PendingEdgeConveyanceRow > & pending_ec_rows,
const std::string & mesh_file,
const std::string & inp_base_dir )

Load 2D mesh sections from an external file.

Opens the file referenced by mesh_file (resolved relative to inp_base_dir if it is a relative path) and parses any [2D_OPTIONS], [2D_VERTICES], [2D_TRIANGLES], [2D_VERTEX_NODE_MAP], and [2D_TRIANGLE_NODE_MAP] sections found in it.

Parameters
meshMesh data to populate.
optsSolver options to populate.
mesh_filePath from the [2D_MESH_FILE] FILE token.
inp_base_dirDirectory of the parent .inp file (may be empty).
Returns
Empty string on success, or an error description on failure.
Here is the call graph for this function:

◆ makeSurfaceSolver()

std::unique_ptr< ISurfaceSolver > openswmm::twoD::makeSurfaceSolver ( const SolverOptions2D & opts,
std::string * chosen = nullptr,
int n_cells = 0 )

Construct the 2D surface solver per the runtime backend policy.

Never throws on plugin absence or load failure — it resolves to the serial CPU solver instead. A successfully loaded GPU plugin is reported on stderr; the default CPU path is silent so ordinary runs produce no extra output.

Parameters
optsSolver options (reserved for future per-model backend hints).
chosenOptional out-param; receives a human-readable backend label (e.g. "cpu (serial CVODE)" or "cuda (NVIDIA A100)").
n_cellsTriangle count of the mesh (0 = unknown). Under the default auto policy a mesh below the parallel-worthwhile threshold (env OPENSWMM_2D_MIN_PARALLEL_CELLS, default 20000) selects the serial CPU solver even when a GPU/OpenMP plugin is present — Kokkos' per-kernel launch overhead makes the accelerated path slower than serial on small meshes. An explicit OPENSWMM_2D_BACKEND always wins (no size gate).
Returns
An owned ISurfaceSolver. For a plugin-backed solver the owning shared library is kept resident for the process lifetime.
Here is the caller graph for this function:

◆ parse2DBoundaryConditionsLine()

std::string openswmm::twoD::parse2DBoundaryConditionsLine ( const std::vector< std::string > & tokens,
std::vector< SurfaceRouter2D::PendingBoundaryRow > & pending_rows )

V-E3 — parse a single line from the [2D_BOUNDARY_CONDITIONS] section into a SurfaceRouter2D::PendingBoundaryRow appended to pending_rows.

Format: TRI EDGE TYPE [PARAM_1 [PARAM_2 [GROUP]]] TYPE ∈ WALL / NORMAL_FLOW / SPECIFIED_STAGE / TS_STAGE / SPECIFIED_FLOW / TS_FLOW / RATING_CURVE PARAM_1 = slope / head / TS name / flow / TS name / curve name (by type) PARAM_2 reserved, always "*" GROUP optional named group ("*" = none)

Rows are drained into BoundaryData inside SurfaceRouter2D::initialize() after boundary_.resize() has sized the per-edge slots.

Here is the caller graph for this function:

◆ parse2DEdgeConveyanceLine()

std::string openswmm::twoD::parse2DEdgeConveyanceLine ( const std::vector< std::string > & tokens,
std::vector< SurfaceRouter2D::PendingEdgeConveyanceRow > & pending_rows )

§11A — parse a single [2D_EDGE_CONVEYANCE] line.

Format: FROM_VERTEX TO_VERTEX CONVEYANCE FROM_VERTEX, TO_VERTEX : non-negative integer mesh-vertex indices. Must differ. The pair is undirected. CONVEYANCE : double in [0, 1] (strict, clamped at parse).

Rows are accumulated into pending (a scratch buffer on SurfaceRouter2D) and resolved against the mesh topology in SurfaceRouter2D::initialize() after buildMeshTopology populates the neighbour table.

Here is the caller graph for this function:

◆ parse2DOptionsLine()

std::string openswmm::twoD::parse2DOptionsLine ( const std::vector< std::string > & tokens,
SolverOptions2D & opts )

Parse a single line from the [2D_OPTIONS] section.

Parameters
tokensWhitespace-split tokens from the line.
optsOutput solver options to populate.
Returns
Empty string on success, or error description.
Here is the caller graph for this function:

◆ parse2DTriangleLine()

std::string openswmm::twoD::parse2DTriangleLine ( const std::vector< std::string > & tokens,
MeshData & mesh )

Parse a single line from the [2D_TRIANGLES] section.

Format: V1 V2 V3 MANNINGS_N [TAG]

Parameters
tokensWhitespace-split tokens from the line.
meshMesh data to append triangle to.
Returns
Empty string on success, or error description.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ parse2DTriangleNodeMapLine()

std::string openswmm::twoD::parse2DTriangleNodeMapLine ( const std::vector< std::string > & tokens,
MeshData & mesh )

Parse a single line from the [2D_TRIANGLE_NODE_MAP] section.

Format: TRIANGLE_INDEX_OR_TAG SWMM_NODE_NAME [CD] [AREA]

Parameters
tokensWhitespace-split tokens from the line.
meshMesh data to update coupling map.
Returns
Empty string on success, or error description.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ parse2DVertexLine()

std::string openswmm::twoD::parse2DVertexLine ( const std::vector< std::string > & tokens,
MeshData & mesh )

Parse a single line from the [2D_VERTICES] section.

Format: X Y Z [TAG]

Parameters
tokensWhitespace-split tokens from the line.
meshMesh data to append vertex to.
Returns
Empty string on success, or error description.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ parse2DVertexNodeMapLine()

std::string openswmm::twoD::parse2DVertexNodeMapLine ( const std::vector< std::string > & tokens,
MeshData & mesh )

Parse a single line from the [2D_VERTEX_NODE_MAP] section.

Format: VERTEX_INDEX_OR_TAG SWMM_NODE_NAME [CD] [AREA]

Parameters
tokensWhitespace-split tokens from the line.
meshMesh data to update coupling map.
Returns
Empty string on success, or error description.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ prescan2DUnitsHeader()

void openswmm::twoD::prescan2DUnitsHeader ( const std::string & inp_path,
SolverOptions2D & opts )

Scan inp_path for a ;; UNITS: <value> comment header and set opts.mesh_units_si to true when the value names metres.

Recognised SI markers (case-insensitive): SI (m), m, metre, metres, meter, meters. Any other value (or absent header) leaves the flag unchanged.

Safe to call multiple times; e.g. once on the inline .inp and again on the resolved .2dm — the most recent observation wins, which is the intended precedence (external file overrides inline).

Quietly does nothing if the file cannot be opened — the caller already reports the missing-file error via the normal read path.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ rebuildActiveSet()

void openswmm::twoD::rebuildActiveSet ( const MeshData & mesh,
SurfaceStateData & state,
const BoundaryData * boundary,
const std::vector< CouplingPoint > * coupling_pts,
const SolverOptions2D & opts,
ActiveSetData & as,
bool live_coupling = false )

Rebuild the mask from the CURRENT wet set + sources. O(nt + nv), once per advance window (never per RHS evaluation). Seeds = wet cells (V > eps·A) ∪ nonzero rainfall/coupling sources (runtime forcings must already be folded into those arrays) ∪ cells with a non-WALL boundary edge. With live_coupling set, ALL coupling-point stencils are additionally force-activated: the live exchange is evaluated inside the RHS against the moving 2D head, so coupling_flux alone cannot reveal them. On the held path a zero-flux stencil contributes nothing this window by construction, so the flux seeds above are exact — and skipping the blanket activation is what keeps the active set near the wet front instead of pinning every coupled node's neighbourhood hot for the whole run. Cells that leave the active set get their edge-flux slots zeroed so stale values cannot linger in diagnostics or boundary integration.

Here is the call graph for this function:

◆ recomputeVertexZDependents()

void openswmm::twoD::recomputeVertexZDependents ( MeshData & mesh,
int vidx )

Recompute Z-derived per-triangle / per-edge geometry for triangles incident to a vertex whose Z just changed.

Updates tri_cz (centroid Z = mean of vertex Zs) and edge_mz (per-edge midpoint Z) for every triangle that references vertex vidx. XY-derived fields (tri_area, tri_cx, tri_cy, edge_length, edge_nx, edge_ny, edge_mx, edge_my) are not affected.

Used by swmm_2d_set_vertex_z and exposed here so tests can verify the recompute logic without spinning up a full engine.

Parameters
meshThe mesh; mesh.vz[vidx] is assumed to already hold the new Z.
vidxIndex of the vertex whose Z just changed.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ reconstructVertexHeads()

void openswmm::twoD::reconstructVertexHeads ( const MeshData & mesh,
SurfaceStateData & state,
int nthreads = 1 )

Reconstruct head values at vertices from cell-centred heads.

Evaluates: h_vertex[b] = Σ_i ω_i * h_cell[stencil_idx[i]] Uses the CSR stencil built by buildVertexStencils().

Parameters
meshThe mesh (with stencils built).
stateSurface state (reads head[], writes vert_head[]).
nthreadsOpenMP thread count for the per-vertex loop (1 = serial).
Here is the call graph for this function:
Here is the caller graph for this function:

◆ register2DSections()

void openswmm::twoD::register2DSections ( MeshData & mesh,
SolverOptions2D & options,
std::vector< SurfaceRouter2D::PendingBoundaryRow > & pending_bc_rows,
std::vector< SurfaceRouter2D::PendingEdgeConveyanceRow > & pending_ec_rows,
input::SectionRegistry & registry )

Register all 2D input section handlers with the section registry.

Call during input reader setup (conditional on OPENSWMM_HAS_2D). The handlers will populate the mesh and options data in SimulationContext.

Parameters
meshMesh data to populate.
optionsSolver options to populate.
registrySection registry to register handlers into.
Here is the call graph for this function:

◆ scatterCouplingToYdot()

void openswmm::twoD::scatterCouplingToYdot ( const MeshData & mesh,
const SurfaceStateData & state,
const CouplingPoint & cp,
double Q,
double * ydot )
noexcept

Scatter a signed volumetric exchange Q (m³/s) directly onto the cell derivatives ydot[] of the 2D volume ODE (for the live-RHS path).

Same upwind-HGL stencil distribution as the held-flux scatterCouplingFlux, but adds the per-cell share Q·w (Σw = 1) straight into ydot (m³/s) rather than into a coupling_flux rate, so the exchange is conservative across the stencil. Sign convention matches ydot: positive Q = source INTO the cells, negative = sink OUT of them (so the RHS passes −Q_drain for a 2D → 1D drain).

◆ seedInactiveState()

void openswmm::twoD::seedInactiveState ( const MeshData & mesh,
SurfaceStateData & state,
const SolverOptions2D & opts )

One-time seed: run the FULL vertex-head, gradient, limiter and edge-flux passes so every cell/vertex holds its frozen-correct dry value (dry-cell head = bed, terrain gradients, zero fluxes) before masking starts — the limiter reads inactive neighbours' gradients and the vertex reconstruction reads inactive stencil heads, so those must be exact, not initial zeros. Call from SurfaceRouter2D::initialize() and after every reinitialize().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ transferOutfallDischarges()

int openswmm::twoD::transferOutfallDischarges ( const std::vector< CouplingPoint > & cps,
const MeshData & mesh,
SurfaceStateData & state,
const SimulationContext & ctx,
const SolverOptions2D & opts,
double dt,
std::unordered_map< int, double > & applied_q )

Transfer outfall discharges into 2D coupling cells.

After 1D routing, the outfall discharge is a source for the 2D cell at the outfall coupling point. Withdrawal (net backflow into the pipe) is capped at the water actually available in the receiving cell(s) so the held sink cannot pull cell volumes negative over the window.

Parameters
cpsCoupling points.
meshMesh data.
state2D surface state.
ctxSimulation context.
opts2D solver options (for unit-system coupling factors).
dt2D advance window (s); used for the withdrawal cap.
applied_qOut: net SI exchange (m³/s, +into 2D) actually applied per outfall node index — the mass-balance ledger must book exactly these (clamped) values, not the raw 1D rates.
Returns
Number of outfalls whose withdrawal was clamped this window.

◆ updateOutfallBoundaries()

void openswmm::twoD::updateOutfallBoundaries ( const std::vector< CouplingPoint > & cps,
const MeshData & mesh,
const SurfaceStateData & state,
SimulationContext & ctx,
const SolverOptions2D & opts )

Update outfall boundary depths from 2D surface heads.

For each outfall coupled to the 2D domain, sets the outfall depth to max(h_standard, h_2d) to account for dynamic tailwater from 2D flooding. Must be called before 1D routing step.

Parameters
cpsCoupling points.
meshMesh data.
state2D surface state.
ctxSimulation context.
opts2D solver options (for unit-system coupling factors).
Here is the call graph for this function:
Here is the caller graph for this function:

◆ validateMesh()

std::string openswmm::twoD::validateMesh ( const MeshData & mesh)

Validate mesh data for consistency.

Checks: vertex indices in bounds, positive areas, no degenerate triangles.

Parameters
meshThe mesh to validate.
Returns
Empty string if valid, or a description of the first error found.
Here is the call graph for this function:
Here is the caller graph for this function: