OpenSWMM Engine  6.0.0-alpha.4
Data-oriented, plugin-extensible SWMM Engine (6.0.0-alpha.4)
Loading...
Searching...
No Matches
InertialKernels.hpp
Go to the documentation of this file.
1// SPDX-License-Identifier: Apache-2.0
2//
3// Copyright 2026 Caleb Buahin
4//
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16
52
53#ifndef OPENSWMM_ENGINE_2D_INERTIAL_KERNELS_HPP
54#define OPENSWMM_ENGINE_2D_INERTIAL_KERNELS_HPP
55
56#include <algorithm>
57#include <cmath>
58
59#include "../data/MeshData.hpp"
62#include "../mesh/QuadVfr.hpp"
63
64// Portable kernel-function marker (P5 Kokkos port): host builds get plain
65// `inline`; the GPU plugin defines OPENSWMM_KERNEL_FN to
66// KOKKOS_INLINE_FUNCTION *before* including this header so the identical
67// scalar bodies compile for the device (CUDA/HIP/SYCL) as well. The math uses
68// std:: functions deliberately — CUDA/HIP provide device overloads and Kokkos
69// enables relaxed-constexpr; keeping one spelling preserves single-source
70// bit-identity with the serial marcher.
71#ifndef OPENSWMM_KERNEL_FN
72#define OPENSWMM_KERNEL_FN inline
73#endif
74
76
77inline constexpr double kGravity = 9.80665;
78
85inline constexpr double kEtaDeadband = 1.0e-12;
86
97OPENSWMM_KERNEL_FN double momentumBlendFraction(double theta, double dt,
98 double reference_step) noexcept {
99 if (!(reference_step > 0.0))
100 return 1.0 - theta;
101 if (!(dt > 0.0) || theta == 1.0)
102 return 0.0;
103 return -std::expm1(std::log(theta) * (dt / reference_step));
104}
105
123OPENSWMM_KERNEL_FN double qMagnitude(double x, double y) noexcept {
124 return std::sqrt(x * x + y * y);
125}
126
148OPENSWMM_KERNEL_FN void etaDepthScalar(double area, double cz, double z1, double z2, double z3,
149 bool vfr, double vfr_min_wet_frac, double V, double& eta,
150 double& depth) noexcept {
151 const double v = (V > 0.0) ? V : 0.0;
152 depth = (area > 1.0e-30) ? v / area : 0.0;
153 if (vfr) {
154 vfrSort3(z1, z2, z3);
155 eta = vfrEtaFromMeanDepth(z1, z2, z3, depth, vfr_min_wet_frac);
156 } else {
157 eta = cz + depth;
158 }
159}
160
179OPENSWMM_KERNEL_FN double volumeFromEtaScalar(double area, double cz, double z1, double z2,
180 double z3, bool vfr, double vfr_min_wet_frac,
181 double eta) noexcept {
182 if (vfr) {
183 vfrSort3(z1, z2, z3);
184 return area * vfrMeanDepthFromEta(z1, z2, z3, eta, vfr_min_wet_frac);
185 }
186 const double d = eta - cz;
187 return (d > 0.0) ? area * d : 0.0;
188}
189
212OPENSWMM_KERNEL_FN void etaDepthQuadScalar(double area, double cz, const double* zs, double A1,
213 double A2, bool vfr, double vfr_min_wet_frac, double V,
214 double& eta, double& depth) noexcept {
215 const double v = (V > 0.0) ? V : 0.0;
216 depth = (area > 1.0e-30) ? v / area : 0.0;
217 if (vfr)
218 eta = quadEtaFromMeanDepth(zs, A1, A2, depth, vfr_min_wet_frac);
219 else
220 eta = cz + depth;
221}
222
242OPENSWMM_KERNEL_FN double volumeFromEtaQuadScalar(double area, double cz, const double* zs,
243 double A1, double A2, bool vfr,
244 double vfr_min_wet_frac, double eta) noexcept {
245 if (vfr)
246 return area * quadMeanDepthFromEta(zs, A1, A2, eta, vfr_min_wet_frac);
247 const double d = eta - cz;
248 return (d > 0.0) ? area * d : 0.0;
249}
250
251#if defined(__GNUC__) || defined(__clang__)
252#define OPENSWMM_2D_NOINLINE __attribute__((noinline))
253#elif defined(_MSC_VER)
254#define OPENSWMM_2D_NOINLINE __declspec(noinline)
255#else
256#define OPENSWMM_2D_NOINLINE
257#endif
258
277 int i, double V, double& eta,
278 double& depth) noexcept {
280 m.tri_area[i], m.tri_cz[i], &m.quad_vfr_z[static_cast<std::size_t>(i) * kQuadVfrZ],
281 m.quad_vfr_a[static_cast<std::size_t>(i) * 2],
282 m.quad_vfr_a[static_cast<std::size_t>(i) * 2 + 1], true, o.vfr_min_wet_frac, V, eta, depth);
283}
284
301inline void cellEtaDepth(const MeshData& m, const SolverOptions2D& o, int i, double V, double& eta,
302 double& depth) noexcept {
303 // Branch BEFORE the geometry loads: under the default FLAT closure the
304 // three vertex-index loads and the three scattered vz gathers below are
305 // dead, and this is the hottest call in the marcher (every cell, every
306 // firing).
307 if (o.cell_closure != CellClosure2D::VFR) {
308 etaDepthScalar(m.tri_area[i], m.tri_cz[i], 0.0, 0.0, 0.0, false, o.vfr_min_wet_frac, V, eta,
309 depth);
310 return;
311 }
312 if (m.cell_nv[static_cast<std::size_t>(i)] == 4) {
313 cellEtaDepthQuad(m, o, i, V, eta, depth);
314 return;
315 }
316 etaDepthScalar(m.tri_area[i], m.tri_cz[i], m.vz[m.cell_vertex(i, 0)], m.vz[m.cell_vertex(i, 1)],
317 m.vz[m.cell_vertex(i, 2)], true, o.vfr_min_wet_frac, V, eta, depth);
318}
319
334inline double cellWetArea(const MeshData& m, const SolverOptions2D& o, int i,
335 double volume) noexcept {
336 if (!(volume > 0))
337 return 0;
338 if (o.cell_closure != CellClosure2D::VFR)
339 return m.tri_area[i];
340 double eta = 0, depth = 0;
341 cellEtaDepth(m, o, i, volume, eta, depth);
342 double fraction;
343 if (m.cell_nv[std::size_t(i)] == 4)
344 fraction = quadWetFraction(&m.quad_vfr_z[std::size_t(i) * kQuadVfrZ],
345 m.quad_vfr_a[std::size_t(i) * 2],
346 m.quad_vfr_a[std::size_t(i) * 2 + 1], eta);
347 else {
348 double a = m.vz[m.cell_vertex(i, 0)], b = m.vz[m.cell_vertex(i, 1)],
349 c = m.vz[m.cell_vertex(i, 2)];
350 vfrSort3(a, b, c);
351 fraction = vfrWetFraction(a, b, c, eta);
352 }
353 return m.tri_area[i] * std::clamp(fraction, 0., 1.);
354}
355
371inline double cellVolumeFromEta(const MeshData& m, const SolverOptions2D& o, int i,
372 double eta) noexcept {
373 if (m.cell_nv[static_cast<std::size_t>(i)] == 4) {
375 m.tri_area[i], m.tri_cz[i], &m.quad_vfr_z[static_cast<std::size_t>(i) * kQuadVfrZ],
376 m.quad_vfr_a[static_cast<std::size_t>(i) * 2],
377 m.quad_vfr_a[static_cast<std::size_t>(i) * 2 + 1], o.cell_closure == CellClosure2D::VFR,
378 o.vfr_min_wet_frac, eta);
379 }
380 return volumeFromEtaScalar(m.tri_area[i], m.tri_cz[i], m.vz[m.cell_vertex(i, 0)],
381 m.vz[m.cell_vertex(i, 1)], m.vz[m.cell_vertex(i, 2)],
382 o.cell_closure == CellClosure2D::VFR, o.vfr_min_wet_frac, eta);
383}
384
399OPENSWMM_KERNEL_FN double faceFlowDepth(double etaL, double etaR, double zface) noexcept {
400 return std::max(etaL, etaR) - zface;
401}
402
421OPENSWMM_KERNEL_FN double faceDepthFromEta(double eta, double z_lo, double z_hi) noexcept {
422 if (eta <= z_lo)
423 return 0.0;
424 const double dz = z_hi - z_lo;
425 if (dz < 1.0e-9)
426 return eta - z_lo; // level edge
427 if (eta <= z_hi) {
428 const double t = eta - z_lo;
429 return t * t / (2.0 * dz);
430 }
431 return eta - 0.5 * (z_lo + z_hi);
432}
433
450OPENSWMM_KERNEL_FN double faceFlowDepthVfr(double etaL, double etaR, double z_lo,
451 double z_hi) noexcept {
452 return faceDepthFromEta(std::max(etaL, etaR), z_lo, z_hi);
453}
454
485OPENSWMM_KERNEL_FN double inertialFaceUpdate(double q, double qhat, double hf, double dt,
486 double slope, double n2, double q_mag,
487 double adv = 0.0) noexcept {
488 const double h73 = hf * hf * std::cbrt(hf);
489 const double num = qhat - dt * (kGravity * hf * slope + adv);
490 const double den = 1.0 + kGravity * dt * n2 * q_mag / h73;
491 return num / den;
492}
493
519OPENSWMM_KERNEL_FN double inertialAdvection(double q_f, double unL, double hL, double unR,
520 double hR, double inv_dx_normal) noexcept {
521 const double FL = unL * ((unL > 0.0) ? hL * unL : q_f);
522 const double FR = unR * ((unR > 0.0) ? q_f : hR * unR);
523 return (FR - FL) * inv_dx_normal;
524}
525
539OPENSWMM_KERNEL_FN double froudeCap(double q, double hf, double fr_max) noexcept {
540 const double qcap = fr_max * hf * std::sqrt(kGravity * hf);
541 return std::clamp(q, -qcap, qcap);
542}
543
558OPENSWMM_KERNEL_FN double cellCflDt(double alpha, double lchar, double h, double speed) noexcept {
559 const double c = std::sqrt(kGravity * h) + speed;
560 return (c > 1.0e-12) ? alpha * lchar / c : 1.0e30;
561}
562
577OPENSWMM_KERNEL_FN double positivityScale(double V, double outflow_m3s, double dt,
578 double beta) noexcept {
579 if (outflow_m3s <= 0.0 || dt <= 0.0)
580 return 1.0;
581 const double avail = beta * std::max(V, 0.0);
582 const double take = outflow_m3s * dt;
583 return (take <= avail) ? 1.0 : avail / take;
584}
585
586} // namespace openswmm::twoD::inertial
587
588#endif // OPENSWMM_ENGINE_2D_INERTIAL_KERNELS_HPP
#define OPENSWMM_KERNEL_FN
Definition ExplicitKokkosSurfaceSolver.cpp:22
#define OPENSWMM_2D_NOINLINE
Definition InertialKernels.hpp:256
Structure-of-Arrays (SoA) storage for 2D triangular mesh geometry.
Volume–free-surface relationship (VFR) for a quadrilateral cell.
Configuration options for the 2D surface routing solver.
Volume–free-surface (VFR) closure for a planar-bed triangular cell.
Definition InertialKernels.hpp:75
OPENSWMM_KERNEL_FN double momentumBlendFraction(double theta, double dt, double reference_step) noexcept
Fraction of reconstructed momentum mixed over one physical face step.
Definition InertialKernels.hpp:97
void cellEtaDepth(const MeshData &m, const SolverOptions2D &o, int i, double V, double &eta, double &depth) noexcept
Volume → (η, depth) closure — the SAME semantics as the CVODE/ARKODE reconstructFromVolume: depth = m...
Definition InertialKernels.hpp:301
OPENSWMM_KERNEL_FN double froudeCap(double q, double hf, double fr_max) noexcept
Froude-number clamp: |q| ≤ Fr_max · h_f · √(g·h_f). The steep-face guard — the local-inertial scheme ...
Definition InertialKernels.hpp:539
OPENSWMM_KERNEL_FN double qMagnitude(double x, double y) noexcept
|(x, y)| for the marcher's discharge vectors.
Definition InertialKernels.hpp:123
double cellVolumeFromEta(const MeshData &m, const SolverOptions2D &o, int i, double eta) noexcept
Exact inverse of the closure: cell volume holding free surface η — FLAT A·max(0, η − z_c); VFR the re...
Definition InertialKernels.hpp:371
OPENSWMM_KERNEL_FN double inertialAdvection(double q_f, double unL, double hL, double unR, double hR, double inv_dx_normal) noexcept
Convective momentum flux difference ∂(u·q)/∂n at an interior face — [2D_OPTIONS] ADVECTION,...
Definition InertialKernels.hpp:519
OPENSWMM_KERNEL_FN double cellCflDt(double alpha, double lchar, double h, double speed) noexcept
Per-cell CFL stable step: dt = α·L_char/(√(g·h) + |u|), with |u| = |q⃗|/h the cell speed from the Per...
Definition InertialKernels.hpp:558
constexpr double kEtaDeadband
Definition InertialKernels.hpp:85
OPENSWMM_KERNEL_FN double volumeFromEtaScalar(double area, double cz, double z1, double z2, double z3, bool vfr, double vfr_min_wet_frac, double eta) noexcept
Scalar core of the η → V inverse (device-callable).
Definition InertialKernels.hpp:179
double cellWetArea(const MeshData &m, const SolverOptions2D &o, int i, double volume) noexcept
Physical planimetric water contact, distinct from the cell-mean depth V/A returned above....
Definition InertialKernels.hpp:334
OPENSWMM_2D_NOINLINE void cellEtaDepthQuad(const MeshData &m, const SolverOptions2D &o, int i, double V, double &eta, double &depth) noexcept
Quad (B&S 2007 two-plane) VFR closure, kept OUT OF LINE on purpose: its safeguarded Newton (QuadVfr....
Definition InertialKernels.hpp:276
OPENSWMM_KERNEL_FN void etaDepthScalar(double area, double cz, double z1, double z2, double z3, bool vfr, double vfr_min_wet_frac, double V, double &eta, double &depth) noexcept
Scalar core of the V → (η, depth) closure (device-callable; the MeshData wrapper below loads the geom...
Definition InertialKernels.hpp:148
OPENSWMM_KERNEL_FN double positivityScale(double V, double outflow_m3s, double dt, double beta) noexcept
Positivity scale factor λ for a cell about to export volume: outgoing fluxes (+ explicit sinks) may t...
Definition InertialKernels.hpp:577
OPENSWMM_KERNEL_FN double faceFlowDepth(double etaL, double etaR, double zface) noexcept
Flow depth at a face: the water surface above the higher of the two interface beds....
Definition InertialKernels.hpp:399
OPENSWMM_KERNEL_FN double volumeFromEtaQuadScalar(double area, double cz, const double *zs, double A1, double A2, bool vfr, double vfr_min_wet_frac, double eta) noexcept
Quad η → V inverse (device-callable).
Definition InertialKernels.hpp:242
OPENSWMM_KERNEL_FN double inertialFaceUpdate(double q, double qhat, double hf, double dt, double slope, double n2, double q_mag, double adv=0.0) noexcept
One local-inertial face update over Δt. q is the current face discharge, qhat the θ-averaged discharg...
Definition InertialKernels.hpp:485
OPENSWMM_KERNEL_FN void etaDepthQuadScalar(double area, double cz, const double *zs, double A1, double A2, bool vfr, double vfr_min_wet_frac, double V, double &eta, double &depth) noexcept
Quad (B&S 2007 two-plane) V → (η, depth) closure core. zs / A1 / A2 are the precomputed quad VFR data...
Definition InertialKernels.hpp:212
constexpr double kGravity
matches the existing kernels
Definition InertialKernels.hpp:77
OPENSWMM_KERNEL_FN double 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...
Definition InertialKernels.hpp:421
OPENSWMM_KERNEL_FN double faceFlowDepthVfr(double etaL, double etaR, double z_lo, double z_hi) noexcept
Flow depth at an interior face under FACE_RECONSTRUCTION = VFR_FACE: the B&S Eq. 14 wetted-edge depth...
Definition InertialKernels.hpp:450
OPENSWMM_KERNEL_FN double 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 ...
Definition VfrClosure.hpp:214
OPENSWMM_KERNEL_FN void vfrSort3(double &z1, double &z2, double &z3) noexcept
Sort three vertex elevations in place so z1 <= z2 <= z3.
Definition VfrClosure.hpp:85
constexpr int kQuadVfrZ
Definition QuadVfr.hpp:73
OPENSWMM_KERNEL_FN double 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 quadMe...
Definition QuadVfr.hpp:177
OPENSWMM_KERNEL_FN double 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 (η ≤ ...
Definition VfrClosure.hpp:117
OPENSWMM_KERNEL_FN double 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 closure...
Definition QuadVfr.hpp:110
OPENSWMM_KERNEL_FN double 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 ...
Definition QuadVfr.hpp:88
OPENSWMM_KERNEL_FN double vfrEtaFromMeanDepth(double z1, double z2, double z3, double mean_depth, double eps) noexcept
Regularized free-surface elevation η(h̄) — the solver closure.
Definition VfrClosure.hpp:249
@ VFR
Planar-bed VFR closure (regularized), CPU solvers only.
Definition SolverOptions2D.hpp:62
double * y
Definition odesolve.c:37
SoA storage for 2D mixed triangle/quad mesh geometry and topology.
Definition MeshData.hpp:65
Definition SolverOptions2D.hpp:257