OpenSWMM Engine  6.0.0-alpha.4
Data-oriented, plugin-extensible SWMM Engine (6.0.0-alpha.4)
Loading...
Searching...
No Matches
FvKernels.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
59
60#ifndef OPENSWMM_ENGINE_FV_KERNELS_HPP
61#define OPENSWMM_ENGINE_FV_KERNELS_HPP
62
63#include <algorithm>
64#include <cmath>
65
66#include "FvOptions.hpp"
67#include "NetworkMeshData.hpp"
68
69// Portable kernel-function marker — identical convention to
70// 2d/solver/InertialKernels.hpp:45. Host builds get plain `inline`; the GPU
71// plugin defines OPENSWMM_KERNEL_FN to KOKKOS_INLINE_FUNCTION *before*
72// including this header so the same scalar bodies compile for the device.
73#ifndef OPENSWMM_KERNEL_FN
74#define OPENSWMM_KERNEL_FN inline
75#endif
76
78
81inline constexpr double kGravity = 32.2;
82
86inline constexpr double kDryDepth = 1.0e-7;
87
89inline constexpr double kDryArea = 1.0e-12;
90
94inline constexpr double kEtaDeadband = 1.0e-12;
95
96// ===========================================================================
97// Section evaluation — the ONE place the cross-section machinery is called
98// ===========================================================================
99//
100// These three go through the geometry's own evaluator (XSectKernels.hpp) — the
101// SAME bodies XSection.cpp's public accessors and XSectBatch's kernels run.
102// Only where the evaluator's tables live differs between the host solver and
103// the device backend, so the §6.8 parity harness compares two instantiations of
104// one implementation rather than two implementations.
105
109OPENSWMM_KERNEL_FN double sectionArea(const FvGeometry& g, double h) noexcept {
110 if (h <= 0.0) return 0.0;
111 return g.barrel_scale * g.eval->getAofY(g.xs, (h < g.y_full) ? h : g.y_full);
112}
113
117OPENSWMM_KERNEL_FN double sectionWidth(const FvGeometry& g, double h) noexcept {
118 if (h <= 0.0) return 0.0;
119 return g.barrel_scale * g.eval->getWofY(g.xs, (h < g.y_full) ? h : g.y_full);
120}
121
125OPENSWMM_KERNEL_FN double sectionHydRad(const FvGeometry& g, double h) noexcept {
126 if (h <= 0.0) return 0.0;
127 if (h >= g.y_full) return g.r_full;
128 return g.eval->getRofY(g.xs, h);
129}
130
131// ===========================================================================
132// Preissmann slot taper (plan §3.3.2)
133// ===========================================================================
134
138OPENSWMM_KERNEL_FN double slotRamp(double s) noexcept {
139 if (s <= 0.0) return 0.0;
140 if (s >= 1.0) return 1.0;
141 return s * s * (3.0 - 2.0 * s);
142}
143
146OPENSWMM_KERNEL_FN double slotRampIntegral(double s) noexcept {
147 if (s <= 0.0) return 0.0;
148 if (s >= 1.0) return s - 0.5; // 1 − ½ from the taper, then linear
149 const double s3 = s * s * s;
150 return s3 - 0.5 * s3 * s; // s³ − s⁴/2
151}
152
153// ===========================================================================
154// Closure — one continuous geometry from dry bed to full pressurization
155// ===========================================================================
156//
157// This is the memoryless SLOT closure — every cell, every state, no history.
158// TPA exception (issue #156): under FV_PRESSURE_CLOSURE TPA a cell whose
159// regime flag is set evaluates the tpa* kernels at the end of this file
160// instead. The flag is physical air-pathway history (cleared on cold start
161// and hotstart restore), not numerical relaxation; unflagged cells — and the
162// entire SLOT closure path — are untouched.
163
166OPENSWMM_KERNEL_FN double areaOfDepth(const FvGeometry& g, double h) noexcept {
167 if (h <= 0.0) return 0.0;
168 if (h >= g.y_full) return g.a_crown + g.t_slot * (h - g.y_full);
169 const double band = g.y_full - g.y_crown;
170 const double ax = sectionArea(g, h);
171 if (band <= 0.0) return ax; // open section: no taper band
172 const double s = (h - g.y_crown) / band;
173 return ax + g.t_slot * band * slotRampIntegral(s);
174}
175
177OPENSWMM_KERNEL_FN double widthOfDepth(const FvGeometry& g, double h) noexcept {
178 if (h <= 0.0) return 0.0;
179 if (h >= g.y_full) return g.t_slot;
180 const double band = g.y_full - g.y_crown;
181 const double wx = sectionWidth(g, h);
182 if (band <= 0.0) return wx;
183 const double s = (h - g.y_crown) / band;
184 return wx + g.t_slot * slotRamp(s);
185}
186
188OPENSWMM_KERNEL_FN double hydRadOfDepth(const FvGeometry& g, double h) noexcept {
189 if (h <= 0.0) return 0.0;
190 if (h >= g.y_full) return g.r_full;
191 return sectionHydRad(g, h);
192}
193
205OPENSWMM_KERNEL_FN double i1OfDepth(const FvGeometry& g, double h,
206 double area_at_h) noexcept {
207 if (h <= 0.0) return 0.0;
208 if (h >= g.y_full) {
209 const double d = h - g.y_full;
210 return g.i1_crown + g.a_crown * d + 0.5 * g.t_slot * d * d;
211 }
212 const int n = static_cast<int>(kI1Samples);
213 const double dh = g.y_full / static_cast<double>(n - 1);
214 int i = static_cast<int>(h / dh);
215 if (i < 0) i = 0;
216 if (i > n - 2) i = n - 2;
217 const double h_i = static_cast<double>(i) * dh;
218 // i1_tbl stores I₁ at the sample points; the companion area sample is the
219 // second half of the same buffer (see buildI1Table).
220 const double i1_i = g.i1_tbl[static_cast<std::size_t>(i)];
221 const double a_i = g.i1_tbl[static_cast<std::size_t>(n + i)];
222 return i1_i + 0.5 * (a_i + area_at_h) * (h - h_i);
223}
224
254 double a) noexcept {
255 if (a <= 0.0) return 0.0;
256 if (a >= g.a_crown) return g.y_full + (a - g.a_crown) / g.t_slot;
257
258 const int n = static_cast<int>(kI1Samples);
259 const double dh = g.y_full / static_cast<double>(n - 1);
260
261 int lo = 0, hi = n - 1;
262 while (hi - lo > 1) {
263 const int mid = (lo + hi) / 2;
264 if (g.i1_tbl[static_cast<std::size_t>(n + mid)] <= a) lo = mid;
265 else hi = mid;
266 }
267
268 double xa = static_cast<double>(lo) * dh;
269 double xb = static_cast<double>(hi) * dh;
270 double fa = g.i1_tbl[static_cast<std::size_t>(n + lo)] - a;
271 double fb = g.i1_tbl[static_cast<std::size_t>(n + hi)] - a;
272 if (fa >= 0.0) return xa;
273 if (fb <= 0.0) return xb;
274
275 for (int it = 0; it < 40; ++it) {
276 double x = xb - fb * (xb - xa) / (fb - fa);
277 if (!(x > xa && x < xb)) x = 0.5 * (xa + xb);
278 const double f = areaOfDepth(g, x) - a;
279 if (f == 0.0) return x;
280 if (f < 0.0) { xa = x; fa = f; fb *= 0.5; } // Illinois down-weighting
281 else { xb = x; fb = f; fa *= 0.5; }
282 if (xb - xa <= 1.0e-15 * g.y_full) break;
283 }
284 return 0.5 * (xa + xb);
285}
286
323OPENSWMM_KERNEL_FN double depthOfArea(const FvGeometry& g, double a) noexcept {
324 if (a <= 0.0) return 0.0;
325 if (a >= g.a_crown) return g.y_full + (a - g.a_crown) / g.t_slot;
326
327 const int n = static_cast<int>(kI1Samples);
328 const double da = g.a_crown / static_cast<double>(n - 1);
329 if (!(da > 0.0)) return depthOfAreaBracketed(g, a);
330
331 int j = static_cast<int>(a / da);
332 if (j < 1) j = 1;
333 if (j > n - 3) j = n - 3;
334
335 double xa = g.h_tbl[static_cast<std::size_t>(j - 1)];
336 double xb = g.h_tbl[static_cast<std::size_t>(j + 2)];
337 if (!(xb > xa)) return depthOfAreaBracketed(g, a);
338
339 double fa = areaOfDepth(g, xa) - a;
340 double fb = areaOfDepth(g, xb) - a;
341 if (fa > 0.0 || fb < 0.0) return depthOfAreaBracketed(g, a);
342 if (fa == 0.0) return xa;
343 if (fb == 0.0) return xb;
344
345 const double tol = 1.0e-15 * g.y_full;
346 double c = xa, fc = fa, d = xb - xa, e = d;
347 for (int it = 0; it < 60; ++it) {
348 if (fb * fc > 0.0) { c = xa; fc = fa; d = xb - xa; e = d; }
349 if (std::fabs(fc) < std::fabs(fb)) {
350 xa = xb; xb = c; c = xa;
351 fa = fb; fb = fc; fc = fa;
352 }
353 const double m = 0.5 * (c - xb);
354 if (std::fabs(m) <= tol || fb == 0.0) return xb;
355
356 if (std::fabs(e) < tol || std::fabs(fa) <= std::fabs(fb)) {
357 d = m; e = m; // bisect
358 } else {
359 const double sfb = fb / fa;
360 double p, q;
361 if (xa == c) { // secant
362 p = 2.0 * m * sfb;
363 q = 1.0 - sfb;
364 } else { // inverse quadratic
365 const double qq = fa / fc;
366 const double r = fb / fc;
367 p = sfb * (2.0 * m * qq * (qq - r) - (xb - xa) * (r - 1.0));
368 q = (qq - 1.0) * (r - 1.0) * (sfb - 1.0);
369 }
370 if (p > 0.0) q = -q; else p = -p;
371 if (2.0 * p < ((3.0 * m * q - std::fabs(tol * q) < std::fabs(e * q))
372 ? 3.0 * m * q - std::fabs(tol * q)
373 : std::fabs(e * q))) {
374 e = d; d = p / q;
375 } else {
376 d = m; e = m;
377 }
378 }
379 xa = xb; fa = fb;
380 xb += (std::fabs(d) > tol) ? d : ((m > 0.0) ? tol : -tol);
381 fb = areaOfDepth(g, xb) - a;
382 }
383 return xb;
384}
385
388OPENSWMM_KERNEL_FN double celerity(double a, double t) noexcept {
389 if (a <= kDryArea || t <= 0.0) return 0.0;
390 return std::sqrt(kGravity * a / t);
391}
392
393// ===========================================================================
394// Riemann solver
395// ===========================================================================
396
398struct FaceState {
399 double a = 0.0;
400 double q = 0.0;
401 double u = 0.0;
402 double c = 0.0;
403 double i1 = 0.0;
404};
405
407struct FaceFlux {
408 double mass = 0.0;
409 double mom = 0.0;
410 double sstar = 0.0;
411 double sl = 0.0;
412 double sr = 0.0;
413};
414
419 double& sl, double& sr) noexcept {
420 const bool wetL = (L.a > kDryArea);
421 const bool wetR = (R.a > kDryArea);
422 if (wetL && wetR) {
423 sl = std::min(L.u - L.c, R.u - R.c);
424 sr = std::max(L.u + L.c, R.u + R.c);
425 } else if (wetR) { // dry left
426 sl = R.u - 2.0 * R.c;
427 sr = R.u + R.c;
428 } else if (wetL) { // dry right
429 sl = L.u - L.c;
430 sr = L.u + 2.0 * L.c;
431 } else {
432 sl = 0.0;
433 sr = 0.0;
434 }
435}
436
439 double& fa, double& fq) noexcept {
440 fa = S.q;
441 fq = S.q * S.u + kGravity * S.i1;
442}
443
465 const FaceState& R) noexcept {
466 FaceFlux out;
467 if (L.a <= kDryArea && R.a <= kDryArea) return out;
468
469 double sl = 0.0, sr = 0.0;
470 waveSpeeds(L, R, sl, sr);
471 out.sl = sl;
472 out.sr = sr;
473
474 double fal = 0.0, fql = 0.0, far = 0.0, fqr = 0.0;
475 physicalFlux(L, fal, fql);
476 physicalFlux(R, far, fqr);
477
478 if (sl >= 0.0) { out.mass = fal; out.mom = fql; out.sstar = sl; return out; }
479 if (sr <= 0.0) { out.mass = far; out.mom = fqr; out.sstar = sr; return out; }
480
481 const double dsr = sr - sl;
482 out.mass = (sr * fal - sl * far + sl * sr * (R.a - L.a)) / dsr;
483 out.mom = (sr * fql - sl * fqr + sl * sr * (R.q - L.q)) / dsr;
484
485 // Contact speed (plan §3.2). Falls back to the HLL-averaged velocity when
486 // the denominator degenerates, which happens only when both sides are
487 // vanishing — where the species flux is zero anyway.
488 const double den = R.a * (R.u - sr) - L.a * (L.u - sl);
489 if (std::fabs(den) > 1.0e-14) {
490 out.sstar = (sl * R.a * (R.u - sr) - sr * L.a * (L.u - sl)) / den;
491 } else {
492 const double a_hll = (sr * R.a - sl * L.a - (far - fal)) / dsr;
493 out.sstar = (a_hll > kDryArea) ? out.mass / a_hll : 0.0;
494 }
495 return out;
496}
497
518 const FaceFlux& f, double phi_l,
519 double phi_r, bool hllc) noexcept {
520 if (hllc) return f.mass * ((f.sstar >= 0.0) ? phi_l : phi_r);
521
522 // HLL on the A(phi) component, with the same supersonic branches as the
523 // hydrodynamic flux so the two can never disagree about which side is
524 // upstream of the whole fan.
525 if (f.sl >= 0.0) return L.q * phi_l;
526 if (f.sr <= 0.0) return R.q * phi_r;
527 const double dsr = f.sr - f.sl;
528 return (f.sr * L.q * phi_l - f.sl * R.q * phi_r +
529 f.sl * f.sr * (R.a * phi_r - L.a * phi_l)) / dsr;
530}
531
532// ===========================================================================
533// Source terms
534// ===========================================================================
535
544OPENSWMM_KERNEL_FN double frictionUpdate(double q, double u, double r,
545 double dt, double rough_factor) noexcept {
546 if (r <= 0.0 || rough_factor <= 0.0) return q;
547 const double r43 = r * std::cbrt(r);
548 const double den = 1.0 + dt * rough_factor * std::fabs(u) / r43;
549 return q / den;
550}
551
560OPENSWMM_KERNEL_FN double localLossUpdate(double q, double u, double k,
561 double dx, double dt) noexcept {
562 if (k <= 0.0 || dx <= 0.0) return q;
563 return q / (1.0 + dt * k * std::fabs(u) / (2.0 * dx));
564}
565
582OPENSWMM_KERNEL_FN double ufUpdate(double q, double a, double u_old,
583 double k3, double grad_term,
584 double dt) noexcept {
585 if (k3 <= 0.0 || a <= 0.0) return q;
586 // Dead-band (measured, issue #156): the implicit fold acts as added
587 // inertia — it resists velocity DECAY too, so applied to the ~mm/s
588 // numerical ripple of a storage-coupled pool it sustained noise that
589 // steady friction was correctly killing (0.004 → 0.011 cfs on the
590 // at-rest fixture). UF correlations are calibrated for real transients
591 // (paper velocities O(0.1–1 m/s)); below 0.01 ft/s the term is noise
592 // amplification, not physics. Both the old and candidate velocities must
593 // clear the floor, so a genuinely at-rest deck stays bit-identical.
594 constexpr double kUfVelFloor = 0.01; // ft/s, internal units
595 if (std::fabs(u_old) < kUfVelFloor && std::fabs(q / a) < kUfVelFloor)
596 return q;
597 double qn = (q + k3 * a * u_old) / (1.0 + k3);
598 qn -= dt * k3 * a * grad_term;
599 const double dq = qn - q;
600 const double cap = 0.5 * std::fabs(q);
601 if (std::fabs(dq) > cap) qn = q + ((dq > 0.0) ? cap : -cap);
602 return qn;
603}
604
605// ---------------------------------------------------------------------------
606// TPA — two-component pressure approach (issue #156 Phase 4)
607// ---------------------------------------------------------------------------
608// Vasconcelos, Wright & Roe (2006). For a cell whose regime FLAG is set
609// (state.cell_tpa — physical air-pathway history, updated once per substep by
610// the solver, NOT evaluated here), the closure is the slot line extended to
611// BOTH signs of ΔA = A − a_crown:
612// h(A) = y_full + (A − a_crown)/t_slot (hs = h − y_full, signed)
613// A(h) = a_crown + t_slot·(h − y_full)
614// I₁(h) = i1_crown + a_crown·(h − y_full) (paper Eq. 12b: the slot's
615// ½·t_slot·d² numerical
616// storage pressure is dropped)
617// T = t_slot, R = r_full, c = √(g·A/t_slot) ≈ acoustic celerity a.
618// Free-surface (unflagged) cells use the table closure above UNCHANGED,
619// including the crown taper. The pair is continuous at A = a_crown, h = y_full.
620
621OPENSWMM_KERNEL_FN double tpaDepthOfArea(const FvGeometry& g, double a) noexcept {
622 return g.y_full + (a - g.a_crown) / g.t_slot;
623}
624
625OPENSWMM_KERNEL_FN double tpaAreaOfDepth(const FvGeometry& g, double h) noexcept {
626 return g.a_crown + g.t_slot * (h - g.y_full);
627}
628
629OPENSWMM_KERNEL_FN double tpaI1OfDepth(const FvGeometry& g, double h) noexcept {
630 return g.i1_crown + g.a_crown * (h - g.y_full);
631}
632
634OPENSWMM_KERNEL_FN double faceCflDt(double cfl, double dx, double u,
635 double c) noexcept {
636 const double s = std::fabs(u) + c;
637 return (s > 1.0e-12) ? cfl * dx / s : 1.0e30;
638}
639
648OPENSWMM_KERNEL_FN double positivityScale(double vol, double outflow, double dt) noexcept {
649 if (outflow <= 0.0 || dt <= 0.0) return 1.0;
650 const double take = outflow * dt;
651 const double avail = (vol > 0.0) ? vol : 0.0;
652 return (take <= avail) ? 1.0 : avail / take;
653}
654
655} // namespace openswmm::fv::kernels
656
657#endif // OPENSWMM_ENGINE_FV_KERNELS_HPP
#define OPENSWMM_KERNEL_FN
Definition ExplicitKokkosSurfaceSolver.cpp:18
Option struct for the explicit finite-volume 1D network solver.
SoA storage for the 1D finite-volume network mesh and its state.
Definition FvKernels.hpp:77
OPENSWMM_KERNEL_FN FaceFlux riemannFlux(const FaceState &L, const FaceState &R) noexcept
Flux for the conservative St. Venant system, plus the contact speed.
Definition FvKernels.hpp:464
OPENSWMM_KERNEL_FN double frictionUpdate(double q, double u, double r, double dt, double rough_factor) noexcept
Semi-implicit Manning friction — unconditionally stable, so friction imposes no time-step restriction...
Definition FvKernels.hpp:544
OPENSWMM_KERNEL_FN void physicalFlux(const FaceState &S, double &fa, double &fq) noexcept
Physical flux of one state.
Definition FvKernels.hpp:438
OPENSWMM_KERNEL_FN double ufUpdate(double q, double a, double u_old, double k3, double grad_term, double dt) noexcept
Unsteady-friction momentum update (issue #156).
Definition FvKernels.hpp:582
OPENSWMM_KERNEL_FN double widthOfDepth(const FvGeometry &g, double h) noexcept
Top width dA/dh at depth h, INCLUDING the tapered slot.
Definition FvKernels.hpp:177
OPENSWMM_KERNEL_FN double faceCflDt(double cfl, double dx, double u, double c) noexcept
CFL-limited step for one face: α·Δx/(|u| + c).
Definition FvKernels.hpp:634
OPENSWMM_KERNEL_FN double localLossUpdate(double q, double u, double k, double dx, double dt) noexcept
Semi-implicit entrance/exit loss at a node-coupling face.
Definition FvKernels.hpp:560
OPENSWMM_KERNEL_FN void waveSpeeds(const FaceState &L, const FaceState &R, double &sl, double &sr) noexcept
Definition FvKernels.hpp:418
OPENSWMM_KERNEL_FN double positivityScale(double vol, double outflow, double dt) noexcept
Positivity scale for a cell about to export more volume than it holds.
Definition FvKernels.hpp:648
OPENSWMM_KERNEL_FN double hydRadOfDepth(const FvGeometry &g, double h) noexcept
Hydraulic radius at depth h.
Definition FvKernels.hpp:188
OPENSWMM_KERNEL_FN double sectionArea(const FvGeometry &g, double h) noexcept
Definition FvKernels.hpp:109
OPENSWMM_KERNEL_FN double slotRamp(double s) noexcept
Definition FvKernels.hpp:138
constexpr double kDryArea
Area floor paired with kDryDepth for the u = Q/A division.
Definition FvKernels.hpp:89
constexpr double kEtaDeadband
Definition FvKernels.hpp:94
OPENSWMM_KERNEL_FN double tpaDepthOfArea(const FvGeometry &g, double a) noexcept
Definition FvKernels.hpp:621
OPENSWMM_KERNEL_FN double areaOfDepth(const FvGeometry &g, double h) noexcept
Definition FvKernels.hpp:166
OPENSWMM_KERNEL_FN double i1OfDepth(const FvGeometry &g, double h, double area_at_h) noexcept
Hydrostatic first moment I₁(h) = ∫₀ʰ A(η)dη.
Definition FvKernels.hpp:205
OPENSWMM_KERNEL_FN double speciesFlux(const FaceState &L, const FaceState &R, const FaceFlux &f, double phi_l, double phi_r, bool hllc) noexcept
Species flux for the A(phi) component.
Definition FvKernels.hpp:517
OPENSWMM_KERNEL_FN double depthOfAreaBracketed(const FvGeometry &g, double a) noexcept
Invert A → h — the EXACT inverse of areaOfDepth above.
Definition FvKernels.hpp:253
OPENSWMM_KERNEL_FN double slotRampIntegral(double s) noexcept
Definition FvKernels.hpp:146
OPENSWMM_KERNEL_FN double tpaI1OfDepth(const FvGeometry &g, double h) noexcept
Definition FvKernels.hpp:629
OPENSWMM_KERNEL_FN double sectionWidth(const FvGeometry &g, double h) noexcept
Definition FvKernels.hpp:117
OPENSWMM_KERNEL_FN double depthOfArea(const FvGeometry &g, double a) noexcept
Invert A → h. Same root as depthOfAreaBracketed, found far faster.
Definition FvKernels.hpp:323
constexpr double kDryDepth
Definition FvKernels.hpp:86
OPENSWMM_KERNEL_FN double tpaAreaOfDepth(const FvGeometry &g, double h) noexcept
Definition FvKernels.hpp:625
constexpr double kGravity
Definition FvKernels.hpp:81
OPENSWMM_KERNEL_FN double celerity(double a, double t) noexcept
Definition FvKernels.hpp:388
OPENSWMM_KERNEL_FN double sectionHydRad(const FvGeometry &g, double h) noexcept
Definition FvKernels.hpp:125
constexpr int kI1Samples
Definition NetworkMeshData.hpp:61
Cross-section closure for one conduit's cell chain.
Definition NetworkMeshData.hpp:77
Result of a face flux evaluation.
Definition FvKernels.hpp:407
double mom
F[1] — momentum flux (ft⁴/s²)
Definition FvKernels.hpp:409
double sstar
contact-wave speed; sign selects the species upwind
Definition FvKernels.hpp:410
double mass
F[0] — volumetric flux (cfs)
Definition FvKernels.hpp:408
double sr
right signal speed
Definition FvKernels.hpp:412
double sl
left signal speed (kept for the species flux)
Definition FvKernels.hpp:411
One side of a face after hydrostatic reconstruction.
Definition FvKernels.hpp:398
double c
celerity (ft/s)
Definition FvKernels.hpp:402
double i1
hydrostatic first moment at the reconstructed depth
Definition FvKernels.hpp:403
double a
reconstructed flow area (ft²)
Definition FvKernels.hpp:399
double q
reconstructed discharge (cfs) = a·u
Definition FvKernels.hpp:400
double u
velocity (ft/s)
Definition FvKernels.hpp:401