Skip to content

ENH: Events Class and Flight Rework#968

Draft
MateusStano wants to merge 25 commits into
developfrom
enh/events
Draft

ENH: Events Class and Flight Rework#968
MateusStano wants to merge 25 commits into
developfrom
enh/events

Conversation

@MateusStano

@MateusStano MateusStano commented Jun 15, 2026

Copy link
Copy Markdown
Member

Summary

The addition of custom events was long overdue. In this PR the Event class was added. It was basically impossible to do this without changing a lot of the flight class, so I took the opportunity to rework it and solve all TODOs there.

Several upcoming features will build on top of this (parafoil, multistage, more controllers...), so I want this branch to act as a hub for a few related changes:

  • Make class Event serializable
    • Fix broken tests (broken due to fails on controller serialization)
  • Rework controllers (minor) so they are better designed for use with specific classes, like what is done in FEAT: new actuator class for roll, throttle, and thrust vector control #965.
  • Make flight.solution an instance of a new Solution class. It should behave like the current array, but also allow a variable state length. This is important for adding new parachute models such as parafoil, and for letting us define more complex derivative functions.
  • Aerodynamic classes refactor to make GenericSurface the mother class of all other aero surfaces.

Here is a summary of all the changes:

flight.py

Some flight class methods were reworked, some were moved:

  • The flight now moves forward using events, not a fixed list of phases.
  • flight_derivatives.py: the motion functions (u_dot, u_dot_generalized,
    u_dot_generalized_3dof, rail and parachute), moved out of flight.py.
  • flight_phase.py: now has the _FlightPhases and _TimeNodes classes.
  • event_calling.py: utilities for callling the events in the flight class correctly
  • event_commands.py: applies the commands an event returns back to the flight.
  • a propper logging feature was added

New event code (rocketpy/simulation/events/)

  • event.py (Event): An event has a trigger and a callback. The trigger is checked each step; when it is true, the callback runs. Callbacks can read the state, save data, log results, and send commands that change the simulation. It comes with ready-made presets like apogee and burnout. It also only computes expensive values when an event actually needs them.
  • commands.py (Commands): what a callback can ask for. An event can start a
    new phase, swap the motion function, turn other events on or off, add or
    remove controllers, undo a step (rollback), or stop the flight.
  • event_builders.py: ready-made events that recreate the normal flight steps
    (like leaving the rail).
  • exact_time_solvers.py: finds the exact time an event happens inside a step
    (using a few math methods).

Parachutes, controllers and sensors are no longer special cases in the flight loop. Each one now has a to_event() method that wraps it into an Event. The built-in flight milestones (apogee, out of rail, impact) are now events too.

Parachute triggers and Controllers controller_function now take **kwargs only and read what they need by name (pressure, height_agl, state, sensors, etc.); the old positional parachute and controller signatures still work but raise a DeprecationWarning.

Flight.__init_events() just collects all events (core events, then sensors, parachutes, controllers, and user custom events) and runs them uniformly, and commands an event returns (like swapping the derivative or starting a phase) are applied back to the flight by event_commands.py.

Parachute noise was also removed. The noise parameter on Parachute is now deprecated and has no effect (removal in v1.13). To model a noisy trigger, a Barometer should be used and accesses the noisy measurement in the trigger via kwargs['sensors_by_name']. This fits the new model, where sensors are events and the trigger reads their measurements.

Plots and prints

  • flight_plots.py and flight_prints.py now show sensor data too.
  • compare_flights.py updated.
  • New plots to all_info, and now they show case events:
image

Docs

  • New guides: docs/user/event_usage.rst and docs/user/sensors_usage.rst.
  • New technical page: docs/technical/simulation_loop.rst.
  • Update all places that used old definition of trigger functions (with positional arguments) to now be used with **kwargs

Notes for reviewers

This will be a difficult one to review, specially because the diff tracking in flight.py is not that helpful given I changed the order of a few methods. So I suggest that the best way to understand how events work is to read the user guide: docs/user/event_usage.rst. It walks through the API with runnable examples.

Breaking change

  • No (I hope)

…logging

Expose recorded sensor measurements as the canonical, per-flight record on
`Flight.sensor_data`, and route every flight-scoped consumer through it so
results stay correct when a rocket/sensor is reused across simulations.

- Sensors: add `flight.sensor_data` as the source of truth; `flight.prints.
  sensors()` and `flight.plots.sensor_data()` now read from it. Give the
  per-sensor plots/prints an optional `data=` argument so standalone use still
  works (`_SensorPlots`, `_SensorPrints`).
