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

Namespaces

namespace  diffusive
 
namespace  gpu
 
namespace  inertial
 
namespace  report2d
 Dataset groups the 2D results writer can emit ([2D_OPTIONS] REPORT_2D_VARIABLES). One bit per group; the tokens are the names below. Unselected groups are NOT created in the file, so readers treat every time-varying dataset as optional.
 
namespace  sigma
 
namespace  soil
 
namespace  swe
 

Classes

struct  BoundaryData
 SoA storage for per-edge boundary conditions. More...
 
struct  CouplingPoint
 Descriptor for a single coupling point between 2D and 1D. More...
 
class  ExplicitInertialSolver
 
struct  GwAquiferRow
 
struct  GwBoundaryQualityRow
 One [GW_BOUNDARY_QUALITY] row — a lateral edge BC. More...
 
struct  GwInitialQualityRow
 One [GW_INITIAL_QUALITY] row. More...
 
struct  GwNodeBed
 
struct  GwOptions
 [2D_AQUIFER_OPTIONS]. Defaults are the plan's starred values. More...
 
struct  GwParamsRow
 One [GW_TRANSPORT_PARAMS] row: solute + thermal matrix properties. More...
 
struct  GwSorptionRow
 One [GW_SORPTION] row: retardation input for one species in one scope. More...
 
struct  GwSourceRow
 One [GW_SOURCES] row — a well / point source or sink. More...
 
struct  GwSourceSpeciesTerm
 One [GW_SOURCES] species term. More...
 
struct  GwTransportData
 Every [GW_*] authoring row of one model. More...
 
struct  GwTransportOptions
 
struct  GwUnitFactors
 Project-unit → SI multipliers for the four kinds of aquifer number. More...
 
struct  InertialEdges
 Canonical unique interior-edge layout + per-cell incidence for the local-inertial scheme. More...
 
class  Infil2D
 Owns per-cell infiltration parameters, kernel state and held rates. More...
 
struct  Infil2DDefault
 
struct  Infil2DOptions
 [2D_INFILTRATION_OPTIONS]. More...
 
struct  Infil2DOverride
 One [2D_INFILTRATION] per-cell override row. More...
 
struct  Infil2DRow
 One infiltration specification: method + positional parameters + destination. More...
 
class  ISurfaceSolver
 Abstract time integrator for the 2D surface-routing ODE system. More...
 
struct  MeshData
 SoA storage for 2D mixed triangle/quad mesh geometry and topology. More...
 
struct  NodeData
 Structure-of-Arrays storage for all nodes. More...
 
struct  PendingBoundaryQualityRow
 
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...
 
struct  PendingInitialQualityRow
 One [2D_INITIAL_QUALITY] row (overland transport S1/S2). More...
 
class  RainfallInterpolator
 Builds and applies static rainfall-interpolation weights for the mesh. More...
 
struct  SolverOptions2D
 Configuration for the 2D surface routing solver. More...
 
struct  SubsurfaceConfig
 Everything the parser fills, before resolution onto cells. More...
 
class  SubsurfaceSolver
 The two-zone kernel. Owned by SurfaceRouter2D; driven by the marcher's tier hooks. More...
 
struct  SubsurfaceState
 Per-cell resolved parameters and state (SoA). More...
 
class  SurfaceRouter2D
 Top-level orchestrator for the 2D surface routing module. More...
 
struct  SurfaceStateData
 
struct  SurfaceTransportState
 

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  CellClosure2D : int8_t {
  FLAT = 0 ,
  VFR = 1
}
 Volume → free-surface closure for a 2D cell. More...
 
enum class  FaceDepth2D : int8_t {
  MEAN = 0 ,
  VFR_FACE = 1
}
 Effective conveyance depth at a shared edge for the diffusive-wave 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...
 
enum class  Backend2D : int8_t {
  CPU = 0 ,
  AUTO = 1 ,
  OMP = 2 ,
  CUDA = 3 ,
  HIP = 4 ,
  SYCL = 5
}
 Compute backend for the 2D marcher (mirrors fv::Backend). More...
 
enum class  Momentum2D : int8_t {
  LOCAL_INERTIAL = 0 ,
  FULL_SWE = 1 ,
  DIFFUSIVE_WAVE = 2
}
 Momentum closure of the explicit 2D marcher (2D_FULL_SWE_SHOCK_CAPTURING_PLAN_2026-09-05 §2). More...
 
enum class  OutputPrecision2D : int8_t {
  FLOAT32 = 0 ,
  FLOAT64 = 1
}
 Storage precision of the 2D results file's time-varying datasets ([2D_OPTIONS] OUTPUT_PRECISION). Mesh geometry always stays float64: projected coordinates are O(1e6) m and float32 would lose centimetres there; depth/velocity fields have no such offset. More...
 
enum class  GwZone : int8_t {
  SAT = 0 ,
  UNSAT = 1 ,
  LAYER = 2
}
 Which zone of the two-zone column a row addresses. More...
 
enum class  GwScope : int8_t {
  GLOBAL = 0 ,
  TAG = 1 ,
  CELL = 2
}
 Scope of a per-cell row. More...
 
enum class  Infil2DDest : int {
  LOST = 0 ,
  SUBCATCH_AQUIFER = 1 ,
  AQUIFER_2D = 2
}
 
enum class  Infil2DProvenance : std::uint8_t {
  NONE = 0 ,
  STAR = 1 ,
  TAG = 2 ,
  OVERRIDE = 3
}
 
enum class  SoilChar : int8_t {
  RUSSO = 0 ,
  GARDNER = 1 ,
  BROOKS_COREY = 2 ,
  VAN_GENUCHTEN = 3
}
 
enum class  GwClosure : int8_t {
  AUTO = -1 ,
  CLOSED_FORM = 0 ,
  ENSLAVED = 1 ,
  SIGMA = 2
}
 Unsaturated-zone closure, resolved per cell from AUTO at initialize. More...
 

Functions

double computeNodeCouplingQ (const CouplingPoint &cp, const MeshData &mesh, const SurfaceStateData &state, const NodeData &nodes, const SolverOptions2D &opts, const double *provisional_vol_m3=nullptr, double h1d_offset_m=0.0) noexcept
 Live node-coupling orifice flux for ONE non-outfall coupling point.
 
double computeNodeCouplingDQdh1d (const CouplingPoint &cp, const MeshData &mesh, const SurfaceStateData &state, const NodeData &nodes, const SolverOptions2D &opts) noexcept
 Head sensitivity G = −∂Q/∂h_1d ≥ 0 of the coupling orifice at a point (SI: m³/s per m of 1D head, with h_1d in 2D metres).
 
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 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 accumulateOutfallDischargeStep (const std::vector< CouplingPoint > &cps, const MeshData &mesh, const SurfaceStateData &state, const SimulationContext &ctx, const SolverOptions2D &opts, double dt, std::vector< double > &accum_m3, std::vector< double > &cell_budget_m3, double *sample_row)
 Per-routing-step outfall discharge accumulation.
 
void injectAccumulatedExchange (const std::vector< CouplingPoint > &cps, const MeshData &mesh, SurfaceStateData &state, const std::vector< double > &accum_m3, double window_dt, double sign, const std::vector< double > *node_conc=nullptr)
 Inject a per-point accumulated exchange volume into the 2D window source field.
 
std::vector< PendingBoundaryRowcollectBCRows (const std::vector< PendingBoundaryRow > *pending, const BoundaryData *boundary, bool drained, double flow_to_si_applied=1.0)
 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.
 
const char * gwZoneToken (GwZone z) noexcept
 
bool parseGwZone (const std::string &token, GwZone &z) noexcept
 
const char * gwScopeToken (GwScope s) noexcept
 
std::string parseGwTransportOptionsLine (const std::vector< std::string > &tokens, GwTransportOptions &opts)
 One [GW_TRANSPORT_OPTIONS] line (KEY VALUE [ARG…]). Empty = OK.
 
std::string parseGwParamsLine (const std::vector< std::string > &tokens, std::vector< GwParamsRow > &rows)
 
