OpenSWMM Engine  6.0.0-alpha.4
Data-oriented, plugin-extensible SWMM Engine (6.0.0-alpha.4)
Loading...
Searching...
No Matches
SectionGeometry.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
57
58#ifndef OPENSWMM_ENGINE_FV_SECTION_GEOMETRY_HPP
59#define OPENSWMM_ENGINE_FV_SECTION_GEOMETRY_HPP
60
61#include <algorithm>
62#include <cmath>
63
64#include "../XSectBatch.hpp"
65
67
68inline constexpr double kPi = 3.14159265358979323846;
69
73inline constexpr double kRectAlfMax = 0.97;
74
75namespace detail {
76
86inline double circTheta(double r, double y) {
87 double c = 1.0 - y / r;
88 if (c > 1.0)
89 c = 1.0;
90 if (c < -1.0)
91 c = -1.0;
92 return 2.0 * std::acos(c);
93}
94
103inline double circArea(double r, double y) {
104 if (y <= 0.0)
105 return 0.0;
106 if (y >= 2.0 * r)
107 return kPi * r * r;
108 const double th = circTheta(r, y);
109 return 0.5 * r * r * (th - std::sin(th));
110}
111
120inline double circWidth(double r, double y) {
121 if (y <= 0.0 || y >= 2.0 * r)
122 return 0.0;
123 const double s = y * (2.0 * r - y);
124 return (s > 0.0) ? 2.0 * std::sqrt(s) : 0.0;
125}
126
136inline double circPerim(double r, double y) {
137 if (y <= 0.0)
138 return 0.0;
139 if (y >= 2.0 * r)
140 return 2.0 * kPi * r;
141 return r * circTheta(r, y);
142}
143
164template <class F>
165double simpsonStep(F& f, double a, double b, double fa, double fm, double fb, double whole,
166 double eps, int depth) {
167 const double m = 0.5 * (a + b);
168 const double lm = 0.5 * (a + m), rm = 0.5 * (m + b);
169 const double flm = f(lm), frm = f(rm);
170 const double left = (m - a) / 6.0 * (fa + 4.0 * flm + fm);
171 const double right = (b - m) / 6.0 * (fm + 4.0 * frm + fb);
172 const double delta = left + right - whole;
173 if (depth <= 0 || std::fabs(delta) <= 15.0 * eps)
174 return left + right + delta / 15.0;
175 return simpsonStep(f, a, m, fa, flm, fm, left, 0.5 * eps, depth - 1) +
176 simpsonStep(f, m, b, fm, frm, fb, right, 0.5 * eps, depth - 1);
177}
178
192template <class F> double integrate(F f, double a, double b, double eps = 1.0e-12, int depth = 40) {
193 if (!(b > a))
194 return 0.0;
195 const double fa = f(a), fb = f(b), fm = f(0.5 * (a + b));
196 const double whole = (b - a) / 6.0 * (fa + 4.0 * fm + fb);
197 return simpsonStep(f, a, b, fa, fm, fb, whole, eps, depth);
198}
199
211inline double ellipsePerim(double ax, double by, double y) {
212 if (y <= 0.0)
213 return 0.0;
214 double c = 1.0 - y / by;
215 if (c < -1.0)
216 c = -1.0;
217 if (c > 1.0)
218 c = 1.0;
219 const double phi_max = std::acos(c);
220 auto ds = [&](double phi) {
221 const double cx = ax * std::cos(phi), sy = by * std::sin(phi);
222 return std::sqrt(cx * cx + sy * sy);
223 };
224 return 2.0 * integrate(ds, 0.0, phi_max, 1.0e-12 * (ax + by));
225}
226
227} // namespace detail
228
236inline bool hasClosedForm(int type) {
237 switch (static_cast<XSectShape>(type)) {
247 return true;
248 default:
249 return false;
250 }
251}
252
259inline bool isPolynomialOpen(int type) {
260 switch (static_cast<XSectShape>(type)) {
264 return true;
265 default:
266 return false;
267 }
268}
269
282inline double areaOfDepth(const XSectParams& xs, double h) {
283 if (h <= 0.0)
284 return 0.0;
285 if (h > xs.y_full)
286 h = xs.y_full;
287 switch (static_cast<XSectShape>(xs.type)) {
290 return detail::circArea(0.5 * xs.y_full, h);
292 const double r = 0.5 * (xs.y_full + xs.y_bot);
293 return detail::circArea(r, xs.y_bot + h) - detail::circArea(r, xs.y_bot);
294 }
297 return h * xs.w_max;
299 return (xs.y_bot + xs.s_bot * h) * h;
301 return xs.s_bot * h * h;
303 return (4.0 / 3.0) * xs.r_bot * h * std::sqrt(h);
305 return xs.r_bot * std::pow(h, xs.s_bot + 1.0);
306 default:
307 return xsect::getAofY(xs, h);
308 }
309}
310
323inline double widthOfDepth(const XSectParams& xs, double h) {
324 if (h < 0.0)
325 return 0.0;
326 if (h > xs.y_full)
327 h = xs.y_full;
328 switch (static_cast<XSectShape>(xs.type)) {
331 return detail::circWidth(0.5 * xs.y_full, h);
333 return detail::circWidth(0.5 * (xs.y_full + xs.y_bot), xs.y_bot + h);
336 return xs.w_max;
338 return xs.y_bot + 2.0 * xs.s_bot * h;
340 return 2.0 * xs.s_bot * h;
342 return 2.0 * xs.r_bot * std::sqrt(h);
344 return (h > 0.0) ? (xs.s_bot + 1.0) * xs.r_bot * std::pow(h, xs.s_bot) : 0.0;
345 default:
346 return xsect::getWofY(xs, h);
347 }
348}
349
363inline double perimeterOfDepth(const XSectParams& xs, double h) {
364 if (h <= 0.0)
365 return 0.0;
366 if (h > xs.y_full)
367 h = xs.y_full;
368 switch (static_cast<XSectShape>(xs.type)) {
371 return detail::circPerim(0.5 * xs.y_full, h);
373 const double r = 0.5 * (xs.y_full + xs.y_bot);
374 // Arc above the sediment plus the flat sediment surface.
375 return detail::circPerim(r, xs.y_bot + h) - detail::circPerim(r, xs.y_bot) +
377 }
379 double p = xs.w_max + 2.0 * h;
380 const double alf = (xs.a_full > 0.0) ? (h * xs.w_max) / xs.a_full : 0.0;
381 if (alf > kRectAlfMax)
382 p += (alf - kRectAlfMax) / (1.0 - kRectAlfMax) * xs.w_max;
383 return p;
384 }
386 return xs.w_max + (2.0 - xs.s_bot) * h;
388 return xs.y_bot + h * xs.r_bot;
390 return 2.0 * h * xs.r_bot;
392 const double x = 2.0 * std::sqrt(h) / xs.r_bot;
393 const double t = std::sqrt(1.0 + x * x);
394 return 0.5 * xs.r_bot * xs.r_bot * (x * t + std::log(x + t));
395 }
397 // Half-width x(y) = hc·y^m; arc length 2∫√(1 + x'(y)²) dy. The
398 // integrand has an integrable singularity at y → 0 when m < 1;
399 // adaptive Simpson from a tiny offset handles it at build time.
400 const double hc = (xs.s_bot + 1.0) * xs.r_bot / 2.0;
401 const double m = xs.s_bot;
402 auto ds = [&](double y) {
403 const double dxdy = (y > 0.0) ? hc * m * std::pow(y, m - 1.0) : 0.0;
404 return std::sqrt(1.0 + dxdy * dxdy);
405 };
406 const double y0 = 1.0e-9 * h;
407 return 2.0 *
408 (detail::integrate(ds, y0, h, 1.0e-12 * h) + std::hypot(y0, hc * std::pow(y0, m)));
409 }
410 default: {
411 const double r = xsect::getRofY(xs, h);
412 return (r > 0.0) ? xsect::getAofY(xs, h) / r : 0.0;
413 }
414 }
415}
416
429inline double hydRadOfDepth(const XSectParams& xs, double h) {
430 if (h <= 0.0)
431 return 0.0;
432 if (!hasClosedForm(xs.type))
433 return xsect::getRofY(xs, std::min(h, xs.y_full));
434 const double p = perimeterOfDepth(xs, h);
435 return (p > 0.0) ? areaOfDepth(xs, h) / p : 0.0;
436}
437
438} // namespace openswmm::fv::secgeom
439
440#endif // OPENSWMM_ENGINE_FV_SECTION_GEOMETRY_HPP
Cross-section geometry — unified batch + per-element API.
Definition SectionGeometry.hpp:75
double circTheta(double r, double y)
Evaluate the central angle of a circular water segment.
Definition SectionGeometry.hpp:86
double integrate(F f, double a, double b, double eps=1.0e-12, int depth=40)
Integrate a scalar function with adaptive Simpson quadrature.
Definition SectionGeometry.hpp:192
double circPerim(double r, double y)
Evaluate the exact submerged circular arc length.
Definition SectionGeometry.hpp:136
double ellipsePerim(double ax, double by, double y)
Integrate the submerged arc of a mathematical ellipse.
Definition SectionGeometry.hpp:211
double simpsonStep(F &f, double a, double b, double fa, double fm, double fb, double whole, double eps, int depth)
Recursively refine a cached Simpson panel to an absolute error target.
Definition SectionGeometry.hpp:165
double circWidth(double r, double y)
Evaluate the exact circular free-surface chord width.
Definition SectionGeometry.hpp:120
double circArea(double r, double y)
Evaluate the exact wetted area of a circular segment.
Definition SectionGeometry.hpp:103
Definition SectionGeometry.hpp:66
double widthOfDepth(const XSectParams &xs, double h)
Evaluate per-barrel top width including the invert boundary value.
Definition SectionGeometry.hpp:323
double hydRadOfDepth(const XSectParams &xs, double h)
Evaluate per-barrel hydraulic radius for the FV closure builder.
Definition SectionGeometry.hpp:429
double perimeterOfDepth(const XSectParams &xs, double h)
Evaluate per-barrel wetted perimeter for FV geometry sampling.
Definition SectionGeometry.hpp:363
constexpr double kPi
Definition SectionGeometry.hpp:68
bool hasClosedForm(int type)
Identify shapes with an analytic area/width and direct or quadrature perimeter here.
Definition SectionGeometry.hpp:236
double areaOfDepth(const XSectParams &xs, double h)
Evaluate per-barrel flow area for the FV closure builder.
Definition SectionGeometry.hpp:282
bool isPolynomialOpen(int type)
Identify open sections represented by quadratic area and linear perimeter.
Definition SectionGeometry.hpp:259
constexpr double kRectAlfMax
Definition SectionGeometry.hpp:73
double getWofY(const XSectParams &xs, double y)
Query native top width from depth through the shared host evaluator.
Definition XSection.cpp:253
double getRofY(const XSectParams &xs, double y)
Query native hydraulic radius from depth through the shared host evaluator.
Definition XSection.cpp:256
double getAofY(const XSectParams &xs, double y)
Query native wetted area from depth through the shared host evaluator.
Definition XSection.cpp:250
XSectShape
Definition XSectBatch.hpp:77
@ TRIANGULAR
Definition XSectBatch.hpp:84
@ RECT_CLOSED
Definition XSectBatch.hpp:81
@ POWERFUNC
Definition XSectBatch.hpp:86
@ FORCE_MAIN
Definition XSectBatch.hpp:102
@ RECT_OPEN
Definition XSectBatch.hpp:82
@ CIRCULAR
Definition XSectBatch.hpp:79
@ TRAPEZOIDAL
Definition XSectBatch.hpp:83
@ PARABOLIC
Definition XSectBatch.hpp:85
@ FILLED_CIRCULAR
Definition XSectBatch.hpp:80
double * y
Definition odesolve.c:37
Native cross-section geometry and borrowed optional normalized transect tables.
Definition XSectBatch.hpp:119
double s_bot
Slope of bottom section / exponent.
Definition XSectBatch.hpp:134
double a_full
Area when full (ft2)
Definition XSectBatch.hpp:127
double r_bot
Radius of bottom section / coefficient.
Definition XSectBatch.hpp:135
int type
Definition XSectBatch.hpp:120
double w_max
Width at widest point (ft)
Definition XSectBatch.hpp:125
double y_bot
Depth of bottom section / fill depth.
Definition XSectBatch.hpp:132
double y_full
Full depth (ft)
Definition XSectBatch.hpp:124