STAR
Cal STAR — firmware, DAQ server, and engine design
Loading...
Searching...
No Matches
engine.pipeline.stability.timelag Namespace Reference

Classes

class  StreamThermo
class  ChamberThermo
class  LagBreakdown

Functions

float atomization_lag (StreamThermo st, ChamberThermo ch)
float evaporation_constant_leonardi (float MR, float T_inf, float T_crit)
float convection_correction_leonardi (float Pc_bar)
float ranz_marshall_correction (float Re_d, float Pr=0.7)
float _slip_reynolds (StreamThermo st, ChamberThermo ch, float mu_g=7.0e-5)
float vaporization_lag_leonardi (StreamThermo st, ChamberThermo ch, *, str convection=DEFAULT_CONVECTION_MODEL)
Tuple[float, float] vaporization_lag_d2 (StreamThermo st, ChamberThermo ch)
float resolve_mixing_lag (float tau_vap_liquid, float mix_fraction)
_ModelResult _model_d2_law (StreamThermo st, ChamberThermo ch, str convection)
_ModelResult _model_leonardi (StreamThermo st, ChamberThermo ch, str convection)
Dict[str, LagBreakdown] compute_lags (Dict[str, StreamThermo] streams, ChamberThermo ch, *, str model="d2_law", float mix_fraction=0.0, str convection=DEFAULT_CONVECTION_MODEL, Optional[Callable[[str, float, str, str], float]] on_fallback=None)

Variables

list __all__
float MASS_HALF_LIFE_FRACTION = float(1.0 - 0.5 ** (2.0 / 3.0))
dict CONVECTION_MODELS
str DEFAULT_CONVECTION_MODEL = "none"
 _ModelResult = Tuple[float, float, float, float, Tuple[str, ...]]
dict TIME_LAG_MODELS

Detailed Description

Conversion time lags for the chug loop — a registry of NAMED models.

The chug characteristic equation (``chug.py``) needs one number per propellant stream: the lag
between an injection-rate perturbation and the heat release it produces. That number is where the
propellant and the injector enter the stability problem, so it is the one place that must not
hardcode either.

Two models, both selectable through ``StabilityConfig.time_lag_model``:

``d2_law``
    The historical STAR model: ``tau = D32**2 / K_v`` with the Godsave/Spalding evaporation
    constant. This is the **quiescent** droplet lifetime — no convection, no atomization, no
    mixing — and it is reproduced here bit-for-bit so old results stay reproducible.

``leonardi_dtl``
    The double-time-lag decomposition of Leonardi, Nasuti, Di Matteo & Steelant, *"A methodology
    to study the possible occurrence of chugging in liquid rocket engines during transient
    start-up"*, Acta Astronautica 139 (2017) 344-356 [hereafter **L17**]:

        tau_tot = tau_atom + tau_vap + tau_mix                                        (L17 eq. 5)

    with a convection-corrected vaporization lag (eq. 8-9) and an atomization lag (eq. 6-7). The
    decisive structural point is **phase**: L17 §3.2 gives the gaseous propellant *only* the mixing
    lag, because a gas neither atomizes nor vaporizes. STAR's previous code ran the d^2-law on both
    streams unconditionally, which silently invents a droplet lifetime for a gas.

What is deliberately NOT implemented
------------------------------------
L17 eq. 10 (the initial droplet diameter ``D0``) is a correlation "specifically developed for
coaxial injectors and liquid oxygen". EngineDesign already solves a Sauter mean diameter with the
injector-appropriate model — Ingebo for impinging, Lefebvre for coaxial, the pintle sheet model for
pintle — so ``D0`` is taken from that solve. Adopting eq. 10 would hardcode *one* injector and *one*
oxidizer into a multi-injector, multi-propellant tool, which is precisely the coupling this module
exists to remove. (It is also not dimensionally closed as printed: the exponents 2.25 and -2.65 do
not cancel, so its units depend on ref. [24] and cannot be reconstructed from L17 alone.)

The convection correction (L17 eq. 8) is OFF by default — and why
-----------------------------------------------------------------
L17 eq. 8 divides the quiescent droplet lifetime by ``1 + 1.5*alpha``, ``alpha = 1 - 3e-3*pc[bar]``.
Applying it makes the model *worse* against the paper's own experiment, so it ships behind a named
switch set to ``"none"``. The evidence (``scripts/chug_timelag_benchmark.py``, benchmarks B and E,
on the GH2/LOX rig of L17 ref. [25]):

  * Eq. 9's evaporation constant in the QUIESCENT form, ``tau_vap = D0**2 / k``, gives 4.98 ms at
    L17's own reference point (D0 = 83 um, MR 5, 44.8 bar, 2038 K) against the experiment-derived
    4.4 ms — **+13 %**. Apply eq. 8 and it becomes 2.17 ms, **-51 %**.
  * End-to-end through ``chug.py``, quiescent eq. 9 predicts 53 Hz and a stability boundary at
    Delta_p_ox/pc = 0.30 against a measured 66 Hz and 0.35. With eq. 8 it is 104 Hz and 0.20.
  * Eq. 8 also disagrees with Ranz-Marshall in *trend*, not just magnitude: it depends on chamber
    pressure alone — no slip velocity, no drop size — and it FALLS as Pc rises, while the physical
    correction does not.

