1"""LE4 with its ground support drawn: a Rocket page and a GSE page, joined at
2paired disconnects (``fixtures/le4_rocket_and_gse.json``, the team's drawing of
5Read page-blind, the cart was more vehicle: its fuel transfer tank took the
6tanker load alongside the flight tank, its K-bottles started empty, its hand
7vents were open for good, its dome-control line was plumbed into the press
8manifold, and only the dome regulator had a knob. Each check here is one of
9those, against what the stand does.
12from __future__
import annotations
15from dataclasses
import replace
16from pathlib
import Path
23HERE = Path(__file__).resolve().parent
24DRAWING = HERE /
"fixtures" /
"le4_rocket_and_gse.json"
26ENGINE = STAR /
"EngineDesign" /
"configs" /
"ethalox_6800N.yaml"
27CEA = STAR /
"EngineDesign" /
"output" /
"cache" /
"cea_cache_LOX_Ethanol_3D.npz"
28TABLES = STAR /
"feed-twin" /
"backend" /
"statemachines"
32 return json.loads(DRAWING.read_text())
35def _ids() -> dict[str, str]:
36 return {n[
"data"][
"label"]: n[
"id"]
for n
in _payload()[
"nodes"]}
39@pytest.fixture(scope="module")
41 return build_network(read_diagram(
_payload(), name=
"LE4 (6)"))
46 assert built.vehicle
is not None
47 for label
in (
"COPV",
"Eth-Tank",
"LOX-Tank",
"DPR_HP",
"QD-FFB"):
48 assert ids[label]
in built.vehicle, label
49 for label
in (
"Fuel Transfer Tank",
"6K-GN2",
"PR-1",
"QD-FFA",
"HPC_SOL"):
50 assert ids[label]
not in built.vehicle, label
55 assert built.supplies == {ids[
"Fuel Transfer Tank"]: frozenset({ids[
"Eth-Tank"]})}
59 """Vents, bleeds and inline isolation alike: shut until a hand opens it."""
61 for valve
in (
"FV-MAN",
"FF-MAN-Vent",
"DR-MV",
"HP-Down-MAN",
"FF-MAN-Output"):
62 assert ids[valve]
in built.hand_valves
63 assert built.rest[ids[valve]] == 0.0, valve
68 for node
in payload[
"nodes"]:
69 if node[
"data"][
"label"] ==
"FF-MAN-Output":
70 node[
"data"].setdefault(
"options", {})[
"normalPosition"] =
"open"
71 built = build_network(read_diagram(payload, name=
"LE4 (6)"))
72 assert built.rest[
_ids()[
"FF-MAN-Output"]] == 1.0
77 mov = ids[
"OF-MOT-Dump"]
78 assert mov
in built.network.branches
79 assert built.actuators[mov] ==
"OF-MOT-Dump.command"
80 assert mov
not in built.hand_valves
84 """DR-REG-G reaches DPR_HP's dome through DR-CTRL-G, the mated DR QDs and
85 DR-CTRL-R. It is the loader, and nothing joins the dome line to the
88 assert ids[
"DR-REG-G"]
in built.dome_loaders
89 assert built.dome_loaders[ids[
"DR-REG-G"]].signal ==
"DPR_HP.dome"
90 body = {f
"{ids['DPR_HP']}.in", f
"{ids['DPR_HP']}.out"}
91 dome_side = built.node_of.get(ids[
"DR-CTRL-R"])
92 for branch
in built.network.branches.values():
93 if branch.id == ids[
"DPR_HP"]:
95 ends = {branch.upstream, branch.downstream}
96 assert not (ends & body
and dome_side
in ends), branch.id
102 return assemble_model(read_diagram(
_payload(), name=
"LE4 (6)"), diagram_id=
"le4")
109 knobs = {k.id: k
for k
in suggest(
_model(), 500.0, 4500.0).knobs}
110 assert knobs[DOME].regulators == (ids[
"DR-REG-G"],)
111 assert knobs[CHARGE].regulators == (ids[
"PR-1"],)
112 assert knobs[CHARGE].label ==
"COPV fill (PR-1)"
115 assert knobs[DOME].psig == pytest.approx(535.0)
116 assert knobs[CHARGE].psig == pytest.approx(4000.0)
117 assert knobs[ids[
"LP-PR"]].regulators == (ids[
"LP-PR"],)
121 """LE4 (6) draws DPR_HP's dome at 535 and the COPV at 4000, and leaves its
122 dome loader (DR-REG-G) and COPV fill (PR-1) blank. The knobs start at those:
123 a loader's job is the dome it loads, a fill's the charge it fills to. They
124 started at the session's 500 and 4,500, so the cockpit primed 35 psi under
125 Layer X on the same drawing and read its COPV amber at its own charge.
126 Blank all the way down, they still start at the session's numbers."""
132 hookup = suggest(model, 500.0, 4500.0)
133 assert knob_starts(hookup, model) == {
134 DOME: pytest.approx(535.0),
135 CHARGE: pytest.approx(4000.0),
137 loader = next(r
for r
in regulators(model)
if r.id == ids[
"DR-REG-G"])
138 assert loader.drawn_psig == pytest.approx(535.0)
141 for node
in payload[
"nodes"]:
142 params = node[
"data"].get(
"params")
or {}
143 if node[
"data"].get(
"label") ==
"DPR_HP":
144 params.pop(
"dome_pressure")
145 if node[
"data"].get(
"label") ==
"COPV":
146 params.pop(
"pressure")
147 blank = assemble_model(read_diagram(payload, name=
"LE4"), diagram_id=
"le4")
148 knobs = {k.id: k
for k
in suggest(blank, 500.0, 4500.0).knobs}
149 assert knobs[DOME].psig == 500.0
150 assert knobs[CHARGE].psig == 4500.0
151 assert knob_starts(suggest(blank, 500.0, 4500.0), blank) == {}
154@pytest.mark.skipif(not TABLES.is_dir(), reason=
"no state machine tables")
160 bound = binding(
_model(), load_machine(tables=TABLES),
None).to_symbol
161 assert bound[
"Fuel Vent"] == ids[
"FV-SOL"]
162 assert bound[
"Fuel Fill Vent"] == ids[
"FF-SOL-Vent"]
163 assert bound[
"GSE High Press Control"] == ids[
"HPC_SOL"]
164 assert bound[
"LOX Dump"] == ids[
"OF-MOT-Dump"]
167 assert bound[
"LOX Vent"] == ids[
"QD-OV-B"]
168 assert ids[
"FF-MAN-Output"]
not in bound.values(),
"hand valves are the crew's"
172 not (ENGINE.exists()
and CEA.exists()
and TABLES.is_dir()),
173 reason=
"engine, CEA table or state machine tables absent",
176 """GN2 High Press charges the COPV through PR-1 at the COPV fill knob; Fuel
177 Fill presses the transfer tank to its drawn 150 psig and pushes fuel across
178 the QD pair until the flight tank holds its load. Nothing moves before, no
179 propellant appears from nowhere, and the stand keeps its mass."""
189 design = engine_from_config(yaml.safe_load(ENGINE.read_text()), name=
"6800N")
190 model = assemble_model(
191 read_diagram(
_payload(), name=
"LE4 (6)"),
196 session = open_session(
197 model, load_machine(tables=TABLES), hookup=suggest(model, 500.0, 4000.0)
199 session.setup = replace(session.setup, copv_target_psi=4000.0)
201 session.tanks[ids[
"Eth-Tank"]],
202 session.tanks[ids[
"Fuel Transfer Tank"]],
204 copv = session.bottles[ids[
"COPV"]]
205 start_cart = cart.state.liquid_mass
206 assert start_cart > 10.0
and fuel.state.liquid_mass == 0.0
208 session.state =
"GN2 High Press"
211 assert psig(copv.pressure) == pytest.approx(4000.0, abs=40.0)
212 assert fuel.state.liquid_mass == 0.0,
"nothing moves before Fuel Fill"
214 session.state =
"Fuel Fill"
217 load = fuel.state.liquid_mass
218 assert load == pytest.approx(fuel._wanted(), rel=0.02)
221 assert start_cart - cart.state.liquid_mass == pytest.approx(load, abs=5e-3)
222 assert psig(cart.pressure) == pytest.approx(150.0, abs=2.0)
223 last = session.solver_log[-1]
224 assert abs(last.mass_error_kg - last.guard_kg) < 1e-3
235 model = assemble_model(
236 read_diagram(payload
or _payload(), name=
"LE4 (6)"), diagram_id=
"le4"
239 model, load_machine(tables=TABLES), hookup=suggest(model, 500.0, 4500.0)
245 line = built.dome_lines[ids[
"DPR_HP"]]
246 assert line.loader == ids[
"DR-REG-G"]
247 assert line.valves == frozenset(
248 ids[v]
for v
in (
"DR-CTRL-G",
"DR-CTRL-R",
"DR-MV",
"DR-Vent")
250 assert line.vents == frozenset({ids[
"DR-MV"], ids[
"DR-Vent"]})
251 assert built.valve_roles[ids[
"DR-CTRL-G"]] == frozenset(
252 {
"gse",
"med",
"press",
"control"}
256@pytest.mark.skipif(not TABLES.is_dir(), reason=
"no state machine tables")
258 """Unloaded, the 1092 holds its 50 psi spring bias less its supply effect --
259 with the COPV charged, less than nothing, so the tank does not press. GN2 Low Press
260 opens DR-CTRL-G ("GSE Med Press Control") and the dome follows DR-REG-G; the
261 next state shuts it and the dome keeps what is in it, as the stand keeps it
262 through the burn with the GSE disconnected. The dome PT reads it."""
267 assert session.binding.to_symbol[
"GSE Med Press Control"] == ids[
"DR-CTRL-G"]
268 session.state =
"GN2 High Press"
271 session.state =
"Fuel Press"
274 fuel = session.tanks[ids[
"Eth-Tank"]]
275 copv = session.bottles[ids[
"COPV"]]
277 supply = 14.7e-3 * psig(copv.pressure)
278 assert supply > 50.0,
"a charged COPV outweighs the spring bias"
279 assert psig(fuel.pressure) == pytest.approx(0.0, abs=2.0),
"unloaded: shut"
281 session.state =
"GN2 Low Press"
284 session.state =
"Fuel Press"
287 lockup = 500.0 + 50.0 - 14.7e-3 * psig(copv.pressure)
288 assert psig(fuel.pressure) == pytest.approx(
290 ),
"dome + bias - S x COPV"
291 dome_pt = next(i
for i
in session.model.built.instruments
if i.id == ids[
"DP-PT-R"])
292 assert psig(session.history[-1].pressures[dome_pt.node]) == pytest.approx(500.0)
293 assert ids[
"DR-CTRL-G"]
not in session.forced
294 assert psig(session._dome_held[ids[
"DPR_HP"]]) == pytest.approx(500.0)
301 """LE4 (6) with its LOX fill line finished, as ADR 0006 asks of the drawing:
302 the fill valve's junction to the fill manifold, the manifold to OF-QDA, and
303 the LOX vent disconnects paired."""
306 def line(i: str, s: str, sh: str, t: str, th: str) -> dict[str, Any]:
313 "type":
"smoothstep",
318 payload[
"edges"] += [
319 line(
"fix-1",
"junc_85",
"r",
"junc_86",
"r"),
320 line(
"fix-2", ids[
"OF-Manifold"],
"in", ids[
"OF-QDA"],
"r"),
322 for node
in payload[
"nodes"]:
323 if node[
"data"][
"label"] ==
"OV-QD-A":
324 node[
"data"].setdefault(
"options", {})[
"pairedWith"] = ids[
"QD-OV-B"]
331 assert ids[
"LOX-DW-350 PSI"]
in built.tanks
332 assert built.supplies[ids[
"LOX-DW-350 PSI"]] == frozenset({ids[
"LOX-Tank"]})
335@pytest.mark.skipif(not TABLES.is_dir(), reason=
"no state machine tables")
341 dewar, lox = session.tanks[ids[
"LOX-DW-350 PSI"]], session.tanks[ids[
"LOX-Tank"]]
342 assert psig(dewar.pressure) == pytest.approx(50.0, abs=0.5)
343 start = dewar.state.liquid_mass
344 session.state =
"Ox Fill"
347 assert lox.state.liquid_mass == pytest.approx(lox._wanted(), rel=0.03)
348 assert start - dewar.state.liquid_mass >= lox.state.liquid_mass
349 session.state =
"Armed"
352 assert psig(dewar.pressure) == pytest.approx(50.0, abs=5.0),
"its own circuit"
353 last = session.solver_log[-1]
354 assert abs(last.mass_error_kg - last.guard_kg) < 1e-3
357@pytest.mark.skipif(not TABLES.is_dir(), reason=
"no state machine tables")
359 """Topped to its load in Ox Fill, the LOX tank passes ~20 g/s of boil-off
360 through a 0.45 g ullage to its vent. The solve closed that ullage on the
361 stiffer of its gas-in and gas-out slopes, and over boiling LOX gas in is
362 eight times stiffer: the node the vent flowed from sat at 3.1 psig while
363 the tank read 7.2, a gap that grew with the coupling step. And the 10 %
364 mass rule counted the vented gas, so the whole stand was solved nine
365 times every 20 ms -- the fill ran at a third of real time. A venting
366 ullage is closed on its own gas-out slope, and gas gone to the sky asks
367 for no extra solves."""
372 lox = session.tanks[ids[
"LOX-Tank"]]
373 session.state =
"Ox Fill"
376 assert lox.state.liquid_mass == pytest.approx(lox._wanted(), rel=0.01)
378 gas_in, gas_out = session._split_at(lox.ullage_node, session._last_flows)
379 assert gas_out > 20.0 * lox.state.ullage.mass,
"the ullage turns over each 50 ms"
380 node = session._last_pressures[lox.ullage_node]
381 assert psig(lox.pressure) == pytest.approx(psig(node), abs=0.3)
382 assert session.solver_log[-1].couplings == 1
389 """LE4 (6) at T-0 with the engine on, then 0.3 s into Fire."""
397 design = engine_from_config(yaml.safe_load(ENGINE.read_text()), name=
"6800N")
398 model = assemble_model(
399 read_diagram(
_payload(), name=
"LE4 (6)"),
404 session = open_session(
405 model, load_machine(tables=TABLES), hookup=suggest(model, 500.0, 4500.0)
407 session.setup = replace(session.setup, ground_rests=rests)
408 jump_to_t0(session, copv_psi=4500.0, fill_fraction=0.95)
409 session.command_state(
"Fire")
413 most =
max(most, session.solver_log[-1].couplings)
418 not (ENGINE.exists()
and CEA.exists()
and TABLES.is_dir()),
419 reason=
"engine, CEA table or state machine tables absent",
422 """Fire on LE4 (6) with the cart drawn: every cart vessel is cut off from the
423 vehicle by shut valves, so none is integrated and its lines leave the solve,
424 and the burn is the one the whole stand integrated every step gives."""
430 for label
in (
"Fuel Transfer Tank",
"LOX-DW-350 PSI",
"6K-GN2")
431 if ids[label]
in rested.tanks
or ids[label]
in rested.bottles
433 branches, vessels = rested._resting
434 assert cart
and cart <= vessels
435 assert branches,
"the cart's lines are out of the solve"
436 assert not vessels & set(rested.vehicle_tanks)
437 held = {k: rested.tanks[k].state
for k
in vessels
if k
in rested.tanks}
439 for k, state
in held.items():
440 assert rested.tanks[k].state
is state,
"a resting vessel is not integrated"
442 for a, b
in zip(rested.history, worked.history):
443 assert a.chamber
is not None and b.chamber
is not None
444 assert a.chamber.thrust == pytest.approx(b.chamber.thrust, rel=1e-9, abs=1e-6)
448 not (ENGINE.exists()
and CEA.exists()
and TABLES.is_dir()),
449 reason=
"engine, CEA table or state machine tables absent",
452 """The fuel tank's top QD hangs a wide fitting off its ullage that ends at a
453 cap. It carries nothing, so it is no press path: priced as one at the drain
454 flow, it asked the first steps of Fire for 0.07 ms couplings -- 64 in a 20 ms
455 step here, ~300 on the cockpit's first tick, most of a second before the
456 first frame of the burn."""
457 session, most =
_fired(
True)
458 assert session.history[-1].chamber.thrust > 5000.0
459 assert most < 20, f
"{most} coupling steps in one 20 ms step"
463 not (ENGINE.exists()
and CEA.exists()
and TABLES.is_dir()),
464 reason=
"engine, CEA table or state machine tables absent",
467 """The vehicle carries what the competition allows (the engine config's
468 lox_tank.mass and fuel_tank.mass), not 95 % of the tanks drawn: LE4 (6)
469 draws both at 8.19 L, a third more LOX than a fire is loaded with."""
477 config = yaml.safe_load(ENGINE.read_text())
478 design = engine_from_config(config, name=
"6800N")
479 assert design.fire_load == {
480 "lox": pytest.approx(config[
"lox_tank"][
"mass"]),
481 "fuel": pytest.approx(config[
"fuel_tank"][
"mass"]),
483 model = assemble_model(
484 read_diagram(
_payload(), name=
"LE4 (6)"),
489 session = open_session(
490 model, load_machine(tables=TABLES), hookup=suggest(model, 500.0, 4500.0)
492 jump_to_t0(session, copv_psi=4500.0, fill_fraction=0.95)
494 lox, fuel = session.tanks[ids[
"LOX-Tank"]], session.tanks[ids[
"Eth-Tank"]]
495 assert lox.state.liquid_mass == pytest.approx(config[
"lox_tank"][
"mass"])
496 assert fuel.state.liquid_mass == pytest.approx(config[
"fuel_tank"][
"mass"])
499 assert lox._wanted() == pytest.approx(config[
"lox_tank"][
"mass"])
503 """A cart regulator the drawing sets is still turned by hand on the pad, so it
504 gets a knob -- starting where the drawing sets it. It used to get none, and the
505 dome and COPV fill started at 500 and 4,500 whatever the sheet said."""
511 settings = {
"PR-1": 3750,
"DR-REG-G": 535,
"LP-PR": 150}
512 for node
in payload[
"nodes"]:
513 label = node[
"data"][
"label"]
514 if label
in settings:
515 node[
"data"].setdefault(
"params", {})[
"setpoint"] = {
516 "value": settings[label],
518 "source":
"estimated",
520 model = assemble_model(read_diagram(payload, name=
"LE4"), diagram_id=
"le4")
521 hookup = suggest(model, 500.0, 4500.0)
522 knobs = {k.id: k
for k
in hookup.knobs}
523 assert knobs[DOME].regulators == (ids[
"DR-REG-G"],)
524 assert knobs[CHARGE].regulators == (ids[
"PR-1"],)
525 assert knobs[ids[
"LP-PR"]].regulators == (ids[
"LP-PR"],)
526 assert knob_starts(hookup, model) == {
527 DOME: pytest.approx(535.0),
528 CHARGE: pytest.approx(3750.0),
529 ids[
"LP-PR"]: pytest.approx(150.0),
531 assert knobs[DOME].psig == pytest.approx(535.0)
532 assert knobs[CHARGE].psig == pytest.approx(3750.0)
533 assert knobs[ids[
"LP-PR"]].psig == pytest.approx(150.0)
536@pytest.mark.skipif(not TABLES.is_dir(), reason=
"no state machine tables")
538 """The flight fuel tank's fill line stays open to it when dry so it can be
539 loaded -- in its fill state, and only then. Open always, with a dump open
540 beyond it (LE4's FD-ROT-G, uncommanded and resting open; here the cart's
541 hand dump and transfer valve opened, the transfer held by hand), the dry
542 tank "drained" through the line forever and the vessel's floor re-made the
543 mass: ~0.46 kg/s of propellant from nothing, booked as guard."""
548 for node
in payload[
"nodes"]:
549 if node[
"data"][
"label"] ==
"FF-MAN-Dump":
550 node[
"data"].setdefault(
"options", {})[
"normalPosition"] =
"open"
552 jump_to_t0(session, copv_psi=4000.0, fill_fraction=0.95)
553 session.release(ids[
"FF-MAN-Output"])
554 session.set_valve(ids[
"FF-MAN-Output"],
True)
555 fuel = session.tanks[ids[
"Eth-Tank"]]
559 (leaving,) = [n
for n
in session.history[-1].notes
if "losing ethanol" in n]
560 assert "FF-MAN-Dump" in leaving
and "nothing in the state table" in leaving
563 assert fuel.state.liquid_mass == pytest.approx(0.0, abs=1e-3)
564 guard = session.solver_log[-1].guard_kg
567 last = session.solver_log[-1]
568 assert last.guard_kg - guard < 0.05,
"the floor is re-making propellant"
569 assert abs(last.mass_error_kg - last.guard_kg) < 1e-6
572@pytest.mark.skipif(not TABLES.is_dir(), reason=
"no state machine tables")
574 """A fresh stand with a drawn load has the crew's transfer valve shut by the
575 twin. That is not the operator's hand: no "1 held", and Release leaves it."""
578 valve = ids[
"FF-MAN-Output"]
579 assert valve
in session.forced
580 assert session.operator_held == []
581 session.set_valve(ids[
"FM-R"],
True)
582 assert session.operator_held == [ids[
"FM-R"]]
584 assert session.operator_held == []
and valve
in session.forced
586 session.set_valve(valve,
True)
587 assert session.operator_held == [valve]
590@pytest.mark.skipif(not TABLES.is_dir(), reason=
"no state machine tables")
592 """Both run tanks primed over MAWP trip the stand at once. It named the
593 first tank it looked at (Eth-Tank, 935.7 psia) while the LOX tank stood
594 at 963.5 psia (D-33); it names the one furthest over its MAWP."""
599 fuel, lox = session.tanks[ids[
"Eth-Tank"]], session.tanks[ids[
"LOX-Tank"]]
600 assert fuel.mawp == pytest.approx(lox.mawp)
601 session.prime(fill_fraction=0.5, tank_psi=900.0, copv_psi=4000.0, state=
"Ready")
602 lox.state = lox.tank.initial_state(
603 pressure=from_psig(950.0),
604 liquid_mass=lox.state.liquid_mass,
605 liquid_temperature=lox.state.liquid_temperature,
606 gas_temperature=293.15,
608 assert fuel.pressure > fuel.mawp
and lox.pressure > fuel.pressure
609 session._check_limits()
610 assert session.trip
is not None and session.trip.vessel == ids[
"LOX-Tank"]
611 assert session.tripped
and session.tripped.startswith(
"LOX-Tank reached")
614@pytest.mark.skipif(not TABLES.is_dir(), reason=
"no state machine tables")
616 """The trip limit is the drawing's burst over a factor of two, and the trip
617 said "The tank would have failed" at half the burst. LE4's tank reliefs are
618 drawn at 750 psi, the trip limit itself, so they can never lift before it:
619 the message says so rather than leave the operator wondering why the drawn
620 relief did nothing."""
625 lox = session.tanks[ids[
"LOX-Tank"]]
626 session.prime(fill_fraction=0.5, tank_psi=500.0, copv_psi=4000.0, state=
"Ready")
630 label: branch.component.set_pressure / PSI
631 for branch_id, branch
in session._reliefs
632 for label
in [next(k
for k, v
in ids.items()
if v == branch_id)]
634 assert sets[
"RV_O"] == pytest.approx(psig(lox.mawp), abs=0.5), sets
635 assert sets[
"RV-HPF"] > psig(lox.mawp) + 1000.0, sets
636 lox.state = lox.tank.initial_state(
637 pressure=lox.mawp + 5.0 * PSI,
638 liquid_mass=lox.state.liquid_mass,
639 liquid_temperature=lox.state.liquid_temperature,
640 gas_temperature=293.15,
642 session._check_limits()
643 message = session.tripped
or ""
644 assert message.startswith(
"LOX-Tank reached"), message
645 assert "would have failed" not in message
646 assert "MAWP" in message
and "at risk" in message
647 assert "RV_O is set at or above that limit and cannot lift" in message, message
648 assert "RV-HPF" not in message,
"the COPV relief protects the COPV"
655 """The cart's Fuel Transfer Tank after the loads it gave: dry, as a real fill
656 leaves it (``TankSim.advance`` sets ``empty``, and a resting cart vessel is
657 never advanced again to clear it)."""
658 cart = session.tanks[
_ids()[
"Fuel Transfer Tank"]]
659 cart.state = replace(cart.state, liquid_mass=0.0)
665 not (ENGINE.exists()
and CEA.exists()
and TABLES.is_dir()),
666 reason=
"engine, CEA table or state machine tables absent",
669 """Only the vehicle's tanks end a burn. ``_burnout_check`` counted every tank
670 on the stand, so a cart transfer tank emptied by the loads it gave sent each
671 Fire to Vent at T+0.00 s with the flight tanks full ("Burnout at T+0.00 s:
672 Fuel Transfer Tank ran dry")."""
680 design = engine_from_config(yaml.safe_load(ENGINE.read_text()), name=
"6800N")
681 model = assemble_model(
682 read_diagram(
_payload(), name=
"LE4 (6)"),
687 session = open_session(
688 model, load_machine(tables=TABLES), hookup=suggest(model, 500.0, 4500.0)
690 jump_to_t0(session, copv_psi=4500.0, fill_fraction=0.95)
692 session.command_state(
"Fire")
695 assert session.burnout
is None
696 assert session.state ==
"Fire"
697 assert session.history[-1].chamber.thrust > 5000.0
700@pytest.mark.skipif(not TABLES.is_dir(), reason=
"no state machine tables")
702 """With no engine model the ENGINE symbol's design pressure is the boundary
703 while the feed can hold it. "The feed" counted the cart: a dewar at 50 psig
704 or a dry transfer tank under the 385 psia design kept the chamber at
705 atmosphere with both flight tanks pressed well above it."""
709 jump_to_t0(session, copv_psi=4000.0, fill_fraction=0.95)
711 session.command_state(
"Fire")
714 vehicle = [session.tanks[k]
for k
in session.vehicle_tanks]
715 assert min(sim.outlet_pressure
for sim
in vehicle) > session._design_chamber
716 chamber = session.model.built.network.nodes[session._chamber_node]
717 assert chamber.pressure == pytest.approx(session._design_chamber)
721 """HP-QD-A and HP-QD-B are paired and joined by their own line; OF-QDA has
722 no line at all, which is worth saying."""
723 unmated = [w
for w
in built.warnings
if "unmated" in w]
724 assert not [w
for w
in unmated
if "HP-QD" in w], unmated
725 assert [w
for w
in unmated
if "OF-QDA" in w],
"the real one is still said"
None test_ox_fill_loads_the_flight_lox_tank_from_the_dewar()
None test_a_cold_flow_is_lit_by_the_vehicle_tanks_not_the_cart()
None test_t0_loads_the_fire_load_not_the_tank()
None test_the_pad_charges_the_copv_and_loads_fuel_through_the_drawing()
None test_the_vehicle_is_what_is_joined_to_the_engine(Any built)
None test_a_trip_names_the_vessel_furthest_over()
None test_the_dome_line_is_gated_by_its_valves(Any built)
None test_a_motorised_valve_is_a_commanded_valve(Any built)
None test_a_pair_drawn_joined_is_not_called_unmated(Any built)
None test_the_transfer_tank_supplies_the_flight_fuel_tank(Any built)
None test_the_dome_line_loads_the_dome_and_carries_no_feed(Any built)
None test_every_hand_valve_rests_shut(Any built)
None test_while_the_engine_burns_the_cart_rests_and_the_burn_is_the_same()
None test_the_crews_transfer_valve_is_not_the_operators_hold()
None test_blank_loader_and_fill_start_at_what_they_feed()
None test_an_empty_cart_tank_is_not_a_burnout()
None test_ignition_is_not_stepped_on_a_capped_port()
None test_the_cart_regulators_drawn_with_settings_get_knobs_at_them()
None test_a_topped_lox_tank_on_its_vent_lands_where_the_solve_put_it()
None test_a_dry_tank_does_not_drain_back_through_its_fill_line()
None test_a_liquid_dewar_is_a_supply_tank()
None test_a_trip_says_what_it_is_and_which_reliefs_it_beat()
None test_the_gse_valves_bind_to_the_table()
Any _cart_tank_ran_dry(Any session)
Any _with_lox_fill_drawn()
None test_a_hand_valve_drawn_normally_open_rests_open()
None test_the_dome_loads_on_the_pad_and_holds_when_its_valve_shuts()
Any _session(Any|None payload=None)
None test_every_hand_loaded_regulator_gets_a_knob()