Class FluidConservationGuard
WHY IT EXISTS. The moment-closure drift can leave the simplex: on a station
where min(n,c) is not the identity the Gaussian correction to the
per-class share can drive a coordinate negative, and since the drift is
conservative another grows to match. In MATLAB odeset('NonNegative')
projects the ACCEPTED step, so the excursion is CLAMPED rather than reported --
which injects mass, collapses the step size, and leaves the window never
returning. One MATLAB suite run sat in test_CQN_Cox_CS_9 for 3h16m and
the 2026-08-27 run was killed after test11_interlock_lqnx had held it
for 100 minutes.
THIS PORT CANNOT HANG THE SAME WAY, and the difference is worth stating
rather than papering over: the JAR never projects the clamp back into the
integration (PassageTimeODE.computeDerivatives evaluates the drift at
the integrator's own state, and MethodStepHandler clamps only what it
RECORDS), so the same divergence surfaces as an LSODA corrector failure or as
a finished window holding a state that is not a solution. The second of those
is silent, and this is what makes it loud. The check is therefore applied to
the window's recorded rows rather than as an integration-halting callback.
THE TEST IS AN EXACT INVARIANT, not a heuristic bound on time or magnitude. The drift conserves the population of every CLOSED CHAIN exactly, so any deviation is a divergence and nothing else. The tolerance is a generous fraction of that population rather than a numerical tolerance: the integrator's own error is ~1e-4 relative, while the documented excursion reaches 5.2e4 against a true population of 0.05. A closed model whose population has moved by TOL is no longer solving the model, whatever it is converging to.
THE CHAIN IS THE CONSERVED UNIT, NOT THE CLASS, and the difference is the
whole correctness of this check. sn.njobs(k) is the population class
k STARTS with; class switching then moves jobs between the classes of one
chain, so only the chain total is invariant. Watching classes instead
condemns every class-switching model out of hand -- measured on
cqn_twoclass_hyperl (313 of 447 accepted states), on
init_state_ps (286 of 310) and on every one of the 162 fluid layers
an LQN builds under the srvn.cs encoding, where the chain sum never
moved at all. A cache model is the same story with the hit/miss classes.
A wall-clock budget would have caught the same thing and was rejected: it makes the answer depend on how busy the host is, so the same model would fall back on one machine and not on another. This invariant is deterministic.
- See Also:
-
Field Summary
FieldsModifier and TypeFieldDescriptionstatic final doubleRelative population drift that counts as having left the model. -
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionvoidassertConserved(double[] x, double t) RaisesFluidNonHyperbolicExceptionwhen the state has left the model, so the fallback ladder in SolverFluid answers with 'dae' and then with 'matrix'/'closing' instead of returning a state that is not a solution.static booleanclosureActive(SolverOptions options) True when the closure is what is being integrated, i.e.intviolation(double[] x)
-
Field Details
-
TOL
public static final double TOLRelative population drift that counts as having left the model.- See Also:
-
-
Constructor Details
-
FluidConservationGuard
- Parameters:
sn- the network struct, for the chain membership and populationsphases- (nstations,nclasses) phases per pair, as the analyzer builds it from mu; this is what fixes the coordinate blocks
-
-
Method Details
-
closureActive
True when the closure is what is being integrated, i.e. some sigma2 is nonzero. -
violation
public int violation(double[] x) - Parameters:
x- a state vector- Returns:
- the chain whose conserved population has drifted past TOL, or -1
-
assertConserved
public void assertConserved(double[] x, double t) RaisesFluidNonHyperbolicExceptionwhen the state has left the model, so the fallback ladder in SolverFluid answers with 'dae' and then with 'matrix'/'closing' instead of returning a state that is not a solution.- Parameters:
x- the window's final statet- the time that window reached
-