- Flight plots: improve event markers and the trajectory/ground-track plots
  (square ground track with equal axis ticks, trajectory omitted from the
  legend) and related layout cleanups in `flight_plots.py`.
- Logging: add a centralized `rocketpy._logging` module exposing `logger`,
  `set_log_level` and `enable_logging`, following the NullHandler library
  convention; wire it into the event/simulation code.
- Simulation: supporting changes in flight, flight derivatives, flight phases,
  events (commands, exact-time solvers) and the data exporter.
- Stochastic: rebuild the air-brakes `_Controller` with its current signature
  (controlled_objects / context), fixing a stale constructor call.
- Docs: update getting-started and sensors notebooks to the `flight.exports.*`
  API and document sensor usage.
@MateusStano
MateusStano requested a review from a team as a code owner June 15, 2026 17:39
@MateusStano MateusStano added Enhancement New feature or request, including adjustments in current codes Parachute Related to parachutes methods and usage Refactor Flight Flight Class related features Sensors Events labels Jun 15, 2026
@MateusStano
MateusStano marked this pull request as draft June 15, 2026 17:47

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a new Event abstraction and refactors the Flight simulation loop to be event-driven, unifying handling of core milestones (out-of-rail/apogee/impact), parachutes, controllers, sensors, and user-defined hooks. It also expands sensor support (plots/prints/export), updates documentation to the new **kwargs trigger/controller patterns, and adds logging configuration utilities.

Changes:

  • Added rocketpy.simulation.events (Event/Commands/exact-time solvers/builders) and new helper modules to evaluate/apply events uniformly during simulation.
  • Refactored parachute/controller/sensor integration to be event-based, added per-flight sensor data presentation, and updated exporters/docs accordingly.
  • Added centralized logging configuration and updated tests/docs to match renamed APIs and new behaviors.

Reviewed changes

