Skip to content

Commit a50d07d

Browse files
thc1006Gui-FernandesBRclaude
authored
ENH: continue a Monte Carlo study from the root its rows were drawn with (#1187)
* ENH: continue a Monte Carlo study from the root its rows were drawn with A Monte Carlo run cannot be seeded on develop: simulate() takes no random_seed. Seeding it per simulation index is not enough on its own, because an append then derives a fresh root and writes it into the same file, so a study resumed after a restart holds two lineages with nothing afterwards to say which simulation came from which. Both halves are here. A simulation takes its seed from its own index, so a serial run and a run split over workers draw the same inputs for the same index. Every input row records the root that drew it, and an append reads it back rather than needing to be given it again. A seed that disagrees with the rows is refused, as is a log whose rows disagree with each other, and one whose rows carry no root at all, which is how a log written before this looks. Output rows carry a digest of that root, so a log belonging to another study is refused even when its indices line up with this one's. The worker tests in #1182 drive the producer with a stand-in monitor, so they move to the claim along with it. A reseed failure now names the index it was seeding for rather than worker startup, because the seeding happens after the claim rather than once above the loop. The seeding half was #1054, closed in favour of this. Addresses #1053 and #1075. Signed-off-by: thc1006 <84045975+thc1006@users.noreply.github.com> * TST: carry #1177's interrupt tests onto the reseeded serial loop The rebase onto develop was clean in three files and left one add/add conflict in __run_in_serial: both sides bind a name before the try so the handlers cannot meet it unbound. #1177 binds inputs_json for the KeyboardInterrupt handler, this branch binds sim_idx for the Exception one. Both are kept; dropping either puts back the UnboundLocalError the other side had just removed. Keeping both then pushed the function to 27 statements, two over max-statements, so the error-file append the two handlers had copies of moved into __record_failed_inputs. The guard travels with it: the error file is created in __setup_files, so appending "" to it was already a no-op, and the unconditional copy and the guarded one did the same thing. What the merge could not show is that #1177's tests were written against the loop this branch replaces: - _InterruptingMonteCarlo stubs only what __run_in_serial touched, and the loop now also calls __seed_this_simulation, which reads models the double has none of. Stubbed, like the other name-mangled members; seeding is pinned by test_monte_carlo_seeding.py. - Its rows carried no run_root, so the append test tripped the guard for studies written before that check existed instead of exercising the continuation. The double now writes the root the way the real row builders do, the inputs file whole and the outputs file by digest. - _SimMonitor.keep_simulating is now claim_next_index, and one test monkeypatches it by name. - Serial numbering now starts at zero, as the parallel path always did, so the three tests reading indices off disk expected 1, 2, 3 where a run writes 0, 1, 2. tests/unit/simulation: 322 passed, 8 skipped. ruff clean, pylint 10.00. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * DOC: record the serial renumbering and the refused append as changes The entry this branch added sits under Added, and both of these are visible to someone whose code already reads a study off disk: a serial run now numbers its simulations from zero, so the index field shifts by one, and an append onto a study written before this release is refused rather than continued. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * DOC: say how a run numbers its simulations Reviewing the Changed entry @Gui-FernandesBR added turned up a gap on my side: the serial renumbering is a break for anyone reading indices off disk, and the only place it was written down was the changelog. The guide's note leans on it without saying so. It claims simulation 7 draws the same inputs serially or split over workers, which is only one claim if both paths agree on which simulation 7 is, so the note now says a run of n numbers them 0 to n - 1. The notebook says it where a reader meets simulate(). Its stored outputs predate this and are left alone: nbsphinx never re-executes them, and a seeding change moves every number in them, which is not a diff worth reading. Signed-off-by: thc1006 <84045975+thc1006@users.noreply.github.com> * TST: take the wall clock out of the shared-deadline test This branch's CI went red on windows-latest 3.10 with assert min(offered) < 0.05 E assert 0.0500000000001819 < 0.05 The test lives on develop rather than in this branch, and #1182 is where I added it. The stand-in workers return at once, so nothing makes the clock move between _wait_for_the_workers setting its deadline and reading it back for the last of them, and (t + 0.05) - t is not exactly 0.05 in binary. Every other leg passed, and so did the same job on 3.14. The clock is a counter now, so what each worker is offered is decided by arithmetic rather than by how coarse the platform's timer is. The shape the test is about is asserted as well: inside one stage the offers shrink along the fleet, where one deadline each would hand every worker the whole grace. Giving each worker its own grace turns both assertions red and leaves the other twelve in the file green. It sits here because it blocks this pull request's CI, and it is a test-only change to a file this branch does not otherwise touch. Happy to move it to its own pull request if you would rather keep the two apart. Signed-off-by: thc1006 <84045975+thc1006@users.noreply.github.com> --------- Signed-off-by: thc1006 <84045975+thc1006@users.noreply.github.com> Co-authored-by: Gui-FernandesBR <guilherme_fernandes@usp.br> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent ebe1b77 commit a50d07d

10 files changed

Lines changed: 1331 additions & 71 deletions

‎CHANGELOG.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ Attention: The newest changes should be on top -->
3232

3333
### Added
3434

35+
- ENH: Reproducible Monte Carlo seeding, and an append that continues the same study [#1187](https://github.com/RocketPy-Team/RocketPy/pull/1187) [#1053](https://github.com/RocketPy-Team/RocketPy/issues/1053) [#1075](https://github.com/RocketPy-Team/RocketPy/issues/1075)
3536
- ENH: 3-DOF single rail button flight phase (tip-off analysis), enabled with the opt-in `Flight(use_udot_rail2=True)`. Between the upper rail button leaving the rail and the lower one following it, the rocket pivots about the lower button under a solved constraint wrench instead of jumping straight to free 6-DOF flight. The window is reported as `between_rails_time`, `between_rails_velocity` and `tip_off_duration`, printed by `Flight.info()` and shaded in the attitude plots. Requires `simulation_mode="6 DOF"` and `equations_of_motion="standard"`. [#920](https://github.com/RocketPy-Team/RocketPy/pull/920)
3637
- ENH: Support fixed-time parachute deployment triggers [#1133](https://github.com/RocketPy-Team/RocketPy/pull/1133) [#437](https://github.com/RocketPy-Team/RocketPy/issues/437)
3738
- DOC: Add SIL parachute ejection integration example [#1131](https://github.com/RocketPy-Team/RocketPy/pull/1131) [#524](https://github.com/RocketPy-Team/RocketPy/issues/524)
@@ -51,6 +52,7 @@ Attention: The newest changes should be on top -->
5152

5253
### Changed
5354

55+
- ENH: Number the simulations of a serial Monte Carlo run from zero, as the parallel path already did — the two used to name the same simulation `1, 2, 3` and `0, 1, 2`. The `index` field written into the inputs and outputs rows shifts by one for serial runs, so code reading indices off those files has to expect a run of `n` simulations to be numbered `0` to `n - 1`. An append onto a study written before this release is refused rather than continued, since its rows do not record which root drew them. [#1187](https://github.com/RocketPy-Team/RocketPy/pull/1187)
5456
- MNT: Store the reference-area correction factor on each rocket aero surface component, so it is carried with the surface instead of being recomputed at every lift evaluation. `Rocket.aerodynamic_surfaces`, `rail_buttons` and `sensors` now yield `(component, position, ref_factor)`, so code that unpacks a pair from them (`for surface, position in rocket.aerodynamic_surfaces`) has to take the third field or absorb it. Simulation results are unchanged, and `.rpy` files written before this still load. [#1129](https://github.com/RocketPy-Team/RocketPy/pull/1129) [#561](https://github.com/RocketPy-Team/RocketPy/issues/561)
5557
- ENH: Compute the rocket static margin lazily [#1135](https://github.com/RocketPy-Team/RocketPy/pull/1135) [#780](https://github.com/RocketPy-Team/RocketPy/issues/780)
5658
- DOC: Tighten the comments that came with the sampler seed groups [#1154](https://github.com/RocketPy-Team/RocketPy/pull/1154)

‎docs/notebooks/monte_carlo_analysis/monte_carlo_class_usage.ipynb‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -772,7 +772,9 @@
772772
"Finally, let's simulate our flights. \n",
773773
"We can run the simulations using the method `MonteCarlo.simulate()`.\n",
774774
"\n",
775-
"Set `append=False` to overwrite the previous results, or `append=True` to add the new results to the previous ones.\n"
775+
"Set `append=False` to overwrite the previous results, or `append=True` to add the new results to the previous ones.\n",
776+
"\n",
777+
"The rows a run writes are numbered from zero, serially and in parallel alike. An append carries on the study already in the file. Every input row records the seed root it was drawn from, so the seed does not have to be given again, and one that disagrees with the rows is refused rather than mixed in. A log written before this behaviour existed carries no root, so an append onto one is refused too: run the study again to write a log that can be continued.\n"
776778
]
777779
},
778780
{

‎docs/user/stochastic.rst‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,25 @@ draws under a fixed seed. The rocket's own inputs, such as ``mass`` and
341341
one model. Sharing one between two components leaves each of them seeding it
342342
from their own child, and the last one to be reset decides what both draw.
343343

344+
.. note::
345+
A whole run is fixed by ``MonteCarlo.simulate(random_seed=...)`` rather than
346+
by seeding these models yourself. Each simulation takes its seed from its own
347+
index, so simulation 7 draws the same inputs whether the run was serial or
348+
split over any number of workers, and whether it was reached first or last.
349+
A run of ``n`` simulations numbers them 0 to ``n - 1`` either way, which is
350+
what makes one index name one simulation.
351+
Every input row records the root it came from, so appending carries that
352+
study on whether or not the seed is given again, and a different one is
353+
refused rather than mixed in. Without a seed a run draws fresh entropy and
354+
reproduces nothing.
355+
356+
An index fixes the draw, not the object it is drawn around. Where the note
357+
above says a value follows its deterministic object, moving that object
358+
between runs still moves what is sampled, however the seed was set. A
359+
``CustomSampler`` that draws from the process-global ``numpy.random``
360+
rather than from the generator it is handed sits outside all of this, as
361+
:ref:`custom_sampler` warns.
362+
344363
Conclusion
345364
----------
346365

0 commit comments

Comments
 (0)