std::string parseGwSorptionLine (const std::vector< std::string > &tokens, std::vector< GwSorptionRow > &rows)
 One [GW_SORPTION] line: scope, SPECIES, Kd [decay]. Empty = OK.
 
std::string parseGwInitialQualityLine (const std::vector< std::string > &tokens, std::vector< GwInitialQualityRow > &rows, std::string &file)
 
std::string parseGwBoundaryQualityLine (const std::vector< std::string > &tokens, std::vector< GwBoundaryQualityRow > &rows)
 
std::string parseGwSourcesLine (const std::vector< std::string > &tokens, std::vector< GwSourceRow > &rows)
 
void registerGwTransportSections (GwTransportData &gw, input::SectionRegistry &registry)
 Register every [GW_*] handler against registry, writing into gw.
 
std::vector< std::string > resolveGwTransport (SimulationContext &ctx, const MeshData &mesh, GwTransportData &gw)
 Validate the authored rows against the model and the species registry, once the mesh and the species are final.
 
std::string gwTransportInertWarning (const GwTransportData &gw)
 
bool parseInfil2DMethod (const std::string &token, InfilModel &method, bool &has_method)
 
const char * infil2DMethodToken (const Infil2DRow &row)
 Inverse of parseInfil2DMethod — the canonical file token.
 
bool parseInfil2DDest (const std::string &token, Infil2DDest &dest)
 Parse a destination token.
 
const char * infil2DDestToken (Infil2DDest dest)
 Inverse of parseInfil2DDest.
 
int infil2DParamCount (InfilModel method)
 
std::string parse2DOptionsLine (const std::vector< std::string > &tokens, SolverOptions2D &opts, std::vector< std::string > *warnings=nullptr)
 Parse a single line from the [2D_OPTIONS] section.
 
bool is2DRetiredOptionKey (const std::string &key)
 True when key (case-insensitive) is a [2D_OPTIONS] parameter retired with the CVODE/ARKODE stack (D2, 2026-07-29).
 
bool is2DOptionKey (const std::string &key)
 True when key (case-insensitive) is a [2D_OPTIONS] parameter accepted by parse2DOptionsLine.
 
const std::vector< std::string > & report2DVarTokens ()
 Format the current value of a [2D_OPTIONS] parameter as the string token parse2DOptionsLine accepts (round-trip safe).
 
std::string parseReport2DVars (const std::string &text, unsigned &mask)
 
std::string formatReport2DVars (unsigned mask)
 
std::string format2DOptionValue (const SolverOptions2D &opts, const std::string &key)
 
std::string format2DOptionValueEx (const SolverOptions2D &opts, const Infil2D *infil, const std::string &key)
 format2DOptionValue with the E2 infiltration keys resolved against the model's [2D_INFILTRATION*] rows: INFIL_STEP falls back to the section value, INFIL_DEFAULT_METHOD to the '*' row's method. INFILTRATION stays AUTO | YES | NO as stored. infil may be null.
 
std::string format2DOptionValueEx (const SolverOptions2D &opts, const Infil2D *infil, const SubsurfaceConfig *aquifer, const std::string &key)
 As above, and additionally resolves the groundwater enables against the [2D_AQUIFER*] authoring: GROUNDWATER AUTO reports the YES|NO the kernel will actually take, and GW_ET reads the [2D_AQUIFER_OPTIONS] field that owns it. aquifer may be null.
 
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 parse2DQuadLine (const std::vector< std::string > &tokens, MeshData &mesh)
 Parse a single line from the [2D_QUADS] section.
 
std::string parse2DInitialVelocityLine (const std::vector< std::string > &tokens, MeshData &mesh)
 Parse a single line from the [2D_INITIAL_VELOCITY] 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.
 
std::string parse2DInfiltrationOptionsLine (const std::vector< std::string > &tokens, Infil2D &infil)
 §5.5 track I — parse a single [2D_INFILTRATION_OPTIONS] line.
 
std::string parse2DInfiltrationDefaultsLine (const std::vector< std::string > &tokens, Infil2D &infil)
 §5.5 track I — parse a single [2D_INFILTRATION_DEFAULTS] line.
 
std::string parse2DInfiltrationLine (const std::vector< std::string > &tokens, Infil2D &infil)
 §5.5 track I — parse a single [2D_INFILTRATION] line.
 
std::string parse2DInitialQualityLine (const std::vector< std::string > &tokens, std::vector< SurfaceRouter2D::PendingInitialQualityRow > &rows)
 S1/S2 — one [2D_INITIAL_QUALITY] line. Empty string = OK.
 
std::string parse2DBoundaryQualityLine (const std::vector< std::string > &tokens, std::vector< SurfaceRouter2D::PendingBoundaryQualityRow > &rows)
 S2 — one [2D_BOUNDARY_QUALITY] line (TRI EDGE SPECIES CONC). Empty = OK.
 
void register2DSections (MeshData &mesh, SolverOptions2D &options, std::vector< SurfaceRouter2D::PendingBoundaryRow > &pending_bc_rows, std::vector< SurfaceRouter2D::PendingEdgeConveyanceRow > &pending_ec_rows, std::vector< SurfaceRouter2D::PendingInitialQualityRow > &pending_iq_rows, std::vector< SurfaceRouter2D::PendingBoundaryQualityRow > &pending_bq_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, Infil2D *infil, const std::string &mesh_file, const std::string &inp_base_dir, std::vector< std::string > *warnings=nullptr, std::vector< SurfaceRouter2D::PendingInitialQualityRow > *pending_iq_rows=nullptr, std::vector< SurfaceRouter2D::PendingBoundaryQualityRow > *pending_bq_rows=nullptr)
 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 recomputeAllZDependents (MeshData &mesh)
 Whole-mesh form of recomputeVertexZDependents.
 
OPENSWMM_KERNEL_FN double quadWetFraction (const double *zs, double A1, double A2, double eta) noexcept
 
OPENSWMM_KERNEL_FN double quadMeanDepthFromEta (const double *zs, double A1, double A2, double eta, double eps) noexcept
 
OPENSWMM_KERNEL_FN double quadMeanDepthSlope (const double *zs, double A1, double A2, double eta, double eps) noexcept
 
OPENSWMM_KERNEL_FN double quadEtaFromMeanDepth (const double *zs, double A1, double A2, double mean_depth, double eps) noexcept
 
OPENSWMM_KERNEL_FN double quadDEtaDMeanDepth (const double *zs, double A1, double A2, double eta, double eps) noexcept
 dη/dh̄ of the regularised quad closure at stage η: 1/max(slope, eps).
 
int quadVfrPrecompute (const double *x, const double *y, const double *z, double *zs, double &A1, double &A2) noexcept
 Choose the B&S 2007 diagonal for a quad and emit its precomputed VFR data.
 
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.
 
double cellFreeSurfaceElevationOf (const MeshData &mesh, int t, double mean_depth)
 
double cellFreeSurfaceElevation (double mean_depth, double za, double zb, double zc)
 Free-surface elevation η of one triangular cell from its mean depth (render/output closure).
 
void reconstructVertexRenderDepths (const MeshData &mesh, SurfaceStateData &state, double dry_depth, int nthreads, std::vector< double > &eta_scratch)
 Wet-masked, depth-weighted free-surface reconstruction at vertices for RENDERING/OUTPUT only (writes state.vert_depth_signed).
 
double vertexHeadAt (const MeshData &mesh, const SurfaceStateData &state, int v) noexcept
 Pseudo-Laplacian head at ONE vertex, evaluated on demand from the current cell heads.
 
void reconstructVertexRenderDepths (const MeshData &mesh, SurfaceStateData &state, double dry_depth, int nthreads=1)
 
OPENSWMM_KERNEL_FN void vfrSort3 (double &z1, double &z2, double &z3) noexcept
 Sort three vertex elevations in place so z1 <= z2 <= z3.
 
OPENSWMM_KERNEL_FN double vfrWetFraction (double z1, double z2, double z3, double eta) noexcept
 
OPENSWMM_KERNEL_FN double vfrMeanDepthFromEtaExact (double z1, double z2, double z3, double eta) noexcept
 
