5#ifndef LINE_SOLVERS_FLUID_FLUID_PASSAGE_H
6#define LINE_SOLVERS_FLUID_FLUID_PASSAGE_H
62 std::vector<double>
t;
63 std::vector<double>
cdf;
84 std::size_t ist, std::size_t cls,
double tol = 1e-4,
85 std::size_t points = 201,
87 const std::size_t M =
sn.nstations, K =
sn.nclasses;
88 if (ist == 0 || ist > M)
throw InputError(
"fluid_passage_time: station out of range");
89 if (cls == 0 || cls > K)
throw InputError(
"fluid_passage_time: class out of range");
90 const std::size_t i = ist - 1, c = cls - 1;
99 if (x_steady.size() != L.
nstates)
100 throw InputError(
"fluid_passage_time: the initial state has the wrong length");
101 if (
sn.stations[i].nodetype == qn::NodeType::Source)
102 throw InputError(
"fluid_passage_time: a Source has no response time");
113 const double flat_end = 100.0 / detail::fluid_slow_rate(
sn, sys.
layout, tol);
114 const std::size_t P = L.
kic[i][c];
117 out.
t.push_back(0.0);
118 out.
t.push_back(flat_end);
119 out.
cdf.push_back(1.0);
120 out.
cdf.push_back(1.0);
124 const std::size_t n = L.
nstates;
125 const std::size_t tag0 = n;
126 const std::size_t nt = n + P;
130 for (std::size_t k = 0; k < P; ++k) fluid0 += x_steady[L.
qidx[i][c] + k];
132 if (!(fluid0 > 0.0)) {
133 out.
t.push_back(0.0);
134 out.
t.push_back(flat_end);
135 out.
cdf.push_back(1.0);
136 out.
cdf.push_back(1.0);
140 std::vector<double> y0(nt, 0.0);
141 for (std::size_t s = 0; s < n; ++s) y0[s] = x_steady[s];
142 for (std::size_t k = 0; k < P; ++k) y0[L.
qidx[i][c] + k] = 0.0;
149 std::size_t minus, plus;
150 std::size_t event_idx;
154 std::vector<TagEvent> tev;
155 const std::size_t base = L.
qidx[i][c];
156 for (std::size_t e = 0; e < sys.
events.size(); ++e) {
159 const std::size_t k = ev.
event_idx - base;
161 t2.event_idx = tag0 + k;
168 t2.plus = tag0 + (ev.
plus - base);
176 const std::size_t Kc = K;
177 const LsodaRhs f = [&sys, &L, &tev, i, c, base, tag0, P, n, nt, Kc](double,
const double* x,
179 std::vector<double> xb(x, x + n);
183 for (std::size_t k = 0; k < P; ++k) tagsum += x[tag0 + k];
184 std::vector<double> g(xb);
185 for (std::size_t k = 0; k < P; ++k) g[base + k] += x[tag0 + k];
186 std::vector<double> gg(g);
189 double blk = 0.0, gblk = 0.0;
190 for (std::size_t k = 0; k < P; ++k) {
192 gblk += gg[base + k];
194 const double share = (blk > 0.0) ? gblk / blk : 1.0;
196 for (std::size_t s = 0; s < nt; ++s) dx[s] = 0.0;
203 const std::size_t k = ev.
event_idx - base;
204 drive = xb[base + k] * share;
207 if (r == 0.0)
continue;
212 for (
const TagEvent& te : tev) {
213 const double r = te.rate_base * x[te.event_idx] * share;
214 if (r == 0.0)
continue;
224 const double min_rate = detail::fluid_slow_rate(
sn, L, tol);
225 const double t_end = 100.0 / min_rate;
227 std::vector<double> grid(points);
228 for (std::size_t j = 0; j < points; ++j)
229 grid[j] = t_end *
static_cast<double>(j) /
static_cast<double>(points - 1);
252 const double kMaxJump = 0.0005;
253 const int kMaxRounds = 5;
254 const std::size_t kMaxPoints = 20001;
255 const std::size_t kSplit = 20;
257 auto integrate_on = [&](
const std::vector<double>& g) {
261 p.
t.reserve(s.
y.size());
262 p.
cdf.reserve(s.
y.size());
263 for (std::size_t j = 0; j < s.
y.size(); ++j) {
265 for (std::size_t k = 0; k < P; ++k) z += std::max(0.0, s.
y[j][tag0 + k]);
266 double v = 1.0 - z / fluid0;
267 if (v < 0.0) v = 0.0;
268 if (v > 1.0) v = 1.0;
269 p.
t.push_back(s.
t[j]);
276 for (
int round = 0; round < kMaxRounds; ++round) {
277 if (cur.
t.size() >= kMaxPoints)
break;
278 std::vector<double> next;
279 next.reserve(cur.
t.size() * 2);
280 bool refined =
false;
281 for (std::size_t j = 0; j + 1 < cur.
t.size(); ++j) {
282 next.push_back(cur.
t[j]);
283 if (cur.
cdf[j + 1] - cur.
cdf[j] > kMaxJump && cur.
t[j + 1] > cur.
t[j]) {
285 const double dt = (cur.
t[j + 1] - cur.
t[j]) /
static_cast<double>(kSplit);
286 for (std::size_t s = 1; s < kSplit; ++s) next.push_back(cur.
t[j] + dt * s);
289 if (!cur.
t.empty()) next.push_back(cur.
t.back());
290 if (!refined || next.size() > kMaxPoints)
break;
291 cur = integrate_on(next);
322 std::size_t cls,
double tol = 1e-4,
323 std::size_t points = 201) {
A network plus its refreshed NetworkStruct.
The exception types the port throws.
The fluid drift: a port of solver_fluid_odes.m and the ode_jumps_new / ode_rate_base / ode_rates_clos...
LSODA: the LINE-facing wrapper over the vendored solver in third_party/lsoda.hpp.
FluidLayout fluid_layout(const qn::NetworkStruct< T > &sn)
Port of the layout half of solver_fluid_odes.m.
FluidPassage fluid_passage_time(const qn::NetworkStruct< T > &sn, const std::vector< double > &x_steady, std::size_t ist, std::size_t cls, double tol=1e-4, std::size_t points=201, const FluidClosure &closure=FluidClosure())
Response-time CDF at station ist for class cls, both 1-based.
LsodaSolution fluid_integrate_grid(const std::function< void(double, const double *, double *)> &f, const std::vector< double > &y0, const std::vector< double > &grid, const LsodaOptions &lopt)
The same retry over a whole output grid, for the callers that ask LSODA for a trajectory rather than ...
FluidOdeSystem fluid_ode_system(const qn::NetworkStruct< T > &sn)
Build the drift of sn: the port of ode_jumps_new and ode_rate_base fused into one pass.
FluidPassage fluid_tran_passage_time(const qn::NetworkStruct< T > &sn, std::size_t ist, std::size_t cls, double tol=1e-4, std::size_t points=201)
Port of @@SolverFLD/getTranCdfPassT: the same passage-time distribution started from the model's INIT...
void fluid_rates_closing(const FluidOdeSystem &sys, const double *x, std::vector< double > &g)
The reference's ode_rates_closing name, kept for the first-order callers.
std::function< void(double t, const double *y, double *dydt)> LsodaRhs
The right-hand side dy/dt = f(t, y).
A queueing network and its refreshed NetworkStruct.
SolverFluid: the closing method, a port of solver_fluid.m, solver_fluid_iteration....
double atol
absolute tolerance, applied to every component
double rtol
relative tolerance, applied to every component
Result of an integration, mirroring OdeSolution in ode.h.
std::vector< std::vector< double > > y
y[i] is the state at t[i]
std::vector< double > t
output times, t[0] = t_eval[0]
The second moment the drift closes its non-linear terms with, i.e.
std::vector< double > sigma2
per station; empty selects first order
std::size_t event_idx
state entry whose g(x) drives this rate
double rate_base
the model-fixed part of the rate
Where each (station, class) block sits in the state vector.
std::vector< std::vector< std::size_t > > qidx
0-based first index of (i,r)
std::size_t nstates
length of the state vector
std::vector< std::vector< std::size_t > > kic
phases held by (i,r); 0 when disabled
std::size_t n_departures
How many leading entries of events are DEPARTURES (a job completing at one block and starting at anot...
FluidClosure closure
The second moment the closures read; empty is the first-order drift.
std::vector< FluidEvent > events
The response-time CDF of one (station, class), sampled on a grid.
std::vector< double > cdf
double fluid0
the marked mass at t = 0; zero means nothing to measure