1"""The regulator's compressible seat (``Setup.regulator_compressible_seat``).
3The seat a dome regulator saturates against was ``K rho v^2 / 2`` at the inlet
4density: no expansion factor, no choke (EngineDesign/docs/layerx/AUDIT.md 5.3).
5On GN2 near burnout that passes 0.398 kg/s across the 441 psi a 1092-50 has, where
6IEC 60534-2-1 with its expansion factor passes 0.319 (AUDIT.md 9.6 C3, by hand,
7Cv 0.8, xT 0.7 assumed, gamma 1.40). On -- the default since 2026-10-08, pinned off
8in the benchmark Study -- it reproduces that; off it is the old law exactly; on, a
9regulator that is regulating -- helium at the LE4's hot-fire flows -- does not move.
11States are the audit's (``scratchpad/audit/drawings/regcalc.py`` at the twin's
12recorded ``PR_D.in``): GN2 at 3.62 s, 1054.78 psia and 199.95 K, outlet target
13614.174 psia; He at 0.55 s and 3.478 s.
16from __future__
import annotations
19from pathlib
import Path
22from fluids.control_valve
import size_control_valve_g
23from fluids.fittings
import Cv_to_K, Kv_to_Cv
35STAR = Path(__file__).resolve().parents[3]
36HE = STAR /
"feed-twin" /
"backend" /
"diagrams" /
"copv_study_he.json"
40ON = {SEAT_XT_SIGNAL: XT_TYPICAL}
43def _p(value: float, unit: str) -> Param:
44 return Param(value, unit, Provenance.ESTIMATED,
"test")
47def _regulator(setpoint_psia: float, droop: bool =
True) -> Regulator:
48 params = {
"setpoint":
_p(setpoint_psia,
"psi"),
"Cv":
_p(CV,
"Cv")}
49 params[
"bore"] =
_p(BORE,
"m")
51 params[
"flow_droop"] =
_p(8.3,
"psi")
52 params[
"rated_flow"] =
_p(0.09646,
"kg/s")
53 component = build_component(
54 ComponentInstance.build(
"PR-1",
"regulator", params, model=
"droop")
56 assert isinstance(component, Regulator)
60def _gas(name: str, psia: float, T: float, signals=
None):
61 return conditions_from_fluid(
Fluid(name), psia * PSI, T, signals, phase=
"gas")
65 """The team turned it on (2026-10-08); the benchmark keeps the law its
66 expectations were stated at."""
67 assert Setup().regulator_compressible_seat
is True
68 assert burn_setup().regulator_compressible_seat
is False
69 assert Setup().regulator_xT == pytest.approx(0.70)
73 """Without the signal the saturated drop is K rho v^2/2 at the inlet density,
74 bit for bit, however the gas is priced."""
76 flow =
_gas(
"nitrogen", 1054.78, 199.95)
77 for mdot
in (0.30, 0.45, 0.70):
79 old = Cv_to_K(CV, BORE) * _dynamic_head(mdot, BORE, flow.rho)
80 required = flow.p_upstream - reg.outlet_setpoint(mdot, flow)
81 assert reg.pressure_drop(mdot, flow) ==
max(required, old)
82 assert reg.pinned_flow(1.0e9, flow)
is None
86 """The flow at which the wide-open seat takes the whole available drop."""
90 lo, hi = (lo, mid)
if reg.is_saturated(mid, flow)
else (mid, hi)
91 return 0.5 * (lo + hi)
95 """441 psi available at 1054.78 psia, 199.95 K: 0.398 kg/s incompressible,
96 0.319 with IEC 60534-2-1's expansion factor (x = 0.418, Y = 0.801)."""
100 assert off == pytest.approx(0.398, abs=0.001)
101 assert on == pytest.approx(0.319, abs=0.001)
104 p1, p2 = 1054.78 * PSI, 614.174 * PSI
105 flow =
_gas(
"nitrogen", 1054.78, 199.95)
107 rho_normal = 101325.0 * molar_mass * 1e-3 / (8.314462618 * 273.15)
108 kv = size_control_valve_g(
113 Z=
Fluid(
"nitrogen").get(
"Z", p=p1, T=199.95),
123 assert Kv_to_Cv(kv) == pytest.approx(CV, rel=3e-3)
126@pytest.mark.parametrize(
127 "psia, T, mdot, target",
128 [(4081.45, 281.9, 0.02154, 583.512), (1633.75, 200.24, 0.03492, 623.972)],
131 psia: float, T: float, mdot: float, target: float
133 """Regulating at 5-18 % of its choked capacity, the seat never binds: the
134 outlet is the droop law with the opt-in on or off (< 0.1 psi; it is 0)."""
135 reg =
_regulator(target + 8.3 * mdot / 0.09646)
136 off =
_gas(
"helium", psia, T)
137 on =
_gas(
"helium", psia, T, ON)
138 outlet_off = off.p_upstream - reg.pressure_drop(mdot, off)
139 outlet_on = on.p_upstream - reg.pressure_drop(mdot, on)
140 assert outlet_off == pytest.approx(target * PSI, abs=0.01 * PSI)
141 assert abs(outlet_on - outlet_off) < 0.1 * PSI
142 assert not reg.is_saturated(mdot, on)
143 capacity = reg.seat_capacity(on)
144 assert capacity
is not None and 0.03 < mdot / capacity < 0.20
146 assert reg.pinned_flow(on.p_upstream - target * PSI, on)
is None
150 """A GN2 bottle at 4039.76 psia, 284.84 K straight into a tank at 100 psia:
151 the regulator is wide open and past F_gamma xT. The solve must carry the
152 IEC choked flow, N6 C (2/3) sqrt(F_gamma xT p1 rho1) with N6 = 2.73
153 (Cv; kg/h, kPa, kg/m^3); the incompressible law passes far more."""
157 net.add_node(
"bottle",
"nitrogen", 284.84, pressure=4039.76 * PSI)
158 net.add_node(
"tank",
"nitrogen", 284.84, pressure=100.0 * PSI)
159 for node
in net.nodes.values():
161 net.add_branch(
"PR",
_regulator(600.0),
"bottle",
"tank")
162 return solve_steady(net, signals=signals, tol=1e-10, max_iterations=200)
164 rho1 =
Fluid(
"nitrogen").get(
"rho", p=4039.76 * PSI, T=284.84)
165 hand = 2.73 * CV * (2.0 / 3.0) * (0.70 * 4039.76 * PSI / 1e3 * rho1) ** 0.5
169 assert on.flows[
"PR"] == pytest.approx(hand, rel=3e-3)
172 assert off.flows[
"PR"] > 1.3 * on.flows[
"PR"]
177 assert "IEC 60534-2-1" in model[
"source"]
178 inputs = model[
"inputs"]
179 assert inputs[
"xT"][
"value"] == XT_TYPICAL
180 assert "assumed" in inputs[
"xT"][
"provenance"]
183@pytest.mark.skipif(not HE.exists(), reason=
"helium drawing absent")
188 model = assemble_model(
189 read_diagram(json.loads(HE.read_text()), name=
"he"), diagram_id=
"he"
191 reg = model.built.network.branches[
"PR_D"].component
192 assert isinstance(reg, Regulator)
and "xT" not in reg.p
193 flow =
_gas(
"nitrogen", 1054.78, 199.95, {SEAT_XT_SIGNAL: 0.5})
194 capacity_05 = reg.seat_capacity(flow, xT=0.5)
195 assert capacity_05
is not None
196 assert reg.pinned_flow(1054.0 * PSI, flow) == pytest.approx(capacity_05)
199ENGINE = STAR /
"EngineDesign" /
"configs" /
"ethalox_doublet_7000N.yaml"
200CEA = STAR /
"EngineDesign" /
"output" /
"cache" /
"cea_cache_LOX_Ethanol_3D.npz"
201TABLES = STAR /
"feed-twin" /
"backend" /
"statemachines"
205 not (HE.exists()
and ENGINE.exists()
and CEA.exists()
and TABLES.is_dir()),
206 reason=
"helium drawing, engine, CEA table or tables absent",
209 """The helium hot-fire drawing burning: every tank, regulator-outlet and
210 injector pressure within 0.1 psi with the opt-in on (it regulates
211 throughout, so it is in fact identical)."""
219 def fire(seat: bool):
220 design = engine_from_config(yaml.safe_load(ENGINE.read_text()), name=
"7000N")
221 model = assemble_model(
222 read_diagram(json.loads(HE.read_text()), name=
"he"),
227 session = open_session(
229 load_machine(tables=TABLES),
230 setup=burn_setup(regulator_compressible_seat=seat),
234 loads={
"OXT": 6.0,
"FUT": 4.0},
239 return run_burn(session, plan)
241 off, on = fire(
False), fire(
True)
242 assert len(on.t) == len(off.t)
243 for node, column
in off.pressure.items():
244 for a, b
in zip(column, on.pressure[node]):
245 assert abs(a - b) < 0.1 * PSI, node
246 for tank, columns
in off.tank.items():
247 for a, b
in zip(columns[
"pressure_Pa"], on.tank[tank][
"pressure_Pa"]):
248 assert abs(a - b) < 0.1 * PSI, tank
None test_on_helium_at_le4_flows_the_outlet_does_not_move(float psia, float T, float mdot, float target)
None test_the_drawn_1092_has_no_xt_so_the_setup_value_is_used()
None test_wide_open_and_choked_the_solve_pins_the_capacity()
_gas(str name, float psia, float T, signals=None)
None test_off_the_seat_is_the_incompressible_law_exactly()
Regulator _regulator(float setpoint_psia, bool droop=True)
None test_on_in_the_cockpit_and_off_in_the_benchmark_study()
None test_a_helium_burn_is_unchanged_with_the_seat_on()
None test_the_seat_model_block()
float _saturation_flow(Regulator reg, flow)
None test_gn2_capacity_near_burnout_falls_as_the_audit_says()
Param _p(float value, str unit)