OPENSWMM_KERNEL_FN double vfrStageAtWetFraction (double z1, double z2, double z3, double eps) noexcept
 
OPENSWMM_KERNEL_FN double vfrMeanDepthFromEta (double z1, double z2, double z3, double eta, double eps) noexcept
 
OPENSWMM_KERNEL_FN double vfrEtaFromMeanDepth (double z1, double z2, double z3, double mean_depth, double eps) noexcept
 
OPENSWMM_KERNEL_FN double vfrDryEta (double z1, double z2, double z3, double eps) noexcept
 
OPENSWMM_KERNEL_FN double vfrDEtaDMeanDepth (double z1, double z2, double z3, double eta, double eps) noexcept
 
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 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 computeBoundaryEdgeFlux (const MeshData &mesh, const SurfaceStateData &state, const SolverOptions2D &opts, double dh_eps, int i, int idx) noexcept
 Boundary-edge flux for cell i across flat mesh edge slot idx (m³/s, inflow-positive). Exported wrapper over the file-local boundary kernel so non-DW solvers (the explicit marcher) evaluate the identical 5-type boundary math per substep.
 
double evapSink (double rate, double depth, double dry_depth) noexcept
 Depth-limited evaporation sink rate (m/s) for one cell.
 
double infilSink (double rate, double depth, double dry_depth) noexcept
 Depth-limited infiltration sink rate (m/s) for one cell (plan §5.5).
 
std::unique_ptr< ISurfaceSolvermakeSurfaceSolver (const SolverOptions2D &opts, std::string *chosen=nullptr, int n_cells=0, bool plugin_capable=true)
 Construct the 2D surface solver per the runtime backend policy.
 
const char * soilCharToken (SoilChar s) noexcept
 
bool parseSoilChar (const std::string &t, SoilChar &s) noexcept
 
const char * gwClosureToken (GwClosure c) noexcept
 
bool parseGwClosure (const std::string &t, GwClosure &c) noexcept
 
std::string parseAquiferOptionsLine (const std::vector< std::string > &tokens, GwOptions &opts)
 One [2D_AQUIFER_OPTIONS] line (KEY VALUE). Empty return = OK.
 
std::string parseAquiferLine (const std::vector< std::string > &tokens, std::vector< GwAquiferRow > &rows)
 One [2D_AQUIFER] line. Empty return = OK.
 
std::string parseAquiferNodeLine (const std::vector< std::string > &tokens, std::vector< GwNodeBed > &beds, std::vector< std::string > &names)
 
void registerSubsurfaceSections (SubsurfaceConfig &cfg, std::vector< std::string > &node_names, input::SectionRegistry &registry)
 Register the three handlers against registry, writing into cfg.
 
GwUnitFactors gwUnitFactors (const SimulationContext &ctx) noexcept
 The factors for this project's unit system.
 
std::vector< std::string > resolveSubsurface (SimulationContext &ctx, const MeshData &mesh, SubsurfaceConfig &cfg, const std::vector< std::string > &names)
 
void writeSubsurfaceSections (const SubsurfaceConfig &cfg, const std::vector< std::string > &node_names, std::string &out)
 
OPENSWMM_KERNEL_FN double faceDepthFromEta (double eta, double z_lo, double z_hi) noexcept
 

Variables

constexpr int kMaxCellVerts = 4
 
constexpr int kInfil2DMaxParams = 5
 
constexpr int kQuadVfrZ = 6
 
constexpr double kVfrFlatRelief = 1.0e-9
 

Enumeration Type Documentation

◆ Backend2D

enum class openswmm::twoD::Backend2D : int8_t
strong

Compute backend for the 2D marcher (mirrors fv::Backend).

AUTO (default) lets SurfaceSolverFactory pick: a device plugin (cuda, hip, sycl) above the device mesh-size floor, the OpenMP plugin above its own (higher) floor, else the built-in CPU marcher. The named values request one backend outright and bypass the size floors; a plugin that is absent or has no usable device falls back to CPU with a stderr notice, never a hard fail. The OPENSWMM_2D_BACKEND environment variable, when set, overrides this option (same precedence as OPENSWMM_FV_BACKEND over FV_BACKEND).

Parsed from [2D_OPTIONS] BACKEND (AUTO|CPU|OMP|CUDA|HIP|SYCL).

Enumerator
CPU 

Built-in marcher (OpenMP-threaded on the host), no plugin.

AUTO 

Default: plugins above their size floors, else CPU.

OMP 

Kokkos OpenMP host plugin.

CUDA 

Kokkos CUDA device plugin (NVIDIA).

HIP 

Kokkos HIP device plugin (AMD).

SYCL 

Kokkos SYCL device plugin (Intel).

◆ 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. All five types are enforced at flux time by the explicit marcher via SurfaceFluxCalculator::boundaryEdgeFlux (and the Kokkos backend's device-side equivalent); time-varying values are resolved each step in SurfaceRouter2D::resolveBoundaryValues.

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)

◆ CellClosure2D

enum class openswmm::twoD::CellClosure2D : int8_t
strong

Volume → free-surface closure for a 2D cell.

FLAT (default, legacy) reconstructs η = tri_cz + V/A — exact only for a fully wetted cell. On a partially wet (slope/step-spanning) cell it overstates η by up to two-thirds of the cell relief, which is the driver of the water-climbs-uphill artifact (spurious head pushes thin films upslope; lake-at-rest is not a steady state at shorelines).

VFR reconstructs η from the exact stage–storage relation of the plane bed through the cell's three vertex elevations (Begnudelli & Sanders 2006/2007 volume/free-surface relationships), C¹-regularized by a wetted-area-fraction floor (VFR_MIN_WET_FRAC). Restores the C-property at shorelines. CPU solver only; the Kokkos GPU backends degrade to FLAT with a one-line notice until ported.

Parsed from [2D_OPTIONS] CELL_CLOSURE (FLAT|VFR). See plans/2d/2D_VFR_SOLVER_CLOSURE_PLAN.md.

Enumerator
FLAT 

Legacy flat-cell closure η = tri_cz + V/A (default).

VFR 

Planar-bed VFR closure (regularized), CPU solvers only.

◆ FaceDepth2D

enum class openswmm::twoD::FaceDepth2D : int8_t
strong

Effective conveyance depth at a shared edge for the diffusive-wave flux.

MEAN (default, legacy) uses the upwind cell's MEAN depth V/A — blind to where the waterline sits relative to the edge, so a cell with water pooled in its low corner can discharge across an edge whose bed is entirely above the waterline (uphill creep), and drainage strands water on slopes.

VFR_FACE reconstructs the depth at the edge from the upwind free surface and the edge's two endpoint bed elevations (Begnudelli & Sanders 2007, Eq. 14, adapted as the Manning conveyance depth): zero when the upwind surface is below the whole edge (no flow — the wetting gate), the exact partially-submerged mean when the waterline crosses the edge. C¹ in η.

Scope per solver path: BOUNDARY edges honour the mode in SurfaceFluxCalculator::boundaryEdgeFlux. Under the explicit local-inertial marcher, VFR_FACE also governs INTERIOR faces: the face flow depth becomes faceFlowDepthVfr(η_L, η_R, ze_lo, ze_hi) — the Eq. 14 wetted-edge depth of the driving surface over the shared edge's TRUE endpoint beds — so thin crests (embankments/levees/road crowns resolved as lines of high vertices) block until the water genuinely reaches the crest instead of the centroid-diluted zface (≈ ⅓-height early overtopping). MEAN keeps the legacy centroid zface bit-identical on interior faces.

Parsed from [2D_OPTIONS] FACE_RECONSTRUCTION (MEAN|VFR_FACE). See plans/2d/2D_VFR_SOLVER_CLOSURE_PLAN.md.

Enumerator
MEAN 

Legacy: upwind cell-mean depth (default).

VFR_FACE 

B&S Eq. 14 face depth + wetting gate.

◆ GwClosure

enum class openswmm::twoD::GwClosure : int8_t
strong

