Skip to content
Open
Show file tree
Hide file tree
Changes from 10 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
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,9 @@ main = calisto.add_parachute(
sampling_rate=105,
lag=1.5,
noise=(0, 8.3, 0.5),
parachute_radius=1.5,
parachute_height=1.5,
porosity=0.0432,
Copy link
Member

Choose a reason for hiding this comment

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

having both cds and parachute_radius? do you think we could possible calculate the cd based on these other parameters?

)

drogue = calisto.add_parachute(
Expand All @@ -270,6 +273,9 @@ drogue = calisto.add_parachute(
sampling_rate=105,
lag=1.5,
noise=(0, 8.3, 0.5),
parachute_radius=1.5,
parachute_height=1.5,
porosity=0.0432,
)
```

Expand Down
6 changes: 6 additions & 0 deletions docs/user/first_simulation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,9 @@ Finally, we can add any number of Parachutes to the ``Rocket`` object.
sampling_rate=105,
lag=1.5,
noise=(0, 8.3, 0.5),
parachute_radius=1.5,
parachute_height=1.5,
porosity=0.0432,
)

drogue = calisto.add_parachute(
Expand All @@ -285,6 +288,9 @@ Finally, we can add any number of Parachutes to the ``Rocket`` object.
sampling_rate=105,
lag=1.5,
noise=(0, 8.3, 0.5),
parachute_radius=1.5,
parachute_height=1.5,
porosity=0.0432,
)

We can then see if the rocket is stable by plotting the static margin:
Expand Down
6 changes: 6 additions & 0 deletions docs/user/rocket/rocket_usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,9 @@ apogee and another that will be deployed at 800 meters above ground level:
sampling_rate=105,
lag=1.5,
noise=(0, 8.3, 0.5),
parachute_radius=1.5,
parachute_height=1.5,
porosity=0.0432,
)

drogue = calisto.add_parachute(
Expand All @@ -311,6 +314,9 @@ apogee and another that will be deployed at 800 meters above ground level:
sampling_rate=105,
lag=1.5,
noise=(0, 8.3, 0.5),
parachute_radius=1.5,
parachute_height=1.5,
porosity=0.0432,
)

.. seealso::
Expand Down
36 changes: 35 additions & 1 deletion rocketpy/rocket/parachute.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@


class Parachute:
"""Keeps parachute information.
"""Keeps information of the parachute, which is modeled as a hemispheroid.

Attributes
----------
Expand Down Expand Up @@ -92,6 +92,15 @@ class Parachute:
Function of noisy_pressure_signal.
Parachute.clean_pressure_signal_function : Function
Function of clean_pressure_signal.
Parachute.parachute_radius : float
Length of the non-unique semi-axis (radius) of the inflated hemispheroid
parachute in meters.
Parachute.parachute_height : float
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
Parachute.parachute_height : float
Parachute.parachute_height : float, None

or

Suggested change
Parachute.parachute_height : float
Parachute.parachute_height : Optional[float]

Length of the unique semi-axis (height) of the inflated hemispheroid
parachute in meters.
Parachute.porosity : float
Porosity of the parachute is the ratio of open space in the canopy to total
canopy area.
"""

def __init__(
Expand All @@ -102,6 +111,9 @@ def __init__(
sampling_rate,
lag=0,
noise=(0, 0, 0),
parachute_radius=1.5,
parachute_height=None,
porosity=0.0432,
):
"""Initializes Parachute class.

Expand Down Expand Up @@ -154,6 +166,17 @@ def __init__(
The values are used to add noise to the pressure signal which is
passed to the trigger function. Default value is ``(0, 0, 0)``.
Units are in Pa.
parachute_radius : float, optional
Copy link
Member

Choose a reason for hiding this comment

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

can you be more spacific on the parachute_radius definition please? I believe it is still not intuitive what the radius is.

Length of the non-unique semi-axis (radius) of the inflated hemispheroid
parachute. Default value is 1.5.
Units are in meters.
parachute_height : float, optional
Length of the unique semi-axis (height) of the inflated hemispheroid
parachute. Default value is the radius of the parachute.
Units are in meters.
porosity : float, optional
Porosity of the parachute is the ratio of open space in the canopy to total
canopy area. Default value is 0.0432 (for consistency with previous versions).
"""
self.name = name
self.cd_s = cd_s
Expand All @@ -170,6 +193,11 @@ def __init__(
self.clean_pressure_signal_function = Function(0)
self.noisy_pressure_signal_function = Function(0)
self.noise_signal_function = Function(0)
self.parachute_radius = parachute_radius
if parachute_height is None:
parachute_height = parachute_radius
self.parachute_height = parachute_height
Copy link
Member

Choose a reason for hiding this comment

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

self.parachute_height = parachute_height or parachute_radius

self.porosity = porosity

alpha, beta = self.noise_corr
self.noise_function = lambda: alpha * self.noise_signal[-1][
Expand Down Expand Up @@ -264,6 +292,9 @@ def to_dict(self, include_outputs=False):
"sampling_rate": self.sampling_rate,
"lag": self.lag,
"noise": self.noise,
"parachute_radius": self.parachute_radius,
"parachute_height": self.parachute_height,
"porosity": self.porosity,
}

if include_outputs:
Expand All @@ -290,6 +321,9 @@ def from_dict(cls, data):
sampling_rate=data["sampling_rate"],
lag=data["lag"],
noise=data["noise"],
parachute_radius=data.get("parachute_radius", 1.5),
parachute_height=data.get("parachute_height", None),
porosity=data.get("porosity", 0.0432),
)

return parachute
38 changes: 34 additions & 4 deletions rocketpy/rocket/rocket.py
Copy link
Member

Choose a reason for hiding this comment

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

Question: how should the Monte Carlo simulations be affected by this PR?
Should we open another PR allowing the user to vary these new arguments within the StochasticParachute (https://docs.rocketpy.org/en/latest/reference/classes/monte_carlo/stochastic_models/stochastic_parachute.html) class?

Original file line number Diff line number Diff line change
Expand Up @@ -1433,7 +1433,16 @@ def add_free_form_fins(
return fin_set

def add_parachute(
self, name, cd_s, trigger, sampling_rate=100, lag=0, noise=(0, 0, 0)
self,
name,
cd_s,
trigger,
sampling_rate=100,
lag=0,
noise=(0, 0, 0),
parachute_radius=1.5,
parachute_height=None,
Copy link
Member

Choose a reason for hiding this comment

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

I just noticed it now...

Since it is the Parachute class, there's no need to specify this is the parachute radius or the parachute height.

Suggested change
parachute_radius=1.5,
parachute_height=None,
radius=1.5,
height=None,

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I was looking in other Rocket class methods such as add_trapezoidal_fins and set_rail_buttons, and both use radius already to refer to the fuselage radius. So changing it in the add_parachute method wouldn't break the consistency?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Also, since we already use radius in some inputs as the fuselage radius, maybe it will be more clear as an input to explicitly mention parachute

Copy link
Member

Choose a reason for hiding this comment

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

I would like to hear a third opinion here, maybe @MateusStano or @phmbressan .

Based on my experience, I don't think parachute_radius is a good idea, it sounds verbose to me.
However, I don't know iff we would have any conflict with other classes.

Copy link
Member

Choose a reason for hiding this comment

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

I think the simpler naming is better. If the documentation is there we can rely on that

porosity=0.0432,
):
"""Creates a new parachute, storing its parameters such as
opening delay, drag coefficients and trigger function.
Expand Down Expand Up @@ -1492,16 +1501,37 @@ def add_parachute(
The values are used to add noise to the pressure signal which is
passed to the trigger function. Default value is (0, 0, 0). Units
are in pascal.
parachute_radius : float, optional
Length of the non-unique semi-axis (radius) of the inflated hemispheroid
parachute. Default value is 1.5.
Units are in meters.
parachute_height : float, optional
Length of the unique semi-axis (height) of the inflated hemispheroid
parachute. Default value is the radius of the parachute.
Units are in meters.
porosity : float, optional
Porosity of the parachute is the ratio of open space in the canopy to total
canopy area. Default value is 0.0432 (for consistency with previous versions).

Returns
-------
parachute : Parachute
Parachute containing trigger, sampling_rate, lag, cd_s, noise
and name. Furthermore, it stores clean_pressure_signal,
Parachute containing trigger, sampling_rate, lag, cd_s, noise, radius,
height, porosity and name. Furthermore, it stores clean_pressure_signal,
noise_signal and noisyPressureSignal which are filled in during
Flight simulation.
"""
parachute = Parachute(name, cd_s, trigger, sampling_rate, lag, noise)
parachute = Parachute(
name,
cd_s,
trigger,
sampling_rate,
lag,
noise,
parachute_radius,
parachute_height,
porosity,
)
self.parachutes.append(parachute)
return self.parachutes[-1]

Expand Down
49 changes: 42 additions & 7 deletions rocketpy/simulation/flight.py
Original file line number Diff line number Diff line change
Expand Up @@ -765,7 +765,18 @@ def __simulate(self, verbose):
callbacks = [
lambda self, parachute_cd_s=parachute.cd_s: setattr(
self, "parachute_cd_s", parachute_cd_s
)
),
lambda self,
parachute_radius=parachute.parachute_radius: setattr(
self, "parachute_radius", parachute_radius
),
lambda self,
parachute_height=parachute.parachute_height: setattr(
self, "parachute_height", parachute_height
),
lambda self, parachute_porosity=parachute.porosity: setattr(
self, "parachute_porosity", parachute_porosity
),
]
self.flight_phases.add_phase(
node.t + parachute.lag,
Expand Down Expand Up @@ -1011,7 +1022,25 @@ def __simulate(self, verbose):
lambda self,
parachute_cd_s=parachute.cd_s: setattr(
self, "parachute_cd_s", parachute_cd_s
)
),
lambda self,
parachute_radius=parachute.parachute_radius: setattr(
self,
"parachute_radius",
parachute_radius,
),
lambda self,
parachute_height=parachute.parachute_height: setattr(
self,
"parachute_height",
parachute_height,
),
lambda self,
parachute_porosity=parachute.porosity: setattr(
self,
"parachute_porosity",
parachute_porosity,
),
]
self.flight_phases.add_phase(
overshootable_node.t + parachute.lag,
Expand Down Expand Up @@ -1965,22 +1994,28 @@ def u_dot_parachute(self, t, u, post_processing=False):

# Get Parachute data
cd_s = self.parachute_cd_s
parachute_radius = self.parachute_radius
parachute_height = self.parachute_height
porosity = self.parachute_porosity

# Get the mass of the rocket
mp = self.rocket.dry_mass

# Define constants
ka = 1 # Added mass coefficient (depends on parachute's porosity)
R = 1.5 # Parachute radius
ka = 1.068 * (
1 - 1.465 * porosity - 0.25975 * porosity**2 + 1.2626 * porosity**3
)
# to = 1.2
# eta = 1
# Rdot = (6 * R * (1 - eta) / (1.2**6)) * (
# (1 - eta) * t**5 + eta * (to**3) * (t**2)
# )
# Rdot = 0

# tf = 8 * nominal diameter / velocity at line stretch

# Calculate added mass
ma = ka * rho * (4 / 3) * np.pi * R**3
ma = ka * rho * (4 / 3) * np.pi * parachute_radius**2 * parachute_height
Copy link
Preview

Copilot AI Jul 16, 2025

Choose a reason for hiding this comment

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

The added mass calculation uses the full ellipsoid volume (4/3·π·a²·c) but the parachute is modeled as a hemispheroid; this should use half the volume (2/3·π·a²·c) to correctly compute added mass.

Suggested change
ma = ka * rho * (4 / 3) * np.pi * parachute_radius**2 * parachute_height
ma = ka * rho * (2 / 3) * np.pi * parachute_radius**2 * parachute_height

Copilot uses AI. Check for mistakes.


# Calculate freestream speed
freestream_x = vx - wind_velocity_x
Expand All @@ -1991,12 +2026,12 @@ def u_dot_parachute(self, t, u, post_processing=False):
# Determine drag force
pseudo_drag = -0.5 * rho * cd_s * free_stream_speed
# pseudo_drag = pseudo_drag - ka * rho * 4 * np.pi * (R**2) * Rdot
Copy link
Member

Choose a reason for hiding this comment

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

These comments??? Should we start using it?

Suggested change
pseudo_drag = -0.5 * rho * cd_s * free_stream_speed
# pseudo_drag = pseudo_drag - ka * rho * 4 * np.pi * (R**2) * Rdot
pseudo_drag = -0.5 * rho * cd_s * free_stream_speed
# pseudo_drag = pseudo_drag - ka * rho * 4 * np.pi * (R**2) * Rdot

Dx = pseudo_drag * freestream_x
Dx = pseudo_drag * freestream_x # add eta efficiency for wake
Dy = pseudo_drag * freestream_y
Dz = pseudo_drag * freestream_z
ax = Dx / (mp + ma)
ay = Dy / (mp + ma)
az = (Dz - 9.8 * mp) / (mp + ma)
az = (Dz - mp * self.env.gravity.get_value_opt(z)) / (mp + ma)

# Add coriolis acceleration
_, w_earth_y, w_earth_z = self.env.earth_rotation_vector
Expand Down
2 changes: 1 addition & 1 deletion tests/unit/test_flight.py
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ def test_aerodynamic_moments(flight_calisto_custom_wind, flight_time, expected_v
("t_initial", (1.654150, 0.659142, -0.067103)),
("out_of_rail_time", (5.052628, 2.013361, -1.75370)),
("apogee_time", (2.321838, -1.613641, -0.962108)),
("t_final", (-0.025792, 0.012030, 159.202481)),
("t_final", (-0.025792, 0.012030, 159.061515)),
],
)
def test_aerodynamic_forces(flight_calisto_custom_wind, flight_time, expected_values):
Expand Down
2 changes: 1 addition & 1 deletion tests/unit/test_utilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ def test_fin_flutter_analysis(flight_calisto_custom_wind):
assert np.isclose(flutter_mach(np.inf), 1.0048188594647927, atol=5e-3)
assert np.isclose(safety_factor(0), 64.78797, atol=5e-3)
assert np.isclose(safety_factor(10), 2.1948620401502072, atol=5e-3)
assert np.isclose(safety_factor(np.inf), 61.64222220697017, atol=5e-3)
assert np.isclose(safety_factor(np.inf), 61.669562809629035, atol=5e-3)


def test_flutter_prints(flight_calisto_custom_wind):
Expand Down
Loading