1"""The iteration tools: pressurant swap, injector what-ifs, drill choices.
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.
11from __future__
import annotations
18sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
20pytest.importorskip(
"feedtwin", reason=
"lib/feedtwin is not installed")
22from engine.layerx import DrawingStore, LayerXSettings, prepare, run_prepared
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")
34@pytest.fixture(scope="module")
40 return {n[
"id"]: (n.get(
"data")
or {}).get(
"fluid")
for n
in payload[
"nodes"]
if (n.get(
"data")
or {}).get(
"fluid")}
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
48 assert _fluids(gn2)[
"KB1"] ==
"nitrogen"
49 back, n_back, _ = swap_pressurant(he,
"nitrogen")
51 same, none, _ = swap_pressurant(gn2,
"nitrogen")
52 assert none == 0
and same
is gn2
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
62 return {
"value": v,
"unit": u,
"source":
"estimated"}
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")}}},
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"
81 cfg = load_config(CFG)
82 gn2, he = drawings[
"copv_study_gn2"], drawings[
"copv_study_he"]
84 def burn(drawing, gas=None):
85 prep = prepare(cfg,
None, drawing,
LayerXSettings(drawing_id=drawing.id, replay=
False, pressurant=gas))
87 return run_prepared(prep, replay=
False)[
"summary"]
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
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
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}})
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_a_design_patch_lands_and_is_bounded()
test_a_copv_drawn_as_a_tank_is_swapped_too()
test_swapping_gn2_for_helium_is_the_helium_drawing(drawings)