STAR
Cal STAR — firmware, DAQ server, and engine design
Loading...
Searching...
No Matches
engine.pipeline.time_varying_solver.TimeVaryingCoupledSolver Class Reference

Public Member Functions

 __init__ (self, PintleEngineConfig config, Any cea_cache, Optional[float] P_ambient=None, bool chug_eroded_geometry=False)
float liner_end (self)
TimeVaryingState solve_time_step (self, float time, float dt, float P_tank_O, float P_tank_F, Optional[TimeVaryingState] previous_state=None)
Dict[str, Dict[str, Any]] wall_layers (self)
Dict[str, Dict[str, float]] soak_back (self, float duration=120.0)
Dict[str, Any] soak_duration (self, float factor=3.0)
Dict[str, Any] soak_back_history (self, Optional[float] duration=None, float factor=3.0, float first_step=1.0e-2, float growth=1.05, int steps_per_duration=1200, float peak_resolution_K=0.05)
List[TimeVaryingState] solve_time_series (self, np.ndarray times, np.ndarray P_tank_O, np.ndarray P_tank_F)
Dict[str, np.ndarray] get_results_dict (self)

Public Attributes

 config = config
 cea_cache = cea_cache
 chug_eroded_geometry = bool(chug_eroded_geometry)
float P_ambient = float(P_ambient)
 V_chamber_initial = cg.volume
 A_throat_initial = cg.A_throat
 A_exit_initial = cg.A_exit
float L_chamber = cg.length if cg.length else 0.18
float L_cylindrical = cg.length_cylindrical if cg.length_cylindrical else 0.12
float L_contraction = cg.length_contraction if cg.length_contraction else 0.06
int D_chamber_initial = cg.chamber_diameter if cg.chamber_diameter and cg.chamber_diameter > 0 else 0.08
int D_throat_initial = np.sqrt(max(0, 4 * self.A_throat_initial / np.pi)) if self.A_throat_initial > 0 else 0.015
int D_exit_initial = np.sqrt(max(0, 4 * self.A_exit_initial / np.pi)) if self.A_exit_initial > 0 else 0.1
list state_history = []

Protected Member Functions

List[Layer] _liner_layers (self)
List[Layer] _graphite_layers (self)
Optional[Layer] _insert_backing (self)
Dict[str, Dict[str, Any]] _build_walls (self)
Dict[str, Any] _station_loads (self, "gas_side.HotGasState" gas, contour, float blow, Optional[Dict[str, float]] throat_comp)
Dict[str, float] _advance_walls (self, float dt)
Dict[str, float] _geometry (self)
Dict[str, float] _wall_report (self, Dict[str, float] rates)
Dict[str, Dict[str, Any]] _station_report (self)

Protected Attributes

 _contour0 = gas_side.contour_for(cg)
Optional[Dict[str, Dict[str, Any]]] _walls = None
Optional[Dict[str, Any]] _loads = None

Detailed Description

Fully-coupled time-varying solver for complete engine analysis.

Integrates all systems simultaneously:
- Reaction chemistry → shifting equilibrium
- Geometry evolution → chamber dynamics
- Ablative/graphite recession → geometry
- Stability analysis → all time-varying effects

Definition at line 136 of file time_varying_solver.py.

Constructor & Destructor Documentation

◆ __init__()

engine.pipeline.time_varying_solver.TimeVaryingCoupledSolver.__init__ ( self,
PintleEngineConfig config,
Any cea_cache,
Optional[float] P_ambient = None,
bool chug_eroded_geometry = False )
Initialize the coupled time-varying solver.

``chug_eroded_geometry`` (default off: the previous behaviour exactly) hands the stability
analysis the eroded geometry of each step -- throat area, chamber volume, L*, bore, exit
and expansion ratio, the copy the chamber was solved on -- instead of the as-built design.
Off, the chug loop's K_c and theta_c rest on the design-point A_t and L* while the chamber
it describes has eroded (Layer X audit D7-D, coupling-map row 19).

``P_ambient`` is the back pressure the nozzle fires into, in Pa. Explicit wins;
otherwise it comes from ``environment.elevation`` through the same standard
atmosphere the steady solve uses; only with neither is it sea level. This used to be
hardcoded to 101325 Pa inside ``solve_time_step`` while ``PintleEngineRunner.evaluate``
derived it from the site, so the two paths disagreed about the same engine by exactly
``(101325 - P_a) * A_exit`` -- 61.35 N on the 6.5 kN ethalox at 626.67 m -- and every
time-series thrust, impulse and burn time was low by the pad's altitude.

