OpenSWMM Engine  6.0.0-alpha.4
Data-oriented, plugin-extensible SWMM Engine (6.0.0-alpha.4)
Loading...
Searching...
No Matches
PressurizedHeadSolver.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
94
95#ifndef OPENSWMM_ENGINE_FV_PRESSURIZED_HEAD_SOLVER_HPP
96#define OPENSWMM_ENGINE_FV_PRESSURIZED_HEAD_SOLVER_HPP
97
98#include <cstdint>
99#include <vector>
100
101#include "FvKernels.hpp"
102#include "INetworkSolver.hpp"
103#include "NetworkMeshData.hpp"
104
105namespace openswmm::fv {
106
111 const NetworkMeshData* mesh = nullptr;
113 const FvOptions* opts = nullptr;
114 const FvStepForcing* forcing = nullptr;
115
116 // Face scratch (owned by ExplicitFvSolver).
117 double* f_mass = nullptr;
118 double* f_sstar = nullptr;
122
123 // Cell scratch.
124 const double* cell_eta = nullptr;
125 double* cell_u = nullptr;
126
131 const std::vector<double>* uf_grad = nullptr;
132
133 const std::vector<int>* active_faces = nullptr;
134 const std::vector<char>* cell_active = nullptr;
135
136 // Node machinery (owned by ExplicitFvSolver).
137 const std::vector<double>* node_qstruct = nullptr;
138 std::vector<double>* node_carry = nullptr;
139 const std::vector<std::uint8_t>* node_alg = nullptr;
140 const std::vector<double>* node_vfull = nullptr;
141 const std::vector<std::uint8_t>* node_lat_div = nullptr;
142 const std::vector<double>* cell_qlat = nullptr;
143};
144
146public:
169 enum : std::uint8_t { kNone = 0, kFull = 1, kDelta = 2 };
170
175 bool classify(const PressurizedView& v);
176
182 void solve(const PressurizedView& v, double dt);
183
188 void finalizeCells(const PressurizedView& v);
189
192 const std::vector<std::uint8_t>& foldedNodes() const noexcept {
193 return node_fold_;
194 }
195
196 bool anyImplicit() const noexcept { return !imp_faces_.empty(); }
197
198 // -- static predicates shared with the census edit ----------------------
199
206 static bool cellPressurized(const NetworkMeshData& mesh,
207 const NetworkStateData& state, int cell) {
208 const auto uc = static_cast<std::size_t>(cell);
209 if (!state.cell_tpa.empty()) return state.cell_tpa[uc] != 0;
210 const FvGeometry& g =
211 mesh.geom[static_cast<std::size_t>(mesh.cell_geom[uc])];
212 return !g.is_open && state.cell_h[uc] >= g.y_crown;
213 }
214
221 static std::uint8_t faceModeOf(const NetworkMeshData& mesh,
222 const NetworkStateData& state, int f) {
223 const auto uf = static_cast<std::size_t>(f);
224 if (mesh.face_gate[uf] != 0) return kNone;
225 if (mesh.face_culvert[uf] >= 0) return kNone;
226 const int cl = mesh.face_cl[uf];
227 const int cr = mesh.face_cr[uf];
228 const int nd = mesh.face_node[uf];
229 if (nd < 0 && (cl < 0 || cr < 0)) return kNone;
230
231 bool pl = false, pr = false, cell_side_press = false;
232 if (cl >= 0) {
233 pl = cellPressurized(mesh, state, cl);
234 cell_side_press = cell_side_press || pl;
235 }
236 if (cr >= 0) {
237 pr = cellPressurized(mesh, state, cr);
238 cell_side_press = cell_side_press || pr;
239 }
240 if (nd >= 0) {
241 const bool gp = ghostPressurized(mesh, state, f);
242 if (cl < 0) pl = gp;
243 if (cr < 0) pr = gp;
244 }
245 if (!cell_side_press) return kNone;
246 return (pl && pr) ? kFull : kDelta;
247 }
248
250 static bool ghostPressurized(const NetworkMeshData& mesh,
251 const NetworkStateData& state, int f) {
252 const auto uf = static_cast<std::size_t>(f);
253 const int nd = mesh.face_node[uf];
254 if (nd < 0) return false;
255 const int other = (mesh.face_cl[uf] >= 0) ? mesh.face_cl[uf]
256 : mesh.face_cr[uf];
257 if (other < 0) return false;
258 const FvGeometry& g = mesh.geom[static_cast<std::size_t>(
259 mesh.cell_geom[static_cast<std::size_t>(other)])];
260 if (g.is_open) return false;
261 // TPA (issue #156 Phase 5): a SEALED node against a flagged interior
262 // cell carries the pressurized column at sub-atmospheric head — the
263 // ghost is pressurized even though head < crown (the same rule
264 // faceSide uses for its ghost regime). Without this the sealed
265 // sub-crown junction's faces degrade to kDelta, the fold pass skips
266 // the node, and the implicit path breaks the column (the P4 pinned
267 // known-issue).
268 if (!state.cell_tpa.empty() &&
269 state.cell_tpa[static_cast<std::size_t>(other)] != 0 &&
270 mesh.node_sur_depth[static_cast<std::size_t>(nd)] > 0.0)
271 return true;
272 const double hg =
273 state.node_head[static_cast<std::size_t>(nd)] - mesh.face_zb[uf];
274 return hg >= g.y_crown;
275 }
276
277private:
278 struct ImpFace {
279 int face = -1;
280 std::uint8_t mode = kNone;
281 // Unknown index of each side (-1 = known/Dirichlet). Left/right in
282 // the FACE frame, so the assembly's sign convention is the flux's.
283 int unk_l = -1;
284 int unk_r = -1;
285 // Dirichlet head of a known side (valid where unk < 0).
286 double dir_l = 0.0;
287 double dir_r = 0.0;
288 // Time-n heads of both sides (the DELTA form needs them).
289 double eta_l = 0.0;
290 double eta_r = 0.0;
291 double alpha = 1.0;
292 double cond = 0.0;
293 double qstar = 0.0;
294 };
295
296 // classify() products, persistent to avoid per-substep allocation.
297 std::vector<std::uint8_t> cell_press_;
298 std::vector<std::uint8_t> node_fold_;
299 std::vector<int> cell_unk_;
300 std::vector<int> node_unk_;
301 std::vector<ImpFace> imp_faces_;
302
303 // Unknown table: entity id (cell c → c; node n → n_cells + n), time-n
304 // head, storage coefficient (T·Δx/Δt for cells, 0 for folded junctions —
305 // filled with Δt at solve time), and component id.
306 std::vector<int> unk_entity_;
307 std::vector<double> unk_eta0_;
308 std::vector<double> unk_store_;
309 std::vector<double> unk_rhs_ext_;
310 std::vector<int> unk_comp_;
311 std::vector<double> unk_head_;
312
313 // Union-find over unknowns.
314 std::vector<int> uf_parent_;
315 int ufFind(int i);
316 void ufUnion(int a, int b);
317
318 // Per-component scratch.
319 std::vector<int> comp_ptr_, comp_unks_;
320 std::vector<double> thom_a_, thom_b_, thom_c_, thom_d_;
321 std::vector<int> path_order_, degree_, adj_ptr_, adj_idx_;
322 std::vector<double> adj_c_;
323 std::vector<double> cg_r_, cg_p_, cg_ap_, cg_z_;
324
325 void buildComponents();
326 void assembleRhs(const PressurizedView& v, double dt);
327 void solveComponent(int comp, const PressurizedView& v, double dt);
328 void backSubstitute(const PressurizedView& v);
329
333 void linearSolve(const std::vector<int>& unks,
334 const std::vector<double>& diag,
335 const std::vector<double>& rhs);
336
337 // diag/rhs assembled per solve (indexed by unknown).
338 std::vector<double> diag_, rhs_;
339 std::vector<std::uint8_t> unk_dirichlet_;
340 std::vector<double> unk_dirvalue_;
341};
342
343} // namespace openswmm::fv
344
345#endif // OPENSWMM_ENGINE_FV_PRESSURIZED_HEAD_SOLVER_HPP
Single-source scalar kernels for the explicit FV 1D network solver.
Backend-neutral interface for the explicit FV 1D network integrator.
SoA storage for the 1D finite-volume network mesh and its state.
Definition PressurizedHeadSolver.hpp:145
void solve(const PressurizedView &v, double dt)
Definition PressurizedHeadSolver.cpp:250
static bool cellPressurized(const NetworkMeshData &mesh, const NetworkStateData &state, int cell)
Definition PressurizedHeadSolver.hpp:206
@ kDelta
Definition PressurizedHeadSolver.hpp:169
@ kNone
Definition PressurizedHeadSolver.hpp:169
@ kFull
Definition PressurizedHeadSolver.hpp:169
bool classify(const PressurizedView &v)
Definition PressurizedHeadSolver.cpp:136
void finalizeCells(const PressurizedView &v)
Definition PressurizedHeadSolver.cpp:805
const std::vector< std::uint8_t > & foldedNodes() const noexcept
Definition PressurizedHeadSolver.hpp:192
bool anyImplicit() const noexcept
Definition PressurizedHeadSolver.hpp:196
static std::uint8_t faceModeOf(const NetworkMeshData &mesh, const NetworkStateData &state, int f)
Definition PressurizedHeadSolver.hpp:221
static bool ghostPressurized(const NetworkMeshData &mesh, const NetworkStateData &state, int f)
Is the node-side ghost of face f pressurized?
Definition PressurizedHeadSolver.hpp:250
Definition ExplicitFvSolver.cpp:25
Cross-section closure for one conduit's cell chain.
Definition NetworkMeshData.hpp:77
Knobs for FLOW_ROUTING FV.
Definition FvOptions.hpp:123
Definition INetworkSolver.hpp:53
SoA mesh geometry and topology for the FV network solver.
Definition NetworkMeshData.hpp:176
std::vector< int > cell_geom
index into geom
Definition NetworkMeshData.hpp:182
std::vector< double > node_sur_depth
SURCHARGE_DEPTH above the rim (ft)
Definition NetworkMeshData.hpp:392
std::vector< double > face_zb
bed elevation at the interface (ft)
Definition NetworkMeshData.hpp:272
std::vector< int > face_cr
right cell (-1 ⇒ node on the right)
Definition NetworkMeshData.hpp:215
std::vector< int > face_cl
left cell (-1 ⇒ node on the left)
Definition NetworkMeshData.hpp:214
std::vector< int > face_node
coupled node (-1 for interior faces)
Definition NetworkMeshData.hpp:216
std::vector< int > face_culvert
Definition NetworkMeshData.hpp:271
std::vector< uint8_t > face_gate
Definition NetworkMeshData.hpp:266
std::vector< FvGeometry > geom
Definition NetworkMeshData.hpp:432
Mutable solver state — the conserved variables and the node volumes.
Definition NetworkMeshData.hpp:489
std::vector< double > node_head
water-surface elevation (ft)
Definition NetworkMeshData.hpp:503
std::vector< double > cell_h
depth (ft)
Definition NetworkMeshData.hpp:497
std::vector< uint8_t > cell_tpa
Definition NetworkMeshData.hpp:525
Definition PressurizedHeadSolver.hpp:110
const std::vector< int > * active_faces
Definition PressurizedHeadSolver.hpp:133
kernels::FaceState * f_state_r
Definition PressurizedHeadSolver.hpp:120
NetworkStateData * state
Definition PressurizedHeadSolver.hpp:112
const std::vector< std::uint8_t > * node_lat_div
Definition PressurizedHeadSolver.hpp:141
const std::vector< double > * uf_grad
Definition PressurizedHeadSolver.hpp:131
const double * cell_eta
Definition PressurizedHeadSolver.hpp:124
const std::vector< double > * cell_qlat
Definition PressurizedHeadSolver.hpp:142
const FvStepForcing * forcing
Definition PressurizedHeadSolver.hpp:114
const FvOptions * opts
Definition PressurizedHeadSolver.hpp:113
const NetworkMeshData * mesh
Definition PressurizedHeadSolver.hpp:111
const std::vector< double > * node_qstruct
Definition PressurizedHeadSolver.hpp:137
kernels::FaceFlux * f_flux
Definition PressurizedHeadSolver.hpp:121
std::vector< double > * node_carry
Definition PressurizedHeadSolver.hpp:138
double * cell_u
Definition PressurizedHeadSolver.hpp:125
const std::vector< std::uint8_t > * node_alg
Definition PressurizedHeadSolver.hpp:139
const std::vector< double > * node_vfull
Definition PressurizedHeadSolver.hpp:140
double * f_sstar
Definition PressurizedHeadSolver.hpp:118
kernels::FaceState * f_state_l
Definition PressurizedHeadSolver.hpp:119
double * f_mass
Definition PressurizedHeadSolver.hpp:117
const std::vector< char > * cell_active
Definition PressurizedHeadSolver.hpp:134
Result of a face flux evaluation.
Definition FvKernels.hpp:407
One side of a face after hydrostatic reconstruction.
Definition FvKernels.hpp:398