OpenSWMM Engine  6.0.0-alpha.4
Data-oriented, plugin-extensible SWMM Engine (6.0.0-alpha.4)
Loading...
Searching...
No Matches
QuadVfr.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
61
62#ifndef OPENSWMM_ENGINE_2D_QUAD_VFR_HPP
63#define OPENSWMM_ENGINE_2D_QUAD_VFR_HPP
64
65#include <cmath>
66
67#include "VfrClosure.hpp"
68
69namespace openswmm::twoD {
70
73inline constexpr int kQuadVfrZ = 6;
74
77OPENSWMM_KERNEL_FN double quadWetFraction(const double* zs, double A1, double A2,
78 double eta) noexcept {
79 const double w1 = vfrWetFraction(zs[0], zs[1], zs[2], eta);
80 const double w2 = vfrWetFraction(zs[3], zs[4], zs[5], eta);
81 return (A1 * w1 + A2 * w2) / (A1 + A2);
82}
83
86OPENSWMM_KERNEL_FN double quadMeanDepthFromEta(const double* zs, double A1, double A2,
87 double eta, double eps) noexcept {
88 const double d1 = vfrMeanDepthFromEta(zs[0], zs[1], zs[2], eta, eps);
89 const double d2 = vfrMeanDepthFromEta(zs[3], zs[4], zs[5], eta, eps);
90 return (A1 * d1 + A2 * d2) / (A1 + A2);
91}
92
96OPENSWMM_KERNEL_FN double quadMeanDepthSlope(const double* zs, double A1, double A2,
97 double eta, double eps) noexcept {
98 double s = 0.0;
99 for (int k = 0; k < 2; ++k) {
100 const double z1 = zs[3 * k], z2 = zs[3 * k + 1], z3 = zs[3 * k + 2];
101 const double A = (k == 0) ? A1 : A2;
102 const double relief = z3 - z1;
103 double w;
104 if (eps <= 0.0 || relief < kVfrFlatRelief) {
105 w = vfrWetFraction(z1, z2, z3, eta);
106 } else {
107 const double eta_s = vfrStageAtWetFraction(z1, z2, z3, eps);
108 if (eta >= eta_s) {
109 w = vfrWetFraction(z1, z2, z3, eta);
110 } else {
111 const double d = vfrMeanDepthFromEta(z1, z2, z3, eta, eps);
112 w = (d > 0.0) ? eps : 0.0;
113 }
114 }
115 s += A * w;
116 }
117 return s / (A1 + A2);
118}
119
125OPENSWMM_KERNEL_FN double quadEtaFromMeanDepth(const double* zs, double A1, double A2,
126 double mean_depth, double eps) noexcept {
127 const double A = A1 + A2;
128 const double zbar1 = (zs[0] + zs[1] + zs[2]) / 3.0;
129 const double zbar2 = (zs[3] + zs[4] + zs[5]) / 3.0;
130 const double zw = (A1 * zbar1 + A2 * zbar2) / A;
131 const double ztop = (zs[2] > zs[5]) ? zs[2] : zs[5];
132 const double zlow = (zs[0] < zs[3]) ? zs[0] : zs[3];
133 const double relief = ztop - zlow;
134
135 // Flat (or degenerate) cell: the flat closure is exact.
136 if (relief < kVfrFlatRelief)
137 return zw + ((mean_depth > 0.0) ? mean_depth : 0.0);
138
139 // Fully wet: both sub-triangles submerged — flat closure exact.
140 if (mean_depth >= ztop - zw) return zw + mean_depth;
141
142 // Dry limit: the stage at which the regularised sum reaches zero — the
143 // lower of the two sub-triangle dry stages (each tail ends at η_s − h_s/ε).
144 const double dry1 = vfrDryEta(zs[0], zs[1], zs[2], eps);
145 const double dry2 = vfrDryEta(zs[3], zs[4], zs[5], eps);
146 const double lo0 = (dry1 < dry2) ? dry1 : dry2;
147 if (!(mean_depth > 0.0)) return lo0;
148
149 // Safeguarded Newton on the monotone sum over [lo0, ztop].
150 double lo = lo0, hi = ztop;
151 double eta = zw + mean_depth; // flat-closure guess
152 if (eta <= lo || eta >= hi) eta = 0.5 * (lo + hi);
153 for (int it = 0; it < 80; ++it) {
154 const double f = quadMeanDepthFromEta(zs, A1, A2, eta, eps) - mean_depth;
155 if (f > 0.0) hi = eta; else lo = eta;
156 const double df = quadMeanDepthSlope(zs, A1, A2, eta, eps);
157 double next = (df > 1.0e-12) ? eta - f / df : 0.5 * (lo + hi);
158 if (next <= lo || next >= hi) next = 0.5 * (lo + hi); // safeguard
159 if (std::abs(next - eta) < 1.0e-13 * (1.0 + relief)) return next;
160 eta = next;
161 }
162 return eta;
163}
164
166OPENSWMM_KERNEL_FN double quadDEtaDMeanDepth(const double* zs, double A1, double A2,
167 double eta, double eps) noexcept {
168 double w = quadMeanDepthSlope(zs, A1, A2, eta, eps);
169 if (w < eps) w = eps;
170 if (w < 1.0e-12) w = 1.0e-12;
171 return 1.0 / w;
172}
173
184inline int quadVfrPrecompute(const double* x, const double* y, const double* z,
185 double* zs, double& A1, double& A2) noexcept {
186 // Sort the four cyclic positions by elevation (stable insertion sort so
187 // ties keep cyclic order — any consistent choice is a valid split).
188 int p[4] = {0, 1, 2, 3};
189 for (int i = 1; i < 4; ++i) {
190 const int key = p[i];
191 int j = i - 1;
192 while (j >= 0 && z[p[j]] > z[key]) { p[j + 1] = p[j]; --j; }
193 p[j + 1] = key;
194 }
195 const int n1 = p[0], n2 = p[1], n3 = p[2], n4 = p[3];
196 auto adjacent = [](int a, int b) { const int d = (a - b + 4) % 4; return d == 1 || d == 3; };
197
198 int t1[3], t2[3], kase;
199 if (!adjacent(n1, n4)) { // Case 1: n1–n4 is a diagonal
200 kase = 1;
201 t1[0] = n1; t1[1] = n2; t1[2] = n4;
202 t2[0] = n1; t2[1] = n3; t2[2] = n4;
203 } else if (adjacent(n2, n1)) { // Case 2: diagonal n2–n4
204 kase = 2;
205 t1[0] = n1; t1[1] = n2; t1[2] = n4;
206 t2[0] = n2; t2[1] = n3; t2[2] = n4;
207 } else { // Case 3: diagonal n3–n4
208 kase = 3;
209 t1[0] = n1; t1[1] = n3; t1[2] = n4;
210 t2[0] = n2; t2[1] = n3; t2[2] = n4;
211 }
212 auto tri_area = [&](const int* t) {
213 const double dx1 = x[t[1]] - x[t[0]], dy1 = y[t[1]] - y[t[0]];
214 const double dx2 = x[t[2]] - x[t[0]], dy2 = y[t[2]] - y[t[0]];
215 return 0.5 * std::abs(dx1 * dy2 - dx2 * dy1);
216 };
217 A1 = tri_area(t1);
218 A2 = tri_area(t2);
219 zs[0] = z[t1[0]]; zs[1] = z[t1[1]]; zs[2] = z[t1[2]];
220 zs[3] = z[t2[0]]; zs[4] = z[t2[1]]; zs[5] = z[t2[2]];
221 vfrSort3(zs[0], zs[1], zs[2]);
222 vfrSort3(zs[3], zs[4], zs[5]);
223 // Degenerate sub-triangle (collinear): fold everything into the other so
224 // the closure stays well-defined (A1 + A2 > 0 was validated upstream).
225 if (!(A1 > 0.0)) { A1 = 0.0; }
226 if (!(A2 > 0.0)) { A2 = 0.0; }
227 return kase;
228}
229
230} // namespace openswmm::twoD
231
232#endif // OPENSWMM_ENGINE_2D_QUAD_VFR_HPP
#define OPENSWMM_KERNEL_FN
Definition ExplicitKokkosSurfaceSolver.cpp:18
Volume–free-surface (VFR) closure for a planar-bed triangular cell.
Definition NodeCoupling.cpp:16
OPENSWMM_KERNEL_FN double vfrMeanDepthFromEta(double z1, double z2, double z3, double eta, double eps) noexcept
Definition VfrClosure.hpp:144
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:78
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
Definition QuadVfr.hpp:125
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.
Definition QuadVfr.hpp:184
OPENSWMM_KERNEL_FN double vfrDryEta(double z1, double z2, double z3, double eps) noexcept
Definition VfrClosure.hpp:228
constexpr double kVfrFlatRelief
Definition VfrClosure.hpp:75
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).
Definition QuadVfr.hpp:166
OPENSWMM_KERNEL_FN double vfrWetFraction(double z1, double z2, double z3, double eta) noexcept
Definition VfrClosure.hpp:87
OPENSWMM_KERNEL_FN double quadMeanDepthFromEta(const double *zs, double A1, double A2, double eta, double eps) noexcept
Definition QuadVfr.hpp:86
OPENSWMM_KERNEL_FN double vfrStageAtWetFraction(double z1, double z2, double z3, double eps) noexcept
Definition VfrClosure.hpp:128
OPENSWMM_KERNEL_FN double quadWetFraction(const double *zs, double A1, double A2, double eta) noexcept
Definition QuadVfr.hpp:77
OPENSWMM_KERNEL_FN double quadMeanDepthSlope(const double *zs, double A1, double A2, double eta, double eps) noexcept
Definition QuadVfr.hpp:96
double * y
Definition odesolve.c:28