Unsaturated-zone closure, resolved per cell from AUTO at initialize.

Enumerator
AUTO 

select from αL at initialize (never stored per cell)

CLOSED_FORM 

closure A: bulk hu + quasi-steady recharge

ENSLAVED 

closure A reduced: hu algebraic in hg (eq. 39)

SIGMA 

closure B: explicit σ column of m layers

◆ GwScope

enum class openswmm::twoD::GwScope : int8_t
strong

Scope of a per-cell row.

Enumerator
GLOBAL 

the * row

TAG 

every cell carrying tag

CELL 

one cell, by 0-based index (1-based in the file)

◆ GwZone

enum class openswmm::twoD::GwZone : int8_t
strong

Which zone of the two-zone column a row addresses.

Enumerator
SAT 

saturated (lower) zone

UNSAT 

unsaturated (upper) zone

LAYER 

an explicit layer index (layer field)

◆ Infil2DDest

enum class openswmm::twoD::Infil2DDest : int
strong

Destination of infiltrated water. D-I4 as amended by U3 and G1: LOST and SUBCATCH_AQUIFER are routed, and AQUIFER_2D is accepted once a [2D_AQUIFER] section resolves — without one it is still refused, with a message naming the section to add.

Note
With a [2D_AQUIFER] present the aquifer OWNS infiltration: every infiltrating cell recharges it and LOST reads as AQUIFER_2D. That is what a user means by putting an aquifer under the mesh, and it keeps the water to one owner. SUBCATCH_AQUIFER is the one destination that cannot coexist with it, for the same one-owner reason the integrated component conflict already states — see SurfaceRouter2D::initialize.
Enumerator
LOST 

Leaves the domain; booked to MassBalance2D::infil_out.

SUBCATCH_AQUIFER 

Legacy subcatchment aquifer (U3 track I-b)

AQUIFER_2D 

The two-zone 2D kernel (G1 step 11b)

◆ Infil2DProvenance

enum class openswmm::twoD::Infil2DProvenance : std::uint8_t
strong

Where a resolved cell's parameters came from. Retained so the writer emits a compact file (D-I3) instead of N per-cell rows.

Enumerator
NONE 

No model resolved.

STAR 

From the '*' default row.

TAG 

From a tag default row.

OVERRIDE 

From a per-cell [2D_INFILTRATION] row.

◆ Momentum2D

enum class openswmm::twoD::Momentum2D : int8_t
strong

Momentum closure of the explicit 2D marcher (2D_FULL_SWE_SHOCK_CAPTURING_PLAN_2026-09-05 §2).

Parsed from [2D_OPTIONS] MOMENTUM_EQUATION. One marcher (tiered LTS, active sets, positivity, coupling, transport) with three face laws:

  • LOCAL_INERTIAL de Almeida & Bates face-q update (default; bit-identical to the pre-2026-09 marcher).
  • FULL_SWE conservative shallow-water equations with the convective term: cell (h, hu, hv), hydrostatic reconstruction, rotated HLLC Riemann flux, shock capturing.
  • DIFFUSIVE_WAVE Manning quasi-steady face flux, no inertia (Hunter et al. 2005 explicit diffusive wave; Δt ∝ Δx² per cell, absorbed by the LTS tiers).
Enumerator
LOCAL_INERTIAL 
FULL_SWE 
DIFFUSIVE_WAVE 

◆ OutputPrecision2D

enum class openswmm::twoD::OutputPrecision2D : int8_t
strong

Storage precision of the 2D results file's time-varying datasets ([2D_OPTIONS] OUTPUT_PRECISION). Mesh geometry always stays float64: projected coordinates are O(1e6) m and float32 would lose centimetres there; depth/velocity fields have no such offset.

Enumerator
FLOAT32 

default — halves the file, ~7 significant digits

FLOAT64 

bit-for-bit parity tooling

◆ 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.

◆ SoilChar

enum class openswmm::twoD::SoilChar : int8_t
strong

Soil-characteristic law: how K(ψ) and θ(ψ) are shaped. (Draft plan §2.5. Broadbridge–White is verification-only and is not a production selection — it lives in the test harness, not here.)

Enumerator
RUSSO 

default; Mualem-consistent, same α slot as Gardner

GARDNER 

K = Ks·e^(αψ); the closed form of eq. 22.

BROOKS_COREY 

piecewise about the air-entry pressure

VAN_GENUCHTEN 

Rosetta/UNSODA parameter databases.

Function Documentation

◆ accumulateOutfallDischargeStep()

int openswmm::twoD::accumulateOutfallDischargeStep ( const std::vector< CouplingPoint > & cps,
const MeshData & mesh,
const SurfaceStateData & state,
const SimulationContext & ctx,
const SolverOptions2D & opts,
double dt,
std::vector< double > & accum_m3,
std::vector< double > & cell_budget_m3,
double * sample_row )

Per-routing-step outfall discharge accumulation.

Per-step successor of the retired transferOutfallDischarges: samples the LIVE net outfall exchange (nodes.inflow − nodes.outflow, this routing step) and accumulates Q_net·dt (m³, + = 1D discharge onto the 2D surface, − = surface water drawn back through the outfall) into accum_m3[k]. Withdrawals are capped by the shared per-cell budget so the accumulated window sink cannot overdraw the frozen 2D state.

Parameters
sample_rowOptional (nullable): net 2D-source rate row for the interpolated-forcing series — this step's capped Q_net written as +Q_net into sample_row[k] (already source-positive). Junction slots left untouched.
Returns
Number of outfalls whose withdrawal was clamped this step.

◆ 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:

◆ cellFreeSurfaceElevation()

double openswmm::twoD::cellFreeSurfaceElevation ( double mean_depth,
double za,
double zb,
double zc )

Free-surface elevation η of one triangular cell from its mean depth (render/output closure).

Inverts the stage–storage relation V(η) of a cell whose bed is the plane through its three vertex elevations. For a partially wet cell (η below the highest vertex) this places the free surface over the wetted fraction only, instead of the flat-cell closure η = z̄ + h̄ which overstates η on cells spanning a bed step. Fully wet cells reduce exactly to η = z̄ + h̄ (tri_cz is the vertex-elevation mean), so the closure is continuous and consistent with the solver's conserved volume V = A·h̄.

Piecewise in η with sorted vertex elevations z1 ≤ z2 ≤ z3: z1 < η ≤ z2 : h̄(η) = (η−z1)³ / (3(z2−z1)(z3−z1)) (closed form) z2 < η ≤ z3 : h̄(η) = (η−z̄) + (z3−η)³ / (3(z3−z1)(z3−z2)) (safeguarded Newton) η ≥ z3 : h̄(η) = η − z̄ (flat)

Parameters
mean_depthCell mean depth h̄ = V/A (m), ≥ 0.
za,zb,zcThe cell's three vertex elevations (any order).
Returns
η (m). For mean_depth ≤ 0 returns the lowest vertex elevation.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ cellFreeSurfaceElevationOf()

double openswmm::twoD::cellFreeSurfaceElevationOf ( const MeshData & mesh,
int t,
double mean_depth )

Cell-shape-aware form: triangle → cellFreeSurfaceElevation over the three vertex beds; quad → the B&S 2007 two-plane closure (mesh/QuadVfr.hpp).

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,
double flow_to_si_applied = 1.0 )
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.
flow_to_si_appliedSolverOptions2D::bc_flow_to_si_applied — the display-flow → m³/s factor initialize() scaled the constant SPECIFIED_FLOW values by (1.0 when it has not run). The file contract is display flow units per metre, so constant flows read from boundary are divided by it. Constant STAGE heads need no such treatment: they follow the mesh scaling, and the ;; UNITS: SI (m) header the writer emits covers them.
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:

◆ computeBoundaryEdgeFlux()

double openswmm::twoD::computeBoundaryEdgeFlux ( const MeshData & mesh,
const SurfaceStateData & state,
const SolverOptions2D & opts,
double dh_eps,
int i,
int idx )
noexcept

Boundary-edge flux for cell i across flat mesh edge slot idx (m³/s, inflow-positive). Exported wrapper over the file-local boundary kernel so non-DW solvers (the explicit marcher) evaluate the identical 5-type boundary math per substep.

