OpenSWMM Engine  6.0.0-alpha.4
Data-oriented, plugin-extensible SWMM Engine (6.0.0-alpha.4)
Loading...
Searching...
No Matches
NetworkMeshData.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
43
44#ifndef OPENSWMM_ENGINE_FV_NETWORK_MESH_DATA_HPP
45#define OPENSWMM_ENGINE_FV_NETWORK_MESH_DATA_HPP
46
47#include <cstdint>
48#include <vector>
49
50#include "../XSectBatch.hpp"
52#include "../XSectKernels.hpp"
53#include "FvClosureKernels.hpp"
54
55namespace openswmm::fv {
56
58inline constexpr int kNodeVolSamples = 129;
59
71struct FvGeometry {
73
74 double y_full = 0.0;
75 double a_full = 0.0;
76 double w_max = 0.0;
77 double r_full = 0.0;
78
81 double barrel_scale = 1.0;
82
86 double y_crown = 0.0;
87
90 double t_slot = 0.0;
91
95 double a_crown = 0.0;
96
98 double i1_crown = 0.0;
99
100 uint8_t is_open = 0;
101
102 int barrels = 1;
103
104 // Friction, losses, slope and the culvert curve are PER CONDUIT and live
105 // in NetworkMeshData::conduit_* (plan Phase 1f): this block is one shared
106 // entry per DISTINCT (section, barrels, open) so a 5000-pipe network with
107 // a dozen sections keeps a dozen closure tables cache-resident instead of
108 // streaming ~4 kB of geometry per cell every substep.
109
118};
119
124
125 // -----------------------------------------------------------------------
126 // Cells — indexed by cell index [0, n_cells)
127 // -----------------------------------------------------------------------
128
129 std::vector<int> cell_geom;
130 std::vector<int> cell_conduit;
131 std::vector<double> cell_dx;
132 std::vector<double> cell_zb;
133
140 std::vector<double> cell_dzdx;
141
147 std::vector<int> cell_face0;
148 std::vector<int> cell_face1;
149 std::vector<int8_t> cell_side0;
150 std::vector<int8_t> cell_side1;
151
152 // -----------------------------------------------------------------------
153 // Faces — indexed by face index [0, n_faces). Flux is positive LEFT → RIGHT.
154 // -----------------------------------------------------------------------
155 // A face with cell_l == -1 has a NODE on its left (a conduit's upstream
156 // boundary); cell_r == -1 puts the node on its right (downstream boundary).
157 // Interior faces (both cells ≥ 0) include virtual junctions, which are
158 // nothing but a splice of two conduits' chains — mass and momentum flux
159 // continuity are then properties of the scheme, not a special treatment.
160
161 std::vector<int> face_cl;
162 std::vector<int> face_cr;
163 std::vector<int> face_node;
164
183 std::vector<int> face_geom;
184
195 if (face_geom.size() == static_cast<std::size_t>(n_faces()))
196 return;
197 face_geom.assign(static_cast<std::size_t>(n_faces()), 0);
198 for (int f = 0; f < n_faces(); ++f) {
199 const auto uf = static_cast<std::size_t>(f);
200 const int c = (face_cl[uf] >= 0) ? face_cl[uf] : face_cr[uf];
201 face_geom[uf] = (c >= 0 && static_cast<std::size_t>(c) < cell_geom.size())
202 ? cell_geom[static_cast<std::size_t>(c)]
203 : 0;
204 }
205 }
206
213 std::vector<uint8_t> face_gate;
214
218 std::vector<int> face_culvert;
219 std::vector<double> face_zb;
220 std::vector<double> face_dx;
221
232 std::vector<int8_t> face_dir_l;
233 std::vector<int8_t> face_dir_r;
234
238 std::vector<uint8_t> face_virtual;
239
246 std::vector<int> face_vj_node;
247
254 std::vector<int> node_vj_face;
255
256 // -----------------------------------------------------------------------
257 // Conduit → cell map. Cells of a conduit are CONTIGUOUS by construction,
258 // so a begin/count pair is a complete (and cheaper) CSR.
259 // -----------------------------------------------------------------------
260
261 std::vector<int> conduit_cell_begin;
262 std::vector<int> conduit_cell_count;
263 std::vector<int> conduit_link;
264 std::vector<int> conduit_section;
265
266 // Per-conduit friction and losses (already lengthening-adjusted, see
267 // Router::init) and the culvert inlet-control data — everything that
268 // differs between conduits sharing one section block (plan Phase 1f).
269 std::vector<double> conduit_roughness;
270 std::vector<double> conduit_rough_factor;
271 std::vector<double> conduit_loss_inlet;
272 std::vector<double> conduit_loss_outlet;
273 std::vector<double> conduit_slope;
274 std::vector<int> conduit_culvert_code;
277 std::vector<hydkernels::CulvertCurve> conduit_culvert_curve;
278 std::vector<uint8_t> conduit_culvert_mitered;
279
280 // -----------------------------------------------------------------------
281 // Cell chains (CSR). A chain is a maximal run of cells joined by INTERIOR
282 // faces, so it spans virtual junctions — a spliced pair of conduits is one
283 // chain, which is exactly the property that makes a virtual junction
284 // indistinguishable from an interior cut.
285 //
286 // Chains give the solver an ordered 1D stencil: the second-order scalar
287 // reconstruction reads neighbours along the chain, and the implicit
288 // dispersion solve is one tridiagonal system per chain (D-FV1). `dir` is
289 // the cell's own axis relative to the chain's walking direction.
290 // -----------------------------------------------------------------------
291
292 std::vector<int> chain_ptr;
293 std::vector<int> chain_cells;
294 std::vector<int8_t> chain_dir;
295 std::vector<int> cell_chain;
296 std::vector<int> cell_chain_pos;
297
305 int n_chains() const noexcept {
306 return chain_ptr.empty() ? 0 : static_cast<int>(chain_ptr.size()) - 1;
307 }
308
309 // -----------------------------------------------------------------------
310 // Non-conduit links (pumps, orifices, weirs, outlets). Evaluated by their
311 // existing structure equations outside the solver and applied here as
312 // source/sink pairs on the two node volumes.
313 //
314 // DUMMY-xsect conduits belong here too. Legacy isTrueConduit
315 // (dynwave.c:411-414) is false for them: they carry no cross-section, so
316 // there is nothing to march, and DW routes them through findNonConduitFlow
317 // as a pure pass-through instead. They differ from a real structure in that
318 // their discharge is not a head relation the engine can evaluate outside
319 // the solver — it is whatever arrives at the upstream node — so the solver
320 // computes it itself (`struct_is_dummy`, see ExplicitFvSolver::
321 // refreshDummyFlows) rather than reading FvStepForcing::structure_flow.
322 // -----------------------------------------------------------------------
323
324 std::vector<int> struct_link;
325 std::vector<int> struct_n1;
326 std::vector<int> struct_n2;
327
330 std::vector<uint8_t> struct_is_dummy;
331
337 std::vector<uint8_t> node_dummy_drain;
338
339 // -----------------------------------------------------------------------
340 // Node → face map (CSR). Two-pass gather with NO atomics: the node update
341 // reads its own face list in a fixed order, so the result is deterministic
342 // and identical across backends and thread counts.
343 // -----------------------------------------------------------------------
344
345 std::vector<int> node_face_ptr;
346 std::vector<int> node_face_idx;
347 std::vector<double> node_face_sign;
348
352 std::vector<double> node_face_zb;
353
354 // -----------------------------------------------------------------------
355 // Node properties
356 // -----------------------------------------------------------------------
357
358 std::vector<double> node_invert;
359 std::vector<double> node_full_depth;
360 std::vector<double> node_ponded_area;
361 std::vector<double> node_sur_depth;
362
366 std::vector<uint8_t> node_can_pond;
367
370 std::vector<uint8_t> node_kind;
371
379 std::vector<double> node_area;
380
381 // -- Tabulated storage relation (STORAGE nodes only) ---------------------
382 //
383 // The solver must invert volume → depth every substep, and it must do so
384 // without reaching back into SimulationContext (the GPU plugin has no
385 // access to it). Storage geometry is therefore FLATTENED at mesh build into
386 // a monotone depth→volume table sampled from node::getVolume, exactly as
387 // the cross-sections are flattened for the device path.
388 //
389 // Junctions need none of this: their storage is linear in depth with the
390 // surface area held over the routing step (plan §3.4).
391
392 std::vector<int> node_vol_off;
393 std::vector<double> node_vol_dmax;
394 std::vector<double> node_vol_atop;
395 std::vector<double> node_vol_tbl;
396
397 // -----------------------------------------------------------------------
398 // Geometry closures — one per conduit row
399 // -----------------------------------------------------------------------
400
401 std::vector<FvGeometry> geom;
402
403 // -----------------------------------------------------------------------
404 // Capacity queries
405 // -----------------------------------------------------------------------
406
413 int n_cells() const noexcept { return static_cast<int>(cell_dx.size()); }
420 int n_faces() const noexcept { return static_cast<int>(face_cl.size()); }
427 int n_nodes() const noexcept { return static_cast<int>(node_invert.size()); }
434 int n_conduits() const noexcept { return static_cast<int>(conduit_cell_begin.size()); }
435
444 void clear() {
445 cell_geom.clear();
446 cell_conduit.clear();
447 cell_dx.clear();
448 cell_zb.clear();
449 cell_dzdx.clear();
450 cell_face0.clear();
451 cell_face1.clear();
452 cell_side0.clear();
453 cell_side1.clear();
454 face_cl.clear();
455 face_cr.clear();
456 face_node.clear();
457 face_gate.clear();
458 face_geom.clear();
459 face_culvert.clear();
460 face_zb.clear();
461 face_dx.clear();
462 face_virtual.clear();
463 face_vj_node.clear();
464 node_vj_face.clear();
465 face_dir_l.clear();
466 face_dir_r.clear();
467 conduit_cell_begin.clear();
468 conduit_cell_count.clear();
469 conduit_link.clear();
470 conduit_section.clear();
471 conduit_roughness.clear();
472 conduit_rough_factor.clear();
473 conduit_loss_inlet.clear();
474 conduit_loss_outlet.clear();
475 conduit_slope.clear();
476 conduit_culvert_code.clear();
477 conduit_culvert_curve.clear();
479 chain_ptr.clear();
480 chain_cells.clear();
481 chain_dir.clear();
482 cell_chain.clear();
483 cell_chain_pos.clear();
484 struct_link.clear();
485 struct_n1.clear();
486 struct_n2.clear();
487 struct_is_dummy.clear();
488 node_dummy_drain.clear();
489 node_face_ptr.clear();
490 node_face_idx.clear();
491 node_face_sign.clear();
492 node_face_zb.clear();
493 node_invert.clear();
494 node_full_depth.clear();
495 node_ponded_area.clear();
496 node_sur_depth.clear();
497 node_can_pond.clear();
498 node_kind.clear();
499 node_area.clear();
500 node_vol_off.clear();
501 node_vol_dmax.clear();
502 node_vol_atop.clear();
503 node_vol_tbl.clear();
504 geom.clear();
505 }
506};
507
509enum : uint8_t { kNodeJunction = 0, kNodeVirtual = 1, kNodeStorage = 2, kNodeOutfall = 3 };
510
520 // Conserved cell variables aggregate every parallel barrel. Link reporting
521 // uses these directly; it must not multiply them by geom.barrels again.
522 std::vector<double> cell_a;
523 std::vector<double> cell_q;
524
525 // Derived, refreshed after every cell update so faces read a consistent
526 // depth without re-inverting A(h) per face.
527 std::vector<double> cell_h;
528
529 // Node state. `volume` is the prognostic mass ledger — depth follows from
530 // it through the node's storage relation — so node mass is conserved
531 // exactly (plan §3.4).
532 std::vector<double> node_volume;
533 std::vector<double> node_head;
534 std::vector<double> node_surf_area;
535 std::vector<double> node_overflow;
536
539 std::vector<double> node_overflow_vol;
540
541 // Advected scalars, species-major: phi[s * n_cells + c]. Empty when transport
542 // is off.
543 std::vector<double> cell_phi;
544 int n_species = 0;
545
555 std::vector<uint8_t> cell_tpa;
556
571 void resize(int n_cells, int n_nodes, int n_species_in) {
572 const auto nc = static_cast<std::size_t>(n_cells);
573 const auto nn = static_cast<std::size_t>(n_nodes);
574 cell_a.assign(nc, 0.0);
575 cell_q.assign(nc, 0.0);
576 cell_h.assign(nc, 0.0);
577 node_volume.assign(nn, 0.0);
578 node_head.assign(nn, 0.0);
579 node_surf_area.assign(nn, 0.0);
580 node_overflow.assign(nn, 0.0);
581 node_overflow_vol.assign(nn, 0.0);
582 n_species = n_species_in;
583 cell_phi.assign(nc * static_cast<std::size_t>(n_species_in), 0.0);
584 }
585};
586
587} // namespace openswmm::fv
588
589#endif // OPENSWMM_ENGINE_FV_NETWORK_MESH_DATA_HPP
The explicit-FV cross-section closure as a single-source POD kernel: one monotone cubic-Hermite table...
Portable scalar closures shared by the host solvers and the device backend: FHWA HEC-5 culvert inlet ...
Cross-section geometry — unified batch + per-element API.
Portable cross-section geometry kernels — one implementation, three consumers (host dynamic wave,...
Definition AcceptedWaterVolumes.hpp:8
@ kNodeJunction
Definition NetworkMeshData.hpp:509
@ kNodeOutfall
Definition NetworkMeshData.hpp:509
@ kNodeVirtual
Definition NetworkMeshData.hpp:509
@ kNodeStorage
Definition NetworkMeshData.hpp:509
constexpr int kNodeVolSamples
Samples in a STORAGE node's flattened depth→volume table.
Definition NetworkMeshData.hpp:58
Definition NodeCoupling2D.cpp:23
Native cross-section geometry and borrowed optional normalized transect tables.
Definition XSectBatch.hpp:119
One section's closure. Fixed-size POD: inline arrays, no pointers.
Definition FvClosureKernels.hpp:89
Cross-section closure for one conduit's cell chain.
Definition NetworkMeshData.hpp:71
double r_full
hydraulic radius when full (ft)
Definition NetworkMeshData.hpp:77
double a_full
area when full (ft²)
Definition NetworkMeshData.hpp:75
double t_slot
Definition NetworkMeshData.hpp:90
uint8_t is_open
open section: no crown, no slot taper
Definition NetworkMeshData.hpp:100
double i1_crown
I₁(y_full) — the table's last entry, cached for the same reason.
Definition NetworkMeshData.hpp:98
double barrel_scale
Definition NetworkMeshData.hpp:81
double y_crown
Definition NetworkMeshData.hpp:86
double a_crown
Definition NetworkMeshData.hpp:95
double y_full
full depth (ft)
Definition NetworkMeshData.hpp:74
FvClosure closure_tbl
Definition NetworkMeshData.hpp:117
double w_max
width at widest point (ft)
Definition NetworkMeshData.hpp:76
int barrels
parallel identical barrels
Definition NetworkMeshData.hpp:102
XSectParams xs
copied scalars; transect/profile table pointers remain borrowed
Definition NetworkMeshData.hpp:72
SoA mesh geometry and topology for the FV network solver.
Definition NetworkMeshData.hpp:123
std::vector< int > cell_geom
index into geom
Definition NetworkMeshData.hpp:129
std::vector< int > conduit_section
index into geom (shared section block)
Definition NetworkMeshData.hpp:264
std::vector< double > node_sur_depth
SURCHARGE_DEPTH above the rim (ft)
Definition NetworkMeshData.hpp:361
std::vector< double > node_ponded_area
ponding area above the rim (ft²)
Definition NetworkMeshData.hpp:360
std::vector< int8_t > face_dir_r
Definition NetworkMeshData.hpp:233
std::vector< double > face_zb
bed elevation at the interface (ft)
Definition NetworkMeshData.hpp:219
int n_faces() const noexcept
Return the logical faces count from its owning vector.
Definition NetworkMeshData.hpp:420
std::vector< double > node_face_sign
+1 flux ENTERS the node, −1 leaves it
Definition NetworkMeshData.hpp:347
std::vector< double > node_full_depth
crown / rim depth (ft)
Definition NetworkMeshData.hpp:359
std::vector< int > face_cr
right cell (-1 ⇒ node on the right)
Definition NetworkMeshData.hpp:162
std::vector< double > node_area
Definition NetworkMeshData.hpp:379
std::vector< double > conduit_loss_outlet
exit loss coefficient K
Definition NetworkMeshData.hpp:272
std::vector< int > conduit_cell_count
cells in conduit row r
Definition NetworkMeshData.hpp:262
std::vector< double > node_face_zb
Definition NetworkMeshData.hpp:352
std::vector< uint8_t > struct_is_dummy
Definition NetworkMeshData.hpp:330
std::vector< int > cell_face0
Definition NetworkMeshData.hpp:147
std::vector< int8_t > cell_side1
Definition NetworkMeshData.hpp:150
std::vector< uint8_t > face_virtual
Definition NetworkMeshData.hpp:238
std::vector< int > cell_conduit
ConduitData row (for reporting)
Definition NetworkMeshData.hpp:130
std::vector< int > node_face_idx
face indices
Definition NetworkMeshData.hpp:346
std::vector< int > struct_n1
upstream node
Definition NetworkMeshData.hpp:325
std::vector< double > node_vol_atop
surface area above dmax (ft²)
Definition NetworkMeshData.hpp:394
std::vector< double > cell_dzdx
Definition NetworkMeshData.hpp:140
std::vector< double > cell_dx
cell length (ft)
Definition NetworkMeshData.hpp:131
std::vector< int > struct_n2
downstream node
Definition NetworkMeshData.hpp:326
std::vector< int > conduit_link
base LinkData index of conduit row r
Definition NetworkMeshData.hpp:263
std::vector< hydkernels::CulvertCurve > conduit_culvert_curve
Definition NetworkMeshData.hpp:277
int n_chains() const noexcept
Return the logical chains count from its owning vector.
Definition NetworkMeshData.hpp:305
std::vector< int > cell_chain
chain of each cell
Definition NetworkMeshData.hpp:295
std::vector< int > face_cl
left cell (-1 ⇒ node on the left)
Definition NetworkMeshData.hpp:161
std::vector< int > conduit_culvert_code
Definition NetworkMeshData.hpp:274
std::vector< int > cell_chain_pos
position within that chain
Definition NetworkMeshData.hpp:296
void deriveFaceGeom()
Populate missing face section indices from adjacent cell geometry.
Definition NetworkMeshData.hpp:194
std::vector< double > node_vol_dmax
depth at the table top (ft)
Definition NetworkMeshData.hpp:393
std::vector< double > conduit_loss_inlet
entrance loss coefficient K
Definition NetworkMeshData.hpp:271
std::vector< double > node_invert
invert elevation (ft)
Definition NetworkMeshData.hpp:358
std::vector< int > conduit_cell_begin
first cell of conduit row r
Definition NetworkMeshData.hpp:261
int n_nodes() const noexcept
Return the logical nodes count from its owning vector.
Definition NetworkMeshData.hpp:427
std::vector< double > conduit_roughness
Manning n.
Definition NetworkMeshData.hpp:269
int n_conduits() const noexcept
Return the logical conduits count from its owning vector.
Definition NetworkMeshData.hpp:434
std::vector< int > face_node
coupled node (-1 for interior faces)
Definition NetworkMeshData.hpp:163
std::vector< double > face_dx
centre-to-centre distance (ft)
Definition NetworkMeshData.hpp:220
std::vector< int8_t > cell_side0
Definition NetworkMeshData.hpp:149
std::vector< int > chain_cells
cell indices in walk order
Definition NetworkMeshData.hpp:293
std::vector< int > node_vol_off
offset into node_vol_tbl, −1 = none
Definition NetworkMeshData.hpp:392
std::vector< double > conduit_rough_factor
g·(n/PHI)² — friction denominator factor
Definition NetworkMeshData.hpp:270
std::vector< double > conduit_slope
bed slope the HEC-5 equations need
Definition NetworkMeshData.hpp:273
std::vector< int > face_vj_node
Definition NetworkMeshData.hpp:246
void clear()
Remove every mesh/topology/geometry entry while retaining vector capacities.
Definition NetworkMeshData.hpp:444
std::vector< uint8_t > conduit_culvert_mitered
Definition NetworkMeshData.hpp:278
std::vector< int8_t > face_dir_l
Definition NetworkMeshData.hpp:232
std::vector< uint8_t > node_kind
Definition NetworkMeshData.hpp:370
std::vector< int > face_culvert
Definition NetworkMeshData.hpp:218
std::vector< uint8_t > node_can_pond
Definition NetworkMeshData.hpp:366
std::vector< int > struct_link
LinkData index.
Definition NetworkMeshData.hpp:324
std::vector< uint8_t > face_gate
Definition NetworkMeshData.hpp:213
std::vector< double > node_vol_tbl
kNodeVolSamples volumes per node
Definition NetworkMeshData.hpp:395
std::vector< int > face_geom
index into geom
Definition NetworkMeshData.hpp:183
std::vector< int > node_face_ptr
[n_nodes + 1] row pointers
Definition NetworkMeshData.hpp:345
int n_cells() const noexcept
Return the logical cells count from its owning vector.
Definition NetworkMeshData.hpp:413
std::vector< int > node_vj_face
Definition NetworkMeshData.hpp:254
std::vector< int > cell_face1
Definition NetworkMeshData.hpp:148
std::vector< FvGeometry > geom
Definition NetworkMeshData.hpp:401
std::vector< int > chain_ptr
[n_chains + 1] row pointers
Definition NetworkMeshData.hpp:292
std::vector< int8_t > chain_dir
cell axis vs. chain direction (±1)
Definition NetworkMeshData.hpp:294
std::vector< double > cell_zb
bed elevation at cell centre (ft)
Definition NetworkMeshData.hpp:132
std::vector< uint8_t > node_dummy_drain
Definition NetworkMeshData.hpp:337
Mutable solver state — the conserved variables and the node volumes.
Definition NetworkMeshData.hpp:519
std::vector< double > cell_q
discharge (cfs)
Definition NetworkMeshData.hpp:523
std::vector< double > node_head
water-surface elevation (ft)
Definition NetworkMeshData.hpp:533
std::vector< double > cell_phi
Definition NetworkMeshData.hpp:543
std::vector< double > cell_h
depth (ft)
Definition NetworkMeshData.hpp:527
int n_species
Definition NetworkMeshData.hpp:544
std::vector< uint8_t > cell_tpa
Definition NetworkMeshData.hpp:555
std::vector< double > cell_a
flow area (ft²)
Definition NetworkMeshData.hpp:522
std::vector< double > node_surf_area
storage area used this routing step (ft²)
Definition NetworkMeshData.hpp:534
std::vector< double > node_volume
stored volume (ft³)
Definition NetworkMeshData.hpp:532
std::vector< double > node_overflow
flooding rate this substep (cfs)
Definition NetworkMeshData.hpp:535
std::vector< double > node_overflow_vol
Definition NetworkMeshData.hpp:539
void resize(int n_cells, int n_nodes, int n_species_in)
Reset conserved, derived and transport arrays to requested zero-filled dimensions.
Definition NetworkMeshData.hpp:571