OpenSWMM Engine  6.0.0-alpha.4
Data-oriented, plugin-extensible SWMM Engine (6.0.0-alpha.4)
Loading...
Searching...
No Matches
SegmentStore.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
44
45#ifndef OPENSWMM_QUALITY_LARD_SEGMENT_STORE_HPP
46#define OPENSWMM_QUALITY_LARD_SEGMENT_STORE_HPP
48
49#include <algorithm>
50#include <cstddef>
51#include <vector>
52
53namespace openswmm {
54namespace lard {
55
58constexpr int kMaxSegmentsPerLink = 100;
59
64constexpr double kMergeAtol = 1.0e-6;
65constexpr double kMergeRtol = 1.0e-4;
66
68public:
83 a.fixed(head_, count_, vol_, conc_);
84 for (int l = 0; l < nl_; ++l)
85 if (head_[l] < 0 || head_[l] >= cap_ || count_[l] < 0 || count_[l] > cap_)
86 throw std::runtime_error("Invalid LARD checkpoint ring.");
87 for (double v : vol_)
88 if (!std::isfinite(v) || v < 0)
89 throw std::runtime_error("Invalid LARD checkpoint water volume.");
90 for (double v : conc_)
91 if (!std::isfinite(v))
92 throw std::runtime_error("Nonfinite LARD checkpoint concentration.");
93 }
94
106 void resize(int n_links, int n_species, int cap = kMaxSegmentsPerLink) {
107 nl_ = n_links;
108 ns_ = n_species;
109 cap_ = cap;
110 const std::size_t slab = static_cast<std::size_t>(nl_) * static_cast<std::size_t>(cap_);
111 head_.assign(static_cast<std::size_t>(nl_), 0);
112 count_.assign(static_cast<std::size_t>(nl_), 0);
113 vol_.assign(slab, 0.0);
114 conc_.assign(slab * static_cast<std::size_t>(ns_ > 0 ? ns_ : 1), 0.0);
115 }
116
122 int count(int l) const { return count_[static_cast<std::size_t>(l)]; }
127 int species() const { return ns_; }
132 int capacity() const { return cap_; }
133
140 double total_volume(int l) const {
141 double v = 0.0;
142 for (int i = 0; i < count(l); ++i)
143 v += vol_[phys(l, i)];
144 return v;
145 }
146
153 double seg_volume(int l, int i) const { return vol_[phys(l, i)]; }
162 double seg_conc(int l, int i, int s) const { return conc_[cidx(l, i, s)]; }
175 void set_seg_conc(int l, int i, int s, double c) { conc_[cidx(l, i, s)] = c; }
176
188 void mean_conc(int l, double* out) const {
189 for (int s = 0; s < ns_; ++s)
190 out[s] = 0.0;
191 double v = 0.0;
192 for (int i = 0; i < count(l); ++i) {
193 const double sv = vol_[phys(l, i)];
194 v += sv;
195 for (int s = 0; s < ns_; ++s)
196 out[s] += sv * conc_[cidx(l, i, s)];
197 }
198 if (v > 0.0)
199 for (int s = 0; s < ns_; ++s)
200 out[s] /= v;
201 }
202
211 void clear_link(int l) {
212 head_[static_cast<std::size_t>(l)] = 0;
213 count_[static_cast<std::size_t>(l)] = 0;
214 }
215
235 double drain_back(int l, double vol_target, double* mass_out) {
236 double drained = 0.0;
237 while (vol_target - drained > 0.0 && count(l) > 0) {
238 const int i = count(l) - 1; // back
239 const std::size_t p = phys(l, i);
240 const double take = std::min(vol_[p], vol_target - drained);
241 for (int s = 0; s < ns_; ++s)
242 mass_out[s] += take * conc_[cidx(l, i, s)];
243 vol_[p] -= take;
244 drained += take;
245 if (vol_[p] <= 0.0)
246 count_[static_cast<std::size_t>(l)] -= 1;
247 }
248 return drained;
249 }
250
267 double drain_front(int l, double vol_target, double* mass_out) {
268 double drained = 0.0;
269 while (vol_target - drained > 0.0 && count(l) > 0) {
270 const std::size_t p = phys(l, 0);
271 const double take = std::min(vol_[p], vol_target - drained);
272 for (int s = 0; s < ns_; ++s)
273 mass_out[s] += take * conc_[cidx(l, 0, s)];
274 vol_[p] -= take;
275 drained += take;
276 if (vol_[p] <= 0.0) {
277 head_[static_cast<std::size_t>(l)] =
278 (head_[static_cast<std::size_t>(l)] + 1) % cap_;
279 count_[static_cast<std::size_t>(l)] -= 1;
280 }
281 }
282 return drained;
283 }
284
305 void push_front(int l, double volume, const double* c) {
306 if (volume <= 0.0)
307 return;
308 if (count(l) > 0 && mergeable(l, 0, c)) {
309 merge_into(l, 0, volume, c);
310 return;
311 }
312 if (count(l) >= cap_) {
313 // D-L5: merge front pair, mass-conservative, then push.
314 merge_front_pair(l);
315 }
316 head_[static_cast<std::size_t>(l)] = (head_[static_cast<std::size_t>(l)] - 1 + cap_) % cap_;
317 count_[static_cast<std::size_t>(l)] += 1;
318 const std::size_t p = phys(l, 0);
319 vol_[p] = volume;
320 for (int s = 0; s < ns_; ++s)
321 conc_[cidx(l, 0, s)] = c[s];
322 }
323
333 void reverse(int l) {
334 const int n = count(l);
335 for (int i = 0, j = n - 1; i < j; ++i, --j) {
336 std::swap(vol_[phys(l, i)], vol_[phys(l, j)]);
337 for (int s = 0; s < ns_; ++s)
338 std::swap(conc_[cidx(l, i, s)], conc_[cidx(l, j, s)]);
339 }
340 }
341
357 void add_species(int s, double delta) {
358 for (int l = 0; l < nl_; ++l)
359 for (int i = 0; i < count(l); ++i)
360 conc_[cidx(l, i, s)] += delta;
361 }
362
379 double decay_species(int s, double factor) {
380 double removed = 0.0;
381 for (int l = 0; l < nl_; ++l)
382 for (int i = 0; i < count(l); ++i) {
383 const std::size_t ci = cidx(l, i, s);
384 removed += vol_[phys(l, i)] * conc_[ci] * (1.0 - factor);
385 conc_[ci] *= factor;
386 }
387 return removed;
388 }
389
390private:
400 std::size_t phys(int l, int i) const {
401 const int slot = (head_[static_cast<std::size_t>(l)] + i) % cap_;
402 return static_cast<std::size_t>(l) * static_cast<std::size_t>(cap_) +
403 static_cast<std::size_t>(slot);
404 }
415 std::size_t cidx(int l, int i, int s) const {
416 return static_cast<std::size_t>(s) * static_cast<std::size_t>(nl_) *
417 static_cast<std::size_t>(cap_) +
418 phys(l, i);
419 }
420
431 bool mergeable(int l, int i, const double* c) const {
432 for (int s = 0; s < ns_; ++s) {
433 const double cf = conc_[cidx(l, i, s)];
434 if (std::abs(c[s] - cf) > kMergeAtol + kMergeRtol * std::abs(cf))
435 return false;
436 }
437 return true;
438 }
439
452 void merge_into(int l, int i, double volume, const double* c) {
453 const std::size_t p = phys(l, i);
454 const double v0 = vol_[p];
455 const double vt = v0 + volume;
456 if (vt <= 0.0)
457 return;
458 for (int s = 0; s < ns_; ++s) {
459 const std::size_t ci = cidx(l, i, s);
460 conc_[ci] = (conc_[ci] * v0 + c[s] * volume) / vt;
461 }
462 vol_[p] = vt;
463 }
464
473 void merge_front_pair(int l) {
474 if (count(l) < 2)
475 return;
476 // Fold segment 1 into segment 0 (volume-weighted), drop slot 1 by
477 // moving the head forward over it after copying 0 into 1.
478 const std::size_t p0 = phys(l, 0);
479 const std::size_t p1 = phys(l, 1);
480 const double v0 = vol_[p0], v1 = vol_[p1], vt = v0 + v1;
481 if (vt > 0.0)
482 for (int s = 0; s < ns_; ++s) {
483 const std::size_t c0 = cidx(l, 0, s);
484 const std::size_t c1 = cidx(l, 1, s);
485 conc_[c1] = (conc_[c0] * v0 + conc_[c1] * v1) / vt;
486 }
487 vol_[p1] = vt;
488 head_[static_cast<std::size_t>(l)] = (head_[static_cast<std::size_t>(l)] + 1) % cap_;
489 count_[static_cast<std::size_t>(l)] -= 1;
490 }
491
492 int nl_ = 0, ns_ = 0, cap_ = kMaxSegmentsPerLink;
493 std::vector<int> head_, count_;
494 std::vector<double> vol_;
495 std::vector<double> conc_;
496};
497
498} // namespace lard
499} // namespace openswmm
500
501#endif // OPENSWMM_QUALITY_LARD_SEGMENT_STORE_HPP
Versioned archives of completed source-boundary state.
Definition CompletedSourceArchive.hpp:25
Definition SegmentStore.hpp:67
double drain_front(int l, double vol_target, double *mass_out)
Same as drain_back but from the FRONT (upstream end) — the volume- reconciliation path for a link tha...
Definition SegmentStore.hpp:267
double seg_conc(int l, int i, int s) const
Read one species concentration in a live segment.
Definition SegmentStore.hpp:162
double decay_species(int s, double factor)
Exact-exponential decay on one species over every LIVE segment.
Definition SegmentStore.hpp:379
int count(int l) const
Read the number of live segments in one ring.
Definition SegmentStore.hpp:122
void resize(int n_links, int n_species, int cap=kMaxSegmentsPerLink)
Replace all segment storage with empty rings at new dimensions.
Definition SegmentStore.hpp:106
double drain_back(int l, double vol_target, double *mass_out)
Drain up to vol_target from the BACK (downstream end).
Definition SegmentStore.hpp:235
void checkpoint(detail::CompletedSourceArchive &a)
Serialize or restore fixed-sized segment ring arrays and validate restored numerical state.
Definition SegmentStore.hpp:82
void push_front(int l, double volume, const double *c)
Release a new segment at the FRONT (upstream end).
Definition SegmentStore.hpp:305
void mean_conc(int l, double *out) const
Volume-weighted mean concentration over the link's segments. out has ns_ entries; an empty link repor...
Definition SegmentStore.hpp:188
void set_seg_conc(int l, int i, int s, double c)
X3b: the RWPT exchange rewrites segment concentrations in place (mass-conserving by its own construct...
Definition SegmentStore.hpp:175
void add_species(int s, double delta)
Add a constant to one species on every LIVE segment.
Definition SegmentStore.hpp:357
void reverse(int l)
§4.4 flow reversal: front becomes back, in place.
Definition SegmentStore.hpp:333
double total_volume(int l) const
Sum live segment water volumes for one link.
Definition SegmentStore.hpp:140
int capacity() const
Read slots reserved per link ring.
Definition SegmentStore.hpp:132
void clear_link(int l)
Remove one whole link's contents (init/re-seed).
Definition SegmentStore.hpp:211
double seg_volume(int l, int i) const
Read a live segment's stored water volume.
Definition SegmentStore.hpp:153
int species() const
Read initialized concentration row count.
Definition SegmentStore.hpp:127
Definition LagrangianSolver.hpp:125
constexpr double kMergeAtol
Definition SegmentStore.hpp:64
constexpr double kMergeRtol
Definition SegmentStore.hpp:65
constexpr int kMaxSegmentsPerLink
Definition SegmentStore.hpp:58
OPENSWMM_POND_HD double volume(double x, double a_eq, double band)
Pond volume held between the rim and depth x above it.
Definition PondModel.hpp:72
Definition ExchangeLaw.hpp:60