The coherent reading is that eq. 9's ``k`` was already calibrated against real chamber data (L17
routes its 4.4 ms through Priem-Heidmann's L50/v_inj), so it *contains* the convective enhancement
and eq. 8 double-counts it. That also reconciles L17's two otherwise-inconsistent statements about
the same working point. Both corrections remain available through ``CONVECTION_MODELS`` so the run
report can say which one produced the answer.

Function Documentation

◆ _model_d2_law()

_ModelResult engine.pipeline.stability.timelag._model_d2_law ( StreamThermo st,
ChamberThermo ch,
str convection )
protected
-> (tau_atom, tau_vap, K_v, tau_mix_basis, notes). tau_mix is applied by ``compute_lags``.

``convection`` is accepted and ignored: the d^2-law is the historical model and is reproduced
unchanged, corrections included (there were none).

Definition at line 343 of file timelag.py.

References vaporization_lag_d2().

◆ _model_leonardi()

_ModelResult engine.pipeline.stability.timelag._model_leonardi ( StreamThermo st,
ChamberThermo ch,
str convection )
protected

Definition at line 356 of file timelag.py.

References atomization_lag(), vaporization_lag_d2(), and vaporization_lag_leonardi().

◆ _slip_reynolds()

float engine.pipeline.stability.timelag._slip_reynolds ( StreamThermo st,
ChamberThermo ch,
float mu_g = 7.0e-5 )
protected
Droplet Reynolds number on the gas-droplet slip velocity. Used only by ``ranz_marshall``.

Definition at line 245 of file timelag.py.

◆ atomization_lag()

float engine.pipeline.stability.timelag.atomization_lag ( StreamThermo st,
ChamberThermo ch )
Atomization lag [s].  **L17 eq. 6-7** (after Boronine, Vollmer & Frey, ref. [23]):

    tau_atom = 6e-4 * (rho_g/rho_l)^-0.32 * We_g^0.03 * Re_l^0.55 * D_l/u_l
    Re_l     = u_l D_l rho_l / mu_l
    We_g     = 2 rho_g (u_g - u_l)^2 D_l / sigma_l

Returns 0.0 for a gas stream (nothing to atomize) and NaN when an input is missing, so a caller
that cannot supply injector geometry gets a recorded fallback rather than a fabricated lag.

Definition at line 169 of file timelag.py.

Referenced by _model_leonardi().

◆ compute_lags()

Dict[str, LagBreakdown] engine.pipeline.stability.timelag.compute_lags ( Dict[str, StreamThermo] streams,
ChamberThermo ch,
* ,
str model = "d2_law",
float mix_fraction = 0.0,
str convection = DEFAULT_CONVECTION_MODEL,
Optional[Callable[[str, float, str, str], float]] on_fallback = None )
Lag breakdown for every stream under one named model.

``mix_fraction`` scales the shared mixing lag off the SLOWEST liquid stream's vaporization lag
(the rate-limiting one — L17 assigns the same tau_mix to both propellants). Pass 0.0 to disable
it, which is what ``d2_law`` does by default so it reproduces the historical numbers exactly.

``on_fallback(name, value, unit, reason) -> value`` is the assumptions hook; the integration
layer passes ``assumptions.assume`` so nothing is substituted silently. Defaults to identity for
pure-numeric use (tests, the benchmark script).

Definition at line 388 of file timelag.py.

References max, and resolve_mixing_lag().

◆ convection_correction_leonardi()

float engine.pipeline.stability.timelag.convection_correction_leonardi ( float Pc_bar)
L17 eq. 8 convective speed-up factor ``1 + 1.5*alpha`` with ``alpha = 1 - 3e-3*pc[bar]``.

``tau_vap = tau_vap(Re=0) / (1 + 1.5*alpha)`` — a droplet in a crossflow lives shorter than a
quiescent one. Note the factor FALLS with chamber pressure (alpha -> 0 near 333 bar), i.e. the
correlation says the convective enhancement washes out at high Pc. Floored at 1.0 so the
correction can never *lengthen* the lag past the quiescent value.

Definition at line 219 of file timelag.py.

References max.

◆ evaporation_constant_leonardi()

float engine.pipeline.stability.timelag.evaporation_constant_leonardi ( float MR,
float T_inf,
float T_crit )
L17 eq. 9 evaporation constant ``k`` [m^2/s]:

    k = 1e-6 * [ 1.01/(1 + MR) + 1.16e-3 (T_inf - T_cr)^0.93 ]^0.86

``T_inf`` is the combustion-gas temperature [K] and ``T_cr`` the LIQUID critical temperature
[K] — a propellant property, which is why it is a per-stream input here and not a constant.

Definition at line 196 of file timelag.py.

Referenced by vaporization_lag_leonardi().

