STAR
Cal STAR — firmware, DAQ server, and engine design
Loading...
Searching...
No Matches
test_failed_solve_holds.py
Go to the documentation of this file.
1"""A failed network solve holds the stand; it does not move it.
2
3``solve_steady(raise_on_failure=False)`` hands back wherever Newton stopped.
4That iterate closes no node balance, so a vessel integrated on it gains or loses
5propellant no path carried, and a gauge reading it shows a pressure nothing
6holds. Through a failed solve the session keeps the last converged flows and
7pressures; right after the circuit changes, when no converged answer for the
8new circuit exists yet, it moves nothing.
9
10Driven by failing the solve on purpose: its real answer, reported as not
11converged, with the flows or the free nodes' pressures scaled -- once a solve
12has not converged, one iterate is as good as another. Both tests go red on the
13session that integrated the iterate (``flows = self._last_flows or
14dict(result.flows)``, and ``result.pressures`` into the step and the frame).
15"""
16
17from __future__ import annotations
18
19from dataclasses import replace
20from typing import Any
21
22import pytest
23from test_press_path_coupling import _helium_stand
24
25from feedtwin.session import Session, core
26
27
29 monkeypatch: pytest.MonkeyPatch,
30 session: Session,
31 *,
32 flows: float = 1.0,
33 pressures: float = 1.0,
34) -> None:
35 """Every solve from here fails, its flows and free pressures scaled."""
36 nodes = session.model.built.network.nodes
37 real = core.solve_steady
38
39 def failed(*args: Any, **kwargs: Any) -> Any:
40 result = real(*args, **kwargs)
41 return replace(
42 result,
43 converged=False,
44 flows={b: f * flows for b, f in result.flows.items()},
45 pressures={
46 n: p if nodes[n].is_fixed else p * pressures
47 for n, p in result.pressures.items()
48 },
49 )
50
51 monkeypatch.setattr(core, "solve_steady", failed)
52
53
55 monkeypatch: pytest.MonkeyPatch,
56) -> None:
57 """Ready -> Fire is a new circuit: the last flows are dropped, and a solve
58 that then fails has nothing converged to hold. The tanks stay put."""
59 session = _helium_stand()
60 for _ in range(5):
61 session.step(0.02)
62 liquid = {k: sim.state.liquid_mass for k, sim in session.tanks.items()}
63 gas = {k: b.state.mass for k, b in session.bottles.items()}
64
65 _break(monkeypatch, session, flows=50.0)
66 session.set_valve("FM", True)
67 session.set_valve("OM", True)
68 sample = session.step(0.02)
69
70 assert not sample.converged
71 assert "FM" not in session._last_isolated, "the mains opened: a new circuit"
72 for k, sim in session.tanks.items():
73 # The iterate drained ~1 kg here: a burn's flow, fifty times over.
74 assert sim.state.liquid_mass == pytest.approx(liquid[k], abs=1e-4), k
75 for k, bottle in session.bottles.items():
76 assert bottle.state.mass == pytest.approx(gas[k], abs=1e-9), k
77 assert session.solver_log[-1].mass_error_kg == pytest.approx(0.0, abs=1e-9)
78
79 # A hold, not a stall: once the solve converges the tanks drain.
80 monkeypatch.undo()
81 for _ in range(5):
82 session.step(0.02)
83 assert session.history[-1].converged
84 for k, sim in session.tanks.items():
85 assert sim.state.liquid_mass < liquid[k] - 1e-3, k
86
87
89 monkeypatch: pytest.MonkeyPatch,
90) -> None:
91 """With a good solve behind it a failed tick holds that solve's flows, and
92 its pressures with them -- into the tank's supply clip, the frame, and the
93 value a trapped leg will later be held at."""
94 session = _helium_stand()
95 session.set_valve("FP", True)
96 session.set_valve("OP", True)
97 for _ in range(10):
98 session.step(0.02)
99 assert session.history[-1].converged
100 manifold = session.history[-1].pressures["MF"]
101
102 acted: list[float] = []
103 real_move = session._move_vessels
104
105 def spy(flows, pressures, *rest): # type: ignore[no-untyped-def]
106 acted.append(pressures["MF"])
107 return real_move(flows, pressures, *rest)
108
109 monkeypatch.setattr(session, "_move_vessels", spy)
110 _break(monkeypatch, session, pressures=3.0)
111 sample = session.step(0.02)
112
113 assert not sample.converged
114 assert acted and all(p == pytest.approx(manifold, rel=1e-12) for p in acted)
115 assert sample.pressures["MF"] == pytest.approx(manifold, rel=1e-12)
116 assert session._trapped["MF"] == pytest.approx(manifold, rel=1e-12)
None test_mains_opening_onto_a_failed_solve_move_no_propellant(pytest.MonkeyPatch monkeypatch)
None _break(pytest.MonkeyPatch monkeypatch, Session session, *, float flows=1.0, float pressures=1.0)
None test_a_failed_tick_acts_on_and_shows_the_last_converged_pressures(pytest.MonkeyPatch monkeypatch)
static void failed(sensors_device_t *d, int err, uint32_t now_us)