◆ 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 − infilSink_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, infil_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:

◆ 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:

◆ computeNodeCouplingDQdh1d()

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

Head sensitivity G = −∂Q/∂h_1d ≥ 0 of the coupling orifice at a point (SI: m³/s per m of 1D head, with h_1d in 2D metres).

Windowless-coupling stabilizer (2026-07-29 plan §5.4): scattered into the dynamic-wave node continuity denominator (sumdqdh) each Picard iteration so the exchange stops being a zero-sensitivity explicit source — the measured fix for drain/spill iteration churn. Gate/ramp derivative terms are dropped to guarantee G ≥ 0 (pure damping; the denominator can only grow).

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,
const double * provisional_vol_m3 = nullptr,
double h1d_offset_m = 0.0 )
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 the retired computeCouplingExchange (which pre-computed a HELD flux per window and capped 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.

Parameters
provisional_vol_m3Optional per-cell volume (m³) overriding the 2D state's own depth/head for the driving-head and wet/dry-ramp terms. nullptr on the live-RHS path (CVODE's state is already current). The per-routing-step decoupled path passes the window's remaining withdrawal budget so the exchange self-limits as the surface drains provisionally — without it the frozen window state makes the same full-rate drain repeat every sub-step and the window's total exchange scales with the number of routing steps in it.

◆ 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:

◆ 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:

◆ faceDepthFromEta()

OPENSWMM_KERNEL_FN double openswmm::twoD::inertial::faceDepthFromEta ( double eta,
double z_lo,
double z_hi )
noexcept

Wetted-edge flow depth from a free surface η over an edge with endpoint beds z_lo ≤ z_hi — Begnudelli & Sanders (2007) Eq. 14, the exact mean depth over the wetted portion of the edge: η ≤ z_lo : 0 (wetting gate: bed above water) z_lo < η ≤ z_hi : (η − z_lo)² / (2(z_hi − z_lo)) (partially submerged) η > z_hi : η − (z_lo + z_hi)/2 (fully submerged: mean depth) The quadratic branch matches value AND slope at both joins (C¹), so overtopping onset is smooth. Single source for the CPU boundary path, the GPU boundary kernels, and the VFR interior-face path — byte-identical to the copies it replaced in SurfaceFluxCalculator.cpp and ExplicitKokkosSurfaceSolver.cpp.

◆ format2DOptionValue()

std::string openswmm::twoD::format2DOptionValue ( const SolverOptions2D & opts,
const std::string & key )
Here is the call graph for this function:
Here is the caller graph for this function:

◆ format2DOptionValueEx() [1/2]

std::string openswmm::twoD::format2DOptionValueEx ( const SolverOptions2D & opts,
const Infil2D * infil,
const std::string & key )

format2DOptionValue with the E2 infiltration keys resolved against the model's [2D_INFILTRATION*] rows: INFIL_STEP falls back to the section value, INFIL_DEFAULT_METHOD to the '*' row's method. INFILTRATION stays AUTO | YES | NO as stored. infil may be null.

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

◆ format2DOptionValueEx() [2/2]

std::string openswmm::twoD::format2DOptionValueEx ( const SolverOptions2D & opts,
const Infil2D * infil,
const SubsurfaceConfig * aquifer,
const std::string & key )

As above, and additionally resolves the groundwater enables against the [2D_AQUIFER*] authoring: GROUNDWATER AUTO reports the YES|NO the kernel will actually take, and GW_ET reads the [2D_AQUIFER_OPTIONS] field that owns it. aquifer may be null.

Here is the call graph for this function:

◆ formatReport2DVars()

std::string openswmm::twoD::formatReport2DVars ( unsigned mask)
Here is the call graph for this function:
Here is the caller graph for this function:

◆ gwClosureToken()

const char * openswmm::twoD::gwClosureToken ( GwClosure c)
noexcept
Here is the caller graph for this function:

◆ gwScopeToken()

const char * openswmm::twoD::gwScopeToken ( GwScope s)
noexcept

◆ gwTransportInertWarning()

std::string openswmm::twoD::gwTransportInertWarning ( const GwTransportData & gw)

The single "authored but no kernel" run-time warning (U4's contract). Empty when nothing was authored.

Here is the call graph for this function:

◆ gwUnitFactors()

GwUnitFactors openswmm::twoD::gwUnitFactors ( const SimulationContext & ctx)
noexcept

The factors for this project's unit system.

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

◆ gwZoneToken()

const char * openswmm::twoD::gwZoneToken ( GwZone z)
noexcept

◆ infil2DDestToken()

const char * openswmm::twoD::infil2DDestToken ( Infil2DDest dest)

Inverse of parseInfil2DDest.

◆ infil2DMethodToken()

const char * openswmm::twoD::infil2DMethodToken ( const Infil2DRow & row)

Inverse of parseInfil2DMethod — the canonical file token.

Here is the caller graph for this function:

◆ infil2DParamCount()

int openswmm::twoD::infil2DParamCount ( InfilModel method)

Number of meaningful positional parameters for method (used by the writer to trim trailing placeholders and by validation).

◆ infilSink()

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

Depth-limited infiltration sink rate (m/s) for one cell (plan §5.5).

Identical contract to evapSink: the held per-cell rate (D-I1, SurfaceStateData::infil_rate) applies in full for depth ≥ dry_depth and shuts off through the same cubic Hermite wet/dry ramp as the cell dries. Negative rates are treated as zero (infiltration is a loss only; exfiltration is not a v1 destination — D-I4).

D-I2 sink ordering: evaporation is taken FIRST with its semantics unchanged, and infiltration is then applied against what is left, so a surface-only project (infil_rate all zero) is bitwise unchanged. Both sinks ramp to zero together as the cell dries, and every call site feeds the sum into a volume update that floors at zero, so the pair cannot leave a cell with negative water.

Parameters
rateHeld infiltration 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:

◆ injectAccumulatedExchange()

void openswmm::twoD::injectAccumulatedExchange ( const std::vector< CouplingPoint > & cps,
const MeshData & mesh,
SurfaceStateData & state,
const std::vector< double > & accum_m3,
double window_dt,
double sign,
const std::vector< double > * node_conc = nullptr )

Inject a per-point accumulated exchange volume into the 2D window source field.

Window-fire counterpart of the per-step accumulators: converts each accumulated volume to the mean rate over the window (Q_k = accum_m3[k] / window_dt) and scatters it into state.coupling_flux with the given sign convention (+1: accum is already 2D-source-positive, e.g. outfall discharge; −1: accum is 2D→1D-drain-positive, e.g. junction exchange, so the 2D-side source is its negation). coupling_flux is NOT cleared here — callers zero it once before injecting both accumulators.

Parameters
node_concS3 (nullable): the 1D nodes' published concentrations, flat [node * n_species + s] with the 2D transport stride. When given, each 2D-source volume also scatters transport.coupling_src at the point's node concentration (callers clear coupling_src alongside coupling_flux).

◆ 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:

◆ is2DRetiredOptionKey()

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

True when key (case-insensitive) is a [2D_OPTIONS] parameter retired with the CVODE/ARKODE stack (D2, 2026-07-29).

parse2DOptionsLine hard-errors on these unless given a warnings sink (the file-load path warns and ignores them); swmm_options_set_ext uses this predicate to reject them up front so they can never land in the generic ext_options map (where they would silently persist to the next save).

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,
Infil2D * infil,
const std::string & mesh_file,
const std::string & inp_base_dir,
std::vector< std::string > * warnings = nullptr,
std::vector< SurfaceRouter2D::PendingInitialQualityRow > * pending_iq_rows = nullptr,
std::vector< SurfaceRouter2D::PendingBoundaryQualityRow > * pending_bq_rows = nullptr )

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], [2D_TRIANGLE_NODE_MAP] and [2D_INFILTRATION*] 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).
infilPer-cell infiltration to populate from the sidecar's [2D_INFILTRATION*] sections (plan §5.5.5 — these are per-cell mesh attributes, so they travel with the mesh exactly like [2D_VERTICES]/[2D_TRIANGLES]). The .2dm is parsed against a detached context, so the handlers cannot reach it through ctx.twod_io.infil and it must be passed explicitly. May be null (rows are then dropped). A section present in BOTH files resolves in favour of the sidecar, matching the [2D_MESH_FILE] rule that the external file overrides the inline one; rows are never merged, so nothing is double-counted.
warningsOptional sink for retired-key warnings (see parse2DOptionsLine); pass &ctx.warnings so a legacy external mesh file still loads.
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,
bool plugin_capable = true )

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; backend ([2D_OPTIONS] BACKEND) is the model's request, overridden by OPENSWMM_2D_BACKEND when set.
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 backend (option or env) 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:

