Skip to content

Commit e716222

Browse files
TST: cover continuous controller per-step invocation and state_history layout
Adds an integration test that runs a real flight with a continuous air-brakes controller (sampling_rate=None) and asserts it is invoked on every solver step (>50 calls), always receives sampling_rate=None, and receives time-prefixed state_history rows (len == len(state)+1) -- the same layout as the discrete path. Previously only node-skipping was covered; per-step invocation was not. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 29de290 commit e716222

1 file changed

Lines changed: 44 additions & 0 deletions

File tree

tests/integration/simulation/test_flight.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -873,3 +873,47 @@ def test_environment_methods_accessible_in_controller(
873873

874874
# Verify all environment methods were successfully called
875875
assert all(methods_called.values()), f"Not all methods called: {methods_called}"
876+
877+
878+
def test_continuous_controller_invoked_every_step(calisto_robust, example_plain_env):
879+
"""A continuous controller (sampling_rate=None) must be called on every
880+
solver step and receive the same state_history layout as a discrete one:
881+
time-prefixed rows (`[t, *state]`), one element longer than ``state``.
882+
This locks in the discrete/continuous parity contract."""
883+
calls = {"count": 0, "sampling_rates": set(), "row_len_matches": True}
884+
885+
def recording_controller( # pylint: disable=unused-argument
886+
time, sampling_rate, state, state_history, observed_variables, air_brakes
887+
):
888+
calls["count"] += 1
889+
calls["sampling_rates"].add(sampling_rate)
890+
# state_history rows are time-prefixed: exactly one longer than state
891+
if len(state_history[-1]) != len(state) + 1:
892+
calls["row_len_matches"] = False
893+
return None
894+
895+
calisto_robust.parachutes = []
896+
calisto_robust.add_air_brakes(
897+
drag_coefficient_curve="data/rockets/calisto/air_brakes_cd.csv",
898+
controller_function=recording_controller,
899+
sampling_rate=None, # continuous
900+
clamp=True,
901+
)
902+
903+
flight = Flight(
904+
rocket=calisto_robust,
905+
environment=example_plain_env,
906+
rail_length=5.2,
907+
inclination=85,
908+
heading=0,
909+
time_overshoot=False,
910+
terminate_on_apogee=True,
911+
)
912+
913+
assert flight.t_final > 0
914+
# Called many times (once per solver step), far more than any fixed rate
915+
assert calls["count"] > 50
916+
# The controller always saw sampling_rate=None (continuous)
917+
assert calls["sampling_rates"] == {None}
918+
# And time-prefixed rows, consistent with the discrete controller path
919+
assert calls["row_len_matches"]

0 commit comments

Comments
 (0)