OpenSWMM Engine  6.0.0-alpha.4
Data-oriented, plugin-extensible SWMM Engine (6.0.0-alpha.4)
Loading...
Searching...
No Matches
SweKernels.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
54
55#ifndef OPENSWMM_ENGINE_2D_SWE_KERNELS_HPP
56#define OPENSWMM_ENGINE_2D_SWE_KERNELS_HPP
57
58#include <algorithm>
59#include <cmath>
60
61#ifndef OPENSWMM_KERNEL_FN
62#define OPENSWMM_KERNEL_FN inline
63#endif
64
66
67inline constexpr double kGravity = 9.80665;
68
72struct FaceSide {
73 double h = 0.0;
74 double un = 0.0;
75 double ut = 0.0;
76};
77
81struct FaceFlux {
82 double mass = 0.0;
83 double mx = 0.0;
84 double my = 0.0;
85 double sstar = 0.0;
86};
87
99OPENSWMM_KERNEL_FN void physicalFlux(const FaceSide& s, double& fh, double& fn,
100 double& ft) noexcept {
101 fh = s.h * s.un;
102 fn = s.h * s.un * s.un + 0.5 * kGravity * s.h * s.h;
103 ft = s.h * s.un * s.ut;
104}
105
119OPENSWMM_KERNEL_FN void waveSpeeds(const FaceSide& L, const FaceSide& R, double& sl,
120 double& sr) noexcept {
121 const double cl = std::sqrt(kGravity * L.h);
122 const double cr = std::sqrt(kGravity * R.h);
123 if (L.h > 0.0 && R.h > 0.0) {
124 const double ustar = 0.5 * (L.un + R.un) + cl - cr;
125 const double cstar = 0.5 * (cl + cr) + 0.25 * (L.un - R.un);
126 sl = std::min(L.un - cl, ustar - cstar);
127 sr = std::max(R.un + cr, ustar + cstar);
128 } else if (L.h > 0.0) { // dry right
129 sl = L.un - cl;
130 sr = L.un + 2.0 * cl;
131 } else if (R.h > 0.0) { // dry left
132 sl = R.un - 2.0 * cr;
133 sr = R.un + cr;
134 } else {
135 sl = sr = 0.0;
136 }
137}
138
154OPENSWMM_KERNEL_FN void hllcFlux(const FaceSide& L, const FaceSide& R, double& fh, double& fn,
155 double& ft, double& sstar) noexcept {
156 if (L.h <= 0.0 && R.h <= 0.0) {
157 fh = fn = ft = 0.0;
158 sstar = 0.0;
159 return;
160 }
161 double sl, sr;
162 waveSpeeds(L, R, sl, sr);
163 double fhl, fnl, ftl, fhr, fnr, ftr;
164 // Contact speed (Toro Eq. 10.70) — falls back to the mean normal
165 // velocity when the denominator degenerates (both sides at rest).
166 const double dl = L.h * (L.un - sl), dr = R.h * (R.un - sr);
167 const double den = dr - dl;
168 sstar = (std::fabs(den) > 1.0e-14) ? (sl * dr - sr * dl) / den : 0.5 * (L.un + R.un);
169
170 if (sl >= 0.0) {
171 physicalFlux(L, fhl, fnl, ftl);
172 fh = fhl;
173 fn = fnl;
174 ft = ftl;
175 } else if (sr <= 0.0) {
176 physicalFlux(R, fhr, fnr, ftr);
177 fh = fhr;
178 fn = fnr;
179 ft = ftr;
180 } else {
181 // HLL middle state for mass and normal momentum.
182 physicalFlux(L, fhl, fnl, ftl);
183 physicalFlux(R, fhr, fnr, ftr);
184 const double inv = 1.0 / (sr - sl);
185 fh = (sr * fhl - sl * fhr + sl * sr * (R.h - L.h)) * inv;
186 fn = (sr * fnl - sl * fnr + sl * sr * (R.h * R.un - L.h * L.un)) * inv;
187 // Tangential momentum: passive scalar upwinded on the contact.
188 ft = fh * ((sstar >= 0.0) ? L.ut : R.ut);
189 }
190}
191
221OPENSWMM_KERNEL_FN bool faceFlux(double etaL, double hL, double qxL, double qyL, double etaR,
222 double hR, double qxR, double qyR, double nx, double ny,
223 double h_dry, FaceFlux& out, double& corrL_x, double& corrL_y,
224 double& corrR_x, double& corrR_y) noexcept {
225 // Beds consistent with the storage: z = η − h̄ (FLAT closure: the cell
226 // mean bed; VFR: the flat-equivalent bed, which is what keeps a uniform η
227 // an exact rest state under either closure).
228 const double zL = etaL - hL, zR = etaR - hR;
229 const double zf = (zL > zR) ? zL : zR;
230 FaceSide L, R;
231 L.h = (etaL - zf > 0.0) ? etaL - zf : 0.0;
232 R.h = (etaR - zf > 0.0) ? etaR - zf : 0.0;
233 // A side whose reconstructed depth is negligible carries no velocity
234 // (avoids q/h blow-ups at fronts); the hydrostatic reconstruction alone
235 // already removes the "dry cell standing higher" flux.
236 const double tx = -ny, ty = nx;
237 if (hL > h_dry && L.h > 0.0) {
238 const double ux = qxL / hL, uy = qyL / hL;
239 L.un = ux * nx + uy * ny;
240 L.ut = ux * tx + uy * ty;
241 }
242 if (hR > h_dry && R.h > 0.0) {
243 const double ux = qxR / hR, uy = qyR / hR;
244 R.un = ux * nx + uy * ny;
245 R.ut = ux * tx + uy * ty;
246 }
247 // Hydrostatic pressure remains on the wet side of a blocked face.
248 // Dropping it when BOTH reconstructed depths vanish accelerates a lake
249 // away from its shoreline even though no water can cross this face.
250 const double cL = 0.5 * kGravity * (L.h * L.h - hL * hL);
251 const double cR = 0.5 * kGravity * (R.h * R.h - hR * hR);
252 corrL_x = cL * nx;
253 corrL_y = cL * ny;
254 corrR_x = -cR * nx;
255 corrR_y = -cR * ny;
256 if (L.h <= 0.0 && R.h <= 0.0) {
257 out = FaceFlux{};
258 return cL != 0.0 || cR != 0.0;
259 }
260 double fh, fn, ft, sstar;
261 hllcFlux(L, R, fh, fn, ft, sstar);
262 out.mass = fh;
263 out.mx = fn * nx + ft * tx;
264 out.my = fn * ny + ft * ty;
265 out.sstar = sstar;
266 return true;
267}
268
306OPENSWMM_KERNEL_FN bool faceFluxReconBed(double etaLf, double uxLf, double uyLf, double zL,
307 double hL_cell, double etaRf, double uxRf, double uyRf,
308 double zR, double hR_cell, double nx, double ny,
309 double h_dry, FaceFlux& out, double& corrL_x,
310 double& corrL_y, double& corrR_x, double& corrR_y,
311 double zLf, double zRf) noexcept {
312 const double zf = (zLf > zRf) ? zLf : zRf;
313 FaceSide L, R;
314 L.h = (etaLf - zf > 0.0) ? etaLf - zf : 0.0;
315 R.h = (etaRf - zf > 0.0) ? etaRf - zf : 0.0;
316 const double tx = -ny, ty = nx;
317 if (hL_cell > h_dry && L.h > 0.0) {
318 L.un = uxLf * nx + uyLf * ny;
319 L.ut = uxLf * tx + uyLf * ty;
320 }
321 if (hR_cell > h_dry && R.h > 0.0) {
322 R.un = uxRf * nx + uyRf * ny;
323 R.ut = uxRf * tx + uyRf * ty;
324 }
325 const double hLf = std::max(0.0, etaLf - zLf);
326 const double hRf = std::max(0.0, etaRf - zRf);
327 // Hydrostatic face correction plus a centered within-cell bed source.
328 const double cL = 0.5 * kGravity * (L.h * L.h - hLf * hLf - (hL_cell + hLf) * (zLf - zL));
329 const double cR = 0.5 * kGravity * (R.h * R.h - hRf * hRf - (hR_cell + hRf) * (zRf - zR));
330 corrL_x = cL * nx;
331 corrL_y = cL * ny;
332 corrR_x = -cR * nx;
333 corrR_y = -cR * ny;
334 if (L.h <= 0.0 && R.h <= 0.0) {
335 out = FaceFlux{};
336 return cL != 0.0 || cR != 0.0;
337 }
338 double fh, fn, ft, sstar;
339 hllcFlux(L, R, fh, fn, ft, sstar);
340 out.mass = fh;
341 out.mx = fn * nx + ft * tx;
342 out.my = fn * ny + ft * ty;
343 out.sstar = sstar;
344 return true;
345}
346
375OPENSWMM_KERNEL_FN bool faceFluxRecon(double etaLf, double uxLf, double uyLf, double zL,
376 double hL_cell, double etaRf, double uxRf, double uyRf,
377 double zR, double hR_cell, double nx, double ny, double h_dry,
378 FaceFlux& out, double& corrL_x, double& corrL_y,
379 double& corrR_x, double& corrR_y) noexcept {
380 return faceFluxReconBed(etaLf, uxLf, uyLf, zL, hL_cell, etaRf, uxRf, uyRf, zR, hR_cell, nx, ny,
381 h_dry, out, corrL_x, corrL_y, corrR_x, corrR_y, zL, zR);
382}
383
397OPENSWMM_KERNEL_FN double bjLimiter(double w, double wf, double wmin, double wmax) noexcept {
398 const double d = wf - w;
399 if (d > 1.0e-14)
400 return std::min(1.0, (wmax - w) / d);
401 else if (d < -1.0e-14)
402 return std::min(1.0, (wmin - w) / d);
403 return 1.0;
404}
405
418OPENSWMM_KERNEL_FN void frictionUpdate(double& qx, double& qy, double h, double n,
419 double dt) noexcept {
420 if (!(h > 0.0)) {
421 qx = qy = 0.0;
422 return;
423 }
424 const double qm = std::sqrt(qx * qx + qy * qy);
425 if (qm == 0.0)
426 return;
427 const double h73 = h * h * std::cbrt(h);
428 const double den = 1.0 + kGravity * dt * n * n * qm / h73;
429 qx /= den;
430 qy /= den;
431}
432
442OPENSWMM_KERNEL_FN double criticalDepth(double q) noexcept {
443 return std::cbrt(q * q / kGravity);
444}
445
473 double h_interior) noexcept {
474 const double rg = std::sqrt(kGravity);
475 double s = std::sqrt(h_interior > 0.0 ? h_interior : 1.0e-6);
476 for (int it = 0; it < 40; ++it) {
477 const double f = 2.0 * rg * s * s * s - r * s * s + q;
478 const double df = 6.0 * rg * s * s - 2.0 * r * s;
479 if (!(std::fabs(df) > 1.0e-12))
480 break;
481 const double s1 = s - f / df;
482 if (!(s1 > 0.0) || !std::isfinite(s1))
483 break;
484 const double step = std::fabs(s1 - s);
485 s = s1;
486 if (step < 1.0e-12 * (1.0 + s)) {
487 const double h = s * s;
488 return (h > 0.0 && std::isfinite(h)) ? h : h_interior;
489 }
490 }
491 const double h = s * s;
492 // Accept only a converged, physical root; otherwise keep the interior
493 // depth (transmissive), which is what the pre-invariant code did.
494 return (h > 0.0 && std::isfinite(h) &&
495 std::fabs(2.0 * rg * s * s * s - r * s * s + q) < 1.0e-6 * (1.0 + std::fabs(q)))
496 ? h
497 : h_interior;
498}
499
500} // namespace openswmm::twoD::swe
501
502#endif // OPENSWMM_ENGINE_2D_SWE_KERNELS_HPP
#define OPENSWMM_KERNEL_FN
Definition ExplicitKokkosSurfaceSolver.cpp:22
Definition SweKernels.hpp:65
OPENSWMM_KERNEL_FN void hllcFlux(const FaceSide &L, const FaceSide &R, double &fh, double &fn, double &ft, double &sstar) noexcept
HLLC flux in the face-normal frame.
Definition SweKernels.hpp:154
OPENSWMM_KERNEL_FN bool faceFluxReconBed(double etaLf, double uxLf, double uyLf, double zL, double hL_cell, double etaRf, double uxRf, double uyRf, double zR, double hR_cell, double nx, double ny, double h_dry, FaceFlux &out, double &corrL_x, double &corrL_y, double &corrR_x, double &corrR_y, double zLf, double zRf) noexcept
Second-order face flux with independently reconstructed depth/bed.
Definition SweKernels.hpp:306
OPENSWMM_KERNEL_FN void frictionUpdate(double &qx, double &qy, double h, double n, double dt) noexcept
Semi-implicit Manning friction on a cell's unit discharge over Δt: q⃗ ← q⃗ / (1 + g·Δt·n²·|q⃗| / h^{7...
Definition SweKernels.hpp:418
OPENSWMM_KERNEL_FN bool faceFluxRecon(double etaLf, double uxLf, double uyLf, double zL, double hL_cell, double etaRf, double uxRf, double uyRf, double zR, double hR_cell, double nx, double ny, double h_dry, FaceFlux &out, double &corrL_x, double &corrL_y, double &corrR_x, double &corrR_y) noexcept
Piecewise-constant-bed compatibility entry point.
Definition SweKernels.hpp:375
OPENSWMM_KERNEL_FN void physicalFlux(const FaceSide &s, double &fh, double &fn, double &ft) noexcept
Physical flux of the SWE in the normal frame: [h·un, h·un² + ½g h², h·un·ut].
Definition SweKernels.hpp:99
OPENSWMM_KERNEL_FN bool faceFlux(double etaL, double hL, double qxL, double qyL, double etaR, double hR, double qxR, double qyR, double nx, double ny, double h_dry, FaceFlux &out, double &corrL_x, double &corrL_y, double &corrR_x, double &corrR_y) noexcept
Full face evaluation: hydrostatic reconstruction, rotation, HLLC, rotation back, and the per-side bed...
Definition SweKernels.hpp:221
constexpr double kGravity
Definition SweKernels.hpp:67
OPENSWMM_KERNEL_FN double criticalDepth(double q) noexcept
Critical depth for a per-metre discharge q: (q²/g)^{1/3} — the ghost depth of a prescribed-flow bound...
Definition SweKernels.hpp:442
OPENSWMM_KERNEL_FN void waveSpeeds(const FaceSide &L, const FaceSide &R, double &sl, double &sr) noexcept
Wave-speed estimates S_L ≤ S_R (Toro 2001 §10.4): two-rarefaction estimate for wet–wet faces,...
Definition SweKernels.hpp:119
OPENSWMM_KERNEL_FN double depthFromInvariantAndDischarge(double r, double q, double h_interior) noexcept
Ghost depth of a SUBCRITICAL prescribed-discharge boundary.
Definition SweKernels.hpp:472
OPENSWMM_KERNEL_FN double bjLimiter(double w, double wf, double wmin, double wmax) noexcept
Barth–Jespersen limiter factor φ ∈ [0, 1] for one cell/variable given the cell value w,...
Definition SweKernels.hpp:397
Definition SweKernels.hpp:81
double my
Definition SweKernels.hpp:84
double mass
Definition SweKernels.hpp:82
double sstar
Definition SweKernels.hpp:85
double mx
Definition SweKernels.hpp:83
Definition SweKernels.hpp:72
double h
Definition SweKernels.hpp:73
double ut
Definition SweKernels.hpp:75
double un
Definition SweKernels.hpp:74