Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 17 additions & 15 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,27 +32,25 @@ Attention: The newest changes should be on top -->

### Added

- ENH: MNT: refactor parachute implementation [#958](https://github.com/RocketPy-Team/RocketPy/pull/958)
- ENH: Individual Fins: add `Fin`, `TrapezoidalFin`, `EllipticalFin`, and `FreeFormFin` classes for modeling asymmetric or individually-positioned fins [#818](https://github.com/RocketPy-Team/RocketPy/pull/818)
- ENH: Add AIGFS and HRRR forecast models to the Environment class [#951](https://github.com/RocketPy-Team/RocketPy/pull/951)
- ENH: Add RingClusterMotor for annular clustered motor modeling [#924](https://github.com/RocketPy-Team/RocketPy/pull/924)
- ENH: Discrete and Continuous Controllers [#946](https://github.com/RocketPy-Team/RocketPy/pull/946)
- ENH: Add custom exceptions and unstable rocket warning [#970](https://github.com/RocketPy-Team/RocketPy/pull/970)
- ENH: MNT: Remove redundant wind_heading/wind_direction functions from EnvironmentAnalysis [#1041](https://github.com/RocketPy-Team/RocketPy/pull/1041)
- ENH: Adopt built-in Python logging instead of print() calls (closes [#450](https://github.com/RocketPy-Team/RocketPy/issues/450)) [#973](https://github.com/RocketPy-Team/RocketPy/pull/973)
- ENH: Pass acceleration (`u_dot`) data to parachute trigger functions [#911](https://github.com/RocketPy-Team/RocketPy/pull/911)
- ENH: Add 3D flight trajectory and attitude animations in Flight plots layer [#909](https://github.com/RocketPy-Team/RocketPy/pull/909)
- ENH: DOC: Configure AI instructions and update developer docs [#975](https://github.com/RocketPy-Team/RocketPy/pull/975)
- ENH: BUG: fix wind heading and direction wraparound interpolation [#974](https://github.com/RocketPy-Team/RocketPy/pull/974)
- ENH: MNT: Sync develop's Renovate config with master (#1039) [#1040](https://github.com/RocketPy-Team/RocketPy/pull/1040)
- ENH: MNT: Configure Renovate Bot targeting develop branch [#972](https://github.com/RocketPy-Team/RocketPy/pull/972)
- MNT: Refactor parachute implementation with abstract base and hemispherical model split [#958](https://github.com/RocketPy-Team/RocketPy/pull/958)
- ENH: Monte Carlo Formatting Options [#947](https://github.com/RocketPy-Team/RocketPy/pull/947)
- ENH: ENH: Auto-Detection of Pressure Conversion Factor [#966](https://github.com/RocketPy-Team/RocketPy/pull/966)
- ENH: Adaptive Monte Carlo via Convergence Criteria [#922](https://github.com/RocketPy-Team/RocketPy/pull/922)
- ENH: Auto-Detection of Pressure Conversion Factor [#966](https://github.com/RocketPy-Team/RocketPy/pull/966)
- ENH: Discrete and Continuous Controllers [#946](https://github.com/RocketPy-Team/RocketPy/pull/946)
- ENH: Introduce pressure unit conversion when using forecast/reanalysis/ensemble data [#955](https://github.com/RocketPy-Team/RocketPy/pull/955)
- MNT: Refactor parachute implementation with abstract base and hemispherical model split [#958](https://github.com/RocketPy-Team/RocketPy/pull/958)
- DOC: Add aerodynamic surfaces user guide [#1043](https://github.com/RocketPy-Team/RocketPy/pull/1043)
- ENH: Add RingClusterMotor for annular clustered motor modeling [#924](https://github.com/RocketPy-Team/RocketPy/pull/924)
- ENH: MNT: introduce pressure unit conversion when using forecast/reanalysis/ensemble data [#955](https://github.com/RocketPy-Team/RocketPy/pull/955)
- DOC: Add Valkyrie flight example (Bisky Team) [#967](https://github.com/RocketPy-Team/RocketPy/pull/967)
- DOC: Configure AI instructions and update developer docs [#975](https://github.com/RocketPy-Team/RocketPy/pull/975)
- ENH: Auto Populate Changelog [#919](https://github.com/RocketPy-Team/RocketPy/pull/919)
- ENH: Adaptive Monte Carlo via Convergence Criteria [#922](https://github.com/RocketPy-Team/RocketPy/pull/922)
- TST: Add acceptance tests for 3DOF flight simulation based on Bella Lui rocket [#914](https://github.com/RocketPy-Team/RocketPy/pull/914)
- ENH: adopt built-in Python logging instead of print() calls [#450](https://github.com/RocketPy-Team/RocketPy/issues/450)
- MNT: Sync develop's Renovate config with master (#1039) [#1040](https://github.com/RocketPy-Team/RocketPy/pull/1040)
- MNT: Configure Renovate Bot targeting develop branch [#972](https://github.com/RocketPy-Team/RocketPy/pull/972)

### Changed

Expand All @@ -68,8 +66,9 @@ Attention: The newest changes should be on top -->

### Fixed

- BUG: Remove duplicate controller process; controllers were being invoked twice per time node [#949](https://github.com/RocketPy-Team/RocketPy/pull/949)
- BUG: fix wind heading and direction wraparound interpolation [#974](https://github.com/RocketPy-Team/RocketPy/pull/974)
- BUG: fix NaN in ND linear interpolation outside convex hull [#926](https://github.com/RocketPy-Team/RocketPy/issues/926)
- BUG: fix NaN in ND linear interpolation outside convex hull (closes [#926](https://github.com/RocketPy-Team/RocketPy/issues/926)) [#969](https://github.com/RocketPy-Team/RocketPy/pull/969)
- BUG: Add wraparound logic for wind direction in environment plots [#939](https://github.com/RocketPy-Team/RocketPy/pull/939)

## [v1.12.1] - 2026-04-03
Expand Down Expand Up @@ -109,6 +108,9 @@ Attention: The newest changes should be on top -->
### Fixed

- BUG: Fix hard-coded radius value for parachute added mass calculation [#889](https://github.com/RocketPy-Team/RocketPy/pull/889)
- BUG: Fix incorrect Jacobian in `only_radial_burn` branch of `SolidMotor.evaluate_geometry` [#944](https://github.com/RocketPy-Team/RocketPy/pull/944)
- ENH: Restore `power_off_drag`/`power_on_drag` as `Function` objects and preserve raw user input in `_input` attributes [#941](https://github.com/RocketPy-Team/RocketPy/pull/941)
- ENH: Add explicit timeouts to ThrustCurve API requests [#940](https://github.com/RocketPy-Team/RocketPy/pull/940)
- DOC: Fix documentation build [#908](https://github.com/RocketPy-Team/RocketPy/pull/908)
- BUG: energy_data plot not working for 3 dof sims [[#906](https://github.com/RocketPy-Team/RocketPy/issues/906)]
- BUG: Fix CSV column header spacing in FlightDataExporter [#864](https://github.com/RocketPy-Team/RocketPy/issues/864)
Expand Down
3 changes: 2 additions & 1 deletion docs/examples/valkyrie_flight_sim.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,10 @@
"outputs": [],
"source": [
"import json\n",
"\n",
"import matplotlib.pyplot as plt\n",
"\n",
"from rocketpy import Environment, Flight, Rocket, SolidMotor, Function\n",
"from rocketpy import Environment, Flight, Function, Rocket, SolidMotor\n",
"from rocketpy.simulation.flight_data_importer import FlightDataImporter\n",
"\n",
"plt.style.use(\"seaborn-v0_8-colorblind\")\n",
Expand Down
14 changes: 14 additions & 0 deletions docs/reference/classes/exceptions.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
Exceptions and Warnings
=======================

RocketPy raises a small hierarchy of custom exceptions and warnings so that
error handling can be more specific than catching plain ``ValueError`` or
``UserWarning``. All exceptions inherit from :class:`~rocketpy.exceptions.RocketPyError`,
which makes it possible to catch any RocketPy-specific error with a single
``except`` clause while still deriving from the relevant built-in type
(e.g. :class:`~rocketpy.exceptions.InvalidParameterError` is also a
``ValueError``).

.. automodule:: rocketpy.exceptions
:members:
:show-inheritance:
3 changes: 2 additions & 1 deletion docs/reference/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@ This reference manual details functions, modules, methods and attributes include
classes/Flight
Utilities <classes/utils/index>
classes/EnvironmentAnalysis
Monte Carlo Analysis <classes/monte_carlo/index>
Monte Carlo Analysis <classes/monte_carlo/index>
Sensitivity Analysis <classes/Sensitivity>
Multivariate Rejection Sampler <classes/MultivariateRejectionSampler>
Exceptions and Warnings <classes/exceptions>

.. toctree::
:maxdepth: 2
Expand Down
48 changes: 43 additions & 5 deletions docs/user/airbrakes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,11 @@ To create an air brakes model, we essentially need to define the following:
``deployment_level`` attribute. Inside this function, any controller logic,
filters, and apogee prediction can be implemented.

- The **sampling rate** of the controller function, in seconds. This is the time
between each call of the controller function, in simulation time. Must be
given in Hertz.
- The **sampling rate** of the controller function, in Hertz. This is how often
the controller function is called in simulation time (a **discrete**
controller). It can also be set to ``None`` to create a **continuous**
controller that is called at every solver step (see
:ref:`discrete-vs-continuous-controllers` below).

Defining the Controller Function
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Expand All @@ -108,8 +110,9 @@ The ``controller_function`` must take in the following arguments, in this
order:

1. ``time`` (float): The current simulation time in seconds.
2. ``sampling_rate`` (float): The rate at which the controller
function is called, measured in Hertz (Hz).
2. ``sampling_rate`` (float or ``None``): The rate at which the controller
function is called, measured in Hertz (Hz). It is ``None`` for continuous
controllers, so guard any ``1 / sampling_rate`` computation against ``None``.
3. ``state`` (list): The state vector of the simulation. The state
is a list containing the following values, in this order:

Expand Down Expand Up @@ -372,6 +375,41 @@ controller function. If you want to disable this feature, set ``clamp`` to
For more information on the :class:`rocketpy.AirBrakes` class
initialization, see :class:`rocketpy.AirBrakes.__init__` section.

.. _discrete-vs-continuous-controllers:

Discrete vs. Continuous Controllers
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

The ``sampling_rate`` argument determines *when* the controller function is
called during the flight simulation:

- **Discrete controller** (``sampling_rate`` set to a number, e.g. ``10``):
the controller function is called at fixed intervals of ``1 / sampling_rate``
seconds. This mirrors a real flight computer that reads its sensors and
updates its actuators at a fixed frequency, and is the recommended choice
when you want the simulation to reflect the actual control-loop rate of your
hardware.
- **Continuous controller** (``sampling_rate=None``): the controller function
is called at *every* solver step of the numerical integrator. Use this when
you want the control law to act as a continuous function of the state rather
than a sampled one (for example, when validating a control model
analytically).

.. warning::

For continuous controllers, ``sampling_rate`` is passed to your controller
function as ``None``. Any computation such as ``1 / sampling_rate`` (a
common pattern for rate-limiting deployment, as shown above) must guard
against ``None`` to avoid a ``TypeError``.

.. note::

Discrete controllers add their sampling instants as time nodes to the
simulation, so remember to set ``time_overshoot=False`` in the ``Flight``
(see below) to make the integrator stop exactly at those instants.
Continuous controllers do not add time nodes; they are evaluated on the
integrator's own steps.

Simulating a Flight
-------------------

Expand Down
10 changes: 5 additions & 5 deletions docs/user/environment/1-atm-models/forecast.rst
Original file line number Diff line number Diff line change
Expand Up @@ -173,14 +173,14 @@ hourly updates. It is generally best for day-of-launch weather assessment and
rapidly changing local conditions.

RocketPy supports HRRR through a dedicated THREDDS shortcut.
Like NAM and RAP, HRRR is a regional model over North America.
Like NAM and RAP, HRRR is a regional model over North America, so you need to
specify latitude and longitude points within its coverage area. The same
SpacePort America coordinates used above will be reused here.

If you have a HIRESW-compatible dataset from another provider (or a local copy),
you can still load it explicitly by passing the path/URL in ``file`` and an
appropriate mapping in ``dictionary``.
.. code-block:: python

env_hrrr = Environment(
date=now_plus_twelve,
date=tomorrow,
latitude=32.988528,
longitude=-106.975056,
)
Expand Down
10 changes: 8 additions & 2 deletions docs/user/motors/tanks.rst
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,13 @@ The predefined ``CylindricalTank`` class is easy to use and is defined as such:

.. jupyter-execute::

cylindrical_geometry = CylindricalTank(radius=0.1, height=2.0, spherical_caps=False)
cylindrical_geometry = CylindricalTank(radius_function=0.1, height=2.0, spherical_caps=False)

.. deprecated:: 1.13.0
The ``radius`` keyword argument of ``CylindricalTank`` and ``SphericalTank``
has been renamed to ``radius_function``. Passing ``radius=`` still works but
now raises a ``DeprecationWarning`` and will be removed in v2.0.0. Use
``radius_function=`` instead (it accepts the same constant radius value).

.. note::
The ``spherical_caps`` parameter is optional and defaults to ``False``. If set
Expand All @@ -116,7 +122,7 @@ The predefined ``SphericalTank`` is defined with:

.. jupyter-execute::

spherical_geometry = SphericalTank(radius=0.1)
spherical_geometry = SphericalTank(radius_function=0.1)

.. seealso::
:class:`rocketpy.CylindricalTank` and :class:`rocketpy.SphericalTank`
Expand Down
23 changes: 23 additions & 0 deletions docs/user/rocket/rocket_usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,17 @@ Optionally, we can also define:
- The parachute trigger system lag ``lag``.
- The parachute trigger system noise ``noise``.

.. note::

Since v1.13.0, :class:`~rocketpy.Parachute` is an **abstract base class**
and can no longer be instantiated directly. Instead, instantiate a concrete
parachute model such as :class:`~rocketpy.HemisphericalParachute` (used
below), which derives its geometry-dependent quantities (e.g. the added
mass during descent) from the parachute ``radius`` and ``height``. As a
convenience shortcut, ``Rocket.add_parachute(...)`` can still be called with
keyword arguments (``name``, ``cd_s``, ``trigger``, ...) and will build a
hemispherical parachute for you.

Lets add two parachutes to the rocket, one that will be deployed at
apogee and another that will be deployed at 800 meters above ground level:

Expand Down Expand Up @@ -435,6 +446,18 @@ First, lets guarantee that the rocket is stable, by plotting the static margin:
If it is unreasonably **high**, your rocket is **super stable** and the
simulation will most likely **fail**.

.. note::

RocketPy helps you catch this automatically: if the static margin is
**negative at motor ignition**, an
:class:`~rocketpy.exceptions.UnstableRocketWarning` is issued when the
rocket is used in a simulation. The check is skipped when the rocket has
any ``GenericSurface`` aerodynamic surfaces, since their lift coefficient
derivative is not accounted for in the center of pressure calculation,
which makes the static margin unreliable in that case. See
:doc:`/reference/classes/exceptions` for the full list of RocketPy
exceptions and warnings.

The lets check all the information available about the rocket:

.. jupyter-execute::
Expand Down
2 changes: 1 addition & 1 deletion rocketpy/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import logging

from . import utils
from .control import _Controller
from .environment import Environment, EnvironmentAnalysis
from .exceptions import (
Expand Down Expand Up @@ -56,7 +57,6 @@
TrapezoidalFins,
)
from .sensitivity import SensitivityModel
from . import utils
from .sensors import Accelerometer, Barometer, GnssReceiver, Gyroscope
from .simulation import Flight, MonteCarlo, MultivariateRejectionSampler
from .stochastic import (
Expand Down
1 change: 0 additions & 1 deletion rocketpy/environment/environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import logging
import re
import warnings

from collections import namedtuple
from datetime import datetime

Expand Down
1 change: 0 additions & 1 deletion rocketpy/environment/environment_analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import json
import logging
import warnings

from collections import defaultdict
from functools import cached_property

Expand Down
1 change: 0 additions & 1 deletion rocketpy/environment/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

import logging
import math

import warnings

import netCDF4
Expand Down
1 change: 0 additions & 1 deletion rocketpy/mathutils/function.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import logging
import operator
import warnings

from bisect import bisect_left
from collections.abc import Iterable
from copy import deepcopy
Expand Down
1 change: 0 additions & 1 deletion rocketpy/plots/aero_surface_plots.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
from abc import ABC, abstractmethod

import matplotlib.pyplot as plt

import numpy as np
from matplotlib.patches import Ellipse

Expand Down
1 change: 0 additions & 1 deletion rocketpy/plots/compare/compare_flights.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import numpy as np

from ..plot_helpers import show_or_save_fig, show_or_save_plot

from .compare import Compare

logger = logging.getLogger(__name__)
Expand Down
1 change: 0 additions & 1 deletion rocketpy/plots/flight_plots.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
from importlib import resources

import matplotlib.pyplot as plt

import numpy as np

from ..mathutils import Function
Expand Down
1 change: 0 additions & 1 deletion rocketpy/plots/monte_carlo_plots.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
from pathlib import Path

import matplotlib.pyplot as plt

import numpy as np
from matplotlib.transforms import offset_copy
from PIL import UnidentifiedImageError
Expand Down
1 change: 0 additions & 1 deletion rocketpy/plots/rocket_plots.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
import numpy as np

from rocketpy.mathutils.vector_matrix import Vector

from rocketpy.motors import HybridMotor, LiquidMotor, SolidMotor
from rocketpy.rocket.aero_surface import Fin, Fins, NoseCone, Tail
from rocketpy.rocket.aero_surface.generic_surface import GenericSurface
Expand Down
1 change: 0 additions & 1 deletion rocketpy/rocket/aero_surface/nose_cone.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
import warnings

import numpy as np

from scipy.optimize import fsolve

from rocketpy.mathutils.function import Function
Expand Down
11 changes: 5 additions & 6 deletions rocketpy/rocket/rocket.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,17 @@
import logging
import math
import warnings

from typing import Iterable
from warnings import warn

import numpy as np

from rocketpy.control.controller import _Controller
from rocketpy.exceptions import (
InvalidInertiaError,
InvalidParameterError,
UnstableRocketWarning,
)
from rocketpy.mathutils.function import Function
from rocketpy.mathutils.vector_matrix import Matrix, Vector
from rocketpy.motors.empty_motor import EmptyMotor
Expand All @@ -29,11 +33,6 @@
from rocketpy.rocket.aero_surface.fins.free_form_fins import FreeFormFins
from rocketpy.rocket.aero_surface.fins.trapezoidal_fin import TrapezoidalFin
from rocketpy.rocket.aero_surface.generic_surface import GenericSurface
from rocketpy.exceptions import (
InvalidInertiaError,
InvalidParameterError,
UnstableRocketWarning,
)
from rocketpy.rocket.components import Components
from rocketpy.rocket.parachutes.hemispherical_parachute import HemisphericalParachute
from rocketpy.rocket.parachutes.parachute import Parachute
Expand Down
1 change: 0 additions & 1 deletion rocketpy/simulation/flight.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
import logging
import math
import warnings

from copy import deepcopy
from functools import cached_property

Expand Down
1 change: 0 additions & 1 deletion rocketpy/simulation/flight_data_exporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import logging

import numpy as np

import simplekml

logger = logging.getLogger(__name__)
Expand Down
1 change: 0 additions & 1 deletion rocketpy/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
import re
import time
import warnings

from bisect import bisect_left

import dill
Expand Down
Loading
Loading