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
125OPENSWMM_KERNEL_FN double rectAofY(double y, double w_max) {
126 return y * w_max;
127}
128
140OPENSWMM_KERNEL_FN double trapezAofY(double y, double y_bot, double s_bot) {
141 return (y_bot + s_bot * y) * y;
142}
143
152OPENSWMM_KERNEL_FN double triangAofY(double y, double s_bot) {
153 return y * y * s_bot;
154}
155
164OPENSWMM_KERNEL_FN double parabAofY(double y, double r_bot) {
165 return (4.0 / 3.0) * r_bot * y * std::sqrt(y);
166}
167
179OPENSWMM_KERNEL_FN double powerfuncAofY(double y, double s_bot, double r_bot) {
180 return r_bot * std::pow(y, s_bot + 1.0);
181}
182
183// ---- Hydraulic radius ----
184
195OPENSWMM_KERNEL_FN double rectClosedRofA(double a, double w_max, double a_full) {
196 if (a <= 0.0)
197 return 0.0;
198 double p = w_max + 2.0 * a / w_max;
199 if (a / a_full > RECT_ALFMAX)
200 p += (a / a_full - RECT_ALFMAX) / (1.0 - RECT_ALFMAX) * w_max;
201 return a / p;
202}
203
214OPENSWMM_KERNEL_FN double rectOpenRofA(double a, double w_max, double s_bot) {
215 if (a <= 0.0)
216 return 0.0;
217 return a / (w_max + (2.0 - s_bot) * a / w_max);
218}
219
232OPENSWMM_KERNEL_FN double trapezRofY(double y, double y_bot, double s_bot, double r_bot) {
233 if (y == 0.0)
234 return 0.0;
235 return trapezAofY(y, y_bot, s_bot) / (y_bot + y * r_bot);
236}
237
247OPENSWMM_KERNEL_FN double triangRofY(double y, double s_bot, double r_bot) {
248 return (y * s_bot) / (2.0 * r_bot);
249}
250
251// ---- Top width ----
252
262OPENSWMM_KERNEL_FN double rectClosedWofY(double y_norm, double w_max) {
263 if (y_norm == 1.0)
264 return 0.0;
265 return w_max;
266}
267
277OPENSWMM_KERNEL_FN double trapezWofY(double y, double y_bot, double s_bot) {
278 return y_bot + 2.0 * y * s_bot;
279}
280
289OPENSWMM_KERNEL_FN double triangWofY(double y, double s_bot) {
290 return 2.0 * s_bot * y;
291}
292
301OPENSWMM_KERNEL_FN double parabWofY(double y, double r_bot) {
302 return 2.0 * r_bot * std::sqrt(y);
303}
304
314OPENSWMM_KERNEL_FN double powerfuncWofY(double y, double s_bot, double r_bot) {
315 return (s_bot + 1.0) * r_bot * std::pow(y, s_bot);
316}
317
318} // namespace shape
319
356
358 const double* A_Arch = nullptr;
359 const double* A_Baskethandle = nullptr;
360 const double* A_Circ = nullptr;
361 const double* A_Egg = nullptr;
362 const double* A_HorizEllipse = nullptr;
363 const double* A_Horseshoe = nullptr;
364 const double* A_VertEllipse = nullptr;
365 const double* R_Arch = nullptr;
366 const double* R_Baskethandle = nullptr;
367 const double* R_Circ = nullptr;
368 const double* R_Egg = nullptr;
369 const double* R_HorizEllipse = nullptr;
370 const double* R_Horseshoe = nullptr;
371 const double* R_VertEllipse = nullptr;
372 const double* S_BasketHandle = nullptr;
373 const double* S_Catenary = nullptr;
374 const double* S_Circ = nullptr;
375 const double* S_Egg = nullptr;
376 const double* S_Gothic = nullptr;
377 const double* S_Horseshoe = nullptr;
378 const double* S_SemiCirc = nullptr;
379 const double* S_SemiEllip = nullptr;
380 const double* W_Arch = nullptr;
381 const double* W_BasketHandle = nullptr;
382 const double* W_Catenary = nullptr;
383 const double* W_Circ = nullptr;
384 const double* W_Egg = nullptr;
385 const double* W_Gothic = nullptr;
386 const double* W_HorizEllipse = nullptr;
387 const double* W_Horseshoe = nullptr;
388 const double* W_SemiCirc = nullptr;
389 const double* W_SemiEllip = nullptr;
390 const double* W_VertEllipse = nullptr;
391 const double* Y_BasketHandle = nullptr;
392 const double* Y_Catenary = nullptr;
393 const double* Y_Circ = nullptr;
394 const double* Y_Egg = nullptr;
395 const double* Y_Gothic = nullptr;
396 const double* Y_Horseshoe = nullptr;
397 const double* Y_SemiCirc = nullptr;
398 const double* Y_SemiEllip = nullptr;
399 const double* Amax = nullptr;
401 const LocateLut* luts = nullptr;
402 int N_A_Arch = 0;
404 int N_A_Circ = 0;
405 int N_A_Egg = 0;
409 int N_R_Arch = 0;
411 int N_R_Circ = 0;
412 int N_R_Egg = 0;
418 int N_S_Circ = 0;
419 int N_S_Egg = 0;
420 int N_S_Gothic = 0;
424 int N_W_Arch = 0;
427 int N_W_Circ = 0;
428 int N_W_Egg = 0;
429 int N_W_Gothic = 0;
437 int N_Y_Circ = 0;
438 int N_Y_Egg = 0;
439 int N_Y_Gothic = 0;
443
454 return luts ? (luts + static_cast<int>(id)) : nullptr;
455 }
456};
457
465struct XsectEval {
467
468 // ============================================================================
469 // Root finders — faithful ports of legacy findroot.c (Numerical Recipes).
470 // ============================================================================
471
472 // Newton-Raphson + bisection. func(x, &f, &df). Root bracketed in [x1, x2].
473 template <class Func>
491 OPENSWMM_KERNEL_FN int findroot_Newton(double x1, double x2, double* rts, double xacc,
492 Func func) const {
493 constexpr int MAXIT = 60;
494 int n = 0;
495 double df, dx, dxold, f, x, temp, xhi, xlo;
496
497 x = *rts;
498 xlo = x1;
499 xhi = x2;
500 dxold = std::fabs(x2 - x1);
501 dx = dxold;
502 func(x, &f, &df);
503 n++;
504 for (int j = 1; j <= MAXIT; ++j) {
505 if (((x - xhi) * df - f) * ((x - xlo) * df - f) >= 0.0 ||
506 (std::fabs(2.0 * f) > std::fabs(dxold * df))) {
507 dxold = dx;
508 dx = 0.5 * (xhi - xlo);
509 x = xlo + dx;
510 if (xlo == x)
511 break;
512 } else {
513 dxold = dx;
514 dx = f / df;
515 temp = x;
516 x -= dx;
517 if (temp == x)
518 break;
519 }
520 if (std::fabs(dx) < xacc)
521 break;
522 func(x, &f, &df);
523 n++;
524 if (f < 0.0)
525 xlo = x;
526 else
527 xhi = x;
528 }
529 *rts = x;
530 return (n <= MAXIT) ? n : 0;
531 }
532
533 // Ridder's method. func(x) -> double. Root bracketed in [x1, x2].
534 template <class Func>
549 OPENSWMM_KERNEL_FN double findroot_Ridder(double x1, double x2, double xacc, Func func) const {
550 constexpr int MAXIT = 60;
551 auto SIGN = [](double a, double b) { return (b >= 0.0) ? std::fabs(a) : -std::fabs(a); };
552
553 double ans, fhi, flo, fm, fnew, s, xhi, xlo, xm, xnew;
554 flo = func(x1);
555 fhi = func(x2);
556 if (flo == 0.0)
557 return x1;
558 if (fhi == 0.0)
559 return x2;
560 ans = 0.5 * (x1 + x2);
561 if ((flo > 0.0 && fhi < 0.0) || (flo < 0.0 && fhi > 0.0)) {
562 xlo = x1;
563 xhi = x2;
564 for (int j = 1; j <= MAXIT; ++j) {
565 xm = 0.5 * (xlo + xhi);
566 fm = func(xm);
567 s = std::sqrt(fm * fm - flo * fhi);
568 if (s == 0.0)
569 return ans;
570 xnew = xm + (xm - xlo) * ((flo >= fhi ? 1.0 : -1.0) * fm / s);
571 if (std::fabs(xnew - ans) <= xacc)
572 break;
573 ans = xnew;
574 fnew = func(ans);
575 if (SIGN(fm, fnew) != fm) {
576 xlo = xm;
577 flo = fm;
578 xhi = ans;
579 fhi = fnew;
580 } else if (SIGN(flo, fnew) != flo) {
581 xhi = ans;
582 fhi = fnew;
583 } else if (SIGN(fhi, fnew) != fhi) {
584 xlo = ans;
585 flo = fnew;
586 } else
587 return ans;
588 if (std::fabs(xhi - xlo) <= xacc)
589 return ans;
590 }
591 return ans;
592 }
593 return -1.e20;
594 }
595
596 // ============================================================================
597 // Lookup helpers — identical to legacy lookup()/invLookup()/locate()
598 // ============================================================================
599
610 OPENSWMM_KERNEL_FN int locate(double y, const double* table, int jLast) const {
611 // Bisection: highest index j with table[j] <= y (legacy locate()).
612 return locate_bisect(y, table, jLast);
613 }
614
629 OPENSWMM_KERNEL_FN double lookup(double x, const double* table, int n_items) const {
630 // The tables are defined on the normalized domain x in [0, 1]. Guard the
631 // bounds BEFORE converting x to an int index: x can arrive non-finite when
632 // a caller normalizes by a zero full-depth (e.g. a conduit finalized
633 // before its cross-section geometry is set, so y/y_full == inf).
634 // static_cast<int> of inf/NaN is undefined behavior — on x86 (int)inf is
635 // INT_MIN, which slips past the upper-bound check below and indexes the
636 // table wildly out of bounds (segfault); ARM saturates to INT_MAX and
637 // happened to mask the bug. The legacy lookup() guarded x <= 0; restore
638 // that and add the matching upper bound so non-finite x is handled safely.
639 //
640 // On the finite domain (0, 1] this is bit-identical to legacy lookup():
641 // - x <= 0 / NaN -> table[0] (legacy lookup(0) also returns table[0]).
642 // - x > 1 / +inf -> table[n-1] (legacy takes the i >= n-1 early-out).
643 // The bound is strict (`> 1.0`, not `>= 1.0`): at x == 1.0 legacy does
644 // NOT early-out — 1.0/delta rounds just below n-1 — so it interpolates
645 // the last segment. Deferring x == 1.0 to lookup_exact reproduces that;
646 // no representable double lies in (1.0, n-1 * delta), so the strict
647 // guard is exact.
648 if (!(x > 0.0))
649 return table[0]; // x <= 0 or NaN
650 if (x > 1.0)
651 return table[n_items - 1]; // x > 1 or +inf
652 return lookup_exact(x, table, n_items);
653 }
654
670 OPENSWMM_KERNEL_FN double invLookup(double y, const double* table, int n_items,
671 const LocateLut* lut = nullptr) const {
672 double dx = 1.0 / static_cast<double>(n_items - 1);
673 int n = n_items;
674
675 // Truncate item count if last 2 entries are decreasing (section-factor tables)
676 if (table[n - 3] > table[n - 1])
677 n = n - 2;
678
679 int i;
680 if (n < n_items && y > table[n_items - 1]) {
681 if (y >= table[n_items - 3])
682 return static_cast<double>(n - 1) * dx;
683 if (y <= table[n_items - 2])
684 i = n_items - 2;
685 else
686 i = n_items - 3;
687 } else {
688 i = locate_maybe_lut(y, table, n - 1, lut);
689 }
690 if (i >= n - 1)
691 return static_cast<double>(n - 1) * dx;
692
693 double x0 = i * dx;
694 double dy = table[i + 1] - table[i];
695 double x;
696 if (dy == 0.0)
697 x = x0;
698 else
699 x = x0 + (y - table[i]) * dx / dy;
700 if (x < 0.0)
701 x = 0.0;
702 if (x > 1.0)
703 x = 1.0;
704 return x;
705 }
706
707 // ============================================================================
708 // Circular small-area special functions (legacy getYcircular/getScircular/...)
709 // ============================================================================
710
722 OPENSWMM_KERNEL_FN double getThetaOfAlpha(double alpha) const {
723 double theta, theta1, ap, d;
724 if (alpha > 0.04)
725 theta = 1.2 + 5.08 * (alpha - 0.04) / 0.96;
726 else
727 theta = 0.031715 - 12.79384 * alpha + 8.28479 * std::sqrt(alpha);
728 theta1 = theta;
729 ap = (2.0 * PI) * alpha;
730 for (int k = 1; k <= 40; ++k) {
731 d = -(ap - theta + std::sin(theta)) / (1.0 - std::cos(theta));
732 if (d > 1.0)
733 d = std::copysign(1.0, d);
734 theta = theta - d;
735 if (std::fabs(d) <= 0.0001)
736 return theta;
737 }
738 return theta1;
739 }
740
750 OPENSWMM_KERNEL_FN double getThetaOfPsi(double psi) const {
751 double theta, theta1, ap, tt, tt23, t3, d;
752 if (psi > 0.90)
753 theta = 4.17 + 1.12 * (psi - 0.90) / 0.176;
754 else if (psi > 0.5)
755 theta = 3.14 + 1.03 * (psi - 0.5) / 0.4;
756 else if (psi > 0.015)
757 theta = 1.2 + 1.94 * (psi - 0.015) / 0.485;
758 else
759 theta = 0.12103 - 55.5075 * psi + 15.62254 * std::sqrt(psi);
760 theta1 = theta;
761 ap = (2.0 * PI) * psi;
762 for (int k = 1; k <= 40; ++k) {
763 theta = std::fabs(theta);
764 tt = theta - std::sin(theta);
765 tt23 = std::pow(tt, 2.0 / 3.0);
766 t3 = std::pow(theta, 1.0 / 3.0);
767 d = ap * theta / t3 - tt * tt23;
768 d = d / (ap * (2.0 / 3.0) / t3 - (5.0 / 3.0) * tt23 * (1.0 - std::cos(theta)));
769 theta = theta - d;
770 if (std::fabs(d) <= 0.0001)
771 return theta;
772 }
773 return theta1;
774 }
775
786 OPENSWMM_KERNEL_FN double getYcircular(double alpha) const {
787 double theta;
788 if (alpha >= 1.0)
789 return 1.0;
790 if (alpha <= 0.0)
791 return 0.0;
792 if (alpha <= 1.0e-5) {
793 theta = std::pow(37.6911 * alpha, 1.0 / 3.0);
794 return theta * theta / 16.0;
795 }
796 theta = getThetaOfAlpha(alpha);
797 return (1.0 - std::cos(theta / 2.0)) / 2.0;
798 }
799
810 OPENSWMM_KERNEL_FN double getScircular(double alpha) const {
811 double theta;
812 if (alpha >= 1.0)
813 return 1.0;
814 if (alpha <= 0.0)
815 return 0.0;
816 if (alpha <= 1.0e-5) {
817 theta = std::pow(37.6911 * alpha, 1.0 / 3.0);
818 return std::pow(theta, 13.0 / 3.0) / 124.4797;
819 }
820 theta = getThetaOfAlpha(alpha);
821 return std::pow((theta - std::sin(theta)), 5.0 / 3.0) / (2.0 * PI) /
822 std::pow(theta, 2.0 / 3.0);
823 }
824
834 OPENSWMM_KERNEL_FN double getAcircular(double psi) const {
835 double theta;
836 if (psi >= 1.0)
837 return 1.0;
838 if (psi <= 0.0)
839 return 0.0;
840 if (psi <= 1.0e-6) {
841 theta = std::pow(124.4797 * psi, 3.0 / 13.0);
842 return theta * theta * theta / 37.6911;
843 }
844 theta = getThetaOfPsi(psi);
845 return (theta - std::sin(theta)) / (2.0 * PI);
846 }
847
848 // ============================================================================
849 // Generic / tabular section-factor derivatives (legacy)
850 // ============================================================================
851
863 OPENSWMM_KERNEL_FN double generic_getdSdA(const XSectParams& xs, double a) const {
864 double alpha = a / xs.a_full;
865 double alpha1 = alpha - 0.001;
866 double alpha2 = alpha + 0.001;
867 if (alpha1 < 0.0)
868 alpha1 = 0.0;
869 double a1 = alpha1 * xs.a_full;
870 double a2 = alpha2 * xs.a_full;
871 return (getSofA(xs, a2) - getSofA(xs, a1)) / (a2 - a1);
872 }
873
887 OPENSWMM_KERNEL_FN double tabular_getdSdA(const XSectParams& xs, double a, const double* table,
888 int n_items) const {
889 double alpha = a / xs.a_full;
890 double delta = 1.0 / static_cast<double>(n_items - 1);
891 int i = static_cast<int>(alpha / delta);
892 if (i >= n_items - 1)
893 i = n_items - 2;
894 double dSdA = (table[i + 1] - table[i]) / delta;
895 return dSdA * xs.s_full / xs.a_full;
896 }
897
898 // ============================================================================
899 // Circular
900 // ============================================================================
901
916 OPENSWMM_KERNEL_FN double circ_getAofY(const XSectParams& xs, double y) const {
917 double y_norm = y / xs.y_full;
918 return xs.a_full * lookup(y_norm, tbl.A_Circ, tbl.N_A_Circ);
919 }
920
934 OPENSWMM_KERNEL_FN double circ_getYofA(const XSectParams& xs, double a) const {
935 double alpha = a / xs.a_full;
936 if (alpha < 0.04)
937 return xs.y_full * getYcircular(alpha);
938 return xs.y_full * lookup(alpha, tbl.Y_Circ, tbl.N_Y_Circ);
939 }
940
954 OPENSWMM_KERNEL_FN double circ_getSofA(const XSectParams& xs, double a) const {
955 double alpha = a / xs.a_full;
956 if (alpha < 0.04)
957 return xs.s_full * getScircular(alpha);
958 return xs.s_full * lookup(alpha, tbl.S_Circ, tbl.N_S_Circ);
959 }
960
974 OPENSWMM_KERNEL_FN double circ_getAofS(const XSectParams& xs, double s) const {
975 double psi = s / xs.s_full;
976 if (psi == 0.0)
977 return 0.0;
978 if (psi >= 1.0)
979 return xs.a_full;
980 if (psi <= 0.015)
981 return xs.a_full * getAcircular(psi);
982 return xs.a_full * invLookup(psi, tbl.S_Circ, tbl.N_S_Circ, tbl.lut(LutId::S_Circ));
983 }
984
998 OPENSWMM_KERNEL_FN double circ_getdSdA(const XSectParams& xs, double a) const {
999 double alpha = a / xs.a_full;
1000 if (alpha <= 1.0e-30)
1001 return 1.0e-30;
1002 if (alpha < 0.04) {
1003 double theta = getThetaOfAlpha(alpha);
1004 double p = theta * xs.y_full / 2.0;
1005 double r = a / p;
1006 double dPdA = 4.0 / xs.y_full / (1.0 - std::cos(theta));
1007 return (5.0 / 3.0 - (2.0 / 3.0) * dPdA * r) * std::pow(r, 2.0 / 3.0);
1008 }
1009 return tabular_getdSdA(xs, a, tbl.S_Circ, tbl.N_S_Circ);
1010 }
1011
1012 // ============================================================================
1013 // Filled circular (yBot/aBot/sBot/rBot = filled bottom: depth/area/width/perim)
1014 // ============================================================================
1015
1030 OPENSWMM_KERNEL_FN double filled_circ_getAofY(const XSectParams& xs, double y) const {
1031 // Temporarily restore the unfilled full circle (legacy expands yFull/aFull).
1032 XSectParams t = xs;
1033 t.y_full += xs.y_bot;
1034 t.a_full += xs.a_bot;
1035 double a = circ_getAofY(t, y + xs.y_bot);
1036 return a - xs.a_bot;
1037 }
1038
1052 OPENSWMM_KERNEL_FN double filled_circ_getYofA(const XSectParams& xs, double a) const {
1053 XSectParams t = xs;
1054 t.y_full += xs.y_bot;
1055 t.a_full += xs.a_bot;
1056 double y = circ_getYofA(t, a + xs.a_bot);
1057 return y - xs.y_bot;
1058 }
1059
1074 OPENSWMM_KERNEL_FN double filled_circ_getRofY(const XSectParams& xs, double y) const {
1075 XSectParams t = xs;
1076 t.y_full += xs.y_bot;
1077 t.a_full += xs.a_bot;
1078 double yy = y + xs.y_bot;
1079 double a = circ_getAofY(t, yy);
1080 double r = 0.25 * t.y_full * lookup(yy / t.y_full, tbl.R_Circ, tbl.N_R_Circ);
1081 double p = (a / r);
1082 a = a - xs.a_bot;
1083 p = p - xs.r_bot + xs.s_bot; // rBot = filled perimeter, sBot = filled width
1084 return a / p;
1085 }
1086
1087 // ============================================================================
1088 // Rectangular closed / open
1089 // ============================================================================
1090
1104 OPENSWMM_KERNEL_FN double rect_closed_getRofA(const XSectParams& xs, double a) const {
1105 return shape::rectClosedRofA(a, xs.w_max, xs.a_full);
1106 }
1107
1121 OPENSWMM_KERNEL_FN double rect_closed_getSofA(const XSectParams& xs, double a) const {
1122 if (a / xs.a_full > RECT_ALFMAX)
1123 return xs.s_max +
1124 (xs.s_full - xs.s_max) * (a / xs.a_full - RECT_ALFMAX) / (1.0 - RECT_ALFMAX);
1125 return a * std::pow(rect_closed_getRofA(xs, a), 2.0 / 3.0);
1126 }
1127
1141 OPENSWMM_KERNEL_FN double rect_closed_getdSdA(const XSectParams& xs, double a) const {
1142 double alpha = a / xs.a_full;
1143 if (alpha > RECT_ALFMAX)
1144 return (xs.s_full - xs.s_max) / ((1.0 - RECT_ALFMAX) * xs.a_full);
1145 if (alpha <= 1.0e-30)
1146 return generic_getdSdA(xs, a);
1147 double r = rect_closed_getRofA(xs, a);
1148 return (5.0 / 3.0 - (2.0 / 3.0) * (2.0 / xs.w_max) * r) * std::pow(r, 2.0 / 3.0);
1149 }
1150
1164 OPENSWMM_KERNEL_FN double rect_open_getSofA(const XSectParams& xs, double a) const {
1165 double y = a / xs.w_max;
1166 double r = a / ((2.0 - xs.s_bot) * y + xs.w_max);
1167 return a * std::pow(r, 2.0 / 3.0);
1168 }
1169
1183 OPENSWMM_KERNEL_FN double rect_open_getdSdA(const XSectParams& xs, double a) const {
1184 if (a / xs.a_full <= 1.0e-30)
1185 return generic_getdSdA(xs, a);
1186 double r = getRofA(xs, a);
1187 double dPdA = (2.0 - xs.s_bot) / xs.w_max;
1188 return (5.0 / 3.0 - (2.0 / 3.0) * dPdA * r) * std::pow(r, 2.0 / 3.0);
1189 }
1190
1191 // ============================================================================
1192 // Rect-triangular (triangular bottom + rectangular top)
1193 // ============================================================================
1194
1209 OPENSWMM_KERNEL_FN double rect_triang_getAofY(const XSectParams& xs, double y) const {
1210 if (y <= xs.y_bot)
1211 return y * y * xs.s_bot;
1212 return xs.a_bot + (y - xs.y_bot) * xs.w_max;
1213 }
1214
1229 OPENSWMM_KERNEL_FN double rect_triang_getWofY(const XSectParams& xs, double y) const {
1230 if (y <= xs.y_bot)
1231 return 2.0 * xs.s_bot * y;
1232 return xs.w_max;
1233 }
1234
1248 OPENSWMM_KERNEL_FN double rect_triang_getYofA(const XSectParams& xs, double a) const {
1249 if (a <= xs.a_bot)
1250 return std::sqrt(a / xs.s_bot);
1251 return xs.y_bot + (a - xs.a_bot) / xs.w_max;
1252 }
1253
1267 OPENSWMM_KERNEL_FN double rect_triang_getRofA(const XSectParams& xs, double a) const {
1268 if (a <= 0.0)
1269 return 0.0;
1270 double y = rect_triang_getYofA(xs, a);
1271 if (y <= xs.y_bot)
1272 return a / (2.0 * y * xs.r_bot);
1273 double p = 2.0 * xs.y_bot * xs.r_bot + 2.0 * (y - xs.y_bot);
1274 double alf = (a / xs.a_full) - RECT_TRIANG_ALFMAX;
1275 if (alf > 0.0)
1276 p += alf / (1.0 - RECT_TRIANG_ALFMAX) * xs.w_max;
1277 return a / p;
1278 }
1279
1294 OPENSWMM_KERNEL_FN double rect_triang_getRofY(const XSectParams& xs, double y) const {
1295 if (y <= xs.y_bot)
1296 return y * xs.s_bot / (2.0 * xs.r_bot);
1297 double a = xs.a_bot + (y - xs.y_bot) * xs.w_max;
1298 double p = 2.0 * xs.y_bot * xs.r_bot + 2.0 * (y - xs.y_bot);
1299 double alf = (a / xs.a_full) - RECT_TRIANG_ALFMAX;
1300 if (alf > 0.0)
1301 p += alf / (1.0 - RECT_TRIANG_ALFMAX) * xs.w_max;
1302 return a / p;
1303 }
1304
1318 OPENSWMM_KERNEL_FN double rect_triang_getSofA(const XSectParams& xs, double a) const {
1319 double alfMax = RECT_TRIANG_ALFMAX;
1320 if (a / xs.a_full > alfMax)
1321 return xs.s_max + (xs.s_full - xs.s_max) * (a / xs.a_full - alfMax) / (1.0 - alfMax);
1322 return a * std::pow(rect_triang_getRofA(xs, a), 2.0 / 3.0);
1323 }
1324
1338 OPENSWMM_KERNEL_FN double rect_triang_getdSdA(const XSectParams& xs, double a) const {
1339 double alfMax = RECT_TRIANG_ALFMAX;
1340 double alpha = a / xs.a_full;
1341 if (alpha > alfMax)
1342 return (xs.s_full - xs.s_max) / ((1.0 - alfMax) * xs.a_full);
1343 if (alpha <= 1.0e-30)
1344 return generic_getdSdA(xs, a);
1345 double dPdA;
1346 if (a > xs.a_bot)
1347 dPdA = 2.0 / xs.w_max;
1348 else
1349 dPdA = xs.r_bot / std::sqrt(a * xs.s_bot);
1350 double r = rect_triang_getRofA(xs, a);
1351 return (5.0 / 3.0 - (2.0 / 3.0) * dPdA * r) * std::pow(r, 2.0 / 3.0);
1352 }
1353
1354 // ============================================================================
1355 // Rect-round (circular invert + rectangular top)
1356 // ============================================================================
1357
1371 OPENSWMM_KERNEL_FN double rect_round_getYofA(const XSectParams& xs, double a) const {
1372 if (a > xs.a_bot)
1373 return xs.y_bot + (a - xs.a_bot) / xs.w_max;
1374 double alpha = a / (PI * xs.r_bot * xs.r_bot);
1375 if (alpha < 0.04)
1376 return (2.0 * xs.r_bot) * getYcircular(alpha);
1377 return (2.0 * xs.r_bot) * lookup(alpha, tbl.Y_Circ, tbl.N_Y_Circ);
1378 }
1379
1394 OPENSWMM_KERNEL_FN double rect_round_getAofY(const XSectParams& xs, double y) const {
1395 if (y > xs.y_bot)
1396 return xs.a_bot + (y - xs.y_bot) * xs.w_max;
1397 double theta1 = 2.0 * std::acos(1.0 - y / xs.r_bot);
1398 return 0.5 * xs.r_bot * xs.r_bot * (theta1 - std::sin(theta1));
1399 }
1400
1415 OPENSWMM_KERNEL_FN double rect_round_getWofY(const XSectParams& xs, double y) const {
1416 if (y > xs.y_bot)
1417 return xs.w_max;
1418 return 2.0 * std::sqrt(y * (2.0 * xs.r_bot - y));
1419 }
1420
1434 OPENSWMM_KERNEL_FN double rect_round_getRofA(const XSectParams& xs, double a) const {
1435 if (a <= 0.0)
1436 return 0.0;
1437 if (a > xs.a_bot) {
1438 double y1 = (a - xs.a_bot) / xs.w_max;
1439 double theta1 = 2.0 * std::asin(xs.w_max / 2.0 / xs.r_bot);
1440 double p = xs.r_bot * theta1 + 2.0 * y1;
1441 double arg = (a / xs.a_full) - RECT_ROUND_ALFMAX;
1442 if (arg > 0.0)
1443 p += arg / (1.0 - RECT_ROUND_ALFMAX) * xs.w_max;
1444 return a / p;
1445 }
1446 double y1 = rect_round_getYofA(xs, a);
1447 double theta1 = 2.0 * std::acos(1.0 - y1 / xs.r_bot);
1448 double p = xs.r_bot * theta1;
1449 return a / p;
1450 }
1451
1466 OPENSWMM_KERNEL_FN double rect_round_getRofY(const XSectParams& xs, double y) const {
1467 if (y <= 0.0)
1468 return 0.0;
1469 if (y > xs.y_bot)
1470 return rect_round_getRofA(xs, rect_round_getAofY(xs, y));
1471 double theta1 = 2.0 * std::acos(1.0 - y / xs.r_bot);
1472 return 0.5 * xs.r_bot * (1.0 - std::sin(theta1)) / theta1;
1473 }
1474
1488 OPENSWMM_KERNEL_FN double rect_round_getSofA(const XSectParams& xs, double a) const {
1489 double alfMax = RECT_ROUND_ALFMAX;
1490 if (a / xs.a_full > alfMax)
1491 return xs.s_max + (xs.s_full - xs.s_max) * (a / xs.a_full - alfMax) / (1.0 - alfMax);
1492 if (a > xs.a_bot)
1493 return a * std::pow(rect_round_getRofA(xs, a), 2.0 / 3.0);
1494 double aFull = PI * xs.r_bot * xs.r_bot;
1495 double alpha = a / aFull;
1496 double sFull = xs.s_bot;
1497 if (alpha < 0.04)
1498 return sFull * getScircular(alpha);
1499 return sFull * lookup(alpha, tbl.S_Circ, tbl.N_S_Circ);
1500 }
1501
1515 OPENSWMM_KERNEL_FN double rect_round_getdSdA(const XSectParams& xs, double a) const {
1516 double alfMax = RECT_ROUND_ALFMAX;
1517 if (a / xs.a_full > alfMax)
1518 return (xs.s_full - xs.s_max) / ((1.0 - alfMax) * xs.a_full);
1519 if (a > xs.a_bot) {
1520 double r = rect_round_getRofA(xs, a);
1521 double dPdA = 2.0 / xs.w_max;
1522 return (5.0 / 3.0 - (2.0 / 3.0) * dPdA * r) * std::pow(r, 2.0 / 3.0);
1523 }
1524 return generic_getdSdA(xs, a);
1525 }
1526
1527 // ============================================================================
1528 // Modified baskethandle (rectangular bottom + circular-arc top).
1529 // Note: rBot/yBot/aBot/sBot refer to the CIRCULAR TOP portion.
1530 // ============================================================================
1531
1545 OPENSWMM_KERNEL_FN double mod_basket_getYofA(const XSectParams& xs, double a) const {
1546 if (a <= xs.a_full - xs.a_bot)
1547 return a / xs.w_max;
1548 double alpha = (xs.a_full - a) / (PI * xs.r_bot * xs.r_bot);
1549 double y1;
1550 if (alpha < 0.04)
1551 y1 = getYcircular(alpha);
1552 else
1553 y1 = lookup(alpha, tbl.Y_Circ, tbl.N_Y_Circ);
1554 y1 = 2.0 * xs.r_bot * y1;
1555 return xs.y_full - y1;
1556 }
1557
1572 OPENSWMM_KERNEL_FN double mod_basket_getAofY(const XSectParams& xs, double y) const {
1573 if (y <= xs.y_full - xs.y_bot)
1574 return y * xs.w_max;
1575 double y1 = xs.y_full - y;
1576 double theta1 = 2.0 * std::acos(1.0 - y1 / xs.r_bot);
1577 double a1 = 0.5 * xs.r_bot * xs.r_bot * (theta1 - std::sin(theta1));
1578 return xs.a_full - a1;
1579 }
1580
1595 OPENSWMM_KERNEL_FN double mod_basket_getWofY(const XSectParams& xs, double y) const {
1596 if (y <= 0.0)
1597 return 0.0;
1598 if (y <= xs.y_full - xs.y_bot)
1599 return xs.w_max;
1600 double y1 = xs.y_full - y;
1601 return 2.0 * std::sqrt(y1 * (2.0 * xs.r_bot - y1));
1602 }
1603
1617 OPENSWMM_KERNEL_FN double mod_basket_getRofA(const XSectParams& xs, double a) const {
1618 if (a <= xs.a_full - xs.a_bot)
1619 return a / (xs.w_max + 2.0 * a / xs.w_max);
1620 double y1 = xs.y_full - mod_basket_getYofA(xs, a);
1621 double theta1 = 2.0 * std::acos(1.0 - y1 / xs.r_bot);
1622 double p = (xs.s_bot - theta1) * xs.r_bot; // sBot = full circular opening angle
1623 y1 = xs.y_full - xs.y_bot;
1624 p = p + 2.0 * y1 + xs.w_max;
1625 return a / p;
1626 }
1627
1641 OPENSWMM_KERNEL_FN double mod_basket_getdSdA(const XSectParams& xs, double a) const {
1642 if (a <= xs.a_full - xs.a_bot && a / xs.a_full > 1.0e-30) {
1643 double r = a / (xs.w_max + 2.0 * a / xs.w_max);
1644 double dPdA = 2.0 / xs.w_max;
1645 return (5.0 / 3.0 - (2.0 / 3.0) * dPdA * r) * std::pow(r, 2.0 / 3.0);
1646 }
1647 return generic_getdSdA(xs, a);
1648 }
1649
1650 // ============================================================================
1651 // Trapezoidal (yBot = bottom width, sBot = avg side slope, rBot = side length/depth)
1652 // ============================================================================
1653
1668 OPENSWMM_KERNEL_FN double trapez_getAofY(const XSectParams& xs, double y) const {
1669 return shape::trapezAofY(y, xs.y_bot, xs.s_bot);
1670 }
1671
1686 OPENSWMM_KERNEL_FN double trapez_getWofY(const XSectParams& xs, double y) const {
1687 return shape::trapezWofY(y, xs.y_bot, xs.s_bot);
1688 }
1689
1703 OPENSWMM_KERNEL_FN double trapez_getYofA(const XSectParams& xs, double a) const {
1704 if (xs.s_bot == 0.0)
1705 return a / xs.y_bot;
1706 return (std::sqrt(xs.y_bot * xs.y_bot + 4.0 * xs.s_bot * a) - xs.y_bot) / (2.0 * xs.s_bot);
1707 }
1708
1722 OPENSWMM_KERNEL_FN double trapez_getRofA(const XSectParams& xs, double a) const {
1723 return a / (xs.y_bot + trapez_getYofA(xs, a) * xs.r_bot);
1724 }
1725
1740 OPENSWMM_KERNEL_FN double trapez_getRofY(const XSectParams& xs, double y) const {
1741 return shape::trapezRofY(y, xs.y_bot, xs.s_bot, xs.r_bot);
1742 }
1743
1757 OPENSWMM_KERNEL_FN double trapez_getdSdA(const XSectParams& xs, double a) const {
1758 if (a / xs.a_full <= 1.0e-30)
1759 return generic_getdSdA(xs, a);
1760 double r = trapez_getRofA(xs, a);
1761 double dPdA = xs.r_bot / std::sqrt(xs.y_bot * xs.y_bot + 4.0 * xs.s_bot * a);
1762 return (5.0 / 3.0 - (2.0 / 3.0) * dPdA * r) * std::pow(r, 2.0 / 3.0);
1763 }
1764
1765 // ============================================================================
1766 // Triangular
1767 // ============================================================================
1768
1783 OPENSWMM_KERNEL_FN double triang_getAofY(const XSectParams& xs, double y) const {
1784 return shape::triangAofY(y, xs.s_bot);
1785 }
1786
1801 OPENSWMM_KERNEL_FN double triang_getWofY(const XSectParams& xs, double y) const {
1802 return shape::triangWofY(y, xs.s_bot);
1803 }
1804
1818 OPENSWMM_KERNEL_FN double triang_getYofA(const XSectParams& xs, double a) const {
1819 return std::sqrt(a / xs.s_bot);
1820 }
1821
1835 OPENSWMM_KERNEL_FN double triang_getRofA(const XSectParams& xs, double a) const {
1836 return a / (2.0 * triang_getYofA(xs, a) * xs.r_bot);
1837 }
1838
1853 OPENSWMM_KERNEL_FN double triang_getRofY(const XSectParams& xs, double y) const {
1854 return shape::triangRofY(y, xs.s_bot, xs.r_bot);
1855 }
1856
1870 OPENSWMM_KERNEL_FN double triang_getdSdA(const XSectParams& xs, double a) const {
1871 if (a / xs.a_full <= 1.0e-30)
1872 return generic_getdSdA(xs, a);
1873 double r = triang_getRofA(xs, a);
1874 double dPdA = xs.r_bot / std::sqrt(a * xs.s_bot);
1875 return (5.0 / 3.0 - (2.0 / 3.0) * dPdA * r) * std::pow(r, 2.0 / 3.0);
1876 }
1877
1878 // ============================================================================
1879 // Parabolic (rBot = 1/sqrt(c) where y = c*x^2)
1880 // ============================================================================
1881
1896 OPENSWMM_KERNEL_FN double parab_getAofY(const XSectParams& xs, double y) const {
1897 return shape::parabAofY(y, xs.r_bot);
1898 }
1899
1914 OPENSWMM_KERNEL_FN double parab_getWofY(const XSectParams& xs, double y) const {
1915 return shape::parabWofY(y, xs.r_bot);
1916 }
1917
1931 OPENSWMM_KERNEL_FN double parab_getYofA(const XSectParams& xs, double a) const {
1932 return std::pow((3.0 / 4.0) * a / xs.r_bot, 2.0 / 3.0);
1933 }
1934
1935 // Analytical wetted perimeter (legacy parab_getPofY).
1950 OPENSWMM_KERNEL_FN double parab_getPofY(const XSectParams& xs, double y) const {
1951 double x = 2.0 * std::sqrt(y) / xs.r_bot;
1952 double t = std::sqrt(1.0 + x * x);
1953 return 0.5 * xs.r_bot * xs.r_bot * (x * t + std::log(x + t));
1954 }
1955
1970 OPENSWMM_KERNEL_FN double parab_getRofY(const XSectParams& xs, double y) const {
1971 if (y <= 0.0)
1972 return 0.0;
1973 return parab_getAofY(xs, y) / parab_getPofY(xs, y);
1974 }
1975
1989 OPENSWMM_KERNEL_FN double parab_getRofA(const XSectParams& xs, double a) const {
1990 if (a <= 0.0)
1991 return 0.0;
1992 return a / parab_getPofY(xs, parab_getYofA(xs, a));
1993 }
1994
1995 // ============================================================================
1996 // Power function (sBot = 1/exponent, rBot = coefficient)
1997 // ============================================================================
1998
2014 OPENSWMM_KERNEL_FN double powerfunc_getAofY(const XSectParams& xs, double y) const {
2015 return shape::powerfuncAofY(y, xs.s_bot, xs.r_bot);
2016 }
2017
2033 OPENSWMM_KERNEL_FN double powerfunc_getWofY(const XSectParams& xs, double y) const {
2034 return shape::powerfuncWofY(y, xs.s_bot, xs.r_bot);
2035 }
2036
2051 OPENSWMM_KERNEL_FN double powerfunc_getYofA(const XSectParams& xs, double a) const {
2052 return std::pow(a / xs.r_bot, 1.0 / (xs.s_bot + 1.0));
2053 }
2054
2055 // Numerical wetted perimeter (legacy powerfunc_getPofY — stepwise integration).
2071 OPENSWMM_KERNEL_FN double powerfunc_getPofY(const XSectParams& xs, double y) const {
2072 double dy1 = 0.02 * xs.y_full;
2073 double h = (xs.s_bot + 1.0) * xs.r_bot / 2.0;
2074 double m = xs.s_bot;
2075 double p = 0.0, y1 = 0.0, x1 = 0.0;
2076 double x2, y2, dx, dy;
2077 do {
2078 y2 = y1 + dy1;
2079 if (y2 > y)
2080 y2 = y;
2081 x2 = h * std::pow(y2, m);
2082 dx = x2 - x1;
2083 dy = y2 - y1;
2084 p += std::sqrt(dx * dx + dy * dy);
2085 x1 = x2;
2086 y1 = y2;
2087 } while (y2 < y);
2088 return 2.0 * p;
2089 }
2090
2106 OPENSWMM_KERNEL_FN double powerfunc_getRofY(const XSectParams& xs, double y) const {
2107 if (y <= 0.0)
2108 return 0.0;
2109 return powerfunc_getAofY(xs, y) / powerfunc_getPofY(xs, y);
2110 }
2111
2126 OPENSWMM_KERNEL_FN double powerfunc_getRofA(const XSectParams& xs, double a) const {
2127 if (a <= 0.0)
2128 return 0.0;
2129 return a / powerfunc_getPofY(xs, powerfunc_getYofA(xs, a));
2130 }
2131
2132 // ============================================================================
2133 // Main dispatch: getAofY
2134 // ============================================================================
2135
2150 OPENSWMM_KERNEL_FN double getAofY(const XSectParams& xs, double y) const {
2151 if (y <= 0.0)
2152 return 0.0;
2153 // A section with no (or not-yet-set) full depth has no area. Guard the
2154 // division so a degenerate cross-section — e.g. a conduit finalized before
2155 // its geometry is assigned — yields 0 instead of normalizing to inf/NaN.
2156 if (xs.y_full <= 0.0)
2157 return 0.0;
2158 double y_norm = y / xs.y_full;
2159
2160 switch (static_cast<XSectShape>(xs.type)) {
2163 return xs.a_full * lookup(y_norm, tbl.A_Circ, tbl.N_A_Circ);
2165 return filled_circ_getAofY(xs, y);
2167 return xs.a_full * lookup(y_norm, tbl.A_Egg, tbl.N_A_Egg);
2169 return xs.a_full * lookup(y_norm, tbl.A_Horseshoe, tbl.N_A_Horseshoe);
2170 case XSectShape::GOTHIC:
2171 return xs.a_full *
2172 invLookup(y_norm, tbl.Y_Gothic, tbl.N_Y_Gothic, tbl.lut(LutId::Y_Gothic));
2174 return xs.a_full *
2175 invLookup(y_norm, tbl.Y_Catenary, tbl.N_Y_Catenary, tbl.lut(LutId::Y_Catenary));
2177 return xs.a_full * invLookup(y_norm, tbl.Y_SemiEllip, tbl.N_Y_SemiEllip,
2178 tbl.lut(LutId::Y_SemiEllip));
2180 return xs.a_full * lookup(y_norm, tbl.A_Baskethandle, tbl.N_A_Baskethandle);
2182 return xs.a_full *
2183 invLookup(y_norm, tbl.Y_SemiCirc, tbl.N_Y_SemiCirc, tbl.lut(LutId::Y_SemiCirc));
2185 return xs.a_full * lookup(y_norm, tbl.A_HorizEllipse, tbl.N_A_HorizEllipse);
2187 return xs.a_full * lookup(y_norm, tbl.A_VertEllipse, tbl.N_A_VertEllipse);
2188 case XSectShape::ARCH:
2189 return xs.a_full * lookup(y_norm, tbl.A_Arch, tbl.N_A_Arch);
2192 return shape::rectAofY(y, xs.w_max);
2194 return rect_triang_getAofY(xs, y);
2196 return rect_round_getAofY(xs, y);
2198 return mod_basket_getAofY(xs, y);
2200 return trapez_getAofY(xs, y);
2202 return triang_getAofY(xs, y);
2204 return parab_getAofY(xs, y);
2206 return powerfunc_getAofY(xs, y);
2208 case XSectShape::CUSTOM:
2210 // Tabulated transect (legacy xsect_getAofY IRREGULAR case).
2211 if (xs.area_tbl)
2212 return xs.a_full * lookup(y_norm, xs.area_tbl, xs.transect_tbl_size);
2213 return 0.0;
2214 default:
2215 return 0.0;
2216 }
2217 }
2218
2219 // ============================================================================
2220 // Main dispatch: getWofY
2221 // ============================================================================
2222
2237 OPENSWMM_KERNEL_FN double getWofY(const XSectParams& xs, double y) const {
2238 double y_norm = y / xs.y_full;
2239
2240 switch (static_cast<XSectShape>(xs.type)) {
2243 return xs.w_max * lookup(y_norm, tbl.W_Circ, tbl.N_W_Circ);
2245 double yn = (y + xs.y_bot) / (xs.y_full + xs.y_bot);
2246 return xs.w_max * lookup(yn, tbl.W_Circ, tbl.N_W_Circ);
2247 }
2249 return xs.w_max * lookup(y_norm, tbl.W_Egg, tbl.N_W_Egg);
2251 return xs.w_max * lookup(y_norm, tbl.W_Horseshoe, tbl.N_W_Horseshoe);
2252 case XSectShape::GOTHIC:
2253 return xs.w_max * lookup(y_norm, tbl.W_Gothic, tbl.N_W_Gothic);
2255 return xs.w_max * lookup(y_norm, tbl.W_Catenary, tbl.N_W_Catenary);
2257 return xs.w_max * lookup(y_norm, tbl.W_SemiEllip, tbl.N_W_SemiEllip);
2259 return xs.w_max * lookup(y_norm, tbl.W_BasketHandle, tbl.N_W_BasketHandle);
2261 return xs.w_max * lookup(y_norm, tbl.W_SemiCirc, tbl.N_W_SemiCirc);
2263 return xs.w_max * lookup(y_norm, tbl.W_HorizEllipse, tbl.N_W_HorizEllipse);
2265 return xs.w_max * lookup(y_norm, tbl.W_VertEllipse, tbl.N_W_VertEllipse);
2266 case XSectShape::ARCH:
2267 return xs.w_max * lookup(y_norm, tbl.W_Arch, tbl.N_W_Arch);
2269 return shape::rectClosedWofY(y_norm, xs.w_max);
2271 return xs.w_max;
2273 return rect_triang_getWofY(xs, y);
2275 return rect_round_getWofY(xs, y);
2277 return mod_basket_getWofY(xs, y);
2279 return trapez_getWofY(xs, y);
2281 return triang_getWofY(xs, y);
2283 return parab_getWofY(xs, y);
2285 return powerfunc_getWofY(xs, y);
2287 case XSectShape::CUSTOM:
2289 // Tabulated transect (legacy xsect_getWofY IRREGULAR case).
2290 if (xs.width_tbl)
2291 return xs.w_max * lookup(y_norm, xs.width_tbl, xs.transect_tbl_size);
2292 return 0.0;
2293 default:
2294 return 0.0;
2295 }
2296 }
2297
2298 // ============================================================================
2299 // Main dispatch: getRofY
2300 // ============================================================================
2301
2316 OPENSWMM_KERNEL_FN double getRofY(const XSectParams& xs, double y) const {
2317 double y_norm = y / xs.y_full;
2318
2319 switch (static_cast<XSectShape>(xs.type)) {
2322 return xs.r_full * lookup(y_norm, tbl.R_Circ, tbl.N_R_Circ);
2324 if (xs.y_bot == 0.0)
2325 return xs.r_full * lookup(y_norm, tbl.R_Circ, tbl.N_R_Circ);
2326 return filled_circ_getRofY(xs, y);
2328 return xs.r_full * lookup(y_norm, tbl.R_Egg, tbl.N_R_Egg);
2330 return xs.r_full * lookup(y_norm, tbl.R_Horseshoe, tbl.N_R_Horseshoe);
2332 return xs.r_full * lookup(y_norm, tbl.R_Baskethandle, tbl.N_R_Baskethandle);
2334 return xs.r_full * lookup(y_norm, tbl.R_HorizEllipse, tbl.N_R_HorizEllipse);
2336 return xs.r_full * lookup(y_norm, tbl.R_VertEllipse, tbl.N_R_VertEllipse);
2337 case XSectShape::ARCH:
2338 return xs.r_full * lookup(y_norm, tbl.R_Arch, tbl.N_R_Arch);
2340 return rect_triang_getRofY(xs, y);
2342 return rect_round_getRofY(xs, y);
2344 return trapez_getRofY(xs, y);
2346 return triang_getRofY(xs, y);
2348 return parab_getRofY(xs, y);
2350 return powerfunc_getRofY(xs, y);
2352 case XSectShape::CUSTOM:
2354 // Tabulated transect (legacy xsect_getRofY IRREGULAR case). Must be
2355 // explicit: the generic default below would call getRofA→getRofY
2356 // and recurse forever for these shapes.
2357 if (xs.hrad_tbl)
2358 return xs.r_full * lookup(y_norm, xs.hrad_tbl, xs.transect_tbl_size);
2359 return 0.0;
2360 default: // RECT_CLOSED, RECT_OPEN, MOD_BASKET, tabulated S-only shapes
2361 return getRofA(xs, getAofY(xs, y));
2362 }
2363 }
2364
2365 // ============================================================================
2366 // Main dispatch: getYofA
2367 // ============================================================================
2368
2382 OPENSWMM_KERNEL_FN double getYofA(const XSectParams& xs, double a) const {
2383 if (a <= 0.0)
2384 return 0.0;
2385 double alpha = a / xs.a_full;
2386
2387 switch (static_cast<XSectShape>(xs.type)) {
2390 return circ_getYofA(xs, a);
2392 return filled_circ_getYofA(xs, a);
2394 return xs.y_full * lookup(alpha, tbl.Y_Egg, tbl.N_Y_Egg);
2396 return xs.y_full * lookup(alpha, tbl.Y_Horseshoe, tbl.N_Y_Horseshoe);
2397 case XSectShape::GOTHIC:
2398 return xs.y_full * lookup(alpha, tbl.Y_Gothic, tbl.N_Y_Gothic);
2400 return xs.y_full * lookup(alpha, tbl.Y_Catenary, tbl.N_Y_Catenary);
2402 return xs.y_full * lookup(alpha, tbl.Y_SemiEllip, tbl.N_Y_SemiEllip);
2404 return xs.y_full * lookup(alpha, tbl.Y_BasketHandle, tbl.N_Y_BasketHandle);
2406 return xs.y_full * lookup(alpha, tbl.Y_SemiCirc, tbl.N_Y_SemiCirc);
2408 return xs.y_full * invLookup(alpha, tbl.A_HorizEllipse, tbl.N_A_HorizEllipse,
2411 return xs.y_full * invLookup(alpha, tbl.A_VertEllipse, tbl.N_A_VertEllipse,
2413 case XSectShape::ARCH:
2414 return xs.y_full * invLookup(alpha, tbl.A_Arch, tbl.N_A_Arch, tbl.lut(LutId::A_Arch));
2417 return a / xs.w_max;
2419 return rect_triang_getYofA(xs, a);
2421 return rect_round_getYofA(xs, a);
2423 return mod_basket_getYofA(xs, a);
2425 return trapez_getYofA(xs, a);
2427 return triang_getYofA(xs, a);
2429 return parab_getYofA(xs, a);
2431 return powerfunc_getYofA(xs, a);
2433 case XSectShape::CUSTOM:
2435 // Invert the normalized area table (legacy xsect_getYofA IRREGULAR).
2436 if (xs.area_tbl)
2437 return xs.y_full * invLookup(alpha, xs.area_tbl, xs.transect_tbl_size, xs.area_lut);
2438 return 0.0;
2439 default:
2440 return 0.0;
2441 }
2442 }
2443
2444 // ============================================================================
2445 // Main dispatch: getSofA
2446 // ============================================================================
2447
2461 OPENSWMM_KERNEL_FN double getSofA(const XSectParams& xs, double a) const {
2462 double alpha = a / xs.a_full;
2463
2464 switch (static_cast<XSectShape>(xs.type)) {
2467 return circ_getSofA(xs, a);
2469 return xs.s_full * lookup(alpha, tbl.S_Egg, tbl.N_S_Egg);
2471 return xs.s_full * lookup(alpha, tbl.S_Horseshoe, tbl.N_S_Horseshoe);
2472 case XSectShape::GOTHIC:
2473 return xs.s_full * lookup(alpha, tbl.S_Gothic, tbl.N_S_Gothic);
2475 return xs.s_full * lookup(alpha, tbl.S_Catenary, tbl.N_S_Catenary);
2477 return xs.s_full * lookup(alpha, tbl.S_SemiEllip, tbl.N_S_SemiEllip);
2479 return xs.s_full * lookup(alpha, tbl.S_BasketHandle, tbl.N_S_BasketHandle);
2481 return xs.s_full * lookup(alpha, tbl.S_SemiCirc, tbl.N_S_SemiCirc);
2483 return rect_closed_getSofA(xs, a);
2485 return rect_open_getSofA(xs, a);
2487 return rect_triang_getSofA(xs, a);
2489 return rect_round_getSofA(xs, a);
2490 default: {
2491 if (a == 0.0)
2492 return 0.0;
2493 double r = getRofA(xs, a);
2494 if (r < TINY)
2495 return 0.0;
2496 return a * std::pow(r, 2.0 / 3.0);
2497 }
2498 }
2499 }
2500
2501 // ============================================================================
2502 // getRofA — hydraulic radius from area
2503 // ============================================================================
2504
2518 OPENSWMM_KERNEL_FN double getRofA(const XSectParams& xs, double a) const {
2519 if (a <= 0.0)
2520 return 0.0;
2521 switch (static_cast<XSectShape>(xs.type)) {
2524 case XSectShape::ARCH:
2527 case XSectShape::CUSTOM:
2529 return getRofY(xs, getYofA(xs, a));
2531 return rect_closed_getRofA(xs, a);
2533 return shape::rectOpenRofA(a, xs.w_max, xs.s_bot);
2535 return rect_triang_getRofA(xs, a);
2537 return rect_round_getRofA(xs, a);
2539 return mod_basket_getRofA(xs, a);
2541 return trapez_getRofA(xs, a);
2543 return triang_getRofA(xs, a);
2545 return parab_getRofA(xs, a);
2547 return powerfunc_getRofA(xs, a);
2548 default: {
2549 double s = getSofA(xs, a);
2550 if (s < TINY || a < TINY)
2551 return 0.0;
2552 return std::pow(s / a, 3.0 / 2.0);
2553 }
2554 }
2555 }
2556
2557 // ============================================================================
2558 // getdSdA — derivative of section factor w.r.t. area
2559 // ============================================================================
2560
2574 OPENSWMM_KERNEL_FN double getdSdA(const XSectParams& xs, double a) const {
2575 switch (static_cast<XSectShape>(xs.type)) {
2578 return circ_getdSdA(xs, a);
2580 return tabular_getdSdA(xs, a, tbl.S_Egg, tbl.N_S_Egg);
2582 return tabular_getdSdA(xs, a, tbl.S_Horseshoe, tbl.N_S_Horseshoe);
2583 case XSectShape::GOTHIC:
2584 return tabular_getdSdA(xs, a, tbl.S_Gothic, tbl.N_S_Gothic);
2586 return tabular_getdSdA(xs, a, tbl.S_Catenary, tbl.N_S_Catenary);
2588 return tabular_getdSdA(xs, a, tbl.S_SemiEllip, tbl.N_S_SemiEllip);
2590 return tabular_getdSdA(xs, a, tbl.S_BasketHandle, tbl.N_S_BasketHandle);
2592 return tabular_getdSdA(xs, a, tbl.S_SemiCirc, tbl.N_S_SemiCirc);
2594 return rect_closed_getdSdA(xs, a);
2596 return rect_open_getdSdA(xs, a);
2598 return rect_triang_getdSdA(xs, a);
2600 return rect_round_getdSdA(xs, a);
2602 return mod_basket_getdSdA(xs, a);
2604 return trapez_getdSdA(xs, a);
2606 return triang_getdSdA(xs, a);
2607 default:
2608 return generic_getdSdA(xs, a);
2609 }
2610 }
2611
2612 // ============================================================================
2613 // getAofS — area from section factor
2614 // ============================================================================
2615
2629 OPENSWMM_KERNEL_FN double getAofS(const XSectParams& xs, double s) const {
2630 double psi = s / xs.s_full;
2631 if (s <= 0.0)
2632 return 0.0;
2633 if (s > xs.s_max)
2634 s = xs.s_max;
2635
2636 switch (static_cast<XSectShape>(xs.type)) {
2637 case XSectShape::DUMMY:
2638 return 0.0;
2641 return circ_getAofS(xs, s);
2643 return xs.a_full * invLookup(psi, tbl.S_Egg, tbl.N_S_Egg, tbl.lut(LutId::S_Egg));
2645 return xs.a_full *
2646 invLookup(psi, tbl.S_Horseshoe, tbl.N_S_Horseshoe, tbl.lut(LutId::S_Horseshoe));
2647 case XSectShape::GOTHIC:
2648 return xs.a_full *
2649 invLookup(psi, tbl.S_Gothic, tbl.N_S_Gothic, tbl.lut(LutId::S_Gothic));
2651 return xs.a_full *
2652 invLookup(psi, tbl.S_Catenary, tbl.N_S_Catenary, tbl.lut(LutId::S_Catenary));
2654 return xs.a_full *
2655 invLookup(psi, tbl.S_SemiEllip, tbl.N_S_SemiEllip, tbl.lut(LutId::S_SemiEllip));
2657 return xs.a_full * invLookup(psi, tbl.S_BasketHandle, tbl.N_S_BasketHandle,
2660 return xs.a_full *
2661 invLookup(psi, tbl.S_SemiCirc, tbl.N_S_SemiCirc, tbl.lut(LutId::S_SemiCirc));
2662 default: {
2663 // Newton-Raphson on S(a) = s, bracketed in [a1, a2] (legacy generic_getAofS).
2664 // a2 = absolute area at max flow = xsect_getAmax.
2665 // PARITY: legacy xsect_getAmax (xsect.c:711-713) returns aBot for
2666 // BOTH IRREGULAR and CUSTOM (the physical area at the max section
2667 // factor, set from the transect/shape tables); every other shape
2668 // uses aFull * Amax-ratio.
2669 double a1, a2;
2670 const XSectShape sh = static_cast<XSectShape>(xs.type);
2671 double a_max = (sh == XSectShape::CUSTOM || sh == XSectShape::IRREGULAR)
2672 ? xs.a_bot
2673 : xs.a_full * getAmax(xs);
2674 if ((s <= xs.s_max && s >= xs.s_full) && xs.s_max != xs.s_full) {
2675 a1 = xs.a_full; // sFull < sMax: root lies between aFull and aMax
2676 a2 = a_max;
2677 } else {
2678 a1 = 0.0;
2679 a2 = a_max;
2680 }
2681 double a = 0.5 * (a1 + a2);
2682 double tol = 0.0001 * xs.a_full;
2683 findroot_Newton(a1, a2, &a, tol, [&](double aa, double* f, double* df) {
2684 *f = getSofA(xs, aa) - s;
2685 *df = getdSdA(xs, aa);
2686 });
2687 return a;
2688 }
2689 }
2690 }
2691
2692 // ============================================================================
2693 // getAmax — ratio of area at max flow to full area
2694 // ============================================================================
2695
2706 OPENSWMM_KERNEL_FN double getAmax(const XSectParams& xs) const {
2707 if (xs.type >= 0 && xs.type <= 25)
2708 return tbl.Amax[xs.type];
2709 return 1.0;
2710 }
2711
2712 // ============================================================================
2713 // getYcrit — critical depth for a given flow rate (legacy xsect_getYcrit)
2714 // ============================================================================
2715
2730 OPENSWMM_KERNEL_FN double getYcrit(const XSectParams& xs, double q) const {
2731 if (q <= 0.0)
2732 return 0.0;
2733 double q2g = q * q / GRAVITY;
2734 if (q2g == 0.0)
2735 return 0.0;
2736
2737 double y;
2738 switch (static_cast<XSectShape>(xs.type)) {
2739 case XSectShape::DUMMY:
2740 return 0.0;
2743 y = std::pow(q2g / (xs.w_max * xs.w_max), 1.0 / 3.0);
2744 break;
2746 y = std::pow(2.0 * q2g / (xs.s_bot * xs.s_bot), 1.0 / 5.0);
2747 break;
2749 y = std::pow(27.0 / 32.0 * q2g / (xs.r_bot * xs.r_bot), 1.0 / 4.0);
2750 break;
2752 y = 1.0 / (2.0 * xs.s_bot + 3.0);
2753 y = std::pow(q2g * (xs.s_bot + 1.0) / (xs.r_bot * xs.r_bot), y);
2754 break;
2755 default: {
2756 // Critical flow function Q_c(yc) - qTarget (legacy getQcritical).
2757 auto qCritical = [&](double yc, double qTarget) -> double {
2758 double a = getAofY(xs, yc);
2759 double w = getWofY(xs, yc);
2760 if (w > 0.0)
2761 return a * std::sqrt(GRAVITY * a / w) - qTarget;
2762 return -qTarget;
2763 };
2764
2765 // Initial estimate from equivalent circular conduit.
2766 double y0 = 1.01 * std::pow(q2g / xs.y_full, 0.25);
2767 if (y0 >= xs.y_full)
2768 y0 = 0.97 * xs.y_full;
2769
2770 // Ratio of conduit area to equivalent circular area.
2771 double r = xs.a_full / (PI / 4.0 * xs.y_full * xs.y_full);
2772
2773 if (r >= 0.5 && r <= 2.0) {
2774 // --- interval enumeration (legacy getYcritEnum), 25 increments
2775 constexpr int N_INC = 25;
2776 double dy = xs.y_full / N_INC;
2777 int i1 = static_cast<int>(y0 / dy);
2778 double q0 = qCritical(i1 * dy, 0.0);
2779 if (q0 < q) {
2780 y = xs.y_full;
2781 for (int i = i1 + 1; i <= N_INC; ++i) {
2782 double qc = qCritical(i * dy, 0.0);
2783 if (qc >= q) {
2784 y = ((q - q0) / (qc - q0) + static_cast<double>(i - 1)) * dy;
2785 break;
2786 }
2787 q0 = qc;
2788 }
2789 } else {
2790 y = 0.0;
2791 for (int i = i1 - 1; i >= 0; --i) {
2792 double qc = qCritical(i * dy, 0.0);
2793 if (qc < q) {
2794 y = ((q - qc) / (q0 - qc) + static_cast<double>(i)) * dy;
2795 break;
2796 }
2797 q0 = qc;
2798 }
2799 }
2800 } else {
2801 // --- Ridder's method (legacy getYcritRidder)
2802 double y1 = 0.0;
2803 double y2 = 0.99 * xs.y_full;
2804 double q2 = qCritical(y2, 0.0);
2805 if (q2 < q) {
2806 y = xs.y_full;
2807 break;
2808 }
2809 double q0 = qCritical(y0, 0.0);
2810 double q1 = qCritical(0.5 * xs.y_full, 0.0);
2811 if (q0 > q) {
2812 y2 = y0;
2813 if (q1 < q)
2814 y1 = 0.5 * xs.y_full;
2815 } else {
2816 y1 = y0;
2817 if (q1 > q)
2818 y2 = 0.5 * xs.y_full;
2819 }
2820 y = findroot_Ridder(y1, y2, 0.001, [&](double yc) { return qCritical(yc, q); });
2821 }
2822 break;
2823 }
2824 }
2825 return std::min(y, xs.y_full);
2826 }
2827
2828 // ============================================================================
2829 // isOpen — returns true for open shapes
2830 // ============================================================================
2831
2842 OPENSWMM_KERNEL_FN bool isOpen(int type) const {
2843 // PARITY xsect.c:215 xsect_isOpen() = (Amax[type] >= 1.0): the shapes
2844 // whose Amax entry is 1.0 (xsect.c:59-85) are DUMMY, RECT_OPEN,
2845 // TRAPEZOIDAL, TRIANGULAR, PARABOLIC, POWERFUNC, IRREGULAR and STREET.
2846 switch (static_cast<XSectShape>(type)) {
2847 case XSectShape::DUMMY:
2855 return true;
2856 default:
2857 return false;
2858 }
2859 }
2860};
2861
2866const XsectTables& hostTables();
2867const XsectEval& hostEval();
2868
2869} // namespace openswmm::xsect
2870
2871#endif // OPENSWMM_XSECT_KERNELS_HPP
#define OPENSWMM_KERNEL_FN
Definition ExplicitKokkosSurfaceSolver.cpp:22
Bit-exact geometry-table interpolation — the single source of truth.
#define SIGN(a, b)
Definition findroot.c:26
#define MAXIT
Definition findroot.c:27
The analytic shape formulas — the single definition of each.
Definition XSectKernels.hpp:111
OPENSWMM_KERNEL_FN double rectClosedWofY(double y_norm, double w_max)
Apply the closed rectangular crown top-width convention.
Definition XSectKernels.hpp:262
OPENSWMM_KERNEL_FN double trapezRofY(double y, double y_bot, double s_bot, double r_bot)
Compute trapezoid hydraulic radius from depth.
Definition XSectKernels.hpp:232
OPENSWMM_KERNEL_FN double trapezAofY(double y, double y_bot, double s_bot)
Compute trapezoid area as (bottom width+side slope*depth)*depth.
Definition XSectKernels.hpp:140
OPENSWMM_KERNEL_FN double rectAofY(double y, double w_max)
Compute rectangular area as depth times width.
Definition XSectKernels.hpp:125
OPENSWMM_KERNEL_FN double trapezWofY(double y, double y_bot, double s_bot)
Compute trapezoid top width from depth.
Definition XSectKernels.hpp:277
OPENSWMM_KERNEL_FN double triangWofY(double y, double s_bot)
Compute triangular top width from depth.
Definition XSectKernels.hpp:289
OPENSWMM_KERNEL_FN double parabAofY(double y, double r_bot)
Compute parabolic area using its square-root depth relation.
Definition XSectKernels.hpp:164
OPENSWMM_KERNEL_FN double powerfuncWofY(double y, double s_bot, double r_bot)
Compute power-function top width as the area-depth derivative.
Definition XSectKernels.hpp:314
OPENSWMM_KERNEL_FN double powerfuncAofY(double y, double s_bot, double r_bot)
Compute power-function section area.
Definition XSectKernels.hpp:179
OPENSWMM_KERNEL_FN double rectClosedRofA(double a, double w_max, double a_full)
Compute closed rectangular hydraulic radius with near-crown perimeter correction.
Definition XSectKernels.hpp:195
OPENSWMM_KERNEL_FN double parabWofY(double y, double r_bot)
Compute parabolic top width from depth.
Definition XSectKernels.hpp:301
OPENSWMM_KERNEL_FN double rectOpenRofA(double a, double w_max, double s_bot)
Compute open rectangular radius excluding selected side banks.
Definition XSectKernels.hpp:214
OPENSWMM_KERNEL_FN double triangAofY(double y, double s_bot)
Compute triangular area from depth and side slope.
Definition XSectKernels.hpp:152
OPENSWMM_KERNEL_FN double triangRofY(double y, double s_bot, double r_bot)
Compute triangular hydraulic radius from its side geometry.
Definition XSectKernels.hpp:247
Definition XSectBatch.hpp:158
const XsectEval & hostEval()
Borrow the immutable host evaluator bound to process-lifetime tables.
Definition XSection.cpp:229
const XsectTables & hostTables()
Borrow host pointers, lengths and inverse maps for every built-in geometry table.
Definition XSection.cpp:129
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
Interpolate a normalized geometry table with the legacy arithmetic grouping.
Definition XSectLookup.hpp:76
int locate_maybe_lut(double y, const double *table, int jLast, const LocateLut *L) noexcept
Use a matching enabled bucket map or fall back to plain bisection.
Definition XSectLookup.hpp:378
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:338
@ S_Egg
Definition XSectKernels.hpp:347
@ A_Arch
Definition XSectKernels.hpp:345
@ Y_Gothic
Definition XSectKernels.hpp:339
@ A_VertEllipse
Definition XSectKernels.hpp:344
@ COUNT
Definition XSectKernels.hpp:354
@ Y_SemiCirc
Definition XSectKernels.hpp:342
@ A_HorizEllipse
Definition XSectKernels.hpp:343
@ S_SemiEllip
Definition XSectKernels.hpp:351
@ S_Catenary
Definition XSectKernels.hpp:350
@ Y_SemiEllip
Definition XSectKernels.hpp:341
@ S_Gothic
Definition XSectKernels.hpp:349
@ S_Circ
Definition XSectKernels.hpp:346
@ S_BasketHandle
Definition XSectKernels.hpp:352
@ S_Horseshoe
Definition XSectKernels.hpp:348
@ S_SemiCirc
Definition XSectKernels.hpp:353
@ Y_Catenary
Definition XSectKernels.hpp:340
int locate_bisect(double y, const double *table, int jLast) noexcept
Find a lower bracketing index in a nondecreasing table.
Definition XSectLookup.hpp:274
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:37
Native cross-section geometry and borrowed optional normalized transect tables.
Definition XSectBatch.hpp:119
double s_full
Section factor when full (normally ft^(8/3); FORCE_MAIN uses R^0.63)
Definition XSectBatch.hpp:129
double s_bot
Slope of bottom section / exponent.
Definition XSectBatch.hpp:134
const double * hrad_tbl
Normalized hyd-radius vs y/y_full.
Definition XSectBatch.hpp:144
int transect_tbl_size
Entry count of the tables above.
Definition XSectBatch.hpp:146
const double * width_tbl
Normalized width vs y/y_full.
Definition XSectBatch.hpp:145
double a_full
Area when full (ft2)
Definition XSectBatch.hpp:127
double r_bot
Radius of bottom section / coefficient.
Definition XSectBatch.hpp:135
double s_max
Section factor at max flow (same convention as s_full)
Definition XSectBatch.hpp:130
int type
Definition XSectBatch.hpp:120
double a_bot
Area of bottom section.
Definition XSectBatch.hpp:133
const xsect::LocateLut * area_lut
Definition XSectBatch.hpp:151
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
double r_full
Hydraulic radius when full (ft)
Definition XSectBatch.hpp:128
const double * area_tbl
Normalized area vs y/y_full.
Definition XSectBatch.hpp:143
Definition XSectLookup.hpp:232
The geometry layer itself, parameterized on where its tables live.
Definition XSectKernels.hpp:465
OPENSWMM_KERNEL_FN double getRofY(const XSectParams &xs, double y) const
Dispatch native hydraulic radius from depth.
Definition XSectKernels.hpp:2316
OPENSWMM_KERNEL_FN double filled_circ_getAofY(const XSectParams &xs, double y) const
Compute sediment-filled circular wetted area from depth.
Definition XSectKernels.hpp:1030
OPENSWMM_KERNEL_FN double rect_triang_getYofA(const XSectParams &xs, double a) const
Compute triangular-invert rectangular water depth from wetted area.
Definition XSectKernels.hpp:1248
OPENSWMM_KERNEL_FN double rect_open_getdSdA(const XSectParams &xs, double a) const
Compute open rectangular section-factor derivative with respect to area.
Definition XSectKernels.hpp:1183
OPENSWMM_KERNEL_FN double rect_triang_getRofA(const XSectParams &xs, double a) const
Compute triangular-invert rectangular hydraulic radius from area.
Definition XSectKernels.hpp:1267
OPENSWMM_KERNEL_FN double tabular_getdSdA(const XSectParams &xs, double a, const double *table, int n_items) const
Differentiate the local normalized section-factor table segment.
Definition XSectKernels.hpp:887
OPENSWMM_KERNEL_FN double parab_getRofA(const XSectParams &xs, double a) const
Compute parabolic hydraulic radius from area.
Definition XSectKernels.hpp:1989
OPENSWMM_KERNEL_FN double rect_open_getSofA(const XSectParams &xs, double a) const
Compute open rectangular section factor from area.
Definition XSectKernels.hpp:1164
OPENSWMM_KERNEL_FN double filled_circ_getRofY(const XSectParams &xs, double y) const
Compute sediment-filled circular hydraulic radius from depth.
Definition XSectKernels.hpp:1074
OPENSWMM_KERNEL_FN double rect_round_getYofA(const XSectParams &xs, double a) const
Compute circular-invert rectangular water depth from wetted area.
Definition XSectKernels.hpp:1371
OPENSWMM_KERNEL_FN double mod_basket_getdSdA(const XSectParams &xs, double a) const
Compute modified baskethandle section-factor derivative with respect to area.
Definition XSectKernels.hpp:1641
OPENSWMM_KERNEL_FN bool isOpen(int type) const
Classify kernel section types using the legacy open-shape set.
Definition XSectKernels.hpp:2842
OPENSWMM_KERNEL_FN double rect_round_getWofY(const XSectParams &xs, double y) const
Compute circular-invert rectangular top width from depth.
Definition XSectKernels.hpp:1415
OPENSWMM_KERNEL_FN double mod_basket_getWofY(const XSectParams &xs, double y) const
Compute modified baskethandle top width from depth.
Definition XSectKernels.hpp:1595
OPENSWMM_KERNEL_FN double rect_round_getRofY(const XSectParams &xs, double y) const
Compute circular-invert rectangular hydraulic radius from depth.
Definition XSectKernels.hpp:1466
OPENSWMM_KERNEL_FN double generic_getdSdA(const XSectParams &xs, double a) const
Estimate section-factor derivative with a centered normalized-area difference.
Definition XSectKernels.hpp:863
OPENSWMM_KERNEL_FN double rect_triang_getRofY(const XSectParams &xs, double y) const
Compute triangular-invert rectangular hydraulic radius from depth.
Definition XSectKernels.hpp:1294
OPENSWMM_KERNEL_FN double powerfunc_getWofY(const XSectParams &xs, double y) const
Compute power-function top width from depth.
Definition XSectKernels.hpp:2033
OPENSWMM_KERNEL_FN double lookup(double x, const double *table, int n_items) const
Interpolate normalized geometry with guarded endpoints and exact legacy arithmetic.
Definition XSectKernels.hpp:629
OPENSWMM_KERNEL_FN double findroot_Ridder(double x1, double x2, double xacc, Func func) const
Find a sign-bracketed scalar root using Ridder iterations.
Definition XSectKernels.hpp:549
OPENSWMM_KERNEL_FN double filled_circ_getYofA(const XSectParams &xs, double a) const
Compute sediment-filled circular water depth from wetted area.
Definition XSectKernels.hpp:1052
OPENSWMM_KERNEL_FN double parab_getPofY(const XSectParams &xs, double y) const
Compute parabolic wetted perimeter from depth.
Definition XSectKernels.hpp:1950
OPENSWMM_KERNEL_FN double trapez_getWofY(const XSectParams &xs, double y) const
Compute trapezoidal top width from depth.
Definition XSectKernels.hpp:1686
OPENSWMM_KERNEL_FN double getYcircular(double alpha) const
Compute the circular normalized depth from area.
Definition XSectKernels.hpp:786
OPENSWMM_KERNEL_FN double circ_getAofY(const XSectParams &xs, double y) const
Compute circular wetted area from depth.
Definition XSectKernels.hpp:916
OPENSWMM_KERNEL_FN double powerfunc_getAofY(const XSectParams &xs, double y) const
Compute power-function wetted area from depth.
Definition XSectKernels.hpp:2014
OPENSWMM_KERNEL_FN double circ_getAofS(const XSectParams &xs, double s) const
Compute circular area from section factor.
Definition XSectKernels.hpp:974
OPENSWMM_KERNEL_FN int locate(double y, const double *table, int jLast) const
Find a lower interpolation bracket through shared legacy bisection.
Definition XSectKernels.hpp:610
OPENSWMM_KERNEL_FN double getAcircular(double psi) const
Compute the circular normalized area from section factor.
Definition XSectKernels.hpp:834
OPENSWMM_KERNEL_FN double trapez_getRofY(const XSectParams &xs, double y) const
Compute trapezoidal hydraulic radius from depth.
Definition XSectKernels.hpp:1740
OPENSWMM_KERNEL_FN double rect_triang_getSofA(const XSectParams &xs, double a) const
Compute triangular-invert rectangular section factor from area.
Definition XSectKernels.hpp:1318
OPENSWMM_KERNEL_FN double rect_round_getAofY(const XSectParams &xs, double y) const
Compute circular-invert rectangular wetted area from depth.
Definition XSectKernels.hpp:1394
OPENSWMM_KERNEL_FN double trapez_getYofA(const XSectParams &xs, double a) const
Compute trapezoidal water depth from wetted area.
Definition XSectKernels.hpp:1703
OPENSWMM_KERNEL_FN double mod_basket_getYofA(const XSectParams &xs, double a) const
Compute modified baskethandle water depth from wetted area.
Definition XSectKernels.hpp:1545
OPENSWMM_KERNEL_FN double trapez_getAofY(const XSectParams &xs, double y) const
Compute trapezoidal wetted area from depth.
Definition XSectKernels.hpp:1668
OPENSWMM_KERNEL_FN double getWofY(const XSectParams &xs, double y) const
Dispatch native top width from depth.
Definition XSectKernels.hpp:2237
OPENSWMM_KERNEL_FN double rect_triang_getdSdA(const XSectParams &xs, double a) const
Compute triangular-invert rectangular section-factor derivative with respect to area.
Definition XSectKernels.hpp:1338
OPENSWMM_KERNEL_FN double powerfunc_getYofA(const XSectParams &xs, double a) const
Compute power-function water depth from wetted area.
Definition XSectKernels.hpp:2051
OPENSWMM_KERNEL_FN double getSofA(const XSectParams &xs, double a) const
Dispatch native section factor from area.
Definition XSectKernels.hpp:2461
OPENSWMM_KERNEL_FN double powerfunc_getRofA(const XSectParams &xs, double a) const
Compute power-function hydraulic radius from area.
Definition XSectKernels.hpp:2126
OPENSWMM_KERNEL_FN double getThetaOfAlpha(double alpha) const
Compute the circular angle from normalized area.
Definition XSectKernels.hpp:722
OPENSWMM_KERNEL_FN double getAofS(const XSectParams &xs, double s) const
Invert native section factor to wetted area.
Definition XSectKernels.hpp:2629
OPENSWMM_KERNEL_FN double getAmax(const XSectParams &xs) const
Read the built-in ratio of area at maximum conveyance to full area.
Definition XSectKernels.hpp:2706
OPENSWMM_KERNEL_FN double rect_triang_getAofY(const XSectParams &xs, double y) const
Compute triangular-invert rectangular wetted area from depth.
Definition XSectKernels.hpp:1209
OPENSWMM_KERNEL_FN double triang_getWofY(const XSectParams &xs, double y) const
Compute triangular top width from depth.
Definition XSectKernels.hpp:1801
OPENSWMM_KERNEL_FN double getRofA(const XSectParams &xs, double a) const
Dispatch native hydraulic radius from area.
Definition XSectKernels.hpp:2518
OPENSWMM_KERNEL_FN double rect_round_getRofA(const XSectParams &xs, double a) const
Compute circular-invert rectangular hydraulic radius from area.
Definition XSectKernels.hpp:1434
OPENSWMM_KERNEL_FN double triang_getYofA(const XSectParams &xs, double a) const
Compute triangular water depth from wetted area.
Definition XSectKernels.hpp:1818
OPENSWMM_KERNEL_FN double triang_getdSdA(const XSectParams &xs, double a) const
Compute triangular section-factor derivative with respect to area.
Definition XSectKernels.hpp:1870
OPENSWMM_KERNEL_FN double triang_getRofY(const XSectParams &xs, double y) const
Compute triangular hydraulic radius from depth.
Definition XSectKernels.hpp:1853
OPENSWMM_KERNEL_FN double mod_basket_getAofY(const XSectParams &xs, double y) const
Compute modified baskethandle wetted area from depth.
Definition XSectKernels.hpp:1572
OPENSWMM_KERNEL_FN double getThetaOfPsi(double psi) const
Compute the circular angle from normalized section factor.
Definition XSectKernels.hpp:750
OPENSWMM_KERNEL_FN double rect_closed_getRofA(const XSectParams &xs, double a) const
Compute closed rectangular hydraulic radius from area.
Definition XSectKernels.hpp:1104
OPENSWMM_KERNEL_FN double parab_getYofA(const XSectParams &xs, double a) const
Compute parabolic water depth from wetted area.
Definition XSectKernels.hpp:1931
OPENSWMM_KERNEL_FN double getYcrit(const XSectParams &xs, double q) const
Compute critical depth for a nonnegative native discharge.
Definition XSectKernels.hpp:2730
OPENSWMM_KERNEL_FN double rect_round_getSofA(const XSectParams &xs, double a) const
Compute circular-invert rectangular section factor from area.
Definition XSectKernels.hpp:1488
OPENSWMM_KERNEL_FN double getAofY(const XSectParams &xs, double y) const
Dispatch native wetted area from depth.
Definition XSectKernels.hpp:2150
OPENSWMM_KERNEL_FN double rect_closed_getSofA(const XSectParams &xs, double a) const
Compute closed rectangular section factor from area.
Definition XSectKernels.hpp:1121
OPENSWMM_KERNEL_FN double invLookup(double y, const double *table, int n_items, const LocateLut *lut=nullptr) const
Invert normalized geometry with legacy upper-branch handling.
Definition XSectKernels.hpp:670
OPENSWMM_KERNEL_FN double parab_getWofY(const XSectParams &xs, double y) const
Compute parabolic top width from depth.
Definition XSectKernels.hpp:1914
OPENSWMM_KERNEL_FN double getYofA(const XSectParams &xs, double a) const
Dispatch native water depth from wetted area.
Definition XSectKernels.hpp:2382
OPENSWMM_KERNEL_FN double rect_round_getdSdA(const XSectParams &xs, double a) const
Compute circular-invert rectangular section-factor derivative with respect to area.
Definition XSectKernels.hpp:1515
OPENSWMM_KERNEL_FN double rect_closed_getdSdA(const XSectParams &xs, double a) const
Compute closed rectangular section-factor derivative with respect to area.
Definition XSectKernels.hpp:1141
OPENSWMM_KERNEL_FN double circ_getSofA(const XSectParams &xs, double a) const
Compute circular section factor from area.
Definition XSectKernels.hpp:954
OPENSWMM_KERNEL_FN double triang_getRofA(const XSectParams &xs, double a) const
Compute triangular hydraulic radius from area.
Definition XSectKernels.hpp:1835
OPENSWMM_KERNEL_FN double trapez_getRofA(const XSectParams &xs, double a) const
Compute trapezoidal hydraulic radius from area.
Definition XSectKernels.hpp:1722
XsectTables tbl
Definition XSectKernels.hpp:466
OPENSWMM_KERNEL_FN double triang_getAofY(const XSectParams &xs, double y) const
Compute triangular wetted area from depth.
Definition XSectKernels.hpp:1783
OPENSWMM_KERNEL_FN double parab_getRofY(const XSectParams &xs, double y) const
Compute parabolic hydraulic radius from depth.
Definition XSectKernels.hpp:1970
OPENSWMM_KERNEL_FN double circ_getdSdA(const XSectParams &xs, double a) const
Compute circular section-factor derivative with respect to area.
Definition XSectKernels.hpp:998
OPENSWMM_KERNEL_FN int findroot_Newton(double x1, double x2, double *rts, double xacc, Func func) const
Find a bracketed scalar root with safeguarded Newton/bisection iterations.
Definition XSectKernels.hpp:491
OPENSWMM_KERNEL_FN double mod_basket_getRofA(const XSectParams &xs, double a) const
Compute modified baskethandle hydraulic radius from area.
Definition XSectKernels.hpp:1617
OPENSWMM_KERNEL_FN double trapez_getdSdA(const XSectParams &xs, double a) const
Compute trapezoidal section-factor derivative with respect to area.
Definition XSectKernels.hpp:1757
OPENSWMM_KERNEL_FN double circ_getYofA(const XSectParams &xs, double a) const
Compute circular water depth from wetted area.
Definition XSectKernels.hpp:934
OPENSWMM_KERNEL_FN double getdSdA(const XSectParams &xs, double a) const
Dispatch the native section-factor derivative by shape.
Definition XSectKernels.hpp:2574
OPENSWMM_KERNEL_FN double parab_getAofY(const XSectParams &xs, double y) const
Compute parabolic wetted area from depth.
Definition XSectKernels.hpp:1896
OPENSWMM_KERNEL_FN double getScircular(double alpha) const
Compute the circular normalized section factor from area.
Definition XSectKernels.hpp:810
OPENSWMM_KERNEL_FN double powerfunc_getPofY(const XSectParams &xs, double y) const
Compute power-function wetted perimeter from depth.
Definition XSectKernels.hpp:2071
OPENSWMM_KERNEL_FN double rect_triang_getWofY(const XSectParams &xs, double y) const
Compute triangular-invert rectangular top width from depth.
Definition XSectKernels.hpp:1229
OPENSWMM_KERNEL_FN double powerfunc_getRofY(const XSectParams &xs, double y) const
Compute power-function hydraulic radius from depth.
Definition XSectKernels.hpp:2106
Definition XSectKernels.hpp:357
int N_W_VertEllipse
Definition XSectKernels.hpp:434
int N_Y_Catenary
Definition XSectKernels.hpp:436
const double * S_Catenary
Definition XSectKernels.hpp:373
const double * A_HorizEllipse
Definition XSectKernels.hpp:362
int N_W_Horseshoe
Definition XSectKernels.hpp:431
const double * W_Gothic
Definition XSectKernels.hpp:385
const double * Y_Gothic
Definition XSectKernels.hpp:395
const double * R_Egg
Definition XSectKernels.hpp:368
const double * Y_SemiEllip
Definition XSectKernels.hpp:398
int N_W_Circ
Definition XSectKernels.hpp:427
const double * R_Arch
Definition XSectKernels.hpp:365
const double * A_Baskethandle
Definition XSectKernels.hpp:359
int N_W_SemiEllip
Definition XSectKernels.hpp:433
const double * R_Circ
Definition XSectKernels.hpp:367
int N_S_Circ
Definition XSectKernels.hpp:418
int N_W_Gothic
Definition XSectKernels.hpp:429
const double * Amax
Definition XSectKernels.hpp:399
const double * Y_Egg
Definition XSectKernels.hpp:394
int N_W_BasketHandle
Definition XSectKernels.hpp:425
int N_R_Horseshoe
Definition XSectKernels.hpp:414
int N_W_HorizEllipse
Definition XSectKernels.hpp:430
const double * A_Arch
Definition XSectKernels.hpp:358
const double * R_HorizEllipse
Definition XSectKernels.hpp:369
const LocateLut * luts
Bucket maps for the inverted tables, indexed by LutId; null == bisect.
Definition XSectKernels.hpp:401
int N_W_Catenary
Definition XSectKernels.hpp:426
const double * S_SemiEllip
Definition XSectKernels.hpp:379
int N_Y_Circ
Definition XSectKernels.hpp:437
const double * W_HorizEllipse
Definition XSectKernels.hpp:386
int N_R_Circ
Definition XSectKernels.hpp:411
const double * W_Circ
Definition XSectKernels.hpp:383
const double * R_Baskethandle
Definition XSectKernels.hpp:366
int N_S_Catenary
Definition XSectKernels.hpp:417
const double * S_Egg
Definition XSectKernels.hpp:375
int N_A_Egg
Definition XSectKernels.hpp:405
const double * S_Horseshoe
Definition XSectKernels.hpp:377
int N_Y_BasketHandle
Definition XSectKernels.hpp:435
const double * Y_SemiCirc
Definition XSectKernels.hpp:397
int N_Y_Egg
Definition XSectKernels.hpp:438
const double * R_Horseshoe
Definition XSectKernels.hpp:370
int N_W_Arch
Definition XSectKernels.hpp:424
const double * W_VertEllipse
Definition XSectKernels.hpp:390
int N_A_Arch
Definition XSectKernels.hpp:402
const double * A_Circ
Definition XSectKernels.hpp:360
const double * S_Gothic
Definition XSectKernels.hpp:376
const double * Y_BasketHandle
Definition XSectKernels.hpp:391
OPENSWMM_KERNEL_FN const LocateLut * lut(LutId id) const
Borrow the optional bucket map selected by a geometry-table enum.
Definition XSectKernels.hpp:453
const double * Y_Horseshoe
Definition XSectKernels.hpp:396
int N_S_BasketHandle
Definition XSectKernels.hpp:416
int N_S_SemiEllip
Definition XSectKernels.hpp:423
int N_A_VertEllipse
Definition XSectKernels.hpp:408
int N_A_Horseshoe
Definition XSectKernels.hpp:407
const double * W_Arch
Definition XSectKernels.hpp:380
const double * Y_Circ
Definition XSectKernels.hpp:393
int N_A_Baskethandle
Definition XSectKernels.hpp:403
int N_W_SemiCirc
Definition XSectKernels.hpp:432
const double * R_VertEllipse
Definition XSectKernels.hpp:371
int N_S_Gothic
Definition XSectKernels.hpp:420
const double * W_Catenary
Definition XSectKernels.hpp:382
const double * S_SemiCirc
Definition XSectKernels.hpp:378
const double * A_VertEllipse
Definition XSectKernels.hpp:364
int N_A_Circ
Definition XSectKernels.hpp:404
int N_S_Horseshoe
Definition XSectKernels.hpp:421
int N_R_Arch
Definition XSectKernels.hpp:409
const double * A_Egg
Definition XSectKernels.hpp:361
int N_R_Baskethandle
Definition XSectKernels.hpp:410
const double * S_Circ
Definition XSectKernels.hpp:374
int N_Y_SemiEllip
Definition XSectKernels.hpp:442
int N_W_Egg
Definition XSectKernels.hpp:428
const double * Y_Catenary
Definition XSectKernels.hpp:392
const double * W_BasketHandle
Definition XSectKernels.hpp:381
int N_R_VertEllipse
Definition XSectKernels.hpp:415
const double * W_SemiCirc
Definition XSectKernels.hpp:388
int N_R_HorizEllipse
Definition XSectKernels.hpp:413
int N_Y_Horseshoe
Definition XSectKernels.hpp:440
const double * W_Horseshoe
Definition XSectKernels.hpp:387
int N_S_Egg
Definition XSectKernels.hpp:419
const double * A_Horseshoe
Definition XSectKernels.hpp:363
int N_Y_SemiCirc
Definition XSectKernels.hpp:441
int N_S_SemiCirc
Definition XSectKernels.hpp:422
const double * S_BasketHandle
Definition XSectKernels.hpp:372
int N_R_Egg
Definition XSectKernels.hpp:412
const double * W_Egg
Definition XSectKernels.hpp:384
const double * W_SemiEllip
Definition XSectKernels.hpp:389
int N_A_HorizEllipse
Definition XSectKernels.hpp:406
int N_Y_Gothic
Definition XSectKernels.hpp:439