51def _drive(run_endpoint) -> tuple[int, int | None]:
52 """Run `run_endpoint(session)` on a fresh loop; return (loop thread, spy thread)."""
53 captured: dict[str, int] = {}
55 def spy(*_args, **_kwargs):
56 captured[
"tid"] = threading.get_ident()
60 captured[
"loop"] = threading.get_ident()
61 session = get_session(
_request(
"compute-offload-test@berkeley.edu"))
62 assert session.app_state.has_config(),
"default config should be loaded"
64 with pytest.raises(HTTPException):
65 await run_endpoint(session, spy)
68 return captured[
"loop"], captured.get(
"tid")
88 async def run(session, spy):
89 monkeypatch.setattr(timeseries_router,
"compute_timeseries_results", spy)
90 profile = {
"profile_type":
"linear",
"start_pressure_psi": 750,
"end_pressure_psi": 525}
91 await timeseries_router.generate_timeseries(
92 timeseries_router.GenerateProfileRequest(
93 duration_s=5.0, n_steps=11, lox_profile=profile, fuel_profile=profile,
98 loop_tid, tid =
_drive(run)
99 assert tid
is not None,
"/api/timeseries/generate did not call compute_timeseries_results"
100 assert tid != loop_tid, (
101 "the Time-Series run went on the event-loop thread -- /api/health and every other "
102 "request wait the whole run (~23 s on CI). Run it through session.run_compute."
107 async def run(session, spy):
108 monkeypatch.setattr(timeseries_router,
"compute_timeseries_results", spy)
109 seg = {
"length_ratio": 1.0,
"type":
"linear",
"start_pressure_psi": 750,
"end_pressure_psi": 525}
110 await timeseries_router.generate_from_segments(
111 timeseries_router.SegmentsRequest(
112 duration_s=5.0, n_points=11, lox_segments=[seg], fuel_segments=[seg],
117 loop_tid, tid =
_drive(run)
118 assert tid
is not None,
"/api/timeseries/from-segments did not call compute_timeseries_results"
119 assert tid != loop_tid,
"the segments run went on the event-loop thread"
160 """Forward Mode's report describes the design the numbers came from."""
166 monkeypatch.setattr(config_switch,
"design_staleness",
lambda cfg: described.append(cfg))
168 handcheck_mod,
"handcheck",
169 lambda cfg, *_a, **_k: described.append(cfg)
or {
"rows": [],
"flags": []},
172 forward_report_mod,
"forward_report",
lambda cfg, *_a, **_k: described.append(cfg)
or {},
176 session = get_session(
_request(
"compute-snapshot-evaluate@berkeley.edu"))
177 solved_on = session.app_state.config
180 def solve(*_args, **_kwargs):
181 session.app_state.set_config(other)
182 return {
"P_ambient": 101325.0,
"elevation": 0.0}
184 monkeypatch.setattr(session.app_state.runner,
"evaluate", solve)
185 await evaluate_router.evaluate(
186 evaluate_router.EvaluateRequest(lox_pressure_psi=750, fuel_pressure_psi=600),
189 assert session.app_state.config
is other,
"the mid-solve write did not land; nothing tested"
190 return solved_on, other
192 solved_on, other = asyncio.run(
main())
193 assert len(described) == 3, described
194 assert all(cfg
is solved_on
for cfg
in described), (
195 "the report was built from a config written while the solve ran -- one design's numbers "
196 "against another's. Take session.app_state.snapshot() once, before the solve."
201 """The blowdown loop calls the engine every step; a mid-run edit must not split the burn."""
204 engines: list[str] = []
207 def evaluate(**_kwargs):
209 return {
"mdot_O": 1.0,
"mdot_F": 0.8}
214 blowdown,
"resolve_blowdown_tank_state",
lambda *_a, **_k: (0.02, 10.0,
None, 0.02, 8.0,
None),
218 session = get_session(
_request(
"compute-snapshot-blowdown@berkeley.edu"))
219 monkeypatch.setattr(session.app_state.runner,
"evaluate",
engine(
"started on"))
222 def simulate(*, evaluate_engine_fn, **_kwargs):
223 evaluate_engine_fn(5e6, 4e6)
224 session.app_state.set_config(other)
225 monkeypatch.setattr(session.app_state.runner,
"evaluate",
engine(
"written mid-run"))
226 evaluate_engine_fn(5e6, 4e6)
229 monkeypatch.setattr(blowdown,
"simulate_coupled_blowdown", simulate)
230 with pytest.raises(HTTPException):
231 await timeseries_router.generate_from_segments(
232 timeseries_router.SegmentsRequest(
233 duration_s=5.0, n_points=11, blowdown_mode=
True,
234 lox_initial_pressure_psi=600, fuel_initial_pressure_psi=550,
240 assert engines == [
"started on",
"started on"], (
241 f
"engine calls went to {engines}: the blowdown run switched runners when the config was "
242 "written mid-run. Take the runner once, from session.app_state.snapshot()."
247 """While the new runner builds (seconds, for a CEA cache), a solve that starts takes the old pair."""
248 from backend
import state
as state_mod
250 app = state_mod.AppState()
251 old_config, old_runner, new_config = object(), object(), object()
252 app.config, app.runner = old_config, old_runner
255 class BuildingRunner:
256 def __init__(self, config):
258 t = threading.Thread(target=
lambda: taken.append(app.snapshot()))
261 assert not t.is_alive(),
"snapshot() waited for the runner to build"
263 monkeypatch.setattr(state_mod,
"PintleEngineRunner", BuildingRunner)
264 app.set_config(new_config)
266 assert taken == [(old_config, old_runner)], (
267 "a solve starting while the runner built took the new config beside the old runner"
269 assert app.config
is new_config
and isinstance(app.runner, BuildingRunner)
284 time_array=[0.0, 1.0, 2.0],
285 thrust_array=[6800.0, 6800.0, 6800.0],
286 mdot_O_array=[1.9, 1.9, 1.9],
287 mdot_F_array=[1.2, 1.2, 1.2],
323 return control_router.MeasurementRequest(
324 P_copv=18.96e6, P_reg=6.89e6, P_u_fuel=3.5e6, P_u_ox=3.5e6, P_d_fuel=3.4e6, P_d_ox=3.4e6,
338 """Each builds the DDP controller (its engine wrapper) and, but for init, loops it."""
340 async def run(session, spy):
341 session.controller.controller =
None
342 monkeypatch.setattr(control_router,
"RobustDDPController", spy)
343 if endpoint ==
"init":
344 await control_router.init_controller(control_router.ControllerInitRequest(), session=session)
345 elif endpoint ==
"simulate":
346 await control_router.simulate_controller(
347 control_router.ControllerSimulateRequest(
353 await control_router.simulate_layer2_controller(
354 control_router.Layer2ControllerSimulateRequest(
355 thrust_curve_time=[0.0, 0.1], thrust_curve_values=[6800.0, 6800.0],
360 loop_tid, tid =
_drive(run)
361 assert tid
is not None, f
"/api/control/{endpoint} did not build the controller"
362 assert tid != loop_tid, f
"/api/control/{endpoint} ran its solve on the event-loop thread"