STAR
Cal STAR — firmware, DAQ server, and engine design
Loading...
Searching...
No Matches
test_watch.py
Go to the documentation of this file.
1"""The watcher (``feedtwin.session.watch``), and the faults it found first.
2
3A clean LE4 (6) burn and abort raises no flag and keeps its mass; each check
4fires when its condition is put into a real stand; flags are episodes, not
5ticks. And the two faults the watcher turned up on its first runs:
6
7* The chamber of a drawing with no engine model counted any flow touching it
8 as "lit", including flow *out* of it: after a burn it stayed at its drawn
9 385 psia, and an Engine Abort's open mains drove 3 kg/s from the dead engine
10 back into the tanks -- 16 kg the mass balance could not account for.
11* A tank the cart loads through the drawing credited any liquid arriving at
12 its outlet, wherever it came from: at Fire on unpressed tanks the chamber's
13 push-back filled a full fuel tank 46 g a second, and on a longer pad
14 overfilled it ("tank is overfull") and stopped the stand.
15"""
16
17from __future__ import annotations
18
19from dataclasses import replace
20
21import pytest
22
23from feedtwin.session.burn import jump_to_t0
24from feedtwin.session.watch import CLOSE_AFTER_S, Watcher, _Seen
25from feedtwin.solve.steady import INITIAL_FLOW
26
27from test_ignore_gse import DRAWING, TABLES, _ids, _session
28
29needs_le4 = pytest.mark.skipif(
30 not (DRAWING.exists() and TABLES.is_dir()), reason="LE4 fixture or tables absent"
31)
32
33
34def _burn_and_abort(): # type: ignore[no-untyped-def]
35 session = _session(True)
36 jump_to_t0(
37 session,
38 copv_psi=session.setup.copv_target_psi,
39 fill_fraction=session.setup.full_fraction,
40 )
41 for state, seconds in (("Ready", 2.0), ("Fire", 8.0), ("Engine Abort", 8.0)):
42 session.state = state
43 for _ in range(int(seconds / 0.5)):
44 session.step(0.5)
45 return session
46
47
48@needs_le4
50 session = _burn_and_abort()
51 record = session.solver_log[-1]
52 assert record.mass_error_kg - record.guard_kg == pytest.approx(0.0, abs=1e-5)
53 assert [f.to_dict() for f in session.watcher.flags] == []
54
55
56@needs_le4
58 """After burnout the chamber is the air, not its drawn 385 psia."""
59 session = _burn_and_abort()
60 net = session.model.built.network
61 assert net.nodes[session._chamber_node].pressure == pytest.approx(101325.0)
62 assert all(sim.refused_kg == 0.0 for sim in session.tanks.values())
63
64
65@needs_le4
67 ids = _ids()
68 session = _session(False)
69 fuel = session.tanks[ids["Eth-Tank"]]
70 for state, steps in (("Armed", 1), ("Fuel Fill", 30), ("Armed", 1)):
71 session.state = state
72 for _ in range(steps):
73 session.step(0.5)
74 full = fuel.state.liquid_mass
75 assert full > 6.0
76 session.state = "Fire" # on tanks nobody pressed
77 for _ in range(4):
78 session.step(0.25)
79 assert fuel.state.liquid_mass <= full + 1e-9, "no liquid from the chamber"
80
81
82@needs_le4
84 ids = _ids()
85 session = _session(True)
86 session.state = "Armed"
87 session.step(0.5)
88 record = session.solver_log[-1]
89 fuel = session.tanks[ids["Eth-Tank"]]
90
91 watcher = Watcher()
92 watcher.observe(session, record)
93 assert not watcher.flags, "a stand at rest is clean"
94
95 fuel.sealed, fuel.floored_kg = True, fuel.floored_kg + 1e-3
96 branch = next(
97 b
98 for b in session.model.built.network.branches
99 if b not in session._last_isolated
100 )
101 session._last_flows = {**session._last_flows, branch: INITIAL_FLOW}
102 fuel.refused_kg += 1.0
103 watcher.observe(session, replace(record, t=record.t + 0.5))
104 checks = {f.check for f in watcher.flags}
105 assert {"sealed_gain", "stuck_guess", "liquid_from_nowhere"} <= checks
106
107
109 """Held for its hold before it opens; closed once gone for a second; one
110 row however many ticks it lasts."""
111 watcher = Watcher()
112 held = _Seen("held_open", "Eth-Tank (gas)", "stuck", hold=2.0)
113 for t in (0.0, 0.5, 1.0, 1.5):
114 watcher._update(t, [held])
115 assert not watcher.flags, "not yet held for two seconds"
116 for t in (2.0, 2.5, 3.0):
117 watcher._update(t, [held])
118 assert len(watcher.flags) == 1 and watcher.flags[0].started == 0.0
119 watcher._update(3.5, [])
120 assert watcher.flags[0].open, "a tick off is not the end of it"
121 watcher._update(3.0 + CLOSE_AFTER_S, [])
122 assert not watcher.flags[0].open and watcher.flags[0].last == 3.0
123 assert len(watcher.flags) == 1
None test_a_full_fuel_tank_does_not_fill_from_the_chamber()
Definition test_watch.py:66
None test_a_dead_engine_does_not_push_back_up_the_mains()
Definition test_watch.py:57
None test_a_burn_and_an_abort_raise_nothing_and_keep_their_mass()
Definition test_watch.py:49
None test_a_flag_is_an_episode()
None test_each_check_fires_on_its_condition()
Definition test_watch.py:83
_burn_and_abort()
Definition test_watch.py:34