Skip to content

Commit db87517

Browse files
committed
test: characterization tests for Flight.step_simulation()
Verify the stepped-simulation API (run_simulation=False plus repeated step_simulation() calls) reproduces a one-shot simulate(): - initial _step_state; stepping reaches the finished state - stepped final state (t, y_sol) and the full solution array match simulate() to tight tolerance - post_process_simulation / initialize_prints_plots fire on finish (t_final, prints, plots present) - stepping after finished is a no-op Uses the parachute-free calisto flight, since parachute triggers are not yet migrated into the stepping path.
1 parent a6dda6a commit db87517

1 file changed

Lines changed: 94 additions & 0 deletions

File tree

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
"""Characterization tests for ``Flight.step_simulation()``.
2+
3+
The fork's stepped-simulation API (``run_simulation=False`` plus repeated
4+
``step_simulation()`` calls) must reproduce the one-shot ``simulate()`` result so
5+
that callers driving the flight one node at a time -- e.g. real-time control
6+
loops such as the Balloon Popping Challenge environment -- obtain the same
7+
trajectory. A parachute-free rocket (``calisto`` in the plain environment) is
8+
used because parachute triggers are not yet migrated into the stepping path.
9+
"""
10+
11+
import numpy as np
12+
13+
from rocketpy import Flight
14+
15+
# Standard parachute-free launch, matching the ``flight_calisto`` fixture.
16+
RAIL_LENGTH = 5.2
17+
INCLINATION = 85
18+
HEADING = 0
19+
20+
21+
def _make_flight(calisto, example_plain_env, run_simulation):
22+
return Flight(
23+
environment=example_plain_env,
24+
rocket=calisto,
25+
rail_length=RAIL_LENGTH,
26+
inclination=INCLINATION,
27+
heading=HEADING,
28+
terminate_on_apogee=False,
29+
run_simulation=run_simulation,
30+
)
31+
32+
33+
def _run_stepped(flight, max_steps=100000):
34+
"""Drive ``step_simulation`` to completion; return the number of calls."""
35+
steps = 0
36+
while not flight._step_state["finished"]:
37+
flight.step_simulation()
38+
steps += 1
39+
assert steps < max_steps, "stepped simulation did not terminate"
40+
return steps
41+
42+
43+
class TestStepSimulation:
44+
"""Stepping must match a one-shot ``simulate()`` and finalise correctly."""
45+
46+
def test_initial_step_state(self, calisto, example_plain_env):
47+
flight = _make_flight(calisto, example_plain_env, run_simulation=False)
48+
assert flight._step_state == {
49+
"phase_index": 0,
50+
"node_index": 0,
51+
"phase_initialized": False,
52+
"finished": False,
53+
}
54+
55+
def test_stepping_reaches_finished(self, calisto, example_plain_env):
56+
flight = _make_flight(calisto, example_plain_env, run_simulation=False)
57+
assert flight._step_state["finished"] is False
58+
_run_stepped(flight)
59+
assert flight._step_state["finished"] is True
60+
61+
def test_stepped_final_state_matches_simulate(self, calisto, example_plain_env):
62+
reference = _make_flight(calisto, example_plain_env, run_simulation=True)
63+
stepped = _make_flight(calisto, example_plain_env, run_simulation=False)
64+
_run_stepped(stepped)
65+
np.testing.assert_allclose(stepped.t, reference.t, rtol=1e-9, atol=1e-9)
66+
np.testing.assert_allclose(stepped.y_sol, reference.y_sol, rtol=1e-8, atol=1e-9)
67+
68+
def test_stepped_full_solution_matches_simulate(self, calisto, example_plain_env):
69+
reference = _make_flight(calisto, example_plain_env, run_simulation=True)
70+
stepped = _make_flight(calisto, example_plain_env, run_simulation=False)
71+
_run_stepped(stepped)
72+
reference_solution = np.array(reference.solution)
73+
stepped_solution = np.array(stepped.solution)
74+
assert stepped_solution.shape == reference_solution.shape
75+
np.testing.assert_allclose(
76+
stepped_solution, reference_solution, rtol=1e-8, atol=1e-9
77+
)
78+
79+
def test_post_process_artifacts_after_stepping(self, calisto, example_plain_env):
80+
stepped = _make_flight(calisto, example_plain_env, run_simulation=False)
81+
_run_stepped(stepped)
82+
# post_process_simulation() and initialize_prints_plots() fire on finish.
83+
assert stepped.t_final == stepped.t
84+
assert stepped.prints is not None
85+
assert stepped.plots is not None
86+
87+
def test_step_after_finished_is_noop(self, calisto, example_plain_env):
88+
stepped = _make_flight(calisto, example_plain_env, run_simulation=False)
89+
_run_stepped(stepped)
90+
t_final = stepped.t
91+
y_final = np.array(stepped.y_sol)
92+
stepped.step_simulation() # already finished -> must return immediately
93+
assert stepped.t == t_final
94+
np.testing.assert_array_equal(stepped.y_sol, y_final)

0 commit comments

Comments
 (0)