OpenSWMM Engine  6.0.0-alpha.4
Data-oriented, plugin-extensible SWMM Engine (6.0.0-alpha.4)
Loading...
Searching...
No Matches
ArdEngine.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
63
64#ifndef OPENSWMM_ENGINE_TRANSPORT_ARD_ENGINE_HPP
65#define OPENSWMM_ENGINE_TRANSPORT_ARD_ENGINE_HPP
66
67#include <fstream>
68#include <string>
69#include <vector>
70
73
74namespace openswmm {
76}
77
78namespace openswmm::transport {
79
80class ArdEngine {
81public:
88 bool init(SimulationContext& ctx);
89
98 void step(SimulationContext& ctx, double dt);
99
100 bool initialized() const noexcept { return initialized_; }
101 const std::vector<std::string>& warnings() const noexcept { return warnings_; }
102
115
118 std::vector<double> totalMass(const SimulationContext& ctx) const;
119
120private:
121 // Continuity-consistent face fluxes from the link/node fields of the
122 // current routing step (plan §3.2): within each conduit the face flux
123 // ramps linearly so every cell receives an equal share of the conduit's
124 // volume change, and the two end faces carry the flux the node exchange
125 // actually saw. Splice faces (virtual junctions) take the mean of the
126 // two conduits' end values.
127 void projectHydraulics(SimulationContext& ctx, double dt);
128
129 // One advection substep of length dt_sub: kernel reconstruction + FCT on
130 // interior faces, donor-upwinded node boundary fluxes, cell mass/area
131 // update, node CSTR update (external loads prorated by dt_sub / dt_step),
132 // then the implicit dispersion solve over the updated cells (E3,
133 // sequential Lie split: one full advection step, then one full
134 // dispersion step — NOT Strang, which would halve the advection).
135 void substep(SimulationContext& ctx, double dt_sub, double load_frac);
136
137 // E3: refresh cell_disp_ from the routing step's hydraulics. Per-conduit
138 // overrides always win; otherwise VALUE mode broadcasts the global
139 // coefficient and FISCHER mode evaluates D = 0.011 v²B²/(Y·U*),
140 // U* = √(g·Y·S) from the link fields (all internal ft units).
141 void updateDispersion(SimulationContext& ctx);
142
143 void publish(SimulationContext& ctx);
144
147
148 // Projected per-face volumetric flux for the current routing step and the
149 // per-cell start-of-step areas it is consistent with.
150 std::vector<double> face_q_;
151
152 // Node stores: species mass [node * ns + s] and water volume the mass
153 // sits in (tracked from the same fluxes, so node concentration is
154 // self-consistent between substeps).
155 std::vector<double> node_mass_;
156 std::vector<double> node_vol_;
157
158 // Lateral loads at VIRTUAL junctions (plans/VJ_LATERAL_INFLOW_PLAN_
159 // 2026-09-04.md §E4). A virtual junction owns no faces — its conduits
160 // were spliced into one interior face — so a node store there could
161 // never discharge and the per-step resync zeroed it: the load was
162 // destroyed. Its water and mass go straight into the two cells adjoining
163 // the splice, half each, mirroring the hydraulic solver's cell_qlat_
164 // split. Rebuilt every substep; has_vj_src_ gates the whole path so a
165 // deck without a fed virtual junction executes no new arithmetic.
166 std::vector<double> cell_src_vol_;
167 std::vector<double> cell_src_mass_;
168 bool has_vj_src_ = false;
169
170 // Kernel scratch (sized once in init; see SpeciesKernelView).
171 std::vector<double> f_mass_, f_sstar_, f_phi_l_, f_phi_r_, f_phi_flux_,
172 cell_slope_, lo_flux_, anti_flux_, td_, anew_, rplus_, rminus_, cell_u_;
173 std::vector<fv::kernels::FaceState> f_state_l_, f_state_r_;
174 std::vector<fv::kernels::FaceFlux> f_flux_;
175 std::vector<char> cell_active_;
176 std::vector<int> active_faces_;
177
178 // E5a: transport boundaries/sources resolved onto the mesh at init.
179 // Boundaries: the node's external inflow water carries the species at
180 // bc_now_ (evaluated once per routing step: VALUE or timeseries).
181 // Sources: internal-rate mass distributed over the conduit's wet cells.
182 void updateTransportRows(SimulationContext& ctx);
183 std::vector<int> bc_node_, bc_srow_, bc_ts_;
184 std::vector<double> bc_value_, bc_now_;
185 std::vector<int> src_crow_, src_srow_, src_ts_;
186 std::vector<double> src_value_, src_len_, src_now_;
187
188 // E3 dispersion state. disp_active_ gates the whole path: when false the
189 // substep passes no per-cell array and dispersionSolve early-outs — the
190 // pre-E3 behavior, bit-identical for existing ARD decks.
191 bool disp_active_ = false;
192 int disp_mode_ = 0;
193 double disp_global_ft_ = 0.0;
194 std::vector<double> conduit_disp_ft_;
195 std::vector<double> cell_disp_;
196
197 // E5b: per-cell CSV sidecar ([TRANSPORT_OPTIONS] DETAILED_OUTPUT).
198 // Written every routing step (documented decision — a detail feature
199 // for short diagnostic runs; cadence control can come later if sizes
200 // demand it). Columns: time_s, element, kind (L link cell / N node
201 // store), cell index, species name, concentration.
202 void writeDetailRows(SimulationContext& ctx);
203 std::ofstream detail_out_;
204 bool detail_active_ = false;
205 double detail_time_s_ = 0.0;
206
209 int age_row_ = -1;
210
213 int nm_ = 0;
214
217 void applyHeatFluxes(SimulationContext& ctx, double dt);
218
221 void applyBedSoluteExchange(SimulationContext& ctx, double dt);
222
227 int cellLink(std::size_t cell) const noexcept;
228
233 int temp_row_ = -1;
234
235 std::vector<std::string> warnings_;
236 bool initialized_ = false;
237 bool warned_cfl_clamp_ = false;
238};
239
240} // namespace openswmm::transport
241
242#endif // OPENSWMM_ENGINE_TRANSPORT_ARD_ENGINE_HPP
Single-source scalar kernels for the explicit FV 1D network solver.
SoA storage for the 1D finite-volume network mesh and its state.
Definition ArdEngine.hpp:80
void step(SimulationContext &ctx, double dt)
One transport step over the routing step dt.
Definition ArdEngine.cpp:1161
bool initialized() const noexcept
Definition ArdEngine.hpp:100
const std::vector< std::string > & warnings() const noexcept
Definition ArdEngine.hpp:101
bool init(SimulationContext &ctx)
Build the transport mesh and size all state; call once after the model is fully resolved (Router::ini...
Definition ArdEngine.cpp:97
void absorbTreatedNodeConc(SimulationContext &ctx)
E5b treatment interop: absorb treated node concentrations back into the node stores.
Definition ArdEngine.cpp:1284
std::vector< double > totalMass(const SimulationContext &ctx) const
Definition ArdEngine.cpp:1760
Definition ExplicitFvSolver.hpp:52
Definition NodeCoupling.cpp:16
Central, reentrant simulation context.
Definition SimulationContext.hpp:353
SoA mesh geometry and topology for the FV network solver.
Definition NetworkMeshData.hpp:176
Mutable solver state — the conserved variables and the node volumes.
Definition NetworkMeshData.hpp:489