Describe the bug
Parachute takes its pressure noise from the process-global NumPy RNG:
# rocketpy/rocket/parachute.py
self.noise_signal = [[-1e-6, np.random.normal(noise[0], noise[1])]] # line 270
...
+ beta * np.random.normal(noise[0], noise[1]) # line 285
__init_noise draws once when the object is built, and noise_function keeps drawing from that same global state every time Flight samples the trigger. Nothing in StochasticParachute reaches it. The stochastic model seeds cd_s, lag, the sampling rate and the other constructor parameters, but the noise generator is not part of that.
Flight adds this noise to the pressure it hands the trigger, and the trigger height is computed from the noisy pressure, so it can move the deployment time and with it the descent, the impact point and the flight time.
There is a second copy of the problem in StochasticRocket.create_object(). It builds one Parachute through stochastic_parachute.create_object() (line 749) to read the parameters off, then builds another through rocket.add_parachute(...) (line 841). Both constructions draw from the global RNG and the first object is thrown away.
To Reproduce
The shared fixtures already use non-zero noise, so nothing special is needed to reach this:
# tests/fixtures/parachutes/parachute_fixtures.py
noise=(0, 8.3, 0.5)
Run the same seed twice in one process, or the same simulation index under different worker counts, and compare parachute events or trajectory outputs rather than .inputs.txt. Under fork the workers inherit one global state, so their first simulations can share a noise stream. Under spawn and forkserver each process starts from unrelated entropy, so the same index can get different noise depending on which worker picked it up.
Expected behavior
A run identified by a root seed and a simulation index should fly the same trajectory, not only record the same sampled parameters.
Give Parachute its own generator and take the noise from it:
class Parachute:
def __init__(self, ..., rng=None):
self.rng = np.random.default_rng() if rng is None else rng
then derive a child seed for the runtime noise alongside the existing parameter seed, and make sure the Parachute that ends up on the Rocket is the one holding it. Removing the double construction would be worth doing at the same time, since it is what makes "which object got which stream" ambiguous.
Additional context
#1054 builds a per-index seed tree over the stochastic parameter objects. This is the largest built-in random source that tree does not reach, which is why that PR is scoped to reproducible sampled inputs rather than reproducible trajectories. Closing this gap is what would let the guarantee be stated in terms of results.
Worth auditing the other runtime random sources at the same time, sensors in particular, so the boundary can be stated once instead of per component.
Describe the bug
Parachutetakes its pressure noise from the process-global NumPy RNG:__init_noisedraws once when the object is built, andnoise_functionkeeps drawing from that same global state every timeFlightsamples the trigger. Nothing inStochasticParachutereaches it. The stochastic model seedscd_s,lag, the sampling rate and the other constructor parameters, but the noise generator is not part of that.Flightadds this noise to the pressure it hands the trigger, and the trigger height is computed from the noisy pressure, so it can move the deployment time and with it the descent, the impact point and the flight time.There is a second copy of the problem in
StochasticRocket.create_object(). It builds oneParachutethroughstochastic_parachute.create_object()(line 749) to read the parameters off, then builds another throughrocket.add_parachute(...)(line 841). Both constructions draw from the global RNG and the first object is thrown away.To Reproduce
The shared fixtures already use non-zero noise, so nothing special is needed to reach this:
Run the same seed twice in one process, or the same simulation index under different worker counts, and compare parachute events or trajectory outputs rather than
.inputs.txt. Underforkthe workers inherit one global state, so their first simulations can share a noise stream. Underspawnandforkservereach process starts from unrelated entropy, so the same index can get different noise depending on which worker picked it up.Expected behavior
A run identified by a root seed and a simulation index should fly the same trajectory, not only record the same sampled parameters.
Give
Parachuteits own generator and take the noise from it:then derive a child seed for the runtime noise alongside the existing parameter seed, and make sure the
Parachutethat ends up on theRocketis the one holding it. Removing the double construction would be worth doing at the same time, since it is what makes "which object got which stream" ambiguous.Additional context
#1054 builds a per-index seed tree over the stochastic parameter objects. This is the largest built-in random source that tree does not reach, which is why that PR is scoped to reproducible sampled inputs rather than reproducible trajectories. Closing this gap is what would let the guarantee be stated in terms of results.
Worth auditing the other runtime random sources at the same time, sensors in particular, so the boundary can be stated once instead of per component.