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
89OPENSWMM_KERNEL_FN void physicalFlux(const FaceSide& s, double& fh, double& fn,
90 double& ft) noexcept {
91 fh = s.h * s.un;
92 fn = s.h * s.un * s.un + 0.5 * kGravity * s.h * s.h;
93 ft = s.h * s.un * s.ut;
94}
95
99 double& sl, double& sr) noexcept {
100 const double cl = std::sqrt(kGravity * L.h);
101 const double cr = std::sqrt(kGravity * R.h);
102 if (L.h > 0.0 && R.h > 0.0) {
103 const double ustar = 0.5 * (L.un + R.un) + cl - cr;
104 const double cstar = 0.5 * (cl + cr) + 0.25 * (L.un - R.un);
105 sl = std::min(L.un - cl, ustar - cstar);
106 sr = std::max(R.un + cr, ustar + cstar);
107 } else if (L.h > 0.0) { // dry right
108 sl = L.un - cl;
109 sr = L.un + 2.0 * cl;
110 } else if (R.h > 0.0) { // dry left
111 sl = R.un - 2.0 * cr;
112 sr = R.un + cr;
113 } else {
114 sl = sr = 0.0;
115 }
116}
117
128 double& fh, double& fn, double& ft,
129 double& sstar) noexcept {
130 if (L.h <= 0.0 && R.h <= 0.0) {
131 fh = fn = ft = 0.0; sstar = 0.0;
132 return;
133 }
134 double sl, sr;
135 waveSpeeds(L, R, sl, sr);
136 double fhl, fnl, ftl, fhr, fnr, ftr;
137 physicalFlux(L, fhl, fnl, ftl);
138 physicalFlux(R, fhr, fnr, ftr);
139
140 // Contact speed (Toro Eq. 10.70) — falls back to the mean normal
141 // velocity when the denominator degenerates (both sides at rest).
142 const double dl = L.h * (L.un - sl), dr = R.h * (R.un - sr);
143 const double den = dr - dl;
144 sstar = (std::fabs(den) > 1.0e-14) ? (sl * dr - sr * dl) / den
145 : 0.5 * (L.un + R.un);
146
147 if (sl >= 0.0) {
148 fh = fhl; fn = fnl; ft = ftl;
149 } else if (sr <= 0.0) {
150 fh = fhr; fn = fnr; ft = ftr;
151 } else {
152 // HLL middle state for mass and normal momentum.
153 const double inv = 1.0 / (sr - sl);
154 fh = (sr * fhl - sl * fhr + sl * sr * (R.h - L.h)) * inv;
155 fn = (sr * fnl - sl * fnr + sl * sr * (R.h * R.un - L.h * L.un)) * inv;
156 // Tangential momentum: passive scalar upwinded on the contact.
157 ft = fh * ((sstar >= 0.0) ? L.ut : R.ut);
158 }
159}
160
176OPENSWMM_KERNEL_FN bool faceFlux(double etaL, double hL, double qxL, double qyL,
177 double etaR, double hR, double qxR, double qyR,
178 double nx, double ny, double h_dry,
179 FaceFlux& out,
180 double& corrL_x, double& corrL_y,
181 double& corrR_x, double& corrR_y) noexcept {
182 // Beds consistent with the storage: z = η − h̄ (FLAT closure: the cell
183 // mean bed; VFR: the flat-equivalent bed, which is what keeps a uniform η
184 // an exact rest state under either closure).
185 const double zL = etaL - hL, zR = etaR - hR;
186 const double zf = (zL > zR) ? zL : zR;
187 FaceSide L, R;
188 L.h = (etaL - zf > 0.0) ? etaL - zf : 0.0;
189 R.h = (etaR - zf > 0.0) ? etaR - zf : 0.0;
190 // A side whose reconstructed depth is negligible carries no velocity
191 // (avoids q/h blow-ups at fronts); the hydrostatic reconstruction alone
192 // already removes the "dry cell standing higher" flux.
193 const double tx = -ny, ty = nx;
194 if (hL > h_dry && L.h > 0.0) {
195 const double ux = qxL / hL, uy = qyL / hL;
196 L.un = ux * nx + uy * ny;
197 L.ut = ux * tx + uy * ty;
198 }
199 if (hR > h_dry && R.h > 0.0) {
200 const double ux = qxR / hR, uy = qyR / hR;
201 R.un = ux * nx + uy * ny;
202 R.ut = ux * tx + uy * ty;
203 }
204 corrL_x = corrL_y = corrR_x = corrR_y = 0.0;
205 if (L.h <= 0.0 && R.h <= 0.0) {
206 out = FaceFlux{};
207 return false;
208 }
209 double fh, fn, ft, sstar;
210 hllcFlux(L, R, fh, fn, ft, sstar);
211 out.mass = fh;
212 out.mx = fn * nx + ft * tx;
213 out.my = fn * ny + ft * ty;
214 out.sstar = sstar;
215 // Audusse bed-slope corrections (½g(h*² − h²)·n̂_out per side).
216 const double cL = 0.5 * kGravity * (L.h * L.h - hL * hL);
217 const double cR = 0.5 * kGravity * (R.h * R.h - hR * hR);
218 corrL_x = cL * nx; corrL_y = cL * ny;
219 corrR_x = -cR * nx; corrR_y = -cR * ny;
220 return true;
221}
222
235OPENSWMM_KERNEL_FN bool faceFluxRecon(double etaLf, double uxLf, double uyLf,
236 double zL, double hL_cell,
237 double etaRf, double uxRf, double uyRf,
238 double zR, double hR_cell,
239 double nx, double ny, double h_dry,
240 FaceFlux& out,
241 double& corrL_x, double& corrL_y,
242 double& corrR_x, double& corrR_y) noexcept {
243 const double zf = (zL > zR) ? zL : zR;
244 FaceSide L, R;
245 L.h = (etaLf - zf > 0.0) ? etaLf - zf : 0.0;
246 R.h = (etaRf - zf > 0.0) ? etaRf - zf : 0.0;
247 const double tx = -ny, ty = nx;
248 if (hL_cell > h_dry && L.h > 0.0) {
249 L.un = uxLf * nx + uyLf * ny;
250 L.ut = uxLf * tx + uyLf * ty;
251 }
252 if (hR_cell > h_dry && R.h > 0.0) {
253 R.un = uxRf * nx + uyRf * ny;
254 R.ut = uxRf * tx + uyRf * ty;
255 }
256 corrL_x = corrL_y = corrR_x = corrR_y = 0.0;
257 if (L.h <= 0.0 && R.h <= 0.0) { out = FaceFlux{}; return false; }
258 double fh, fn, ft, sstar;
259 hllcFlux(L, R, fh, fn, ft, sstar);
260 out.mass = fh;
261 out.mx = fn * nx + ft * tx;
262 out.my = fn * ny + ft * ty;
263 out.sstar = sstar;
264 // Audusse et al. (2004) second order with a piecewise-constant bed per
265 // cell: the correction pairs h* with the RECONSTRUCTED face depth
266 // h⁻ = η_f − z_cell (the centred bed term vanishes), so the flux keeps the
267 // interface pressure difference a linear surface implies. Reduces to the
268 // first-order form when the gradients are zero (rest state exact).
269 const double hLf = (etaLf - zL > 0.0) ? etaLf - zL : 0.0;
270 const double hRf = (etaRf - zR > 0.0) ? etaRf - zR : 0.0;
271 const double cL = 0.5 * kGravity * (L.h * L.h - hLf * hLf);
272 const double cR = 0.5 * kGravity * (R.h * R.h - hRf * hRf);
273 corrL_x = cL * nx; corrL_y = cL * ny;
274 corrR_x = -cR * nx; corrR_y = -cR * ny;
275 return true;
276}
277
281OPENSWMM_KERNEL_FN double bjLimiter(double w, double wf, double wmin, double wmax) noexcept {
282 const double d = wf - w;
283 if (d > 1.0e-14) return std::min(1.0, (wmax - w) / d);
284 else if (d < -1.0e-14) return std::min(1.0, (wmin - w) / d);
285 return 1.0;
286}
287
290OPENSWMM_KERNEL_FN void frictionUpdate(double& qx, double& qy, double h, double n,
291 double dt) noexcept {
292 if (!(h > 0.0)) { qx = qy = 0.0; return; }
293 const double qm = std::sqrt(qx * qx + qy * qy);
294 if (qm == 0.0) return;
295 const double h73 = h * h * std::cbrt(h);
296 const double den = 1.0 + kGravity * dt * n * n * qm / h73;
297 qx /= den;
298 qy /= den;
299}
300
303OPENSWMM_KERNEL_FN double criticalDepth(double q) noexcept {
304 return std::cbrt(q * q / kGravity);
305}
306
323 double h_interior) noexcept {
324 const double rg = std::sqrt(kGravity);
325 double s = std::sqrt(h_interior > 0.0 ? h_interior : 1.0e-6);
326 for (int it = 0; it < 40; ++it) {
327 const double f = 2.0 * rg * s * s * s - r * s * s + q;
328 const double df = 6.0 * rg * s * s - 2.0 * r * s;
329 if (!(std::fabs(df) > 1.0e-12)) break;
330 const double s1 = s - f / df;
331 if (!(s1 > 0.0) || !std::isfinite(s1)) break;
332 const double step = std::fabs(s1 - s);
333 s = s1;
334 if (step < 1.0e-12 * (1.0 + s)) {
335 const double h = s * s;
336 return (h > 0.0 && std::isfinite(h)) ? h : h_interior;
337 }
338 }
339 const double h = s * s;
340 // Accept only a converged, physical root; otherwise keep the interior
341 // depth (transmissive), which is what the pre-invariant code did.
342 return (h > 0.0 && std::isfinite(h) &&
343 std::fabs(2.0 * rg * s * s * s - r * s * s + q) < 1.0e-6 * (1.0 + std::fabs(q)))
344 ? h : h_interior;
345}
346
347} // namespace openswmm::twoD::swe
348
349#endif // OPENSWMM_ENGINE_2D_SWE_KERNELS_HPP
#define OPENSWMM_KERNEL_FN
Definition ExplicitKokkosSurfaceSolver.cpp:18
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:127
OPENSWMM_KERNEL_FN void frictionUpdate(double &qx, double &qy, double h, double n, double dt) noexcept
Definition SweKernels.hpp:290
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
Face flux from RECONSTRUCTED side states (RECONSTRUCTION_ORDER 2).
Definition SweKernels.hpp:235
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:89
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:176
constexpr double kGravity
Definition SweKernels.hpp:67
OPENSWMM_KERNEL_FN double criticalDepth(double q) noexcept
Definition SweKernels.hpp:303
OPENSWMM_KERNEL_FN void waveSpeeds(const FaceSide &L, const FaceSide &R, double &sl, double &sr) noexcept
Definition SweKernels.hpp:98
OPENSWMM_KERNEL_FN double depthFromInvariantAndDischarge(double r, double q, double h_interior) noexcept
Ghost depth of a SUBCRITICAL prescribed-discharge boundary.
Definition SweKernels.hpp:322
OPENSWMM_KERNEL_FN double bjLimiter(double w, double wf, double wmin, double wmax) noexcept
Definition SweKernels.hpp:281
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