From 0f4b33f96ecd623b2e3c6398c702306184511ba4 Mon Sep 17 00:00:00 2001 From: thc1006 <84045975+thc1006@users.noreply.github.com> Date: Sat, 15 Aug 2026 05:29:23 +0800 Subject: [PATCH] BUG: build Monte Carlo flights with the configuration they were given __run_single_simulation writes the Flight constructor out by hand and stops at time_overshoot. StochasticFlight.create_object passes nine more: max_time, the two time steps, rtol, atol, name, equations_of_motion, ode_solver and simulation_mode. So a Monte Carlo run ignored the max time, the tolerances, the solver and the equations of motion the caller had set, and reset the simulation mode to the constructor default. The same rocket, environment and flight therefore flew differently under MonteCarlo than under StochasticFlight.create_object. #1070 added StochasticFlight's handling of these; this path never picked it up. Signed-off-by: thc1006 <84045975+thc1006@users.noreply.github.com> --- rocketpy/simulation/monte_carlo.py | 13 +++++++ tests/unit/simulation/test_monte_carlo.py | 46 +++++++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/rocketpy/simulation/monte_carlo.py b/rocketpy/simulation/monte_carlo.py index 2640bc5b1..7cbb37fc9 100644 --- a/rocketpy/simulation/monte_carlo.py +++ b/rocketpy/simulation/monte_carlo.py @@ -471,6 +471,19 @@ def __run_single_simulation(self): initial_solution=self.flight.initial_solution, terminate_on_apogee=self.flight.terminate_on_apogee, time_overshoot=self.flight.time_overshoot, + # The rest of what StochasticFlight.create_object passes. Left out + # here, a run ignored the max_time, tolerances, solver, equations of + # motion and simulation mode the caller had set, which is what #1070 + # added StochasticFlight's own handling of them for. + max_time=self.flight.max_time, + max_time_step=self.flight.obj.max_time_step, + min_time_step=self.flight.obj.min_time_step, + rtol=self.flight.obj.rtol, + atol=self.flight.obj.atol, + name=self.flight.obj.name, + equations_of_motion=self.flight.obj.equations_of_motion, + ode_solver=self.flight.obj.ode_solver, + simulation_mode=self.flight.obj.simulation_mode, ) def estimate_confidence_interval( diff --git a/tests/unit/simulation/test_monte_carlo.py b/tests/unit/simulation/test_monte_carlo.py index d3ef02be9..e0a8c6b5b 100644 --- a/tests/unit/simulation/test_monte_carlo.py +++ b/tests/unit/simulation/test_monte_carlo.py @@ -1,6 +1,7 @@ import csv import json import pathlib +import types from collections import namedtuple import matplotlib as plt @@ -550,3 +551,48 @@ def test_simulate_convergence_runs_until_max_when_not_converging(): assert mc.num_of_loaded_sims == 200 assert all(width > 0.5 for width in history) assert len(history) == 4 # 200 / 50 batches + + +def test_a_monte_carlo_flight_keeps_the_configuration_it_was_given(monkeypatch): + """A run must build the same ``Flight`` ``StochasticFlight`` would. + + Monte Carlo wrote out the constructor by hand and stopped at + ``time_overshoot``, so ``max_time``, the tolerances, the solver, the + equations of motion and the simulation mode were silently reset to their + defaults. #1070 added StochasticFlight's handling of exactly those. + """ + base = types.SimpleNamespace( + max_time_step=0.5, + min_time_step=0.01, + rtol=1e-9, + atol=1e-9, + name="named", + equations_of_motion="solid_propulsion", + ode_solver="RK23", + simulation_mode="native", + ) + stochastic_flight = types.SimpleNamespace( + obj=base, + max_time=123.0, + initial_solution=None, + terminate_on_apogee=True, + time_overshoot=False, + _randomize_rail_length=lambda: 5.0, + _randomize_inclination=lambda: 84.0, + _randomize_heading=lambda: 133.0, + ) + analysis = object.__new__(MonteCarlo) + analysis.flight = stochastic_flight + analysis.rocket = types.SimpleNamespace(create_object=lambda: "rocket") + analysis.environment = types.SimpleNamespace(create_object=lambda: "environment") + monkeypatch.setattr("rocketpy.simulation.monte_carlo.Flight", types.SimpleNamespace) + + flight = MonteCarlo._MonteCarlo__run_single_simulation(analysis) + + assert flight.max_time == 123.0 + assert (flight.rtol, flight.atol) == (1e-9, 1e-9) + assert (flight.max_time_step, flight.min_time_step) == (0.5, 0.01) + assert flight.ode_solver == "RK23" + assert flight.equations_of_motion == "solid_propulsion" + assert flight.simulation_mode == "native" + assert flight.name == "named"