Skip to content
Open
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ Attention: The newest changes should be on top -->

### Fixed

- BUG: rocket with a late-starting thrust curve never leaves the rail [#1085](https://github.com/RocketPy-Team/RocketPy/pull/1085)

## [v1.13.0] - 2026-07-21

### Added
Expand Down
12 changes: 12 additions & 0 deletions rocketpy/simulation/flight.py
Original file line number Diff line number Diff line change
Expand Up @@ -848,6 +848,18 @@ def __setup_phase_time_nodes(self, phase):
# Add last time node
phase.time_nodes.add_node(phase.time_bound, [], [], [])

# A thrust curve that starts at t > 0 leaves the rocket stationary at
# ignition-minus, so the solver (max_step defaults to inf) can take one
# huge step clean over the burn and the rocket never lifts off (#411).
# Force solver stops at ignition and burn-out so the burn is always
# sampled. Guarded to burn_start > 0, so ordinary motors are untouched.
motor = self.rocket.motor
burn_start = getattr(motor, "burn_start_time", 0) or 0
if burn_start > 0:
for t_burn in (motor.burn_start_time, motor.burn_out_time):
if phase.t < t_burn < phase.time_bound:
phase.time_nodes.add_node(t_burn, [], [], [])

# Organize time nodes
phase.time_nodes.sort()
phase.time_nodes.merge()
Expand Down
63 changes: 62 additions & 1 deletion tests/integration/simulation/test_flight.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,72 @@
import numpy.testing as npt
import pytest

from rocketpy import Flight
from rocketpy import Flight, Rocket, SolidMotor

plt.rcParams.update({"figure.max_open_warning": 0})


def test_flight_with_delayed_burn_leaves_rail(example_plain_env):
"""Regression test for #411: a motor whose thrust curve starts at t > 0 must
still lift the rocket off the rail. Previously the solver stepped over the
burn (``max_step`` defaults to ``inf``) so the rocket never left the pad
(``out_of_rail_time`` came out as 0).

Parameters
----------
example_plain_env : rocketpy.Environment
A plain environment object, this is a pytest fixture.
"""
motor = SolidMotor(
thrust_source=[(8.0, 1500.0), (9.0, 2000.0), (14.0, 2000.0), (20.0, 0.0)],
burn_time=(8, 20),
dry_mass=1.815,
dry_inertia=(0.125, 0.125, 0.002),
nozzle_radius=33 / 1000,
grain_number=5,
grain_density=1815,
grain_outer_radius=33 / 1000,
grain_initial_inner_radius=15 / 1000,
grain_initial_height=120 / 1000,
grain_separation=5 / 1000,
grains_center_of_mass_position=0.397,
center_of_dry_mass_position=0.317,
nozzle_position=0,
throat_radius=11 / 1000,
coordinate_system_orientation="nozzle_to_combustion_chamber",
)
rocket = Rocket(
radius=127 / 2000,
mass=14.426,
inertia=(6.321, 6.321, 0.034),
power_off_drag=0.5,
power_on_drag=0.5,
center_of_mass_without_motor=0,
coordinate_system_orientation="tail_to_nose",
)
rocket.add_motor(motor, position=-1.255)
rocket.add_nose(length=0.55829, kind="vonKarman", position=1.278)
rocket.add_trapezoidal_fins(
n=4, root_chord=0.120, tip_chord=0.060, span=0.110, position=-1.04956
)
rocket.set_rail_buttons(0.082, -0.618)

flight = Flight(
rocket=rocket,
environment=example_plain_env,
rail_length=5.2,
inclination=85,
heading=0,
max_time=60,
)

# Ignition is at t = 8 s; the rocket must leave the rail shortly after,
# not remain at the pad the whole flight.
assert flight.out_of_rail_time > 8.0
assert flight.out_of_rail_velocity > 10.0
assert flight.apogee - example_plain_env.elevation > 1000.0


@pytest.mark.parametrize(
"flight_fixture", ["flight_calisto_robust", "flight_calisto_robust_solid_eom"]
)
Expand Down