Parameters:
-----------
config : PintleEngineConfig
    Engine configuration
cea_cache : CEACache
    CEA cache for thermochemical properties

Definition at line 147 of file time_varying_solver.py.

Member Function Documentation

◆ _advance_walls()

◆ _build_walls()

◆ _geometry()

◆ _graphite_layers()

◆ _insert_backing()

Optional[Layer] engine.pipeline.time_varying_solver.TimeVaryingCoupledSolver._insert_backing ( self)
protected
What sits between the graphite insert and the case at the throat.

The case is a cylinder at the chamber bore plus the liner (``D_chamber/2 + t_liner``); the
insert's outside is the throat radius plus its thickness. The gap between them is filled with
the liner's material, the only other material the config declares (assumed: the config does
not say what backs the insert). Without it the graphite sat directly on the steel, which
sank its heat into the case: LE4's throat growth fell from ~4 % to ~1.5 % (2026-10-03). None
when there is no liner or no gap.

Definition at line 237 of file time_varying_solver.py.

References backend.routers.documents.CreatePayload.config, backend.routers.documents.ReleasePayload.config, backend.state.AppState.config, board_simulator.SimulatedBoard.config, calibration_robustness.AnomalyDetector.config, calibration_robustness.BackupManager.config, calibration_robustness.CalibrationValidator.config, calibration_robustness.HealthMonitor.config, calibration_robustness.RobustnessManager.config, combined_fsw_gui.ConfigManager.config, combined_gui.ConfigManager.config, emmc_log_t.config, engine.control.robust_ddp.engine_wrapper.EngineWrapper.config, engine.core.chamber_solver.ChamberSolver.config, engine.core.runner.PintleEngineRunner.config, engine.layerx.card.EngineSampler.config, engine.pipeline.cea_cache.CEACache.config, engine.pipeline.system_diagnostics.SystemDiagnostics.config, config, idle_t.config, self_test_t.config, solenoid_characterization_gui.ConfigManager.config, StarOTA::Server.config, D_chamber_initial, and D_throat_initial.

Referenced by _graphite_layers().

◆ _liner_layers()

◆ _station_loads()

◆ _station_report()

Dict[str, Dict[str, Any]] engine.pipeline.time_varying_solver.TimeVaryingCoupledSolver._station_report ( self)
protected
Every wall station now: recession, first-layer thickness left, surface, back-face and
first-interface temperatures, and the gas-side flux at the current surface temperature
under the loads just built for the next interval (the same evaluation ``_wall_report``
makes for the barrel and throat). Read-only: nothing here feeds the solve.

``q_conv`` is the convection the wall model takes (blown: pyrolysis blowing on the liner,
the oxidation blowing factor on graphite), ``q_chem`` the heat the carbon-oxidiser
reactions absorb (graphite only), ``q_net = q_conv + q_rad - q_chem`` the surface load.

Definition at line 638 of file time_varying_solver.py.

References _loads, and _walls.

Referenced by solve_time_step().

◆ _wall_report()

◆ get_results_dict()

Dict[str, np.ndarray] engine.pipeline.time_varying_solver.TimeVaryingCoupledSolver.get_results_dict ( self)
Convert state history to results dictionary (compatible with existing code).

Returns:
--------
results : dict
    Dictionary with arrays of all metrics

Definition at line 837 of file time_varying_solver.py.

References A_throat_initial, state_history, and wall_layers().

◆ liner_end()

◆ soak_back()

Dict[str, Dict[str, float]] engine.pipeline.time_varying_solver.TimeVaryingCoupledSolver.soak_back ( self,
float duration = 120.0 )
Peak back-face and bondline temperatures while each wall's stored heat soaks back
after shutdown (hot face adiabatic). Run after solve_time_series; the walls are copied.

Definition at line 685 of file time_varying_solver.py.

References _walls.

◆ soak_back_history()

Dict[str, Any] engine.pipeline.time_varying_solver.TimeVaryingCoupledSolver.soak_back_history ( self,
Optional[float] duration = None,
float factor = 3.0,
float first_step = 1.0e-2,
float growth = 1.05,
int steps_per_duration = 1200,
float peak_resolution_K = 0.05 )
Soak-back after shutdown, with the time each peak is reached.