◆ ranz_marshall_correction()

float engine.pipeline.stability.timelag.ranz_marshall_correction ( float Re_d,
float Pr = 0.7 )
Textbook convective correction ``Nu/2 = 1 + 0.3 Re_d^0.5 Pr^(1/3)`` (Ranz-Marshall).

Not used by any model — it is the independent yardstick the benchmark scores L17 eq. 8 against,
so a disagreement shows up as a number instead of an assumption. ``Re_d`` is the droplet
Reynolds number based on the slip velocity and the drop diameter.

Definition at line 233 of file timelag.py.

◆ resolve_mixing_lag()

float engine.pipeline.stability.timelag.resolve_mixing_lag ( float tau_vap_liquid,
float mix_fraction )
Mixing lag [s] shared by all streams, as a fraction of the rate-limiting vaporization lag.

**Why a fraction and not L17 fig. 1.** The paper reads tau_mix off Szuch's empirical curve of
mixing time versus L50 (the length to vaporize 50% of the liquid, NASA TN-D-7026). That curve is
a figure, not a table, and is not reproduced here; digitizing it by eye would be inventing a
correlation. What L17 *does* state numerically is its own calibration point — tau_vap = 4.4 ms
and tau_mix = 2.2 ms for the validation engine (L17 §3.1), i.e. a ratio of 0.5 — and L50 is
itself proportional to tau_vap at fixed droplet speed (``MASS_HALF_LIFE_FRACTION``), so a
constant of proportionality is the faithful reduction of the curve to one number.

The default 0.5 therefore carries the paper's provenance, and the caller records it through the
assumptions registry. Swap in a digitized curve by replacing this function, not by tuning 0.5.

Definition at line 315 of file timelag.py.

Referenced by compute_lags().

◆ vaporization_lag_d2()

Tuple[float, float] engine.pipeline.stability.timelag.vaporization_lag_d2 ( StreamThermo st,
ChamberThermo ch )
Quiescent d^2-law lag [s] and its evaporation constant K_v — STAR's historical model.

Exactly ``core.lags_from_smd(..., chi=1.0)``: same call, same arguments, same order, so the
``d2_law`` branch is bit-for-bit what the code did before this module existed.

Definition at line 296 of file timelag.py.

Referenced by _model_d2_law(), and _model_leonardi().

◆ vaporization_lag_leonardi()

float engine.pipeline.stability.timelag.vaporization_lag_leonardi ( StreamThermo st,
ChamberThermo ch,
* ,
str convection = DEFAULT_CONVECTION_MODEL )
Vaporization lag [s] from **L17 eq. 9**'s evaporation constant: ``tau = D0**2 / k / factor``.

``convection`` names an entry of ``CONVECTION_MODELS``; the default ``"none"`` leaves the
quiescent form, which is the variant that matches the experiment (module docstring). 0.0 for a
gas stream.

Definition at line 267 of file timelag.py.

References evaporation_constant_leonardi().

Referenced by _model_leonardi().

Variable Documentation

◆ __all__

list engine.pipeline.stability.timelag.__all__
private
Initial value:
1= [
2 "StreamThermo",
3 "ChamberThermo",
4 "LagBreakdown",
5 "atomization_lag",
6 "vaporization_lag_leonardi",
7 "evaporation_constant_leonardi",
8 "convection_correction_leonardi",
9 "ranz_marshall_correction",
10 "CONVECTION_MODELS",
11 "DEFAULT_CONVECTION_MODEL",
12 "TIME_LAG_MODELS",
13 "compute_lags",
14 "resolve_mixing_lag",
15 "MASS_HALF_LIFE_FRACTION",
16]

Definition at line 69 of file timelag.py.

◆ _ModelResult

engine.pipeline.stability.timelag._ModelResult = Tuple[float, float, float, float, Tuple[str, ...]]
protected

Definition at line 340 of file timelag.py.

◆ CONVECTION_MODELS

dict engine.pipeline.stability.timelag.CONVECTION_MODELS
Initial value:
1= {
2 "none": lambda st, ch: 1.0,
3 "leonardi_eq8": lambda st, ch: convection_correction_leonardi(ch.Pc_bar),
4 "ranz_marshall": lambda st, ch: ranz_marshall_correction(_slip_reynolds(st, ch)),
5}

Definition at line 256 of file timelag.py.

◆ DEFAULT_CONVECTION_MODEL

str engine.pipeline.stability.timelag.DEFAULT_CONVECTION_MODEL = "none"

Definition at line 264 of file timelag.py.

◆ MASS_HALF_LIFE_FRACTION

float engine.pipeline.stability.timelag.MASS_HALF_LIFE_FRACTION = float(1.0 - 0.5 ** (2.0 / 3.0))

Definition at line 92 of file timelag.py.

◆ TIME_LAG_MODELS

dict engine.pipeline.stability.timelag.TIME_LAG_MODELS
Initial value:
1= {
2 "d2_law": _model_d2_law,
3 "leonardi_dtl": _model_leonardi,
4}

Definition at line 382 of file timelag.py.