Copilot reviewed 82 out of 87 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
tests/unit/test_plots.py Marks animation-based unit tests as slow.
tests/unit/test_logging.py Adds unit coverage for RocketPy logging setup and phase-collision warnings.
tests/unit/test_flight_data_exporter.py Updates tests to use new exporter method names (data, pressures, sensor_data, kml).
tests/unit/simulation/test_flight.py Adds regression test for monotonic solution time; updates controller observed-variable expectations.
tests/unit/simulation/test_flight_time_nodes.py Updates tests to target _TimeNodes/_TimeNode internal classes and new event storage shape.
tests/unit/simulation/test_event.py Adds comprehensive unit tests for Event construction, validation, calling, and exact-time behavior.
tests/unit/rocket/test_parachute.py Updates parachute serialization expectations after noise deprecation/removal from dict.
tests/unit/rocket/aero_surface/test_linear_generic_surfaces.py Adapts aero-surface tests to new reynolds computation inputs.
tests/unit/rocket/aero_surface/test_generic_surfaces.py Adapts generic surface tests to new reynolds computation inputs.
tests/integration/simulation/test_flight.py Updates controller logging expectations; adds tests for deprecated positional/kwargs controller APIs.
tests/integration/simulation/test_flight_3dof.py Updates expected simulation_mode string format.
tests/integration/simulation/test_event.py Adds integration tests for event loop behaviors, exact-time solvers, and Commands.
tests/fixtures/parachutes/parachute_fixtures.py Updates parachute trigger fixtures to **kwargs signatures and removes noise usage.
tests/acceptance/test_ndrt_2020_rocket.py Updates acceptance parachute triggers to **kwargs and removes noise usage.
tests/acceptance/test_bella_lui_rocket.py Updates acceptance parachute trigger to **kwargs and removes noise usage.
rocketpy/utilities.py Adds calculate_stall_wind_velocity helper and annotates Flight import cycle.
rocketpy/tools.py Adds inverted_haversine_array for vectorized inverse haversine.
rocketpy/stochastic/stochastic_rocket.py Updates stochastic rocket controller instantiation to new controller fields/shape.
rocketpy/simulation/helpers/event_commands.py Adds command application layer to mutate flight state from event Commands.
rocketpy/simulation/helpers/event_calling.py Adds event-kwargs construction and event-calling orchestration utilities.
rocketpy/simulation/helpers/init.py Exports derivative helpers from the simulation helpers package.
rocketpy/simulation/flight_data_exporter.py Renames exporter methods and provides deprecated export_* aliases.
rocketpy/simulation/events/exact_time_solvers.py Adds exact-time root solvers and root filtering policy helpers.
rocketpy/simulation/events/event_builders.py Adds core event builders (out-of-rail/apogee/impact) and exact-time helpers.
rocketpy/simulation/events/commands.py Adds Commands container used by event callbacks to request simulation actions.
rocketpy/simulation/events/init.py Exposes event module public API.
rocketpy/simulation/init.py Exports Event from simulation package.
rocketpy/sensors/sensor.py Makes sensors event-driven via to_event, adds info/all_info, validates sampling_rate.
rocketpy/sensors/gyroscope.py Adds channels metadata and sensor plots integration.
rocketpy/sensors/gnss_receiver.py Adds channels metadata and sensor plots integration.
rocketpy/sensors/barometer.py Adds channels metadata and sensor plots integration.
rocketpy/sensors/accelerometer.py Adds channels metadata and sensor plots integration.
rocketpy/rocket/rocket.py Adds sensors-by-name, sensor events, and updates airbrakes/controller API + deprecations.
rocketpy/rocket/point_mass_rocket.py Adds _is_point_mass flag for simulation branching/import avoidance.
rocketpy/rocket/parachute.py Wraps parachutes as Events, migrates triggers to **kwargs, deprecates noise, adds trigger_needs.
rocketpy/rocket/aero_surface/generic_surface.py Moves Reynolds computation inside generic surface using density/viscosity inputs.
rocketpy/rocket/aero_surface/air_brakes.py Tweaks airbrakes drag coefficient Function configuration.
rocketpy/prints/sensors_prints.py Adds per-channel measured data summaries.
rocketpy/prints/flight_prints.py Extends flight prints to include sensors, sensor/controller/custom event reporting, and extra maxima.
rocketpy/prints/controller_prints.py Fixes controller source display and handles continuous sampling-rate output.
rocketpy/plots/sensors_plots.py Adds generic sensor plotting utilities for time series and multi-instance sensors.
rocketpy/plots/compare/compare_flights.py Refactors to use show_or_save_plot and expands aerodynamic force comparisons.
rocketpy/plots/aero_surface_plots.py Minor cleanup of header formatting.
rocketpy/motors/point_mass_motor.py Adds _is_point_mass flag for simulation branching/import avoidance.
rocketpy/motors/motor.py Initializes _is_point_mass flag for motors.
rocketpy/environment/environment.py Adds height_above_ground_level Function and keeps it in sync with elevation/pressure profiles.
rocketpy/_logging.py Adds centralized logger, NullHandler default, and enable/level helpers.
rocketpy/_encoders.py Restores additional Flight integration settings and derived lists when decoding minimal flight objects.
rocketpy/init.py Exposes Event and logging helpers at top-level import.
docs/user/three_dof_simulation.rst Updates export examples to use flight.exports.data.
docs/user/sensors_usage.rst Adds new sensors user guide with examples, plots, and export usage.
docs/user/rocket/rocket_usage.rst Updates parachute trigger docs to **kwargs model and deprecations.
docs/user/index.rst Adds Event Usage and Sensors pages to user guide navigation.
docs/user/first_simulation.rst Updates parachute noise usage, adds logging/verbose guidance, updates exporter usage.
docs/user/deployable.rst Removes parachute noise usage from examples.
docs/user/compare_flights.rst Removes parachute noise usage from examples.
docs/user/airbrakes.rst Updates controller examples to **kwargs, removes time_overshoot requirement note, updates logging access.
docs/technical/simulation_loop.rst Adds new technical overview of phase/time-node/event-driven simulation structure.
docs/technical/index.rst Adds Simulation Loop page to technical docs toctree.
docs/reference/classes/sensors/gyroscope.rst Fixes autoclass target to top-level export.
docs/reference/classes/sensors/gnss_receiver.rst Fixes autoclass target to top-level export.
docs/reference/classes/sensors/barometer.rst Fixes autoclass target to top-level export.
docs/reference/classes/sensors/accelerometer.rst Fixes autoclass target to top-level export.
docs/notebooks/monte_carlo_analysis/parachute_drop_from_helicopter.ipynb Updates parachute setup example to new trigger style/noise removal.
docs/notebooks/monte_carlo_analysis/monte_carlo_sensitivity_simulation.py Removes parachute noise usage.
docs/notebooks/monte_carlo_analysis/monte_carlo_class_usage.ipynb Removes parachute noise usage.
docs/notebooks/monte_carlo_analysis/monte_carlo_analysis.ipynb Removes legacy drogue trigger cell and noise usage; clears execution counts.
docs/notebooks/getting_started_colab.ipynb Updates to new exporter usage and removes parachute noise usage.
docs/examples/valetudo_flight_sim.ipynb Removes parachute noise usage.
docs/examples/ndrt_2020_flight_sim.ipynb Removes parachute noise usage.
docs/examples/lince_flight_sim.ipynb Removes parachute noise usage.
docs/examples/juno3_flight_sim.ipynb Removes parachute noise usage.
docs/examples/hedy_flight_sim.ipynb Removes parachute noise usage and related config keys.
docs/examples/genesis_flight_sim.ipynb Removes parachute noise usage.
docs/examples/bella_lui_flight_sim.ipynb Removes parachute noise usage.
docs/development/testing.rst Removes an obsolete TODO comment from testing docs.
.pylintrc Increases max-statements threshold and adjusts a lint rule configuration.

