A vehicle entering or leaving orbit crosses several regimes on the way, and they do not change together. Three axes are tracked independently.
- Flow regime on freestream Knudsen number
- RegimeClassify bands the Knudsen number into a governing model: continuum Navier–Stokes, slip-corrected continuum, transitional, or free-molecular. Every transition is logged.Today this is a diagnostic carried on the evolved state. The crate does not switch closures on it, and no slip, transitional or free-molecular closure is implemented.
- Dynamics regime on force ratio ε = a_aero/a_grav
- While gravity dominates, a trajectory advances on the exact KS-conformal core with aero applied as a between-step kick. Once aero dominates, direct Cowell integration is the accurate choice. This ratio is the criterion for entering and leaving orbit.RegimeSwitch and aero_gravity_ratio are public API, but the shipped navigation engine does not call them. Applying the switch is the caller’s job.
- Link regime on evolved electron density
- The electron density sets the plasma frequency, and the plasma frequency decides whether the GNSS link exists. The Kalman filter’s measurement gating follows it.
The classifier is a stage in the coupling stack. It re-runs each step and writes its result onto the evolved field, and a march predicate reads it back. A transition is therefore an event the run finds, not a station it was told to stop at:
// RegimeClassify sits in the coupling stack and re-runs every step.
Coupling::between_steps()
.then(FiniteRateIonizationStage::new(n_tot)) // writes "n_e"
.then(RegimeClassify::new(l_char, trigger)) // reads it, classifies
.build()
// field.regime() -> Option<RegimeClass<R>> {
// model, // Continuum | Slip | Transitional | FreeMolecular
// knudsen, // the Kn the model was selected from
// plasma_frequency, // omega_p at the peak electron density
// gnss_denied, // omega_p above the configured comms band
// mach_regime, thrust_state, touchdown, // the powered-descent axes
// }
// A transition is a leg boundary: march until the classification changes.
let onset = CfdFlow::march(&nominal)
.couple(world::corridor_coupling(1.0, 0))
.from_field(world::initial_field())
.until(|f, _| f.regime().map(|r| r.gnss_denied).unwrap_or(false))?; // link lost
let exit = CfdFlow::march(&nominal)
.alternate_context(&committed)
.couple(world::corridor_coupling(1.0, 0))
.from(peak.state())
.until(|f, _| f.regime().map(|r| !r.gnss_denied).unwrap_or(false))?; // link back
Blackout becomes an interval the run discovers. March to the onset event, fly the committed world through the dark, continue to the recovery event. Every transition lands in the provenance log. From a committed corridor run:
regime -> slip (GNSS-available), Kn=0.07829109848665225
regime -> slip (GNSS-denied), Kn=0.012690837165407727
regime -> continuum (GNSS-denied), Kn=0.00993838892165156
regime -> continuum (GNSS-available), Kn=0.0002551442196046344
One descent moves through orbit-like dynamics, slip flow, continuum flow, comms blackout and reacquisition, in one uninterrupted program.
Walk 1: the classifier in the stackBlueprint: handle a regime change