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

The min-normal closure as a DIFFERENTIAL-ALGEBRAIC system: solver_fluid_dae.m. More...

#include <algorithm>
#include <cmath>
#include <cstddef>
#include <limits>
#include <string>
#include <vector>
#include "line/solvers/fluid/fluid_moments.h"
#include "line/solvers/fluid/fluid_nonhyperbolic.h"
#include "line/solvers/fluid/solver_fluid.h"
#include "line/util/error.h"
#include "line/util/lstsq.h"
#include "line/util/lu.h"
#include "line/util/matrix.h"
#include "rodas.hpp"
Include dependency graph for fluid_dae.h:

Go to the source code of this file.

Classes

struct  line::fluid::FluidDaeConstraints
 Finite capacity regions as linear admission constraints on the fluid state. More...
struct  line::fluid::FluidDaeGates
 Which events each cap throttles, and what happens to the mass it stops. More...
struct  line::fluid::FluidDaeStaging
 The waiting room outside a capped region, as fluid coordinates. More...
struct  line::fluid::FluidDaeLegs
 The two legs of every event under the active caps, and the waiting rooms. More...
struct  line::fluid::FluidDaeConservation
 Population conservation, one row per CLOSED chain, in state space. More...
struct  line::fluid::FluidDaeOptions
 Options that only the DAE route reads. More...
struct  line::fluid::FluidDaeSystem
 Everything the residual needs, gathered so the Newton can stay generic. More...
struct  line::fluid::FluidDaeNewtonInfo
 What the Newton reports about where it stopped. More...
struct  line::fluid::FluidDaeSwitch
 What a hybrid transient did, beside the trajectory. More...

Namespaces

namespace  line
namespace  line::fluid

Functions

double line::fluid::fluid_dae_reach (const std::vector< double > &row, const std::vector< std::size_t > &coord_class, const std::vector< double > &njobs, std::size_t K)
 The largest row x the population can produce, ignoring the coupling.
template<typename T>
FluidDaeConstraints line::fluid::fluid_dae_constraints (const qn::NetworkStruct< T > &sn, const FluidMomentTerms &terms)
template<typename T>
FluidDaeGates line::fluid::fluid_dae_gates (const qn::NetworkStruct< T > &sn, const FluidMomentTerms &t, const FluidDaeConstraints &con)
FluidDaeStaging line::fluid::fluid_dae_staging (const FluidMomentTerms &t, const FluidDaeConstraints &con)
void line::fluid::fluid_dae_extend (FluidDaeConstraints &con, const FluidDaeStaging &stg, const FluidMomentTerms &t)
 Extend every cap to the staging coordinates that hold mass INSIDE it.
FluidDaeLegs line::fluid::fluid_dae_legs (const FluidMomentTerms &t, const FluidDaeGates &gates, const FluidDaeStaging &stg, const FluidDaeConstraints &con, const std::vector< std::size_t > &active, const std::vector< double > &sg, const std::vector< double > &r, const std::vector< double > &mult, bool staged_flow)
 Shared by the steady-state residual and the transient right-hand side so that the two solve the SAME model and not two spellings of it.
template<typename T>
FluidDaeConservation line::fluid::fluid_dae_conservation (const qn::NetworkStruct< T > &sn, const FluidMomentTerms &terms, const FluidDaeStaging &stg)
 The conserved chains as equations.
FluidDaeOptions line::fluid::fluid_dae_options (const FluidOptions &opt, const FluidDaeOptions &dopt)
 The controls the DAE route actually reads: the struct a caller pinned, with whatever options.config set on top of it.
std::vector< std::size_t > line::fluid::fluid_dae_closable (const FluidMomentTerms &t)
 Stations whose variance enters the drift.
std::vector< double > line::fluid::fluid_dae_sigma_from (const Matrix< double > &Sigma, const FluidMomentTerms &t, const std::vector< std::size_t > &cidx)
 Project a state-level covariance onto the per-station variances the drift reads.
Matrix< double > line::fluid::fluid_dae_clamp_tangent (const FluidDaeConstraints &con, const std::vector< std::size_t > &active, const FluidMomentTerms &t)
 Orthogonal projector onto the subspace the CLAMPING caps leave free: I - R'(RR')^-1 R over the covariance coordinates.
bool line::fluid::fluid_dae_residual (const FluidDaeSystem &sysd, const std::vector< double > &u, std::vector< double > &G, std::vector< double > *rates_out, bool rethrow=false, std::vector< double > *fire_out=nullptr)
 The coupled algebraic system, stacked: drift, conservation, closure consistency.
void line::fluid::fluid_dae_project (std::vector< double > &u, std::size_t nfree)
 Onto the feasible box: the state is free, the variances are not.
FluidDaeNewtonInfo line::fluid::fluid_dae_newton (const FluidDaeSystem &sysd, std::vector< double > &u, double tol, std::size_t maxit)
 Damped PROJECTED Newton with a finite-difference Jacobian and an Armijo backtrack on the residual norm.
std::vector< std::vector< double > > line::fluid::fluid_dae_integrate (const FluidMomentTerms &terms, const FluidDaeConservation &cons, const FluidClosure &closure, const std::vector< double > &x0, const std::vector< double > &grid, double tol, bool withcov=false, const std::vector< std::size_t > &closable=std::vector< std::size_t >())
 Integrate the closure as an index-1 DAE, with RODAS, and report the state at every point of grid.