Comment thread rocketpy/utilities.py
Comment on lines +612 to +630
theta = np.radians(flight.inclination)
stall_angle = np.radians(stall_angle)

c = (np.cos(stall_angle) ** 2 - np.cos(theta) ** 2) / np.sin(stall_angle) ** 2
w_v = (
2 * v_f * np.cos(theta) / c
+ (
4 * v_f * v_f * np.cos(theta) * np.cos(theta) / (c**2)
+ 4 * 1 * v_f * v_f / c
)
** 0.5
) / 2

stall_angle = np.degrees(stall_angle)
print(
"Maximum wind velocity at Rail Departure time before angle"
+ f" of attack exceeds {stall_angle:.3f}°: {w_v:.3f} m/s"
)
return w_v
Comment on lines +356 to +361
- ``state_dot`` (list of float): time derivative of the state vector,
``[vx, vy, vz, ax, ay, az, e0_dot, e1_dot, e2_dot, e3_dot, w1_dot, w2_dot, w3_dot]``.
In particular, ``(ax, ay, az)`` at indices 3, 4 and 5 are the acceleration
components, so ``state_dot[5]`` is the vertical acceleration.
- ``pressure`` (float): current atmospheric pressure in Pa at the rocket's
altitude.
Comment on lines +375 to +377
- ``sensors`` (dict): dictionary mapping sensor names (or class names) to sensor
instances, each exposing its most recent ``measurement``. If several sensors
share a name, the value is a list.
Comment thread rocketpy/rocket/rocket.py
Comment on lines +1826 to +1831
.. deprecated:: 1.13
Passing `initial_observed_variables` directly to
``add_air_brakes`` is deprecated. Provide initial observed
variables via the ``context`` parameter as
``context={'observed_variables': [...]}`` instead. Support
for the positional argument will be removed in v1.13.
Comment on lines +263 to +272
if noise[0] != 0 or noise[1] != 0 or noise[2] != 0:
warnings.warn(
"The `noise` parameter on Parachute is deprecated and has no "
"effect; it will be removed in v1.13. Use a Sensor (e.g. a "
"Barometer) with built-in noise instead, and read the noisy "
"measurement via `kwargs['sensors_by_name']` in your trigger "
"function.",
DeprecationWarning,
stacklevel=3,
)
Comment on lines +271 to +273
z : float
Altitude of the surface, used to evaluate ``density`` and
``dynamic_viscosity``.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let it clear if its above sea level ou ground level please

        z : float
            Altitude of the surface, used to evaluate ``density`` and
            ``dynamic_viscosity``.

@phmbressan phmbressan Jul 23, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A diagram like below would probably help this page a lot.
flowchart TD
    A["Initialize Flight"] --> B["Build event set<br/>and flight phases"]
    B --> C["Start phase<br/>create solver and time-node schedule"]
    C --> D["Advance solver<br/>and store state"]
    D --> E["Evaluate events<br/>scheduled, continuous, or interpolated"]
    E --> F["Run triggered callbacks"]
    F --> G["Apply Commands<br/>update events, dynamics, phases, or termination"]

    G --> H{"Next action"}
    H -- Continue phase --> D
    H -- Roll back --> I["Restore event state<br/>and restart solver"]
    I --> D
    H -- Start new phase --> C
    H -- Flight complete --> J["Finalize and cache results"]

    classDef main fill:#17324d,color:#fff,stroke:#17324d;
    classDef process fill:#e8f1f8,color:#17324d,stroke:#5b84a6;
    classDef event fill:#fff3d6,color:#5b4100,stroke:#d39b24;
    classDef decision fill:#fff,color:#333,stroke:#666;

    class A,B,J main;
    class C,D,I process;
    class E,F,G event;
    class H decision;
