Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
0873d21
ENH: added radius and porosity to parachute
ArthurJWH Jun 19, 2025
02ddd1b
ENH: added parachute radius
ArthurJWH Jun 26, 2025
1119ef8
ENH: fixing radius and height attribute in flight class
ArthurJWH Jun 26, 2025
413503f
STY: ruff formatting
ArthurJWH Jun 26, 2025
f09a4b0
TST: updated test value due to gravity update in u_dot_parachute
ArthurJWH Jun 30, 2025
78ef92e
ENH: added new parameters into add_parachute method in Rocket class
ArthurJWH Jul 1, 2025
7defc38
DOC: added new parameters to the documentation
ArthurJWH Jul 3, 2025
465f526
Merge branch 'develop' into enh/improve-parachute-geometric-parametri…
ArthurJWH Jul 4, 2025
c5ce166
TST: updated test values to match the right gravity model
ArthurJWH Jul 15, 2025
f84459d
DOC: improved descriptions of the new parameters
ArthurJWH Jul 15, 2025
4fcb406
ENH: added new parameters to the stochastic parachute
ArthurJWH Aug 12, 2025
159cce8
ENH: implementing previous comments
ArthurJWH Aug 12, 2025
0c434f8
ENH: fixing formatting and adding docs
ArthurJWH Aug 12, 2025
f136ffb
ENH: added ka property to Parachute class
ArthurJWH Aug 12, 2025
112b75a
ENH: fixing variable name
ArthurJWH Aug 18, 2025
40d10c5
ENH: fixed attribute name
ArthurJWH Aug 18, 2025
1e2a2fd
ENH: simplifying variable names
ArthurJWH Aug 19, 2025
65530fd
DOC: adding parameters documentation into StochasticParachute descrip…
ArthurJWH Aug 19, 2025
363bfc8
ENH: changing ma calculation and porosity documentation from previous…
ArthurJWH Sep 5, 2025
53b0940
TST: updated test value due to change in ma calculation
ArthurJWH Sep 5, 2025
87216ca
ENH: changing parameter ka to added_mass_coefficient
ArthurJWH Sep 12, 2025
4f20568
DOC: updating the CHANGELOG
ArthurJWH Sep 12, 2025
68fc191
Merge branch 'develop' into enh/improve-parachute-geometric-parametri…
ArthurJWH Sep 12, 2025
9a03c01
ENH: properly substituted all ka parameters for added_mass_coefficient
ArthurJWH Sep 12, 2025
486a5d1
Merge branch 'enh/improve-parachute-geometric-parametrization' of htt…
ArthurJWH Sep 12, 2025
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
31 changes: 31 additions & 0 deletions rocketpy/rocket/parachute.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,13 @@ class Parachute:
Function of noisy_pressure_signal.
Parachute.clean_pressure_signal_function : Function
Function of clean_pressure_signal.
Parachute.parachute_radius : float
Radius of the inflated 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]

Height of the inflated parachute in meters.
Parachute.porosity : float
Porosity of the parachute material, which is a measure of how much air can
pass through the parachute material.
"""

def __init__(
Expand All @@ -102,6 +109,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 +164,16 @@ 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.

Radius of the inflated parachute. Default value is 1.5.
Units are in meters.
parachute_height : float, optional
Height of the inflated parachute. Default value is the radius parachute.
Units are in meters.
porosity : float, optional
Porosity of the parachute material, which is a measure of how much air can
pass through the parachute material.
Default value is 0.0432 (for consistency with previous versions).
"""
self.name = name
self.cd_s = cd_s
Expand All @@ -170,6 +190,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 +289,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 +318,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
37 changes: 33 additions & 4 deletions rocketpy/rocket/rocket.py
Original file line number Diff line number Diff line change
Expand Up @@ -1434,7 +1434,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 @@ -1493,16 +1502,36 @@ 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
Radius of the inflated parachute. Default value is 1.5.
Units are in meters.
parachute_height : float, optional
Height of the inflated parachute. Default value is the radius parachute.
Units are in meters.
porosity : float, optional
Porosity of the parachute material, which is a measure of how much air can
pass through the parachute material.
Default value is 0.0432 (for consistency with previous versions).
Copy link
Member

Choose a reason for hiding this comment

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

Could you define more precisely what porosity is? Something like: "The ratio of the area of the porous structure to the total area of the canopy" (I don't know if this is the correct definition in our case, just an example)


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 @@ -767,7 +767,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 @@ -1013,7 +1024,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 @@ -1961,22 +1990,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

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 @@ -1987,12 +2022,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)

if post_processing:
self.__post_processed_variables.append(
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.339424, -1.648934, -0.938867)),
("t_final", (0, 0, 159.2210)),
("t_final", (0, 0, 159.0800)),
],
)
def test_aerodynamic_forces(flight_calisto_custom_wind, flight_time, expected_values):
Expand Down
Loading