From 95b8f637c64f057a172e5a3ba901b4bdf060d049 Mon Sep 17 00:00:00 2001 From: Giovani Hidalgo Ceotto Date: Wed, 14 Feb 2024 00:55:28 -0300 Subject: [PATCH 01/10] FIX: Add reference area factor correction to lift and CP calculations and prints --- rocketpy/prints/rocket_prints.py | 9 ++++++++- rocketpy/rocket/rocket.py | 10 +++++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/rocketpy/prints/rocket_prints.py b/rocketpy/prints/rocket_prints.py index 264a67bb0..e6f476434 100644 --- a/rocketpy/prints/rocket_prints.py +++ b/rocketpy/prints/rocket_prints.py @@ -114,9 +114,13 @@ def rocket_aerodynamics_quantities(self): print("\nAerodynamics Lift Coefficient Derivatives\n") for surface, position in self.rocket.aerodynamic_surfaces: name = surface.name + # ref_factor corrects lift for different reference areas + ref_factor = (surface.rocket_radius / self.radius) ** 2 print( name - + " Lift Coefficient Derivative: {:.3f}".format(surface.clalpha(0)) + + " Lift Coefficient Derivative: {:.3f}".format( + ref_factor * surface.clalpha(0) + ) + "/rad" ) @@ -135,6 +139,9 @@ def rocket_aerodynamics_quantities(self): print( f"Center of Mass position (time=0): {self.rocket.center_of_mass(0):.3f} m" ) + print( + f"Center of Pressure position (time=0): {self.rocket.cp_position(0):.3f} m" + ) print( "Initial Static Margin (mach=0, time=0): " + "{:.3f}".format(self.rocket.static_margin(0)) diff --git a/rocketpy/rocket/rocket.py b/rocketpy/rocket/rocket.py index 30f5d389b..18545d9ea 100644 --- a/rocketpy/rocket/rocket.py +++ b/rocketpy/rocket/rocket.py @@ -517,9 +517,13 @@ def evaluate_center_of_pressure(self): # Calculate total lift coefficient derivative and center of pressure if len(self.aerodynamic_surfaces) > 0: for aero_surface, position in self.aerodynamic_surfaces: - self.total_lift_coeff_der += aero_surface.clalpha - self.cp_position += aero_surface.clalpha * ( - position - self._csys * aero_surface.cpz + # ref_factor corrects lift for different reference areas + ref_factor = (aero_surface.rocket_radius / self.radius) ** 2 + self.total_lift_coeff_der += ref_factor * aero_surface.clalpha + self.cp_position += ( + ref_factor + * aero_surface.clalpha + * (position - self._csys * aero_surface.cpz) ) self.cp_position /= self.total_lift_coeff_der From 700c165de4a01700105ee9741cd0126fd373fa78 Mon Sep 17 00:00:00 2001 From: Giovani Hidalgo Ceotto Date: Wed, 14 Feb 2024 01:06:16 -0300 Subject: [PATCH 02/10] ENH: Improve docs and arguments for add_nose and add_*_fins --- rocketpy/rocket/rocket.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/rocketpy/rocket/rocket.py b/rocketpy/rocket/rocket.py index 18545d9ea..5781d22ed 100644 --- a/rocketpy/rocket/rocket.py +++ b/rocketpy/rocket/rocket.py @@ -875,7 +875,9 @@ def add_tail( self.add_surfaces(tail, position) return tail - def add_nose(self, length, kind, position, bluffness=0, name="Nose Cone"): + def add_nose( + self, length, kind, position, bluffness=0, base_radius=None, name="Nose Cone" + ): """Creates a nose cone, storing its parameters as part of the aerodynamic_surfaces list. Its parameters are the axial position along the rocket and its derivative of the coefficient of lift @@ -896,6 +898,8 @@ def add_nose(self, length, kind, position, bluffness=0, name="Nose Cone"): bluffness : float, optional Ratio between the radius of the circle on the tip of the ogive and the radius of the base of the ogive. + base_radius : int, float, optional + Nose cone base radius in meters. If not given, use rocket radius. name : string Nose cone name. Default is "Nose Cone". @@ -911,8 +915,8 @@ def add_nose(self, length, kind, position, bluffness=0, name="Nose Cone"): nose = NoseCone( length=length, kind=kind, - base_radius=self.radius, - rocket_radius=self.radius, + base_radius=base_radius or self.radius, + rocket_radius=base_radius or self.radius, bluffness=bluffness, name=name, ) @@ -987,8 +991,9 @@ def add_trapezoidal_fins( with its base perpendicular to the rocket's axis. Cannot be used in conjunction with sweep_length. radius : int, float, optional - Reference radius to calculate lift coefficient. If None, which is - default, use rocket radius. + Reference fuselage radius where the fins are located. This is used + to calculate lift coefficient and to draw the rocket. If None, + which is default, use rocket radius. airfoil : tuple, optional Default is null, in which case fins will be treated as flat plates. Otherwise, if tuple, fins will be considered as airfoils. The @@ -1068,8 +1073,9 @@ def add_elliptical_fins( Fins cant angle with respect to the rocket centerline. Must be given in degrees. radius : int, float, optional - Reference radius to calculate lift coefficient. If None, which - is default, use rocket radius. + Reference fuselage radius where the fins are located. This is used + to calculate lift coefficient and to draw the rocket. If None, + which is default, use rocket radius. airfoil : tuple, optional Default is null, in which case fins will be treated as flat plates. Otherwise, if tuple, fins will be considered as airfoils. The From 09fa80589873c2f5262bee7e962eeae22ece7a48 Mon Sep 17 00:00:00 2001 From: Giovani Hidalgo Ceotto Date: Wed, 14 Feb 2024 01:08:00 -0300 Subject: [PATCH 03/10] FIX: make sure NoseCone.rocket_radius is always defined --- rocketpy/rocket/aero_surface.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/rocketpy/rocket/aero_surface.py b/rocketpy/rocket/aero_surface.py index eb3c0f36c..728c15ec1 100644 --- a/rocketpy/rocket/aero_surface.py +++ b/rocketpy/rocket/aero_surface.py @@ -349,12 +349,20 @@ def evaluate_geometrical_parameters(self): # If base radius is not given, the ratio between base radius and # rocket radius is assumed as 1, meaning that the nose cone has the # same radius as the rocket - if self.base_radius is None or self.rocket_radius is None: + if self.base_radius is None and self.rocket_radius is not None: self.radius_ratio = 1 + self.base_radius = self.rocket_radius + elif self.base_radius is not None and self.rocket_radius is None: + self.radius_ratio = 1 + self.rocket_radius = self.base_radius # If base radius is given, the ratio between base radius and rocket # radius is calculated - else: + elif self.base_radius is not None or self.rocket_radius is not None: self.radius_ratio = self.base_radius / self.rocket_radius + else: + raise ValueError( + "Either base radius or rocket radius must be given to calculate the nose cone radius ratio." + ) self.fineness_ratio = self.length / (2 * self.base_radius) return None From 7da6c588e0529a4cc92629005e19f2f231d57850 Mon Sep 17 00:00:00 2001 From: Giovani Hidalgo Ceotto Date: Wed, 14 Feb 2024 01:20:16 -0300 Subject: [PATCH 04/10] FIX: rocket radius reference in rocket prints --- rocketpy/prints/rocket_prints.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rocketpy/prints/rocket_prints.py b/rocketpy/prints/rocket_prints.py index e6f476434..615bb55ac 100644 --- a/rocketpy/prints/rocket_prints.py +++ b/rocketpy/prints/rocket_prints.py @@ -115,7 +115,7 @@ def rocket_aerodynamics_quantities(self): for surface, position in self.rocket.aerodynamic_surfaces: name = surface.name # ref_factor corrects lift for different reference areas - ref_factor = (surface.rocket_radius / self.radius) ** 2 + ref_factor = (surface.rocket_radius / self.rocket.radius) ** 2 print( name + " Lift Coefficient Derivative: {:.3f}".format( From 67819457ea0616ff78c2d231502955f5dad4757a Mon Sep 17 00:00:00 2001 From: Giovani Hidalgo Ceotto Date: Wed, 14 Feb 2024 06:19:52 -0300 Subject: [PATCH 05/10] FIX: change conditional when evaluating base and rocket radius values Co-authored-by: Gui-FernandesBR <63590233+Gui-FernandesBR@users.noreply.github.com> --- rocketpy/rocket/aero_surface.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rocketpy/rocket/aero_surface.py b/rocketpy/rocket/aero_surface.py index 728c15ec1..c5d154f3e 100644 --- a/rocketpy/rocket/aero_surface.py +++ b/rocketpy/rocket/aero_surface.py @@ -357,7 +357,7 @@ def evaluate_geometrical_parameters(self): self.rocket_radius = self.base_radius # If base radius is given, the ratio between base radius and rocket # radius is calculated - elif self.base_radius is not None or self.rocket_radius is not None: + elif self.base_radius is not None and self.rocket_radius is not None: self.radius_ratio = self.base_radius / self.rocket_radius else: raise ValueError( From c63cb148dbf1e66b162af2a23cabd2896d0bc0e6 Mon Sep 17 00:00:00 2001 From: giovaniceotto Date: Wed, 14 Feb 2024 07:35:08 -0300 Subject: [PATCH 06/10] MAINT: make base_radius last add_nose argument to avoid breaking changes --- rocketpy/rocket/rocket.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rocketpy/rocket/rocket.py b/rocketpy/rocket/rocket.py index 5781d22ed..4b7fb2d91 100644 --- a/rocketpy/rocket/rocket.py +++ b/rocketpy/rocket/rocket.py @@ -876,7 +876,7 @@ def add_tail( return tail def add_nose( - self, length, kind, position, bluffness=0, base_radius=None, name="Nose Cone" + self, length, kind, position, bluffness=0, name="Nose Cone", base_radius=None ): """Creates a nose cone, storing its parameters as part of the aerodynamic_surfaces list. Its parameters are the axial position @@ -898,10 +898,10 @@ def add_nose( bluffness : float, optional Ratio between the radius of the circle on the tip of the ogive and the radius of the base of the ogive. - base_radius : int, float, optional - Nose cone base radius in meters. If not given, use rocket radius. name : string Nose cone name. Default is "Nose Cone". + base_radius : int, float, optional + Nose cone base radius in meters. If not given, use rocket radius. See Also -------- From 51ec868b7a1ea89187f2eaf909f20d40cbe66030 Mon Sep 17 00:00:00 2001 From: Gui-FernandesBR Date: Mon, 19 Feb 2024 13:34:17 -0500 Subject: [PATCH 07/10] MNT: update CHANGELOG with #558 --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9005764c2..7372e91e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Fixed +## [Unreleased] - 2024-02-22 + +You can install this version by running `pip install rocketpy==1.2.1` + +### Fixed + +- BUG: Add reference area factor correction to aero surfaces (solves #557) [#558](https://github.com/RocketPy-Team/RocketPy/pull/558) + ## [v1.2.0] - 2024-02-12 You can install this version by running `pip install rocketpy==1.2.0` From 955f61da83d90bda0d5d8443ff70debc99cd0f27 Mon Sep 17 00:00:00 2001 From: Gui-FernandesBR Date: Mon, 19 Feb 2024 13:34:42 -0500 Subject: [PATCH 08/10] DOC: change active to passive voice in rocket_radius documentation --- rocketpy/rocket/rocket.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/rocketpy/rocket/rocket.py b/rocketpy/rocket/rocket.py index 4b7fb2d91..ffcf3b1c8 100644 --- a/rocketpy/rocket/rocket.py +++ b/rocketpy/rocket/rocket.py @@ -901,7 +901,8 @@ def add_nose( name : string Nose cone name. Default is "Nose Cone". base_radius : int, float, optional - Nose cone base radius in meters. If not given, use rocket radius. + Nose cone base radius in meters. If not given, the rocket radius + will be used. See Also -------- @@ -993,7 +994,7 @@ def add_trapezoidal_fins( radius : int, float, optional Reference fuselage radius where the fins are located. This is used to calculate lift coefficient and to draw the rocket. If None, - which is default, use rocket radius. + which is default, the rocket radius will be used. airfoil : tuple, optional Default is null, in which case fins will be treated as flat plates. Otherwise, if tuple, fins will be considered as airfoils. The @@ -1075,7 +1076,7 @@ def add_elliptical_fins( radius : int, float, optional Reference fuselage radius where the fins are located. This is used to calculate lift coefficient and to draw the rocket. If None, - which is default, use rocket radius. + which is default, the rocket radius will be used. airfoil : tuple, optional Default is null, in which case fins will be treated as flat plates. Otherwise, if tuple, fins will be considered as airfoils. The From b4e565be9badfbd81ab0c1ae2e18409459c8faa7 Mon Sep 17 00:00:00 2001 From: Gui-FernandesBR Date: Mon, 19 Feb 2024 13:35:27 -0500 Subject: [PATCH 09/10] TST: add tests to cover the #557 issue --- tests/test_rocket.py | 75 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/tests/test_rocket.py b/tests/test_rocket.py index 21616a5fc..cd77f41a1 100644 --- a/tests/test_rocket.py +++ b/tests/test_rocket.py @@ -1,8 +1,10 @@ from unittest.mock import patch import numpy as np +import pytest from rocketpy import Rocket, SolidMotor +from rocketpy.rocket import NoseCone @patch("matplotlib.pyplot.show") @@ -205,3 +207,76 @@ def test_air_brakes_clamp_off(mock_show, calisto_air_brakes_clamp_off): assert air_brakes_clamp_off.deployment_level == 0 assert air_brakes_clamp_off.all_info() == None + + +def test_add_surfaces_different_noses(calisto): + """Test the add_surfaces method with different nose cone configurations. + More specifically, this will checks the static margin of the rocket with + different nose cone configurations. + + Parameters + ---------- + calisto : Rocket + Pytest fixture for the calisto rocket. + """ + length = 0.55829 + kind = "vonkarman" + position = 1.16 + bluffness = 0 + base_radius = 0.0635 + rocket_radius = 0.0635 + + # Case 1: base_radius == rocket_radius + nose1 = NoseCone( + length, + kind, + base_radius=base_radius, + bluffness=bluffness, + rocket_radius=rocket_radius, + name="Nose Cone 1", + ) + calisto.add_surfaces(nose1, position) + assert nose1.radius_ratio == pytest.approx(1, 1e-8) + assert calisto.static_margin(0) == pytest.approx(-8.9053, 0.01) + + # Case 2: base_radius == rocket_radius / 2 + calisto.aerodynamic_surfaces.remove(nose1) + nose2 = NoseCone( + length, + kind, + base_radius=base_radius / 2, + bluffness=bluffness, + rocket_radius=rocket_radius, + name="Nose Cone 2", + ) + calisto.add_surfaces(nose2, position) + assert nose2.radius_ratio == pytest.approx(0.5, 1e-8) + assert calisto.static_margin(0) == pytest.approx(-8.9053, 0.01) + + # Case 3: base_radius == None + calisto.aerodynamic_surfaces.remove(nose2) + nose3 = NoseCone( + length, + kind, + base_radius=None, + bluffness=bluffness, + rocket_radius=rocket_radius * 2, + name="Nose Cone 3", + ) + calisto.add_surfaces(nose3, position) + assert nose3.radius_ratio == pytest.approx(1, 1e-8) + assert calisto.static_margin(0) == pytest.approx(-8.9053, 0.01) + + # Case 4: rocket_radius == None + calisto.aerodynamic_surfaces.remove(nose3) + nose4 = NoseCone( + length, + kind, + base_radius=base_radius, + bluffness=bluffness, + rocket_radius=None, + name="Nose Cone 4", + ) + calisto.add_surfaces(nose4, position) + assert nose4.radius_ratio == pytest.approx(1, 1e-8) + assert calisto.static_margin(0) == pytest.approx(-8.9053, 0.01) From 643ea6eeb235311297972b3a1599700cc6709e4f Mon Sep 17 00:00:00 2001 From: Gui-FernandesBR Date: Mon, 19 Feb 2024 17:39:27 -0500 Subject: [PATCH 10/10] MNT: Fix typos from review --- CHANGELOG.md | 2 +- tests/test_rocket.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7372e91e0..4ab36ca79 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,7 +39,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Fixed -## [Unreleased] - 2024-02-22 +## [v1.2.1] - 2024-02-22 You can install this version by running `pip install rocketpy==1.2.1` diff --git a/tests/test_rocket.py b/tests/test_rocket.py index cd77f41a1..70da36e95 100644 --- a/tests/test_rocket.py +++ b/tests/test_rocket.py @@ -211,7 +211,7 @@ def test_air_brakes_clamp_off(mock_show, calisto_air_brakes_clamp_off): def test_add_surfaces_different_noses(calisto): """Test the add_surfaces method with different nose cone configurations. - More specifically, this will checks the static margin of the rocket with + More specifically, this will check the static margin of the rocket with different nose cone configurations. Parameters