From 0873d217bc31c87fd5b5af173105e1a67840292c Mon Sep 17 00:00:00 2001 From: ArthurJWH Date: Thu, 19 Jun 2025 12:01:00 -0400 Subject: [PATCH 01/22] ENH: added radius and porosity to parachute --- rocketpy/rocket/parachute.py | 8 ++++++++ rocketpy/simulation/flight.py | 30 +++++++++++++++++++++++------- 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/rocketpy/rocket/parachute.py b/rocketpy/rocket/parachute.py index a18cb4793..11bdce63e 100644 --- a/rocketpy/rocket/parachute.py +++ b/rocketpy/rocket/parachute.py @@ -102,6 +102,8 @@ def __init__( sampling_rate, lag=0, noise=(0, 0, 0), + parachute_radius=1.5, + porosity=0.0432, ): """Initializes Parachute class. @@ -161,6 +163,8 @@ def __init__( self.sampling_rate = sampling_rate self.lag = lag self.noise = noise + self.parachute_radius = parachute_radius + self.porosity = porosity self.noise_signal = [[-1e-6, np.random.normal(noise[0], noise[1])]] self.noisy_pressure_signal = [] self.clean_pressure_signal = [] @@ -264,6 +268,8 @@ def to_dict(self, include_outputs=False): "sampling_rate": self.sampling_rate, "lag": self.lag, "noise": self.noise, + "parachute_radius": self.parachute_radius, + "porosity": self.porosity, } if include_outputs: @@ -290,6 +296,8 @@ def from_dict(cls, data): sampling_rate=data["sampling_rate"], lag=data["lag"], noise=data["noise"], + parachute_radius=data["parachute_radius"], + porosity=data["porosity"], ) return parachute diff --git a/rocketpy/simulation/flight.py b/rocketpy/simulation/flight.py index ce728dafe..9c3b1b9d3 100644 --- a/rocketpy/simulation/flight.py +++ b/rocketpy/simulation/flight.py @@ -767,7 +767,13 @@ 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.radius: setattr( + self, "parachute_radius", parachute_radius + ), + lambda self, parachute_porosity=parachute.porosity: setattr( + self, "parachute_porosity", parachute_porosity + ), ] self.flight_phases.add_phase( node.t + parachute.lag, @@ -1013,7 +1019,13 @@ 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.radius: setattr( + self, "parachute_radius", parachute_radius + ), + lambda self, parachute_porosity=parachute.porosity: setattr( + self, "parachute_porosity", parachute_porosity + ), ] self.flight_phases.add_phase( overshootable_node.t + parachute.lag, @@ -1961,13 +1973,15 @@ def u_dot_parachute(self, t, u, post_processing=False): # Get Parachute data cd_s = self.parachute_cd_s + R = self.parachute_radius + 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)) * ( @@ -1975,8 +1989,10 @@ def u_dot_parachute(self, t, u, post_processing=False): # ) # 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 * R**3 # ma = ka * rho * (4 / 3) * np.pi * R**2 * height # Calculate freestream speed freestream_x = vx - wind_velocity_x @@ -1985,9 +2001,9 @@ def u_dot_parachute(self, t, u, post_processing=False): free_stream_speed = (freestream_x**2 + freestream_y**2 + freestream_z**2) ** 0.5 # Determine drag force - pseudo_drag = -0.5 * rho * cd_s * free_stream_speed + pseudo_drag = -0.5 * rho * cd_s * free_stream_speed # * Area # 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) From 02ddd1b638c79ca9c650b2871d7d554e945190f3 Mon Sep 17 00:00:00 2001 From: ArthurJWH Date: Wed, 25 Jun 2025 20:57:52 -0400 Subject: [PATCH 02/22] ENH: added parachute radius --- rocketpy/rocket/parachute.py | 19 +++++++++++++++++-- rocketpy/simulation/flight.py | 15 +++++++++++---- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/rocketpy/rocket/parachute.py b/rocketpy/rocket/parachute.py index 11bdce63e..a1f96f912 100644 --- a/rocketpy/rocket/parachute.py +++ b/rocketpy/rocket/parachute.py @@ -103,6 +103,7 @@ def __init__( lag=0, noise=(0, 0, 0), parachute_radius=1.5, + parachute_height=None, porosity=0.0432, ): """Initializes Parachute class. @@ -156,6 +157,15 @@ 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 + Radius of the inflated parachute in meters. Default value is 1.5. + Units are in meters. + parachute_height : float, optional + Height of the inflated parachute in meters. + 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. """ self.name = name self.cd_s = cd_s @@ -164,6 +174,9 @@ def __init__( self.lag = lag self.noise = noise self.parachute_radius = parachute_radius + if parachute_height is None: + parachute_height = parachute_radius + self.parachute_height = parachute_height self.porosity = porosity self.noise_signal = [[-1e-6, np.random.normal(noise[0], noise[1])]] self.noisy_pressure_signal = [] @@ -269,6 +282,7 @@ def to_dict(self, include_outputs=False): "lag": self.lag, "noise": self.noise, "parachute_radius": self.parachute_radius, + "parachute_height": self.parachute_height, "porosity": self.porosity, } @@ -296,8 +310,9 @@ def from_dict(cls, data): sampling_rate=data["sampling_rate"], lag=data["lag"], noise=data["noise"], - parachute_radius=data["parachute_radius"], - porosity=data["porosity"], + parachute_radius=data.get("parachute_radius", 1.5), + parachute_height=data.get("parachute_height", None), + porosity=data.get("porosity", 0.0432), ) return parachute diff --git a/rocketpy/simulation/flight.py b/rocketpy/simulation/flight.py index 9c3b1b9d3..f5b6edc18 100644 --- a/rocketpy/simulation/flight.py +++ b/rocketpy/simulation/flight.py @@ -771,6 +771,9 @@ def __simulate(self, verbose): lambda self, parachute_radius=parachute.radius: setattr( self, "parachute_radius", parachute_radius ), + lambda self, parachute_height=parachute.height: setattr( + self, "parachute_height", parachute_height + ), lambda self, parachute_porosity=parachute.porosity: setattr( self, "parachute_porosity", parachute_porosity ), @@ -1023,6 +1026,9 @@ def __simulate(self, verbose): lambda self, parachute_radius=parachute.radius: setattr( self, "parachute_radius", parachute_radius ), + lambda self, parachute_height=parachute.height: setattr( + self, "parachute_height", parachute_height + ), lambda self, parachute_porosity=parachute.porosity: setattr( self, "parachute_porosity", parachute_porosity ), @@ -1973,7 +1979,8 @@ def u_dot_parachute(self, t, u, post_processing=False): # Get Parachute data cd_s = self.parachute_cd_s - R = self.parachute_radius + parachute_radius = self.parachute_radius + parachute_height = self.parachute_height porosity = self.parachute_porosity @@ -1992,7 +1999,7 @@ def u_dot_parachute(self, t, u, post_processing=False): # 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 * R**2 * height + ma = ka * rho * (4 / 3) * np.pi * parachute_radius**2 * parachute_height # Calculate freestream speed freestream_x = vx - wind_velocity_x @@ -2001,14 +2008,14 @@ def u_dot_parachute(self, t, u, post_processing=False): free_stream_speed = (freestream_x**2 + freestream_y**2 + freestream_z**2) ** 0.5 # Determine drag force - pseudo_drag = -0.5 * rho * cd_s * free_stream_speed # * Area + 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 # 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( From 1119ef8d581c3fd1616041a3e35140fdea677c0a Mon Sep 17 00:00:00 2001 From: ArthurJWH Date: Wed, 25 Jun 2025 21:12:21 -0400 Subject: [PATCH 03/22] ENH: fixing radius and height attribute in flight class --- rocketpy/simulation/flight.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rocketpy/simulation/flight.py b/rocketpy/simulation/flight.py index f5b6edc18..0e0bc597c 100644 --- a/rocketpy/simulation/flight.py +++ b/rocketpy/simulation/flight.py @@ -768,10 +768,10 @@ 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.radius: setattr( + lambda self, parachute_radius=parachute.parachute_radius: setattr( self, "parachute_radius", parachute_radius ), - lambda self, parachute_height=parachute.height: setattr( + lambda self, parachute_height=parachute.parachute_height: setattr( self, "parachute_height", parachute_height ), lambda self, parachute_porosity=parachute.porosity: setattr( @@ -1023,10 +1023,10 @@ def __simulate(self, verbose): parachute_cd_s=parachute.cd_s: setattr( self, "parachute_cd_s", parachute_cd_s ), - lambda self, parachute_radius=parachute.radius: setattr( + lambda self, parachute_radius=parachute.parachute_radius: setattr( self, "parachute_radius", parachute_radius ), - lambda self, parachute_height=parachute.height: setattr( + lambda self, parachute_height=parachute.parachute_height: setattr( self, "parachute_height", parachute_height ), lambda self, parachute_porosity=parachute.porosity: setattr( From 413503fc3a3669df1e03943413e6995c27cff4c8 Mon Sep 17 00:00:00 2001 From: ArthurJWH Date: Thu, 26 Jun 2025 12:31:44 -0400 Subject: [PATCH 04/22] STY: ruff formatting --- rocketpy/simulation/flight.py | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/rocketpy/simulation/flight.py b/rocketpy/simulation/flight.py index 0e0bc597c..c78c961bd 100644 --- a/rocketpy/simulation/flight.py +++ b/rocketpy/simulation/flight.py @@ -768,10 +768,12 @@ 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( + lambda self, + parachute_radius=parachute.parachute_radius: setattr( self, "parachute_radius", parachute_radius ), - lambda self, parachute_height=parachute.parachute_height: setattr( + lambda self, + parachute_height=parachute.parachute_height: setattr( self, "parachute_height", parachute_height ), lambda self, parachute_porosity=parachute.porosity: setattr( @@ -1023,14 +1025,23 @@ def __simulate(self, verbose): 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_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_height=parachute.parachute_height: setattr( + self, + "parachute_height", + parachute_height, ), - lambda self, parachute_porosity=parachute.porosity: setattr( - self, "parachute_porosity", parachute_porosity + lambda self, + parachute_porosity=parachute.porosity: setattr( + self, + "parachute_porosity", + parachute_porosity, ), ] self.flight_phases.add_phase( @@ -1983,12 +1994,13 @@ def u_dot_parachute(self, t, u, post_processing=False): parachute_height = self.parachute_height porosity = self.parachute_porosity - # Get the mass of the rocket mp = self.rocket.dry_mass # Define constants - ka = 1.068 * (1 - 1.465 * porosity - 0.25975 * porosity**2 + 1.2626 * porosity**3) + 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)) * ( @@ -2010,7 +2022,7 @@ 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 - Dx = pseudo_drag * freestream_x # add eta efficiency for wake + Dx = pseudo_drag * freestream_x # add eta efficiency for wake Dy = pseudo_drag * freestream_y Dz = pseudo_drag * freestream_z ax = Dx / (mp + ma) From f09a4b0fa8bffad56e4f5cd1be322fee06850155 Mon Sep 17 00:00:00 2001 From: ArthurJWH Date: Mon, 30 Jun 2025 15:45:55 -0400 Subject: [PATCH 05/22] TST: updated test value due to gravity update in u_dot_parachute --- tests/unit/test_flight.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/test_flight.py b/tests/unit/test_flight.py index 260a2b138..a2cc1012a 100644 --- a/tests/unit/test_flight.py +++ b/tests/unit/test_flight.py @@ -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): From 78ef92e76c00210c64660a6b3b90d231520d0d65 Mon Sep 17 00:00:00 2001 From: ArthurJWH Date: Tue, 1 Jul 2025 11:07:29 -0400 Subject: [PATCH 06/22] ENH: added new parameters into add_parachute method in Rocket class --- rocketpy/rocket/parachute.py | 26 ++++++++++++++++--------- rocketpy/rocket/rocket.py | 37 ++++++++++++++++++++++++++++++++---- 2 files changed, 50 insertions(+), 13 deletions(-) diff --git a/rocketpy/rocket/parachute.py b/rocketpy/rocket/parachute.py index a1f96f912..7d5212c61 100644 --- a/rocketpy/rocket/parachute.py +++ b/rocketpy/rocket/parachute.py @@ -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 + 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__( @@ -158,14 +165,15 @@ def __init__( passed to the trigger function. Default value is ``(0, 0, 0)``. Units are in Pa. parachute_radius : float, optional - Radius of the inflated parachute in meters. Default value is 1.5. + Radius of the inflated parachute. Default value is 1.5. Units are in meters. parachute_height : float, optional - Height of the inflated parachute in meters. - Default value is the radius parachute. Units are in meters. + 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. + pass through the parachute material. + Default value is 0.0432 (for consistency with previous versions). """ self.name = name self.cd_s = cd_s @@ -173,11 +181,6 @@ def __init__( self.sampling_rate = sampling_rate self.lag = lag self.noise = noise - self.parachute_radius = parachute_radius - if parachute_height is None: - parachute_height = parachute_radius - self.parachute_height = parachute_height - self.porosity = porosity self.noise_signal = [[-1e-6, np.random.normal(noise[0], noise[1])]] self.noisy_pressure_signal = [] self.clean_pressure_signal = [] @@ -187,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 + self.porosity = porosity alpha, beta = self.noise_corr self.noise_function = lambda: alpha * self.noise_signal[-1][ diff --git a/rocketpy/rocket/rocket.py b/rocketpy/rocket/rocket.py index bf938d4be..c0f91b744 100644 --- a/rocketpy/rocket/rocket.py +++ b/rocketpy/rocket/rocket.py @@ -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, + porosity=0.0432, ): """Creates a new parachute, storing its parameters such as opening delay, drag coefficients and trigger function. @@ -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). 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] From 7defc38ceabb689e886d1acbba07d85571e5caaf Mon Sep 17 00:00:00 2001 From: ArthurJWH Date: Thu, 3 Jul 2025 10:35:18 -0400 Subject: [PATCH 07/22] DOC: added new parameters to the documentation --- README.md | 6 ++++++ docs/user/first_simulation.rst | 6 ++++++ docs/user/rocket/rocket_usage.rst | 6 ++++++ 3 files changed, 18 insertions(+) diff --git a/README.md b/README.md index 15127e966..52f5985bd 100644 --- a/README.md +++ b/README.md @@ -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, ) drogue = calisto.add_parachute( @@ -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, ) ``` diff --git a/docs/user/first_simulation.rst b/docs/user/first_simulation.rst index 5624ed926..e3ff65724 100644 --- a/docs/user/first_simulation.rst +++ b/docs/user/first_simulation.rst @@ -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( @@ -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: diff --git a/docs/user/rocket/rocket_usage.rst b/docs/user/rocket/rocket_usage.rst index 1b4d2bbd5..349471463 100644 --- a/docs/user/rocket/rocket_usage.rst +++ b/docs/user/rocket/rocket_usage.rst @@ -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( @@ -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:: From c5ce166fa42aaf69a0daf3fb4f3bcb2dcc3bee14 Mon Sep 17 00:00:00 2001 From: ArthurJWH Date: Tue, 15 Jul 2025 07:41:02 -0400 Subject: [PATCH 08/22] TST: updated test values to match the right gravity model --- tests/unit/test_flight.py | 2 +- tests/unit/test_utilities.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit/test_flight.py b/tests/unit/test_flight.py index 04bd97c40..31e27de42 100644 --- a/tests/unit/test_flight.py +++ b/tests/unit/test_flight.py @@ -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): diff --git a/tests/unit/test_utilities.py b/tests/unit/test_utilities.py index 55d45de95..2bb8608b5 100644 --- a/tests/unit/test_utilities.py +++ b/tests/unit/test_utilities.py @@ -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): From f84459d7e78ca081f9dcd205d4674c359a7fb12f Mon Sep 17 00:00:00 2001 From: ArthurJWH Date: Tue, 15 Jul 2025 07:49:17 -0400 Subject: [PATCH 09/22] DOC: improved descriptions of the new parameters --- rocketpy/rocket/parachute.py | 23 +++++++++++++---------- rocketpy/rocket/rocket.py | 11 ++++++----- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/rocketpy/rocket/parachute.py b/rocketpy/rocket/parachute.py index 7d5212c61..90c457393 100644 --- a/rocketpy/rocket/parachute.py +++ b/rocketpy/rocket/parachute.py @@ -9,7 +9,7 @@ class Parachute: - """Keeps parachute information. + """Keeps information of the parachute, which is modeled as a hemispheroid. Attributes ---------- @@ -93,12 +93,14 @@ class Parachute: Parachute.clean_pressure_signal_function : Function Function of clean_pressure_signal. Parachute.parachute_radius : float - Radius of the inflated parachute in meters. + Length of the non-unique semi-axis (radius) of the inflated hemispheroid + parachute in meters. Parachute.parachute_height : float - Height of the inflated parachute in meters. + Length of the unique semi-axis (height) of the inflated hemispheroid + 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. + Porosity of the parachute is the ratio of open space in the canopy to total + canopy area. """ def __init__( @@ -165,15 +167,16 @@ def __init__( passed to the trigger function. Default value is ``(0, 0, 0)``. Units are in Pa. parachute_radius : float, optional - Radius of the inflated parachute. Default value is 1.5. + 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 - Height of the inflated parachute. Default value is the radius parachute. + 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 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). + 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 diff --git a/rocketpy/rocket/rocket.py b/rocketpy/rocket/rocket.py index 3b3b5655a..0324250c1 100644 --- a/rocketpy/rocket/rocket.py +++ b/rocketpy/rocket/rocket.py @@ -1502,15 +1502,16 @@ def add_parachute( 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. + 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 - Height of the inflated parachute. Default value is the radius parachute. + 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 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). + 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 ------- From 4fcb40692af1e0dcbbf94fb62e6528c583f65e05 Mon Sep 17 00:00:00 2001 From: ArthurJWH Date: Tue, 12 Aug 2025 09:39:09 -0400 Subject: [PATCH 10/22] ENH: added new parameters to the stochastic parachute --- rocketpy/stochastic/stochastic_parachute.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/rocketpy/stochastic/stochastic_parachute.py b/rocketpy/stochastic/stochastic_parachute.py index 4cf7746d7..3cfa80647 100644 --- a/rocketpy/stochastic/stochastic_parachute.py +++ b/rocketpy/stochastic/stochastic_parachute.py @@ -39,6 +39,9 @@ def __init__( sampling_rate=None, lag=None, noise=None, + parachute_radius=None, + parachute_height=None, + porosity=None, ): """Initializes the Stochastic Parachute class. @@ -70,6 +73,9 @@ def __init__( self.sampling_rate = sampling_rate self.lag = lag self.noise = noise + self.parachute_radius = parachute_radius + self.parachute_height = parachute_height + self.porosity = porosity self._validate_trigger(trigger) self._validate_noise(noise) @@ -81,6 +87,9 @@ def __init__( lag=lag, noise=noise, name=None, + parachute_radius=parachute_radius, + parachute_height=parachute_height, + porosity=porosity ) def _validate_trigger(self, trigger): From 159cce8ed02706906e022302cf559c0d425996ac Mon Sep 17 00:00:00 2001 From: ArthurJWH Date: Tue, 12 Aug 2025 09:48:46 -0400 Subject: [PATCH 11/22] ENH: implementing previous comments --- rocketpy/rocket/parachute.py | 4 +--- rocketpy/simulation/flight.py | 12 +++--------- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/rocketpy/rocket/parachute.py b/rocketpy/rocket/parachute.py index 90c457393..e637f1d51 100644 --- a/rocketpy/rocket/parachute.py +++ b/rocketpy/rocket/parachute.py @@ -194,9 +194,7 @@ def __init__( 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 + self.parachute_height = parachute_height or parachute_radius self.porosity = porosity alpha, beta = self.noise_corr diff --git a/rocketpy/simulation/flight.py b/rocketpy/simulation/flight.py index 036224d94..2606c7b06 100644 --- a/rocketpy/simulation/flight.py +++ b/rocketpy/simulation/flight.py @@ -1992,18 +1992,12 @@ def u_dot_parachute(self, t, u, post_processing=False): wind_velocity_x = self.env.wind_velocity_x.get_value_opt(z) wind_velocity_y = self.env.wind_velocity_y.get_value_opt(z) - # 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.068 * ( - 1 - 1.465 * porosity - 0.25975 * porosity**2 + 1.2626 * porosity**3 + 1 - 1.465 * self.parachute_porosity - 0.25975 * self.parachute_porosity**2 + 1.2626 * self.parachute_porosity**3 ) # to = 1.2 # eta = 1 @@ -2015,7 +2009,7 @@ def u_dot_parachute(self, t, u, post_processing=False): # tf = 8 * nominal diameter / velocity at line stretch # Calculate added mass - ma = ka * rho * (4 / 3) * np.pi * parachute_radius**2 * parachute_height + ma = ka * rho * (4 / 3) * np.pi * self.parachute_radius**2 * self.parachute_height # Calculate freestream speed freestream_x = vx - wind_velocity_x @@ -2024,7 +2018,7 @@ def u_dot_parachute(self, t, u, post_processing=False): free_stream_speed = (freestream_x**2 + freestream_y**2 + freestream_z**2) ** 0.5 # Determine drag force - pseudo_drag = -0.5 * rho * cd_s * free_stream_speed + pseudo_drag = -0.5 * rho * self.parachute_cd_s * free_stream_speed # pseudo_drag = pseudo_drag - ka * rho * 4 * np.pi * (R**2) * Rdot Dx = pseudo_drag * freestream_x # add eta efficiency for wake Dy = pseudo_drag * freestream_y From 0c434f8993c08646f0338a6a5926d4c6e0158125 Mon Sep 17 00:00:00 2001 From: ArthurJWH Date: Tue, 12 Aug 2025 10:04:24 -0400 Subject: [PATCH 12/22] ENH: fixing formatting and adding docs --- rocketpy/rocket/parachute.py | 2 +- rocketpy/simulation/flight.py | 14 ++++++++++++-- rocketpy/stochastic/stochastic_parachute.py | 8 +++++++- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/rocketpy/rocket/parachute.py b/rocketpy/rocket/parachute.py index e637f1d51..3997769cd 100644 --- a/rocketpy/rocket/parachute.py +++ b/rocketpy/rocket/parachute.py @@ -95,7 +95,7 @@ class Parachute: Parachute.parachute_radius : float Length of the non-unique semi-axis (radius) of the inflated hemispheroid parachute in meters. - Parachute.parachute_height : float + Parachute.parachute_height : float, None Length of the unique semi-axis (height) of the inflated hemispheroid parachute in meters. Parachute.porosity : float diff --git a/rocketpy/simulation/flight.py b/rocketpy/simulation/flight.py index 2606c7b06..9c8c8cb89 100644 --- a/rocketpy/simulation/flight.py +++ b/rocketpy/simulation/flight.py @@ -1997,7 +1997,10 @@ def u_dot_parachute(self, t, u, post_processing=False): # Define constants ka = 1.068 * ( - 1 - 1.465 * self.parachute_porosity - 0.25975 * self.parachute_porosity**2 + 1.2626 * self.parachute_porosity**3 + 1 + - 1.465 * self.parachute_porosity + - 0.25975 * self.parachute_porosity**2 + + 1.2626 * self.parachute_porosity**3 ) # to = 1.2 # eta = 1 @@ -2009,7 +2012,14 @@ def u_dot_parachute(self, t, u, post_processing=False): # tf = 8 * nominal diameter / velocity at line stretch # Calculate added mass - ma = ka * rho * (4 / 3) * np.pi * self.parachute_radius**2 * self.parachute_height + ma = ( + ka + * rho + * (4 / 3) + * np.pi + * self.parachute_radius**2 + * self.parachute_height + ) # Calculate freestream speed freestream_x = vx - wind_velocity_x diff --git a/rocketpy/stochastic/stochastic_parachute.py b/rocketpy/stochastic/stochastic_parachute.py index 3cfa80647..5a74f02ff 100644 --- a/rocketpy/stochastic/stochastic_parachute.py +++ b/rocketpy/stochastic/stochastic_parachute.py @@ -66,6 +66,12 @@ def __init__( noise : list List of tuples in the form of (mean, standard deviation, time-correlation). + parachute_radius : tuple, list, int, float + Radius of the parachute in meters. + parachute_height : tuple, list, int, float + Height of the parachute in meters. + porosity : tuple, list, int, float + Porosity of the parachute. """ self.parachute = parachute self.cd_s = cd_s @@ -89,7 +95,7 @@ def __init__( name=None, parachute_radius=parachute_radius, parachute_height=parachute_height, - porosity=porosity + porosity=porosity, ) def _validate_trigger(self, trigger): From f136ffbaace3000a26a6671fe8c3e325ef2da946 Mon Sep 17 00:00:00 2001 From: ArthurJWH Date: Tue, 12 Aug 2025 15:23:16 -0400 Subject: [PATCH 13/22] ENH: added ka property to Parachute class --- rocketpy/rocket/parachute.py | 6 ++++++ rocketpy/simulation/flight.py | 15 +++++++-------- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/rocketpy/rocket/parachute.py b/rocketpy/rocket/parachute.py index 3997769cd..f4b07a927 100644 --- a/rocketpy/rocket/parachute.py +++ b/rocketpy/rocket/parachute.py @@ -196,6 +196,12 @@ def __init__( self.parachute_radius = parachute_radius self.parachute_height = parachute_height or parachute_radius self.porosity = porosity + self.ka = 1.068 * ( + 1 + - 1.465 * self.parachute_porosity + - 0.25975 * self.parachute_porosity**2 + + 1.2626 * self.parachute_porosity**3 + ) alpha, beta = self.noise_corr self.noise_function = lambda: alpha * self.noise_signal[-1][ diff --git a/rocketpy/simulation/flight.py b/rocketpy/simulation/flight.py index 9c8c8cb89..13dd0eb9e 100644 --- a/rocketpy/simulation/flight.py +++ b/rocketpy/simulation/flight.py @@ -777,6 +777,9 @@ def __simulate(self, verbose): lambda self, parachute_porosity=parachute.porosity: setattr( self, "parachute_porosity", parachute_porosity ), + lambda self, ka=parachute.ka: setattr( + self, "parachute_ka", ka + ), ] self.flight_phases.add_phase( node.t + parachute.lag, @@ -1041,6 +1044,9 @@ def __simulate(self, verbose): "parachute_porosity", parachute_porosity, ), + lambda self, ka=parachute.ka: setattr( + self, "parachute_ka", ka + ), ] self.flight_phases.add_phase( overshootable_node.t + parachute.lag, @@ -1995,13 +2001,6 @@ def u_dot_parachute(self, t, u, post_processing=False): # Get the mass of the rocket mp = self.rocket.dry_mass - # Define constants - ka = 1.068 * ( - 1 - - 1.465 * self.parachute_porosity - - 0.25975 * self.parachute_porosity**2 - + 1.2626 * self.parachute_porosity**3 - ) # to = 1.2 # eta = 1 # Rdot = (6 * R * (1 - eta) / (1.2**6)) * ( @@ -2013,7 +2012,7 @@ def u_dot_parachute(self, t, u, post_processing=False): # Calculate added mass ma = ( - ka + self.ka * rho * (4 / 3) * np.pi From 112b75aa5b85a511fc4b7a582ba60afb90f16aa3 Mon Sep 17 00:00:00 2001 From: ArthurJWH Date: Mon, 18 Aug 2025 16:39:53 -0400 Subject: [PATCH 14/22] ENH: fixing variable name --- rocketpy/rocket/parachute.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rocketpy/rocket/parachute.py b/rocketpy/rocket/parachute.py index f4b07a927..7c55a7ff2 100644 --- a/rocketpy/rocket/parachute.py +++ b/rocketpy/rocket/parachute.py @@ -198,9 +198,9 @@ def __init__( self.porosity = porosity self.ka = 1.068 * ( 1 - - 1.465 * self.parachute_porosity - - 0.25975 * self.parachute_porosity**2 - + 1.2626 * self.parachute_porosity**3 + - 1.465 * self.porosity + - 0.25975 * self.porosity**2 + + 1.2626 * self.porosity**3 ) alpha, beta = self.noise_corr From 40d10c5069439032343550f0e9118529d4d86187 Mon Sep 17 00:00:00 2001 From: ArthurJWH Date: Mon, 18 Aug 2025 17:18:17 -0400 Subject: [PATCH 15/22] ENH: fixed attribute name --- rocketpy/simulation/flight.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rocketpy/simulation/flight.py b/rocketpy/simulation/flight.py index 13dd0eb9e..48fa7c75c 100644 --- a/rocketpy/simulation/flight.py +++ b/rocketpy/simulation/flight.py @@ -1045,7 +1045,7 @@ def __simulate(self, verbose): parachute_porosity, ), lambda self, ka=parachute.ka: setattr( - self, "parachute_ka", ka + self, "ka", ka ), ] self.flight_phases.add_phase( From 1e2a2fdd997b3c9c4c6ec9af009946ca3a3040f6 Mon Sep 17 00:00:00 2001 From: ArthurJWH Date: Tue, 19 Aug 2025 16:59:41 -0400 Subject: [PATCH 16/22] ENH: simplifying variable names --- rocketpy/rocket/parachute.py | 24 ++++++++++----------- rocketpy/rocket/rocket.py | 12 +++++------ rocketpy/simulation/flight.py | 4 ++-- rocketpy/stochastic/stochastic_parachute.py | 16 +++++++------- 4 files changed, 28 insertions(+), 28 deletions(-) diff --git a/rocketpy/rocket/parachute.py b/rocketpy/rocket/parachute.py index 7c55a7ff2..b5a8dd729 100644 --- a/rocketpy/rocket/parachute.py +++ b/rocketpy/rocket/parachute.py @@ -92,10 +92,10 @@ class Parachute: Function of noisy_pressure_signal. Parachute.clean_pressure_signal_function : Function Function of clean_pressure_signal. - Parachute.parachute_radius : float + Parachute.radius : float Length of the non-unique semi-axis (radius) of the inflated hemispheroid parachute in meters. - Parachute.parachute_height : float, None + Parachute.height : float, None Length of the unique semi-axis (height) of the inflated hemispheroid parachute in meters. Parachute.porosity : float @@ -111,8 +111,8 @@ def __init__( sampling_rate, lag=0, noise=(0, 0, 0), - parachute_radius=1.5, - parachute_height=None, + radius=1.5, + height=None, porosity=0.0432, ): """Initializes Parachute class. @@ -166,11 +166,11 @@ 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 + 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 + 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. @@ -193,8 +193,8 @@ 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 - self.parachute_height = parachute_height or parachute_radius + self.radius = radius + self.height = height or radius self.porosity = porosity self.ka = 1.068 * ( 1 @@ -296,8 +296,8 @@ 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, + "radius": self.radius, + "height": self.height, "porosity": self.porosity, } @@ -325,8 +325,8 @@ 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), + radius=data.get("radius", 1.5), + height=data.get("height", None), porosity=data.get("porosity", 0.0432), ) diff --git a/rocketpy/rocket/rocket.py b/rocketpy/rocket/rocket.py index 0324250c1..752d33896 100644 --- a/rocketpy/rocket/rocket.py +++ b/rocketpy/rocket/rocket.py @@ -1440,8 +1440,8 @@ def add_parachute( sampling_rate=100, lag=0, noise=(0, 0, 0), - parachute_radius=1.5, - parachute_height=None, + radius=1.5, + height=None, porosity=0.0432, ): """Creates a new parachute, storing its parameters such as @@ -1501,11 +1501,11 @@ 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 : 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 + 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. @@ -1528,8 +1528,8 @@ def add_parachute( sampling_rate, lag, noise, - parachute_radius, - parachute_height, + radius, + height, porosity, ) self.parachutes.append(parachute) diff --git a/rocketpy/simulation/flight.py b/rocketpy/simulation/flight.py index 48fa7c75c..57e8a3b72 100644 --- a/rocketpy/simulation/flight.py +++ b/rocketpy/simulation/flight.py @@ -1027,13 +1027,13 @@ def __simulate(self, verbose): self, "parachute_cd_s", parachute_cd_s ), lambda self, - parachute_radius=parachute.parachute_radius: setattr( + parachute_radius=parachute.radius: setattr( self, "parachute_radius", parachute_radius, ), lambda self, - parachute_height=parachute.parachute_height: setattr( + parachute_height=parachute.height: setattr( self, "parachute_height", parachute_height, diff --git a/rocketpy/stochastic/stochastic_parachute.py b/rocketpy/stochastic/stochastic_parachute.py index 5a74f02ff..8fc0a82bf 100644 --- a/rocketpy/stochastic/stochastic_parachute.py +++ b/rocketpy/stochastic/stochastic_parachute.py @@ -39,8 +39,8 @@ def __init__( sampling_rate=None, lag=None, noise=None, - parachute_radius=None, - parachute_height=None, + radius=None, + height=None, porosity=None, ): """Initializes the Stochastic Parachute class. @@ -66,9 +66,9 @@ def __init__( noise : list List of tuples in the form of (mean, standard deviation, time-correlation). - parachute_radius : tuple, list, int, float + radius : tuple, list, int, float Radius of the parachute in meters. - parachute_height : tuple, list, int, float + height : tuple, list, int, float Height of the parachute in meters. porosity : tuple, list, int, float Porosity of the parachute. @@ -79,8 +79,8 @@ def __init__( self.sampling_rate = sampling_rate self.lag = lag self.noise = noise - self.parachute_radius = parachute_radius - self.parachute_height = parachute_height + self.radius = radius + self.height = height self.porosity = porosity self._validate_trigger(trigger) @@ -93,8 +93,8 @@ def __init__( lag=lag, noise=noise, name=None, - parachute_radius=parachute_radius, - parachute_height=parachute_height, + radius=radius, + height=height, porosity=porosity, ) From 65530fd175f5e95b7bdfa306a011d15d52cf3ed1 Mon Sep 17 00:00:00 2001 From: ArthurJWH Date: Tue, 19 Aug 2025 17:01:43 -0400 Subject: [PATCH 17/22] DOC: adding parameters documentation into StochasticParachute description --- rocketpy/stochastic/stochastic_parachute.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/rocketpy/stochastic/stochastic_parachute.py b/rocketpy/stochastic/stochastic_parachute.py index 8fc0a82bf..dea8a077d 100644 --- a/rocketpy/stochastic/stochastic_parachute.py +++ b/rocketpy/stochastic/stochastic_parachute.py @@ -29,6 +29,12 @@ class StochasticParachute(StochasticModel): time-correlation). name : list[str] List with the name of the parachute object. This cannot be randomized. + radius : tuple, list, int, float + Radius of the parachute in meters. + height : tuple, list, int, float + Height of the parachute in meters. + porosity : tuple, list, int, float + Porosity of the parachute. """ def __init__( From 363bfc80a022fda377fff2d292483174f001990e Mon Sep 17 00:00:00 2001 From: ArthurJWH Date: Fri, 5 Sep 2025 09:18:32 -0400 Subject: [PATCH 18/22] ENH: changing ma calculation and porosity documentation from previous comments --- rocketpy/rocket/parachute.py | 12 ++++++++---- rocketpy/rocket/rocket.py | 6 ++++-- rocketpy/simulation/flight.py | 2 +- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/rocketpy/rocket/parachute.py b/rocketpy/rocket/parachute.py index b5a8dd729..cdf943901 100644 --- a/rocketpy/rocket/parachute.py +++ b/rocketpy/rocket/parachute.py @@ -99,8 +99,10 @@ class Parachute: 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. + Geometric porosity of the canopy (ratio of open area to total canopy area), + in [0, 1]. Affects only the added-mass scaling during descent; it does + not change ``cd_s`` (drag). The default, 0.0432, yields an added-mass + of 1.0 (“neutral” behavior). """ def __init__( @@ -175,8 +177,10 @@ def __init__( 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). + Geometric porosity of the canopy (ratio of open area to total canopy area), + in [0, 1]. Affects only the added-mass scaling during descent; it does + not change ``cd_s`` (drag). The default, 0.0432, yields an added-mass + of 1.0 (“neutral” behavior). """ self.name = name self.cd_s = cd_s diff --git a/rocketpy/rocket/rocket.py b/rocketpy/rocket/rocket.py index 752d33896..c421f6abc 100644 --- a/rocketpy/rocket/rocket.py +++ b/rocketpy/rocket/rocket.py @@ -1510,8 +1510,10 @@ def add_parachute( 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). + Geometric porosity of the canopy (ratio of open area to total canopy area), + in [0, 1]. Affects only the added-mass scaling during descent; it does + not change ``cd_s`` (drag). The default, 0.0432, yields an added-mass + of 1.0 (“neutral” behavior). Returns ------- diff --git a/rocketpy/simulation/flight.py b/rocketpy/simulation/flight.py index 57e8a3b72..040287ed1 100644 --- a/rocketpy/simulation/flight.py +++ b/rocketpy/simulation/flight.py @@ -2014,7 +2014,7 @@ def u_dot_parachute(self, t, u, post_processing=False): ma = ( self.ka * rho - * (4 / 3) + * (2 / 3) * np.pi * self.parachute_radius**2 * self.parachute_height From 53b09401f11c0027766847df568aa9318caeb451 Mon Sep 17 00:00:00 2001 From: ArthurJWH Date: Fri, 5 Sep 2025 15:49:13 -0400 Subject: [PATCH 19/22] TST: updated test value due to change in ma calculation --- tests/unit/test_flight.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/test_flight.py b/tests/unit/test_flight.py index 31e27de42..1bd9384e2 100644 --- a/tests/unit/test_flight.py +++ b/tests/unit/test_flight.py @@ -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.061515)), + ("t_final", (-0.019802, 0.012030, 159.051604)), ], ) def test_aerodynamic_forces(flight_calisto_custom_wind, flight_time, expected_values): From 87216caf5eb8686a8ff2715330bac8a9d3a259f7 Mon Sep 17 00:00:00 2001 From: ArthurJWH Date: Fri, 12 Sep 2025 11:13:19 -0400 Subject: [PATCH 20/22] ENH: changing parameter ka to added_mass_coefficient --- rocketpy/rocket/parachute.py | 5 ++++- rocketpy/simulation/flight.py | 7 ++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/rocketpy/rocket/parachute.py b/rocketpy/rocket/parachute.py index cdf943901..732f16e1f 100644 --- a/rocketpy/rocket/parachute.py +++ b/rocketpy/rocket/parachute.py @@ -103,6 +103,9 @@ class Parachute: in [0, 1]. Affects only the added-mass scaling during descent; it does not change ``cd_s`` (drag). The default, 0.0432, yields an added-mass of 1.0 (“neutral” behavior). + Parachute.added_mass_coefficient : float + Coefficient used to calculate the added-mass due to dragged air. It is + calculated from the porosity of the parachute. """ def __init__( @@ -200,7 +203,7 @@ def __init__( self.radius = radius self.height = height or radius self.porosity = porosity - self.ka = 1.068 * ( + self.added_mass_coefficient = 1.068 * ( 1 - 1.465 * self.porosity - 0.25975 * self.porosity**2 diff --git a/rocketpy/simulation/flight.py b/rocketpy/simulation/flight.py index 040287ed1..d8b69e84a 100644 --- a/rocketpy/simulation/flight.py +++ b/rocketpy/simulation/flight.py @@ -777,7 +777,7 @@ def __simulate(self, verbose): lambda self, parachute_porosity=parachute.porosity: setattr( self, "parachute_porosity", parachute_porosity ), - lambda self, ka=parachute.ka: setattr( + lambda self, ka=parachute.added_mass_coefficient: setattr( self, "parachute_ka", ka ), ] @@ -1044,7 +1044,8 @@ def __simulate(self, verbose): "parachute_porosity", parachute_porosity, ), - lambda self, ka=parachute.ka: setattr( + lambda self, + ka=parachute.added_mass_coefficient: setattr( self, "ka", ka ), ] @@ -2012,7 +2013,7 @@ def u_dot_parachute(self, t, u, post_processing=False): # Calculate added mass ma = ( - self.ka + self.added_mass_coefficient * rho * (2 / 3) * np.pi From 4f20568f6579aee6195178e59c81db0724bf20b6 Mon Sep 17 00:00:00 2001 From: ArthurJWH Date: Fri, 12 Sep 2025 11:17:05 -0400 Subject: [PATCH 21/22] DOC: updating the CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f78b34edb..f3393aeb5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,7 @@ Attention: The newest changes should be on top --> ### Added - ENH: Add the Coriolis Force to the Flight class [#799](https://github.com/RocketPy-Team/RocketPy/pull/799) +- ENH: Improve parachute geometric parametrization [#835](https://github.com/RocketPy-Team/RocketPy/pull/835) ### Changed From 9a03c01cd305b68f46f1a308955448304d564ce2 Mon Sep 17 00:00:00 2001 From: ArthurJWH Date: Fri, 12 Sep 2025 13:23:31 -0400 Subject: [PATCH 22/22] ENH: properly substituted all ka parameters for added_mass_coefficient --- rocketpy/simulation/flight.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/rocketpy/simulation/flight.py b/rocketpy/simulation/flight.py index d8b69e84a..11e737b5a 100644 --- a/rocketpy/simulation/flight.py +++ b/rocketpy/simulation/flight.py @@ -777,8 +777,11 @@ def __simulate(self, verbose): lambda self, parachute_porosity=parachute.porosity: setattr( self, "parachute_porosity", parachute_porosity ), - lambda self, ka=parachute.added_mass_coefficient: setattr( - self, "parachute_ka", ka + lambda self, + added_mass_coefficient=parachute.added_mass_coefficient: setattr( + self, + "parachute_added_mass_coefficient", + added_mass_coefficient, ), ] self.flight_phases.add_phase( @@ -1045,8 +1048,10 @@ def __simulate(self, verbose): parachute_porosity, ), lambda self, - ka=parachute.added_mass_coefficient: setattr( - self, "ka", ka + added_mass_coefficient=parachute.added_mass_coefficient: setattr( + self, + "parachute_added_mass_coefficient", + added_mass_coefficient, ), ] self.flight_phases.add_phase( @@ -2013,7 +2018,7 @@ def u_dot_parachute(self, t, u, post_processing=False): # Calculate added mass ma = ( - self.added_mass_coefficient + self.parachute_added_mass_coefficient * rho * (2 / 3) * np.pi