STAR
Cal STAR — firmware, DAQ server, and engine design
Loading...
Searching...
No Matches
test_rupe_criterion.py
Go to the documentation of this file.
1"""Rupe's mixing criterion for an unlike doublet, reported by the impinging solve.
2
3Rupe (JPL PR 20-195 / 20-209), restated by Elverum & Morey, JPL Memo 30-5 (1959) eq. 1: the
4most uniform mixture-ratio distribution of a one-on-one unlike doublet occurs at
5
6 M = rho_O v_O^2 d_O / (rho_F v_F^2 d_F) = 1,
7
8and eq. 4 gives the same number in flow terms, (rho_F/rho_O)(mdot_O/mdot_F)^2 (d_F/d_O)^3 per
9element pair. The solver's ``momentum_ratio_R`` is sqrt(rho_O v_O^2 / rho_F v_F^2); with
10mdot = Cd A sqrt(2 rho dp), rho v^2 = 2 Cd^2 dp, so R is (Cd_O/Cd_F) sqrt(dp_O/dp_F) exactly --
11no density, no hole size, and not Rupe's criterion.
12
13Expected values here come from eq. 4 on the solved flows, not from the module's own formula.
14"""
15from __future__ import annotations
16
17import math
18from pathlib import Path
19
20import pytest
21
22ROOT = Path(__file__).resolve().parents[1]
23PSI = 6894.757
24P_TANK = 584.2669657943025 * PSI
25
26
27@pytest.fixture(autouse=True, scope="module")
29 """The authoritative Python path; engine/accel mirrors it and test_numba_ab_parity holds the
30 two together."""
31 from engine import accel
32 real = accel.enabled, accel.require
33 accel.enabled = lambda: False
34 accel.require = lambda: False
35 try:
36 yield
37 finally:
38 accel.enabled, accel.require = real
39
40
41def _cfg(**geom):
42 from engine.pipeline.io import load_config
43 from engine.pipeline.config_schemas import PintleEngineConfig
44 d = load_config(str(ROOT / "configs/ethalox_6500N.yaml")).model_dump()
45 for side, kv in geom.items():
46 d["injector"]["geometry"][side].update(kv)
47 if d["injector"].get("plate"):
48 # Rupe/R identities at the holes' own drop: the plenum closure (with ring channels part
49 # of the drop is spent in the manifold; see test_injector_ring_manifold).
50 d["injector"]["plate"]["manifold_model"] = "plenum"
51 return PintleEngineConfig.model_validate(d)
52
53
54def _em_eq4(cfg, diag, mdot_O, mdot_F):
55 g = cfg.injector.geometry
56 rho_O, rho_F = cfg.fluids["oxidizer"].density, cfg.fluids["fuel"].density
57 nO, nF = g.oxidizer.n_elements, g.fuel.n_elements
58 return (rho_F / rho_O) * ((mdot_O / nO) / (mdot_F / nF)) ** 2 * (g.fuel.d_jet / g.oxidizer.d_jet) ** 3
59
60
61@pytest.fixture(scope="module")
62def solved():
63 from engine.core.injectors.impinging import ImpingingInjector
64 cfg = _cfg()
65 mO, mF, diag = ImpingingInjector(cfg).solve(P_TANK, P_TANK, 431.75 * PSI)
66 return cfg, mO, mF, diag
67
68
70 cfg, mO, mF, diag = solved
71 assert "rupe_M" in diag
72 assert diag["rupe_M"] == pytest.approx(_em_eq4(cfg, diag, mO, mF), rel=1e-9)
73
74
76 _, _, _, diag = solved
77 R = (diag["Cd_O"] / diag["Cd_F"]) * math.sqrt(diag["delta_p_injector_O"] / diag["delta_p_injector_F"])
78 assert diag["momentum_ratio_R"] == pytest.approx(R, rel=1e-5)
79 # M = R^2 d_O / d_F: the hole-size term R leaves out
80 assert diag["rupe_M"] == pytest.approx(diag["momentum_ratio_R"] ** 2 * diag["d_jet_O"] / diag["d_jet_F"], rel=1e-9)
81
82
84 """The shipped holes at the solved design point: M 1.21 (hand, from v_O 32.61 / v_F 37.22 m/s,
85 d_O 1.548 / d_F 1.421 mm), where R 1.05 reads as balanced."""
86 from engine.core.runner import PintleEngineRunner
87 cfg = _cfg()
88 r = PintleEngineRunner(cfg).evaluate(P_tank_O=P_TANK, P_tank_F=P_TANK)
89 diag = r["diagnostics"]
90 assert diag["rupe_M"] == pytest.approx(_em_eq4(cfg, diag, r["mdot_O"], r["mdot_F"]), rel=1e-6)
91 assert 1.1 < diag["rupe_M"] < 1.3
92
93
95 """Same injector dp on both sides, larger LOX hole: M grows with d_O, R does not see it."""
96 from engine.core.injectors.impinging import rupe_mixing_ratio, momentum_ratio_R_from_bulk_velocities
97 rho_O, rho_F, dp = 1140.0, 789.0, 900e3
98 v = lambda rho: 0.8 * math.sqrt(2.0 * dp / rho) # noqa: E731 same Cd and dp both sides
99 for dO in (1.4e-3, 1.55e-3, 1.7e-3):
100 M = rupe_mixing_ratio(rho_O, v(rho_O), dO, rho_F, v(rho_F), 1.42e-3)
101 assert M == pytest.approx(dO / 1.42e-3, rel=1e-12)
102 assert momentum_ratio_R_from_bulk_velocities(rho_O, rho_F, v(rho_O), v(rho_F)) == pytest.approx(1.0)
_em_eq4(cfg, diag, mdot_O, mdot_F)
test_rupe_M_is_reported_and_is_elverum_morey_eq4(solved)
test_R_is_the_cd_dp_ratio_it_always_was(solved)