45#ifndef OPENSWMM_QUALITY_LARD_SEGMENT_STORE_HPP
46#define OPENSWMM_QUALITY_LARD_SEGMENT_STORE_HPP
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);
80 int count(
int l)
const {
return count_[
static_cast<std::size_t
>(l)]; }
86 for (
int i = 0; i <
count(l); ++i) v += vol_[phys(l, i)];
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)]; }
96 conc_[cidx(l, i, s)] = c;
102 for (
int s = 0; s < ns_; ++s) out[s] = 0.0;
104 for (
int i = 0; i <
count(l); ++i) {
105 const double sv = vol_[phys(l, i)];
107 for (
int s = 0; s < ns_; ++s) out[s] += sv * conc_[cidx(l, i, s)];
110 for (
int s = 0; s < ns_; ++s) out[s] /= v;
115 head_[
static_cast<std::size_t
>(l)] = 0;
116 count_[
static_cast<std::size_t
>(l)] = 0;
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;
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)];
136 if (vol_[p] <= 0.0) count_[
static_cast<std::size_t
>(l)] -= 1;
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)];
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;
169 if (volume <= 0.0)
return;
170 if (
count(l) > 0 && mergeable(l, 0, c)) {
171 merge_into(l, 0, volume, c);
174 if (
count(l) >= cap_) {
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);
183 for (
int s = 0; s < ns_; ++s) conc_[cidx(l, 0, s)] = c[s];
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)]);
204 for (
int l = 0; l < nl_; ++l)
205 for (
int i = 0; i <
count(l); ++i) conc_[cidx(l, i, s)] += delta;
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);
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);
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_) +
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)];
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;
260 void merge_front_pair(
int l) {
261 if (
count(l) < 2)
return;
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;
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;
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;
280 std::vector<int> head_, count_;
281 std::vector<double> vol_;
282 std::vector<double> conc_;
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