BUG: draw the eccentricities again, whichever way they were added - #1167
Conversation
RocketPy-Team#1122 made dict_generator walk the inputs a model declared instead of every attribute on it, which is the right shape for RocketPy-Team#1109. add_cp_eccentricity and add_thrust_eccentricity run after __init__ has built that list, so their distributions stopped being drawn from: the value was set on the instance and every simulation used the same one, with nothing to say so. Bisected: at 3e16c9f all four eccentricities appear in the generated dictionary, at 5a71eb9 none of them do. An add_* method now declares what it installed, with the argument as given rather than the validated form, so _set_stochastic validates it again on each reseed and binds the distribution to the generator that is live then. ensemble_member was already fine, since StochasticEnvironment passes it through the constructor and the hasattr guard covers it not being set yet. Signed-off-by: thc1006 <84045975+thc1006@users.noreply.github.com>
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## develop #1167 +/- ##
===========================================
+ Coverage 84.33% 84.38% +0.04%
===========================================
Files 130 130
Lines 17266 17274 +8
===========================================
+ Hits 14562 14577 +15
+ Misses 2704 2697 -7 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
Verified and merging. One correction for the record, because it matters for the changelog entry: the mechanism you describe is right, but the symptom in the PR description is not. The description says "every simulation uses the same one". It does not — What actually breaks is seed reproducibility. Because the eccentricities are not declared, So the user-visible bug is "a fixed seed did not reproduce a Monte Carlo run whose eccentricities were added via Also worth a follow-up (not blocking): now that the eccentricities are declared, Local checks on your branch: |
#1167 declared the eccentricities that add_cp_eccentricity and add_thrust_eccentricity install, so dict_generator draws them. _create_eccentricities then drew them a second time and overwrote the first value. The exported inputs matched the applied ones only because the second write wins, and the extra draw moved every component position create_object places after it. Read what has been drawn already, and draw only a half the caller left out, which is not a declared input and so never reaches dict_generator. The docstring correction is the explanation #1167 landed with, which named the wrong symptom: the value did vary between simulations, what a fixed seed failed to do was reproduce it. Signed-off-by: thc1006 <84045975+thc1006@users.noreply.github.com>
Pull request type
Checklist
ruff check/ruff format --check,pylint) has passed locallypytest tests/unit tests/integration) have passed locallyCurrent behavior
#1122 made
dict_generatorwalk the inputs a model declared rather than every attribute on it. That is the right shape for #1109, andinitial_solutionis correctly out of the draw now.The declared list is built in
__init__.add_cp_eccentricityandadd_thrust_eccentricityrun after that, so what they install is never declared, and_set_stochastic(seed)does not re-validate it.That matters because validation is what binds a distribution to a generator. An undeclared eccentricity keeps the one built in
__init__, which no seed reaches, so a fixed seed does not reproduce a run whose eccentricities were added throughadd_*. The values are not frozen:create_objectreaches them through_create_eccentricitiesand_randomize_position, which draws afresh every simulation. They simply do not answer torandom_seed.Four
create_object()calls withadd_cp_eccentricity(x=(0.0, 0.01), y=(0.0, 0.01)), readingcp_eccentricity_x:Thanks to @Gui-FernandesBR for catching that my first description had the symptom wrong. I had read the eccentricities out of
dict_generator, seen them absent and concluded the value was stuck, without checking whatcreate_objectactually does with them.Bisected on
developby reading the generated dictionary, which is where the declaration went missing:ensemble_memberwas fine either way:StochasticEnvironmentpasses it through the constructor, so it is declared, and thehasattrguard #1122 added covers_validate_ensemblenot having set it yet. It is the fields no constructor declares that fall out.New behavior
An
add_*method that installs a distribution declares it, so the draw reaches it again.What is declared is the argument as given, not the validated form.
_set_stochasticvalidates every declared input again on each reseed, and validation binds a distribution to the generator that is live at the time, so handing it the raw argument is what ties the draw to the reseed rather than to whichever one happened to come first._validate_eccentricitydispatches to the same_validate_tuple/_validate_scalar/_validate_listthat_set_stochasticuses, so the two agree on what a spec means.Noneis not declared, since there is nothing to draw.Breaking change
Runs that set an eccentricity spread were not reproducible from their seed. Their values change, which is the bug being fixed.
Additional information
Three tests: the four names appear in the generated dictionary for each
add_*method, the values are numbers, and the same seed twice gives the same eccentricity while a different seed moves it. Dropping any one of the four declarations turns them red.Local run against
developat be195d4: ruff clean, pylint exit 0,pytest tests/unit tests/integration2261 passed, 51 skipped.Found while rebasing #1054, which has an eccentricity regression test of its own that went red on the same change.