OpenSWMM Engine  6.0.0-alpha.4
Data-oriented, plugin-extensible SWMM Engine (6.0.0-alpha.4)
Loading...
Searching...
No Matches
XSectKernels.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_XSECT_KERNELS_HPP
59#define OPENSWMM_XSECT_KERNELS_HPP
60
61#include <algorithm>
62#include <cmath>
63
64#include "XSectLookup.hpp"
65#include "../data/LinkData.hpp"
66
67// Portable kernel-function marker — same convention as FvKernels.hpp and
68// 2d/solver/InertialKernels.hpp. Host builds get plain `inline`; a device
69// consumer defines this to KOKKOS_INLINE_FUNCTION *before* including.
70#ifndef OPENSWMM_KERNEL_FN
71#define OPENSWMM_KERNEL_FN inline
72#endif
73
74namespace openswmm::xsect {
75
76// Constants matching legacy consts.h / xsect.c exactly (for bit-faithful
77// parity). They moved here with the bodies that use them; setParams() in
78// XSection.cpp reads the same definitions rather than a second copy.
79inline constexpr double TINY = 1.0e-6;
80inline constexpr double PI = 3.141592654;
81inline constexpr double GRAVITY = 32.2;
82
83inline constexpr double RECT_ALFMAX = 0.97;
84inline constexpr double RECT_TRIANG_ALFMAX = 0.98;
85inline constexpr double RECT_ROUND_ALFMAX = 0.98;
86
111namespace shape {
112
113// ---- Area from depth ----
114
115OPENSWMM_KERNEL_FN double rectAofY(double y, double w_max) {
116 return y * w_max;
117}
118
119OPENSWMM_KERNEL_FN double trapezAofY(double y, double y_bot, double s_bot) {
120 return (y_bot + s_bot * y) * y;
121}
122
123OPENSWMM_KERNEL_FN double triangAofY(double y, double s_bot) {
124 return y * y * s_bot;
125}
126
127OPENSWMM_KERNEL_FN double parabAofY(double y, double r_bot) {
128 return (4.0 / 3.0) * r_bot * y * std::sqrt(y);
129}
130
131OPENSWMM_KERNEL_FN double powerfuncAofY(double y, double s_bot, double r_bot) {
132 return r_bot * std::pow(y, s_bot + 1.0);
133}
134
135// ---- Hydraulic radius ----
136
139OPENSWMM_KERNEL_FN double rectClosedRofA(double a, double w_max, double a_full) {
140 if (a <= 0.0) return 0.0;
141 double p = w_max + 2.0 * a / w_max;
142 if (a / a_full > RECT_ALFMAX)
143 p += (a / a_full - RECT_ALFMAX) / (1.0 - RECT_ALFMAX) * w_max;
144 return a / p;
145}
146
149OPENSWMM_KERNEL_FN double rectOpenRofA(double a, double w_max, double s_bot) {
150 if (a <= 0.0) return 0.0;
151 return a / (w_max + (2.0 - s_bot) * a / w_max);
152}
153
154OPENSWMM_KERNEL_FN double trapezRofY(double y, double y_bot, double s_bot,
155 double r_bot) {
156 if (y == 0.0) return 0.0;
157 return trapezAofY(y, y_bot, s_bot) / (y_bot + y * r_bot);
158}
159
160OPENSWMM_KERNEL_FN double triangRofY(double y, double s_bot, double r_bot) {
161 return (y * s_bot) / (2.0 * r_bot);
162}
163
164// ---- Top width ----
165
168OPENSWMM_KERNEL_FN double rectClosedWofY(double y_norm, double w_max) {
169 if (y_norm == 1.0) return 0.0;
170 return w_max;
171}
172
173OPENSWMM_KERNEL_FN double trapezWofY(double y, double y_bot, double s_bot) {
174 return y_bot + 2.0 * y * s_bot;
175}
176
177OPENSWMM_KERNEL_FN double triangWofY(double y, double s_bot) {
178 return 2.0 * s_bot * y;
179}
180
181OPENSWMM_KERNEL_FN double parabWofY(double y, double r_bot) {
182 return 2.0 * r_bot * std::sqrt(y);
183}
184
185OPENSWMM_KERNEL_FN double powerfuncWofY(double y, double s_bot, double r_bot) {
186 return (s_bot + 1.0) * r_bot * std::pow(y, s_bot);
187}
188
189} // namespace shape
190
227
229 const double* A_Arch = nullptr;
230 const double* A_Baskethandle = nullptr;
231 const double* A_Circ = nullptr;
232 const double* A_Egg = nullptr;
233 const double* A_HorizEllipse = nullptr;
234 const double* A_Horseshoe = nullptr;
235 const double* A_VertEllipse = nullptr;
236 const double* R_Arch = nullptr;
237 const double* R_Baskethandle = nullptr;
238 const double* R_Circ = nullptr;
239 const double* R_Egg = nullptr;
240 const double* R_HorizEllipse = nullptr;
241 const double* R_Horseshoe = nullptr;
242 const double* R_VertEllipse = nullptr;
243 const double* S_BasketHandle = nullptr;
244 const double* S_Catenary = nullptr;
245 const double* S_Circ = nullptr;
246 const double* S_Egg = nullptr;
247 const double* S_Gothic = nullptr;
248 const double* S_Horseshoe = nullptr;
249 const double* S_SemiCirc = nullptr;
250 const double* S_SemiEllip = nullptr;
251 const double* W_Arch = nullptr;
252 const double* W_BasketHandle = nullptr;
253 const double* W_Catenary = nullptr;
254 const double* W_Circ = nullptr;
255 const double* W_Egg = nullptr;
256 const double* W_Gothic = nullptr;
257 const double* W_HorizEllipse = nullptr;
258 const double* W_Horseshoe = nullptr;
259 const double* W_SemiCirc = nullptr;
260 const double* W_SemiEllip = nullptr;
261 const double* W_VertEllipse = nullptr;
262 const double* Y_BasketHandle = nullptr;
263 const double* Y_Catenary = nullptr;
264 const double* Y_Circ = nullptr;
265 const double* Y_Egg = nullptr;
266 const double* Y_Gothic = nullptr;
267 const double* Y_Horseshoe = nullptr;
268 const double* Y_SemiCirc = nullptr;
269 const double* Y_SemiEllip = nullptr;
270 const double* Amax = nullptr;
272 const LocateLut* luts = nullptr;
273 int N_A_Arch = 0;
275 int N_A_Circ = 0;
276 int N_A_Egg = 0;
280 int N_R_Arch = 0;
282 int N_R_Circ = 0;
283 int N_R_Egg = 0;
289 int N_S_Circ = 0;
290 int N_S_Egg = 0;
291 int N_S_Gothic = 0;
295 int N_W_Arch = 0;
298 int N_W_Circ = 0;
299 int N_W_Egg = 0;
300 int N_W_Gothic = 0;
308 int N_Y_Circ = 0;
309 int N_Y_Egg = 0;
310 int N_Y_Gothic = 0;
314
317 return luts ? (luts + static_cast<int>(id)) : nullptr;
318 }
319};
320
328struct XsectEval {
330
331 // ============================================================================
332 // Root finders — faithful ports of legacy findroot.c (Numerical Recipes).
333 // ============================================================================
334
335 // Newton-Raphson + bisection. func(x, &f, &df). Root bracketed in [x1, x2].
336 template <class Func>
337 OPENSWMM_KERNEL_FN int findroot_Newton(double x1, double x2, double* rts, double xacc, Func func) const {
338 constexpr int MAXIT = 60;
339 int n = 0;
340 double df, dx, dxold, f, x, temp, xhi, xlo;
341
342 x = *rts;
343 xlo = x1;
344 xhi = x2;
345 dxold = std::fabs(x2 - x1);
346 dx = dxold;
347 func(x, &f, &df);
348 n++;
349 for (int j = 1; j <= MAXIT; ++j) {
350 if (((x - xhi) * df - f) * ((x - xlo) * df - f) >= 0.0 ||
351 (std::fabs(2.0 * f) > std::fabs(dxold * df))) {
352 dxold = dx;
353 dx = 0.5 * (xhi - xlo);
354 x = xlo + dx;
355 if (xlo == x) break;
356 } else {
357 dxold = dx;
358 dx = f / df;
359 temp = x;
360 x -= dx;
361 if (temp == x) break;
362 }
363 if (std::fabs(dx) < xacc) break;
364 func(x, &f, &df);
365 n++;
366 if (f < 0.0) xlo = x;
367 else xhi = x;
368 }
369 *rts = x;
370 return (n <= MAXIT) ? n : 0;
371 }
372
373 // Ridder's method. func(x) -> double. Root bracketed in [x1, x2].
374 template <class Func>
375 OPENSWMM_KERNEL_FN double findroot_Ridder(double x1, double x2, double xacc, Func func) const {
376 constexpr int MAXIT = 60;
377 auto SIGN = [](double a, double b) { return (b >= 0.0) ? std::fabs(a) : -std::fabs(a); };
378
379 double ans, fhi, flo, fm, fnew, s, xhi, xlo, xm, xnew;
380 flo = func(x1);
381 fhi = func(x2);
382 if (flo == 0.0) return x1;
383 if (fhi == 0.0) return x2;
384 ans = 0.5 * (x1 + x2);
385 if ((flo > 0.0 && fhi < 0.0) || (flo < 0.0 && fhi > 0.0)) {
386 xlo = x1;
387 xhi = x2;
388 for (int j = 1; j <= MAXIT; ++j) {
389 xm = 0.5 * (xlo + xhi);
390 fm = func(xm);
391 s = std::sqrt(fm * fm - flo * fhi);
392 if (s == 0.0) return ans;
393 xnew = xm + (xm - xlo) * ((flo >= fhi ? 1.0 : -1.0) * fm / s);
394 if (std::fabs(xnew - ans) <= xacc) break;
395 ans = xnew;
396 fnew = func(ans);
397 if (SIGN(fm, fnew) != fm) { xlo = xm; flo = fm; xhi = ans; fhi = fnew; }
398 else if (SIGN(flo, fnew) != flo) { xhi = ans; fhi = fnew; }
399 else if (SIGN(fhi, fnew) != fhi) { xlo = ans; flo = fnew; }
400 else return ans;
401 if (std::fabs(xhi - xlo) <= xacc) return ans;
402 }
403 return ans;
404 }
405 return -1.e20;
406 }
407
408 // ============================================================================
409 // Lookup helpers — identical to legacy lookup()/invLookup()/locate()
410 // ============================================================================
411
412 OPENSWMM_KERNEL_FN int locate(double y, const double* table, int jLast) const {
413 // Bisection: highest index j with table[j] <= y (legacy locate()).
414 return locate_bisect(y, table, jLast);
415 }
416
417 OPENSWMM_KERNEL_FN double lookup(double x, const double* table, int n_items) const {
418 // The tables are defined on the normalized domain x in [0, 1]. Guard the
419 // bounds BEFORE converting x to an int index: x can arrive non-finite when
420 // a caller normalizes by a zero full-depth (e.g. a conduit finalized
421 // before its cross-section geometry is set, so y/y_full == inf).
422 // static_cast<int> of inf/NaN is undefined behavior — on x86 (int)inf is
423 // INT_MIN, which slips past the upper-bound check below and indexes the
424 // table wildly out of bounds (segfault); ARM saturates to INT_MAX and
425 // happened to mask the bug. The legacy lookup() guarded x <= 0; restore
426 // that and add the matching upper bound so non-finite x is handled safely.
427 //
428 // On the finite domain (0, 1] this is bit-identical to legacy lookup():
429 // - x <= 0 / NaN -> table[0] (legacy lookup(0) also returns table[0]).
430 // - x > 1 / +inf -> table[n-1] (legacy takes the i >= n-1 early-out).
431 // The bound is strict (`> 1.0`, not `>= 1.0`): at x == 1.0 legacy does
432 // NOT early-out — 1.0/delta rounds just below n-1 — so it interpolates
433 // the last segment. Deferring x == 1.0 to lookup_exact reproduces that;
434 // no representable double lies in (1.0, n-1 * delta), so the strict
435 // guard is exact.
436 if (!(x > 0.0)) return table[0]; // x <= 0 or NaN
437 if (x > 1.0) return table[n_items - 1]; // x > 1 or +inf
438 return lookup_exact(x, table, n_items);
439 }
440
444 OPENSWMM_KERNEL_FN double invLookup(double y, const double* table, int n_items,
445 const LocateLut* lut = nullptr) const {
446 double dx = 1.0 / static_cast<double>(n_items - 1);
447 int n = n_items;
448
449 // Truncate item count if last 2 entries are decreasing (section-factor tables)
450 if (table[n - 3] > table[n - 1]) n = n - 2;
451
452 int i;
453 if (n < n_items && y > table[n_items - 1]) {
454 if (y >= table[n_items - 3]) return static_cast<double>(n - 1) * dx;
455 if (y <= table[n_items - 2]) i = n_items - 2;
456 else i = n_items - 3;
457 } else {
458 i = locate_maybe_lut(y, table, n - 1, lut);
459 }
460 if (i >= n - 1) return static_cast<double>(n - 1) * dx;
461
462 double x0 = i * dx;
463 double dy = table[i + 1] - table[i];
464 double x;
465 if (dy == 0.0) x = x0;
466 else x = x0 + (y - table[i]) * dx / dy;
467 if (x < 0.0) x = 0.0;
468 if (x > 1.0) x = 1.0;
469 return x;
470 }
471
472 // ============================================================================
473 // Circular small-area special functions (legacy getYcircular/getScircular/...)
474 // ============================================================================
475
476 OPENSWMM_KERNEL_FN double getThetaOfAlpha(double alpha) const {
477 double theta, theta1, ap, d;
478 if (alpha > 0.04) theta = 1.2 + 5.08 * (alpha - 0.04) / 0.96;
479 else theta = 0.031715 - 12.79384 * alpha + 8.28479 * std::sqrt(alpha);
480 theta1 = theta;
481 ap = (2.0 * PI) * alpha;
482 for (int k = 1; k <= 40; ++k) {
483 d = -(ap - theta + std::sin(theta)) / (1.0 - std::cos(theta));
484 if (d > 1.0) d = std::copysign(1.0, d);
485 theta = theta - d;
486 if (std::fabs(d) <= 0.0001) return theta;
487 }
488 return theta1;
489 }
490
491 OPENSWMM_KERNEL_FN double getThetaOfPsi(double psi) const {
492 double theta, theta1, ap, tt, tt23, t3, d;
493 if (psi > 0.90) theta = 4.17 + 1.12 * (psi - 0.90) / 0.176;
494 else if (psi > 0.5) theta = 3.14 + 1.03 * (psi - 0.5) / 0.4;
495 else if (psi > 0.015) theta = 1.2 + 1.94 * (psi - 0.015) / 0.485;
496 else theta = 0.12103 - 55.5075 * psi + 15.62254 * std::sqrt(psi);
497 theta1 = theta;
498 ap = (2.0 * PI) * psi;
499 for (int k = 1; k <= 40; ++k) {
500 theta = std::fabs(theta);
501 tt = theta - std::sin(theta);
502 tt23 = std::pow(tt, 2.0 / 3.0);
503 t3 = std::pow(theta, 1.0 / 3.0);
504 d = ap * theta / t3 - tt * tt23;
505 d = d / (ap * (2.0 / 3.0) / t3 - (5.0 / 3.0) * tt23 * (1.0 - std::cos(theta)));
506 theta = theta - d;
507 if (std::fabs(d) <= 0.0001) return theta;
508 }
509 return theta1;
510 }
511
512 OPENSWMM_KERNEL_FN double getYcircular(double alpha) const {
513 double theta;
514 if (alpha >= 1.0) return 1.0;
515 if (alpha <= 0.0) return 0.0;
516 if (alpha <= 1.0e-5) {
517 theta = std::pow(37.6911 * alpha, 1.0 / 3.0);
518 return theta * theta / 16.0;
519 }
520 theta = getThetaOfAlpha(alpha);
521 return (1.0 - std::cos(theta / 2.0)) / 2.0;
522 }
523
524 OPENSWMM_KERNEL_FN double getScircular(double alpha) const {
525 double theta;
526 if (alpha >= 1.0) return 1.0;
527 if (alpha <= 0.0) return 0.0;
528 if (alpha <= 1.0e-5) {
529 theta = std::pow(37.6911 * alpha, 1.0 / 3.0);
530 return std::pow(theta, 13.0 / 3.0) / 124.4797;
531 }
532 theta = getThetaOfAlpha(alpha);
533 return std::pow((theta - std::sin(theta)), 5.0 / 3.0) / (2.0 * PI) /
534 std::pow(theta, 2.0 / 3.0);
535 }
536
537 OPENSWMM_KERNEL_FN double getAcircular(double psi) const {
538 double theta;
539 if (psi >= 1.0) return 1.0;
540 if (psi <= 0.0) return 0.0;
541 if (psi <= 1.0e-6) {
542 theta = std::pow(124.4797 * psi, 3.0 / 13.0);
543 return theta * theta * theta / 37.6911;
544 }
545 theta = getThetaOfPsi(psi);
546 return (theta - std::sin(theta)) / (2.0 * PI);
547 }
548
549 // ============================================================================
550 // Generic / tabular section-factor derivatives (legacy)
551 // ============================================================================
552
553 OPENSWMM_KERNEL_FN double generic_getdSdA(const XSectParams& xs, double a) const {
554 double alpha = a / xs.a_full;
555 double alpha1 = alpha - 0.001;
556 double alpha2 = alpha + 0.001;
557 if (alpha1 < 0.0) alpha1 = 0.0;
558 double a1 = alpha1 * xs.a_full;
559 double a2 = alpha2 * xs.a_full;
560 return (getSofA(xs, a2) - getSofA(xs, a1)) / (a2 - a1);
561 }
562
564 const double* table, int n_items) const {
565 double alpha = a / xs.a_full;
566 double delta = 1.0 / static_cast<double>(n_items - 1);
567 int i = static_cast<int>(alpha / delta);
568 if (i >= n_items - 1) i = n_items - 2;
569 double dSdA = (table[i + 1] - table[i]) / delta;
570 return dSdA * xs.s_full / xs.a_full;
571 }
572
573 // ============================================================================
574 // Circular
575 // ============================================================================
576
577 OPENSWMM_KERNEL_FN double circ_getAofY(const XSectParams& xs, double y) const {
578 double y_norm = y / xs.y_full;
579 return xs.a_full * lookup(y_norm, tbl.A_Circ, tbl.N_A_Circ);
580 }
581
582 OPENSWMM_KERNEL_FN double circ_getYofA(const XSectParams& xs, double a) const {
583 double alpha = a / xs.a_full;
584 if (alpha < 0.04) return xs.y_full * getYcircular(alpha);
585 return xs.y_full * lookup(alpha, tbl.Y_Circ, tbl.N_Y_Circ);
586 }
587
588 OPENSWMM_KERNEL_FN double circ_getSofA(const XSectParams& xs, double a) const {
589 double alpha = a / xs.a_full;
590 if (alpha < 0.04) return xs.s_full * getScircular(alpha);
591 return xs.s_full * lookup(alpha, tbl.S_Circ, tbl.N_S_Circ);
592 }
593
594 OPENSWMM_KERNEL_FN double circ_getAofS(const XSectParams& xs, double s) const {
595 double psi = s / xs.s_full;
596 if (psi == 0.0) return 0.0;
597 if (psi >= 1.0) return xs.a_full;
598 if (psi <= 0.015) return xs.a_full * getAcircular(psi);
599 return xs.a_full * invLookup(psi, tbl.S_Circ, tbl.N_S_Circ, tbl.lut(LutId::S_Circ));
600 }
601
602 OPENSWMM_KERNEL_FN double circ_getdSdA(const XSectParams& xs, double a) const {
603 double alpha = a / xs.a_full;
604 if (alpha <= 1.0e-30) return 1.0e-30;
605 if (alpha < 0.04) {
606 double theta = getThetaOfAlpha(alpha);
607 double p = theta * xs.y_full / 2.0;
608 double r = a / p;
609 double dPdA = 4.0 / xs.y_full / (1.0 - std::cos(theta));
610 return (5.0 / 3.0 - (2.0 / 3.0) * dPdA * r) * std::pow(r, 2.0 / 3.0);
611 }
612 return tabular_getdSdA(xs, a, tbl.S_Circ, tbl.N_S_Circ);
613 }
614
615 // ============================================================================
616 // Filled circular (yBot/aBot/sBot/rBot = filled bottom: depth/area/width/perim)
617 // ============================================================================
618
619 OPENSWMM_KERNEL_FN double filled_circ_getAofY(const XSectParams& xs, double y) const {
620 // Temporarily restore the unfilled full circle (legacy expands yFull/aFull).
621 XSectParams t = xs;
622 t.y_full += xs.y_bot;
623 t.a_full += xs.a_bot;
624 double a = circ_getAofY(t, y + xs.y_bot);
625 return a - xs.a_bot;
626 }
627
628 OPENSWMM_KERNEL_FN double filled_circ_getYofA(const XSectParams& xs, double a) const {
629 XSectParams t = xs;
630 t.y_full += xs.y_bot;
631 t.a_full += xs.a_bot;
632 double y = circ_getYofA(t, a + xs.a_bot);
633 return y - xs.y_bot;
634 }
635
636 OPENSWMM_KERNEL_FN double filled_circ_getRofY(const XSectParams& xs, double y) const {
637 XSectParams t = xs;
638 t.y_full += xs.y_bot;
639 t.a_full += xs.a_bot;
640 double yy = y + xs.y_bot;
641 double a = circ_getAofY(t, yy);
642 double r = 0.25 * t.y_full *
643 lookup(yy / t.y_full, tbl.R_Circ, tbl.N_R_Circ);
644 double p = (a / r);
645 a = a - xs.a_bot;
646 p = p - xs.r_bot + xs.s_bot; // rBot = filled perimeter, sBot = filled width
647 return a / p;
648 }
649
650 // ============================================================================
651 // Rectangular closed / open
652 // ============================================================================
653
654 OPENSWMM_KERNEL_FN double rect_closed_getRofA(const XSectParams& xs, double a) const {
655 return shape::rectClosedRofA(a, xs.w_max, xs.a_full);
656 }
657
658 OPENSWMM_KERNEL_FN double rect_closed_getSofA(const XSectParams& xs, double a) const {
659 if (a / xs.a_full > RECT_ALFMAX)
660 return xs.s_max + (xs.s_full - xs.s_max) *
661 (a / xs.a_full - RECT_ALFMAX) / (1.0 - RECT_ALFMAX);
662 return a * std::pow(rect_closed_getRofA(xs, a), 2.0 / 3.0);
663 }
664
665 OPENSWMM_KERNEL_FN double rect_closed_getdSdA(const XSectParams& xs, double a) const {
666 double alpha = a / xs.a_full;
667 if (alpha > RECT_ALFMAX)
668 return (xs.s_full - xs.s_max) / ((1.0 - RECT_ALFMAX) * xs.a_full);
669 if (alpha <= 1.0e-30) return generic_getdSdA(xs, a);
670 double r = rect_closed_getRofA(xs, a);
671 return (5.0 / 3.0 - (2.0 / 3.0) * (2.0 / xs.w_max) * r) * std::pow(r, 2.0 / 3.0);
672 }
673
674 OPENSWMM_KERNEL_FN double rect_open_getSofA(const XSectParams& xs, double a) const {
675 double y = a / xs.w_max;
676 double r = a / ((2.0 - xs.s_bot) * y + xs.w_max);
677 return a * std::pow(r, 2.0 / 3.0);
678 }
679
680 OPENSWMM_KERNEL_FN double rect_open_getdSdA(const XSectParams& xs, double a) const {
681 if (a / xs.a_full <= 1.0e-30) return generic_getdSdA(xs, a);
682 double r = getRofA(xs, a);
683 double dPdA = (2.0 - xs.s_bot) / xs.w_max;
684 return (5.0 / 3.0 - (2.0 / 3.0) * dPdA * r) * std::pow(r, 2.0 / 3.0);
685 }
686
687 // ============================================================================
688 // Rect-triangular (triangular bottom + rectangular top)
689 // ============================================================================
690
691 OPENSWMM_KERNEL_FN double rect_triang_getAofY(const XSectParams& xs, double y) const {
692 if (y <= xs.y_bot) return y * y * xs.s_bot;
693 return xs.a_bot + (y - xs.y_bot) * xs.w_max;
694 }
695
696 OPENSWMM_KERNEL_FN double rect_triang_getWofY(const XSectParams& xs, double y) const {
697 if (y <= xs.y_bot) return 2.0 * xs.s_bot * y;
698 return xs.w_max;
699 }
700
701 OPENSWMM_KERNEL_FN double rect_triang_getYofA(const XSectParams& xs, double a) const {
702 if (a <= xs.a_bot) return std::sqrt(a / xs.s_bot);
703 return xs.y_bot + (a - xs.a_bot) / xs.w_max;
704 }
705
706 OPENSWMM_KERNEL_FN double rect_triang_getRofA(const XSectParams& xs, double a) const {
707 if (a <= 0.0) return 0.0;
708 double y = rect_triang_getYofA(xs, a);
709 if (y <= xs.y_bot) return a / (2.0 * y * xs.r_bot);
710 double p = 2.0 * xs.y_bot * xs.r_bot + 2.0 * (y - xs.y_bot);
711 double alf = (a / xs.a_full) - RECT_TRIANG_ALFMAX;
712 if (alf > 0.0) p += alf / (1.0 - RECT_TRIANG_ALFMAX) * xs.w_max;
713 return a / p;
714 }
715
716 OPENSWMM_KERNEL_FN double rect_triang_getRofY(const XSectParams& xs, double y) const {
717 if (y <= xs.y_bot) return y * xs.s_bot / (2.0 * xs.r_bot);
718 double a = xs.a_bot + (y - xs.y_bot) * xs.w_max;
719 double p = 2.0 * xs.y_bot * xs.r_bot + 2.0 * (y - xs.y_bot);
720 double alf = (a / xs.a_full) - RECT_TRIANG_ALFMAX;
721 if (alf > 0.0) p += alf / (1.0 - RECT_TRIANG_ALFMAX) * xs.w_max;
722 return a / p;
723 }
724
725 OPENSWMM_KERNEL_FN double rect_triang_getSofA(const XSectParams& xs, double a) const {
726 double alfMax = RECT_TRIANG_ALFMAX;
727 if (a / xs.a_full > alfMax)
728 return xs.s_max + (xs.s_full - xs.s_max) *
729 (a / xs.a_full - alfMax) / (1.0 - alfMax);
730 return a * std::pow(rect_triang_getRofA(xs, a), 2.0 / 3.0);
731 }
732
733 OPENSWMM_KERNEL_FN double rect_triang_getdSdA(const XSectParams& xs, double a) const {
734 double alfMax = RECT_TRIANG_ALFMAX;
735 double alpha = a / xs.a_full;
736 if (alpha > alfMax)
737 return (xs.s_full - xs.s_max) / ((1.0 - alfMax) * xs.a_full);
738 if (alpha <= 1.0e-30) return generic_getdSdA(xs, a);
739 double dPdA;
740 if (a > xs.a_bot) dPdA = 2.0 / xs.w_max;
741 else dPdA = xs.r_bot / std::sqrt(a * xs.s_bot);
742 double r = rect_triang_getRofA(xs, a);
743 return (5.0 / 3.0 - (2.0 / 3.0) * dPdA * r) * std::pow(r, 2.0 / 3.0);
744 }
745
746 // ============================================================================
747 // Rect-round (circular invert + rectangular top)
748 // ============================================================================
749
750 OPENSWMM_KERNEL_FN double rect_round_getYofA(const XSectParams& xs, double a) const {
751 if (a > xs.a_bot) return xs.y_bot + (a - xs.a_bot) / xs.w_max;
752 double alpha = a / (PI * xs.r_bot * xs.r_bot);
753 if (alpha < 0.04) return (2.0 * xs.r_bot) * getYcircular(alpha);
754 return (2.0 * xs.r_bot) * lookup(alpha, tbl.Y_Circ, tbl.N_Y_Circ);
755 }
756
757 OPENSWMM_KERNEL_FN double rect_round_getAofY(const XSectParams& xs, double y) const {
758 if (y > xs.y_bot) return xs.a_bot + (y - xs.y_bot) * xs.w_max;
759 double theta1 = 2.0 * std::acos(1.0 - y / xs.r_bot);
760 return 0.5 * xs.r_bot * xs.r_bot * (theta1 - std::sin(theta1));
761 }
762
763 OPENSWMM_KERNEL_FN double rect_round_getWofY(const XSectParams& xs, double y) const {
764 if (y > xs.y_bot) return xs.w_max;
765 return 2.0 * std::sqrt(y * (2.0 * xs.r_bot - y));
766 }
767
768 OPENSWMM_KERNEL_FN double rect_round_getRofA(const XSectParams& xs, double a) const {
769 if (a <= 0.0) return 0.0;
770 if (a > xs.a_bot) {
771 double y1 = (a - xs.a_bot) / xs.w_max;
772 double theta1 = 2.0 * std::asin(xs.w_max / 2.0 / xs.r_bot);
773 double p = xs.r_bot * theta1 + 2.0 * y1;
774 double arg = (a / xs.a_full) - RECT_ROUND_ALFMAX;
775 if (arg > 0.0) p += arg / (1.0 - RECT_ROUND_ALFMAX) * xs.w_max;
776 return a / p;
777 }
778 double y1 = rect_round_getYofA(xs, a);
779 double theta1 = 2.0 * std::acos(1.0 - y1 / xs.r_bot);
780 double p = xs.r_bot * theta1;
781 return a / p;
782 }
783
784 OPENSWMM_KERNEL_FN double rect_round_getRofY(const XSectParams& xs, double y) const {
785 if (y <= 0.0) return 0.0;
786 if (y > xs.y_bot) return rect_round_getRofA(xs, rect_round_getAofY(xs, y));
787 double theta1 = 2.0 * std::acos(1.0 - y / xs.r_bot);
788 return 0.5 * xs.r_bot * (1.0 - std::sin(theta1)) / theta1;
789 }
790
791 OPENSWMM_KERNEL_FN double rect_round_getSofA(const XSectParams& xs, double a) const {
792 double alfMax = RECT_ROUND_ALFMAX;
793 if (a / xs.a_full > alfMax)
794 return xs.s_max + (xs.s_full - xs.s_max) *
795 (a / xs.a_full - alfMax) / (1.0 - alfMax);
796 if (a > xs.a_bot)
797 return a * std::pow(rect_round_getRofA(xs, a), 2.0 / 3.0);
798 double aFull = PI * xs.r_bot * xs.r_bot;
799 double alpha = a / aFull;
800 double sFull = xs.s_bot;
801 if (alpha < 0.04) return sFull * getScircular(alpha);
802 return sFull * lookup(alpha, tbl.S_Circ, tbl.N_S_Circ);
803 }
804
805 OPENSWMM_KERNEL_FN double rect_round_getdSdA(const XSectParams& xs, double a) const {
806 double alfMax = RECT_ROUND_ALFMAX;
807 if (a / xs.a_full > alfMax)
808 return (xs.s_full - xs.s_max) / ((1.0 - alfMax) * xs.a_full);
809 if (a > xs.a_bot) {
810 double r = rect_round_getRofA(xs, a);
811 double dPdA = 2.0 / xs.w_max;
812 return (5.0 / 3.0 - (2.0 / 3.0) * dPdA * r) * std::pow(r, 2.0 / 3.0);
813 }
814 return generic_getdSdA(xs, a);
815 }
816
817 // ============================================================================
818 // Modified baskethandle (rectangular bottom + circular-arc top).
819 // Note: rBot/yBot/aBot/sBot refer to the CIRCULAR TOP portion.
820 // ============================================================================
821
822 OPENSWMM_KERNEL_FN double mod_basket_getYofA(const XSectParams& xs, double a) const {
823 if (a <= xs.a_full - xs.a_bot) return a / xs.w_max;
824 double alpha = (xs.a_full - a) / (PI * xs.r_bot * xs.r_bot);
825 double y1;
826 if (alpha < 0.04) y1 = getYcircular(alpha);
827 else y1 = lookup(alpha, tbl.Y_Circ, tbl.N_Y_Circ);
828 y1 = 2.0 * xs.r_bot * y1;
829 return xs.y_full - y1;
830 }
831
832 OPENSWMM_KERNEL_FN double mod_basket_getAofY(const XSectParams& xs, double y) const {
833 if (y <= xs.y_full - xs.y_bot) return y * xs.w_max;
834 double y1 = xs.y_full - y;
835 double theta1 = 2.0 * std::acos(1.0 - y1 / xs.r_bot);
836 double a1 = 0.5 * xs.r_bot * xs.r_bot * (theta1 - std::sin(theta1));
837 return xs.a_full - a1;
838 }
839
840 OPENSWMM_KERNEL_FN double mod_basket_getWofY(const XSectParams& xs, double y) const {
841 if (y <= 0.0) return 0.0;
842 if (y <= xs.y_full - xs.y_bot) return xs.w_max;
843 double y1 = xs.y_full - y;
844 return 2.0 * std::sqrt(y1 * (2.0 * xs.r_bot - y1));
845 }
846
847 OPENSWMM_KERNEL_FN double mod_basket_getRofA(const XSectParams& xs, double a) const {
848 if (a <= xs.a_full - xs.a_bot)
849 return a / (xs.w_max + 2.0 * a / xs.w_max);
850 double y1 = xs.y_full - mod_basket_getYofA(xs, a);
851 double theta1 = 2.0 * std::acos(1.0 - y1 / xs.r_bot);
852 double p = (xs.s_bot - theta1) * xs.r_bot; // sBot = full circular opening angle
853 y1 = xs.y_full - xs.y_bot;
854 p = p + 2.0 * y1 + xs.w_max;
855 return a / p;
856 }
857
858 OPENSWMM_KERNEL_FN double mod_basket_getdSdA(const XSectParams& xs, double a) const {
859 if (a <= xs.a_full - xs.a_bot && a / xs.a_full > 1.0e-30) {
860 double r = a / (xs.w_max + 2.0 * a / xs.w_max);
861 double dPdA = 2.0 / xs.w_max;
862 return (5.0 / 3.0 - (2.0 / 3.0) * dPdA * r) * std::pow(r, 2.0 / 3.0);
863 }
864 return generic_getdSdA(xs, a);
865 }
866
867 // ============================================================================
868 // Trapezoidal (yBot = bottom width, sBot = avg side slope, rBot = side length/depth)
869 // ============================================================================
870
871 OPENSWMM_KERNEL_FN double trapez_getAofY(const XSectParams& xs, double y) const {
872 return shape::trapezAofY(y, xs.y_bot, xs.s_bot);
873 }
874
875 OPENSWMM_KERNEL_FN double trapez_getWofY(const XSectParams& xs, double y) const {
876 return shape::trapezWofY(y, xs.y_bot, xs.s_bot);
877 }
878
879 OPENSWMM_KERNEL_FN double trapez_getYofA(const XSectParams& xs, double a) const {
880 if (xs.s_bot == 0.0) return a / xs.y_bot;
881 return (std::sqrt(xs.y_bot * xs.y_bot + 4.0 * xs.s_bot * a) - xs.y_bot) /
882 (2.0 * xs.s_bot);
883 }
884
885 OPENSWMM_KERNEL_FN double trapez_getRofA(const XSectParams& xs, double a) const {
886 return a / (xs.y_bot + trapez_getYofA(xs, a) * xs.r_bot);
887 }
888
889 OPENSWMM_KERNEL_FN double trapez_getRofY(const XSectParams& xs, double y) const {
890 return shape::trapezRofY(y, xs.y_bot, xs.s_bot, xs.r_bot);
891 }
892
893 OPENSWMM_KERNEL_FN double trapez_getdSdA(const XSectParams& xs, double a) const {
894 if (a / xs.a_full <= 1.0e-30) return generic_getdSdA(xs, a);
895 double r = trapez_getRofA(xs, a);
896 double dPdA = xs.r_bot / std::sqrt(xs.y_bot * xs.y_bot + 4.0 * xs.s_bot * a);
897 return (5.0 / 3.0 - (2.0 / 3.0) * dPdA * r) * std::pow(r, 2.0 / 3.0);
898 }
899
900 // ============================================================================
901 // Triangular
902 // ============================================================================
903
904 OPENSWMM_KERNEL_FN double triang_getAofY(const XSectParams& xs, double y) const {
905 return shape::triangAofY(y, xs.s_bot);
906 }
907
908 OPENSWMM_KERNEL_FN double triang_getWofY(const XSectParams& xs, double y) const {
909 return shape::triangWofY(y, xs.s_bot);
910 }
911
912 OPENSWMM_KERNEL_FN double triang_getYofA(const XSectParams& xs, double a) const {
913 return std::sqrt(a / xs.s_bot);
914 }
915
916 OPENSWMM_KERNEL_FN double triang_getRofA(const XSectParams& xs, double a) const {
917 return a / (2.0 * triang_getYofA(xs, a) * xs.r_bot);
918 }
919
920 OPENSWMM_KERNEL_FN double triang_getRofY(const XSectParams& xs, double y) const {
921 return shape::triangRofY(y, xs.s_bot, xs.r_bot);
922 }
923
924 OPENSWMM_KERNEL_FN double triang_getdSdA(const XSectParams& xs, double a) const {
925 if (a / xs.a_full <= 1.0e-30) return generic_getdSdA(xs, a);
926 double r = triang_getRofA(xs, a);
927 double dPdA = xs.r_bot / std::sqrt(a * xs.s_bot);
928 return (5.0 / 3.0 - (2.0 / 3.0) * dPdA * r) * std::pow(r, 2.0 / 3.0);
929 }
930
931 // ============================================================================
932 // Parabolic (rBot = 1/sqrt(c) where y = c*x^2)
933 // ============================================================================
934
935 OPENSWMM_KERNEL_FN double parab_getAofY(const XSectParams& xs, double y) const {
936 return shape::parabAofY(y, xs.r_bot);
937 }
938
939 OPENSWMM_KERNEL_FN double parab_getWofY(const XSectParams& xs, double y) const {
940 return shape::parabWofY(y, xs.r_bot);
941 }
942
943 OPENSWMM_KERNEL_FN double parab_getYofA(const XSectParams& xs, double a) const {
944 return std::pow((3.0 / 4.0) * a / xs.r_bot, 2.0 / 3.0);
945 }
946
947 // Analytical wetted perimeter (legacy parab_getPofY).
948 OPENSWMM_KERNEL_FN double parab_getPofY(const XSectParams& xs, double y) const {
949 double x = 2.0 * std::sqrt(y) / xs.r_bot;
950 double t = std::sqrt(1.0 + x * x);
951 return 0.5 * xs.r_bot * xs.r_bot * (x * t + std::log(x + t));
952 }
953
954 OPENSWMM_KERNEL_FN double parab_getRofY(const XSectParams& xs, double y) const {
955 if (y <= 0.0) return 0.0;
956 return parab_getAofY(xs, y) / parab_getPofY(xs, y);
957 }
958
959 OPENSWMM_KERNEL_FN double parab_getRofA(const XSectParams& xs, double a) const {
960 if (a <= 0.0) return 0.0;
961 return a / parab_getPofY(xs, parab_getYofA(xs, a));
962 }
963
964 // ============================================================================
965 // Power function (sBot = 1/exponent, rBot = coefficient)
966 // ============================================================================
967
968 OPENSWMM_KERNEL_FN double powerfunc_getAofY(const XSectParams& xs, double y) const {
969 return shape::powerfuncAofY(y, xs.s_bot, xs.r_bot);
970 }
971
972 OPENSWMM_KERNEL_FN double powerfunc_getWofY(const XSectParams& xs, double y) const {
973 return shape::powerfuncWofY(y, xs.s_bot, xs.r_bot);
974 }
975
976 OPENSWMM_KERNEL_FN double powerfunc_getYofA(const XSectParams& xs, double a) const {
977 return std::pow(a / xs.r_bot, 1.0 / (xs.s_bot + 1.0));
978 }
979
980 // Numerical wetted perimeter (legacy powerfunc_getPofY — stepwise integration).
981 OPENSWMM_KERNEL_FN double powerfunc_getPofY(const XSectParams& xs, double y) const {
982 double dy1 = 0.02 * xs.y_full;
983 double h = (xs.s_bot + 1.0) * xs.r_bot / 2.0;
984 double m = xs.s_bot;
985 double p = 0.0, y1 = 0.0, x1 = 0.0;
986 double x2, y2, dx, dy;
987 do {
988 y2 = y1 + dy1;
989 if (y2 > y) y2 = y;
990 x2 = h * std::pow(y2, m);
991 dx = x2 - x1;
992 dy = y2 - y1;
993 p += std::sqrt(dx * dx + dy * dy);
994 x1 = x2;
995 y1 = y2;
996 } while (y2 < y);
997 return 2.0 * p;
998 }
999
1000 OPENSWMM_KERNEL_FN double powerfunc_getRofY(const XSectParams& xs, double y) const {
1001 if (y <= 0.0) return 0.0;
1002 return powerfunc_getAofY(xs, y) / powerfunc_getPofY(xs, y);
1003 }
1004
1005 OPENSWMM_KERNEL_FN double powerfunc_getRofA(const XSectParams& xs, double a) const {
1006 if (a <= 0.0) return 0.0;
1007 return a / powerfunc_getPofY(xs, powerfunc_getYofA(xs, a));
1008 }
1009
1010 // ============================================================================
1011 // Main dispatch: getAofY
1012 // ============================================================================
1013
1014 OPENSWMM_KERNEL_FN double getAofY(const XSectParams& xs, double y) const {
1015 if (y <= 0.0) return 0.0;
1016 // A section with no (or not-yet-set) full depth has no area. Guard the
1017 // division so a degenerate cross-section — e.g. a conduit finalized before
1018 // its geometry is assigned — yields 0 instead of normalizing to inf/NaN.
1019 if (xs.y_full <= 0.0) return 0.0;
1020 double y_norm = y / xs.y_full;
1021
1022 switch (static_cast<XSectShape>(xs.type)) {
1025 return xs.a_full * lookup(y_norm, tbl.A_Circ, tbl.N_A_Circ);
1028 return xs.a_full * lookup(y_norm, tbl.A_Egg, tbl.N_A_Egg);
1030 return xs.a_full * lookup(y_norm, tbl.A_Horseshoe, tbl.N_A_Horseshoe);
1031 case XSectShape::GOTHIC:
1032 return xs.a_full * invLookup(y_norm, tbl.Y_Gothic, tbl.N_Y_Gothic, tbl.lut(LutId::Y_Gothic));
1034 return xs.a_full * invLookup(y_norm, tbl.Y_Catenary, tbl.N_Y_Catenary, tbl.lut(LutId::Y_Catenary));
1036 return xs.a_full * invLookup(y_norm, tbl.Y_SemiEllip, tbl.N_Y_SemiEllip, tbl.lut(LutId::Y_SemiEllip));
1038 return xs.a_full * lookup(y_norm, tbl.A_Baskethandle, tbl.N_A_Baskethandle);
1040 return xs.a_full * invLookup(y_norm, tbl.Y_SemiCirc, tbl.N_Y_SemiCirc, tbl.lut(LutId::Y_SemiCirc));
1042 return xs.a_full * lookup(y_norm, tbl.A_HorizEllipse, tbl.N_A_HorizEllipse);
1044 return xs.a_full * lookup(y_norm, tbl.A_VertEllipse, tbl.N_A_VertEllipse);
1045 case XSectShape::ARCH:
1046 return xs.a_full * lookup(y_norm, tbl.A_Arch, tbl.N_A_Arch);
1048 case XSectShape::RECT_OPEN: return shape::rectAofY(y, xs.w_max);
1052 case XSectShape::TRAPEZOIDAL: return trapez_getAofY(xs, y);
1053 case XSectShape::TRIANGULAR: return triang_getAofY(xs, y);
1054 case XSectShape::PARABOLIC: return parab_getAofY(xs, y);
1055 case XSectShape::POWERFUNC: return powerfunc_getAofY(xs, y);
1057 case XSectShape::CUSTOM:
1059 // Tabulated transect (legacy xsect_getAofY IRREGULAR case).
1060 if (xs.area_tbl)
1061 return xs.a_full * lookup(y_norm, xs.area_tbl, xs.transect_tbl_size);
1062 return 0.0;
1063 default: return 0.0;
1064 }
1065 }
1066
1067 // ============================================================================
1068 // Main dispatch: getWofY
1069 // ============================================================================
1070
1071 OPENSWMM_KERNEL_FN double getWofY(const XSectParams& xs, double y) const {
1072 double y_norm = y / xs.y_full;
1073
1074 switch (static_cast<XSectShape>(xs.type)) {
1077 return xs.w_max * lookup(y_norm, tbl.W_Circ, tbl.N_W_Circ);
1079 double yn = (y + xs.y_bot) / (xs.y_full + xs.y_bot);
1080 return xs.w_max * lookup(yn, tbl.W_Circ, tbl.N_W_Circ);
1081 }
1083 return xs.w_max * lookup(y_norm, tbl.W_Egg, tbl.N_W_Egg);
1085 return xs.w_max * lookup(y_norm, tbl.W_Horseshoe, tbl.N_W_Horseshoe);
1086 case XSectShape::GOTHIC:
1087 return xs.w_max * lookup(y_norm, tbl.W_Gothic, tbl.N_W_Gothic);
1089 return xs.w_max * lookup(y_norm, tbl.W_Catenary, tbl.N_W_Catenary);
1091 return xs.w_max * lookup(y_norm, tbl.W_SemiEllip, tbl.N_W_SemiEllip);
1093 return xs.w_max * lookup(y_norm, tbl.W_BasketHandle, tbl.N_W_BasketHandle);
1095 return xs.w_max * lookup(y_norm, tbl.W_SemiCirc, tbl.N_W_SemiCirc);
1097 return xs.w_max * lookup(y_norm, tbl.W_HorizEllipse, tbl.N_W_HorizEllipse);
1099 return xs.w_max * lookup(y_norm, tbl.W_VertEllipse, tbl.N_W_VertEllipse);
1100 case XSectShape::ARCH:
1101 return xs.w_max * lookup(y_norm, tbl.W_Arch, tbl.N_W_Arch);
1103 return shape::rectClosedWofY(y_norm, xs.w_max);
1104 case XSectShape::RECT_OPEN: return xs.w_max;
1108 case XSectShape::TRAPEZOIDAL: return trapez_getWofY(xs, y);
1109 case XSectShape::TRIANGULAR: return triang_getWofY(xs, y);
1110 case XSectShape::PARABOLIC: return parab_getWofY(xs, y);
1111 case XSectShape::POWERFUNC: return powerfunc_getWofY(xs, y);
1113 case XSectShape::CUSTOM:
1115 // Tabulated transect (legacy xsect_getWofY IRREGULAR case).
1116 if (xs.width_tbl)
1117 return xs.w_max * lookup(y_norm, xs.width_tbl, xs.transect_tbl_size);
1118 return 0.0;
1119 default: return 0.0;
1120 }
1121 }
1122
1123 // ============================================================================
1124 // Main dispatch: getRofY
1125 // ============================================================================
1126
1127 OPENSWMM_KERNEL_FN double getRofY(const XSectParams& xs, double y) const {
1128 double y_norm = y / xs.y_full;
1129
1130 switch (static_cast<XSectShape>(xs.type)) {
1133 return xs.r_full * lookup(y_norm, tbl.R_Circ, tbl.N_R_Circ);
1135 if (xs.y_bot == 0.0)
1136 return xs.r_full * lookup(y_norm, tbl.R_Circ, tbl.N_R_Circ);
1137 return filled_circ_getRofY(xs, y);
1139 return xs.r_full * lookup(y_norm, tbl.R_Egg, tbl.N_R_Egg);
1141 return xs.r_full * lookup(y_norm, tbl.R_Horseshoe, tbl.N_R_Horseshoe);
1143 return xs.r_full * lookup(y_norm, tbl.R_Baskethandle, tbl.N_R_Baskethandle);
1145 return xs.r_full * lookup(y_norm, tbl.R_HorizEllipse, tbl.N_R_HorizEllipse);
1147 return xs.r_full * lookup(y_norm, tbl.R_VertEllipse, tbl.N_R_VertEllipse);
1148 case XSectShape::ARCH:
1149 return xs.r_full * lookup(y_norm, tbl.R_Arch, tbl.N_R_Arch);
1152 case XSectShape::TRAPEZOIDAL: return trapez_getRofY(xs, y);
1153 case XSectShape::TRIANGULAR: return triang_getRofY(xs, y);
1154 case XSectShape::PARABOLIC: return parab_getRofY(xs, y);
1155 case XSectShape::POWERFUNC: return powerfunc_getRofY(xs, y);
1157 case XSectShape::CUSTOM:
1159 // Tabulated transect (legacy xsect_getRofY IRREGULAR case). Must be
1160 // explicit: the generic default below would call getRofA→getRofY
1161 // and recurse forever for these shapes.
1162 if (xs.hrad_tbl)
1163 return xs.r_full * lookup(y_norm, xs.hrad_tbl, xs.transect_tbl_size);
1164 return 0.0;
1165 default: // RECT_CLOSED, RECT_OPEN, MOD_BASKET, tabulated S-only shapes
1166 return getRofA(xs, getAofY(xs, y));
1167 }
1168 }
1169
1170 // ============================================================================
1171 // Main dispatch: getYofA
1172 // ============================================================================
1173
1174 OPENSWMM_KERNEL_FN double getYofA(const XSectParams& xs, double a) const {
1175 if (a <= 0.0) return 0.0;
1176 double alpha = a / xs.a_full;
1177
1178 switch (static_cast<XSectShape>(xs.type)) {
1180 case XSectShape::CIRCULAR: return circ_getYofA(xs, a);
1183 return xs.y_full * lookup(alpha, tbl.Y_Egg, tbl.N_Y_Egg);
1185 return xs.y_full * lookup(alpha, tbl.Y_Horseshoe, tbl.N_Y_Horseshoe);
1186 case XSectShape::GOTHIC:
1187 return xs.y_full * lookup(alpha, tbl.Y_Gothic, tbl.N_Y_Gothic);
1189 return xs.y_full * lookup(alpha, tbl.Y_Catenary, tbl.N_Y_Catenary);
1191 return xs.y_full * lookup(alpha, tbl.Y_SemiEllip, tbl.N_Y_SemiEllip);
1193 return xs.y_full * lookup(alpha, tbl.Y_BasketHandle, tbl.N_Y_BasketHandle);
1195 return xs.y_full * lookup(alpha, tbl.Y_SemiCirc, tbl.N_Y_SemiCirc);
1197 return xs.y_full * invLookup(alpha, tbl.A_HorizEllipse, tbl.N_A_HorizEllipse, tbl.lut(LutId::A_HorizEllipse));
1199 return xs.y_full * invLookup(alpha, tbl.A_VertEllipse, tbl.N_A_VertEllipse, tbl.lut(LutId::A_VertEllipse));
1200 case XSectShape::ARCH:
1201 return xs.y_full * invLookup(alpha, tbl.A_Arch, tbl.N_A_Arch, tbl.lut(LutId::A_Arch));
1203 case XSectShape::RECT_OPEN: return a / xs.w_max;
1204 case XSectShape::RECT_TRIANG: return rect_triang_getYofA(xs, a);
1205 case XSectShape::RECT_ROUND: return rect_round_getYofA(xs, a);
1206 case XSectShape::MOD_BASKET: return mod_basket_getYofA(xs, a);
1207 case XSectShape::TRAPEZOIDAL: return trapez_getYofA(xs, a);
1208 case XSectShape::TRIANGULAR: return triang_getYofA(xs, a);
1209 case XSectShape::PARABOLIC: return parab_getYofA(xs, a);
1210 case XSectShape::POWERFUNC: return powerfunc_getYofA(xs, a);
1212 case XSectShape::CUSTOM:
1214 // Invert the normalized area table (legacy xsect_getYofA IRREGULAR).
1215 if (xs.area_tbl)
1216 return xs.y_full * invLookup(alpha, xs.area_tbl, xs.transect_tbl_size, xs.area_lut);
1217 return 0.0;
1218 default: return 0.0;
1219 }
1220 }
1221
1222 // ============================================================================
1223 // Main dispatch: getSofA
1224 // ============================================================================
1225
1226 OPENSWMM_KERNEL_FN double getSofA(const XSectParams& xs, double a) const {
1227 double alpha = a / xs.a_full;
1228
1229 switch (static_cast<XSectShape>(xs.type)) {
1231 case XSectShape::CIRCULAR: return circ_getSofA(xs, a);
1233 return xs.s_full * lookup(alpha, tbl.S_Egg, tbl.N_S_Egg);
1235 return xs.s_full * lookup(alpha, tbl.S_Horseshoe, tbl.N_S_Horseshoe);
1236 case XSectShape::GOTHIC:
1237 return xs.s_full * lookup(alpha, tbl.S_Gothic, tbl.N_S_Gothic);
1239 return xs.s_full * lookup(alpha, tbl.S_Catenary, tbl.N_S_Catenary);
1241 return xs.s_full * lookup(alpha, tbl.S_SemiEllip, tbl.N_S_SemiEllip);
1243 return xs.s_full * lookup(alpha, tbl.S_BasketHandle, tbl.N_S_BasketHandle);
1245 return xs.s_full * lookup(alpha, tbl.S_SemiCirc, tbl.N_S_SemiCirc);
1246 case XSectShape::RECT_CLOSED: return rect_closed_getSofA(xs, a);
1247 case XSectShape::RECT_OPEN: return rect_open_getSofA(xs, a);
1248 case XSectShape::RECT_TRIANG: return rect_triang_getSofA(xs, a);
1249 case XSectShape::RECT_ROUND: return rect_round_getSofA(xs, a);
1250 default: {
1251 if (a == 0.0) return 0.0;
1252 double r = getRofA(xs, a);
1253 if (r < TINY) return 0.0;
1254 return a * std::pow(r, 2.0 / 3.0);
1255 }
1256 }
1257 }
1258
1259 // ============================================================================
1260 // getRofA — hydraulic radius from area
1261 // ============================================================================
1262
1263 OPENSWMM_KERNEL_FN double getRofA(const XSectParams& xs, double a) const {
1264 if (a <= 0.0) return 0.0;
1265 switch (static_cast<XSectShape>(xs.type)) {
1268 case XSectShape::ARCH:
1271 case XSectShape::CUSTOM:
1273 return getRofY(xs, getYofA(xs, a));
1274 case XSectShape::RECT_CLOSED: return rect_closed_getRofA(xs, a);
1276 return shape::rectOpenRofA(a, xs.w_max, xs.s_bot);
1277 case XSectShape::RECT_TRIANG: return rect_triang_getRofA(xs, a);
1278 case XSectShape::RECT_ROUND: return rect_round_getRofA(xs, a);
1279 case XSectShape::MOD_BASKET: return mod_basket_getRofA(xs, a);
1280 case XSectShape::TRAPEZOIDAL: return trapez_getRofA(xs, a);
1281 case XSectShape::TRIANGULAR: return triang_getRofA(xs, a);
1282 case XSectShape::PARABOLIC: return parab_getRofA(xs, a);
1283 case XSectShape::POWERFUNC: return powerfunc_getRofA(xs, a);
1284 default: {
1285 double s = getSofA(xs, a);
1286 if (s < TINY || a < TINY) return 0.0;
1287 return std::pow(s / a, 3.0 / 2.0);
1288 }
1289 }
1290 }
1291
1292 // ============================================================================
1293 // getdSdA — derivative of section factor w.r.t. area
1294 // ============================================================================
1295
1296 OPENSWMM_KERNEL_FN double getdSdA(const XSectParams& xs, double a) const {
1297 switch (static_cast<XSectShape>(xs.type)) {
1299 case XSectShape::CIRCULAR: return circ_getdSdA(xs, a);
1301 return tabular_getdSdA(xs, a, tbl.S_Egg, tbl.N_S_Egg);
1303 return tabular_getdSdA(xs, a, tbl.S_Horseshoe, tbl.N_S_Horseshoe);
1304 case XSectShape::GOTHIC:
1305 return tabular_getdSdA(xs, a, tbl.S_Gothic, tbl.N_S_Gothic);
1307 return tabular_getdSdA(xs, a, tbl.S_Catenary, tbl.N_S_Catenary);
1309 return tabular_getdSdA(xs, a, tbl.S_SemiEllip, tbl.N_S_SemiEllip);
1311 return tabular_getdSdA(xs, a, tbl.S_BasketHandle, tbl.N_S_BasketHandle);
1313 return tabular_getdSdA(xs, a, tbl.S_SemiCirc, tbl.N_S_SemiCirc);
1314 case XSectShape::RECT_CLOSED: return rect_closed_getdSdA(xs, a);
1315 case XSectShape::RECT_OPEN: return rect_open_getdSdA(xs, a);
1316 case XSectShape::RECT_TRIANG: return rect_triang_getdSdA(xs, a);
1317 case XSectShape::RECT_ROUND: return rect_round_getdSdA(xs, a);
1318 case XSectShape::MOD_BASKET: return mod_basket_getdSdA(xs, a);
1319 case XSectShape::TRAPEZOIDAL: return trapez_getdSdA(xs, a);
1320 case XSectShape::TRIANGULAR: return triang_getdSdA(xs, a);
1321 default: return generic_getdSdA(xs, a);
1322 }
1323 }
1324
1325 // ============================================================================
1326 // getAofS — area from section factor
1327 // ============================================================================
1328
1329 OPENSWMM_KERNEL_FN double getAofS(const XSectParams& xs, double s) const {
1330 double psi = s / xs.s_full;
1331 if (s <= 0.0) return 0.0;
1332 if (s > xs.s_max) s = xs.s_max;
1333
1334 switch (static_cast<XSectShape>(xs.type)) {
1335 case XSectShape::DUMMY: return 0.0;
1337 case XSectShape::CIRCULAR: return circ_getAofS(xs, s);
1339 return xs.a_full * invLookup(psi, tbl.S_Egg, tbl.N_S_Egg, tbl.lut(LutId::S_Egg));
1341 return xs.a_full * invLookup(psi, tbl.S_Horseshoe, tbl.N_S_Horseshoe, tbl.lut(LutId::S_Horseshoe));
1342 case XSectShape::GOTHIC:
1343 return xs.a_full * invLookup(psi, tbl.S_Gothic, tbl.N_S_Gothic, tbl.lut(LutId::S_Gothic));
1345 return xs.a_full * invLookup(psi, tbl.S_Catenary, tbl.N_S_Catenary, tbl.lut(LutId::S_Catenary));
1347 return xs.a_full * invLookup(psi, tbl.S_SemiEllip, tbl.N_S_SemiEllip, tbl.lut(LutId::S_SemiEllip));
1349 return xs.a_full * invLookup(psi, tbl.S_BasketHandle, tbl.N_S_BasketHandle, tbl.lut(LutId::S_BasketHandle));
1351 return xs.a_full * invLookup(psi, tbl.S_SemiCirc, tbl.N_S_SemiCirc, tbl.lut(LutId::S_SemiCirc));
1352 default: {
1353 // Newton-Raphson on S(a) = s, bracketed in [a1, a2] (legacy generic_getAofS).
1354 // a2 = absolute area at max flow = xsect_getAmax.
1355 // PARITY: legacy xsect_getAmax (xsect.c:711-713) returns aBot for
1356 // BOTH IRREGULAR and CUSTOM (the physical area at the max section
1357 // factor, set from the transect/shape tables); every other shape
1358 // uses aFull * Amax-ratio.
1359 double a1, a2;
1360 const XSectShape sh = static_cast<XSectShape>(xs.type);
1361 double a_max = (sh == XSectShape::CUSTOM || sh == XSectShape::IRREGULAR)
1362 ? xs.a_bot
1363 : xs.a_full * getAmax(xs);
1364 if ((s <= xs.s_max && s >= xs.s_full) && xs.s_max != xs.s_full) {
1365 a1 = xs.a_full; // sFull < sMax: root lies between aFull and aMax
1366 a2 = a_max;
1367 } else {
1368 a1 = 0.0;
1369 a2 = a_max;
1370 }
1371 double a = 0.5 * (a1 + a2);
1372 double tol = 0.0001 * xs.a_full;
1373 findroot_Newton(a1, a2, &a, tol, [&](double aa, double* f, double* df) {
1374 *f = getSofA(xs, aa) - s;
1375 *df = getdSdA(xs, aa);
1376 });
1377 return a;
1378 }
1379 }
1380 }
1381
1382 // ============================================================================
1383 // getAmax — ratio of area at max flow to full area
1384 // ============================================================================
1385
1386 OPENSWMM_KERNEL_FN double getAmax(const XSectParams& xs) const {
1387 if (xs.type >= 0 && xs.type <= 25)
1388 return tbl.Amax[xs.type];
1389 return 1.0;
1390 }
1391
1392 // ============================================================================
1393 // getYcrit — critical depth for a given flow rate (legacy xsect_getYcrit)
1394 // ============================================================================
1395
1396 OPENSWMM_KERNEL_FN double getYcrit(const XSectParams& xs, double q) const {
1397 if (q <= 0.0) return 0.0;
1398 double q2g = q * q / GRAVITY;
1399 if (q2g == 0.0) return 0.0;
1400
1401 double y;
1402 switch (static_cast<XSectShape>(xs.type)) {
1403 case XSectShape::DUMMY: return 0.0;
1406 y = std::pow(q2g / (xs.w_max * xs.w_max), 1.0 / 3.0);
1407 break;
1409 y = std::pow(2.0 * q2g / (xs.s_bot * xs.s_bot), 1.0 / 5.0);
1410 break;
1412 y = std::pow(27.0 / 32.0 * q2g / (xs.r_bot * xs.r_bot), 1.0 / 4.0);
1413 break;
1415 y = 1.0 / (2.0 * xs.s_bot + 3.0);
1416 y = std::pow(q2g * (xs.s_bot + 1.0) / (xs.r_bot * xs.r_bot), y);
1417 break;
1418 default: {
1419 // Critical flow function Q_c(yc) - qTarget (legacy getQcritical).
1420 auto qCritical = [&](double yc, double qTarget) -> double {
1421 double a = getAofY(xs, yc);
1422 double w = getWofY(xs, yc);
1423 if (w > 0.0) return a * std::sqrt(GRAVITY * a / w) - qTarget;
1424 return -qTarget;
1425 };
1426
1427 // Initial estimate from equivalent circular conduit.
1428 double y0 = 1.01 * std::pow(q2g / xs.y_full, 0.25);
1429 if (y0 >= xs.y_full) y0 = 0.97 * xs.y_full;
1430
1431 // Ratio of conduit area to equivalent circular area.
1432 double r = xs.a_full / (PI / 4.0 * xs.y_full * xs.y_full);
1433
1434 if (r >= 0.5 && r <= 2.0) {
1435 // --- interval enumeration (legacy getYcritEnum), 25 increments
1436 constexpr int N_INC = 25;
1437 double dy = xs.y_full / N_INC;
1438 int i1 = static_cast<int>(y0 / dy);
1439 double q0 = qCritical(i1 * dy, 0.0);
1440 if (q0 < q) {
1441 y = xs.y_full;
1442 for (int i = i1 + 1; i <= N_INC; ++i) {
1443 double qc = qCritical(i * dy, 0.0);
1444 if (qc >= q) {
1445 y = ((q - q0) / (qc - q0) + static_cast<double>(i - 1)) * dy;
1446 break;
1447 }
1448 q0 = qc;
1449 }
1450 } else {
1451 y = 0.0;
1452 for (int i = i1 - 1; i >= 0; --i) {
1453 double qc = qCritical(i * dy, 0.0);
1454 if (qc < q) {
1455 y = ((q - qc) / (q0 - qc) + static_cast<double>(i)) * dy;
1456 break;
1457 }
1458 q0 = qc;
1459 }
1460 }
1461 } else {
1462 // --- Ridder's method (legacy getYcritRidder)
1463 double y1 = 0.0;
1464 double y2 = 0.99 * xs.y_full;
1465 double q2 = qCritical(y2, 0.0);
1466 if (q2 < q) { y = xs.y_full; break; }
1467 double q0 = qCritical(y0, 0.0);
1468 double q1 = qCritical(0.5 * xs.y_full, 0.0);
1469 if (q0 > q) {
1470 y2 = y0;
1471 if (q1 < q) y1 = 0.5 * xs.y_full;
1472 } else {
1473 y1 = y0;
1474 if (q1 > q) y2 = 0.5 * xs.y_full;
1475 }
1476 y = findroot_Ridder(y1, y2, 0.001,
1477 [&](double yc) { return qCritical(yc, q); });
1478 }
1479 break;
1480 }
1481 }
1482 return std::min(y, xs.y_full);
1483 }
1484
1485 // ============================================================================
1486 // isOpen — returns true for open shapes
1487 // ============================================================================
1488
1489 OPENSWMM_KERNEL_FN bool isOpen(int type) const {
1490 switch (static_cast<XSectShape>(type)) {
1497 return true;
1498 default:
1499 return false;
1500 }
1501 }
1502
1503};
1504
1509const XsectTables& hostTables();
1510const XsectEval& hostEval();
1511
1512} // namespace openswmm::xsect
1513
1514#endif // OPENSWMM_XSECT_KERNELS_HPP
#define OPENSWMM_KERNEL_FN
Definition ExplicitKokkosSurfaceSolver.cpp:18
Bit-exact geometry-table interpolation — the single source of truth.
#define SIGN(a, b)
Definition findroot.c:16
#define MAXIT
Definition findroot.c:17
The analytic shape formulas — the single definition of each.
Definition XSectKernels.hpp:111
OPENSWMM_KERNEL_FN double rectClosedWofY(double y_norm, double w_max)
Definition XSectKernels.hpp:168
OPENSWMM_KERNEL_FN double trapezRofY(double y, double y_bot, double s_bot, double r_bot)
Definition XSectKernels.hpp:154
OPENSWMM_KERNEL_FN double trapezAofY(double y, double y_bot, double s_bot)
Definition XSectKernels.hpp:119
OPENSWMM_KERNEL_FN double rectAofY(double y, double w_max)
Definition XSectKernels.hpp:115
OPENSWMM_KERNEL_FN double trapezWofY(double y, double y_bot, double s_bot)
Definition XSectKernels.hpp:173
OPENSWMM_KERNEL_FN double triangWofY(double y, double s_bot)
Definition XSectKernels.hpp:177
OPENSWMM_KERNEL_FN double parabAofY(double y, double r_bot)
Definition XSectKernels.hpp:127
OPENSWMM_KERNEL_FN double powerfuncWofY(double y, double s_bot, double r_bot)
Definition XSectKernels.hpp:185
OPENSWMM_KERNEL_FN double powerfuncAofY(double y, double s_bot, double r_bot)
Definition XSectKernels.hpp:131
OPENSWMM_KERNEL_FN double rectClosedRofA(double a, double w_max, double a_full)
Definition XSectKernels.hpp:139
OPENSWMM_KERNEL_FN double parabWofY(double y, double r_bot)
Definition XSectKernels.hpp:181
OPENSWMM_KERNEL_FN double rectOpenRofA(double a, double w_max, double s_bot)
Definition XSectKernels.hpp:149
OPENSWMM_KERNEL_FN double triangAofY(double y, double s_bot)
Definition XSectKernels.hpp:123
OPENSWMM_KERNEL_FN double triangRofY(double y, double s_bot, double r_bot)
Definition XSectKernels.hpp:160
Definition XSectBatch.hpp:149
const XsectEval & hostEval()
Definition XSection.cpp:209
const XsectTables & hostTables()
Definition XSection.cpp:114
constexpr double TINY
Definition XSectKernels.hpp:79
constexpr double RECT_ROUND_ALFMAX
Definition XSectKernels.hpp:85
constexpr double RECT_ALFMAX
Definition XSectKernels.hpp:83
constexpr double GRAVITY
Definition XSectKernels.hpp:81
double lookup_exact(double x, const double *t, int n) noexcept
Bit-exact transliteration of legacy xsect.c:lookup().
Definition XSectLookup.hpp:70
int locate_maybe_lut(double y, const double *table, int jLast, const LocateLut *L) noexcept
Dispatch: use the map when one was built for this exact table extent.
Definition XSectLookup.hpp:261
constexpr double RECT_TRIANG_ALFMAX
Definition XSectKernels.hpp:84
LutId
Pointers to the shared geometry tables, in whichever memory space the consumer runs in.
Definition XSectKernels.hpp:209
@ S_Egg
Definition XSectKernels.hpp:218
@ A_Arch
Definition XSectKernels.hpp:216
@ Y_Gothic
Definition XSectKernels.hpp:210
@ A_VertEllipse
Definition XSectKernels.hpp:215
@ COUNT
Definition XSectKernels.hpp:225
@ Y_SemiCirc
Definition XSectKernels.hpp:213
@ A_HorizEllipse
Definition XSectKernels.hpp:214
@ S_SemiEllip
Definition XSectKernels.hpp:222
@ S_Catenary
Definition XSectKernels.hpp:221
@ Y_SemiEllip
Definition XSectKernels.hpp:212
@ S_Gothic
Definition XSectKernels.hpp:220
@ S_Circ
Definition XSectKernels.hpp:217
@ S_BasketHandle
Definition XSectKernels.hpp:223
@ S_Horseshoe
Definition XSectKernels.hpp:219
@ S_SemiCirc
Definition XSectKernels.hpp:224
@ Y_Catenary
Definition XSectKernels.hpp:211
int locate_bisect(double y, const double *table, int jLast) noexcept
Definition XSectLookup.hpp:201
constexpr double PI
Definition XSectKernels.hpp:80
XSectShape
Definition XSectBatch.hpp:77
@ TRIANGULAR
Definition XSectBatch.hpp:84
@ RECT_CLOSED
Definition XSectBatch.hpp:81
@ POWERFUNC
Definition XSectBatch.hpp:86
@ ARCH
Definition XSectBatch.hpp:92
@ FORCE_MAIN
Definition XSectBatch.hpp:102
@ RECT_OPEN
Definition XSectBatch.hpp:82
@ BASKETHANDLE
Definition XSectBatch.hpp:98
@ STREET_XSECT
Definition XSectBatch.hpp:103
@ SEMICIRCULAR
Definition XSectBatch.hpp:99
@ IRREGULAR
Definition XSectBatch.hpp:100
@ MOD_BASKET
Definition XSectBatch.hpp:89
@ HORIZ_ELLIPSE
Definition XSectBatch.hpp:90
@ VERT_ELLIPSE
Definition XSectBatch.hpp:91
@ CUSTOM
Definition XSectBatch.hpp:101
@ RECT_ROUND
Definition XSectBatch.hpp:88
@ EGGSHAPED
Definition XSectBatch.hpp:93
@ SEMIELLIPTICAL
Definition XSectBatch.hpp:97
@ CATENARY
Definition XSectBatch.hpp:96
@ CIRCULAR
Definition XSectBatch.hpp:79
@ TRAPEZOIDAL
Definition XSectBatch.hpp:83
@ HORSESHOE
Definition XSectBatch.hpp:94
@ DUMMY
Definition XSectBatch.hpp:78
@ PARABOLIC
Definition XSectBatch.hpp:85
@ RECT_TRIANG
Definition XSectBatch.hpp:87
@ FILLED_CIRCULAR
Definition XSectBatch.hpp:80
@ GOTHIC
Definition XSectBatch.hpp:95
double * y
Definition odesolve.c:28
Definition XSectBatch.hpp:110
double s_full
Section factor when full (ft^4/3)
Definition XSectBatch.hpp:120
double s_bot
Slope of bottom section / exponent.
Definition XSectBatch.hpp:125
const double * hrad_tbl
Normalized hyd-radius vs y/y_full.
Definition XSectBatch.hpp:135
int transect_tbl_size
Entry count of the tables above.
Definition XSectBatch.hpp:137
const double * width_tbl
Normalized width vs y/y_full.
Definition XSectBatch.hpp:136
double a_full
Area when full (ft2)
Definition XSectBatch.hpp:118
double r_bot
Radius of bottom section / coefficient.
Definition XSectBatch.hpp:126
double s_max
Section factor at max flow (ft^4/3)
Definition XSectBatch.hpp:121
int type
Definition XSectBatch.hpp:111
double a_bot
Area of bottom section.
Definition XSectBatch.hpp:124
const xsect::LocateLut * area_lut
Definition XSectBatch.hpp:142
double w_max
Width at widest point (ft)
Definition XSectBatch.hpp:116
double y_bot
Depth of bottom section / fill depth.
Definition XSectBatch.hpp:123
double y_full
Full depth (ft)
Definition XSectBatch.hpp:115
double r_full
Hydraulic radius when full (ft)
Definition XSectBatch.hpp:119
const double * area_tbl
Normalized area vs y/y_full.
Definition XSectBatch.hpp:134
Definition XSectLookup.hpp:182
The geometry layer itself, parameterized on where its tables live.
Definition XSectKernels.hpp:328
OPENSWMM_KERNEL_FN double getRofY(const XSectParams &xs, double y) const
Definition XSectKernels.hpp:1127
OPENSWMM_KERNEL_FN double filled_circ_getAofY(const XSectParams &xs, double y) const
Definition XSectKernels.hpp:619
OPENSWMM_KERNEL_FN double rect_triang_getYofA(const XSectParams &xs, double a) const
Definition XSectKernels.hpp:701
OPENSWMM_KERNEL_FN double rect_open_getdSdA(const XSectParams &xs, double a) const
Definition XSectKernels.hpp:680
OPENSWMM_KERNEL_FN double rect_triang_getRofA(const XSectParams &xs, double a) const
Definition XSectKernels.hpp:706
OPENSWMM_KERNEL_FN double tabular_getdSdA(const XSectParams &xs, double a, const double *table, int n_items) const
Definition XSectKernels.hpp:563
OPENSWMM_KERNEL_FN double parab_getRofA(const XSectParams &xs, double a) const
Definition XSectKernels.hpp:959
OPENSWMM_KERNEL_FN double rect_open_getSofA(const XSectParams &xs, double a) const
Definition XSectKernels.hpp:674
OPENSWMM_KERNEL_FN double filled_circ_getRofY(const XSectParams &xs, double y) const
Definition XSectKernels.hpp:636
OPENSWMM_KERNEL_FN double rect_round_getYofA(const XSectParams &xs, double a) const
Definition XSectKernels.hpp:750
OPENSWMM_KERNEL_FN double mod_basket_getdSdA(const XSectParams &xs, double a) const
Definition XSectKernels.hpp:858
OPENSWMM_KERNEL_FN bool isOpen(int type) const
Definition XSectKernels.hpp:1489
OPENSWMM_KERNEL_FN double rect_round_getWofY(const XSectParams &xs, double y) const
Definition XSectKernels.hpp:763
OPENSWMM_KERNEL_FN double mod_basket_getWofY(const XSectParams &xs, double y) const
Definition XSectKernels.hpp:840
OPENSWMM_KERNEL_FN double rect_round_getRofY(const XSectParams &xs, double y) const
Definition XSectKernels.hpp:784
OPENSWMM_KERNEL_FN double generic_getdSdA(const XSectParams &xs, double a) const
Definition XSectKernels.hpp:553
OPENSWMM_KERNEL_FN double rect_triang_getRofY(const XSectParams &xs, double y) const
Definition XSectKernels.hpp:716
OPENSWMM_KERNEL_FN double powerfunc_getWofY(const XSectParams &xs, double y) const
Definition XSectKernels.hpp:972
OPENSWMM_KERNEL_FN double lookup(double x, const double *table, int n_items) const
Definition XSectKernels.hpp:417
OPENSWMM_KERNEL_FN double findroot_Ridder(double x1, double x2, double xacc, Func func) const
Definition XSectKernels.hpp:375
OPENSWMM_KERNEL_FN double filled_circ_getYofA(const XSectParams &xs, double a) const
Definition XSectKernels.hpp:628
OPENSWMM_KERNEL_FN double parab_getPofY(const XSectParams &xs, double y) const
Definition XSectKernels.hpp:948
OPENSWMM_KERNEL_FN double trapez_getWofY(const XSectParams &xs, double y) const
Definition XSectKernels.hpp:875
OPENSWMM_KERNEL_FN double getYcircular(double alpha) const
Definition XSectKernels.hpp:512
OPENSWMM_KERNEL_FN double circ_getAofY(const XSectParams &xs, double y) const
Definition XSectKernels.hpp:577
OPENSWMM_KERNEL_FN double powerfunc_getAofY(const XSectParams &xs, double y) const
Definition XSectKernels.hpp:968
OPENSWMM_KERNEL_FN double circ_getAofS(const XSectParams &xs, double s) const
Definition XSectKernels.hpp:594
OPENSWMM_KERNEL_FN int locate(double y, const double *table, int jLast) const
Definition XSectKernels.hpp:412
OPENSWMM_KERNEL_FN double getAcircular(double psi) const
Definition XSectKernels.hpp:537
OPENSWMM_KERNEL_FN double trapez_getRofY(const XSectParams &xs, double y) const
Definition XSectKernels.hpp:889
OPENSWMM_KERNEL_FN double rect_triang_getSofA(const XSectParams &xs, double a) const
Definition XSectKernels.hpp:725
OPENSWMM_KERNEL_FN double rect_round_getAofY(const XSectParams &xs, double y) const
Definition XSectKernels.hpp:757
OPENSWMM_KERNEL_FN double trapez_getYofA(const XSectParams &xs, double a) const
Definition XSectKernels.hpp:879
OPENSWMM_KERNEL_FN double mod_basket_getYofA(const XSectParams &xs, double a) const
Definition XSectKernels.hpp:822
OPENSWMM_KERNEL_FN double trapez_getAofY(const XSectParams &xs, double y) const
Definition XSectKernels.hpp:871
OPENSWMM_KERNEL_FN double getWofY(const XSectParams &xs, double y) const
Definition XSectKernels.hpp:1071
OPENSWMM_KERNEL_FN double rect_triang_getdSdA(const XSectParams &xs, double a) const
Definition XSectKernels.hpp:733
OPENSWMM_KERNEL_FN double powerfunc_getYofA(const XSectParams &xs, double a) const
Definition XSectKernels.hpp:976
OPENSWMM_KERNEL_FN double getSofA(const XSectParams &xs, double a) const
Definition XSectKernels.hpp:1226
OPENSWMM_KERNEL_FN double powerfunc_getRofA(const XSectParams &xs, double a) const
Definition XSectKernels.hpp:1005
OPENSWMM_KERNEL_FN double getThetaOfAlpha(double alpha) const
Definition XSectKernels.hpp:476
OPENSWMM_KERNEL_FN double getAofS(const XSectParams &xs, double s) const
Definition XSectKernels.hpp:1329
OPENSWMM_KERNEL_FN double getAmax(const XSectParams &xs) const
Definition XSectKernels.hpp:1386
OPENSWMM_KERNEL_FN double rect_triang_getAofY(const XSectParams &xs, double y) const
Definition XSectKernels.hpp:691
OPENSWMM_KERNEL_FN double triang_getWofY(const XSectParams &xs, double y) const
Definition XSectKernels.hpp:908
OPENSWMM_KERNEL_FN double getRofA(const XSectParams &xs, double a) const
Definition XSectKernels.hpp:1263
OPENSWMM_KERNEL_FN double rect_round_getRofA(const XSectParams &xs, double a) const
Definition XSectKernels.hpp:768
OPENSWMM_KERNEL_FN double triang_getYofA(const XSectParams &xs, double a) const
Definition XSectKernels.hpp:912
OPENSWMM_KERNEL_FN double triang_getdSdA(const XSectParams &xs, double a) const
Definition XSectKernels.hpp:924
OPENSWMM_KERNEL_FN double triang_getRofY(const XSectParams &xs, double y) const
Definition XSectKernels.hpp:920
OPENSWMM_KERNEL_FN double mod_basket_getAofY(const XSectParams &xs, double y) const
Definition XSectKernels.hpp:832
OPENSWMM_KERNEL_FN double getThetaOfPsi(double psi) const
Definition XSectKernels.hpp:491
OPENSWMM_KERNEL_FN double rect_closed_getRofA(const XSectParams &xs, double a) const
Definition XSectKernels.hpp:654
OPENSWMM_KERNEL_FN double parab_getYofA(const XSectParams &xs, double a) const
Definition XSectKernels.hpp:943
OPENSWMM_KERNEL_FN double getYcrit(const XSectParams &xs, double q) const
Definition XSectKernels.hpp:1396
OPENSWMM_KERNEL_FN double rect_round_getSofA(const XSectParams &xs, double a) const
Definition XSectKernels.hpp:791
OPENSWMM_KERNEL_FN double getAofY(const XSectParams &xs, double y) const
Definition XSectKernels.hpp:1014
OPENSWMM_KERNEL_FN double rect_closed_getSofA(const XSectParams &xs, double a) const
Definition XSectKernels.hpp:658
OPENSWMM_KERNEL_FN double invLookup(double y, const double *table, int n_items, const LocateLut *lut=nullptr) const
Definition XSectKernels.hpp:444
OPENSWMM_KERNEL_FN double parab_getWofY(const XSectParams &xs, double y) const
Definition XSectKernels.hpp:939
OPENSWMM_KERNEL_FN double getYofA(const XSectParams &xs, double a) const
Definition XSectKernels.hpp:1174
OPENSWMM_KERNEL_FN double rect_round_getdSdA(const XSectParams &xs, double a) const
Definition XSectKernels.hpp:805
OPENSWMM_KERNEL_FN double rect_closed_getdSdA(const XSectParams &xs, double a) const
Definition XSectKernels.hpp:665
OPENSWMM_KERNEL_FN double circ_getSofA(const XSectParams &xs, double a) const
Definition XSectKernels.hpp:588
OPENSWMM_KERNEL_FN double triang_getRofA(const XSectParams &xs, double a) const
Definition XSectKernels.hpp:916
OPENSWMM_KERNEL_FN double trapez_getRofA(const XSectParams &xs, double a) const
Definition XSectKernels.hpp:885
XsectTables tbl
Definition XSectKernels.hpp:329
OPENSWMM_KERNEL_FN double triang_getAofY(const XSectParams &xs, double y) const
Definition XSectKernels.hpp:904
OPENSWMM_KERNEL_FN double parab_getRofY(const XSectParams &xs, double y) const
Definition XSectKernels.hpp:954
OPENSWMM_KERNEL_FN double circ_getdSdA(const XSectParams &xs, double a) const
Definition XSectKernels.hpp:602
OPENSWMM_KERNEL_FN int findroot_Newton(double x1, double x2, double *rts, double xacc, Func func) const
Definition XSectKernels.hpp:337
OPENSWMM_KERNEL_FN double mod_basket_getRofA(const XSectParams &xs, double a) const
Definition XSectKernels.hpp:847
OPENSWMM_KERNEL_FN double trapez_getdSdA(const XSectParams &xs, double a) const
Definition XSectKernels.hpp:893
OPENSWMM_KERNEL_FN double circ_getYofA(const XSectParams &xs, double a) const
Definition XSectKernels.hpp:582
OPENSWMM_KERNEL_FN double getdSdA(const XSectParams &xs, double a) const
Definition XSectKernels.hpp:1296
OPENSWMM_KERNEL_FN double parab_getAofY(const XSectParams &xs, double y) const
Definition XSectKernels.hpp:935
OPENSWMM_KERNEL_FN double getScircular(double alpha) const
Definition XSectKernels.hpp:524
OPENSWMM_KERNEL_FN double powerfunc_getPofY(const XSectParams &xs, double y) const
Definition XSectKernels.hpp:981
OPENSWMM_KERNEL_FN double rect_triang_getWofY(const XSectParams &xs, double y) const
Definition XSectKernels.hpp:696
OPENSWMM_KERNEL_FN double powerfunc_getRofY(const XSectParams &xs, double y) const
Definition XSectKernels.hpp:1000
Definition XSectKernels.hpp:228
int N_W_VertEllipse
Definition XSectKernels.hpp:305
int N_Y_Catenary
Definition XSectKernels.hpp:307
const double * S_Catenary
Definition XSectKernels.hpp:244
const double * A_HorizEllipse
Definition XSectKernels.hpp:233
int N_W_Horseshoe
Definition XSectKernels.hpp:302
const double * W_Gothic
Definition XSectKernels.hpp:256
const double * Y_Gothic
Definition XSectKernels.hpp:266
const double * R_Egg
Definition XSectKernels.hpp:239
const double * Y_SemiEllip
Definition XSectKernels.hpp:269
int N_W_Circ
Definition XSectKernels.hpp:298
const double * R_Arch
Definition XSectKernels.hpp:236
const double * A_Baskethandle
Definition XSectKernels.hpp:230
int N_W_SemiEllip
Definition XSectKernels.hpp:304
const double * R_Circ
Definition XSectKernels.hpp:238
int N_S_Circ
Definition XSectKernels.hpp:289
int N_W_Gothic
Definition XSectKernels.hpp:300
const double * Amax
Definition XSectKernels.hpp:270
const double * Y_Egg
Definition XSectKernels.hpp:265
int N_W_BasketHandle
Definition XSectKernels.hpp:296
int N_R_Horseshoe
Definition XSectKernels.hpp:285
int N_W_HorizEllipse
Definition XSectKernels.hpp:301
const double * A_Arch
Definition XSectKernels.hpp:229
const double * R_HorizEllipse
Definition XSectKernels.hpp:240
const LocateLut * luts
Bucket maps for the inverted tables, indexed by LutId; null == bisect.
Definition XSectKernels.hpp:272
int N_W_Catenary
Definition XSectKernels.hpp:297
const double * S_SemiEllip
Definition XSectKernels.hpp:250
int N_Y_Circ
Definition XSectKernels.hpp:308
const double * W_HorizEllipse
Definition XSectKernels.hpp:257
int N_R_Circ
Definition XSectKernels.hpp:282
const double * W_Circ
Definition XSectKernels.hpp:254
const double * R_Baskethandle
Definition XSectKernels.hpp:237
int N_S_Catenary
Definition XSectKernels.hpp:288
const double * S_Egg
Definition XSectKernels.hpp:246
int N_A_Egg
Definition XSectKernels.hpp:276
const double * S_Horseshoe
Definition XSectKernels.hpp:248
int N_Y_BasketHandle
Definition XSectKernels.hpp:306
const double * Y_SemiCirc
Definition XSectKernels.hpp:268
int N_Y_Egg
Definition XSectKernels.hpp:309
const double * R_Horseshoe
Definition XSectKernels.hpp:241
int N_W_Arch
Definition XSectKernels.hpp:295
const double * W_VertEllipse
Definition XSectKernels.hpp:261
int N_A_Arch
Definition XSectKernels.hpp:273
const double * A_Circ
Definition XSectKernels.hpp:231
const double * S_Gothic
Definition XSectKernels.hpp:247
const double * Y_BasketHandle
Definition XSectKernels.hpp:262
OPENSWMM_KERNEL_FN const LocateLut * lut(LutId id) const
The bucket map for one inverted table, or null when none was built.
Definition XSectKernels.hpp:316
const double * Y_Horseshoe
Definition XSectKernels.hpp:267
int N_S_BasketHandle
Definition XSectKernels.hpp:287
int N_S_SemiEllip
Definition XSectKernels.hpp:294
int N_A_VertEllipse
Definition XSectKernels.hpp:279
int N_A_Horseshoe
Definition XSectKernels.hpp:278
const double * W_Arch
Definition XSectKernels.hpp:251
const double * Y_Circ
Definition XSectKernels.hpp:264
int N_A_Baskethandle
Definition XSectKernels.hpp:274
int N_W_SemiCirc
Definition XSectKernels.hpp:303
const double * R_VertEllipse
Definition XSectKernels.hpp:242
int N_S_Gothic
Definition XSectKernels.hpp:291
const double * W_Catenary
Definition XSectKernels.hpp:253
const double * S_SemiCirc
Definition XSectKernels.hpp:249
const double * A_VertEllipse
Definition XSectKernels.hpp:235
int N_A_Circ
Definition XSectKernels.hpp:275
int N_S_Horseshoe
Definition XSectKernels.hpp:292
int N_R_Arch
Definition XSectKernels.hpp:280
const double * A_Egg
Definition XSectKernels.hpp:232
int N_R_Baskethandle
Definition XSectKernels.hpp:281
const double * S_Circ
Definition XSectKernels.hpp:245
int N_Y_SemiEllip
Definition XSectKernels.hpp:313
int N_W_Egg
Definition XSectKernels.hpp:299
const double * Y_Catenary
Definition XSectKernels.hpp:263
const double * W_BasketHandle
Definition XSectKernels.hpp:252
int N_R_VertEllipse
Definition XSectKernels.hpp:286
const double * W_SemiCirc
Definition XSectKernels.hpp:259
int N_R_HorizEllipse
Definition XSectKernels.hpp:284
int N_Y_Horseshoe
Definition XSectKernels.hpp:311
const double * W_Horseshoe
Definition XSectKernels.hpp:258
int N_S_Egg
Definition XSectKernels.hpp:290
const double * A_Horseshoe
Definition XSectKernels.hpp:234
int N_Y_SemiCirc
Definition XSectKernels.hpp:312
int N_S_SemiCirc
Definition XSectKernels.hpp:293
const double * S_BasketHandle
Definition XSectKernels.hpp:243
int N_R_Egg
Definition XSectKernels.hpp:283
const double * W_Egg
Definition XSectKernels.hpp:255
const double * W_SemiEllip
Definition XSectKernels.hpp:260
int N_A_HorizEllipse
Definition XSectKernels.hpp:277
int N_Y_Gothic
Definition XSectKernels.hpp:310