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

The tbi method: a port of solver_fluid_tbi_iteration.m and tbi_partition.m. More...

#include <algorithm>
#include <cmath>
#include <cstddef>
#include <vector>
#include "line/lang/qn/network_struct.h"
#include "line/solvers/fluid/fluid_odes.h"
#include "line/solvers/fluid/fluid_stiff.h"
#include "line/util/lsoda.h"
Include dependency graph for fluid_tbi.h:

Go to the source code of this file.

Classes

struct  line::fluid::TbiOptions
 Controls of the time-based iteration, mirroring options.config.tbi_*. More...

Namespaces

namespace  line
namespace  line::fluid

Functions

template<class T>
std::vector< std::vector< std::size_t > > line::fluid::tbi_partition (const qn::NetworkStruct< T > &sn, std::size_t cellsize=5)
 Port of tbi_partition.m: stations grouped by routing coupling.
std::vector< double > line::fluid::tbi_advance (const FluidOdeSystem &sys, const std::vector< std::vector< std::size_t > > &cells, const std::vector< double > &y0, double t0, double t1, const TbiOptions &topt, const LsodaOptions &lopt)
 Advance the state over [t0, t1] by time-based iteration.

Detailed Description

The tbi method: a port of solver_fluid_tbi_iteration.m and tbi_partition.m.

WHAT TBI IS FOR. The closing drift couples every station to every other, so one integration works on the whole state vector at once and its cost grows with the model. Time-based iteration splits the stations into CELLS, solves each cell's sub-drift on its own, and treats the flow arriving from the other cells as a KNOWN FUNCTION OF TIME, frozen at the previous sweep's trajectories. Sweeping until the trajectories stop moving recovers the coupled solution, while each solve only ever sees one cell's states. It is a domain decomposition in time, and it pays off when the model is too large for one integration to be comfortable.

THE PARTITION is the reference's greedy merge: start with one station per cell and repeatedly merge the pair with the largest routing coupling (W + W', diagonal dropped) whose combined size stays within twice the target cell size, until the cell count reaches ceil(M / cellsize) with cellsize 5. When nothing can be merged within the cap, the two smallest cells are merged instead so the loop always terminates.

GAUSS-SEIDEL BY DEFAULT. After a cell is solved, the frozen rates of ITS events are refreshed immediately, so later cells in the same sweep already see the update. The reference offers a Jacobi variant for parallel runs; this port implements the sequential Gauss-Seidel default, which is what the reference selects when it is not asked for parallelism.

ONE DELIBERATE SIMPLIFICATION, and what it costs. The reference carries whatever output grid its ODE solver happens to produce, takes the union of the cells' grids and interpolates every cell onto it. This port integrates every cell on ONE FIXED GRID per segment instead, so the sweeps compare trajectories sampled at the same instants and no interpolation of one cell onto another's grid is needed. The frozen inbound drift is still linearly interpolated between grid points, exactly as fluid_interpcols does.

THE GRID IS THE ACCURACY KNOB, and the error it leaves is measurable. Only the EXTERNAL contribution is approximated – each cell's own drift is integrated exactly – so the residual behaves like the interpolation error, falling roughly quadratically as the grid is refined. On a ten-queue model whose exact population is 4, the closed-form closing solve gives 4.000000 and this decomposition gives

grid   16    65     129      257
pop    4.167 4.0222 4.00285  3.99936

so the default of 129 holds the population to under a tenth of a percent. A model that needs more can raise TbiOptions::grid; nothing else changes, because the fixed point being chased is the same coupled ODE either way.

A SINGLE CELL IS THE CLOSING METHOD. With M <= cellsize the partition has one cell, there are no external events, the inbound drift is identically zero and the cell drift IS the closing drift. That is the case the tests pin, because it is the one where TBI has an independent right answer to be checked against.

Definition in file fluid_tbi.h.