Each station's wall is copied and continued with its hot face adiabatic (no gas, no
re-radiation: an upper bound on what reaches the back) and its back face adiabatic as
always (wall_conduction.py: no backing is modelled behind the last layer). Backward Euler
on the same grid, with a step that grows geometrically from ``first_step`` to
``duration / steps_per_duration``: the stack is fully implicit, so the long steps are
stable, and the short ones resolve the first seconds when a thin insert equilibrates.
Backward Euler slows a mode of rate lambda by ~lambda*h/2; at the default 1200 steps over
3 L^2/alpha the slowest mode (lambda = pi^2 alpha/L^2) has lambda*h = 0.025, so its clock
runs ~1 % slow. With constant properties the stored heat, and so the soaked peak, is
conserved exactly (both faces adiabatic); a cp(T) layer (graphite) has its capacity lagged
one step, as in the burn, so its energy is conserved to that lag.

``soak_duration`` sizes the window from the slowest *single layer*. For a stack of layers
the slowest mode is slower than any one layer's: by eigen-decomposition of this grid, a
12 mm liner at the schema's default properties (k 0.35, rho 1600, cp 1500) on a 1/4 in
steel case (k 16, rho 8000, cp 500, typical 300-series) is 2.3x slower than the liner alone, and 3x the liner's L^2/alpha still leaves ~3e-6 of that mode; a 6 mm
graphite insert on the same case, 1.6x and ~2e-8. Inside the window for such stacks.

``duration`` None is :meth:`soak_duration` (``factor`` x the longest L^2/alpha).

Per station: the peak back-face and first-interface temperatures and when they occur
(``t_*_peak_s``: the first time within ``peak_resolution_K`` of the peak, so a plateau
reached early is dated when it was reached, not by round-off along it), and
``t_back_95_s``, when the back face had made 95 % of its rise. With both faces adiabatic
the back face rises monotonically to the stored-heat equilibrium; the 95 % time says when
it gets there.

Definition at line 721 of file time_varying_solver.py.

References _walls, max, min, and soak_duration().

◆ soak_duration()

Dict[str, Any] engine.pipeline.time_varying_solver.TimeVaryingCoupledSolver.soak_duration ( self,
float factor = 3.0 )
``factor`` times the longest conduction time L^2/alpha over every layer of every wall
station, from the wall models' own properties: L the layer's current thickness (the hot
layer less its recession), alpha = k/(rho cp). A layer with cp(T) takes the larger of
its stated cp and cp at its hottest node, the slower diffusion.

The slowest mode of a slab with both faces adiabatic decays as exp(-pi^2 alpha t / L^2)
(the Fourier-series solution for a slab with insulated faces; Carslaw & Jaeger, Conduction
of Heat in Solids, 2nd ed., 1959, ch. III), so after 3 L^2/alpha it is down by
exp(-3 pi^2) ~ 1e-13: the stored heat has finished arriving.

Definition at line 694 of file time_varying_solver.py.

References _walls, and max.

Referenced by soak_back_history().

◆ solve_time_series()

List[TimeVaryingState] engine.pipeline.time_varying_solver.TimeVaryingCoupledSolver.solve_time_series ( self,
np.ndarray times,
np.ndarray P_tank_O,
np.ndarray P_tank_F )
Solve complete time series with full coupling.

Parameters:
-----------
times : np.ndarray
    Time points [s]
P_tank_O : np.ndarray
    Oxidizer tank pressures [Pa]
P_tank_F : np.ndarray
    Fuel tank pressures [Pa]

Returns:
--------
states : List[TimeVaryingState]
    Complete state history

Definition at line 791 of file time_varying_solver.py.

References solve_time_step(), and state_history.

◆ solve_time_step()

TimeVaryingState engine.pipeline.time_varying_solver.TimeVaryingCoupledSolver.solve_time_step ( self,
float time,
float dt,
float P_tank_O,
float P_tank_F,
Optional[TimeVaryingState] previous_state = None )
Solve one time step (explicit in the wall, exact in the chamber):

1. advance the wall conduction over [t - dt, t] under the previous state's gas loads;
2. rebuild the geometry from the cumulative recession;
3. solve Pc, and compute thrust, on that same geometry;
4. evaluate the gas-side loads at this state for the next interval.

``previous_state`` None starts the walls cold (ambient) at the initial geometry.

