MNT: refactor parachute implementation#958
Conversation
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## develop #958 +/- ##
===========================================
+ Coverage 80.27% 81.42% +1.15%
===========================================
Files 104 118 +14
Lines 12769 15160 +2391
===========================================
+ Hits 10250 12344 +2094
- Misses 2519 2816 +297 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Pull request overview
Refactors RocketPy’s parachute architecture to support multiple parachute models by introducing an abstract Parachute base class, moving parachute EoM (u_dot) into parachute implementations, and updating the Rocket API + project examples/tests/docs to the new creation/attachment flow.
Changes:
- Introduces
rocketpy.rocket.parachutes.Parachute(abstract) and migrates the existing model toHemisphericalParachute. - Updates
Flightto call a per-parachuteu_dotvia a wrapper instead of hardcoding parachute dynamics inFlight. - Changes
Rocket.add_parachuteto accept a parachute object, and updates tests/docs/notebooks accordingly.
Reviewed changes
Copilot reviewed 38 out of 39 changed files in this pull request and generated 13 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/unit/stochastic/test_stochastic_parachute.py | Updates stochastic parachute unit test to expect HemisphericalParachute. |
| tests/unit/rocket/test_parachute.py | Migrates parachute unit tests from Parachute to HemisphericalParachute (incl. serialization tests). |
| tests/integration/simulation/test_flight.py | Updates integration flight test to construct parachutes explicitly and then add them to the rocket. |
| tests/fixtures/rockets/rocket_fixtures.py | Updates rocket fixtures to use HemisphericalParachute objects before add_parachute. |
| tests/fixtures/parachutes/parachute_fixtures.py | Updates parachute fixtures to instantiate HemisphericalParachute. |
| tests/acceptance/test_ndrt_2020_rocket.py | Updates acceptance test parachute creation + attachment to new API. |
| tests/acceptance/test_bella_lui_rocket.py | Updates acceptance test parachute creation + attachment to new API. |
| rocketpy/stochastic/stochastic_rocket.py | Adapts stochastic rocket parachute handling to the new parachute class/module. |
| rocketpy/stochastic/stochastic_parachute.py | Updates stochastic parachute object creation to instantiate HemisphericalParachute. |
| rocketpy/simulation/flight.py | Replaces Flight.u_dot_parachute with a per-parachute wrapper calling parachute.u_dot. |
| rocketpy/rocket/rocket.py | Changes Rocket.add_parachute API to accept a parachute instance and append it. |
| rocketpy/rocket/parachutes/parachute.py | Adds new abstract base Parachute with trigger/noise utilities + (current) base serialization helpers. |
| rocketpy/rocket/parachutes/hemispherical_parachute.py | Turns old parachute implementation into HemisphericalParachute(Parachute) and implements u_dot + serialization. |
| rocketpy/rocket/parachutes/init.py | Adds parachutes package export(s). |
| rocketpy/rocket/init.py | Updates rocket package exports to include HemisphericalParachute. |
| rocketpy/init.py | Updates top-level exports to include HemisphericalParachute (and remove old Parachute). |
| docs/user/rocket/rocket_usage.rst | Updates user docs to the new parachute construction + add_parachute flow. |
| docs/user/first_simulation.rst | Updates “first simulation” docs to import/use HemisphericalParachute and then add to rocket. |
| docs/user/deployable.rst | Updates deployable example to use HemisphericalParachute and then add to rocket. |
| docs/user/compare_flights.rst | Updates compare-flights docs to the new parachute API. |
| docs/notebooks/trajectory.kml | Adds a KML output artifact used by notebook workflows/examples. |
| docs/notebooks/monte_carlo_analysis/monte_carlo_sensitivity_simulation.py | Updates Monte Carlo sensitivity notebook script to the new parachute API. |
| docs/notebooks/monte_carlo_analysis/monte_carlo_class_usage.ipynb | Updates Monte Carlo usage notebook imports + parachute creation/attachment. |
| docs/notebooks/monte_carlo_analysis/monte_carlo_analysis.ipynb | Updates Monte Carlo analysis notebook imports + parachute creation/attachment. |
| docs/notebooks/getting_started_colab.ipynb | Updates getting started (Colab) notebook imports + parachute creation/attachment. |
| docs/notebooks/getting_started.ipynb | Updates getting started notebook imports + parachute creation/attachment. |
| docs/examples/valetudo_flight_sim.ipynb | Updates example notebook to new parachute creation/attachment. |
| docs/examples/prometheus_2022_flight_sim.ipynb | Updates example notebook to new parachute creation/attachment. |
| docs/examples/ndrt_2020_flight_sim.ipynb | Updates example notebook to new parachute creation/attachment. |
| docs/examples/lince_flight_sim.ipynb | Updates example notebook to new parachute creation/attachment. |
| docs/examples/juno3_flight_sim.ipynb | Updates example notebook to new parachute creation/attachment. |
| docs/examples/halcyon_flight_sim.ipynb | Updates example notebook to new parachute creation/attachment. |
| docs/examples/genesis_flight_sim.ipynb | Updates example notebook to new parachute creation/attachment. |
| docs/examples/defiance_flight_sim.ipynb | Updates example notebook to new parachute creation/attachment. |
| docs/examples/camoes_flight_sim.ipynb | Updates example notebook to new parachute creation/attachment. |
| docs/examples/bella_lui_flight_sim.ipynb | Updates example notebook to new parachute creation/attachment. |
| docs/examples/andromeda_flight_sim.ipynb | Updates example notebook to new parachute creation/attachment. |
| README.md | Updates README snippets to the new parachute creation + add_parachute flow. |
Add changelog entry for PR #958, align Parachute trigger docstrings with the 4-argument runtime signature, and set a concrete deprecation removal target (1.14.0) for legacy add_parachute usage. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add changelog entry for PR #958, align Parachute trigger docstrings with the 4-argument runtime signature, and set a concrete deprecation removal target (1.14.0) for legacy add_parachute usage. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
0d92cd3 to
ad0777b
Compare
|
Quick update on #958 (latest head:
Local checks run for this update:
|
…t parachute class to BaseParachute
Add changelog entry for PR #958, align Parachute trigger docstrings with the 4-argument runtime signature, and set a concrete deprecation removal target (1.14.0) for legacy add_parachute usage. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Address review findings on PR #958 (parachute abstract base + hemispherical model): - rocket.py: validate that the new `add_parachute(parachute=...)` object path receives a Parachute subclass instance, raising a clear TypeError instead of letting a wrong object fail opaquely deep inside the ODE solver. - parachute.py: fix `__repr__` unit label — `lag` is seconds, was mislabeled `m2` (copy-paste from the old `cd_s` repr). - docs/reference: add a `HemisphericalParachute` autodoc page (the concrete class users instantiate; previously only the abstract base was documented). - docs notebooks/scripts: fix Monte Carlo examples that called `Rocket.add_parachute(obj)` positionally — `parachute` is the last kwarg, so the object bound to `name` and the example crashed. Use `add_parachute(parachute=obj)`. (StochasticRocket.add_parachute(obj) is positional and was already correct.) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ad0777b to
e83ded6
Compare
Resolve conflicts in the refactored parachute module (rocketpy/rocket/parachutes/parachute.py) by adopting develop's 3/4/5-argument trigger function API (sensors + u_dot aware), which the rest of the refactored file already relies on.
The refactored parachute implementation saved each parachute's dynamic information (``parachutes_info``, e.g. drag vs. time) from inside ``u_dot_parachute`` during the raw integration. Because the ODE solver evaluates the derivative at internal RK stages and rejected steps, the stored time series was non-monotonic, contained points that did not match the accepted trajectory and even extended past ``t_final``. Move the ``add_information_to_flight`` call to the post-processing branch, which replays the derivatives over the accepted ``solution`` steps only, so the recorded drag now matches the flight trajectory one-to-one. Since post-processing is evaluated lazily, ``FlightPlots.parachutes_info`` now forces it before reading the data so the plot still works when called standalone.
- tests/unit/test_parachute_triggers.py (added on develop) still imported the pre-refactor module `rocketpy.rocket.parachute` and instantiated the now-abstract `Parachute` directly, which errored at collection time after the merge. Point it at `HemisphericalParachute` / the new package path. - FlightPlots.parachutes_info(name): the single-parachute branch indexed `parachutes_info[name]` directly and raised KeyError when the named parachute never deployed (or the name was misspelled). Unify the two duplicated branches and print a helpful message listing the available parachutes instead. - Flight.to_dict serializes `parachutes_info`, but the decoder's set_minimal_flight_attributes never restored it, so parachute drag data was silently dropped on every .rpy/JSON round-trip. Add it to the restored attribute list (guarded by the existing KeyError handling for older files).
The abstract Parachute.to_dict/from_dict referenced model-specific attributes (cd_s, radius, drag_coefficient, height, porosity) that the base no longer owns, and from_dict forwarded a `parachute_type` kwarg that concrete constructors do not accept. They worked only because HemisphericalParachute fully overrode them; any future parachute model inheriting them would hit AttributeError/TypeError. Reduce the base methods to the fields the base actually owns and expose a shared `_decode_trigger` helper. HemisphericalParachute now builds on `super().to_dict()` (matching the repo-wide serialization convention) and adds only its own fields, removing the verbatim duplication. Serialized output and round-trip behavior are unchanged. Drop the now-unused imports.
- ruff format: collapse the parachutes_info source comprehension to one line. - pylint (no-value-for-parameter, fail-under=10): the base Parachute could not offer a generic from_dict because each concrete model has different required constructor arguments. Make from_dict abstract (mirroring u_dot and add_information_to_flight); the shared _decode_trigger helper stays on the base and HemisphericalParachute keeps its concrete implementation.
* MNT: final fixes before next release Documentation, CHANGELOG and test polish ahead of the next release (v1.13.0), covering PRs merged since v1.12.0. CHANGELOG - Add missing entries: Individual Fins (#818), AIGFS/HRRR forecast models (#951), duplicate-controller fix (#949), Valkyrie flight example (#967). - Hygiene: de-duplicate #958/#966, move #974 to Fixed and #1041 to Removed only, drop the already-released #914 duplicate, and point the logging (#973) and ND-interp (#969) entries at their PRs. - Backfill #940/#941/#944 (shipped in v1.12.0 code but never logged) under the [v1.12.0] section. Docs - New exceptions reference page (rocketpy.exceptions) wired into the reference index; note UnstableRocketWarning in the rocket stability docs (#970). - tanks.rst: switch examples to radius_function= and add a deprecation note (#957). - forecast.rst: fix the HRRR example (missing code directive + stray sentence) (#951). - airbrakes.rst: document discrete vs continuous controllers (sampling_rate=None) (#946). - rocket_usage.rst: note that Parachute is now an abstract base; instantiate HemisphericalParachute (#958). Tests - New regression tests: 3D ND-interp NaN outside convex hull (#969), abstract Parachute cannot be instantiated (#958), Monte Carlo convergence stopping (#922), EnvironmentAnalysis surviving wind API (#1041), radial-burn grain geometry over time (#944), RingClusterMotor full flight (#924), discrete controller invoked once per node (#949), acceleration-based parachute trigger deploys (#911). - Backfill regression tests for ThrustCurve API timeouts (#940) and power_off/on_drag Function objects + _input attributes (#941). * make format
Pull request type
Checklist
black rocketpy/ tests/) has passed locallypytest tests -m slow --runslow) have passed locallyCHANGELOG.mdhas been updated (if relevant)Current behavior
The current implementation of parachutes in RocketPy considers a simplified drag based round parachute, henceforth called Hemispherical Parachute. The
Parachuteclass stores basic parachute information (drag, triggers, etc...) and all the physics (equations of motion) is handled by theFlightclass inside theu_dot_parachutefunction. The problem is: different parachute types and models require different physics, which would create several "u_dot_parachute"s functions (or a really big messy one). Moreover, different parachutes require different information (some have lift coefficient, others have yaw coefficient, others have bank angle, angle of attack, and so on), so perhaps we should have different parachute classes, not just a big one.New behavior
The
Parachuteclass is now an abstract class which cannot be instantiated. All parachute models which will be created from now on should have it as a parent class. Our current implementation of the parachute is in theHemisphericalParachuteclass.Each parachute class will have only the attributes it uses for its modelling and it contains the u_dot function! The idea is that the
Parachuteclass has an abstract method calledu_dot_parachutewhich each child must implement! Hence, the design proposal is to allow each parachute model to handle the physics! The SciPy solver is still called in theFlightclass: anu_dot_parachute_wrapperis constructed in theFlightclass to allow the parachute to use flight information and still maintain its arguments conforming to the SciPy solver requirement.The
Rocketmethodadd_parachutewas also changed. Since the idea is to have several parachute models, we now construct the parachute and then pass it as and object to that method. Hence, we now have a syntax such asHence,
add_parachutealso changed (compare to how it was done before).Breaking change
Additional information
I look forward for your opinions on design choice and modifications/improvements!
I modified the tests and notebooks to work properly. However, I did not change much of the documentation, so it probably teaches how to add a Parachute incorrectly. Moreover, I did a quick dirty job on the Monte Carlo modification, it needs to be rewritten.
TODO LIST:
[ ] Verify and modify the documentation
[ ] Modify the Stochastic version of parachute
🪂 I'm baking up a low-fidelity 3DoF Parafoil model which motivated this hard refactor :) 🪂