From 0873d217bc31c87fd5b5af173105e1a67840292c Mon Sep 17 00:00:00 2001 From: ArthurJWH Date: Thu, 19 Jun 2025 12:01:00 -0400 Subject: [PATCH 01/17] 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/17] 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/17] 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/17] 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/17] 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/17] 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/17] 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/17] 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/17] 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/17] 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/17] 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/17] 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/17] 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/17] 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/17] 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/17] 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/17] 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__(