1"""The drawing's feed written into the design the injector is sized with.
3EngineDesign sizes the injector at lockup less one K per side. The drawing's tank does not hold
4lockup while firing (regulator droop, the press line), and its lines are not the design's K. The
5fit expresses both in the design's own form; checked against the twin, not against itself:
7* the design as it stands misses the twin's manifold pressure by more than 7 psi on each side;
8* with the fitted K0, EngineDesign's own forward solve at lockup lands on the twin's burn: its
9 flows within 1.5 %, its O/F within 0.5 %;
10* writing the fit into the design does not move Layer X's burn, which never used the design's K0.
13from __future__
import annotations
15from pathlib
import Path
21STUDY_LOCKUP_PSIA = 564.7
23pytest.importorskip(
"feedtwin", reason=
"lib/feedtwin is not installed")
25from engine.layerx import DrawingStore, LayerXSettings, prepare, run_prepared
30FIXTURE = Path(__file__).parent /
"fixtures" /
"ethalox_6500N_doublet_cad_2026-09-28.yaml"
34PSI = 6894.757293168361
35pytestmark = pytest.mark.skipif(
not (shipped_drawings_dir() / f
"{GN2}.json").is_file(),
36 reason=
"feed-twin's shipped drawings are not next to this checkout")
39@pytest.fixture(scope="module")
41 return load_config(str(FIXTURE))
44@pytest.fixture(scope="module")
46 return {d.name: d
for d
in DrawingStore(
None).list()}[GN2]
49@pytest.fixture(scope="module")
53 prep = prepare(config,
None, drawing,
LayerXSettings(drawing_id=drawing.id, tank_pressure_psia=STUDY_LOCKUP_PSIA, ack_gn2_condensation=
True, replay=
False))
54 return run_prepared(prep, replay=
False, config=config)
60 raw = config.model_dump()
61 for side, u
in update[
"feed_system"].items():
62 raw[
"feed_system"][side].update(u)
63 return PintleEngineConfig(**raw)
67 fit = burned[
"feed_fit"]
68 assert fit[
"available"]
69 for side
in (
"oxidizer",
"fuel"):
70 s = fit[
"sides"][side]
72 assert s[
"manifold_gap_psi"] < -7.0, (side, s[
"manifold_gap_psi"])
73 assert s[
"supply_deficit_psi"] > 5.0
74 assert s[
"K_line"] > 0.0
and s[
"K_supply"] > 0.0
75 assert s[
"K0"] == pytest.approx(s[
"K_line"] + s[
"K_supply"])
77 assert s[
"manifold_fitted_psia"] == pytest.approx(s[
"manifold_psia"], abs=0.05)
83 fit = burned[
"feed_fit"]
84 lockup = fit[
"sides"][
"oxidizer"][
"lockup_psia"] * PSI
87 return PintleEngineRunner(cfg).evaluate(lockup, lockup, silent=
True)
89 today, fitted = solve(config), solve(
_with(config, fit[
"design_update"]))
90 twin = {
"mdot_O": fit[
"sides"][
"oxidizer"][
"mdot_kg_s"],
"mdot_F": fit[
"sides"][
"fuel"][
"mdot_kg_s"]}
91 twin_of = twin[
"mdot_O"] / twin[
"mdot_F"]
92 for key
in (
"mdot_O",
"mdot_F"):
93 assert fitted[key] == pytest.approx(twin[key], rel=0.015), key
95 assert abs(today[key] / twin[key] - 1.0) > abs(fitted[key] / twin[key] - 1.0), key
96 assert fitted[
"MR"] == pytest.approx(twin_of, rel=0.005)
100 upd = burned[
"feed_fit"][
"design_update"][
"feed_system"]
101 for side
in (
"oxidizer",
"fuel"):
103 assert u[
"roughness_m"]
is None and u[
"fittings"] == []
and u[
"phi_type"] ==
"none" and u[
"K1"] == 0.0
104 rec = u[
"derived_from"]
105 assert rec[
"by"] ==
"Layer X feed fit" and rec[
"drawing"] == GN2
and len(rec[
"drawing_sha256"]) == 64
106 assert rec[
"K_line"] + rec[
"K_supply"] == pytest.approx(u[
"K0"], abs=2e-4)
107 assert rec[
"condition"] ==
"on the pad"
111 """Layer X burns through the drawing's lines and samples EngineDesign with its feed zeroed, so
112 the design's K0 is not in its burn: written in, the burn is the same burn."""
113 upd = design_update(burned[
"feed_fit"], run_id=
"t")
114 prep = prepare(
_with(config, upd),
None, drawing,
LayerXSettings(drawing_id=drawing.id, tank_pressure_psia=STUDY_LOCKUP_PSIA, ack_gn2_condensation=
True, replay=
False))
115 again = run_prepared(prep, replay=
False)
116 assert again[
"summary"][
"total_impulse_Ns"] == pytest.approx(burned[
"summary"][
"total_impulse_Ns"], rel=1e-9)
120 """LE4 (6), flown on helium: its LOX side is drawn with no line (tank, OM-R, injector) and the
121 regulator's outlet climbs as the bottle empties, so the fit came out at K0 -0.448, and the Record
122 page offered to write it into the design (D-58). A loss coefficient at or below zero is refused,
123 in words, side by side; a positive one is written as before."""
126 side = {
"label":
"LOX",
"K0": -0.448,
"K_line": -0.0118,
"K_supply": -0.4359,
"line_loss_psi": -0.33,
127 "line_loss_design_psi": 16.15,
"tank_firing_psia": 550.77,
"lockup_psia": 539.82,
"K0_design": 0.643,
128 "mdot_kg_s": 1.86,
"supply_deficit_psi": -10.95,
"design_path":
"K0"}
129 why = _refusal(
"LOX", side[
"K0"], side[
"K_line"], side[
"K_supply"], side[
"line_loss_psi"],
130 side[
"line_loss_design_psi"], side[
"tank_firing_psia"], side[
"lockup_psia"])
131 assert why
and "likely undrawn" in why
and "550.8 against 539.8 psia" in why
132 with pytest.raises(ValueError, match=
"not a loss"):
133 design_update({
"sides": {
"oxidizer": side},
"drawing": {
"name":
"LE4 (6)",
"sha256":
"a" * 64}}, run_id=
"t")
134 ok = dict(side, K0=0.31, K_line=0.29, K_supply=0.02, line_loss_psi=7.0, tank_firing_psia=538.0)
135 assert _refusal(
"LOX", ok[
"K0"], ok[
"K_line"], ok[
"K_supply"], ok[
"line_loss_psi"], 16.15, 538.0, 539.82)
is None
136 upd = design_update({
"sides": {
"oxidizer": ok},
"drawing": {
"name":
"LE4 (6)",
"sha256":
"a" * 64}}, run_id=
"t")
137 assert upd[
"feed_system"][
"oxidizer"][
"K0"] == 0.31
141 assert burned[
"feed_fit"][
"writable"]
is True and burned[
"feed_fit"][
"write_refused"]
is None
test_writing_the_fit_does_not_move_layer_x(burned, config, drawing)
test_a_k0_that_is_not_a_loss_is_never_offered_for_the_design()
test_the_study_fit_is_writable(burned)
test_with_the_fit_enginedesign_lands_on_the_burn(burned, config)
test_the_update_is_the_lumped_path_with_its_record(burned)
test_the_design_misses_the_drawings_manifold(burned)