Loading
Or a more complex/comprehensive:
flowchart TD
    A["Flight.__init__"] --> B["Initialize events, equations of motion,<br/>solution history, and solver settings"]

    B --> C["Classify events by priority"]
    C --> C1["Non-overshootable<br/>scheduled nodes or continuous checks"]
    C --> C2["Sampled + overshootable<br/>checked through interpolation"]

    C1 --> D["Flight.__simulate"]
    C2 --> D

    D --> E["_FlightPhases<br/>initial phase + max-time sentinel"]
    E --> F{"Next flight phase?"}

    F -- Yes --> G["Create phase solver<br/>derivative, initial state, time bound"]
    G --> H["_TimeNodes builds phase schedule<br/>phase bounds + sampled events"]
    H --> I{"Next time node?"}

    I -- Yes --> J["Set solver bound to next node"]
    J --> K["Evaluate events scheduled<br/>at current node"]

    K --> L["Event pipeline<br/>trigger → optional exact-time refinement<br/>→ callback → Commands"]
    L --> M["Apply commands"]

    M --> M1["Enable, disable, or add events<br/>update _TimeNodes"]
    M --> M2["Set derivative or start phase<br/>insert _FlightPhase and finish current solver"]
    M --> M3["Terminate flight<br/>truncate phases and finish solver"]

    M1 --> N["Re-sync solver bound if<br/>node schedule changed"]
    N --> O
    M2 --> O
    M3 --> O

    O{"Solver still running?"}
    O -- Yes --> P["solver.step"]
    P --> Q["Store time, state, and<br/>function-evaluation count"]

    Q --> R["Build sampled event nodes between<br/>previous and current solver times"]
    R --> S["Use dense output to evaluate<br/>interpolated states"]

    S --> T{"Overshoot event<br/>requires rollback?"}
    T -- Yes --> U["Restore interpolated<br/>time and state"]
    T -- No --> V
    U --> V["Combine triggered overshoot events<br/>with continuous events"]

    V --> W["Evaluate by priority through<br/>the Event pipeline"]
    W --> X{"Rolled back, but phase<br/>and derivative unchanged?"}

    X -- Yes --> Y["Rebuild solver from<br/>rolled-back state"]
    X -- No --> Z["Continue with current solver state"]
    Y --> AA
    Z --> AA

    AA["Derivative post-processing<br/>when dynamics-changing events exist"]
    AA --> O

    O -- No --> I
    I -- No --> F
    F -- No --> AB["Finalize simulation<br/>cache outputs and sensor data"]

    classDef flight fill:#17324d,color:#fff,stroke:#17324d;
    classDef structure fill:#e8f1f8,color:#17324d,stroke:#5b84a6;
    classDef event fill:#fff3d6,color:#5b4100,stroke:#d39b24;
    classDef command fill:#f5e8f7,color:#53245c,stroke:#9b59a5;
    classDef decision fill:#fff,color:#333,stroke:#666;

    class A,B,D,AB flight;
    class E,G,H,J,P,Q,R,S,Y,AA structure;
    class C,C1,C2,K,L,V,W event;
    class M,M1,M2,M3,N,U command;
    class F,I,O,T,X decision;
Loading

Comment thread docs/user/event_usage.rst
Comment on lines +202 to +203
A callable that returns ``True`` when the event should fire. If ``None``, the
event acts as a passive hook and always triggers when called.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think one could elaborate more on the meaning of always here. I would phrase it like this (if I understood correctly):

If `None`, the `Event` `callback` is executed unconditionally when 
sampled by the simulation (see `sampling_rate` below). Therefore
this `Event` acts as a passive hook.

Comment thread docs/user/event_usage.rst
Comment on lines +438 to +448
**enable_on** (optional)
Automatically enable a disabled event based on a condition. Uses the same
formats as ``disable_on``:

- String preset: ``"apogee"`` or ``"burnout"``
- Simulation time threshold
- Callable predicate

.. tip::
The times when the event is enabled through ``enable_on`` are recorded in
the list ``event.enabled_times`` for later inspection.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which has precedence if both disable_on and enable_on simultaneously trigger?

Comment on lines +223 to +225
Inside a controller function, every sensor attached to the rocket is available
through the ``sensors`` keyword argument (a list, in the order they were added)
and through ``sensors_by_name`` (a dictionary keyed by the sensor ``name``).

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why both? If it really eases the use, I understand, but it seems a bit much, since the two parameters are so similar, if I got it correctly: sensors == sensors_by_name.values(). Personally, I would keep only the dict version, but maybe I am out of touch here.

