STAR
Cal STAR — firmware, DAQ server, and engine design
Loading...
Searching...
No Matches
test_layerx_iterate.py
Go to the documentation of this file.
1"""The iteration tools: pressurant swap, injector what-ifs, drill choices.
2
3Checked against things that exist independently of the code under test:
4 * feed-twin ships the stand drawn twice, once with GN2 and once with helium. Swapping the GN2
5 drawing's pressurant must reproduce the helium drawing's fluids exactly, and burn the same;
6 * a what-if patch lands on the injector and the passage L/d it was given, and the router
7 refuses one outside its limits;
8 * number drills are ASME B94.11M's.
9"""
10
11from __future__ import annotations
12
13import os
14import sys
15
16import pytest
17
18sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
19
20pytest.importorskip("feedtwin", reason="lib/feedtwin is not installed")
21
22from engine.layerx import DrawingStore, LayerXSettings, prepare, run_prepared # noqa: E402
23from engine.layerx.prepare import swap_pressurant # noqa: E402
24from engine.layerx.reconcile import drill_candidates # noqa: E402
25from engine.layerx.sources import shipped_drawings_dir # noqa: E402
26from engine.pipeline.io import load_config # noqa: E402
27
28ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
29CFG = os.path.join(ROOT, "configs", "ethalox_6800N.yaml")
30pytestmark = pytest.mark.skipif(not (shipped_drawings_dir() / "copv_study_he.json").is_file(),
31 reason="feed-twin's shipped drawings are not next to this checkout")
32
33
34@pytest.fixture(scope="module")
36 return {d.name: d for d in DrawingStore(None).list()}
37
38
39def _fluids(payload):
40 return {n["id"]: (n.get("data") or {}).get("fluid") for n in payload["nodes"] if (n.get("data") or {}).get("fluid")}
41
42
44 gn2, he = drawings["copv_study_gn2"].payload, drawings["copv_study_he"].payload
45 swapped, n, was = swap_pressurant(gn2, "helium")
46 assert was == "nitrogen" and n == 10
47 assert _fluids(swapped) == _fluids(he)
48 assert _fluids(gn2)["KB1"] == "nitrogen" # the drawing itself is untouched
49 back, n_back, _ = swap_pressurant(he, "nitrogen")
50 assert _fluids(back) == _fluids(gn2) and n_back == 10
51 same, none, _ = swap_pressurant(gn2, "nitrogen")
52 assert none == 0 and same is gn2
53
54
56 """A pid-designer drawing can draw its COPV with the tank symbol (copv is a tank wall
57 material there); feedtwin reads a TANK holding gas above its critical temperature as the
58 pressurant bottle. The swap looked only for KBOTTLE symbols, found none, and changed
59 nothing -- so "Use helium" on LE4 left it on nitrogen and the GN2-over-LOX check kept
60 blocking the run."""
61 def P(v, u):
62 return {"value": v, "unit": u, "source": "estimated"}
63
64 payload = {
65 "nodes": [
66 {"id": "copv", "data": {"componentType": "TANK", "label": "TK-1", "fluid": "nitrogen",
67 "options": {"material": "copv"},
68 "params": {"pressure": P(4000, "psi"), "temperature": P(293, "K")}}},
69 {"id": "lox", "data": {"componentType": "TANK", "label": "TK-3", "fluid": "oxygen",
70 "params": {"pressure": P(585, "psi"), "temperature": P(90.19, "K")}}},
71 ],
72 "edges": [],
73 }
74 swapped, n, was = swap_pressurant(payload, "helium")
75 assert was == "nitrogen" and n == 1
76 assert _fluids(swapped) == {"copv": "helium", "lox": "oxygen"}
77 assert _fluids(payload)["copv"] == "nitrogen" # the drawing itself is untouched
78
79
81 cfg = load_config(CFG)
82 gn2, he = drawings["copv_study_gn2"], drawings["copv_study_he"]
83
84 def burn(drawing, gas=None):
85 prep = prepare(cfg, None, drawing, LayerXSettings(drawing_id=drawing.id, replay=False, pressurant=gas))
86 assert prep.ok
87 return run_prepared(prep, replay=False)["summary"]
88
89 swapped, drawn = burn(gn2, "helium"), burn(he)
90 for key in ("total_impulse_Ns", "burn_time_s", "copv_used_kg", "copv_end_psia"):
91 assert swapped[key] == pytest.approx(drawn[key], rel=1e-9), key
92
93
95 from backend.routers.layerx import Settings, apply_design_patch
96
97 cfg = load_config(CFG)
98 drawn = cfg.injector.geometry.oxidizer.d_jet
99 patch = {"oxidizer": {"d_jet": 0.0017018, "orifice_l_over_d": 4.795}, "fuel": {"d_jet": 0.0015113}}
100 out = apply_design_patch(cfg, patch)
101 assert out.injector.geometry.oxidizer.d_jet == pytest.approx(0.0017018)
102 assert out.injector.geometry.fuel.d_jet == pytest.approx(0.0015113)
103 assert out.discharge["oxidizer"].orifice_l_over_d == pytest.approx(4.795)
104 assert cfg.injector.geometry.oxidizer.d_jet == drawn != 0.0017018 # the design is not touched
105 with pytest.raises(Exception):
106 Settings(drawing_id="x", design_patch={"oxidizer": {"d_jet": 0.5}})
107 with pytest.raises(Exception):
108 Settings(drawing_id="x", design_patch={"chamber": {"d_jet": 0.001}})
109
110
111@pytest.mark.parametrize("no, inch", [(51, 0.0670), (53, 0.0595)])
113 first = drill_candidates(inch * 25.4)[0]
114 assert first["drill"] == f"#{no}" and first["area_error"] == pytest.approx(0.0, abs=1e-12)
115 assert len(drill_candidates(1.6)) == 4
test_the_nearest_drill_is_the_standard_one(no, inch)
test_a_swapped_drawing_burns_as_the_drawn_one(drawings)
test_swapping_gn2_for_helium_is_the_helium_drawing(drawings)