77#ifndef OPENSWMM_QUALITY_LARD_RWPT_DISPERSION_HPP
78#define OPENSWMM_QUALITY_LARD_RWPT_DISPERSION_HPP
95constexpr double kDm = 1.3454e-8;
108 z += 0x9e3779b97f4a7c15ULL;
109 z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9ULL;
110 z = (z ^ (z >> 27)) * 0x94d049bb133111ebULL;
111 return z ^ (z >> 31);
128 std::uint64_t particle, std::uint64_t draw) {
133 return (
static_cast<double>(k >> 11) + 0.5) * (1.0 / 9007199254740992.0);
149 std::uint64_t particle, std::uint64_t draw) {
152 return std::sqrt(-2.0 * std::log(u1)) * std::cos(6.283185307179586 * u2);
170inline double rwpt_u_dev(
double eta,
double ubar,
double ustar,
bool turbulent) {
175 return (ustar /
kKappa) * (1.0 + std::log(std::max(eta,
kEtaMin)));
176 return 1.5 * ubar * eta * (2.0 - eta) - ubar;
191inline double rwpt_d_eta(
double eta,
double h,
double ustar,
bool turbulent) {
193 return kDm / (h * h);
194 const double e = std::min(std::max(eta, 0.0), 1.0);
195 return kKappa * ustar * e * (1.0 - e) / h;
210 return kKappa * ustar * (1.0 - 2.0 * eta) / h;
227 if (!circular || diam <= 0.0 || depth <= 0.0)
228 return std::max(depth, 0.0);
229 const double ratio = std::min(std::max(1.0 - 2.0 * depth / diam, -1.0), 1.0);
230 const double theta = 2.0 * std::acos(ratio);
232 return std::max(depth, 0.0);
233 const double a = diam * diam / 8.0 * (theta - std::sin(theta));
234 const double p = diam * theta / 2.0;
236 return (p > 0.0) ? a / p : std::max(depth, 0.0);
256 a.fixed(zeta_, eta_, seg_idx_);
257 for (
double v : zeta_)
258 if (!std::isfinite(v))
259 throw std::runtime_error(
"Invalid LARD particle position.");
260 for (
double v : eta_)
261 if (!std::isfinite(v) || v < 0 || v > 1)
262 throw std::runtime_error(
"Invalid LARD particle ordinate.");
276 const std::size_t total =
static_cast<std::size_t
>(nl_) *
static_cast<std::size_t
>(npart_);
277 zeta_.assign(total, -1.0);
278 eta_.assign(total, 0.5);
279 seg_idx_.assign(total, 0);
322 double rh,
double n_manning,
double v_in,
double q_dt,
double dt,
323 std::uint64_t stepc, std::uint64_t seed, std::vector<double>& delta) {
325 const int nseg = segs.
count(l);
326 if (nseg < 2 || h <= 1.0e-3 || ubar <= 0.0)
334 const double sf = (n_manning * ubar) * (n_manning * ubar) /
335 (2.208 * std::pow(std::max(rh, 1.0e-3), 4.0 / 3.0));
336 const double ustar = std::sqrt(32.174 * std::max(rh, 1.0e-3) * sf);
340 bounds_.assign(
static_cast<std::size_t
>(nseg) + 1, 0.0);
341 for (
int j = 0; j < nseg; ++j)
342 bounds_[
static_cast<std::size_t
>(j) + 1] =
343 bounds_[
static_cast<std::size_t
>(j)] + segs.
seg_volume(l, j);
345 const std::size_t base =
static_cast<std::size_t
>(l) *
static_cast<std::size_t
>(npart_);
346 for (
int p = 0; p < npart_; ++p) {
347 const std::size_t ip = base +
static_cast<std::size_t
>(p);
348 double z = zeta_[ip];
354 z = vol * (
static_cast<double>(p) + 0.5) /
static_cast<double>(npart_);
355 e =
rwpt_uniform(seed,
static_cast<std::uint64_t
>(l), stepc,
356 static_cast<std::uint64_t
>(p), 7);
360 z = std::min(v_in, vol *
rwpt_uniform(seed,
static_cast<std::uint64_t
>(l),
361 stepc,
static_cast<std::uint64_t
>(p), 8));
362 e =
rwpt_uniform(seed,
static_cast<std::uint64_t
>(l), stepc,
363 static_cast<std::uint64_t
>(p), 9);
367 const int j_from = locate(z, nseg);
374 const double dz = (
rwpt_u_dev(e, ubar, ustar, turbulent) / ubar) * q_dt;
378 const double d_eta =
rwpt_d_eta(e, h, ustar, turbulent);
380 e += drift * dt +
rwpt_normal(seed,
static_cast<std::uint64_t
>(l), stepc,
381 static_cast<std::uint64_t
>(p), 3) *
382 std::sqrt(std::max(2.0 * d_eta * dt, 0.0));
383 while (e < 0.0 || e > 1.0) {
393 while (z < 0.0 || z > vol) {
400 const int j_to = locate(z, nseg);
405 if (j_to != j_from) {
406 const double vshare = vol /
static_cast<double>(npart_);
408 const int stepdir = (j_to > j_from) ? 1 : -1;
410 const int jn = j + stepdir;
419 const double bnd = bounds_[
static_cast<std::size_t
>(std::max(j, jn))];
420 const double pen = std::min(std::abs(z - bnd), vshare);
421 for (
int s = 0; s < ns; ++s)
422 delta[
static_cast<std::size_t
>(s)] = segs.
seg_conc(l, j, s) * pen;
423 exchange(segs, l, j, jn, delta.data(), ns);
447 int locate(
double z,
int nseg)
const {
449 while (j + 1 < nseg && z >= bounds_[
static_cast<std::size_t
>(j) + 1])
476 void exchange(SegmentStore& segs,
int l,
int j,
int jn,
const double* delta,
int ns) {
477 for (
int s = 0; s < ns; ++s) {
478 const double vj = segs.seg_volume(l, j);
479 const double vn = segs.seg_volume(l, jn);
480 if (vj <= 0.0 || vn <= 0.0)
482 const double cj = segs.seg_conc(l, j, s);
483 const double cn = segs.seg_conc(l, jn, s);
484 const double take = std::min({delta[s], cj * vj, std::max(0.0, (cj - cn)) * vn});
487 segs.set_seg_conc(l, j, s, (cj * vj - take) / vj);
488 segs.set_seg_conc(l, jn, s, (cn * vn + take) / vn);
492 int nl_ = 0, npart_ = 0;
493 std::vector<double> zeta_, eta_;
494 std::vector<int> seg_idx_;
495 std::vector<double> bounds_;
Versioned archives of completed source-boundary state.
LARD segment slabs — per-link ring buffers of plug-flow segments.
The central, reentrant simulation context for the new engine.
Definition CompletedSourceArchive.hpp:25
Persistent particle field + the per-substep exchange operator.
Definition RwptDispersion.hpp:242
void checkpoint(detail::CompletedSourceArchive &a)
Serialize/restore fixed particle state then validate position/ordinate values.
Definition RwptDispersion.hpp:255
void disperse(SimulationContext &ctx, SegmentStore &segs, int l, double ubar, double h, double rh, double n_manning, double v_in, double q_dt, double dt, std::uint64_t stepc, std::uint64_t seed, std::vector< double > &delta)
One substep of dispersive exchange on link l's segments.
Definition RwptDispersion.hpp:321
void resize(int n_links, int n_per_link=kRwptParticlesPerLink)
Reset deterministic particle histories for new owner dimensions.
Definition RwptDispersion.hpp:273
Definition SegmentStore.hpp:67
double seg_conc(int l, int i, int s) const
Read one species concentration in a live segment.
Definition SegmentStore.hpp:162
int count(int l) const
Read the number of live segments in one ring.
Definition SegmentStore.hpp:122
double total_volume(int l) const
Sum live segment water volumes for one link.
Definition SegmentStore.hpp:140
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 kKappa
von Kármán
Definition RwptDispersion.hpp:93
constexpr int kRwptParticlesPerLink
Definition RwptDispersion.hpp:92
double rwpt_d_eta_grad(double eta, double h, double ustar, bool turbulent)
Evaluate coordinate diffusivity gradient used as stochastic drift.
Definition RwptDispersion.hpp:207
std::uint64_t rwpt_hash(std::uint64_t z)
Mix one unsigned64-bit deterministic random key.
Definition RwptDispersion.hpp:107
double rwpt_hyd_radius(double area, double depth, double diam, bool circular)
Hydraulic radius: exact for CIRCULAR from flow area, ≈ depth otherwise (wide-channel v1 approximation...
Definition RwptDispersion.hpp:226
constexpr double kReTurb
Definition RwptDispersion.hpp:97
constexpr double kDm
molecular diffusivity, ft²/s
Definition RwptDispersion.hpp:95
double rwpt_normal(std::uint64_t seed, std::uint64_t link, std::uint64_t stepc, std::uint64_t particle, std::uint64_t draw)
Standard normal (Box–Muller on two keyed uniforms).
Definition RwptDispersion.hpp:148
double rwpt_d_eta(double eta, double h, double ustar, bool turbulent)
Vertical diffusivity in η-units (D_t/h²), and its η-gradient (the Itô drift). Turbulent: Rouse parabo...
Definition RwptDispersion.hpp:191
constexpr double kEtaMin
log-law floor
Definition RwptDispersion.hpp:96
double rwpt_uniform(std::uint64_t seed, std::uint64_t link, std::uint64_t stepc, std::uint64_t particle, std::uint64_t draw)
Uniform in (0,1), keyed. draw distinguishes multiple draws per particle.
Definition RwptDispersion.hpp:127
constexpr double kNuWater
kinematic viscosity, ft²/s
Definition RwptDispersion.hpp:94
double rwpt_u_dev(double eta, double ubar, double ustar, bool turbulent)
Velocity DEVIATION u(η) − ū. Turbulent: log-law; laminar: parabola.
Definition RwptDispersion.hpp:170
Definition ExchangeLaw.hpp:60
Central, reentrant simulation context.
Definition SimulationContext.hpp:390