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
47
48#include <algorithm>
49#include <cstddef>
50#include <vector>
51
52namespace openswmm {
53namespace lard {
54
57constexpr int kMaxSegmentsPerLink = 100;
58
63constexpr double kMergeAtol = 1.0e-6;
64constexpr double kMergeRtol = 1.0e-4;
65
67public:
68 void resize(int n_links, int n_species, int cap = kMaxSegmentsPerLink) {
69 nl_ = n_links;
70 ns_ = n_species;
71 cap_ = cap;
72 const std::size_t slab =
73 static_cast<std::size_t>(nl_) * static_cast<std::size_t>(cap_);
74 head_.assign(static_cast<std::size_t>(nl_), 0);
75 count_.assign(static_cast<std::size_t>(nl_), 0);
76 vol_.assign(slab, 0.0);
77 conc_.assign(slab * static_cast<std::size_t>(ns_ > 0 ? ns_ : 1), 0.0);
78 }
79
80 int count(int l) const { return count_[static_cast<std::size_t>(l)]; }
81 int species() const { return ns_; }
82 int capacity() const { return cap_; }
83
84 double total_volume(int l) const {
85 double v = 0.0;
86 for (int i = 0; i < count(l); ++i) v += vol_[phys(l, i)];
87 return v;
88 }
89
90 double seg_volume(int l, int i) const { return vol_[phys(l, i)]; }
91 double seg_conc(int l, int i, int s) const { return conc_[cidx(l, i, s)]; }
95 void set_seg_conc(int l, int i, int s, double c) {
96 conc_[cidx(l, i, s)] = c;
97 }
98
101 void mean_conc(int l, double* out) const {
102 for (int s = 0; s < ns_; ++s) out[s] = 0.0;
103 double v = 0.0;
104 for (int i = 0; i < count(l); ++i) {
105 const double sv = vol_[phys(l, i)];
106 v += sv;
107 for (int s = 0; s < ns_; ++s) out[s] += sv * conc_[cidx(l, i, s)];
108 }
109 if (v > 0.0)
110 for (int s = 0; s < ns_; ++s) out[s] /= v;
111 }
112
114 void clear_link(int l) {
115 head_[static_cast<std::size_t>(l)] = 0;
116 count_[static_cast<std::size_t>(l)] = 0;
117 }
118
126 double drain_back(int l, double vol_target, double* mass_out) {
127 double drained = 0.0;
128 while (vol_target - drained > 0.0 && count(l) > 0) {
129 const int i = count(l) - 1; // back
130 const std::size_t p = phys(l, i);
131 const double take = std::min(vol_[p], vol_target - drained);
132 for (int s = 0; s < ns_; ++s)
133 mass_out[s] += take * conc_[cidx(l, i, s)];
134 vol_[p] -= take;
135 drained += take;
136 if (vol_[p] <= 0.0) count_[static_cast<std::size_t>(l)] -= 1;
137 }
138 return drained;
139 }
140
143 double drain_front(int l, double vol_target, double* mass_out) {
144 double drained = 0.0;
145 while (vol_target - drained > 0.0 && count(l) > 0) {
146 const std::size_t p = phys(l, 0);
147 const double take = std::min(vol_[p], vol_target - drained);
148 for (int s = 0; s < ns_; ++s)
149 mass_out[s] += take * conc_[cidx(l, 0, s)];
150 vol_[p] -= take;
151 drained += take;
152 if (vol_[p] <= 0.0) {
153 head_[static_cast<std::size_t>(l)] =
154 (head_[static_cast<std::size_t>(l)] + 1) % cap_;
155 count_[static_cast<std::size_t>(l)] -= 1;
156 }
157 }
158 return drained;
159 }
160
168 void push_front(int l, double volume, const double* c) {
169 if (volume <= 0.0) return;
170 if (count(l) > 0 && mergeable(l, 0, c)) {
171 merge_into(l, 0, volume, c);
172 return;
173 }
174 if (count(l) >= cap_) {
175 // D-L5: merge front pair, mass-conservative, then push.
176 merge_front_pair(l);
177 }
178 head_[static_cast<std::size_t>(l)] =
179 (head_[static_cast<std::size_t>(l)] - 1 + cap_) % cap_;
180 count_[static_cast<std::size_t>(l)] += 1;
181 const std::size_t p = phys(l, 0);
182 vol_[p] = volume;
183 for (int s = 0; s < ns_; ++s) conc_[cidx(l, 0, s)] = c[s];
184 }
185
187 void reverse(int l) {
188 const int n = count(l);
189 for (int i = 0, j = n - 1; i < j; ++i, --j) {
190 std::swap(vol_[phys(l, i)], vol_[phys(l, j)]);
191 for (int s = 0; s < ns_; ++s)
192 std::swap(conc_[cidx(l, i, s)], conc_[cidx(l, j, s)]);
193 }
194 }
195
203 void add_species(int s, double delta) {
204 for (int l = 0; l < nl_; ++l)
205 for (int i = 0; i < count(l); ++i) conc_[cidx(l, i, s)] += delta;
206 }
207
214 double decay_species(int s, double factor) {
215 double removed = 0.0;
216 for (int l = 0; l < nl_; ++l)
217 for (int i = 0; i < count(l); ++i) {
218 const std::size_t ci = cidx(l, i, s);
219 removed += vol_[phys(l, i)] * conc_[ci] * (1.0 - factor);
220 conc_[ci] *= factor;
221 }
222 return removed;
223 }
224
225private:
227 std::size_t phys(int l, int i) const {
228 const int slot = (head_[static_cast<std::size_t>(l)] + i) % cap_;
229 return static_cast<std::size_t>(l) * static_cast<std::size_t>(cap_) +
230 static_cast<std::size_t>(slot);
231 }
233 std::size_t cidx(int l, int i, int s) const {
234 return static_cast<std::size_t>(s) * static_cast<std::size_t>(nl_) *
235 static_cast<std::size_t>(cap_) +
236 phys(l, i);
237 }
238
239 bool mergeable(int l, int i, const double* c) const {
240 for (int s = 0; s < ns_; ++s) {
241 const double cf = conc_[cidx(l, i, s)];
242 if (std::abs(c[s] - cf) > kMergeAtol + kMergeRtol * std::abs(cf))
243 return false;
244 }
245 return true;
246 }
247
248 void merge_into(int l, int i, double volume, const double* c) {
249 const std::size_t p = phys(l, i);
250 const double v0 = vol_[p];
251 const double vt = v0 + volume;
252 if (vt <= 0.0) return;
253 for (int s = 0; s < ns_; ++s) {
254 const std::size_t ci = cidx(l, i, s);
255 conc_[ci] = (conc_[ci] * v0 + c[s] * volume) / vt;
256 }
257 vol_[p] = vt;
258 }
259
260 void merge_front_pair(int l) {
261 if (count(l) < 2) return;
262 // Fold segment 1 into segment 0 (volume-weighted), drop slot 1 by
263 // moving the head forward over it after copying 0 into 1.
264 const std::size_t p0 = phys(l, 0);
265 const std::size_t p1 = phys(l, 1);
266 const double v0 = vol_[p0], v1 = vol_[p1], vt = v0 + v1;
267 if (vt > 0.0)
268 for (int s = 0; s < ns_; ++s) {
269 const std::size_t c0 = cidx(l, 0, s);
270 const std::size_t c1 = cidx(l, 1, s);
271 conc_[c1] = (conc_[c0] * v0 + conc_[c1] * v1) / vt;
272 }
273 vol_[p1] = vt;
274 head_[static_cast<std::size_t>(l)] =
275 (head_[static_cast<std::size_t>(l)] + 1) % cap_;
276 count_[static_cast<std::size_t>(l)] -= 1;
277 }
278
279 int nl_ = 0, ns_ = 0, cap_ = kMaxSegmentsPerLink;
280 std::vector<int> head_, count_;
281 std::vector<double> vol_;
282 std::vector<double> conc_;
283};
284
285} // namespace lard
286} // namespace openswmm
287
288#endif // OPENSWMM_QUALITY_LARD_SEGMENT_STORE_HPP
Definition SegmentStore.hpp:66
double drain_front(int l, double vol_target, double *mass_out)
Definition SegmentStore.hpp:143
double seg_conc(int l, int i, int s) const
Definition SegmentStore.hpp:91
double decay_species(int s, double factor)
Exact-exponential decay on one species over every LIVE segment.
Definition SegmentStore.hpp:214
int count(int l) const
Definition SegmentStore.hpp:80
void resize(int n_links, int n_species, int cap=kMaxSegmentsPerLink)
Definition SegmentStore.hpp:68
double drain_back(int l, double vol_target, double *mass_out)
Drain up to vol_target from the BACK (downstream end).
Definition SegmentStore.hpp:126
void push_front(int l, double volume, const double *c)
Release a new segment at the FRONT (upstream end).
Definition SegmentStore.hpp:168
void mean_conc(int l, double *out) const
Definition SegmentStore.hpp:101
void set_seg_conc(int l, int i, int s, double c)
Definition SegmentStore.hpp:95
void add_species(int s, double delta)
Add a constant to one species on every LIVE segment.
Definition SegmentStore.hpp:203
void reverse(int l)
ยง4.4 flow reversal: front becomes back, in place.
Definition SegmentStore.hpp:187
double total_volume(int l) const
Definition SegmentStore.hpp:84
int capacity() const
Definition SegmentStore.hpp:82
void clear_link(int l)
Remove one whole link's contents (init/re-seed).
Definition SegmentStore.hpp:114
double seg_volume(int l, int i) const
Definition SegmentStore.hpp:90
int species() const
Definition SegmentStore.hpp:81
Definition LagrangianSolver.hpp:122
constexpr double kMergeAtol
Definition SegmentStore.hpp:63
constexpr double kMergeRtol
Definition SegmentStore.hpp:64
constexpr int kMaxSegmentsPerLink
Definition SegmentStore.hpp:57
Definition NodeCoupling.cpp:16