Comment on lines +333 to +335
data = test_flight.sensor_data[accelerometer]
print("Number of accelerometer samples:", len(data))
print("First sample (t, ax, ay, az):", data[0])

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If (big if) this is the only place the actual Sensor object is used for keying values, I would probably consider adding a method to the Flight class (even though I would generally advise against it so as to avoid bloat). Something akin to:

def get_sensor_values(self, sensor: Sensor):
    """get values/can be reused across reruns"""
    try:
        return self.sensor_data[sensor]
    except KeyError as e:
        raise ... no sensor ... found ... from e

This is, since naming/parameter names is becoming a pretty common key across a few uses, it wouldn't surprise me one attempting:

my_flight.sensor_data["my_accelerometer"]

Non blocking, since the correct way is already documented.

Comment on lines +290 to +308
@property
def log(self):
"""Return the controller callback log."""
return self._log

@log.setter
def log(self, value):
self._log = value
if hasattr(self, "event"):
self.event.callback_log = value

@property
def return_log(self):
"""Alias for :attr:`log`."""
return self.log

@return_log.setter
def return_log(self, value):
self.log = value

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this is an unnecessary duplicate.

height=None,
porosity=0.0432,
drag_coefficient=1.4,
trigger_needs=None,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider putting a , *, parameter before it if you want it to always be last and not positionally usable.

This might apply to other classes also.

Comment on lines +263 to +272
if noise[0] != 0 or noise[1] != 0 or noise[2] != 0:
warnings.warn(
"The `noise` parameter on Parachute is deprecated and has no "
"effect; it will be removed in v1.13. Use a Sensor (e.g. a "
"Barometer) with built-in noise instead, and read the noisy "
"measurement via `kwargs['sensors_by_name']` in your trigger "
"function.",
DeprecationWarning,
stacklevel=3,
)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe the noise parameter is here for too long to remove it that quickly and also in a class that has no _ prefix.

Comment on lines +156 to +194
def solve_exact_time_brentq(
previous_state,
current_state,
interpolator,
event_function,
no_root_error_message,
target=0.0,
xtol=1e-12,
rtol=1e-8,
maxiter=100,
**context,
):
"""Solve exact event time using Brent's method on the interpolated state.

This tries a robust root find on the scalar event function evaluated on the
dense-output interpolator. It requires the event to change sign over the
interval; otherwise a ValueError is raised.
"""
function_context = dict(context)
function_context.pop("state", None)

t0 = previous_state[0]
t1 = current_state[0]

def f(t):
return event_function(interpolator(t), **function_context) - target

y0 = f(t0)
y1 = f(t1)

if y0 == y1:
raise ValueError(no_root_error_message)

try:
event_time = brentq(f, t0, t1, xtol=xtol, rtol=rtol, maxiter=maxiter)
except Exception as e:
raise ValueError(no_root_error_message) from e

return {"event_time": event_time, "event_state": interpolator(event_time)}

@phmbressan phmbressan Jul 23, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could there be any benefits here on making use of different scipy root finders via the root_scalar?

Comment on lines +602 to +603
solid propulsion rockets. Solid motors are automatically mapped to
"solid_propulsion".

@phmbressan phmbressan Jul 23, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Haven't we attempted this automatic mapping before and there were marginal differences in output? Also, two comments:

  • Correct me if I am wrong, but isn't the largest difference between the two sets of equation the inertias and their derivatives? Can't we allow the usage of the simpler set to any rocket, with the warning for other kind of motors that it is less accurate?
  • Can't we merge the two sets and simply put an if clause on the current u_dot_generalized to replicate the older u_dot (e.g. skip products of inertia computation)?

@phmbressan

phmbressan commented Jul 23, 2026

Copy link
Copy Markdown
Collaborator

Rebasing this branch with develop will really be something else. Once this PR is done, I would even consider squashing and cherry-picking on top of develop.

Overall, the new Event API is outstanding, the possibilities of new/complex simulations scenarios are rather interesting.

As it was mentioned in the PR description, a Solution (or analogous) class, with quickly accessible attributes would ease the use of indexing quite a bit. If the class has a closed-set of attributes (which I think it is likely), I recommend __slots__ to make it slightly faster and memory efficient.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Enhancement New feature or request, including adjustments in current codes Events Flight Flight Class related features Parachute Related to parachutes methods and usage Refactor Sensors

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants