95#ifndef OPENSWMM_QUALITY_LARD_LAGRANGIAN_SOLVER_HPP
96#define OPENSWMM_QUALITY_LARD_LAGRANGIAN_SOLVER_HPP
202 if (!initialized_) init(ctx);
205 auto& links = ctx.
links;
209 bool topo_dirty =
false;
210 for (
int l = 0; l < nl; ++l) {
211 const auto ul =
static_cast<std::size_t
>(l);
212 const double q = links.flow[ul];
214 const std::int8_t sign = (q >= 0.0) ? 1 : -1;
215 if (sign != flow_sign_[ul]) {
217 flow_sign_[ul] = sign;
221 if (topo_dirty || topo_.empty()) computeTopoOrder(ctx);
230 const int nsub = (dtq > 0.0 && dtq < dt_routing)
231 ?
static_cast<int>(std::ceil(dt_routing / dtq))
233 const double dt = dt_routing /
static_cast<double>(nsub);
234 const double frac = 1.0 /
static_cast<double>(nsub);
235 for (
int sub = 0; sub < nsub; ++sub)
substep(ctx, dt, frac);
240 applyFluxesAndBed(ctx, dt_routing);
258 const bool age = (L.
age_row >= 0);
259 const bool heat = (L.
temp_row >= 0);
262 auto& nodes = ctx.
nodes;
263 auto& links = ctx.
links;
271 store_.add_species(L.
age_row, dt);
272 for (
int n = 0; n < nn; ++n)
273 ws.node_age[
static_cast<std::size_t
>(n)] += dt;
277 scratch_.assign(
static_cast<std::size_t
>(ns), 0.0);
278 treat_cin_.assign(
static_cast<std::size_t
>(np > 0 ? np : 0), 0.0);
279 for (
int l = 0; l < nl; ++l) {
280 const auto ul =
static_cast<std::size_t
>(l);
282 const double q = std::abs(links.flow[ul]);
283 const int dn = downstreamNode(ctx, l);
284 const int up = upstreamNode(ctx, l);
287 std::fill(scratch_.begin(), scratch_.end(), 0.0);
288 const double drained = store_.drain_back(l, q * dt,
290 addToLedger(dn, drained, scratch_.data(), ns);
296 const double v_new = links.volume[ul];
297 const double v_rem = store_.total_volume(l);
298 if (v_rem > v_new && up >= 0) {
299 std::fill(scratch_.begin(), scratch_.end(), 0.0);
300 const double shed = store_.drain_front(l, v_rem - v_new,
302 addToLedger(up, shed, scratch_.data(), ns);
307 for (
const int n : topo_) {
308 const auto un =
static_cast<std::size_t
>(n);
309 const double v_old = nodes.old_volume[un];
310 const double v_in = node_vol_in_[un];
318 v_in + nodes.qual_vol_in[un] * frac <= 0.0;
320 for (
int s = 0; s < ns; ++s) {
324 const bool is_age = (s == L.
age_row);
325 const bool is_temp = (s == L.
temp_row);
332 is_msx ? un *
static_cast<std::size_t
>(
334 static_cast<std::size_t
>(s - L.
msx_first)
336 const auto li = un *
static_cast<std::size_t
>(ns) +
337 static_cast<std::size_t
>(s);
343 const auto ci = un *
static_cast<std::size_t
>(np) +
344 static_cast<std::size_t
>(s);
350 const double st_old =
351 is_age ? ws.node_age[un]
352 : is_temp ? hs.node_temp[un]
358 is_age ? ws.node_age_vol_in[un] * dt
359 : is_temp ? hs.node_temp_vol_in[un] * dt
362 : nodes.qual_mass_in[ci] * dt;
363 double m = st_old * v_old + node_mass_in_[li] + m_ext;
366 if (!is_age && !is_temp && !is_msx &&
367 s <
static_cast<int>(treat_cin_.size()))
368 treat_cin_[
static_cast<std::size_t
>(s)] =
369 node_mass_in_[li] + m_ext;
379 if (m < 0.0 && !is_temp) {
387 v_old + v_in + nodes.qual_vol_in[un] * frac;
398 const double st_new =
400 : ((denom > 1.0e-12) ? m / denom : st_old);
401 if (is_age) ws.node_age[un] = st_new;
402 else if (is_temp) hs.node_temp[un] = st_new;
404 else nodes.conc[ci] = st_new;
405 node_mass_in_[li] = 0.0;
407 node_vol_in_[un] = 0.0;
419 if (np > 0 && dt > 0.0 &&
422 const double v_in_total =
423 v_in + nodes.qual_vol_in[un] * frac;
424 for (
int p = 0; p < np; ++p)
425 treat_cin_[
static_cast<std::size_t
>(p)] =
426 (v_in_total > 1.0e-12)
429 static_cast<std::size_t
>(p)]) /
439 for (
const int l : node_out_links_[un]) {
440 const auto ul =
static_cast<std::size_t
>(l);
442 const double q = std::abs(links.flow[ul]);
444 const int dn = downstreamNode(ctx, l);
445 if (dn < 0)
continue;
446 for (
int p = 0; p < np; ++p) {
447 const auto ni = un *
static_cast<std::size_t
>(np) +
448 static_cast<std::size_t
>(p);
449 const auto lp = ul *
static_cast<std::size_t
>(np) +
450 static_cast<std::size_t
>(p);
451 scratch_[
static_cast<std::size_t
>(p)] = nodes.conc[ni];
452 links.conc[lp] = nodes.conc[ni];
455 scratch_[
static_cast<std::size_t
>(L.
age_row)] =
457 ws.link_age[ul] = ws.node_age[un];
460 scratch_[
static_cast<std::size_t
>(L.
temp_row)] =
462 hs.link_temp[ul] = hs.node_temp[un];
466 for (
int sp = 0; sp < nsp; ++sp) {
468 un *
static_cast<std::size_t
>(nsp) +
469 static_cast<std::size_t
>(sp)];
470 scratch_[
static_cast<std::size_t
>(
473 ul *
static_cast<std::size_t
>(nsp) +
474 static_cast<std::size_t
>(sp)] = c;
477 addToLedgerRate(dn, q * dt, scratch_.data(), ns);
482 for (
int l = 0; l < nl; ++l) {
483 const auto ul =
static_cast<std::size_t
>(l);
485 const int up = upstreamNode(ctx, l);
486 const double need = links.volume[ul] - store_.total_volume(l);
487 release_vol_[ul] = (need > 0.0) ? need : 0.0;
488 if (need <= 0.0 || up < 0)
continue;
489 const auto uu =
static_cast<std::size_t
>(up);
490 for (
int p = 0; p < np; ++p)
491 scratch_[
static_cast<std::size_t
>(p)] =
492 nodes.conc[uu *
static_cast<std::size_t
>(np) +
493 static_cast<std::size_t
>(p)];
495 scratch_[
static_cast<std::size_t
>(L.
age_row)] =
498 scratch_[
static_cast<std::size_t
>(L.
temp_row)] =
502 for (
int sp = 0; sp < nsp; ++sp)
503 scratch_[
static_cast<std::size_t
>(
506 uu *
static_cast<std::size_t
>(nsp) +
507 static_cast<std::size_t
>(sp)];
509 store_.push_front(l, need, scratch_.data());
522 for (
int l = 0; l < nl; ++l) {
523 const auto ul =
static_cast<std::size_t
>(l);
525 const double q = std::abs(links.flow[ul]);
528 if (row < 0)
continue;
529 const auto ur =
static_cast<std::size_t
>(row);
530 const double len = cond.length[ur];
531 const double vol = links.volume[ul];
532 if (len <= 0.0 || vol <= 0.0)
continue;
533 const double a_flow = vol / len;
534 const double ubar = q / a_flow;
535 const double h = links.depth[ul];
540 rwpt_.disperse(ctx, store_, l, ubar, h, rh,
541 cond.roughness[ur], release_vol_[ul],
542 q * dt, dt, substep_counter_,
543 static_cast<std::uint64_t
>(
550 for (
int p = 0; p < np; ++p) {
552 if (k == 0.0)
continue;
553 const double f = std::exp(-k * dt);
554 double removed = store_.decay_species(p, f);
555 for (
int n = 0; n < nn; ++n) {
556 const auto idx =
static_cast<std::size_t
>(n) *
557 static_cast<std::size_t
>(np) +
558 static_cast<std::size_t
>(p);
559 const double v = nodes.volume[
static_cast<std::size_t
>(n)];
560 removed += nodes.conc[idx] * (1.0 - f) * v;
561 nodes.conc[idx] *= f;
563 if (
static_cast<std::size_t
>(p) <
566 static_cast<std::size_t
>(p)] += removed;
579 if (
static_cast<int>(msx_block_.size()) < nsp)
580 msx_block_.assign(
static_cast<std::size_t
>(nsp), 0.0);
581 if (
static_cast<int>(msx_poll_.size()) < np && np > 0)
582 msx_poll_.assign(
static_cast<std::size_t
>(np), 0.0);
583 for (
int l = 0; l < nl; ++l) {
584 const auto ul =
static_cast<std::size_t
>(l);
586 const int cnt = store_.count(l);
587 for (
int i = 0; i < cnt; ++i) {
588 for (
int sp = 0; sp < nsp; ++sp)
589 msx_block_[
static_cast<std::size_t
>(sp)] =
591 for (
int p = 0; p < np; ++p)
592 msx_poll_[
static_cast<std::size_t
>(p)] =
593 store_.seg_conc(l, i, p);
597 const double seg_temp_c =
600 : std::numeric_limits<double>::quiet_NaN();
602 ctx,
false, dt, msx_block_.data(),
603 np > 0 ? msx_poll_.data() :
nullptr, 0.0,
605 for (
int sp = 0; sp < nsp; ++sp)
606 store_.set_seg_conc(l, i, L.
msx_first + sp,
608 static_cast<std::size_t
>(
612 for (
int n = 0; n < nn; ++n) {
613 const auto un =
static_cast<std::size_t
>(n);
614 for (
int sp = 0; sp < nsp; ++sp)
615 msx_block_[
static_cast<std::size_t
>(sp)] =
616 rx.msx_node_conc[un *
static_cast<std::size_t
>(
618 static_cast<std::size_t
>(sp)];
619 for (
int p = 0; p < np; ++p)
620 msx_poll_[
static_cast<std::size_t
>(p)] =
621 nodes.conc[un *
static_cast<std::size_t
>(np) +
622 static_cast<std::size_t
>(p)];
624 (un < nodes.hrt.size()) ? nodes.hrt[un] : 0.0;
625 const double node_temp_c =
629 : std::numeric_limits<double>::quiet_NaN();
631 ctx,
true, dt, msx_block_.data(),
632 np > 0 ? msx_poll_.data() :
nullptr, hrt,
634 for (
int sp = 0; sp < nsp; ++sp)
635 rx.msx_node_conc[un *
static_cast<std::size_t
>(nsp) +
636 static_cast<std::size_t
>(sp)] =
637 msx_block_[
static_cast<std::size_t
>(sp)];
649 const bool age = (L.
age_row >= 0);
650 const bool heat = (L.
temp_row >= 0);
652 auto& nodes = ctx.
nodes;
653 auto& links = ctx.
links;
658 for (
int l = 0; l < nl; ++l) {
659 const auto ul =
static_cast<std::size_t
>(l);
661 store_.mean_conc(l, scratch_.data());
662 for (
int p = 0; p < np; ++p)
663 links.conc[ul *
static_cast<std::size_t
>(np) +
664 static_cast<std::size_t
>(p)] =
665 scratch_[
static_cast<std::size_t
>(p)];
674 if (store_.count(l) > 0)
676 scratch_[
static_cast<std::size_t
>(L.
age_row)];
678 ws.link_age[ul] += dt_routing;
686 if (store_.count(l) > 0)
688 scratch_[
static_cast<std::size_t
>(L.
temp_row)];
692 if (L.
msx_first >= 0 && store_.count(l) > 0) {
694 for (
int sp = 0; sp < nsp; ++sp)
696 ul *
static_cast<std::size_t
>(nsp) +
697 static_cast<std::size_t
>(sp)] =
698 scratch_[
static_cast<std::size_t
>(
703 links.conc_old = links.conc;
704 nodes.conc_old = nodes.conc;
732 const bool heat_on = (L.
temp_row >= 0);
737 if ((!flux_on && !bed_on) || !(dt > 0.0))
return;
740 if (flux_on) th::updateSolarForcing(ctx);
741 if (bed_on) th::seedBedTemperature(ctx);
742 const double t_gr = bed_on ? th::groundTemperature(ctx) : 0.0;
745 constexpr double kSqFt = 0.09290304;
746 constexpr double kCuFt = 0.028316846592;
756 for (
int n = 0; n < nn; ++n) {
757 const auto un =
static_cast<std::size_t
>(n);
758 if (un >= hs.node_temp.size())
break;
760 if (!(vol > 0.0))
continue;
764 if (!(a > 0.0))
continue;
765 const double t = hs.node_temp[un];
767 hs.node_temp[un] += th::relaxT(
768 th::netFluxOut(ctx, ne, t),
769 th::netFluxOut(ctx, ne, t + th::kProbeC), th::kProbeC,
770 a * kSqFt, vol * kCuFt, dt, rho, cp);
779 if (bed_on && n_bed > 0 &&
780 (bed.n_species != n_bed ||
781 bed.link_conc.size() !=
static_cast<std::size_t
>(nl) *
782 static_cast<std::size_t
>(n_bed))) {
783 bed.link_conc.assign(
static_cast<std::size_t
>(nl) *
784 static_cast<std::size_t
>(n_bed),
786 bed.n_species = n_bed;
789 for (
int l = 0; l < nl; ++l) {
790 const auto ul =
static_cast<std::size_t
>(l);
792 const int cnt = store_.count(l);
793 if (cnt <= 0)
continue;
794 const double vol_ft3 = store_.total_volume(l);
795 if (!(vol_ft3 > 0.0))
continue;
797 store_.mean_conc(l, scratch_.data());
805 const double t_mean =
806 scratch_[
static_cast<std::size_t
>(L.
temp_row)];
807 const double surf_m2 =
808 flux_on ? th::linkFreeSurfaceFt2(ctx, l) * kSqFt : 0.0;
810 bool bed_stepped =
false;
811 if (bed_on && ul < bed.link_temp.size()) {
813 const auto& sd = th::sedimentFor(ctx, le);
814 const th::BedCoupling
g = th::bedCouplingFromContact(
815 ctx, sd, th::linkBedAreaM2(ctx, l), vol_ft3,
816 th::groundTempFor(ctx, sd, t_gr));
818 const th::PairStep ps = th::relaxPair(
819 g, t_mean, bed.link_temp[ul],
820 flux_on ? th::netFluxOut(ctx, le, t_mean) : 0.0,
821 flux_on ? th::netFluxOut(ctx, le,
822 t_mean + th::kProbeC)
824 flux_on ? th::kProbeC : 0.0, surf_m2, dt);
826 bed.link_temp[ul] += ps.dt_b;
830 if (!bed_stepped && flux_on && surf_m2 > 0.0) {
832 th::netFluxOut(ctx, le, t_mean),
833 th::netFluxOut(ctx, le, t_mean + th::kProbeC),
834 th::kProbeC, surf_m2, vol_ft3 * kCuFt, dt, rho, cp);
837 for (
int i = 0; i < cnt; ++i)
840 store_.seg_conc(l, i, L.
temp_row) + d_tw);
844 if (bed_on && n_bed > 0) {
847 const auto& sd = th::sedimentFor(ctx, le);
848 const double bed_m2 = th::linkBedAreaM2(ctx, l);
849 const double vol_b = bed_m2 * sd.bed_thickness;
850 const double q_exch = th::bedExchangeQ(sd, bed_m2);
851 if (q_exch > 0.0 && vol_b > 0.0) {
852 for (
int b = 0; b < n_bed; ++b) {
855 const int row = (b < L.
np)
857 : L.msx_first + (b - L.np);
858 const double c_mean =
859 scratch_[
static_cast<std::size_t
>(row)];
860 double& cb = bed.link_conc[
861 ul *
static_cast<std::size_t
>(n_bed) +
862 static_cast<std::size_t
>(b)];
863 const th::SolutePairStep st = th::exchangePair(
864 c_mean, cb, vol_ft3 * kCuFt, vol_b, q_exch,
867 for (
int i = 0; i < cnt; ++i)
870 store_.seg_conc(l, i, row) + st.dc_w);
878 void init(SimulationContext& ctx) {
880 const SpeciesRowLayout L =
rowLayout(ctx);
885 const bool age = (L.age_row >= 0);
886 const bool heat = (L.temp_row >= 0);
887 const int nn = ctx.n_nodes();
888 const int nl = ctx.n_links();
891 store_.resize(nl, ns,
892 std::max(2, ctx.options.max_segments_per_link));
893 flow_sign_.assign(
static_cast<std::size_t
>(nl), 1);
894 node_mass_in_.assign(
895 static_cast<std::size_t
>(nn) *
static_cast<std::size_t
>(ns), 0.0);
896 node_vol_in_.assign(
static_cast<std::size_t
>(nn), 0.0);
897 scratch_.assign(
static_cast<std::size_t
>(ns), 0.0);
898 treat_cin_.assign(
static_cast<std::size_t
>(np > 0 ? np : 0), 0.0);
899 node_out_links_.assign(
static_cast<std::size_t
>(nn), {});
900 release_vol_.assign(
static_cast<std::size_t
>(nl), 0.0);
901 if (ctx.options.lard_rwpt) rwpt_.resize(nl);
916 auto& ws = ctx.water_age_state;
917 if (ws.node_age.size() !=
static_cast<std::size_t
>(nn))
918 ws.resize(nn, nl, ctx.n_subcatches());
919 if (!ws.hotstart_loaded) {
920 const double a0 = ctx.water_age_config.global_age[
923 std::fill(ws.node_age.begin(), ws.node_age.end(), a0);
924 std::fill(ws.link_age.begin(), ws.link_age.end(), a0);
945 auto& hstate = ctx.heat_state;
946 const double t0 = ctx.heat_config.global_temp[
948 if (hstate.node_temp.size() !=
static_cast<std::size_t
>(nn))
949 hstate.resize(nn, nl, t0);
950 if (!hstate.legacy_seeded) {
951 std::fill(hstate.node_temp.begin(), hstate.node_temp.end(),
953 std::fill(hstate.link_temp.begin(), hstate.link_temp.end(),
956 hstate.legacy_seeded =
true;
962 for (
int l = 0; l < nl; ++l) {
963 const auto ul =
static_cast<std::size_t
>(l);
964 store_.clear_link(l);
966 const double v = ctx.links.volume[ul];
967 if (v <= 0.0)
continue;
968 for (
int p = 0; p < np; ++p)
969 scratch_[
static_cast<std::size_t
>(p)] =
970 ctx.links.conc[ul *
static_cast<std::size_t
>(np) +
971 static_cast<std::size_t
>(p)];
973 scratch_[
static_cast<std::size_t
>(L.age_row)] =
974 ctx.water_age_state.link_age[ul];
976 scratch_[
static_cast<std::size_t
>(L.temp_row)] =
977 ctx.heat_state.link_temp[ul];
978 if (L.msx_first >= 0) {
979 const int nsp = ns - L.msx_first;
980 for (
int sp = 0; sp < nsp; ++sp)
981 scratch_[
static_cast<std::size_t
>(
983 ctx.reactions.msx_link_conc[
984 ul *
static_cast<std::size_t
>(nsp) +
985 static_cast<std::size_t
>(sp)];
987 store_.push_front(l, v, scratch_.data());
988 flow_sign_[ul] = (ctx.links.flow[ul] >= 0.0) ? 1 : -1;
990 computeTopoOrder(ctx);
994 int upstreamNode(
const SimulationContext& ctx,
int l)
const {
995 const auto ul =
static_cast<std::size_t
>(l);
996 return (flow_sign_[ul] >= 0) ? ctx.links.node1[ul]
997 : ctx.links.node2[ul];
999 int downstreamNode(
const SimulationContext& ctx,
int l)
const {
1000 const auto ul =
static_cast<std::size_t
>(l);
1001 return (flow_sign_[ul] >= 0) ? ctx.links.node2[ul]
1002 : ctx.links.node1[ul];
1005 void addToLedger(
int n,
double vol,
const double* mass,
int np) {
1006 const auto un =
static_cast<std::size_t
>(n);
1007 node_vol_in_[un] += vol;
1008 for (
int p = 0; p < np; ++p)
1009 node_mass_in_[un *
static_cast<std::size_t
>(np) +
1010 static_cast<std::size_t
>(p)] +=
1011 mass[
static_cast<std::size_t
>(p)];
1014 void addToLedgerRate(
int n,
double vol,
const double* conc,
int np) {
1015 const auto un =
static_cast<std::size_t
>(n);
1016 node_vol_in_[un] += vol;
1017 for (
int p = 0; p < np; ++p)
1018 node_mass_in_[un *
static_cast<std::size_t
>(np) +
1019 static_cast<std::size_t
>(p)] +=
1020 vol * conc[
static_cast<std::size_t
>(p)];
1027 void computeTopoOrder(SimulationContext& ctx) {
1028 const int nn = ctx.n_nodes();
1029 const int nl = ctx.n_links();
1030 std::vector<int> indeg(
static_cast<std::size_t
>(nn), 0);
1031 for (
auto& v : node_out_links_) v.clear();
1032 for (
int l = 0; l < nl; ++l) {
1033 const auto ul =
static_cast<std::size_t
>(l);
1034 const int up = upstreamNode(ctx, l);
1035 const int dn = downstreamNode(ctx, l);
1036 if (up < 0 || dn < 0)
continue;
1037 node_out_links_[
static_cast<std::size_t
>(up)].push_back(l);
1038 if (std::abs(ctx.links.flow[ul]) >
kTinyFlow)
1039 indeg[
static_cast<std::size_t
>(dn)] += 1;
1042 topo_.reserve(
static_cast<std::size_t
>(nn));
1044 for (
int n = 0; n < nn; ++n)
1045 if (indeg[
static_cast<std::size_t
>(n)] == 0) q.push_back(n);
1046 std::vector<char> seen(
static_cast<std::size_t
>(nn), 0);
1048 while (qi < q.size()) {
1049 const int n = q[qi++];
1050 if (seen[
static_cast<std::size_t
>(n)])
continue;
1051 seen[
static_cast<std::size_t
>(n)] = 1;
1053 for (
const int l : node_out_links_[
static_cast<std::size_t
>(n)]) {
1054 if (std::abs(ctx.links.flow[
static_cast<std::size_t
>(l)]) <=
1057 const int dn = downstreamNode(ctx, l);
1058 if (dn >= 0 && --indeg[
static_cast<std::size_t
>(dn)] == 0)
1062 for (
int n = 0; n < nn; ++n)
1063 if (!seen[
static_cast<std::size_t
>(n)]) topo_.push_back(n);
1066 SegmentStore store_;
1067 RwptDispersion rwpt_;
1068 std::vector<double> release_vol_;
1069 std::uint64_t substep_counter_ = 0;
1070 std::vector<int> topo_;
1071 std::vector<std::vector<int>> node_out_links_;
1072 std::vector<std::int8_t> flow_sign_;
1073 std::vector<double> node_mass_in_;
1074 std::vector<double> node_vol_in_;
1075 std::vector<double> scratch_;
1079 std::vector<double> treat_cin_;
1080 std::vector<double> msx_block_;
1081 std::vector<double> msx_poll_;
1082 bool initialized_ =
false;
Plan H6b — bed conduction, deep-ground conduction, and hyporheic exchange, integrated SIMULTANEOUSLY ...
Plan D-H5e — the single node/link surface-flux binding.
Plan PE — resolve per-element attributes, and read them.
[INITIAL_QUALITY] reserved-species (WATER_AGE/__TEMPERATURE__) helpers for the per-engine seed sites ...
D-NS1: one clamp-bookkeeping seam for all three quality engines.
Node hydraulics — volume/depth/head conversions, surface area, overflow.
Water quality routing — constituent transport, mixing, decay.
Phase R4 — reaction binding for the LEGACY (CSTR) quality engine.
X3b: RWPT longitudinal dispersion on LARD segments.
LARD segment slabs — per-link ring buffers of plug-flow segments.
The central, reentrant simulation context for the new engine.
Phase H6a — where incoming shortwave Jin comes from (heat plan §2.5, D-H6a).
Phase H2 — latent and sensible heat exchange at the water surface (heat plan §2.1; CSHComponent §4....
The Domain × Species-class transport contract (OPT plan §5, E2).
Global unit conversion factors — matching legacy SWMM Ucf[]/Qcf[].
Definition LagrangianSolver.hpp:187
void publish(SimulationContext &ctx, double dt_routing)
Definition LagrangianSolver.hpp:644
void step(SimulationContext &ctx, double dt_routing)
One routing step of LTD transport. Lazily initializes on the first call (needs router-set volumes,...
Definition LagrangianSolver.hpp:193
void substep(SimulationContext &ctx, double dt, double frac)
One LTD substep: AGE → DRAIN → MIX(+passthrough) → RELEASE → DECAY.
Definition LagrangianSolver.hpp:252
Definition LagrangianSolver.hpp:122
double rwpt_hyd_radius(double area, double depth, double diam, bool circular)
Definition RwptDispersion.hpp:156
constexpr double kTinyFlow
cfs; below this a link moves nothing
Definition LagrangianSolver.hpp:124
SpeciesRowLayout rowLayout(const SimulationContext &ctx)
Definition LagrangianSolver.hpp:163
double getSurfArea(const NodeData &nodes, int idx, double depth, TableData *tables, int unit_sys, const NodeSubtypes *subs)
Compute surface area at a given depth for a single node.
Definition Node.cpp:272
void applyNodeTreatment(SimulationContext &ctx, int j, double dt, double q_raw, const double *cin)
One node's [TREATMENT] application, against the CALLER's inflow figures — the seam the LEGACY pass an...
Definition QualityRouting.cpp:1102
void bookNegativeAgeClamp(SimulationContext &ctx, int node)
Definition NegativeSources.hpp:83
void bookNegativeSourceClamp(SimulationContext &ctx, int node, int p, double shortfall)
Book one pollutant-row clamp: count it and un-book the shortfall from the external-extraction ledger ...
Definition NegativeSources.hpp:60
Definition BedExchange.cpp:37
bool bedExchangeEnabled(const SimulationContext &ctx) noexcept
True when [HEAT_FLUXES] SEDIMENT_EXCHANGE is on and heat transport is.
Definition BedExchange.cpp:176
ClassEnables network1DEnables(const SimulationContext &ctx) noexcept
1D network enables: LEGACY / ARD / LARD all size from this.
Definition TransportPolicy.cpp:78
void ensureMsxState(SimulationContext &ctx)
Definition ReactionLegacyBinding.cpp:65
void applyInitialTempOverrides(SimulationContext &ctx)
Apply TEMPERATURE rows onto heat_state.node_temp/link_temp (degC). No-op on unsized arrays.
Definition InitialQualitySeeds.hpp:109
void applyInitialAgeOverrides(SimulationContext &ctx)
Apply WATER_AGE rows onto water_age_state.node_age/link_age (hours -> seconds). No-op when a hotstart...
Definition InitialQualitySeeds.hpp:90
void reactSpeciesBlock(SimulationContext &ctx, bool tank, double dt, double *species_block, const double *pollut, double hrt_seconds, double temp_c)
Definition ReactionLegacyBinding.cpp:195
bool legacyReactionsActive(const SimulationContext &ctx)
Definition ReactionLegacyBinding.cpp:224
Counters g
Definition KokkosPerfCounters.hpp:90
int getUnitSystem(int flow_units)
Determine unit system (0=US, 1=SI) from flow units.
Definition UnitConversion.cpp:33
Definition NodeCoupling.cpp:16
@ CONDUIT
Definition LinkData.hpp:59
@ OUTFALL
Definition NodeData.hpp:61
@ INITIAL_STATE
water in the network at t = 0
Definition WaterAgeData.hpp:59
@ CIRCULAR
Definition LinkData.hpp:71
@ INITIAL_STATE
water in the network at t = 0
Definition HeatData.hpp:79
bool radiative_exchange
Definition HeatData.hpp:323
bool surface_exchange
[HEAT_FLUXES] SURFACE_EXCHANGE ON — latent + sensible exchange at the free surface (plan §2....
Definition HeatData.hpp:338
Which element a flux evaluator is being called for.
Definition HeatOverrideData.hpp:96
static HeatElement link(int i)
Definition HeatOverrideData.hpp:101
static HeatElement node(int i)
Definition HeatOverrideData.hpp:100
std::vector< double > node_temp
[node], °C
Definition HeatData.hpp:423
std::vector< LinkType > type
Link type.
Definition LinkData.hpp:132
ConduitData conduits
Definition LinkSubtypes.hpp:374
int conduit_row(int i) const noexcept
Definition LinkSubtypes.hpp:488
std::vector< double > depth
Current water depth above invert (project length units).
Definition NodeData.hpp:227
std::vector< double > volume
Current water volume (project volume units).
Definition NodeData.hpp:239
std::vector< double > k_decay
First-order decay coefficient (1/sec). The [POLLUTANTS] Kdecay column is 1/day: the parser divides by...
Definition PollutantData.hpp:96
std::vector< double > msx_ext_mass_in
Definition ReactionData.hpp:157
std::vector< double > msx_node_conc
Definition ReactionData.hpp:146
std::vector< double > msx_link_conc
Definition ReactionData.hpp:147
std::vector< double > qual_routing_reacted
Quality mass lost to decay.
Definition SimulationContext.hpp:1151
Central, reentrant simulation context.
Definition SimulationContext.hpp:353
struct openswmm::SimulationContext::MassBalance mass_balance
LinkSubtypes link_subtypes
Relational per-subtype link side-tables (Phase 6) — the link analogue of node_subtypes....
Definition SimulationContext.hpp:521
ReactionData reactions
Multispecies reaction system (EPANET-MSX conventions), parsed from the reactions component's config f...
Definition SimulationContext.hpp:557
int n_nodes() const noexcept
Number of nodes.
Definition SimulationContext.hpp:1868
NodeData nodes
All node state and properties.
Definition SimulationContext.hpp:496
TableData tables
All time series and rating curves.
Definition SimulationContext.hpp:629
BedZoneState bed_state
The bed / hyporheic transient-storage zone (phase H6b).
Definition SimulationContext.hpp:601
NodeSubtypes node_subtypes
Relational side-tables for node subtypes (storage/outfall/divider).
Definition SimulationContext.hpp:507
WaterAgeState water_age_state
Definition SimulationContext.hpp:573
TreatmentData treatment
Definition SimulationContext.hpp:679
LinkData links
All link state and properties.
Definition SimulationContext.hpp:513
int n_links() const noexcept
Number of links.
Definition SimulationContext.hpp:1871
HeatState heat_state
Definition SimulationContext.hpp:589
HeatConfigData heat_config
Heat transport (phase H1): per-source inlet temperatures parsed from the heat component (model....
Definition SimulationContext.hpp:588
SimulationOptions options
Parsed simulation options (from [OPTIONS] section).
Definition SimulationContext.hpp:398
PollutantData pollutants
Pollutant definitions and per-object quality state.
Definition SimulationContext.hpp:539
double water_density
Water density, kg/m³ ([OPTIONS] WATER_DENSITY; CSH Table 4.1).
Definition SimulationOptions.hpp:369
bool outfall_backflow_zero
[OPTIONS] OUTFALL_BACKFLOW_QUALITY LAST|ZERO — quality carried by reverse flow at outfalls (false = L...
Definition SimulationOptions.hpp:319
FlowUnits flow_units
Flow units system.
Definition SimulationOptions.hpp:232
int rwpt_seed
[OPTIONS] RWPT_SEED — deterministic counter-RNG seed (D-L6). Same seed ⇒ bit-identical runs at any th...
Definition SimulationOptions.hpp:287
double water_specific_heat
Water specific heat capacity, J/kg/°C (WATER_SPECIFIC_HEAT_CAPACITY).
Definition SimulationOptions.hpp:372
bool lard_rwpt
[OPTIONS] DISPERSION RWPT|OFF — LARD RWPT dispersion (X3b; strategy §5; the GUI plan's Lagrangian-gro...
Definition SimulationOptions.hpp:281
double quality_step
[OPTIONS] QUALITY_STEP — transport substep, seconds (HH:MM:SS or seconds; 0 = follow ROUTING_STEP).
Definition SimulationOptions.hpp:261
std::vector< bool > has_treatment
Per-node flag: true if any pollutant has a treatment expression.
Definition QualityData.hpp:160
The segment store's species-row layout, computed in ONE place.
Definition LagrangianSolver.hpp:151
int np
pollutant rows occupy [0, np)
Definition LagrangianSolver.hpp:152
int temp_row
temperature row index, or -1 (H7b)
Definition LagrangianSolver.hpp:154
int ns
total rows the store carries
Definition LagrangianSolver.hpp:157
int age_row
water-age row index, or -1 when absent
Definition LagrangianSolver.hpp:153
int msx_first
Definition LagrangianSolver.hpp:155
Allocation-free class enables for one domain — what the engines size from. n_msx is the reactions com...
Definition TransportPolicy.hpp:102
bool temperature
Definition TransportPolicy.hpp:106
bool age
Definition TransportPolicy.hpp:105
int n_pollut
Definition TransportPolicy.hpp:103
int n_msx
Definition TransportPolicy.hpp:104