LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
fluid_odes.h File Reference

The fluid drift: a port of solver_fluid_odes.m and the ode_jumps_new / ode_rate_base / ode_rates_closing triple it drives on the DEFAULT (closing) method. More...

#include <cmath>
#include <cstddef>
#include <functional>
#include <string>
#include <vector>
#include "line/api/mam/map_moment.h"
#include "line/lang/qn/network_struct.h"
#include "line/solvers/fluid/fluid_closures.h"
#include "line/util/error.h"
#include "line/api/mc/dtmc_stochcomp.h"
#include "line/util/matrix.h"
Include dependency graph for fluid_odes.h:

Go to the source code of this file.

Classes

struct  line::fluid::FluidLayout
 Where each (station, class) block sits in the state vector. More...
struct  line::fluid::FluidEvent
 One event of the drift. More...
struct  line::fluid::FluidClosure
 The second moment the drift closes its non-linear terms with, i.e. More...
struct  line::fluid::FluidRateMult
 The assembled drift: the layout, the events, and the per-station schedule. More...
struct  line::fluid::FluidOdeSystem

Namespaces

namespace  line
namespace  line::fluid

Functions

void line::fluid::fluid_interpcols (const std::vector< double > &tg, const Matrix< double > &B, double tt, std::vector< double > &out)
 Port of fluid_interpcols.m: clamped piecewise-linear interpolation of the columns of B at a scalar time, into out.
template<class T>
FluidLayout line::fluid::fluid_layout (const qn::NetworkStruct< T > &sn)
 Port of the layout half of solver_fluid_odes.m.
template<class T>
FluidOdeSystem line::fluid::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.
Matrix< double > line::fluid::fluid_jump_matrix (const FluidOdeSystem &sys)
 The reference's dense jump matrix D, (nstates x nevents), rebuilt from the two-index event form this port stores instead.
void line::fluid::fluid_rates_closing_factors (const FluidOdeSystem &sys, const double *x, std::vector< double > &g)
 Port of ode_rates_closing_factors: the state-dependent factor g(x), in place.
void line::fluid::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, const double *, double *)> line::fluid::fluid_drift (const FluidOdeSystem &sys)
 The drift dx/dt, ready to hand to the integrator.

Detailed Description

The fluid drift: a port of solver_fluid_odes.m and the ode_jumps_new / ode_rate_base / ode_rates_closing triple it drives on the DEFAULT (closing) method.

WHAT THE STATE VECTOR IS. One entry per (station, class, service phase), laid out station-major then class-major then phase, and holding the mean number of class-r jobs at station i currently in phase k. q_indices(i,r) is where that block starts and Kic(i,r) is how long it is; a (station, class) pair with no service contributes NO entries at all, which is why the layout has to be computed rather than assumed to be M*K*phases.

WHY THE DRIFT FACTORS THE WAY IT DOES. Every event is a single job moving: it either completes at (i,c,ki) and starts at (j,l,kj), or it changes phase within (i,c). So each event's effect on the state is a vector with one -1 and one +1, and the drift is

dx/dt = sum over events of jump_e * rate_e(x)

The reference stores those jumps as a dense (nstates x nevents) matrix and multiplies. THIS PORT STORES THE TWO INDICES INSTEAD, which is the same arithmetic – the matrix has exactly two nonzeros per column – while turning an O(nstates * nevents) product into O(nevents) per evaluation. On a model with a few hundred states that is the difference between a fluid solve dominated by the drift and one dominated by the integrator, and LSODA evaluates the drift thousands of times.

The rate of an event factors into a part fixed by the model and a part that depends on the state:

rate_e(x) = rateBase_e * g(x)_{eventIdx_e}

rateBase folds the phase completion rate, the routing probability and the destination's entry-phase probability into one number computed once; g(x) is where the scheduling lives, and is the only thing re-evaluated. That split is the reference's and is what makes the drift cheap.

WHAT g(x) IS, PER DISCIPLINE (ode_rates_closing_factors). g starts as x itself, which is already right for a delay and for any station whose population is below its server count, and is then corrected: INF nothing, unless a load-dependent alpha(n_i) scales the station EXT the source keeps unit mass per class, so the first phase absorbs whatever the other phases do not hold PS/FCFS the servers are shared: the block is scaled by psi(n_i)/n_i, with psi = min(n_i,c)*alpha(n_i) the work the station clears DPS the same capacity psi, split by the WEIGHTED share w_j x_j / sum GPS the server is split by weight among the BACKLOGGED classes, then equally among that class's own jobs Any other discipline is left as x, exactly as the reference leaves it – which means it is integrated as an infinite server, and is why the featset gate refuses the disciplines that have no branch here.

ALL THREE NON-LINEAR TERMS ABOVE TAKE A SECOND MOMENT WHEN ONE IS SUPPLIED (FluidClosure): min() through fluid_capacity_closure, the PS/DPS ratio through fluid_share_closure, the GPS indicator through fluid_gps_share. With no closure every one of them collapses to its value at the mean, so the first-order methods take exactly the same code path.

Definition in file fluid_odes.h.