14from test_session_burn
import CEA, ENGINE, STAND, TABLES, needs_engine, needs_stand
21 t: float, thrust: float, ox: float, fuel: float, pc: float = 2.5e6
25 chamber = SimpleNamespace(
31 combustion=SimpleNamespace(cstar=1500.0, extrapolated=
False),
33 return SimpleNamespace(t=t, chamber=chamber, balance=
None)
38 t = [0.1, 0.15, 0.25, 0.4]
39 thrust = [1000.0, 2000.0, 2100.0, 1900.0]
40 ox = [0.3, 0.6, 0.6, 0.66]
41 fuel = [0.2, 0.4, 0.4, 0.34]
42 r = summarise(t, thrust, [2e6] * 4, ox, fuel, before=0.0)
43 dt = [0.1, 0.05, 0.1, 0.15]
44 impulse = sum(f * d
for f, d
in zip(thrust, dt))
45 assert r.impulse_Ns == pytest.approx(impulse, rel=1e-12)
46 assert r.end_s - r.start_s == pytest.approx(0.4)
47 assert r.thrust_mean_N == pytest.approx(impulse / 0.4)
48 burned_ox = sum(m * d
for m, d
in zip(ox, dt))
49 burned_fuel = sum(m * d
for m, d
in zip(fuel, dt))
51 assert r.of_mean == pytest.approx(burned_ox / burned_fuel)
52 assert r.of_mean != pytest.approx(
53 sum(o / f
for o, f
in zip(ox, fuel)) / 4, rel=1e-3
55 assert r.isp_s == pytest.approx(impulse / (GRAVITY * (burned_ox + burned_fuel)))
57 assert r.thrust_min_N == 1900.0
58 assert r.of_min == pytest.approx(1.5)
and r.of_max == pytest.approx(0.66 / 0.34)
62 history = [
_sample(0.0, 0, 0, 0)]
63 history += [
_sample(0.02 * k, 2000.0, 0.6, 0.4)
for k
in range(1, 6)]
65 history += [
_sample(0.14 + 0.02 * k, 1000.0, 0.3, 0.2)
for k
in range(1, 4)]
66 found = burns(history)
67 assert len(found) == 2
70 history += [
_sample(0.22, 2000.0, 0.0, 0.4),
_sample(0.24, 2000.0, 0.0, 0.4)]
71 assert burns(history)[-1].impulse_Ns == pytest.approx(found[-1].impulse_Ns)
72 assert not burns(history)[-1].burning
74 assert first.start_s == 0.0
and first.end_s == pytest.approx(0.10)
75 assert first.impulse_Ns == pytest.approx(2000.0 * 0.10)
76 assert not first.burning
77 assert second.start_s == pytest.approx(0.14)
78 assert second.impulse_Ns == pytest.approx(1000.0 * 0.06)
79 assert second.burning,
"the history ends mid-burn: running totals, said so"
85 """The cockpit's way (find the burn in the samples) against the burn's own trace,
86 step for step: same impulse, same propellant, to rounding."""
87 from test_session_burn
import _model
92 session = open_session(
93 _model(engine=
True), load_machine(tables=TABLES), setup=burn_setup()
98 loads={
"OXT": 6.0,
"FUT": 4.0},
104 session, plan, also=
lambda clock, sample, firing: seen.append(sample)
106 inlets = trace.probes.injector_inlet
107 (found,) = burns(seen, inlets)
109 firing = [i
for i, f
in enumerate(trace.firing)
if f]
111 trace.chamber[
"thrust_N"][i] * (trace.t[i] - trace.t[i - 1])
for i
in firing
114 trace.chamber[
"mdot_oxidiser"][i] * (trace.t[i] - trace.t[i - 1])
117 assert found.steps == len(firing)
118 assert found.impulse_Ns == pytest.approx(impulse, rel=1e-9)
119 assert found.oxidiser_kg == pytest.approx(ox, rel=1e-9)
121 total = [ch[
"mdot_oxidiser"][i] + ch[
"mdot_fuel"][i]
for i
in firing]
122 median = sorted(total)[len(total) // 2]
123 full = [i
for i, m
in zip(firing, total)
if m >= 0.8 * median]
125 (
"oxidiser",
"stiffness_oxidiser_min"),
126 (
"fuel",
"stiffness_fuel_min"),
128 pc, inlet = ch[
"pressure_Pa"], trace.pressure[inlets[side]]
129 lowest =
min((inlet[i] - pc[i]) / pc[i]
for i
in full)
130 assert getattr(found, key) == pytest.approx(lowest, rel=1e-12), side
131 assert getattr(found, key) > 0.05, side
136 from dataclasses
import replace
141 if not ENGINE.exists():
142 pytest.skip(
"engine config absent")
143 design = engine_from_config(yaml.safe_load(ENGINE.read_text()), name=
"7000N")
145 constant_card(throat=design.throat_area),
146 provenance={
"ambient_pa_sampled": 94070.0},
148 installed, chamber = card.install(design)
149 assert installed.oxidiser.card
is card.oxidiser
and installed.fuel.card
is card.fuel
150 assert isinstance(chamber, CardChamber)
151 assert chamber.ambient_pressure == 94070.0
152 assert card.install(design, ambient_pressure=1e5)[1].ambient_pressure == 1e5
153 with pytest.raises(ValueError, match=
"throat"):
154 constant_card(throat=design.throat_area * 1.1).install(design)