Definition at line 385 of file time_varying_solver.py.

References _advance_walls(), _build_walls(), _geometry(), feedtwin.comps.gas.GasOrifice._geometry(), _loads, _station_loads(), _station_report(), _wall_report(), _walls, engine.core.chamber_solver.ChamberSolver.cea_cache, engine.core.runner.PintleEngineRunner.cea_cache, cea_cache, chug_eroded_geometry, backend.routers.documents.CreatePayload.config, backend.routers.documents.ReleasePayload.config, backend.state.AppState.config, board_simulator.SimulatedBoard.config, calibration_robustness.AnomalyDetector.config, calibration_robustness.BackupManager.config, calibration_robustness.CalibrationValidator.config, calibration_robustness.HealthMonitor.config, calibration_robustness.RobustnessManager.config, combined_fsw_gui.ConfigManager.config, combined_gui.ConfigManager.config, emmc_log_t.config, engine.control.robust_ddp.engine_wrapper.EngineWrapper.config, engine.core.chamber_solver.ChamberSolver.config, engine.core.runner.PintleEngineRunner.config, engine.layerx.card.EngineSampler.config, engine.pipeline.cea_cache.CEACache.config, engine.pipeline.system_diagnostics.SystemDiagnostics.config, config, idle_t.config, self_test_t.config, solenoid_characterization_gui.ConfigManager.config, StarOTA::Server.config, L_chamber, and P_ambient.

Referenced by solve_time_series().

◆ wall_layers()

Dict[str, Dict[str, Any]] engine.pipeline.time_varying_solver.TimeVaryingCoupledSolver.wall_layers ( self)
Each wall station's layer stack as built (hot face first) and its position: what the
conduction model was given, so a report can list it with its provenance.

Definition at line 673 of file time_varying_solver.py.

References _walls.

Referenced by get_results_dict().

Member Data Documentation

◆ _contour0

engine.pipeline.time_varying_solver.TimeVaryingCoupledSolver._contour0 = gas_side.contour_for(cg)
protected

Definition at line 208 of file time_varying_solver.py.

Referenced by _build_walls(), _geometry(), and liner_end().

◆ _loads

Optional[Dict[str, Any]] engine.pipeline.time_varying_solver.TimeVaryingCoupledSolver._loads = None
protected

◆ _walls

◆ A_exit_initial

engine.pipeline.time_varying_solver.TimeVaryingCoupledSolver.A_exit_initial = cg.A_exit

Definition at line 197 of file time_varying_solver.py.

Referenced by _geometry().

◆ A_throat_initial

engine.pipeline.time_varying_solver.TimeVaryingCoupledSolver.A_throat_initial = cg.A_throat

Definition at line 196 of file time_varying_solver.py.

Referenced by get_results_dict().

◆ cea_cache

engine.pipeline.time_varying_solver.TimeVaryingCoupledSolver.cea_cache = cea_cache

Definition at line 179 of file time_varying_solver.py.

Referenced by solve_time_step().

◆ chug_eroded_geometry

engine.pipeline.time_varying_solver.TimeVaryingCoupledSolver.chug_eroded_geometry = bool(chug_eroded_geometry)

Definition at line 180 of file time_varying_solver.py.

Referenced by solve_time_step().

◆ config

engine.pipeline.time_varying_solver.TimeVaryingCoupledSolver.config = config

Definition at line 178 of file time_varying_solver.py.