std::vector< double > line::fluid::fluid_dae_hold_multipliers (const FluidMomentTerms &terms, const FluidDaeGates &gates, const FluidDaeStaging &stg, const FluidDaeConstraints &con, const std::vector< std::size_t > &active, const std::vector< double > &x, const std::vector< double > &sg, const FluidClosure &cl, const std::vector< double > &m0, bool &ok)
 The multipliers that hold the active caps at this state, by small Newton.
std::vector< std::vector< double > > line::fluid::fluid_dae_integrate_hybrid (const FluidMomentTerms &terms, const FluidDaeConservation &cons, const FluidDaeConstraints &con, const FluidDaeGates &gates, const FluidDaeStaging &stg, const FluidClosure &closure, const std::vector< double > &x0In, const std::vector< double > &grid, double tol, std::vector< FluidDaeSwitch > *switches=nullptr)
 The transient UNDER CAPS: one index-1 DAE per segment, restarted at every located crossing.
void line::fluid::fluid_dae_metrics (const FluidMomentTerms &terms, const std::vector< double > &x, const FluidClosure &cl, Matrix< double > &QN, Matrix< double > &UN, Matrix< double > &RN, Matrix< double > &TN, const std::vector< double > *rates=nullptr)
 The metrics of one state, read exactly as the steady-state table reads them.
template<class T>
FluidSolution line::fluid::solver_fluid_dae (const qn::NetworkStruct< T > &sn, const FluidOptions &opt, const FluidDaeOptions &dopt_in=FluidDaeOptions())
 solver_fluid_dae.m: the min-normal closure solved as one system.
template<class T>
std::vector< FluidTranPointline::fluid::solver_fluid_dae_transient (const qn::NetworkStruct< T > &sn, const FluidOptions &opt, double t_end, std::size_t points=101, const std::vector< double > &out_grid=std::vector< double >(), const FluidDaeOptions &dopt_in=FluidDaeOptions())
 @@SolverFLD/getTranAvg for the DAE route: the metrics ALONG the trajectory.

Detailed Description

The min-normal closure as a DIFFERENTIAL-ALGEBRAIC system: solver_fluid_dae.m.

SOLVER_FLUID_MOMENTS already solves a differential system (the mean) coupled to an algebraic one (the covariance). It solves them by SUCCESSIVE SUBSTITUTION: integrate the mean to its fixed point at a held variance, solve the Lyapunov equation there, extract sigma2, repeat. This states the same closure as one system and solves it as one system. The closure itself is unchanged – the drift, the rate factors and the Lyapunov equation are taken from FLUID_MOMENT_TERMS and FLUID_LYAPUNOV untouched – only the way the coupled equations are discharged.

STEADY STATE is an algebraic system, not an integration:

0 = D r(x, sigma2)               drift residual, nstate rows
0 = C x - Nchain                 population conservation, one row per
                                 closed chain
0 = sigma2 - sigmaOf(x, sigma2)  closure consistency, one row per
                                 closable station

solved simultaneously by a damped projected Newton. Integrating a stable ODE until it stops moving is a poor way to solve f(x)=0: the cost is set by the slowest mode of the model rather than by the accuracy wanted, which is why the stiff models are expensive on the substitution route.

TRANSIENT is an index-1 DAE with a SINGULAR MASS MATRIX, integrated by the vendored RODAS (third_party/rodas.hpp):

d/dt x = D r(x, sigma2)    differential rows
0      = C x - Nchain      algebraic rows

RODAS IS THE DEFAULT AND THE ONLY INTEGRATOR HERE. The rest of the fluid solver runs LSODA (line/util/lsoda.h) so that MATLAB, the JAR, Python and C++ agree in the last digits, but LSODA integrates y' = f and cannot carry a singular mass matrix at all, so it is not a candidate for this path. RODAS is a Rosenbrock method of order (3)4 for M y' = f with singular M, and being a fixed sequence of six linear solves rather than an iteration it has no convergence history for a future port to diverge on – which is what makes it the right choice for a route that the other three codebases do not have yet.

WHY THE CONSTRAINT IS WRITTEN DIFFERENTLY IN THE TWO MODES. At a fixed point every flow already balances, so the differentiated form d/dt(Cx) = 0 is satisfied by anything and pins nothing; the steady state therefore uses C x = N directly. The transient needs the opposite: the mass matrix zeroes one row per chain and the constraint residual is written there, which is the index-1 form RODAS integrates.

WHAT THE ALGEBRAIC CONSTRAINT BUYS. Population conservation otherwise holds only to integrator tolerance: it is a consequence of the drift (the rows of D sum to zero on a closed chain), never an equation. Writing it as a constraint also makes the Newton system solvable, because the drift Jacobian is singular along exactly the conserved directions – the same singularity FLUID_LYAPUNOV works around by projecting onto range(D) – so the constraint rows supply the missing rank instead of a pseudo-inverse hiding it.

WHY THE COVARIANCE IS NOT A NEWTON UNKNOWN. Sigma is nstate^2 entries, so a Jacobian over it is quartic work – strictly worse than the cubic Lyapunov solves it would replace. Sigma is LINEAR in itself for a held x, so it is eliminated by one Lyapunov solve per residual evaluation and only sigma2, M numbers, joins x in the unknown vector.

See also
fluid_moments.h - the closure this solves, and the substitution route
third_party/rodas.hpp - the DAE integrator

Definition in file fluid_dae.h.