87 """The cockpit's way: the engine from its YAML, the card as JSON off the wire, the cockpit's
88 own Setup, put at Layer X's T-0, Fire commanded, ticked at the console's step."""
92 from feedtwin.session import Session, Setup, assemble_model, bind, load_machine
97 text = yaml.safe_dump(config.model_dump(mode=
"json"), sort_keys=
False)
98 engine = engine_from_config(yaml.safe_load(text), name=
"engine")
101 wire = json.loads(json.dumps(card_for_config_text(text)))
102 engine, chamber = EngineCard.from_dict(wire[
"card"]).install(engine)
103 cea = cea_table_path(config)
104 model = assemble_model(read_diagram(prepared.drawing.payload, name=GN2), diagram_id=GN2, engine=engine,
105 chamber=chamber, cea_cache=str(cea)
if cea
else "")
106 machine = load_machine(tables=machines_dir())
107 labels = {n.id: n.label
or n.id
for n
in model.diagram.nodes
if n.id
in model.built.actuators}
108 session = Session(model, machine, bind(machine, labels, roles=model.built.valve_roles),
109 setup=replace(
Setup(), dome_psi=prepared.setup.dome_psi, auto_vent=
False))
111 assert prime_at_t0(session, plan)
112 for _
in range(round(plan.lead_in_s / plan.dt)):
113 session.step(plan.dt)
114 session.command_state(plan.fire_state)
115 while session.t < 30.0
and min(t.state.liquid_mass
for t
in session.tanks.values()) >= plan.dry_kg:
116 session.step(LIVE_STEP)
117 (burn,) = burns(list(session.history), find_probes(session).injector_inlet)
132 cockpit =
_cockpit(prepared, config, card=
True)
133 psi = 6894.757293168361
134 names = ((
"thrust_mean_N",
"N", 1.0), (
"pc_mean_Pa",
"psia", psi), (
"of_mean",
"", 1.0), (
"isp_s",
"s", 1.0),
135 (
"impulse_Ns",
"N·s", 1.0), (
"duration_s",
"s", 1.0))
136 _table([(n.replace(
"_Pa",
""), u, getattr(layer_x, n) / k, getattr(cockpit, n) / k)
for n, u, k
in names])
137 for name
in (
"thrust_mean_N",
"pc_mean_Pa",
"of_mean",
"isp_s"):
138 assert getattr(cockpit, name) == pytest.approx(getattr(layer_x, name), rel=AGREE), name
140 tick = prepared.plan.dt + 0.02
141 assert cockpit.duration_s == pytest.approx(layer_x.duration_s, abs=tick)
144 gap = cockpit.impulse_Ns - layer_x.impulse_Ns
145 assert abs(gap) <= AGREE * layer_x.impulse_Ns + layer_x.thrust_mean_N *
max(prepared.plan.dt, LIVE_STEP)
147 assert abs(gap - layer_x.thrust_mean_N * (cockpit.duration_s - layer_x.duration_s)) <= AGREE * layer_x.impulse_Ns
158 """Layer X keeps its own summary (engine/layerx/analysis.py); the totals in it are the same
159 arithmetic as feedtwin.session.report's -- right-endpoint impulse, O/F and Isp from what was
160 burned -- so a number quoted by either tool is the same number."""
163 s = run_prepared(prepared, replay=
False)[
"summary"]
164 assert s[
"total_impulse_Ns"] == pytest.approx(layer_x.impulse_Ns, rel=1e-6)
165 assert s[
"mean_thrust_N"] == pytest.approx(layer_x.thrust_mean_N, rel=1e-6)
168 assert s[
"of_mean"] == pytest.approx(layer_x.of_mean, rel=1e-3)
169 assert s[
"isp_mean_s"] == pytest.approx(layer_x.isp_s, rel=1e-3)