OpenSWMM Engine  6.0.0-alpha.4
Data-oriented, plugin-extensible SWMM Engine (6.0.0-alpha.4)
Loading...
Searching...
No Matches
KokkosSweKernels.hpp
Go to the documentation of this file.
1// SPDX-License-Identifier: Apache-2.0
2
11
12#pragma once
13#include "KokkosTypes.hpp"
14#pragma push_macro("OPENSWMM_KERNEL_FN")
15#undef OPENSWMM_KERNEL_FN
16#define OPENSWMM_KERNEL_FN KOKKOS_INLINE_FUNCTION
18#pragma pop_macro("OPENSWMM_KERNEL_FN")
19
21// Experimental building blocks, NOT a registered FULL_SWE backend. Geometry
22// uses compact unique faces and padded four-slot cell geometry (tri/quad).
23// Caller owns validated extents, active lists and execution ordering. Views
24// must live in ExecSpace memory. No host pointers are captured by kernels.
35// Four coalesced cell blocks: elevation, u, v, depth (not interleaved).
36struct Gradients {
38};
39// Each face writes only its own left/right slots. Cell gathering must consume
40// and clear its own side in CSR order before a slot is reused or published.
44
70inline void gradients(const Mesh& mesh, const State& state, const Gradients& grad,
71 const IView& cells, int count, double dry) {
72 const int n = static_cast<int>(state.depth.extent(0));
73 Kokkos::parallel_for(
74 "swe_connected_gradients", Kokkos::RangePolicy<ExecSpace>(0, count), KOKKOS_LAMBDA(int k) {
75 const int i = cells(k);
76 grad.x(3 * n + i) = grad.y(3 * n + i) = grad.x(0 * n + i) = grad.y(0 * n + i) =
77 grad.x(1 * n + i) = grad.y(1 * n + i) = grad.x(2 * n + i) = grad.y(2 * n + i) = 0.0;
78 const double hi = state.depth(i);
79 if (hi <= 10.0 * dry)
80 return;
81 const double ei = state.head(i);
82 const double ui = state.qx(i) / hi, vi = state.qy(i) / hi;
83 const double wi[4] = {ei, ui, vi, hi};
84 double gx[4] = {}, gy[4] = {};
85 double wmin[4] = {ei, ui, vi, hi}, wmax[4] = {ei, ui, vi, hi};
86 const int begin = mesh.cell_ptr(i), end = mesh.cell_ptr(i + 1);
87 unsigned connected = 0;
88 int nfaces = 0;
89 bool shore = false;
90 for (int p = begin; p < end; ++p) {
91 const int e = mesh.cell_edge(p);
92 const int j = (mesh.cL(e) == i) ? mesh.cR(e) : mesh.cL(e);
93 const double hj = state.depth(j);
94 if (hj <= dry || !state.active(j)) {
95 shore = true;
96 continue;
97 }
98 const double ej = state.head(j);
99 const double sill = Kokkos::max(ei - hi, ej - hj);
100 // A wet neighbour behind a dry sill is not a sample of this
101 // cell's connected water surface or velocity field.
102 if (ei - sill <= dry || ej - sill <= dry) {
103 shore = true;
104 continue;
105 }
106 connected |= 1u << (p - begin);
107 const double uj = state.qx(j) / hj, vj = state.qy(j) / hj;
108 const double sgn = static_cast<double>(mesh.cell_sign(p));
109 const double nx = sgn * mesh.nx(e) * mesh.xi(e), ny = sgn * mesh.ny(e) * mesh.xi(e);
110 const double w[4] = {0.5 * (ei + ej), 0.5 * (ui + uj), 0.5 * (vi + vj),
111 0.5 * (hi + hj)};
112 const double wj[4] = {ej, uj, vj, hj};
113 for (int m = 0; m < 4; ++m) {
114 gx[m] += w[m] * nx;
115 gy[m] += w[m] * ny;
116 wmin[m] = Kokkos::min(wmin[m], wj[m]);
117 wmax[m] = Kokkos::max(wmax[m], wj[m]);
118 }
119 ++nfaces;
120 }
121 if (nfaces == 0)
122 return;
123 const int nvc = mesh.nv(i);
124 double phi[4] = {1.0, 1.0, 1.0, 1.0};
125 if (shore) {
126 if (nfaces < 2)
127 return;
128 // Fit eta and depth to connected wet neighbours. Inverse squared
129 // distance weighting makes the geometry test scale independent.
130 // Keep this extra work out of the fully wet interior path.
131 double xx = 0.0, xy = 0.0, yy = 0.0;
132 double bx[4] = {}, by[4] = {};
133 for (int p = begin; p < end; ++p) {
134 if (!(connected & (1u << (p - begin))))
135 continue;
136 const int e = mesh.cell_edge(p);
137 const int j = (mesh.cL(e) == i) ? mesh.cR(e) : mesh.cL(e);
138 const double dx = mesh.cx(j) - mesh.cx(i);
139 const double dy = mesh.cy(j) - mesh.cy(i);
140 const double wt = 1.0 / (dx * dx + dy * dy);
141 xx += wt * dx * dx;
142 xy += wt * dx * dy;
143 yy += wt * dy * dy;
144 const double wj[4] = {state.head(j), 0.0, 0.0, state.depth(j)};
145 for (int m = 0; m <= 3; m += 3) {
146 bx[m] += wt * dx * (wj[m] - wi[m]);
147 by[m] += wt * dy * (wj[m] - wi[m]);
148 }
149 }
150 const double det = xx * yy - xy * xy;
151 // Nearly opposite wet neighbours do not reliably constrain a
152 // transverse slope. Require a Gram-matrix condition number <98.
153 if (det <= 1e-2 * (xx + yy) * (xx + yy))
154 return;
155 for (int m = 0; m <= 3; m += 3) {
156 gx[m] = (yy * bx[m] - xy * by[m]) / det;
157 gy[m] = (xx * by[m] - xy * bx[m]) / det;
158 }
159 // A drying cell must export its own velocity. Extrapolating it
160 // from the remaining wet neighbours can remove water faster
161 // than momentum and accelerate the residual film.
162 gx[1] = gy[1] = gx[2] = gy[2] = 0.0;
163 phi[1] = phi[2] = 0.0;
164 } else {
165 // Physical boundary faces have no CSR entry: zero-gradient
166 // contribution w_i*n*length completes the Green-Gauss sum.
167 if (nfaces < nvc) {
168 for (int kk = 0; kk < nvc; ++kk) {
169 if (mesh.neighbour(4 * i + kk) >= 0)
170 continue;
171 const int slot = 4 * i + kk;
172 const double nx = mesh.boundary_nx(slot) * mesh.boundary_length(slot);
173 const double ny = mesh.boundary_ny(slot) * mesh.boundary_length(slot);
174 for (int m = 0; m < 4; ++m) {
175 gx[m] += wi[m] * nx;
176 gy[m] += wi[m] * ny;
177 }
178 }
179 }
180 const double inv_a = 1.0 / mesh.area(i);
181 for (int m = 0; m < 4; ++m) {
182 gx[m] *= inv_a;
183 gy[m] *= inv_a;
184 }
185 }
186 for (int p = begin; p < end; ++p) {
187 if (!(connected & (1u << (p - begin))))
188 continue;
189 const double ax = mesh.cell_arm_x(p), ay = mesh.cell_arm_y(p);
190 for (int m = 0; m < 4; ++m) {
191 const double wf = wi[m] + gx[m] * ax + gy[m] * ay;
192 phi[m] = Kokkos::min(phi[m], swe::bjLimiter(wi[m], wf, wmin[m], wmax[m]));
193 }
194 }
195 // Connected interior faces are already bounded by positive wet
196 // depths. At shore/boundary cells also constrain the omitted faces,
197 // so clipping a negative face value cannot create a different depth
198 // polynomial. Scale eta with h to preserve a flat reconstructed bed.
199 if (nfaces < nvc) {
200 double positive_scale = 1.0;
201 for (int kk = 0; kk < nvc; ++kk) {
202 const double ax = mesh.slot_arm_x(4 * i + kk);
203 const double ay = mesh.slot_arm_y(4 * i + kk);
204 const double dh = phi[3] * (gx[3] * ax + gy[3] * ay);
205 if (dh < -hi)
206 positive_scale = Kokkos::min(positive_scale, -hi / dh);
207 }
208 phi[3] *= positive_scale;
209 phi[0] *= positive_scale;
210 }
211 grad.x(3 * n + i) = phi[3] * gx[3];
212 grad.y(3 * n + i) = phi[3] * gy[3];
213 grad.x(0 * n + i) = phi[0] * gx[0];
214 grad.y(0 * n + i) = phi[0] * gy[0];
215 grad.x(1 * n + i) = phi[1] * gx[1];
216 grad.y(1 * n + i) = phi[1] * gy[1];
217 grad.x(2 * n + i) = phi[2] * gx[2];
218 grad.y(2 * n + i) = phi[2] * gy[2];
219 });
220}
221
222// Interior flux booking for a single Euler stage. Pressure corrections are
223// retained at blocked sills and are not multiplied by the conveyance factor,
224// matching the CPU wall-pressure convention. The positivity factor scales
225// mass and advective/pressure Riemann momentum together.
255inline void faces(const Mesh& mesh, const State& state, const Gradients& grad, const Transfers& acc,
256 const IView& face_list, int count, const IView& face_tier, double dt, double dry,
257 double beta, bool second_order, bool global_step) {
258 const int n = static_cast<int>(state.depth.extent(0));
259 Kokkos::parallel_for(
260 "swe_face_transfers", Kokkos::RangePolicy<ExecSpace>(0, count), KOKKOS_LAMBDA(int k) {
261 const int e = face_list(k), a = mesh.cL(e), b = mesh.cR(e);
263 double clx, cly, crx, cry;
264 bool wet;
265 if (second_order) {
266 const double ha = state.depth(a), hb = state.depth(b);
267 const double za = state.head(a) - ha, zb = state.head(b) - hb;
268 const double ax = mesh.mx(e) - mesh.cx(a), ay = mesh.my(e) - mesh.cy(a);
269 const double bx = mesh.mx(e) - mesh.cx(b), by = mesh.my(e) - mesh.cy(b);
270 const double ea = state.head(a) + grad.x(a) * ax + grad.y(a) * ay;
271 const double eb = state.head(b) + grad.x(b) * bx + grad.y(b) * by;
272 const double ua =
273 (ha > dry ? state.qx(a) / ha : 0.0) + grad.x(n + a) * ax + grad.y(n + a) * ay;
274 const double va = (ha > dry ? state.qy(a) / ha : 0.0) + grad.x(2 * n + a) * ax +
275 grad.y(2 * n + a) * ay;
276 const double ub =
277 (hb > dry ? state.qx(b) / hb : 0.0) + grad.x(n + b) * bx + grad.y(n + b) * by;
278 const double vb = (hb > dry ? state.qy(b) / hb : 0.0) + grad.x(2 * n + b) * bx +
279 grad.y(2 * n + b) * by;
280 const double hfA =
281 Kokkos::max(0.0, ha + grad.x(3 * n + a) * ax + grad.y(3 * n + a) * ay);
282 const double hfB =
283 Kokkos::max(0.0, hb + grad.x(3 * n + b) * bx + grad.y(3 * n + b) * by);
284 wet = swe::faceFluxReconBed(ea, ua, va, za, ha, eb, ub, vb, zb, hb, mesh.nx(e),
285 mesh.ny(e), dry, f, clx, cly, crx, cry, ea - hfA,
286 eb - hfB);
287 } else {
288 wet = swe::faceFlux(state.head(a), state.depth(a), state.qx(a), state.qy(a),
289 state.head(b), state.depth(b), state.qx(b), state.qy(b),
290 mesh.nx(e), mesh.ny(e), dry, f, clx, cly, crx, cry);
291 }
292 if (!wet) {
293 acc.discharge(e) = 0.0;
294 return;
295 }
296 double fh = f.mass * mesh.conveyance(e), fx = f.mx * mesh.conveyance(e),
297 fy = f.my * mesh.conveyance(e);
298 if (fh != 0.0) {
299 const int giver = fh > 0.0 ? a : b;
300 const int refire = global_step ? 1 : (1 << (state.tier(giver) - face_tier(e)));
301 const double budget =
302 (beta / mesh.nv(giver)) / refire * Kokkos::max(state.volume(giver), 0.0);
303 const double take = Kokkos::abs(fh) * mesh.xi(e) * dt;
304 if (take > budget) {
305 const double scale = take > 0.0 ? budget / take : 0.0;
306 fh *= scale;
307 fx *= scale;
308 fy *= scale;
309 }
310 }
311 const double xdt = mesh.xi(e) * dt, dv = fh * xdt;
312 acc.discharge(e) = fh;
313 acc.waterL(e) -= dv;
314 acc.waterR(e) += dv;
315 acc.mxL(e) += (-fx + clx) * xdt;
316 acc.myL(e) += (-fy + cly) * xdt;
317 acc.mxR(e) += (fx + crx) * xdt;
318 acc.myR(e) += (fy + cry) * xdt;
319 });
320}
321
322// Gather only: no closure, wall pressure, friction, sources or RK ledger
323// averaging. Returns integrated momentum increments (m^4/s), not q updates.
324// Those operations belong to the future stage driver; do not call this a
325// complete Euler/RK2 surface advance.
347inline void gather(const Mesh& mesh, const State& state, const Transfers& acc, const IView& cells,
348 int count, const DView& dmx, const DView& dmy) {
349 Kokkos::parallel_for(
350 "swe_cell_gather", Kokkos::RangePolicy<ExecSpace>(0, count), KOKKOS_LAMBDA(int k) {
351 const int i = cells(k);
352 double dv = 0.0, dx = 0.0, dy = 0.0;
353 for (int p = mesh.cell_ptr(i); p < mesh.cell_ptr(i + 1); ++p) {
354 const int e = mesh.cell_edge(p);
355 if (mesh.cell_sign(p) > 0) {
356 dv += acc.waterL(e);
357 dx += acc.mxL(e);
358 dy += acc.myL(e);
359 acc.waterL(e) = acc.mxL(e) = acc.myL(e) = 0.0;
360 } else {
361 dv += acc.waterR(e);
362 dx += acc.mxR(e);
363 dy += acc.myR(e);
364 acc.waterR(e) = acc.mxR(e) = acc.myR(e) = 0.0;
365 }
366 }
367 state.volume(i) = Kokkos::max(0.0, state.volume(i) + dv);
368 dmx(i) = dx;
369 dmy(i) = dy;
370 });
371}
372// Caller must refresh depth/head after volume/source updates before this
373// kernel. wall_ptr/slot enumerate only physical WALL boundaries. Non-wall
374// boundaries and live node exchange are separate ordered stage operations.
402inline void momentum(const Mesh& mesh, const State& state, const IView& cells, int count,
403 const DView& dmx, const DView& dmy, const IView& wall_ptr,
404 const IView& wall_slot, const DView& roughness, double dt, double dry) {
405 Kokkos::parallel_for(
406 "swe_cell_momentum", Kokkos::RangePolicy<ExecSpace>(0, count), KOKKOS_LAMBDA(int k) {
407 const int i = cells(k);
408 const double h = state.depth(i);
409 if (h <= dry) {
410 state.qx(i) = state.qy(i) = 0.0;
411 return;
412 }
413 double dx = dmx(i), dy = dmy(i);
414 for (int w = wall_ptr(i); w < wall_ptr(i + 1); ++w) {
415 const int slot = wall_slot(w);
416 const double nx = mesh.boundary_nx(slot), ny = mesh.boundary_ny(slot);
417 const double ux = state.qx(i) / h, uy = state.qy(i) / h;
418 const double un = ux * nx + uy * ny, ut = -ux * ny + uy * nx;
419 const double qxg = h * (-un * nx - ut * ny), qyg = h * (-un * ny + ut * nx);
421 double lx, ly, rx, ry;
422 if (swe::faceFlux(state.head(i), h, state.qx(i), state.qy(i), state.head(i), h, qxg,
423 qyg, nx, ny, dry, f, lx, ly, rx, ry)) {
424 const double xdt = mesh.boundary_length(slot) * dt;
425 dx += (-f.mx + lx) * xdt;
426 dy += (-f.my + ly) * xdt;
427 }
428 }
429 const double area = mesh.area(i);
430 double qx = (area * state.qx(i) + dx) / area, qy = (area * state.qy(i) + dy) / area;
431 swe::frictionUpdate(qx, qy, h, roughness(i), dt);
432 state.qx(i) = qx;
433 state.qy(i) = qy;
434 });
435}
436
437// Generic RK2 ledger finish: snapshot before either Euler stage, accumulate
438// both stages, then retain half their increment. NOT a state/closure update.
439// Snapshot and live views must not alias and must have equal extents.
451inline void averageLedger(const DView& initial, const DView& live) {
452 Kokkos::parallel_for(
453 "swe_rk2_ledger", Kokkos::RangePolicy<ExecSpace>(0, live.extent(0)),
454 KOKKOS_LAMBDA(int i) { live(i) = initial(i) + 0.5 * (live(i) - initial(i)); });
455}
456} // namespace openswmm::twoD::gpu::swe_kernels
Execution-space selection + view aliases for the Kokkos marcher plugin.
Face kernels for MOMENTUM_EQUATION FULL_SWE — the conservative shallow-water equations with the conve...
Definition KokkosSweKernels.hpp:20
void gradients(const Mesh &mesh, const State &state, const Gradients &grad, const IView &cells, int count, double dry)
Launch connected-wet-neighbor reconstruction for eta, velocity and depth gradients.
Definition KokkosSweKernels.hpp:70
void faces(const Mesh &mesh, const State &state, const Gradients &grad, const Transfers &acc, const IView &face_list, int count, const IView &face_tier, double dt, double dry, double beta, bool second_order, bool global_step)
Launch one Euler stage's interior-face water and momentum receipt booking.
Definition KokkosSweKernels.hpp:255
void gather(const Mesh &mesh, const State &state, const Transfers &acc, const IView &cells, int count, const DView &dmx, const DView &dmy)
Gather and clear selected cells' owned interior-face receipt sides.
Definition KokkosSweKernels.hpp:347
void momentum(const Mesh &mesh, const State &state, const IView &cells, int count, const DView &dmx, const DView &dmy, const IView &wall_ptr, const IView &wall_slot, const DView &roughness, double dt, double dry)
Launch selected-cell momentum adoption with physical-wall pressure and Manning friction.
Definition KokkosSweKernels.hpp:402
void averageLedger(const DView &initial, const DView &live)
Retain half the two-stage increment of a cumulative RK2 ledger.
Definition KokkosSweKernels.hpp:451
Kokkos::View< int *, MemSpace > IView
mutable int array
Definition KokkosTypes.hpp:72
Kokkos::View< double *, MemSpace > DView
mutable double array
Definition KokkosTypes.hpp:70
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 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
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 KokkosSweKernels.hpp:36
DView x
Definition KokkosSweKernels.hpp:37
DView y
Definition KokkosSweKernels.hpp:37
Definition KokkosSweKernels.hpp:25
IView cell_ptr
Definition KokkosSweKernels.hpp:26
DView cy
Definition KokkosSweKernels.hpp:27
DView area
Definition KokkosSweKernels.hpp:27
IView cell_sign
Definition KokkosSweKernels.hpp:26
DView boundary_ny
Definition KokkosSweKernels.hpp:29
IView cell_edge
Definition KokkosSweKernels.hpp:26
IView cL
Definition KokkosSweKernels.hpp:26
IView neighbour
Definition KokkosSweKernels.hpp:26
DView slot_arm_y
Definition KokkosSweKernels.hpp:28
DView slot_arm_x
Definition KokkosSweKernels.hpp:28
DView cell_arm_x
Definition KokkosSweKernels.hpp:28
DView my
Definition KokkosSweKernels.hpp:27
DView cell_arm_y
Definition KokkosSweKernels.hpp:28
DView conveyance
Definition KokkosSweKernels.hpp:27
DView mx
Definition KokkosSweKernels.hpp:27
DView cx
Definition KokkosSweKernels.hpp:27
DView ny
Definition KokkosSweKernels.hpp:27
DView boundary_length
Definition KokkosSweKernels.hpp:29
DView boundary_nx
Definition KokkosSweKernels.hpp:29
IView nv
Definition KokkosSweKernels.hpp:26
DView nx
Definition KokkosSweKernels.hpp:27
IView cR
Definition KokkosSweKernels.hpp:26
DView xi
Definition KokkosSweKernels.hpp:27
Definition KokkosSweKernels.hpp:31
DView qx
Definition KokkosSweKernels.hpp:32
DView volume
Definition KokkosSweKernels.hpp:32
DView head
Definition KokkosSweKernels.hpp:32
DView qy
Definition KokkosSweKernels.hpp:32
DView depth
Definition KokkosSweKernels.hpp:32
IView active
Definition KokkosSweKernels.hpp:33
IView tier
Definition KokkosSweKernels.hpp:33
Definition KokkosSweKernels.hpp:41
DView myL
Definition KokkosSweKernels.hpp:42
DView mxR
Definition KokkosSweKernels.hpp:42
DView mxL
Definition KokkosSweKernels.hpp:42
DView discharge
Definition KokkosSweKernels.hpp:42
DView waterR
Definition KokkosSweKernels.hpp:42
DView waterL
Definition KokkosSweKernels.hpp:42
DView myR
Definition KokkosSweKernels.hpp:42
Definition SweKernels.hpp:81
double my
Definition SweKernels.hpp:84
double mass
Definition SweKernels.hpp:82
double mx
Definition SweKernels.hpp:83