◆ parse2DBoundaryQualityLine()

std::string openswmm::twoD::parse2DBoundaryQualityLine ( const std::vector< std::string > & tokens,
std::vector< SurfaceRouter2D::PendingBoundaryQualityRow > & rows )

S2 — one [2D_BOUNDARY_QUALITY] line (TRI EDGE SPECIES CONC). Empty = OK.

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:

◆ parse2DInfiltrationDefaultsLine()

std::string openswmm::twoD::parse2DInfiltrationDefaultsLine ( const std::vector< std::string > & tokens,
Infil2D & infil )

§5.5 track I — parse a single [2D_INFILTRATION_DEFAULTS] line.

Format: TAG|* METHOD [P1 .. P5] [DEST] TAG : [2D_TRIANGLES] TAG to match, or * for the mesh-wide default. METHOD : HORTON / MODIFIED_HORTON / GREEN_AMPT / MODIFIED_GREEN_AMPT / CURVE_NUMBER / CONSTANT / NONE (parseInfil2DMethod). P1..P5 : POSITIONAL parameters in PROJECT UNITS, laid out per method exactly like legacy [INFILTRATION] (see the table on Infil2DRow). - means "unset"; trailing columns a method does not use may be omitted. DEST : LOST / SUBCATCH_AQUIFER / AQUIFER_2D (default LOST). Only LOST is accepted at validation in this release (D-I4).

Rows are appended to infil.defaults() in file order and resolved against the mesh in Infil2D::resolve() (D-I3: override > tag > * > none).

Here is the caller graph for this function:

◆ parse2DInfiltrationLine()

std::string openswmm::twoD::parse2DInfiltrationLine ( const std::vector< std::string > & tokens,
Infil2D & infil )

§5.5 track I — parse a single [2D_INFILTRATION] line.

Format: CELL METHOD [P1 .. P5] [DEST] CELL is the triangle index, 1-BASED in the file and stored 0-based in Infil2DOverride::tri. The upper bound is checked in Infil2D::resolve() — the mesh is not necessarily loaded when this section is read. The remaining columns are identical to [2D_INFILTRATION_DEFAULTS].

Rows are appended to infil.overrides() and take precedence over every default row for that cell.

Here is the caller graph for this function:

◆ parse2DInfiltrationOptionsLine()

std::string openswmm::twoD::parse2DInfiltrationOptionsLine ( const std::vector< std::string > & tokens,
Infil2D & infil )

§5.5 track I — parse a single [2D_INFILTRATION_OPTIONS] line.

Format: INFIL_STEP <hh:mm:ss> The only key. Accepts the same duration grammar as WET_STEP/DRY_STEP in [OPTIONS] (hh:mm:ss, hh:mm, or plain seconds) and stores seconds in Infil2DOptions::infil_step. 0 means "use the project WET_STEP" (D-I1).

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

◆ parse2DInitialQualityLine()

std::string openswmm::twoD::parse2DInitialQualityLine ( const std::vector< std::string > & tokens,
std::vector< SurfaceRouter2D::PendingInitialQualityRow > & rows )

S1/S2 — one [2D_INITIAL_QUALITY] line. Empty string = OK.

Here is the caller graph for this function:

◆ parse2DInitialVelocityLine()

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

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

Format: TRI U V (m/s; default 0,0 — rows may cover any subset)

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

◆ parse2DOptionsLine()

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

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

Parameters
tokensWhitespace-split tokens from the line.
optsOutput solver options to populate.
warningsWhen non-null (the .inp file-load path), keys retired with the CVODE/ARKODE stack — and retired INTEGRATOR values — are IGNORED with a WARNING 104 pushed here, so legacy models still open. When null (the programmatic swmm_options_set_ext path), they remain hard errors.
Returns
Empty string on success, or error description.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ parse2DQuadLine()

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

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

Format: V1 V2 V3 V4 MANNINGS_N [INIT_DEPTH] [TAG] Quads are appended after every triangle (cell index = n_triangles + j).

Here is the call graph for this function:
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:

◆ parseAquiferLine()

std::string openswmm::twoD::parseAquiferLine ( const std::vector< std::string > & tokens,
std::vector< GwAquiferRow > & rows )

One [2D_AQUIFER] line. Empty return = OK.

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

◆ parseAquiferNodeLine()

std::string openswmm::twoD::parseAquiferNodeLine ( const std::vector< std::string > & tokens,
std::vector< GwNodeBed > & beds,
std::vector< std::string > & names )

One [2D_AQUIFER_NODE] line. node is left as a NAME in names until resolveSubsurface maps it to an index. Empty return = OK.

Here is the caller graph for this function:

◆ parseAquiferOptionsLine()

std::string openswmm::twoD::parseAquiferOptionsLine ( const std::vector< std::string > & tokens,
GwOptions & opts )

One [2D_AQUIFER_OPTIONS] line (KEY VALUE). Empty return = OK.

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

◆ parseGwBoundaryQualityLine()

std::string openswmm::twoD::parseGwBoundaryQualityLine ( const std::vector< std::string > & tokens,
std::vector< GwBoundaryQualityRow > & rows )

One [GW_BOUNDARY_QUALITY] line: CELL EDGE SPECIES CONC v | TS n | / MASSFLUX v|n | HEATFLUX v|n. CELL is 1-based in the file, EDGE is 0-based (EDGE 0..nv-1 of that cell). Empty = OK.

Here is the caller graph for this function:

◆ parseGwClosure()

bool openswmm::twoD::parseGwClosure ( const std::string & t,
GwClosure & c )
noexcept
Here is the caller graph for this function:

◆ parseGwInitialQualityLine()

std::string openswmm::twoD::parseGwInitialQualityLine ( const std::vector< std::string > & tokens,
std::vector< GwInitialQualityRow > & rows,
std::string & file )

One [GW_INITIAL_QUALITY] line: scope, SAT|UNSAT|LAYER <j>, SPECIES, VALUE — or FILE <path>. Empty = OK.

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

◆ parseGwParamsLine()

std::string openswmm::twoD::parseGwParamsLine ( const std::vector< std::string > & tokens,
std::vector< GwParamsRow > & rows )

One [GW_TRANSPORT_PARAMS] line: * | TAG <t> | CELL <n> then the positional matrix properties (see GwParamsRow). Empty = OK.

Here is the caller graph for this function:

◆ parseGwSorptionLine()

std::string openswmm::twoD::parseGwSorptionLine ( const std::vector< std::string > & tokens,
std::vector< GwSorptionRow > & rows )

One [GW_SORPTION] line: scope, SPECIES, Kd [decay]. Empty = OK.

Here is the caller graph for this function:

◆ parseGwSourcesLine()

std::string openswmm::twoD::parseGwSourcesLine ( const std::vector< std::string > & tokens,
std::vector< GwSourceRow > & rows )

One [GW_SOURCES] line: NAME (CELL n | TAG t | XY x y) FLOW v|ts / [SPECIES (CONC|MASS) v|ts]…. Empty = OK.

Here is the caller graph for this function:

◆ parseGwTransportOptionsLine()

std::string openswmm::twoD::parseGwTransportOptionsLine ( const std::vector< std::string > & tokens,
GwTransportOptions & o )

One [GW_TRANSPORT_OPTIONS] line (KEY VALUE [ARG…]). Empty = OK.