Referenced by _advance_walls(), _build_walls(), _geometry(), _graphite_layers(), _insert_backing(), _liner_layers(), _station_loads(), _wall_report(), liner_end(), solve_time_step(), test_robust_ddp_constraints.TestConstraints.test_all_constraints_safe(), test_robust_ddp_engine_wrapper.TestEngineWrapper.test_cache_stats(), test_robust_ddp_engine_wrapper.TestEngineWrapper.test_caching(), test_robust_ddp_engine_wrapper.TestEngineWrapper.test_clear_cache(), test_robust_ddp_constraints.TestConstraints.test_constraint_summary(), test_robust_ddp_engine_wrapper.TestEngineWrapper.test_convenience_function(), test_robust_ddp_constraints.TestConstraints.test_copv_minimum_constraint(), test_robust_ddp_constraints.TestConstraints.test_edge_cases(), test_robust_ddp_engine_wrapper.TestEngineWrapper.test_estimate_error_handling(), test_robust_ddp_engine_wrapper.TestEngineWrapper.test_estimate_from_pressures_mock(), test_robust_ddp_engine_wrapper.TestEngineWrapper.test_estimate_without_stability(), test_robust_ddp_constraints.TestConstraints.test_headroom_constraints(), test_robust_ddp_constraints.TestConstraints.test_injector_stiffness_constraints(), test_robust_ddp_constraints.TestConstraints.test_mixture_ratio_constraints(), test_robust_ddp_robustness.TestRobustness.test_multiple_updates_convergence(), test_robust_ddp_constraints.TestConstraints.test_multiple_violations(), test_robust_ddp_constraints.TestConstraints.test_nan_handling(), test_robust_ddp_dynamics.TestDynamics.test_regulator_behavior(), test_robust_ddp_robustness.TestRobustness.test_tube_propagate_basic(), test_robust_ddp_robustness.TestRobustness.test_tube_propagate_validation(), test_robust_ddp_robustness.TestRobustness.test_tube_propagate_widens(), test_robust_ddp_robustness.TestRobustness.test_tube_propagate_with_large_w_bar(), test_robust_ddp_constraints.TestConstraints.test_ullage_maximum_constraints(), test_robust_ddp_robustness.TestRobustness.test_update_bounds_basic(), test_robust_ddp_robustness.TestRobustness.test_update_bounds_beta(), test_robust_ddp_robustness.TestRobustness.test_update_bounds_ema_behavior(), test_robust_ddp_robustness.TestRobustness.test_update_bounds_inflation(), test_robust_ddp_robustness.TestRobustness.test_update_bounds_requires_mass_flow(), test_robust_ddp_robustness.TestRobustness.test_update_bounds_residual_spike(), test_robust_ddp_robustness.TestRobustness.test_update_bounds_with_engine_wrapper(), and test_robust_ddp_engine_wrapper.TestEngineWrapper.test_wrapper_initialization().

◆ D_chamber_initial

int engine.pipeline.time_varying_solver.TimeVaryingCoupledSolver.D_chamber_initial = cg.chamber_diameter if cg.chamber_diameter and cg.chamber_diameter > 0 else 0.08

Definition at line 202 of file time_varying_solver.py.

Referenced by _geometry(), and _insert_backing().

◆ D_exit_initial

int engine.pipeline.time_varying_solver.TimeVaryingCoupledSolver.D_exit_initial = np.sqrt(max(0, 4 * self.A_exit_initial / np.pi)) if self.A_exit_initial > 0 else 0.1

Definition at line 204 of file time_varying_solver.py.

Referenced by _geometry().

◆ D_throat_initial

int engine.pipeline.time_varying_solver.TimeVaryingCoupledSolver.D_throat_initial = np.sqrt(max(0, 4 * self.A_throat_initial / np.pi)) if self.A_throat_initial > 0 else 0.015

Definition at line 203 of file time_varying_solver.py.

Referenced by _geometry(), and _insert_backing().

◆ L_chamber

float engine.pipeline.time_varying_solver.TimeVaryingCoupledSolver.L_chamber = cg.length if cg.length else 0.18

Definition at line 198 of file time_varying_solver.py.

Referenced by solve_time_step().

◆ L_contraction

float engine.pipeline.time_varying_solver.TimeVaryingCoupledSolver.L_contraction = cg.length_contraction if cg.length_contraction else 0.06

Definition at line 200 of file time_varying_solver.py.

◆ L_cylindrical

float engine.pipeline.time_varying_solver.TimeVaryingCoupledSolver.L_cylindrical = cg.length_cylindrical if cg.length_cylindrical else 0.12

Definition at line 199 of file time_varying_solver.py.

◆ P_ambient

float engine.pipeline.time_varying_solver.TimeVaryingCoupledSolver.P_ambient = float(P_ambient)

Definition at line 182 of file time_varying_solver.py.

Referenced by solve_time_step().

◆ state_history

list engine.pipeline.time_varying_solver.TimeVaryingCoupledSolver.state_history = []

Definition at line 207 of file time_varying_solver.py.

Referenced by get_results_dict(), and solve_time_series().

◆ V_chamber_initial

engine.pipeline.time_varying_solver.TimeVaryingCoupledSolver.V_chamber_initial = cg.volume

Definition at line 195 of file time_varying_solver.py.

Referenced by _geometry().


The documentation for this class was generated from the following file: