Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
c51beb0
ENH: added radius and porosity to parachute
ArthurJWH Jun 19, 2025
c82edc9
ENH: fixing radius and height attribute in flight class
ArthurJWH Jun 26, 2025
e9077b9
STY: ruff formatting
ArthurJWH Jun 26, 2025
c8005c6
ENH: added new parameters into add_parachute method in Rocket class
ArthurJWH Jul 1, 2025
3c40acc
DOC: added new parameters to the documentation
ArthurJWH Jul 3, 2025
a81a9a6
DOC: improved descriptions of the new parameters
ArthurJWH Jul 15, 2025
303491e
ENH: implementing previous comments
ArthurJWH Aug 12, 2025
9db403a
ENH: fixing formatting and adding docs
ArthurJWH Aug 12, 2025
05553d4
ENH: Sketch implementation of parachute inflation
ArthurJWH Nov 1, 2025
fec6bad
ENH: added parachute radius
ArthurJWH Jun 26, 2025
149ea9e
ENH: fixing radius and height attribute in flight class
ArthurJWH Jun 26, 2025
b9d02dd
STY: ruff formatting
ArthurJWH Jun 26, 2025
17bcb39
ENH: added new parameters into add_parachute method in Rocket class
ArthurJWH Jul 1, 2025
8cef755
DOC: added new parameters to the documentation
ArthurJWH Jul 3, 2025
17b502f
MNT: manually fixing remaining errors from rebase
ArthurJWH Nov 10, 2025
3da56ff
MNT: fixing typo for exponentiation operator
ArthurJWH Nov 10, 2025
523c5a6
ENH: added radius and porosity to parachute
ArthurJWH Jun 19, 2025
9f28d6e
ENH: fixing radius and height attribute in flight class
ArthurJWH Jun 26, 2025
7334fc4
STY: ruff formatting
ArthurJWH Jun 26, 2025
bfb3f37
ENH: added new parameters into add_parachute method in Rocket class
ArthurJWH Jul 1, 2025
a16a48c
DOC: added new parameters to the documentation
ArthurJWH Jul 3, 2025
7404daa
DOC: improved descriptions of the new parameters
ArthurJWH Jul 15, 2025
2f15b09
ENH: implementing previous comments
ArthurJWH Aug 12, 2025
81667b5
ENH: fixing formatting and adding docs
ArthurJWH Aug 12, 2025
4dac748
ENH: Sketch implementation of parachute inflation
ArthurJWH Nov 1, 2025
f1fa769
ENH: added parachute radius
ArthurJWH Jun 26, 2025
7aef262
ENH: fixing radius and height attribute in flight class
ArthurJWH Jun 26, 2025
c7dc21d
STY: ruff formatting
ArthurJWH Jun 26, 2025
85fb3b8
ENH: added new parameters into add_parachute method in Rocket class
ArthurJWH Jul 1, 2025
301e08f
DOC: added new parameters to the documentation
ArthurJWH Jul 3, 2025
b18b9bf
MNT: manually fixing remaining errors from rebase
ArthurJWH Nov 10, 2025
b861b2d
MNT: fixing typo for exponentiation operator
ArthurJWH Nov 10, 2025
7067104
Merge branch 'develop' into enh/parachute-inflation-modeling
ArthurJWH Nov 28, 2025
0c75906
Merge branch 'enh/parachute-inflation-modeling' of https://github.com…
ArthurJWH Dec 14, 2025
2d5d255
ENH: first draft of parachute inflation
ArthurJWH Dec 14, 2025
29fcfb4
Merge branch 'develop' into enh/parachute-inflation-modeling
ArthurJWH Dec 14, 2025
5d8bb23
ENH: Adding callback for parachute volume
ArthurJWH Dec 14, 2025
01bccda
STY: fixing snakecase
ArthurJWH Dec 14, 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
1 change: 0 additions & 1 deletion rocketpy/rocket/parachute.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,6 @@ def __init__(
- 0.25975 * self.porosity**2
+ 1.2626 * self.porosity**3
)

alpha, beta = self.noise_corr
self.noise_function = lambda: alpha * self.noise_signal[-1][
1
Expand Down
95 changes: 76 additions & 19 deletions rocketpy/simulation/flight.py
Original file line number Diff line number Diff line change
Expand Up @@ -765,6 +765,21 @@ def __simulate(self, verbose):
"parachute_added_mass_coefficient",
added_mass_coefficient,
),
lambda self: setattr(
self,
"parachute_volume",
Copy link
Member

Choose a reason for hiding this comment

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

@ArthurJWH why do you need to calculate the parachute_volume within the flight simulation? Can you calculate and store it in the Parachute class?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

My original idea was to assume the initial radius being the rocket radius (compartment from where it is ejected), but I cannot access the rocket radius within Parachute class. I can add a new parameter "initial radius" for the Parachute class, if you think that is better

(4 / 3)
* math.pi
* (self.parachute_height / self.parachute_radius)
* (
min(
self.parachute_radius,
self.rocket.radius,
)
)
* 3,
Comment on lines +771 to +780
Copy link

Copilot AI Dec 14, 2025

Choose a reason for hiding this comment

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

The parachute_volume initialization formula appears to be incorrect. The expression simplifies to 4 * π * (height/radius) * min(radius, rocket.radius), which has units of m² (area), not m³ (volume). For a hemispheroid parachute, the initial volume should be approximately (2/3) * π * radius^2 * height (half of an oblate spheroid). The current formula will produce incorrect initial volume values that will affect the entire inflation simulation.

Suggested change
(4 / 3)
* math.pi
* (self.parachute_height / self.parachute_radius)
* (
min(
self.parachute_radius,
self.rocket.radius,
)
)
* 3,
(2 / 3)
* math.pi
* self.parachute_radius ** 2
* self.parachute_height,

Copilot uses AI. Check for mistakes.
),
lambda self: delattr(self, "t0"),
Copy link

Copilot AI Dec 14, 2025

Choose a reason for hiding this comment

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

Attempting to delete the 't0' attribute before it has been set will raise an AttributeError. The 't0' attribute is only created later in u_dot_parachute (line 2774) using getattr with a default value. This callback should either be removed or moved to execute after the parachute phase ends, not when it begins.

Suggested change
lambda self: delattr(self, "t0"),
lambda self: delattr(self, "t0") if hasattr(self, "t0") else None,

Copilot uses AI. Check for mistakes.
]
self.flight_phases.add_phase(
node.t + parachute.lag,
Expand Down Expand Up @@ -987,6 +1002,21 @@ def __check_and_handle_parachute_triggers(
"parachute_added_mass_coefficient",
added_mass_coefficient,
),
lambda self: setattr(
self,
"parachute_volume",
(4 / 3)
* math.pi
* (self.parachute_height / self.parachute_radius)
* (
min(
self.parachute_radius,
self.rocket.radius,
)
)
* 3,
Comment on lines +1008 to +1017
Copy link

Copilot AI Dec 14, 2025

Choose a reason for hiding this comment

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

The parachute_volume initialization formula appears to be incorrect. The expression simplifies to 4 * π * (height/radius) * min(radius, rocket.radius), which has units of m² (area), not m³ (volume). For a hemispheroid parachute, the initial volume should be approximately (2/3) * π * radius^2 * height (half of an oblate spheroid). The current formula will produce incorrect initial volume values that will affect the entire inflation simulation.

Suggested change
(4 / 3)
* math.pi
* (self.parachute_height / self.parachute_radius)
* (
min(
self.parachute_radius,
self.rocket.radius,
)
)
* 3,
(2 / 3)
* math.pi
* (min(self.parachute_radius, self.rocket.radius) ** 2)
* self.parachute_height,

Copilot uses AI. Check for mistakes.
),
lambda self: delattr(self, "t0"),
Comment on lines +1005 to +1019
Copy link

Copilot AI Dec 14, 2025

Choose a reason for hiding this comment

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

This code block is duplicated at lines 768-782. Consider extracting the parachute initialization callbacks into a helper method to avoid duplication and ensure both locations remain consistent. The duplication increases maintenance burden and the risk of inconsistencies if one location is updated but not the other.

Copilot uses AI. Check for mistakes.
Copy link

Copilot AI Dec 14, 2025

Choose a reason for hiding this comment

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

Attempting to delete the 't0' attribute before it has been set will raise an AttributeError. The 't0' attribute is only created later in u_dot_parachute (line 2774) using getattr with a default value. This callback should either be removed or moved to execute after the parachute phase ends, not when it begins.

Suggested change
lambda self: delattr(self, "t0"),
lambda self: delattr(self, "t0") if hasattr(self, "t0") else None,

Copilot uses AI. Check for mistakes.
]
self.flight_phases.add_phase(
node.t + parachute.lag,
Expand Down Expand Up @@ -2702,31 +2732,58 @@ def u_dot_parachute(self, t, u, post_processing=False):
# Get the mass of the rocket
mp = self.rocket.dry_mass

# 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 = (
self.parachute_added_mass_coefficient
* rho
* (2 / 3)
* np.pi
* self.parachute_radius**2
* self.parachute_height
)

# Calculate freestream speed
freestream_x = vx - wind_velocity_x
freestream_y = vy - wind_velocity_y
freestream_z = vz
free_stream_speed = (freestream_x**2 + freestream_y**2 + freestream_z**2) ** 0.5

# Initialize parachute geometrical parameters
radius = self.parachute_radius
Copy link
Member

Choose a reason for hiding this comment

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

Wha is the benefit behind this radius = self.parachute_radius?

Copy link
Contributor Author

@ArthurJWH ArthurJWH Dec 16, 2025

Choose a reason for hiding this comment

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

I thought it would help with the readability of the code, especially when the parameters are used in the convoluted equations. I can remove it

Copy link
Member

Choose a reason for hiding this comment

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

I understand it.
it does not contribute so much to the readability actually.

We can save a few 0.0001 ns by removing this line

height = self.parachute_height
inflated_radius = (
Copy link
Member

Choose a reason for hiding this comment

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

calculating inflated_radius in the Flight class? Do you really need it?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

inflated_radius is the parachute radius in the current Flight stage, it is used to calculate the surface area of the parachute

(3 * self.parachute_volume * radius) / (4 * math.pi * height)
) ** (1 / 3)
inflated_height = inflated_radius * height / radius

# Calculate the surface area of the parachute
if radius > height:
e = math.sqrt(1 - (inflated_height**2) / (inflated_radius**2))
surface_area = (
math.pi * inflated_radius**2 * (1 + (1 - e**2) / e * math.atanh(e))
)
else:
e = math.sqrt(1 - (inflated_radius**2) / (inflated_height**2))
surface_area = (
math.pi
* inflated_radius**2
* (1 + inflated_height / (e * inflated_radius) * math.asin(e))
Comment on lines +2751 to +2760
Copy link

Copilot AI Dec 14, 2025

Choose a reason for hiding this comment

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

The surface area calculation can fail with a domain error when the eccentricity formula yields a negative value under sqrt. This can occur if inflated_height > inflated_radius when radius > height (line 2751), or if inflated_radius > inflated_height when radius <= height (line 2756). Add validation to ensure the discriminant is non-negative before taking the square root, or clamp the eccentricity to valid ranges [0, 1).

Suggested change
e = math.sqrt(1 - (inflated_height**2) / (inflated_radius**2))
surface_area = (
math.pi * inflated_radius**2 * (1 + (1 - e**2) / e * math.atanh(e))
)
else:
e = math.sqrt(1 - (inflated_radius**2) / (inflated_height**2))
surface_area = (
math.pi
* inflated_radius**2
* (1 + inflated_height / (e * inflated_radius) * math.asin(e))
discriminant = 1 - (inflated_height**2) / (inflated_radius**2)
if discriminant < 0:
warnings.warn(
f"Parachute eccentricity discriminant negative ({discriminant:.6f}); "
"clamping to zero. Check parachute geometry parameters."
)
discriminant = 0.0
elif discriminant > 1:
warnings.warn(
f"Parachute eccentricity discriminant > 1 ({discriminant:.6f}); "
"clamping to one. Check parachute geometry parameters."
)
discriminant = 1.0
e = math.sqrt(discriminant)
surface_area = (
math.pi * inflated_radius**2 * (1 + (1 - e**2) / e * math.atanh(e)) if e != 0 else
math.pi * inflated_radius**2 * 2 # limit as e->0
)
else:
discriminant = 1 - (inflated_radius**2) / (inflated_height**2)
if discriminant < 0:
warnings.warn(
f"Parachute eccentricity discriminant negative ({discriminant:.6f}); "
"clamping to zero. Check parachute geometry parameters."
)
discriminant = 0.0
elif discriminant > 1:
warnings.warn(
f"Parachute eccentricity discriminant > 1 ({discriminant:.6f}); "
"clamping to one. Check parachute geometry parameters."
)
discriminant = 1.0
e = math.sqrt(discriminant)
surface_area = (
math.pi
* inflated_radius**2
* (1 + inflated_height / (e * inflated_radius) * math.asin(e)) if e != 0 else
math.pi * inflated_radius**2 * 2 # limit as e->0

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Since the inflated radius / inflated height = radius / height, the if else statement should already account for sign of the value under the square root

)

# Calculate volume flow of air into parachute
volume_flow = (
rho
* freestream_z # considering parachute as vertical
Comment on lines +2763 to +2766
Copy link

Copilot AI Dec 14, 2025

Choose a reason for hiding this comment

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

The volume flow calculation assumes the parachute is vertical by using only freestream_z. However, parachutes don't necessarily descend perfectly vertically, especially in windy conditions or during initial deployment. The horizontal velocity components (freestream_x, freestream_y) should also contribute to the flow into the parachute. Consider using the total freestream velocity magnitude or projecting the velocity onto the parachute's orientation axis.

Suggested change
# Calculate volume flow of air into parachute
volume_flow = (
rho
* freestream_z # considering parachute as vertical
# Calculate volume flow of air into parachute using total freestream velocity magnitude
freestream_velocity_magnitude = math.sqrt(
freestream_x**2 + freestream_y**2 + freestream_z**2
)
volume_flow = (
rho
* freestream_velocity_magnitude

Copilot uses AI. Check for mistakes.
* (
(math.pi * inflated_radius**2)
- (self.parachute_porosity * surface_area)
)
)
Comment on lines +2764 to +2771
Copy link

Copilot AI Dec 14, 2025

Choose a reason for hiding this comment

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

The volume_flow calculation has incorrect units and logic. Currently, it computes rho * velocity * area, which yields mass flow rate (kg/s), not volume flow rate (m³/s). The variable should be renamed to 'mass_flow' and the integration on line 2779 should add mass, not volume. Alternatively, remove the rho multiplication here to get true volume flow, then multiply by rho only when computing the added mass on line 2782.

Copilot uses AI. Check for mistakes.

# Getting time step
Copy link
Member

Choose a reason for hiding this comment

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

do you really need to store step.t0? Why?

Should this really be a public attribute?

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 used it to have access to the time step between u_dot_parachute calls (not ideal). I can change it for a private attribute, or do you have any other suggestion?

Copy link
Member

Choose a reason for hiding this comment

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

private is better

self.t0 = getattr(self, "t0", t)
t1 = t
dt = t1 - self.t0

# Integrating parachute volume
self.parachute_volume += volume_flow * dt

# Dragged air mass
ma = self.parachute_volume * rho

# Moving time step
self.t0 = t1
Comment on lines +2773 to +2785
Copy link

Copilot AI Dec 14, 2025

Choose a reason for hiding this comment

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

This manual integration approach using timestep differences is inefficient and can introduce numerical errors. The ODE solver will call this derivative function multiple times per timestep with different intermediate values of t, but the integration assumes each call represents a true timestep. This will cause parachute_volume to accumulate incorrectly. Consider reformulating this as a proper differential equation by adding parachute_volume to the state vector u, and returning its time derivative as part of u_dot.

Copilot uses AI. Check for mistakes.
Comment on lines +2741 to +2785
Copy link

Copilot AI Dec 14, 2025

Choose a reason for hiding this comment

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

The new parachute inflation model (lines 2741-2785) lacks test coverage. This is a significant new feature that modifies how parachute dynamics are simulated. Consider adding tests that verify: (1) parachute_volume increases over time during inflation, (2) the volume converges to the expected inflated volume, (3) the surface area calculations produce physically reasonable values, and (4) edge cases like very small or very large parachutes are handled correctly.

Copilot uses AI. Check for mistakes.

# Determine drag force
pseudo_drag = -0.5 * rho * self.parachute_cd_s * free_stream_speed
# pseudo_drag = pseudo_drag - ka * rho * 4 * np.pi * (R**2) * Rdot
Expand Down
Loading