Here is the caller graph for this function:

◆ parseGwZone()

bool openswmm::twoD::parseGwZone ( const std::string & token,
GwZone & z )
noexcept
Here is the caller graph for this function:

◆ parseInfil2DDest()

bool openswmm::twoD::parseInfil2DDest ( const std::string & token,
Infil2DDest & dest )

Parse a destination token.

Returns
false if unrecognised.
Here is the caller graph for this function:

◆ parseInfil2DMethod()

bool openswmm::twoD::parseInfil2DMethod ( const std::string & token,
InfilModel & method,
bool & has_method )

Parse a method token (HORTON, MODIFIED_HORTON, GREEN_AMPT, MODIFIED_GREEN_AMPT, CURVE_NUMBER, CONSTANT, NONE), case-insensitive.

Returns
False if the token is unrecognised. NONE returns true with has_method set false.

◆ parseReport2DVars()

std::string openswmm::twoD::parseReport2DVars ( const std::string & text,
unsigned & mask )
Here is the call graph for this function:
Here is the caller graph for this function:

◆ parseSoilChar()

bool openswmm::twoD::parseSoilChar ( const std::string & t,
SoilChar & s )
noexcept
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:

◆ quadDEtaDMeanDepth()

OPENSWMM_KERNEL_FN double openswmm::twoD::quadDEtaDMeanDepth ( const double * zs,
double A1,
double A2,
double eta,
double eps )
noexcept

dη/dh̄ of the regularised quad closure at stage η: 1/max(slope, eps).

Here is the call graph for this function:

◆ quadEtaFromMeanDepth()

OPENSWMM_KERNEL_FN double openswmm::twoD::quadEtaFromMeanDepth ( const double * zs,
double A1,
double A2,
double mean_depth,
double eps )
noexcept

Regularised free-surface elevation η(h̄) of the quad — the solver closure. Monotone inverse of quadMeanDepthFromEta for the same eps; fully wet reduces to the flat closure η = z̄_w + h̄ exactly, where z̄_w is the area-weighted mean of the two sub-triangle mean beds (== the quad's mean bed over its two-plane model).

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

◆ quadMeanDepthFromEta()

OPENSWMM_KERNEL_FN double openswmm::twoD::quadMeanDepthFromEta ( const double * zs,
double A1,
double A2,
double eta,
double eps )
noexcept

Regularised cell-mean depth h̄(η) of the quad — the area-weighted sum of the two sub-triangle closures (exact for eps == 0).

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

◆ quadMeanDepthSlope()

OPENSWMM_KERNEL_FN double openswmm::twoD::quadMeanDepthSlope ( const double * zs,
double A1,
double A2,
double eta,
double eps )
noexcept

Slope dh̄/dη of the REGULARISED sum at η: each sub-triangle contributes its wet fraction above its switch stage, eps inside its linear tail, and 0 once its tail has reached zero depth. Used by the Newton inverse.

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

◆ quadVfrPrecompute()

int openswmm::twoD::quadVfrPrecompute ( const double * x,
const double * y,
const double * z,
double * zs,
double & A1,
double & A2 )
inlinenoexcept

Choose the B&S 2007 diagonal for a quad and emit its precomputed VFR data.

Parameters
x,y,zthe four vertices in CYCLIC order (either orientation)
zsout: six elevations — sorted triple of sub-triangle 1, then sorted triple of sub-triangle 2
A1,A2out: planimetric areas of the two sub-triangles
Returns
the case number (1, 2 or 3) — for tests / diagnostics
Here is the call graph for this function:

◆ quadWetFraction()

OPENSWMM_KERNEL_FN double openswmm::twoD::quadWetFraction ( const double * zs,
double A1,
double A2,
double eta )
noexcept

Wetted-area fraction of the quad at stage η (dh̄/dη away from the ε-tail). zs: six sorted elevations (z1,z2,z3 of sub-triangle 1, then of 2).

Here is the call graph for this function:

◆ recomputeAllZDependents()

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

Whole-mesh form of recomputeVertexZDependents.

Recomputes tri_cz and edge_mz for EVERY triangle in one pass, and produces bitwise-identical values to calling the per-vertex form for every vertex in turn (same operands, same operand order, same arithmetic).

This exists because the per-vertex form scans all triangles per call: doing that once per vertex is O(nVertices x nTriangles), which is minutes on a million-cell mesh. swmm_2d_set_vertex_z_bulk writes every Z and then calls this once, making the same work O(nVertices + nTriangles).

Parameters
meshThe mesh; mesh.vz is assumed to already hold the new Zs.
Here is the call graph for this function:
Here is the caller 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:

◆ reconstructVertexRenderDepths() [1/2]

void openswmm::twoD::reconstructVertexRenderDepths ( const MeshData & mesh,
SurfaceStateData & state,
double dry_depth,
int nthreads,
std::vector< double > & eta_scratch )

Wet-masked, depth-weighted free-surface reconstruction at vertices for RENDERING/OUTPUT only (writes state.vert_depth_signed).

For each vertex, averages the free-surface elevations η_i of incident WET cells (depth ≥ dry_depth) weighted by their depths, then stores the SIGNED vertex depth η_v − z_v. Dry incident cells are excluded, so bed elevations of dry cells can never lift the reconstructed water surface up an adverse slope / bed step (the depth-weighted mean is bounded by the incident wet-cell η's — no-new-maxima). A wet cell additionally votes only where its water actually reaches the corner (wetted-contact gate, η_i > z_v), so a thin film pooled at a cell's base cannot drag the level at the cell's high vertex down to the film (no-new-minima follows from the gate — the mean is bounded below by min η_i > z_v). Consequently the emitted field is either strictly positive or the 0 no-data sentinel; readers should stay tolerant of negatives from files written by older engines. A vertex with no qualifying incident cell gets 0.

Deliberately separate from reconstructVertexHeads(): vert_head is a SOLVER field (gradients/limiters/fluxes and the active-set seed pass rely on dry-cell head = bed elevation) and must keep its semantics.

Parameters
meshThe mesh (with stencil topology built; uses stencil cell lists only, not the pseudo-Laplacian weights).
stateSurface state (reads depth[], writes vert_depth_signed[]).
dry_depthWet/dry threshold (m) — cells below it are excluded.
nthreadsOpenMP thread count for the per-vertex loop (1 = serial).
eta_scratchCaller-owned scratch resized to n_triangles(); the per-cell free-surface elevations are evaluated ONCE here and gathered per vertex. Passing a persistent buffer keeps the closure evaluations at O(nt) instead of O(nt x incidence) and avoids a per-call allocation. Bit-identical to the per-incidence evaluation it replaces (same value, same gather order).
Here is the call graph for this function:
Here is the caller graph for this function:

◆ reconstructVertexRenderDepths() [2/2]

void openswmm::twoD::reconstructVertexRenderDepths ( const MeshData & mesh,
SurfaceStateData & state,
double dry_depth,
int nthreads = 1 )
inline

Convenience overload for callers with no persistent scratch (tests, one-shot seeding): allocates the per-cell buffer for the duration of the call.

Here is the call 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,
std::vector< SurfaceRouter2D::PendingInitialQualityRow > & pending_iq_rows,
std::vector< SurfaceRouter2D::PendingBoundaryQualityRow > & pending_bq_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:

◆ registerGwTransportSections()

void openswmm::twoD::registerGwTransportSections ( GwTransportData & gw,
input::SectionRegistry & registry )

Register every [GW_*] handler against registry, writing into gw.

Here is the call graph for this function:

◆ registerSubsurfaceSections()

void openswmm::twoD::registerSubsurfaceSections ( SubsurfaceConfig & cfg,
std::vector< std::string > & node_names,
input::SectionRegistry & registry )

Register the three handlers against registry, writing into cfg.

Here is the call graph for this function:

◆ report2DVarTokens()

const std::vector< std::string > & openswmm::twoD::report2DVarTokens ( )

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).

Token ↔ bitmask helpers for [2D_OPTIONS] REPORT_2D_VARIABLES.

report2DVarTokens() lists the group tokens in bit order (DEPTH, VELOCITY, EDGE_FLUX, NODE_HEAD, SPECIES, RAINFALL, INFILTRATION, COUPLING, GRADIENTS, CONTINUITY, ENVELOPES). parseReport2DVars() accepts a whitespace/comma-separated list of those tokens or one of the presets DEFAULT | MINIMAL | ALL (case-insensitive); it returns an empty error string on success. formatReport2DVars() writes a preset name when the mask equals one, else the token list.

Here is the call graph for this function:

◆ resolveGwTransport()

std::vector< std::string > openswmm::twoD::resolveGwTransport ( SimulationContext & ctx,
const MeshData & mesh,
GwTransportData & gw )

Validate the authored rows against the model and the species registry, once the mesh and the species are final.

Cell indices must be on the mesh, EDGE must be 0..nv-1 of ITS cell (cell-generic — a triangle has 3, a quad 4), species must resolve through the one registry (pollutants, reactions species, __WATER_AGE__, __TEMPERATURE__), timeseries names must exist. Returns the diagnostics; empty on success. Also loads the [GW_INITIAL_QUALITY] FILE sidecar (relative to the .inp).

Here is the call graph for this function:

◆ resolveSubsurface()

std::vector< std::string > openswmm::twoD::resolveSubsurface ( SimulationContext & ctx,
const MeshData & mesh,
SubsurfaceConfig & cfg,
const std::vector< std::string > & names )

Resolve node names to indices and each bed's cell to a mesh cell. Returns diagnostics; empty on success.

Here is the call graph for this function:
Here is the caller 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).

Here is the call graph for this function:

◆ soilCharToken()

const char * openswmm::twoD::soilCharToken ( SoilChar s)
noexcept
Here is the caller graph for this function:

◆ 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:

◆ vertexHeadAt()

double openswmm::twoD::vertexHeadAt ( const MeshData & mesh,
const SurfaceStateData & state,
int v )
inlinenoexcept

Pseudo-Laplacian head at ONE vertex, evaluated on demand from the current cell heads.

Same CSR stencil and weights as reconstructVertexHeads(), for callers that need a handful of vertices at the current evaluation state (the coupling points inside the CVODE RHS) without paying the all-vertex pass on every RHS/Jv evaluation. The all-vertex pass still runs once per accepted window so every between-window consumer (output, held-exchange evaluation, outfall boundaries) sees exactly the values it saw when the pass lived in the RHS.

Here is the caller graph for this function:

◆ vfrDEtaDMeanDepth()

OPENSWMM_KERNEL_FN double openswmm::twoD::vfrDEtaDMeanDepth ( double z1,
double z2,
double z3,
double eta,
double eps )
noexcept

dη/dh̄ of the regularized closure at stage eta: 1/max(w(η), eps). Divide by the cell area A to get dη/dV (the preconditioner chain-rule factor; equals 1/A for a fully wet cell, matching the flat closure). Inputs must be sorted.

Here is the call graph for this function:

◆ vfrDryEta()

OPENSWMM_KERNEL_FN double openswmm::twoD::vfrDryEta ( double z1,
double z2,
double z3,
double eps )
noexcept

Dry-cell free surface of the regularized closure: η(V = 0). This is the value a dry cell's head must be seeded with under CELL_CLOSURE = VFR so that head → volume seeding (vfrMeanDepthFromEta) returns exactly 0. Inputs must be sorted.

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

◆ vfrEtaFromMeanDepth()

OPENSWMM_KERNEL_FN double openswmm::twoD::vfrEtaFromMeanDepth ( double z1,
double z2,
double z3,
double mean_depth,
double eps )
noexcept

Regularized free-surface elevation η(h̄) — the solver closure.

Exact VFR inverse while the wet fraction is ≥ eps; below that, the C¹ tangent-line tail η = η_s − (h̄_s − h̄)/eps, which bounds dη/dh̄ by 1/eps (i.e. dη/dV ≤ 1/(εA)) and lands at η(0) = η_s − h̄_s/eps ∈ (z1, η_s). eps == 0 gives the exact relation with η(h̄ ≤ 0) = z1 (render use). Fully wet (h̄ ≥ z3 − z̄) reduces to the flat closure η = z̄ + h̄ exactly. Inputs must be sorted.

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

◆ vfrMeanDepthFromEta()

OPENSWMM_KERNEL_FN double openswmm::twoD::vfrMeanDepthFromEta ( double z1,
double z2,
double z3,
double eta,
double eps )
noexcept

Regularized cell-mean depth h̄(η): exact for η ≥ η_s (wet fraction ≥ eps), tangent-line continuation below (slope eps), clamped at 0. eps == 0 gives the exact relation. The EXACT inverse of vfrEtaFromMeanDepth for the same eps, so head ↔ volume seeding round-trips (hotstart, reinitialize). Inputs must be sorted.

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

◆ vfrMeanDepthFromEtaExact()

OPENSWMM_KERNEL_FN double openswmm::twoD::vfrMeanDepthFromEtaExact ( double z1,
double z2,
double z3,
double eta )
noexcept

EXACT cell-mean depth h̄(η) of the planar-bed cell (per unit area; V = A·h̄). Returns 0 for η ≤ z1. Inputs must be sorted.

Here is the caller graph for this function:

◆ vfrSort3()

OPENSWMM_KERNEL_FN void openswmm::twoD::vfrSort3 ( double & z1,
double & z2,
double & z3 )
noexcept

Sort three vertex elevations in place so z1 <= z2 <= z3.

Here is the caller graph for this function:

◆ vfrStageAtWetFraction()

OPENSWMM_KERNEL_FN double openswmm::twoD::vfrStageAtWetFraction ( double z1,
double z2,
double z3,
double eps )
noexcept

Stage η_s at which the wetted-area fraction equals eps (0 < eps < 1). Inputs must be sorted and non-flat (z3 − z1 ≥ kVfrFlatRelief).

Here is the caller graph for this function:

◆ vfrWetFraction()

OPENSWMM_KERNEL_FN double openswmm::twoD::vfrWetFraction ( double z1,
double z2,
double z3,
double eta )
noexcept

Wetted-area fraction A_wet/A of the planar-bed cell at stage eta. Piecewise C0, monotone from 0 (η ≤ z1) to 1 (η ≥ z3). This is dh̄/dη. Inputs must be sorted (z1 <= z2 <= z3).

Here is the caller graph for this function:

◆ writeSubsurfaceSections()

void openswmm::twoD::writeSubsurfaceSections ( const SubsurfaceConfig & cfg,
const std::vector< std::string > & node_names,
std::string & out )

Write the three sections back in the project's own units — that is, the authored values verbatim — omitting anything at its default so a round-trip with no edits adds nothing.

Here is the call graph for this function:

Variable Documentation

◆ kInfil2DMaxParams

int openswmm::twoD::kInfil2DMaxParams = 5
inlineconstexpr

Number of positional parameter columns carried per row. Matches the widest legacy [INFILTRATION] method (Horton: f0 fmin decay dry_time Fmax).

◆ kMaxCellVerts

int openswmm::twoD::kMaxCellVerts = 4
inlineconstexpr

Maximum vertices (and therefore edge slots) per cell. The mesh is a mixed triangle / convex-quadrilateral mesh (2D_TRI_QUAD_MESH_PLAN_2026-09-06): every per-cell connectivity and edge SoA is padded to this fixed stride so the flat [cell * kMaxCellVerts + k] addressing the solvers, the C API, the HDF5 layout and the GUI all share stays a plain stride, never a CSR. Slots k ≥ cell_nv[c] are padding (vertex −1, neighbour −2, zero geometry).

◆ kQuadVfrZ

int openswmm::twoD::kQuadVfrZ = 6
inlineconstexpr

Number of doubles of precomputed elevation data per quad cell: two sorted sub-triangle triples.

◆ kVfrFlatRelief

double openswmm::twoD::kVfrFlatRelief = 1.0e-9
inlineconstexpr

Relief below which a cell is treated as flat (metres). Matches the guard in the pre-existing render closure (cellFreeSurfaceElevation).