From 7c801cea190596b20e0716cac3a5f9018ead5d7f Mon Sep 17 00:00:00 2001 From: Lucas Prates Date: Fri, 13 Sep 2024 17:15:14 -0300 Subject: [PATCH 01/10] MNT: refactoring coefficients argument on __init__ --- .../rocket/aero_surface/generic_surface.py | 124 +++--- .../aero_surface/linear_generic_surface.py | 357 +++++++++--------- 2 files changed, 250 insertions(+), 231 deletions(-) diff --git a/rocketpy/rocket/aero_surface/generic_surface.py b/rocketpy/rocket/aero_surface/generic_surface.py index ff86bd942..006c3dabe 100644 --- a/rocketpy/rocket/aero_surface/generic_surface.py +++ b/rocketpy/rocket/aero_surface/generic_surface.py @@ -17,15 +17,10 @@ def __init__( self, reference_area, reference_length, - cL=0, - cQ=0, - cD=0, - cm=0, - cn=0, - cl=0, + coefficients="all_null", center_of_pressure=(0, 0, 0), name="Generic Surface", - ): + ): # pylint: disable=invalid-name """Create a generic aerodynamic surface, defined by its aerodynamic coefficients. This surface is used to model any aerodynamic surface that does not fit the predefined classes. @@ -51,24 +46,27 @@ def __init__( reference_length : int, float Reference length of the aerodynamic surface. Has the unit of meters. Commonly defined as the rocket's diameter. - cL : str, callable, optional - Lift coefficient. Can be a path to a CSV file or a callable. - Default is 0. - cQ : str, callable, optional - Side force coefficient. Can be a path to a CSV file or a callable. - Default is 0. - cD : str, callable, optional - Drag coefficient. Can be a path to a CSV file or a callable. - Default is 0. - cm : str, callable, optional - Pitch moment coefficient. Can be a path to a CSV file or a callable. - Default is 0. - cn : str, callable, optional - Yaw moment coefficient. Can be a path to a CSV file or a callable. - Default is 0. - cl : str, callable, optional - Roll moment coefficient. Can be a path to a CSV file or a callable. - Default is 0. + coefficients: dict, optional + List of coefficients. Default is "all_null", which creates a + dict with every coefficient set to 0. The valid coefficients are:\n + cL: str, callable, optional + Lift coefficient. Can be a path to a CSV file or a callable. + Default is 0.\n + cQ: str, callable, optional + Side force coefficient. Can be a path to a CSV file or a callable. + Default is 0.\n + cD: str, callable, optional + Drag coefficient. Can be a path to a CSV file or a callable. + Default is 0.\n + cm: str, callable, optional + Pitch moment coefficient. Can be a path to a CSV file or a callable. + Default is 0.\n + cn: str, callable, optional + Yaw moment coefficient. Can be a path to a CSV file or a callable. + Default is 0.\n + cl: str, callable, optional + Roll moment coefficient. Can be a path to a CSV file or a callable. + Default is 0.\n center_of_pressure : tuple, list, optional Application point of the aerodynamic forces and moments. The center of pressure is defined in the local coordinate system of the @@ -76,6 +74,7 @@ def __init__( name : str, optional Name of the aerodynamic surface. Default is 'GenericSurface'. """ + self.reference_area = reference_area self.reference_length = reference_length self.center_of_pressure = center_of_pressure @@ -85,12 +84,55 @@ def __init__( self.cpz = center_of_pressure[2] self.name = name - self.cL = self._process_input(cL, "cL") - self.cD = self._process_input(cD, "cD") - self.cQ = self._process_input(cQ, "cQ") - self.cm = self._process_input(cm, "cm") - self.cn = self._process_input(cn, "cn") - self.cl = self._process_input(cl, "cl") + default_coefficients = self._get_default_coefficients() + coefficients = self._complete_coefficients(coefficients, default_coefficients) + for coeff, coeff_value in coefficients.items(): + value = self._process_input(coeff_value, coeff) + setattr(self, coeff, value) + + def _get_default_coefficients(self): + """Returns default coefficients + + Returns + ------- + default_coefficients: dict + Dictionary whose keys are the coefficients names and keys + are the default values. + """ + default_coefficients = { + "cL": 0, + "cQ": 0, + "cD": 0, + "cm": 0, + "cn": 0, + "cl": 0, + } + return default_coefficients + + def _complete_coefficients(self, input_coefficients, default_coefficients): + """Completes coefficients dictionary from user input in __init__ + + Parameters + ---------- + input_coefficients : str, dict + Coefficients dictionary passed by the user. If the user only specifies some + of the coefficients, the + + Returns + ------- + coefficients: dict + Coefficients dictionary used to setup coefficients attributes + """ + + if input_coefficients == "all_null": + coefficients = default_coefficients + else: # complete user dictionary with null values for the coefficients + coefficients = input_coefficients + for coeff, value in default_coefficients.items(): + if coeff not in coefficients.keys(): + coefficients[coeff] = value + + return coefficients def _compute_from_coefficients( self, @@ -169,12 +211,8 @@ def compute_forces_and_moments( stream_mach, rho, cp, + omega, reynolds, - omega1, - omega2, - omega3, - *args, - **kwargs, ): """Computes the forces and moments acting on the aerodynamic surface. Used in each time step of the simulation. This method is valid for @@ -194,8 +232,8 @@ def compute_forces_and_moments( Center of pressure coordinates in the body frame. reynolds : float Reynolds number. - omega1, omega2, omega3 : float - Angular velocities around the x, y, z axes. + omega: tuple of float + Tuple containing angular velocities around the x, y, z axes. Returns ------- @@ -218,9 +256,9 @@ def compute_forces_and_moments( beta, stream_mach, reynolds, - omega1, - omega2, - omega3, + omega[0], + omega[1], + omega[2], ) # Conversion from aerodynamic frame to body frame @@ -270,7 +308,7 @@ def _process_input(self, input_data, coeff_name): ) return input_data elif callable(input_data): - # Check if callable has 7 inputs (alpha, beta, mach, reynolds, pitch_rate, yaw_rate, roll_rate) + # Check if callable ha#s 7 inputs (alpha, beta, mach, reynolds, pitch_rate, yaw_rate, roll_rate) if input_data.__code__.co_argcount != 7: raise ValueError( f"{coeff_name} function must have 7 input arguments" @@ -320,7 +358,7 @@ def __load_csv(self, file_path, coeff_name): reader = csv.reader(file) header = next(reader) except (FileNotFoundError, IOError) as e: - raise ValueError(f"Error reading {coeff_name} CSV file: {e}") + raise ValueError(f"Error reading {coeff_name} CSV file: {e}") from e if not header: raise ValueError(f"Invalid or empty CSV file for {coeff_name}.") diff --git a/rocketpy/rocket/aero_surface/linear_generic_surface.py b/rocketpy/rocket/aero_surface/linear_generic_surface.py index 863f999d9..b1c56a511 100644 --- a/rocketpy/rocket/aero_surface/linear_generic_surface.py +++ b/rocketpy/rocket/aero_surface/linear_generic_surface.py @@ -11,45 +11,10 @@ def __init__( self, reference_area, reference_length, - cL_0=0, - cL_alpha=0, - cL_beta=0, - cL_p=0, - cL_q=0, - cL_r=0, - cQ_0=0, - cQ_alpha=0, - cQ_beta=0, - cQ_p=0, - cQ_q=0, - cQ_r=0, - cD_0=0, - cD_alpha=0, - cD_beta=0, - cD_p=0, - cD_q=0, - cD_r=0, - cm_0=0, - cm_alpha=0, - cm_beta=0, - cm_p=0, - cm_q=0, - cm_r=0, - cn_0=0, - cn_alpha=0, - cn_beta=0, - cn_p=0, - cn_q=0, - cn_r=0, - cl_0=0, - cl_alpha=0, - cl_beta=0, - cl_p=0, - cl_q=0, - cl_r=0, + coefficients="all_null", center_of_pressure=(0, 0, 0), name="Generic Linear Surface", - ): + ): # pylint: disable=invalid-name """Create a generic linear aerodynamic surface, defined by its aerodynamic coefficients derivatives. This surface is used to model any aerodynamic surface that does not fit the predefined classes. @@ -75,112 +40,115 @@ def __init__( reference_length : int, float Reference length of the aerodynamic surface. Has the unit of meters. Commonly defined as the rocket's diameter. - cL_0 : callable, str, optional - Coefficient of lift at zero angle of attack. Default is 0. - cL_alpha : callable, str, optional - Coefficient of lift derivative with respect to angle of attack. - Default is 0. - cL_beta : callable, str, optional - Coefficient of lift derivative with respect to sideslip angle. - Default is 0. - cL_p : callable, str, optional - Coefficient of lift derivative with respect to roll rate. - Default is 0. - cL_q : callable, str, optional - Coefficient of lift derivative with respect to pitch rate. - Default is 0. - cL_r : callable, str, optional - Coefficient of lift derivative with respect to yaw rate. - Default is 0. - cQ_0 : callable, str, optional - Coefficient of pitch moment at zero angle of attack. - Default is 0. - cQ_alpha : callable, str, optional - Coefficient of pitch moment derivative with respect to angle of - attack. Default is 0. - cQ_beta : callable, str, optional - Coefficient of pitch moment derivative with respect to sideslip - angle. Default is 0. - cQ_p : callable, str, optional - Coefficient of pitch moment derivative with respect to roll rate. - Default is 0. - cQ_q : callable, str, optional - Coefficient of pitch moment derivative with respect to pitch rate. - Default is 0. - cQ_r : callable, str, optional - Coefficient of pitch moment derivative with respect to yaw rate. - Default is 0. - cD_0 : callable, str, optional - Coefficient of drag at zero angle of attack. Default is 0. - cD_alpha : callable, str, optional - Coefficient of drag derivative with respect to angle of attack. - Default is 0. - cD_beta : callable, str, optional - Coefficient of drag derivative with respect to sideslip angle. - Default is 0. - cD_p : callable, str, optional - Coefficient of drag derivative with respect to roll rate. - Default is 0. - cD_q : callable, str, optional - Coefficient of drag derivative with respect to pitch rate. - Default is 0. - cD_r : callable, str, optional - Coefficient of drag derivative with respect to yaw rate. - Default is 0. - cm_0 : callable, str, optional - Coefficient of pitch moment at zero angle of attack. - Default is 0. - cm_alpha : callable, str, optional - Coefficient of pitch moment derivative with respect to angle of - attack. Default is 0. - cm_beta : callable, str, optional - Coefficient of pitch moment derivative with respect to sideslip - angle. Default is 0. - cm_p : callable, str, optional - Coefficient of pitch moment derivative with respect to roll rate. - Default is 0. - cm_q : callable, str, optional - Coefficient of pitch moment derivative with respect to pitch rate. - Default is 0. - cm_r : callable, str, optional - Coefficient of pitch moment derivative with respect to yaw rate. - Default is 0. - cn_0 : callable, str, optional - Coefficient of yaw moment at zero angle of attack. - Default is 0. - cn_alpha : callable, str, optional - Coefficient of yaw moment derivative with respect to angle of - attack. Default is 0. - cn_beta : callable, str, optional - Coefficient of yaw moment derivative with respect to sideslip angle. - Default is 0. - cn_p : callable, str, optional - Coefficient of yaw moment derivative with respect to roll rate. - Default is 0. - cn_q : callable, str, optional - Coefficient of yaw moment derivative with respect to pitch rate. - Default is 0. - cn_r : callable, str, optional - Coefficient of yaw moment derivative with respect to yaw rate. - Default is 0. - cl_0 : callable, str, optional - Coefficient of roll moment at zero angle of attack. - Default is 0. - cl_alpha : callable, str, optional - Coefficient of roll moment derivative with respect to angle of - attack. Default is 0. - cl_beta : callable, str, optional - Coefficient of roll moment derivative with respect to sideslip - angle. Default is 0. - cl_p : callable, str, optional - Coefficient of roll moment derivative with respect to roll rate. - Default is 0. - cl_q : callable, str, optional - Coefficient of roll moment derivative with respect to pitch rate. - Default is 0. - cl_r : callable, str, optional - Coefficient of roll moment derivative with respect to yaw rate. - Default is 0. + coefficients: dict, optional + List of coefficients. Default is "all_null", which creates a + dict with every coefficient set to 0. The valid coefficients are:\n + cL_0: callable, str, optional + Coefficient of lift at zero angle of attack. Default is 0.\n + cL_alpha: callable, str, optional + Coefficient of lift derivative with respect to angle of attack. + Default is 0.\n + cL_beta: callable, str, optional + Coefficient of lift derivative with respect to sideslip angle. + Default is 0.\n + cL_p: callable, str, optional + Coefficient of lift derivative with respect to roll rate. + Default is 0.\n + cL_q: callable, str, optional + Coefficient of lift derivative with respect to pitch rate. + Default is 0.\n + cL_r: callable, str, optional + Coefficient of lift derivative with respect to yaw rate. + Default is 0.\n + cQ_0: callable, str, optional + Coefficient of pitch moment at zero angle of attack. + Default is 0.\n + cQ_alpha: callable, str, optional + Coefficient of pitch moment derivative with respect to angle of + attack. Default is 0.\n + cQ_beta: callable, str, optional + Coefficient of pitch moment derivative with respect to sideslip + angle. Default is 0.\n + cQ_p: callable, str, optional + Coefficient of pitch moment derivative with respect to roll rate. + Default is 0.\n + cQ_q: callable, str, optional + Coefficient of pitch moment derivative with respect to pitch rate. + Default is 0.\n + cQ_r: callable, str, optional + Coefficient of pitch moment derivative with respect to yaw rate. + Default is 0.\n + cD_0: callable, str, optional + Coefficient of drag at zero angle of attack. Default is 0.\n + cD_alpha: callable, str, optional + Coefficient of drag derivative with respect to angle of attack. + Default is 0.\n + cD_beta: callable, str, optional + Coefficient of drag derivative with respect to sideslip angle. + Default is 0.\n + cD_p: callable, str, optional + Coefficient of drag derivative with respect to roll rate. + Default is 0.\n + cD_q: callable, str, optional + Coefficient of drag derivative with respect to pitch rate. + Default is 0.\n + cD_r: callable, str, optional + Coefficient of drag derivative with respect to yaw rate. + Default is 0.\n + cm_0: callable, str, optional + Coefficient of pitch moment at zero angle of attack. + Default is 0.\n + cm_alpha: callable, str, optional + Coefficient of pitch moment derivative with respect to angle of + attack. Default is 0.\n + cm_beta: callable, str, optional + Coefficient of pitch moment derivative with respect to sideslip + angle. Default is 0.\n + cm_p: callable, str, optional + Coefficient of pitch moment derivative with respect to roll rate. + Default is 0.\n + cm_q: callable, str, optional + Coefficient of pitch moment derivative with respect to pitch rate. + Default is 0.\n + cm_r: callable, str, optional + Coefficient of pitch moment derivative with respect to yaw rate. + Default is 0.\n + cn_0: callable, str, optional + Coefficient of yaw moment at zero angle of attack. + Default is 0.\n + cn_alpha: callable, str, optional + Coefficient of yaw moment derivative with respect to angle of + attack. Default is 0.\n + cn_beta: callable, str, optional + Coefficient of yaw moment derivative with respect to sideslip angle. + Default is 0.\n + cn_p: callable, str, optional + Coefficient of yaw moment derivative with respect to roll rate. + Default is 0.\n + cn_q: callable, str, optional + Coefficient of yaw moment derivative with respect to pitch rate. + Default is 0.\n + cn_r: callable, str, optional + Coefficient of yaw moment derivative with respect to yaw rate. + Default is 0.\n + cl_0: callable, str, optional + Coefficient of roll moment at zero angle of attack. + Default is 0.\n + cl_alpha: callable, str, optional + Coefficient of roll moment derivative with respect to angle of + attack. Default is 0.\n + cl_beta: callable, str, optional + Coefficient of roll moment derivative with respect to sideslip + angle. Default is 0.\n + cl_p: callable, str, optional + Coefficient of roll moment derivative with respect to roll rate. + Default is 0.\n + cl_q: callable, str, optional + Coefficient of roll moment derivative with respect to pitch rate. + Default is 0.\n + cl_r: callable, str, optional + Coefficient of roll moment derivative with respect to yaw rate. + Default is 0.\n center_of_pressure : tuple, optional Application point of the aerodynamic forces and moments. The center of pressure is defined in the local coordinate system of the @@ -188,54 +156,66 @@ def __init__( name : str Name of the aerodynamic surface. Default is 'GenericSurface'. """ - self.reference_area = reference_area - self.reference_length = reference_length - self.center_of_pressure = center_of_pressure - self.cp = center_of_pressure - self.cpx = center_of_pressure[0] - self.cpy = center_of_pressure[1] - self.cpz = center_of_pressure[2] - self.name = name - - self.cL_0 = self._process_input(cL_0, "cL_0") - self.cL_alpha = self._process_input(cL_alpha, "cL_alpha") - self.cL_beta = self._process_input(cL_beta, "cL_beta") - self.cL_p = self._process_input(cL_p, "cL_p") - self.cL_q = self._process_input(cL_q, "cL_q") - self.cL_r = self._process_input(cL_r, "cL_r") - self.cQ_0 = self._process_input(cQ_0, "cQ_0") - self.cQ_alpha = self._process_input(cQ_alpha, "cQ_alpha") - self.cQ_beta = self._process_input(cQ_beta, "cQ_beta") - self.cQ_p = self._process_input(cQ_p, "cQ_p") - self.cQ_q = self._process_input(cQ_q, "cQ_q") - self.cQ_r = self._process_input(cQ_r, "cQ_r") - self.cD_0 = self._process_input(cD_0, "cD_0") - self.cD_alpha = self._process_input(cD_alpha, "cD_alpha") - self.cD_beta = self._process_input(cD_beta, "cD_beta") - self.cD_p = self._process_input(cD_p, "cD_p") - self.cD_q = self._process_input(cD_q, "cD_q") - self.cD_r = self._process_input(cD_r, "cD_r") - self.cl_0 = self._process_input(cl_0, "cl_0") - self.cl_alpha = self._process_input(cl_alpha, "cl_alpha") - self.cl_beta = self._process_input(cl_beta, "cl_beta") - self.cl_p = self._process_input(cl_p, "cl_p") - self.cl_q = self._process_input(cl_q, "cl_q") - self.cl_r = self._process_input(cl_r, "cl_r") - self.cm_0 = self._process_input(cm_0, "cm_0") - self.cm_alpha = self._process_input(cm_alpha, "cm_alpha") - self.cm_beta = self._process_input(cm_beta, "cm_beta") - self.cm_p = self._process_input(cm_p, "cm_p") - self.cm_q = self._process_input(cm_q, "cm_q") - self.cm_r = self._process_input(cm_r, "cm_r") - self.cn_0 = self._process_input(cn_0, "cn_0") - self.cn_alpha = self._process_input(cn_alpha, "cn_alpha") - self.cn_beta = self._process_input(cn_beta, "cn_beta") - self.cn_p = self._process_input(cn_p, "cn_p") - self.cn_q = self._process_input(cn_q, "cn_q") - self.cn_r = self._process_input(cn_r, "cn_r") + + super().__init__( + reference_area=reference_area, + reference_length=reference_length, + coefficients=coefficients, + center_of_pressure=center_of_pressure, + name=name, + ) self.compute_all_coefficients() + def _get_default_coefficients(self): + """Returns default coefficients + + Returns + ------- + default_coefficients: dict + Dictionary whose keys are the coefficients names and keys + are the default values. + """ + default_coefficients = { + "cL_0": 0, + "cL_alpha": 0, + "cL_beta": 0, + "cL_p": 0, + "cL_q": 0, + "cL_r": 0, + "cQ_0": 0, + "cQ_alpha": 0, + "cQ_beta": 0, + "cQ_p": 0, + "cQ_q": 0, + "cQ_r": 0, + "cD_0": 0, + "cD_alpha": 0, + "cD_beta": 0, + "cD_p": 0, + "cD_q": 0, + "cD_r": 0, + "cm_0": 0, + "cm_alpha": 0, + "cm_beta": 0, + "cm_p": 0, + "cm_q": 0, + "cm_r": 0, + "cn_0": 0, + "cn_alpha": 0, + "cn_beta": 0, + "cn_p": 0, + "cn_q": 0, + "cn_r": 0, + "cl_0": 0, + "cl_alpha": 0, + "cl_beta": 0, + "cl_p": 0, + "cl_q": 0, + "cl_r": 0, + } + return default_coefficients + def compute_forcing_coefficient(self, c_0, c_alpha, c_beta): """Compute the forcing coefficient from the derivatives of the aerodynamic coefficients.""" @@ -297,6 +277,7 @@ def total_coefficient( def compute_all_coefficients(self): """Compute all the aerodynamic coefficients from the derivatives.""" + # pylint: disable=invalid-name self.cLf = self.compute_forcing_coefficient( self.cL_0, self.cL_alpha, self.cL_beta ) From 11e6777b40544789c0c0f23299ea4fd87b8a9a76 Mon Sep 17 00:00:00 2001 From: Lucas Prates Date: Fri, 13 Sep 2024 17:17:05 -0300 Subject: [PATCH 02/10] MNT: cleaning input arguments from compute_forces_and_moments method --- rocketpy/rocket/aero_surface/aero_surface.py | 3 +-- rocketpy/rocket/aero_surface/fins/fins.py | 10 +++------- rocketpy/simulation/flight.py | 5 ++--- 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/rocketpy/rocket/aero_surface/aero_surface.py b/rocketpy/rocket/aero_surface/aero_surface.py index 5b98f6260..15ca14f1d 100644 --- a/rocketpy/rocket/aero_surface/aero_surface.py +++ b/rocketpy/rocket/aero_surface/aero_surface.py @@ -99,8 +99,7 @@ def compute_forces_and_moments( rho, cp, *args, - **kwargs, - ): + ): # pylint: disable=unused-argument """Computes the forces and moments acting on the aerodynamic surface. Used in each time step of the simulation. This method is valid for the barrowman aerodynamic models. diff --git a/rocketpy/rocket/aero_surface/fins/fins.py b/rocketpy/rocket/aero_surface/fins/fins.py index b4aa2f790..7de9bd441 100644 --- a/rocketpy/rocket/aero_surface/fins/fins.py +++ b/rocketpy/rocket/aero_surface/fins/fins.py @@ -373,13 +373,9 @@ def compute_forces_and_moments( stream_mach, rho, cp, - _, - omega1, - omega2, - omega3, + omega, *args, - **kwargs, - ): + ): # pylint: disable=arguments-differ """Computes the forces and moments acting on the aerodynamic surface. Parameters @@ -408,7 +404,7 @@ def compute_forces_and_moments( * self.reference_area * (self.reference_length) ** 2 * cld_omega.get_value_opt(stream_mach) - * omega3 + * omega[2] # note to STANO: this was omega3 / 2 ) M3 = M3_forcing - M3_damping diff --git a/rocketpy/simulation/flight.py b/rocketpy/simulation/flight.py index e110fc4de..be66db879 100644 --- a/rocketpy/simulation/flight.py +++ b/rocketpy/simulation/flight.py @@ -1671,6 +1671,7 @@ def u_dot_generalized( v = Vector([vx, vy, vz]) # CDM velocity vector e = [e0, e1, e2, e3] # Euler parameters/quaternions w = Vector([omega1, omega2, omega3]) # Angular velocity vector + omega = [omega1, omega2, omega3] # Retrieve necessary quantities ## Rocket mass @@ -1766,10 +1767,8 @@ def u_dot_generalized( comp_stream_mach, rho, comp_cp, + omega, comp_reynolds, - omega1, - omega2, - omega3, ) R1 += X R2 += Y From 3b20605d3883a5ea90846c480669ce662213449f Mon Sep 17 00:00:00 2001 From: Lucas Prates Date: Fri, 13 Sep 2024 17:17:58 -0300 Subject: [PATCH 03/10] DOC: adapting notebook to the new signature of general_surfaces --- docs/notebooks/coeff_testing.ipynb | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/docs/notebooks/coeff_testing.ipynb b/docs/notebooks/coeff_testing.ipynb index 80ea83b55..5bd822164 100644 --- a/docs/notebooks/coeff_testing.ipynb +++ b/docs/notebooks/coeff_testing.ipynb @@ -458,25 +458,31 @@ "gennose = GenericSurface(\n", " reference_area=np.pi * calisto.radius**2,\n", " reference_length=2 * calisto.radius,\n", - " cL=\"nose_cL.csv\",\n", - " cQ=\"nose_cQ.csv\",\n", + " coefficients={\n", + " \"cL\": \"nose_cL.csv\",\n", + " \"cQ\": \"nose_cQ.csv\",\n", + " },\n", " center_of_pressure=(0, 0, 0),\n", " name=\"nose\",\n", ")\n", "genfin = GenericSurface(\n", " reference_area=np.pi * calisto.radius**2,\n", " reference_length=2 * calisto.radius,\n", - " cL=\"fins_cL.csv\",\n", - " cQ=\"fins_cQ.csv\",\n", - " cl=\"fins_roll.csv\",\n", + " coefficients={\n", + " \"cL\": \"fins_cL.csv\",\n", + " \"cQ\": \"fins_cQ.csv\",\n", + " \"cl\": \"fins_roll.csv\",\n", + " },\n", " center_of_pressure=(0, 0, 0),\n", " name=\"fins\",\n", ")\n", "gentail = GenericSurface(\n", " reference_area=np.pi * calisto.radius**2,\n", " reference_length=2 * calisto.radius,\n", - " cL=\"tail_cL.csv\",\n", - " cQ=\"tail_cQ.csv\",\n", + " coefficients={\n", + " \"cL\": \"tail_cL.csv\",\n", + " \"cQ\": \"tail_cQ.csv\",\n", + " },\n", " center_of_pressure=(0, 0, 0),\n", " name=\"tail\",\n", ")\n", @@ -2451,7 +2457,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.12.0" + "version": "3.11.2" }, "vscode": { "interpreter": { From 68073003ce2c9718dbc55dc145e4310c856f0e52 Mon Sep 17 00:00:00 2001 From: Lucas Prates Date: Fri, 13 Sep 2024 17:37:29 -0300 Subject: [PATCH 04/10] MNT: removing unnecessary pylint flags and fixing small errors --- rocketpy/rocket/aero_surface/fins/fins.py | 2 +- rocketpy/rocket/aero_surface/generic_surface.py | 4 ++-- rocketpy/rocket/aero_surface/linear_generic_surface.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/rocketpy/rocket/aero_surface/fins/fins.py b/rocketpy/rocket/aero_surface/fins/fins.py index 7de9bd441..867f106b0 100644 --- a/rocketpy/rocket/aero_surface/fins/fins.py +++ b/rocketpy/rocket/aero_surface/fins/fins.py @@ -404,7 +404,7 @@ def compute_forces_and_moments( * self.reference_area * (self.reference_length) ** 2 * cld_omega.get_value_opt(stream_mach) - * omega[2] # note to STANO: this was omega3 + * omega[2] / 2 ) M3 = M3_forcing - M3_damping diff --git a/rocketpy/rocket/aero_surface/generic_surface.py b/rocketpy/rocket/aero_surface/generic_surface.py index 006c3dabe..051cb5f28 100644 --- a/rocketpy/rocket/aero_surface/generic_surface.py +++ b/rocketpy/rocket/aero_surface/generic_surface.py @@ -20,7 +20,7 @@ def __init__( coefficients="all_null", center_of_pressure=(0, 0, 0), name="Generic Surface", - ): # pylint: disable=invalid-name + ): """Create a generic aerodynamic surface, defined by its aerodynamic coefficients. This surface is used to model any aerodynamic surface that does not fit the predefined classes. @@ -308,7 +308,7 @@ def _process_input(self, input_data, coeff_name): ) return input_data elif callable(input_data): - # Check if callable ha#s 7 inputs (alpha, beta, mach, reynolds, pitch_rate, yaw_rate, roll_rate) + # Check if callable has 7 inputs (alpha, beta, mach, reynolds, pitch_rate, yaw_rate, roll_rate) if input_data.__code__.co_argcount != 7: raise ValueError( f"{coeff_name} function must have 7 input arguments" diff --git a/rocketpy/rocket/aero_surface/linear_generic_surface.py b/rocketpy/rocket/aero_surface/linear_generic_surface.py index b1c56a511..1c2befd7b 100644 --- a/rocketpy/rocket/aero_surface/linear_generic_surface.py +++ b/rocketpy/rocket/aero_surface/linear_generic_surface.py @@ -14,7 +14,7 @@ def __init__( coefficients="all_null", center_of_pressure=(0, 0, 0), name="Generic Linear Surface", - ): # pylint: disable=invalid-name + ): """Create a generic linear aerodynamic surface, defined by its aerodynamic coefficients derivatives. This surface is used to model any aerodynamic surface that does not fit the predefined classes. From 2a700ca39bb4a635fc4f23f33155d16a127be051 Mon Sep 17 00:00:00 2001 From: MateusStano Date: Fri, 13 Sep 2024 17:38:13 +0200 Subject: [PATCH 05/10] DOC: add reference to docs --- docs/reference/classes/aero_surfaces/GenericSurface.rst | 5 +++++ .../reference/classes/aero_surfaces/LinearGenericSurface.rst | 5 +++++ docs/reference/classes/aero_surfaces/index.rst | 2 ++ 3 files changed, 12 insertions(+) create mode 100644 docs/reference/classes/aero_surfaces/GenericSurface.rst create mode 100644 docs/reference/classes/aero_surfaces/LinearGenericSurface.rst diff --git a/docs/reference/classes/aero_surfaces/GenericSurface.rst b/docs/reference/classes/aero_surfaces/GenericSurface.rst new file mode 100644 index 000000000..ed57c23af --- /dev/null +++ b/docs/reference/classes/aero_surfaces/GenericSurface.rst @@ -0,0 +1,5 @@ +Generic Surface Class +--------------------- + +.. autoclass:: rocketpy.GenericSurface + :members: \ No newline at end of file diff --git a/docs/reference/classes/aero_surfaces/LinearGenericSurface.rst b/docs/reference/classes/aero_surfaces/LinearGenericSurface.rst new file mode 100644 index 000000000..0c39469fb --- /dev/null +++ b/docs/reference/classes/aero_surfaces/LinearGenericSurface.rst @@ -0,0 +1,5 @@ +Linear Generic Surface Class +---------------------------- + +.. autoclass:: rocketpy.LinearGenericSurface + :members: \ No newline at end of file diff --git a/docs/reference/classes/aero_surfaces/index.rst b/docs/reference/classes/aero_surfaces/index.rst index 5cbb9eedb..c6e6a3efe 100644 --- a/docs/reference/classes/aero_surfaces/index.rst +++ b/docs/reference/classes/aero_surfaces/index.rst @@ -13,3 +13,5 @@ AeroSurface Classes EllipticalFins RailButtons AirBrakes + GenericSurface + LinearGenericSurface From 76e137cfc2285926024fc5ff6913fed2d3e5b40e Mon Sep 17 00:00:00 2001 From: MateusStano Date: Sat, 14 Sep 2024 13:27:44 +0200 Subject: [PATCH 06/10] DOC: add generic surface doc page --- docs/static/rocket/aeroframe.png | Bin 0 -> 40926 bytes docs/user/rocket/generic_surface.rst | 422 +++++++++++++++++++++++++++ docs/user/rocket/rocket.rst | 6 + tests/fixtures/motor/aeroframe.png | Bin 0 -> 32917 bytes 4 files changed, 428 insertions(+) create mode 100644 docs/static/rocket/aeroframe.png create mode 100644 docs/user/rocket/generic_surface.rst create mode 100644 tests/fixtures/motor/aeroframe.png diff --git a/docs/static/rocket/aeroframe.png b/docs/static/rocket/aeroframe.png new file mode 100644 index 0000000000000000000000000000000000000000..c48c86e1aa4c9cbc9285e113b4cfd27d73f6acb3 GIT binary patch literal 40926 zcmaHScQl)C_;+k#YtUt@2DRFvb||IxEH#VPp0!8ps8ONSmfEp*D6vPSRkio1S=1J^ zVvoF!@9%fs^ZxZZ$9YbY=eh6ezQ*U8Ppq!CDlPaX7z6^*K2=lJ2Z2bKKpU2~;_7djt4DYOkoJ2m)0nQlDFsfk4?_ zPn8u7{fTfF)E|siGIxU%Vpt(7+|kIJUx{O%wsYpZTo*i>Q$NY7&O3{WZ*8W)53q>G z<{#-%-xk>!P2jP#?4J@_rbGnc-)VCvc~g7|QZ4P1PnBQu_^T{iZ6u6(?_d}gZ)5fj zVicFFw%NB}aB$DAKW$5X&D3k);Nop{`Aq4T;@;9V$?P=(fqrZMIzS@``t8Qmp+G&# zNmmzLiHNJig?(Ya!}GeVD*xbi3vNm_6uKw3NhOssbthF zsTAz~U(J1gy&}g{TYn?3art%MpmJx&?M#jY#7m)?4$4QeT#gG1_`C|fI6WvVDe0EQ z4?Ql_+gJ$s{guc-Ff%jr4xE7ZoztoR{J@@x8^jq7(5t^vH{(_luV;bU+1jd!*KGgv ziRJ7xxE?oCpMFQ<6HQi0%ybD8BlbTg>KjTDqmp-2pKB1`;ZIKutVA5v;*yynbx zPx+B`vy3P8rNXZSU}vuXmSV`x;A}9bvVjQN{*kMfrwDbanSsBo0+RmE-dy0g+!3_p!<%N}ZL^A-G&W=R1=`E~|hDpgVoz6Z?I6s_mmjs2KUJ2-9@g~m%+DhSN$N5(h`qbKCIxRmxzx=Mq zrSfD-=NU6?coelJ_}c$-w>R^GNJTTFX%yPBb$WZgb;jG*H_c~j;+J%y)1{Cn`>oPC zd2DN_hDJOQXl$}dK9ML4GBkcvZQDf`ICqGf>7~0iov_D|Fq~_~aW_FRmjNM+hAXc8ZQ%WJb$Y#dpG>o}qM_*p`Zug_7 z^m3~r4xxr-9E)x4SM0ifCAcS;14dI$5%UHQUDxlWMrHSJaGys|`;W6abSYYtdD95E zh;c~ZMK%ZXGEONGlt*w4XL}*g&zn~gq%@29q#i%i=(R>yTCE#HX<0Ft`+HIg_Y4ze zoHOLf6$(dK7QJBZLMj&=FkV&~o@Uw09TUM!b$N(5nFZvm&=xU({g@?{uc^d4)H@6` zRiV>;dE#)S>leA^Rf1pp)5M*Jw&DP@mMZ4Maya4P2G+8moXdfSpd^?nzIl#BE_=6F zYwDxd9N~9INnimu*A?5=B%xpmx-nq{*jjK4<0VweDcz2Bqo;IW|yF$iV|7-o{9 z^u!+#ed9$b0Qo?Afbzk}jUJHQ28HPZG>^Sqm3NT;KHnNdyTnYxblaI2q?~Yw207U+ zViAqRFW4}{<{)N}7e>}pdgP(+_LL%eYkS)z)o~saHvK}C7}kGG5=s6EQ&%@_5m#8q zCqpIW&&>9b%K1z^1_Ug{Nt9z}*O$t88irY&ok-Y#q9=dvmOeNKrKCju=29#GfoBRQ3i+jJXT#v=V6B};bMH)dc> zsZkItVda*laSS!1ro({9R5gDb))xf&XMM#wqt}0)bbY2|JRwUI-*8p|oZ00W(kCncz|6T_L4jZ~N+xVC` zY@UUMuQq4C$~sbR^%{#l1adA(INKmFO3WyAD8;|NAiOutV>~w%0O0_b0)ZIr0v6sh zlxq>W5#jB<0l3lL)(jC%2S1zc_je8haOP0+<8P4>5iDa);hVlU;>d{nURg=PHQ}=c%)3H1T{(hXlh?f>_if5q2}wG z!G@|q5I%_MIkivy<98pHA77&LinWBV)c-%O++x;gZlXZMDB;vGImW4!c`7wd0{DlO z5spG*#%N2|M8-yyW_fp9R72*wz@fEV&c(^Oxy((hC*T%_E}3C-4a-lOA}n22!Gv2IGL~?S2LR2CVx4U1Ihy)0Xc=|X@QuY8XsfSp2g^j)i_-{ zkGErXq(HYcZ%iKKS}?*br4Rprs}8;(WrfTes>+|v7rdZk1Lb4^=ES7h^=WL{$Xnbh z{%;kJhIM9^_d=Cvoax9;q=N4HE4EO5p)SCTxCQ$ry6~fYfc-iluC`O*f(aQh-ySRI zR>%<6Y#c>zDP{wZeU6wKuS3$_S}*$jer#g~6t;a8gwTY>Knw1a8}#6}@;zkuE@7xS zR>|;Y!;VwRR`gZiiNOx@fns$XV;Cfdg#FK-Hs8>jykpqAApJ}c7OteIq?%>8py#wT z^}Cwy%9b%dkbp1@tTAP=x_UlRTv!p8Q=dB>`JeDo&MCRG+B#tNOB-i0emfv+b-J?2 zN^detiv(qHMqyn;_4t1;*7zjlWV#PXPNf}Ge{et{G^VDe(c2Qo?RLoNw)?|3LAn~Z zU5RxiE9(@!$=aqFxOSXErU*r!t`+qfX%`h;MRS2xxH<-IfEFMM)|Ko{5vh+4vIv+Q zKQ4~)aTy_~*4#cf$VvW52FB$SBb7w9D3gib`*J~`-fz1TuLLdU0qBNBLsQJ7M7BGg z0QnJ_SrPbME=_>sE8JbJ<(%+j!_j?jrJNYf0VpP;vB2rvA_J3E{Z@8fRRw`IujKBe zt(pem#)4z>K8g`(%YE#g&UIyAaeKB?PyqagWlu6 z?l_G(JK}!az(<3KruG~D;|3*qi%>+vbQLpuhV8`qX4sr-qqXtsb6*Y7^T=O3udkKDJMoa zvy~~iZbAWf0Vq&074C@xW0~OQLCYzc;3xXi;f<0t_eT8oi=5bRd{Kpg;_Q7AuoR?ieU;XGh}chv<#TG}0Xr zO5@?<2r_C-%sQSFR15*!lFbTvu}bxNg2qUZm2)`9wgB@uW3^3YR_$X&YY7l14d7_Y zYdKSRt>&~(@mp24TBjL#EE5|~(RriS{^w_x*^hW$=I3k4Xxn70a_bg(Tz_Ir8aA?x zrHLQ3dA2Un$EOPdMR5{wO1KeaM3)TbGN3YJe~*U2a{IrBixRo=mP?N7X=h7{7UAeusSeK0xA3o-nyjvt;sks_^&|hi z9JuMA;QF{a7Qgk|voxRW+SRch3In=j(m>{s78y0>b&5fXpOC~?p5Hy#dRlZB#0AE~ynuyZY`Clp)D`8z?k1E(V#V({by3)WXVCk0& z|Haoa2|!ND-aD*xKcREN`X95|n{j(mGuZsN$y0&(8DVo!1>Cl@9)gzdJnWawpZvbc93ahNyY^%6k1WR4wyRF`}lPw0_RH-CZ*!!@fJ59&7Z4ymYj0S;x?eW z4!D$+xfoGl*!s_8UpW2?_G3i4FFz#<csE-sz1D`Ss!=~;E+nuEz-OwVUYuzT53>r@;vf1yESv!H?f^&B z-2^;C-m+u|x|OB1!!%9_!VLkj!4;aDn`uus?5&8f@XUjn3btD~@US!s$SIzUic@9w zBxrK@NqirWeFu>$$7}W97}_^&X%=kQxv0%a@B-Ilhbm60sg~ygoY|fjbLdKoyOJ0$ zknO6)0x3dJ!L&u}II%&8H4qx2;)3fdX7ga@UKU`G@>VCtxF zjDgi0WbepeaT`Enyq7AVZN;<^q)8rNAbXPrGhU{|dy%G8Gs-oN!Qfn^l>DMa?^-u>~AAiL`(zg@dLZQDxF&=<$-{sjkGFz z;(B(IIdse9g02C!=xzc672gK@ z(b;MqMp`(%x#|Pl=9T^BDkg!LppSUCP1bZ{UnIo@{mmz2n^98S6_-rY2ed6RGvc}z zV{SpVDx2h$Nh(Kj67jxeNiDVBqZU>Yyl4o)Ox^KNj36`pyD`}j2=?V!dJJ$Yh8!R! zwaRAGiS^@wwCSILraG=f{mII<3kw{aJI@ zH!QtB6)vWAi0+w;Moi}bq<%mGq^eELhC?eg-;6`YHsky>G&FX+a-QW{wQWoyeNnnr zR~EN)wIFFC=SHdbZN=PAY~gr!4{F+w4C66Vys+-QgN>OYTPAJ>U!6VPN>fNp>i#!^Y^;KQ+Wr)Qc)xI#S`P!Yy^pM zr37J7Vg7|%Q%SdGszp_trkuQ!_7`00;%xin!k4sUlL{oG|oLGhh zpU$TDzu~xae>}f~{a&rWE^eQ7B30us%uE^=HLQ1+!0?n6I(C_giU56idy|TjYiWJt z+HCAlE??ZD??(?I2^IU^nuo?pi;4sZGDR|8ivnT%?k$OJ+5KOGA-`n*{ZvRLY;8)d zDg-Dlwd`M3w31!a>(Hlv>NaahJaY9qKMsAMaQTBM{r=4Bx zR(jeW+V5PpdmZT|QIgB4AlzG9#?L84T!Qh#61Sk2$6X4kTIrTG;Wix+zWZ2eRhrE~ zGI0I!6F_^Ps{#ti>py#U0sA=KL`c{BiOd4ryB153z*twj3ir8=Yih7H+BE%8hm`yn z{bo2X<#Ts(_FmiF#Lq}1ML+zgUn1{GrY(|QvqpYWa+GKLQ94%H%qpAqTIl(*-mcg6 zS1dxO5uYFZC9d&WV|)-A(oD+?W-V5jAaBC}M@$&A~<0&uabJ2s;)`wmPp<4TMEqg<2 zh&S>gvUm&c;e|u3&;KIZc7xXn+Oo2)J(fRwx6*rdnCTe}FFo5_^|!Y9+12*7S?&*< zkoovc;8|AiKV!JD#X;We*-MwMkjS=_Q@KN|3I{0vM&|?!yIxul zdbh(WRn~dpo=kzM$X20XR%zs3ee8JeW#${3Z-bkiKA!4JYsPLJwZ5il>F#3fXLD

ug|0JwuLwJmhd%9u3#*m+%DLFzjG`Y+ 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 ScncZ3cfX;jK@PSAQsR7 literal 0 HcmV?d00001 diff --git a/docs/user/rocket/generic_surface.rst b/docs/user/rocket/generic_surface.rst new file mode 100644 index 000000000..007fc5ec9 --- /dev/null +++ b/docs/user/rocket/generic_surface.rst @@ -0,0 +1,422 @@ +.. _genericsurfaces: + +Generic Surfaces and Custom Aerodynamic Coefficients +==================================================== + +Generic aerodynamic surfaces can be used to model aerodynamic forces based on +force and moment coefficients. The :class:`rocketpy.GenericSurface` class is a +parent class for all generic surfaces. The :class:`rocketpy.LinearGenericSurface` +class is a child class of :class:`rocketpy.GenericSurface` and is used to model +aerodynamic forces based on linear force and moment coefficients. + +Both classes base their coeffcient on the definition of the aerodynamic frame +of reference. + +Aerodynamic Frame +----------------- + +The aerodynamic frame of reference of the rocket is defined as follows: + +- The origin is at the rocket's center of dry mass (``center_of_dry_mass_position``). +- The ``z`` axis is defined along the rocket's centerline, pointing from the center of dry mass towards the nose. +- The ``x`` and ``y`` axes are perpendicular. +- The partial angle of attack (``alpha``) is defined as the angle, in the y-z + plane, from the velocity vector to the z axis. +- The partial side slip angle (``beta``) is defined as the angle, in the x-z + plane, from the velocity vector to the z axis. + +The following figure shows the aerodynamic frame of reference: + +.. figure:: ../../static/rocket/aeroframe.png + :align: center + :alt: Aerodynamic frame of reference + +In the figure we define: + +- :math:`\mathbf{\vec{V}}` as rocket velocity vector. +- :math:`x_B`, :math:`y_B`, and :math:`z_B` as the body axes. +- :math:`x_A`, :math:`y_A`, and :math:`z_A` as the aerodynamic axes. +- :math:`\alpha` as the partial angle of attack. +- :math:`\beta` as the side slip angle. +- :math:`L` as the lift force. +- :math:`D` as the drag force. +- :math:`Q` as the side force. + +Here we define the aerodynamic forces in the aerodynamic frame of reference as: + +.. math:: + \vec{\mathbf{F}}_A=\begin{bmatrix}X_A\\Y_A\\Z_A\end{bmatrix}_A=\begin{bmatrix}Q\\-L\\-D\end{bmatrix}_A + +The aerodynamic forces in the body axes coordinate system are defined as +:math:`\vec{\mathbf{F}}_B`. + +.. math:: + \vec{\mathbf{F}}_B=\begin{bmatrix}X_A\\Y_A\\Z_A\end{bmatrix}_B=\mathbf{M}_{BA}\cdot\begin{bmatrix}Q\\-L\\-D\end{bmatrix}_A + +Where the transformation matrix :math:`\mathbf{M}_{BA}`, which transforms the +aerodynamic forces from the aerodynamic frame of reference to the body axes +coordinate system, is defined as: + +.. math:: + \mathbf{M}_{BA} = \begin{bmatrix} + 1 & 0 & 0 \\ + 0 & \cos(\alpha) & -\sin(\alpha) \\ + 0 & \sin(\alpha) & \cos(\alpha) + \end{bmatrix} + \begin{bmatrix} + \cos(\beta) & 0 & -\sin(\beta) \\ + 0 & 1 & 0 \\ + \sin(\beta) & 0 & \cos(\beta) + \end{bmatrix} + + +The forces coefficients can finally be defined as: + +- :math:`C_L` as the lift coefficient. +- :math:`C_Q` as the side force coefficient (or cross stream force coefficient). +- :math:`C_D` as the drag coefficient. + +And the forces from the coefficients are defined as: + +.. math:: + \begin{bmatrix}X_A\\Y_A\\Z_A\end{bmatrix}_B =\mathbf{M}_{BA}\cdot\overline{q}\cdot A_{ref}\cdot\begin{bmatrix}C_Q\\-C_L\\-C_D\end{bmatrix}_A + +Where: + +- :math:`\bar{q}` is the dynamic pressure. +- :math:`A_{ref}` is the reference area used to calculate the coefficients. + +The moment coefficients can be defined as: + +- :math:`C_l` as the rolling moment coefficient. +- :math:`C_m` as the pitching moment coefficient. +- :math:`C_n` as the yawing moment coefficient. + +And the moments from the coefficients are defined as: + +.. math:: + \vec{\mathbf{M}}_B=\begin{bmatrix}M_{x_A}\\M_{y_A}\\M_{z_A}\end{bmatrix}_B =\overline{q}\cdot A_{ref}\cdot L_{ref}\cdot\begin{bmatrix}C_m\\C_n\\C_l\end{bmatrix} + +Where: + +- :math:`L_{ref}` is the reference length used to calculate the coefficients. + + +Aerodynamic angles +~~~~~~~~~~~~~~~~~~ + +There aerodynamic angles are defined in two different ways in RocketPy: + +- As the angle of attack (:math:`\alpha`) and the side slip + angle (:math:`\beta`), which are defined in the image above. These are used + in the calculation of the generic surface forces and moments. +- As the total angle of attack (:math:`\alpha_{\text{tot}}`), defined as the + angle between the total velocity vector and the rocket's centerline. This is + used in the calculation of the standard aerodynamic surface forces and moments. + +The partial angles are calculated as: + +.. math:: + \begin{aligned} + \alpha &= \arctan\left(\frac{V_y}{V_z}\right) \\ + \beta &= \arctan\left(\frac{V_x}{V_z}\right) + \end{aligned} + +The total angle of attack is calculated as: + +.. math:: + \alpha_{\text{tot}} = \arccos\left(\frac{\mathbf{\vec{V}}\cdot\mathbf{z_B}}{||\mathbf{\vec{V}}||\cdot||\mathbf{z_B}||}\right) + +.. note:: + When the simulation is done, the total angle of attack is accessed through + the :attr:`rocketpy.Flight.angle_of_attack` attribute. + The partial angles of attack and side slip are accessed through the + :attr:`rocketpy.Flight.partial_angle_of_attack` and + :attr:`rocketpy.Flight.angle_of_sideslip` attributes, respectively. + +.. _genericsurface: + +Generic Surface Class +--------------------- + +The :class:`rocketpy.GenericSurface` class is used to define a aerodynamic +surface based on force and moment coefficients. A generic surface is defined +as follows: + +.. seealso:: + For more information on class initialization, see + :class:`rocketpy.GeericSurface.__init__` + + +.. code-block:: python + + from rocketpy import GenericSurface + generic_surface = GenericSurface( + reference_area=np.pi * 0.0635**2, + reference_length=2 * 0.0635, + coeffcients={ + "cL": "cL.csv", + "cQ": "cQ.csv", + "cD": "cD.csv", + "cm": "cm.csv", + "cn": "cn.csv", + "cl": "cl.csv", + }, + name="Generic Surface", + ) + +The ``coeffcients`` argument is a dictionary containing the coefficients of the +generic surface. The keys of the dictionary are the coefficient names, and the +values are the coefficients. The possible coefficient names are: + +- ``cL``: Lift coefficient. +- ``cQ``: Side force coefficient. +- ``cD``: Drag coefficient. +- ``cm``: Pitching moment coefficient. +- ``cn``: Yawing moment coefficient. +- ``cl``: Rolling moment coefficient. + +Only one of the coefficients is required to be provided, but any combination of +the coefficients can be used. The coefficient values can be provided as a +single value, a callable function of seven arguments, or a path to a ``.csv`` +file containing the values. + +The coefficients are all functions of: + +- Angle of attack (:math:`\alpha`) in radians. +- Side slip angle (:math:`\beta`) in radians. +- Mach number (:math:`Ma`). +- Reynolds number (:math:`Re`). +- Pitch rate (:math:`q`) in radians per second. +- Yaw rate (:math:`r`) in radians per second. +- Roll rate (:math:`p`) in radians per second. + +.. math:: + \begin{aligned} + C_L &= f(\alpha, \beta, Ma, Re, q, r, p) \\ + C_Q &= f(\alpha, \beta, Ma, Re, q, r, p) \\ + C_D &= f(\alpha, \beta, Ma, Re, q, r, p) \\ + C_m &= f(\alpha, \beta, Ma, Re, q, r, p) \\ + C_n &= f(\alpha, \beta, Ma, Re, q, r, p) \\ + C_l &= f(\alpha, \beta, Ma, Re, q, r, p) + \end{aligned} + +From the coefficients, the forces and moments are calculated with + +.. math:: + \begin{aligned} + L &= \overline{q}\cdot A_{ref}\cdot C_L \\ + Q &= \overline{q}\cdot A_{ref}\cdot C_Q \\ + D &= \overline{q}\cdot A_{ref}\cdot C_D \\ + M_{m} &= \overline{q}\cdot A_{ref}\cdot L_{ref}\cdot C_m \\ + M_{n} &= \overline{q}\cdot A_{ref}\cdot L_{ref}\cdot C_n \\ + M_{l} &= \overline{q}\cdot A_{ref}\cdot L_{ref}\cdot C_l + \end{aligned} + +These coefficients can be defined as a callable such as: + +.. code-block:: python + + def coefficient(alpha, beta, Ma, Re, q, r, p): + ... + return value + +In which any algorithm can be implemented to calculate the coefficient values. + +Otherwise, the coefficients can be defined as a ``.csv`` file. The file must +contain a header with at least one of the following columns representin the +independent variables: + +- ``alpha``: Angle of attack. +- ``beta``: Side slip angle. +- ``mach``: Mach number. +- ``reynolds``: Reynolds number. +- ``q``: Pitch rate. +- ``r``: Yaw rate. +- ``p``: Roll rate. + +The last column must be the coefficient value, and must contain a header, +though the header name can be anything. + +.. important:: + Not all columns need to be present in the file, but the columns that are + present must be named, **and ordered**, as described above. + +An example of a ``.csv`` file is shown below: + +.. code-block:: + + "alpha", "mach", "coefficient" + -0.017, 0, -0.11 + -0.017, 1, -0.127 + -0.017, 2, -0.084 + -0.017, 3, -0.061 + 0.0, 0, 0.0 + 0.0, 1, 0.0 + 0.0, 2, 0.0 + 0.0, 3, 0.0 + 0.017, 0, 0.11 + 0.017, 1, 0.127 + 0.017, 2, 0.084 + 0.017, 3, 0.061 + +After the definition of the ``GenericSurface`` object, it must be added to the +rocket's configuration: + +.. seealso:: + For more information on how to add a generic surface to the rocket, see + :class:`rocketpy.Rocket.add_generic_surface` + +.. code-block:: python + :emphasize-lines: 5 + + from rocketpy import Rocket + rocket = Rocket( + ... + ) + rocket.add_generic_surface(generic_surface, position=(0,0,0)) + +The position of the generic surface is defined in the User Defined coordinate +System, see :ref:`rocketaxes` for more information. + +.. tip:: + If defining the coeffcients of the entire rocket is desired, only a single + generic surface can be added to the rocket, positioned at the center of dry + mass. This will be equivalent to defining the coefficients of the entire + rocket. + +.. attention:: + If there generic is positioned **not** at the center of dry mass, the + forces generated by the force coefficients (cL, cQ, cD) will generate a + moment around the center of dry mass. This moment will be calculated and + added to the moment generated by the moment coefficients (cm, cn, cl). + + +.. _lineargenericsurface: + +Linear Generic Surface Class +---------------------------- + +The :class:`rocketpy.LinearGenericSurface` class is used to define a aerodynamic +surface based on the forces and moments coeffcient derivatives. A linear generic +surface will receive the derivatives of each coefficient with respect to the +independent variables. The derivatives are defined as: + +- :math:`C_{\alpha}=\frac{dC}{d\alpha}`: Coefficient derivative with respect to angle of attack. +- :math:`C_{\beta}=\frac{dC}{d\beta}`: Coefficient derivative with respect to side slip angle. +- :math:`C_{Ma}=\frac{dC}{dMa}`: Coefficient derivative with respect to Mach number. +- :math:`C_{Re}=\frac{dC}{dRe}`: Coefficient derivative with respect to Reynolds number. +- :math:`C_{q}=\frac{dC}{dq}`: Coefficient derivative with respect to pitch rate. +- :math:`C_{r}=\frac{dC}{dr}`: Coefficient derivative with respect to yaw rate. +- :math:`C_{p}=\frac{dC}{dp}`: Coefficient derivative with respect to roll rate. + +A non derivative coefficient :math:`C_{0}` is also included. + +Each coefficient derivative is defined as a function of all the seven +independent variables. + +The coefficients are then grouped into **forcing** coefficients: + +.. math:: + \begin{aligned} + C_{Lf} &= C_{L0} + C_{L\alpha}\cdot\alpha + C_{L\beta}\cdot\beta + C_{LMa}\cdot Ma + C_{LRe}\cdot Re \\ + C_{Qf} &= C_{Q0} + C_{Q\alpha}\cdot\alpha + C_{Q\beta}\cdot\beta + C_{QMa}\cdot Ma + C_{QRe}\cdot Re \\ + C_{Df} &= C_{D0} + C_{D\alpha}\cdot\alpha + C_{D\beta}\cdot\beta + C_{DMa}\cdot Ma + C_{DRe}\cdot Re \\ + C_{mf} &= C_{m0} + C_{m\alpha}\cdot\alpha + C_{m\beta}\cdot\beta + C_{mMa}\cdot Ma + C_{mRe}\cdot Re \\ + C_{nf} &= C_{n0} + C_{n\alpha}\cdot\alpha + C_{n\beta}\cdot\beta + C_{nMa}\cdot Ma + C_{nRe}\cdot Re \\ + C_{lf} &= C_{l0} + C_{l\alpha}\cdot\alpha + C_{l\beta}\cdot\beta + C_{lMa}\cdot Ma + C_{lRe}\cdot Re + \end{aligned} + +And **damping** coefficients: + +.. math:: + \begin{aligned} + C_{Ld} &= C_{L_{q}}\cdot q + C_{L_{r}}\cdot r + C_{L_{p}}\cdot p \\ + C_{Qd} &= C_{Q_{q}}\cdot q + C_{Q_{r}}\cdot r + C_{Q_{p}}\cdot p \\ + C_{Dd} &= C_{D_{q}}\cdot q + C_{D_{r}}\cdot r + C_{D_{p}}\cdot p \\ + C_{md} &= C_{m_{q}}\cdot q + C_{m_{r}}\cdot r + C_{m_{p}}\cdot p \\ + C_{nd} &= C_{n_{q}}\cdot q + C_{n_{r}}\cdot r + C_{n_{p}}\cdot p \\ + C_{ld} &= C_{l_{q}}\cdot q + C_{l_{r}}\cdot r + C_{l_{p}}\cdot p + \end{aligned} + +The forces and moments are then calculated as: + +.. math:: + \begin{aligned} + L &= \overline{q}\cdot A_{ref}\cdot C_{Lf} + \overline{q}\cdot A_{ref}\cdot \frac{L_{ref}}{2V} C_{Ld} \\ + Q &= \overline{q}\cdot A_{ref}\cdot C_{Qf} + \overline{q}\cdot A_{ref}\cdot \frac{L_{ref}}{2V} C_{Qd} \\ + D &= \overline{q}\cdot A_{ref}\cdot C_{Df} + \overline{q}\cdot A_{ref}\cdot \frac{L_{ref}}{2V} C_{Dd} \\ + M_{m} &= \overline{q}\cdot A_{ref}\cdot L_{ref}\cdot C_{mf} + \overline{q}\cdot A_{ref}\cdot L_{ref}\cdot \frac{L_{ref}}{2V} C_{md} \\ + M_{n} &= \overline{q}\cdot A_{ref}\cdot L_{ref}\cdot C_{nf} + \overline{q}\cdot A_{ref}\cdot L_{ref}\cdot \frac{L_{ref}}{2V} C_{nd} \\ + M_{l} &= \overline{q}\cdot A_{ref}\cdot L_{ref}\cdot C_{lf} + \overline{q}\cdot A_{ref}\cdot L_{ref}\cdot \frac{L_{ref}}{2V} C_{ld} + \end{aligned} + +The linear generic surface is defined very similarly to the generic surface. +The coefficients are defined in the same way, but with the addition of the +derivative values. + +An example of a linear generic surface defined with **all** the coefficients is +shown below: + +.. seealso:: + For more information on class initialization, see + :class:`rocketpy.LinearGenericSurface.__init__` + +.. code-block:: python + + from rocketpy import LinearGenericSurface + linear_generic_surface = LinearGenericSurface( + reference_area=np.pi * 0.0635**2, + reference_length=2 * 0.0635, + coeffcients={ + "cL_0": "cL_0.csv", + "cL_alpha": "cL_alpha.csv", + "cL_beta": "cL_beta.csv", + "cL_Ma": "cL_Ma.csv", + "cL_Re": "cL_Re.csv", + "cL_q": "cL_q.csv", + "cL_r": "cL_r.csv", + "cL_p": "cL_p.csv", + "cQ_0": "cQ_0.csv", + "cQ_alpha": "cQ_alpha.csv", + "cQ_beta": "cQ_beta.csv", + "cQ_Ma": "cQ_Ma.csv", + "cQ_Re": "cQ_Re.csv", + "cQ_q": "cQ_q.csv", + "cQ_r": "cQ_r.csv", + "cQ_p": "cQ_p.csv", + "cD_0": "cD_0.csv", + "cD_alpha": "cD_alpha.csv", + "cD_beta": "cD_beta.csv", + "cD_Ma": "cD_Ma.csv", + "cD_Re": "cD_Re.csv", + "cD_q": "cD_q.csv", + "cD_r": "cD_r.csv", + "cD_p": "cD_p.csv", + "cm_0": "cm_0.csv", + "cm_alpha": "cm_alpha.csv", + "cm_beta": "cm_beta.csv", + "cm_Ma": "cm_Ma.csv", + "cm_Re": "cm_Re.csv", + "cm_q": "cm_q.csv", + "cm_r": "cm_r.csv", + "cm_p": "cm_p.csv", + "cn_0": "cn_0.csv", + "cn_alpha": "cn_alpha.csv", + "cn_beta": "cn_beta.csv", + "cn_Ma": "cn_Ma.csv", + "cn_Re": "cn_Re.csv", + "cn_q": "cn_q.csv", + "cn_r": "cn_r.csv", + "cn_p": "cn_p.csv", + "cl_0": "cl_0.csv", + "cl_alpha": "cl_alpha.csv", + "cl_beta": "cl_beta.csv", + "cl_Ma": "cl_Ma.csv", + "cl_Re": "cl_Re.csv", + "cl_q": "cl_q.csv", + "cl_r": "cl_r.csv", + "cl_p": "cl_p.csv", + }, + ) + diff --git a/docs/user/rocket/rocket.rst b/docs/user/rocket/rocket.rst index 17ec63c1a..38b4ce4e6 100644 --- a/docs/user/rocket/rocket.rst +++ b/docs/user/rocket/rocket.rst @@ -12,4 +12,10 @@ Rocket Usage :caption: Rocket Class Axes Definitions Rocket Class Axes Definitions + +.. toctree:: + :maxdepth: 3 + :caption: Generic Surfaces and Custom Aerodynamic Coefficients + + Generic Surfaces and Custom Aerodynamic Coefficients \ No newline at end of file diff --git a/tests/fixtures/motor/aeroframe.png b/tests/fixtures/motor/aeroframe.png new file mode 100644 index 0000000000000000000000000000000000000000..33868d1e1be794a325d1111173ae9de0315a29c2 GIT binary patch literal 32917 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

|Pn?L>k;qj}Jyq#1+nf;fg zr&pceL%$fo{Pc<>)0bF#mS0A)4Ty9%#p@2|lXF8b|dyV{_YN{uo9PoF;4$cp?P4(&^vZxWoOnr-bo9ltkLa+xF; z0t{o~4K9dqeg45-eWS)#JV}1?QaQ2aX$j?sD8|gona&Z2ey6~Hin0nWTW1F;wQuN@ z%m?^B9v2C?O!a$`8zm=#cvdw1Hw@btt!WVa_20`SDC{QFMeZ=X4InM|v$zYc=v@_y zSw`NTT~gS+f5FIx?YVg^zpnZm9Z_W8!gS?Lt?eQ+pxiizkkQX7`Nj$WJHPkFh~sDVg?t?p zuEZD9k}Wzuwcnsal@w%zD^sRofbi4sonpGVs)A{~XRySIr=09+s|fqS4KUHY!fz)A z_`o>6$EMmCIv$_~ za~fXIF`3QqI4h8*|If%usf2RB?vlVsuuxl6WXu?umk~6w& zlOK6EK+Aw$^zQQ^lYU2ReG`vU~=rudi{6j&ztdGQ*d;P~+B~P!6s5S9q93wQAIPeSz)-z4* z-V2-E5e+qmpSN9YrTf^Q_AfpxR8s~9Zf7#|{CD1I4>3|1C!E*T#$1LzJv*(RhTeG8 zNV<0yeVOKqAc&012G=}rotBY)wzmrnhhB8N-3-=7Ft8x3Z5f9<~Kr1$npuFBo<3d4I6 z9s9* z$5+G+mzbB|tTfK>a9;t69uRYmA&QxI53OoQ<3~2M8|pf@?VFo!=K@d%GFC-8Q~1A8$KNeZ|5Pmeo_)F(mq}X zs!mxY%_o{ml%wH!-0HD_*+c9I^!vw%TSsAIm^|~jJ2^2x@5LEj8=%en6&xjNRHWM- zz~f?&RT5c<;C&^pJVzo$#(OlGsHSU5V&E~Uoye7ZL$Q$J(d=J zDdta5F7iqgP;U59XoV~cPn$XZ4kkc$^E-Gr| z&m?ABJM{5sUPoYZr)hH{;)CFMpWP0F*xZh2C{fx&%V-7fg{_^d8zYn(YW3{R=wRtmD|^2;YGj zUZ$`~wrFPeE&GU7g%K9Whwqq{7cuz4i#6}e&v-O8npm+nTq=L~3c@K(8z|NI%ormL zv}OdPc}X-%0s#GWVmx$~>`REXH9@?kDu|RwK8bEwKsZU6PrS=saP>6)f6FPiYPx%~ zMpzzyyR@!p<6C65Gkr=r*F~-GT|2YBeZVMZ_AR;L1ZQAXx%ybmv=jG2k5wL*?3>i+ zw0#W{)wI-(A9G0do_2n`=Tn=0-hXA16gM670{7*ua6*2_ig-g1$v-HeAo2f)K@1hv zhd+20{{R(%X*nX}tB(1mKmC5@zdfulbP6x9@UG-sS33BWUXDZ#H#RqO&zE~8tKF>$ zTQ0&p9CM$L>(%n~oOnGb>C`uwwY-0^vS52| z$wcuyRpWNm$CXk@@i&VDJ9_CiX9fbe0AsrVgu#@G%nrfJo10Tk5f!$q;T6!a#P~qV z65+Sk%~~?iY;p))FVXvGOL2q6g8>VES5v!l-HbdRxS|>bErl#*b^T&jtG(2!IbO*t zO~l!e05S4Jp#Y#*z=)W4JV|ke!GUF zmf@8=YjNKDsw2jTJ;~;x4Ge8rP?&3}ddLH)z6MZz95EE&IpcbAW}2=f(4>T;U8oOolI)X);Lc?) z^4a0z1{fa-H7pRQHy~w7mI?q4&QoMTx_%yGAoSK(9Odm9uuvt6*AWAr8L?MZDKRgu zsY_rz&mL=^WLkAy-!)LtR`LwN^M%+8XH85(sBKmC84qnno)T7ha~{hcfm}^=&2RgX zrH3gLas9&rBPNQHFxtW z#NT9$3Vf}7`Ap%JyoU0*aPGNHSEWxp{}IdiZgeMAPR!aP!SMz_i=&z^iNGunaNS

#C0HB|(SjsQs!LZ)pj`4~_t|Chn;LMA`%#Qaa+ruQwld zzFc(qBi@`Xt(77d;tynjQ;;|3gLH&*lV`}c4zJ$>V#6DA5iT2OC$56<&QXp_-z{h(8!P`FAJ(#XOLTa0NgOu*zoe-x0}o=R)ve9uNaJT<6LyLQ6X++FOUrp z4Zv|I`C&sJXXTH;v6w66NDEttnl4Z*XtIr0NzBAdtEbJDaw|`wp;$!s-iI5B1m2+n zj$;0>!m-zDDhzMliiY$@6FT3q@OnO+ejov{g%x%luQ$jY;uSNWluJFEq6{VY0q|$8py|xQ+ZmsNXa9!GYVOhF3re7Y z>`;=%F2ZCJKuPl7ztS&Fy`B@M25pu+`gV zWdt)(ZT$dw!zXNltGECeo?`p*Z*RBI1yv#TYUAdMVS73GKU02;Y4b}8yoh-Jn&F-! zczl(SrqryvQInn!vMoHdB@;-njP_L6?~m||SWx-NF&nBHhp zkoSBeuYl&ZtX?%*BvOAeJjrq|OUVXovIpdM}4Za@D}Et@~!fsNKOB2~PsNf41G z?<3YL_B#}Q+9wn>`*lUJDg<*KoML-&w^@;+PBw41;2rRa(!;!-1w($Eb2A0596@?O z#ksxDDUZ6sDta#G&ri_|LIK~joz8vESzgnVqB|v`sJ4&u5#}dK;WjHjS?-;K2VBeB zc@sk^Bl{n%7MgIMS?eBolCiF%5R^S~<$F(GaX#8u-7;o{0`I(`drRi;3gDiMVa-h*54+!H3qY|?^Hm89ON~P4m2t65&9k<5)eTlwzDRz zoCMtOV}qS9J8yV7iEhT}n)=-tM8N;qEU~uV`ueJk>8fvQ{JYO(s-lO#P^@62`cae>C%|w|5Zp~EDIN%@AB>4c z5u0~4TEaqs6k{us67?x`jLmItCA?E@40CslJYdaoE`NTR;k zV<}Lc6MRbeV4kv*3;1isQVqU6!#v(1o*%j&O2KD0cu79(Eq}f*Hcp+I^!u@zm3&;# z?p7R=8+&DbGMh1n>t8psT~o8ag&pke+@&_ZW#zVEV=F##D8HLlOL%4uSDN#t^1s?8 zdsySi>#B*Oye$V!df9u)gpZ>FLcF7gZ07~TTf6XTxmPt_Px2<+i&i-Y0g@6j`VA%I zLV1Fak3>jJly+t2r&PraWr;!w{qGZ;Da#ltrC1o3`v6Cd9vaR zNu1$pJGHORU2Zh_SkPr3dg*;8TTCwiKWK|R3XDg^X20S8gXW(PCgKuMdHamKSo74Z z=p+Z@5(?LCw~-^ba`V5Bne1Q6RvzGtyOk!8@_6)zfIN@#fPJ=MY!?frJHR@w=AYIp zNPEl~sOH$y81!LPti4UU<052Sp*vl3iD@#&&TBwF^$n07@u@4cLHWR0mf(unkxOt> zPqAV0-Ac&0mpML)@0+4tH_Wg_$9Q}~7T`Vlha+fP{5aj&)Xu7B1C^TIee)WLT$3$%v#n#JFJuM;GnUbriQ zGN`l>y@UK5QqK=cqe2hnP* zH_kFkP^)a2eOfW2?-gJ=GussnYc9x1M=m@gySu(Uk)Cf2tRi6;pqy59$}jikftybn{!K26djefj?rt$Lz8AzLyTSI2)ixqp8#^dk^^*)cKdUh{=X_} z-%~&L^d(6(bYOn`vN0{}pzi*ms;Nqy*1$@iso#FeiGS}&I76Jy0OP@9p>5{r#GgfN ziQP5K3s|GgCgsOhpDwoMY!1RyLds{xeLFkW%f*I2honn2NX;SZ@tbd0E0W7^z2WKQ zs-_ww7W)Q|1!Fwe3)rGkU&_GWOoCCtHf`c>RwXQrc6=)lV_`n9N%Jbb7EkO;Y3~iw zbc+$i?aZ_Meb!-l`C=Pt}Xlk-0M(6To_wc=sd_M=e#cQ*wg|u0v;kNaiggA&9KQ&|BTi;eT zU32GwOpbwdmG%P)|55$RoFDJ^zFh?D{RqIk1hA`yAr5m7pk~-B9!bzJ1+?*4dQ?7f zl_d@;B3lAss|sS}gC;Bedo(uqITULq_o(TbivG#=Wc@BB$N`eEuG!5*TXhLbD6%Xh9@$Tx?EZUr0J>1FuE`o(TPx4X$U3-6d~!+fkXhEei-gA230 zk|1WY;HA)Wu$qOtvvxLwt4~f+mw(q{Y_t7I3Qg|mAx^#VX<6TRW8KMmRX*~wD@83W zCDFVtWF%g}sWmM#)z827?#0X@R?$fz19K6hs$eq*2nU>ObO^vkYJu!#2M>K$%^${S z&2r7X-2N`;``KAQX))OrwtFV!_FB=AuWZXi4~24xjH58}adynTT)vAsyXNtzB|6ao z8Biywp8+ME8Qp9x*=m3O%uXOE$JF1F^NopdFGOwA^ox?`p0?ISk(Xtxd!MHSvdu!0 zC_-h?FTez3=j-*K3`z(oWUqLZ;(lR^B09Qw6_MFnk1n&+`?@dqD5b5YmgU!0GHm6b zR@vT`l8Y>_*iQYH1_z@;;lZ27(*FH?4NO>g{ERL_0fqEsR2j4mM5hg)P9(vf5sq!) z9_=;uzbRg@@Q40m#ShKQ@;N7|ICj{vqL=6Ghg9s|LWEaQ6w+LAPFcc+f<`)4<>YaN z&#;+!OcW#dJqntHMM0uWaaL;IMW3r`n0e6$z^v<$3E-UYH!2woaYILUkvN{YF<*Rv zKsk!uI9Qpdp59peD&mm>0eKwV@?`i~^HB3eH2}iqcn9;4%l$5EHRL0jed~w*8K_Lu zVe?Z2ie=qc56u;813(I1FTt3_jCCL(HrQ!%8pINHDM)H<`W`SUL`Nd|J(YAzB9YLh zX^kHob#t4M;deZpHL;?XgGf!uEyGUUe-q}9fyybGt7pyY_}T@peT6zV<3%bkk4El$D`+im-3u;Nh2q{ zbh}cap?6*!gXP1eJ}GKWsXfE zcM9!SYoj@?f2`G4;whrVDLVgotyav!7e~Bvoe)S}{HsyYmKf1uQUu{pB$txcGpv=d42|`2kXm z9jP@)KpEM4WF#g1j-lKA^-bm6F-&o6A`+=!KxtW9Widr8^&}9aUy=}23@8^kZ1EzS zvfCjWmg`60T;MwrZ|lbu2saTE#K;w>dh5|)lY#n#l+=dRg4ENZxMW$iha~=r_BbZ; zNv-lavrkLeRT5uu_*!rTCH?_*CViJ&w8|n~QjvU*`~76`i_gTm%aZONE59uA<*kga z*u+MsK(89>y;a79Hm7$MXoxBmj19^~mNt%?SC7PJ4Eq!shSGDK;3*Ee zfZ2qIUk#)5OLBQ=RPG!GIGsR9_DGz$>nARM4pYe1b_d7LZ=o5B@LAcb<4#eJD?M>w4SWto-=q--ob1g2Lm5=MJrt zYQt!^AN^HcGoFTx=I=a8^R-sn8=MMQy1|_mvu)R#L8l@g8R>Cpoq}AXhek^_}X;GbWbyCX|j zkm{1Z>=)-D!c}L6onXR1RD8s|J#Uvyl~CZ25{LmCt_G&bSu{4!@(!Z5)1jq&&Pu#Z zS8-pvJY-zp1?diAz#}#+&0%^>oja*7y;98#a#7`_&&exu@TsHyehC)K4>erxv`%JVO}60NXse(p(WWO}ovao&nI_yAf>%n$g8!VweTxT*k(%k*OWKAF3u2^Oi6UqV z-nzMREsOKDH{q*qkG0m$4*?^HJN1zUSRSk!v@Xc3$b`p6GQRlfI1qBkW^fHF_Hp)d zSeW;mb8>LIQ(yS8)b={YR%VSF7gZQ`7cM;btZIc(Eg0e62#-Y0Zzj|hJkxqDN{HQ* zURqREn&>|Vm5JWisv0I12NH)St-K|Hr#<*+8aYayS4`1WCJ+^(j+BQMUj)#gO#x!9Q6K7ZM6)Sczm0r%f{;u&kV=Ymw&N~?%D+$=wseMNxXWBiYFgG(^ zdjiMhr{`9$&a#w7BIVuzuW$f*j3>)|8;cWY+IFBmlu{m!2$FVAun`_mSU7IVjqYAYbnMiR{-~@T8IS4qlR4LjshQi0&!oBY6kIy3 zr~F5#7D3+b4X&BAODk(0ToEOb{jDKU_VR9hs1*~B2u%_=!dOj|f?3dl&^ zja3J!8N=|_H))cuRV;}aorz)qw`v?J-zH^FV5DB?L9b(l|{DJ*K<&n{7#yVB9Xn0 zWP4sAQOTW|P<@fBgr`SyD%A84V(syO@(@BU;c8D@u0z3bz@n7A;Qo9BwYDiwgOM?h zErKlOzmhGv>r}k9+&I)_toJSZIDHyT6D>%%-hXaz2ofPJTstw!Tn?J;N^%NIKtMYtq3qn3P_@TEOJNW5HRURf46LBCC(W}JY2dhyqB zhP5~=?%ZWZTX6Wo#KzzK<&<1T{uiD6mb1J^2E>hxCV}Naw<@ z63%-)A*x=Et<^7Z@yVWz|LQdVYQyJw>ENliDat%k3Vf))#&+5X&t!yV#H;*vWU=>@ zT-JZQ_EOW2A{Ll0{F?~lo@sX9$qUVm*#0Btvi4?ig{>wRveRjQfdhWe`{?ObzheRH zX-!9H;s>eUt}AO}%_R2&^SQgP1AdU+drLIZH@!>9i{U2*ydktae*U`gdoDb#xxIak zp_y7l>fP8X$>&%pC+|`H>*}Tak_rCl&vqI{HyPP8Iw@_MhSy^{UGbno`tY%=R zRHZ;+jp{@FML$lCzWHY(^cn6M-Xgt|J{^X2TC}E6fT!5}K>T=NP zL)&;hQ#@?ea|ZW}Kg2oz!@W(D=oR-M^sf71alU+%wgo3TB_#H$@v~`qujbU(Kq0a2 zGuGsFD5OmuE{JgNy$J6+lafz#EnZ*&YcLH4)R`y59W!-o4rlGQ^on!OKiBi_ZuI|5 z#H;mEt{y(G?3M7IwVm}CPPl3$G|0LDYkqJgMhx|C{d}#KRb63^vJ3z%MMemSq?Y@V zp17HJn#)>AY)U+N^@s~-ru!Zh)f4j+&ED!ZZ_6-X(SwLY{)@Ad?7ppwvX7f>mqaY~lrB@|O+ zzKnYsEPQ9pxaP^ZRNz1PMpLTHsIo&Id_WZ6*=UDn8oXEser zDDxq!eqpY$8i`xKl`U7_Mfe)W#}d=y!t_^jhMQPQH>DCt+tA8Q&*2;!U`LsKK^=sz6T@oWkL(Nz%vBz_#&+j>&_kI6<|8gAd@frEt_jTRZIM4GkUx%OcDJcvS zEKL+VmY6%-_DO+?x>oqWf5b0o9B~v&b-I{svWcT+XQ-jGMr@86Mlxs=oEV8 z$c-LQAlhwB99N*0$^FNOJy!$*`(ImBbzcNOUd->*FeyHsDYA=aJz$N|ncfZK;6u|e zw!X5VQ4?UU70+Xw^c5F0i5Y(v} z?%lo<%>lSLONWIKh1s_k#AU)I?*A+E^pzvd)oyoPd%XN~{vtpto+OMuzHatqXF4tw zL9Z`qNHJkt(`;-(usF6KM+jC?@`TU?Chl*CU3u~KO_z1|0H*Z#mHfVGZHD3JhC8 zut!(1(>s5^PnlpNPSe(Y=4@}H_q<{jCEix@GOk|5O!R(@ciX>Me3)qj@GgJzHY?bS z$9foqP)7R9?GSge7+%YlKaKEHJjrzrpgcH=EAG*_fNVPI#*2sWR&NX=FGml(vy10x zz5hsSPSmy0dhk?wMWeF4;o~(ylA1DNNYb-Uxnh?w@#AkoieBKRfoE__g~KVrXucap zlIJr`1|C2wjaA~40zjsC6R-GLmT81owIVmQKWRbxLPt7dxj%b~Vc0$~Re{oygORN> z$9rRm_&lQfNrKo#5&e+=3vhp8t}2_vZDNDX8U6p05~Y@*;i+*=`{wVY!@X4i$Qw~OQs?btM_0rui;~ei4tc^Snqs$C zzr0tZa=P?i-=)h9{jWb-k6B>Ykh=$g5>BpoRfpignUW zWBQ*@J^1cfL-1{$?MeoRcvy}Wg@u1F#cw{NgV78N6ObRN!gP80WZn$f)NW(*TT&Qw zDf%p@Xb4ua_2iJ+%A{FY3*SWLcQa1I;Py$6I7bHX8E56G;EB4WYmB@z`ANtxD+&w1 z#zdLXgZ$=GCgFoiKe3BLi||ce%EWlk1}iJVLye?_U86>#fIF$)4Xy& zUZ*KcHVJ&-7*g>V)E(?T-c*g3@yqkH1zpXCpxSe$P|@3^53{Y0jZ>UXealV#x5k74 zCQ@2Nd_1MaNREfT>dBO){I3;&v?*dAE`HJ*|4SVwKpN!Pkba3cZ(JB7K&uPKknQaC7qz}5o~F87mTYv z!%q!^oArHfRW9)CvGbgEs}4Hs=4kDPO?~;Uk~p2X)TQwiD{DyBNkeTv_-N)T>zrch zTVHSSV}SEx(PN3?UD;Ht2~C4Ae=5fBaNDv=@JZI~ah?qjB_nz*OTPlfU!4r@ehrtb zEMe^f3$cfEWsAj^*k<|3_VQudc)1|DH6B2Gzt;NxDU@c5zrBw(ESJ}b+bq0L*8 zJKTDj+sBGb4iAh3V*;m!0+KmdZ+-+7!|{R9U2Kp!9XsefOXCw?llKqj86AyHmFS&;HT(w92+dk z^u<8X*aKodPSr8fGX@4YpBkt>9O$Lppy zSqb?44k;V`&NgiDL+rId2(ErZzqm`yl4!1#-83fP6z-j|`CtL*ff3{h@c=TsJsTn6351LCN*80hwgjH$_8s@kLl7}n|)!Af{ZX)R9eOp&08>kdva|9my)lNRcUsvP z<@ZU^GI%jsfk9qOU1!uoWJf9GPMa|;E!bN{E- z>2fP=wDUU;9ak%kAb&KXMjZrf&&3xHM#iH{n(|6V2l%~C3-ahF;+E?4gVq`=dZ>gr zy3b}|t+-}Gg*AoOR_1gQaf7~%)*ySg++aJQcA#=n#JVv23g!mTzj7>OpFogIHjn28 zoegW|tZX)}SvkFp@&7E7p#hpsPfm>-5$;SFoP3Og8yJpT#hS1eqlbJFS8CrT8z7G+ zx3|tg7S(V(PXv*$7@J;RDM|pBpE%n!6Z!u7{S9Q(BeS+T!*p!sS|zEB zi_fAstbQ64?x8IePOBj^EZa@*UL29|MuYY&(nN{&1l-Kl&(iks?Xtu}kU}4+WSyn( zIeEO)YwYA1CYPhf`Fg-%_afApX#tNm)w&Wa`5Q!c zf^rg04AVYB1lU{y`VqBrgp^Dvm$cOKfa-f^!evCDILQK73vQt|>gX8O2uH#%h4g-s zFZhrrW4sT53s4wx;Q0I@is zj)%(=PMK`a!`8zN9+h&bnxWIsbio*QEUXBgM7XLhw`My0irl89fNViuQME!J`>%}6 z0Xl%oHKv|~%Mp>MV*I#F3un^kA$H`Q zwS+sGGR>B`$P6V*(wpL5DQ$G8M^)o10`qy8ek|=`zwP`Hc3HzURr_mSIs>%KKJKCL zmSUOm9^bH1df;~>Zr8|0xd*!1PW z4|1WxOzz;+kYbuH|6y)^dh`2&l+%}3B9mc}MYU^&a_2&o+|se76Y0%E&;s=;)z94n=yJhFil`DE>dMS= zwXzq#(T*gmD2yOw&+@whBN5qlcGi(owVDdvx9^EOHJ{$wD;(uL{e*RddPSbh+?B26 zGBKeOPQRg~xlm%U0kS9*+~^UD}C=` z4^2AIDe{rFTQg+8*vop9#R_n15R5v>f3)0|Zjyi^ncN!LdBc{NDgA}4;GEg=5VUa} zVF<%auCIntC_ToDphyXB;N@U|gy%ww6du$Z-9-yJFE1)Rqt+y-RS&<7GVX4PBRjO8 zitlLD;*9whR*R6Y%e55Jui4him=>@+(U>-=rDp82w=p81fg6wYqV9@^eKv`s^8m^# z(n7Jq_?G?w8LZt@TF-6VUS#dkzGx39i>K;g(*z@zTsQ6$Wdl+__eN~hs#8*hU#3=i z(wK@}*dE`|1`MYh08(sNTvc(D7`_W41oEQ1W5}th=W8Gk_xlrlbyLM$T#xuFR7?Ko z_q(lHS#erStedj2C~%}ml^WaRbF!}QQ~lT8syhhCcn7ZkbsOYAfuLU&?rXB1QrX%_ z9__M=qTTxO!N^b=rPt@P;y>6_EV8}bv@JDw)t*N>iuUgqu5VPZwB&igjQvWHv4Lt* zd-qQApzUg{eU-~Hqhu1R*k_d&-JgKz%R5bL(pBD!avH64d`xl?Hxsp5O0q<|Gx8SF zS92^?6TCN{x!EhBRF%;~+8BK?(5)6Qd<73ZDkVqm_UKub(U_{p`k=mx*j`q?rZeR; zvG*ouk)v!2SEzCsuw)pi#LDiqHdGOm0)>d5>xizxK z%pa}|&yL%I`d*;^RmSxJY4$lIx}f_oY$Cmnx+x4a5G*V~sQ8_ZVh~5WSrhX$aPl`; zspraD>Ld>vRiD)F3UVktmkX|jnJrb}l_e#YexKS`ay>zgyk!jUJi$&hEOdfbAuP+b2BHRhKif&&DvtXzwPoay{78}|#w zSCDJxW19qXOyoDEJ$`Gm^*LY)^|DRaEU6DPt=s%Vqr2^or2DEpL^};KS`RmlvuaWq zh0y}v&zo((0+=0qS>W38!u19vkEO`YuARmq|0hAiet9i-ii#`UXZox|HZ!d<*pwD9uciVTM1LUyEjDo?cId<_3Xc_aT!OZ49Kn`KJN zznUJjkzTK3mcsNlsVCjCysxBbZ#pOWRN~Kc<^8wn$FoC4+^oKtv~?m=5EY6Gk`FoE+kQfrR_f`MAYDxw%_2_X`g|692!YL|trdflf zup4T`S#py~Hm9@L7X9gZFi8cii);Y|#*HjhUI%Z%6EGRxlcBlT7f7Zg&lh;SSTY4&$B+Y(j_2Jim@(rh(q}(!MOW* ze%4yT3u?Kh5f;n6_sm|189Qb6t=6;(HG*x-F6OhlH|#U!CdMnb%5p>#{Jko`kZ6ff zZQsi+rp&pT#N<{RmEXCWYdhrHqG0#GChJ`>nLiCti=kkT7O>ku+U%YW4 zZc%w=*l96!ra2SLLshx*W#8l0{!JrcW>iLwfk-iYuNHNx;W1EeQpBtBF%8dpd3jZu zm2&x%4#dw6dg9t^ESxL3!o6t^B$s(rkCgTU*AJGME?#tHdju zM`yNN6n%~svzT_;<5ertUyj%HZM_f)>bwj?>oJBL^qp$?`sBxV+|Vq=eYQ%xuCJ&|3-Cm7t{f*H-9(8SlwMd?ynu3$Ne? z`Yr)GVmi$_p7dq`w`=?I`*PLhkqVf;>}s;-)p6Jlev4w<-uV5&+pz-~=XCW398?Cn zr0lxyPINm-Gc+sEFf|)lPH?;7_lsX7L^0#zwi0F4M16tu1~*esueclerhnRvnYCq- zwt;FTswaQJsV3IjHz0S$=*N6tre>nd&3j@(QM>rpqZ$lt0}*7SCUYcV198P`h!>R) zn|orU&d6I)Ud92`)2?VXkAgk*n-!KrZK%OZp`kLDE(V@E_E`EL&W*~i*tkoYwLTnE z(7Cqf&qP+^s?}G%aFRpA)VZ7c?^Ba+{4I2htSJcOg|W#=*N95iZhX#2N+ZIcOrVqB z+3T_T=E)Oya^TFmwYnikFeyrcD4Uphd=PU^zPx06^f_MN)->551%3;jMVR$UMG(^K zm#Ufy+Lv}+F8TX`RC8s1nC318A?3mLgkqs>DGGHF>O>(?9x{hDb;am#tO*WPz~pO| zZ8HA;Ej-=cJb@M`aK|lLSAHbL5}fKqE9!drSMLs2_zm=w!5_rfSwvrR)9(4oP(F9q z7_hQ&y(+BcO-kicoGg9+`ri)-T2}znZET?Rt_@zkx|(JE32GQWAuP!{H|DkJ`IESrvjrIdNlv>FIhx$&NX?il|0EYNy=2QFA|6dO`% zFs#hTyAf@bA$3G(0~zkK{(UHlNGz$AK#P6T<)JAr=(o&h)@Rvid0O=|3Fk0yVc+aS zpFdq0swho=`tnDlk?A`vL{T1UiZ_4JDi`a-Dm%y4tYH76r{u+&=gzPEAA;t3p!TTO!{u zbmp8nWRpv@x^eoZc;K&6v(lgY=H8F9iwB%4ph${0XDRdqv<9zXzC?cE<#pB&;E#tf zSUsOo1LcuUP!CMlImG9OQ@{}lwU~nlmoI#qY@9P>7YCs}ds#c*6S*7HJ~f6laCT2d zF7%V5{}_XGfs&*(HVN~Bfx>(or!UH6vyGZzm3=8E`>3&30Nn9Cx#XjmsfEZU%;|+} zGtU#v5ShQvKuJAf5?=g4{dN~j3i>Tgw{c>L`>0UJ>iELRa2Ji2DfG>Q5~m2TIE4}8 zwHASkiS7aNrM6vNhkdFjO|-`fYFN;GHD7&)kdvyonqnJzeQ8mZ?+&=L5aQlzTCbrn zO?okdI~4Wu)^+4KX@{@xhPeCX0#cgOL{~RZ)MN&FgryOPym$VIqgt|{LVWuF%dCY zz*OKw^J@RAHJ|jJM@w#E8n)JR<%@xGW~B`ysg40S%CX=fy)`|PflaQ%-n>6PkoJGF z>76HWVGC?q^0z-c|FJr88SPc*OKM5@^>Fo46i14KIfC&IQI!*U@ay+;?V+m;c>!p& zyTZ<~8_8r|=;Scv)Uj(!IPP+lP!P~3PP(RiILOdz%X88rZ@g34$I2jj{3jpk&IhL` z$0!8^hp3oA@xe9k&ofXtzeyUo@@iPq=$CjV_1u3wE;EJY%` zxklU$znzIB^IX&S!H!yQVMd*gy#9WPX?QKrY_)fPk1GHC@<>vOL8%_3rR6$15M!H zI=3Y2Kbu5d25-G0^yCcdFyr%@W-CehCGMuWfrg(|779Bc0KKE@jL?A_MzP~L*7#|E2iwpU zR-g2zGg_%#@E$qPV>flqQt|v&nj?swVi8+#$2-N(IypH3g)s0{y4|2;5{G9<*!4J* z|0@2OF-FKywbVF)d3dQ&rOxs3gJ?1}#f_F-i#MW+9X%sZfv8=-gmF57%`Q6T|#Y2z!XYOH@W?d~vj7?7wa8EyLOSB7OaH5X;UGYvgnGyhLXEa8%2oB)2^R0FiI_^2PP ze`CaEOCB7L!g7C_lks$Mvpb}{^$DMOS+iv3q1=K5Id@N9Uipy`sz`F`uQ?;?ibGim zRQKqY?-oRb!&~;6*0^eAvCe?v9JOpTJCs~>>C94zoy^U@_ZbJwYz^4R(aG(yLRt-hL~r3WTBhMMfoJG75NV-9)qzF_}v0iu#&i=Lkd~HXUNLZmljpM?bE=iyjYq@SidP z@~971+?Q|7^7|)%#a%N*!5UQn4E!EidQ*Q`eMJz#Lb9^40m8aM_yd_ajkwnr>5_tG znkZuXW{l^i7A%F{75t!p=7;Rvmx`7fhjTe4~-6Ja^BcLUk&PV(cO6|;&qrDHs!GZL7Eo96(fqPuvlL^>IhW(mrx zmRk=(D)7`6`6vHEBKC%nnyTkF$&I*QD}5o}xh#KsvZ|6Y6n(}*S6FYAmzBMszBSC) z=JSSM6NF)q_8j0Hxg9o#(G|d>eY_Z+`c<&hwAH9#)``GJFd!&;me3PR-$PWYu~lsR zFO>c>L}}~r*sC1I->JG1FjDizu4Q_(N^k!+$#viRK~PA1-WFRFI6Mi7V5i_F$!jGA zEGHavT{kV1NhTkx=uH&`)~@ui`G*v#VSb@e9?F}Psiot(cSqB8x7P&scf0l^hFyXi zZ89A%(<3mNz-FP?(Gg>+fb5W;2-1*D>Qo)A!yh24lV#bnqH5hRD`;G#6Xjm!Np*As z6_bZ@%ca8|Q|>_5kc;Ov8cPO?V}V%H$W?!!K;?lNhEA_v2sIq7_gM>#x9VfiTPwDF zE*C}Ppo?H0@MET!)YHY=>wYrcngO)~K@~%{mAz(GQ+{~L$0mU#bT{Z`K}}P(_(K{? zs`ned&s>-aDOcTmU{dgU!77*a#zUYB|Fu~QR+(&T<)^#Nb=WOIRiD?pt%Ei_qAtW`V-Z@}{xq{16~&VQOv6->GoV`0A|~_{ ztDHMh!)v1bNG>3QTo3g+Ns!Vq*>VjN=a$p_DRFhM5hM=O- z4qaZbCb(gpb4~E{DPF93AC7hs%rsaY?6ExT>f~a#EIW1H-4p5wYcsbs+bH&S_?z6k zk}_wbXBlE+2hRa*Y!PR2mOP%Ec>3^#p$1Sr9yktM&#GPhawz9%!(K`Zj}}rr`jbdR z^tCk$W|KlBf2cH|LJ!kHud9`kI?p0#$a23P6m0GGx5Pc$pxkMIGI|d3;c*S&Z0|N` z7#!3mTioRR9QgCoE%z3c!^DZS1NWBh8n^r3``oA95*u|L^XL)}-tGPxarZo$O&-aY z^dO{cEU%0#3XO~YV&j#%@EAQ1%_ivDv>qifl7wlU>r>e;UR+ox^+6O=cz*pCG~DHY zWmWRoO@lCFBd)s?o&|^ffJJUb-%IbV^ti(h{Or&tTK^XRoS}y+R27rEL8EeJX;gDyC2oAs4eR zB@!$Z9}iz$G&pnnQ(V)?a6%3dTm9d{zUv7P%i@>dtf1=;_OPN$x}l5jDj!qQYy!ZpzV;Yt zJY3Tbh?DUpJTl(u=dIRiDNl(vbMiSLfA5&mCIbCmR-kdUC zDn}k46NIXEn8c$HlZR_V&=koSD=H*OQzqN!Xsfuikcn?XS$@C!*x}HNbJYO&M4!j4 zxb&yWIhwpNS9SKIOW(Lqt?JLwWaHNE`dMw+4@@JV3wrx2gNGJrFJ~=yxM{rD4HFT^ zvwMZ9C*RdxT6XCB4<#qgf6rW_Y#SwBziC|}Aa2zM0w+a10zaq!5t=Q1-{6rSf*j#hq;VqB4_peY8rwrExoy+)owP`gddfAiTgm1sL zYx!MzZAt48$RACH#7$)q^)t>;KTDQI+|N#gHz%!Z()0g){F3(Kp`y!O@u!CsUIPXQ z|9*Lh+khBui3sugc^YGFa! zzyloWX%C~h^aK^{dQ)Y4KFPS}+>7w1t(q)U`Qh3klO!T;M&2rLS{;8G31V`WQ2e0Z zC0X$cnbhlgb?M}*zvGv#;!ZfjiXU!14}A8HCrO-X*v1HhFs>my)g0FA3Ht`BN(}v8 zSe87F4fM=^tRt-v!{%RC%_?lw$kFK!(7U)*TgYNI{Y{0hNVg!sZs}%ZWIZdy3R++N zhI_3bgt+&56rIdG!qfw*($Zgoa|YqaZH&BwD9l82Mk2cZzcgl*kaN8F(D&|ASnnjD zeuGihdmzeF4wQF83q06bm-?ss2Xb}cBVvwm_C%KiAUjwEbq1!f%$$4a#N8`VPP!L< zo(_s~5*bEvgK-XzDbjB7e*YZ+oN8&S;Y2XrlnKlkpps64Dqp+B-vVibH0bLv7d7VMC7FkMp%w{=+Xae5Z(K+`Rn1g{N`iN5ipVD zbf_1|M631cF#euxOmv3fRxDZ9c<`2oabHFenWAcmaVNXyb&pbljtDjqoZ@m+lJUir z@!CP1&q{+$N(#O)eiL=xRed~a{vhW7@PtSw!^nU47_h{&#=73@xHN1D`&+*1fkIr- zg_3tSj`ciAQODm8R;E;(TpKBHI-wx58h9f36d$PjBz8tm#WAyE)Di=oNl?6dc5eF- z*%ls8ceU*$`H1c{({S`!T^FPc?752%@ra%nWuQ6bN!8O4SKepkvcNv``Yd~i;OW4j z=F~6G&W&Z83X&u7+yM^YVlOW2lcIgXt2jH&`mKcfU=)aq#X;K>28c+u8-_BkUZZsb z_@q~!E<)~GMwu$XIqT&BmE#E<-I@ok56jT+?=`YV<-1v4HWLQw(P}wEv_BW(FQ9Af7dd(&x9~joEa$wG8wP)|VCiY6?rG+sz;Ha?@m)klcdu zFg*}(wpqR1w9t+Y64>VP#Cg|DB3xiYY3|zDCg^S`vZThxo#f$~Un?>k9p0}?&#RWt ziS3y1D-7CQ0g5UevMx6v)EAkYsvlNK^d#nZ0)(%f@A3OYa6XLI5ZDEzye zPYWEP%7_h2ZM`H=PyG}v-4isS()u-oY^@e=GBB09kw;%YjeVK=6ttLMhNWIMa8?DZ z@}yhD?ys*j5d`YQoEST?sC($opPL4PbG2@~I?>6?jfLsx&!)LeC{SN(U~tkkbI@{P zex$6)N*REd7}SognxT`_YFx#rJHV~+<@_UHP#35^E`wFQ#+4uZGfSZ?JB4?tKgZ6P zPEE9bWg_orARv-VPy_DVjT)Y$l9{sbHQ+2KikqSPLj$Rneapp^6HGa&yugdi^bL2L zpasw8xgVmGa*{R+p8<_Ta047}q+5m_^#N^<&O#PI;Uvv7K9D+i1G&Aok9MS^2@NBe zL`BOP6-#!qfz;+@4hxtW@wb&SpqqYjSRQ<+eo4XQDNAFUUQKq$PD~4K4)~Vb7D@@c zM7C3fdWAb4p)LbCT|m@|pMG2u?9G%Nxq?u~&OvBVRO;D;^1~rsn$u@YjRHAZaC&C?=9Ro!FlfdZy36jDF^_S)JTPg?D z(LmnI`H$Xg@oooZC#|U$P#^R#L1Opsy;9p5=Dvni<0ZT&P} zKm6&2e`N;0`Qm{?P_fEoajbQaI2iGgC1UfuwhgOj)V3FYbZ(ewT&3~(0$?tc-vu8d z>5Qu~b}uc6n?bHlaIGYmbH9dKwu|?z?SlWXv{8|6FJ*;btjDXHY%{f^oEmlU%Qv!1 z#J$;CQI4hs*OsjKo?w=mAagxRVN|1$2C-?pDo)cC#aYi zlFP?5jymE-z8|n%W%)$)ou0h|_$kr0HrGJP4Q$=@`EQk-Q8IJ0l+91&ePBS99VF{4 zK-`2*tK$}A4RgpOO+vpT4;Or32jjmtmMwimSO!)zNLAu;cU31%@#z+0zo(6mvYsW zgZpP#YnsXmLH$-g*kFI;|S|5lK^z8*2h9Z^WAIXnBBE zru>}(+e$+6sZ8F=|L;Q622%EKmWOPC}21A%C#wpkwz z$Ox5fCESLR&$g5*O5Xz$!*(*DN<6nr^bX^mdZm|q*B6ZBipU1zuGA7D_2Rjm=-h?> zd@|WX=fbnlhzr8j4x(T?&KXs_0rf>8ZNPJ2Y_Rwvox~HqP98S5f0@8zt0it`yif?Y zGP*m@&H~ATQm2E+&$|42L|fH<6^KI%Zy+&RA%)$QJ0B49B$;}!E49FKRZqi7&W za{vBcq&(ca<|an}aLc8d#n%+Gfrg^GdQj92M+e8yL6YpjAU*k;TOUoA7cR+plsK)L zN!g9w4or468mQ2^9n-zKL^k^%!p{ej6#14>4<)EW6>fC! zbe@av+t8?R?Zj`-F9dN1ZP(kXCZgf{Gdn8l@Pb^0SyVd;E@2T^TNK?=*;4kI>^T9% zJD`(=FF0X6aZf7d9C=X_wlszh+hQMtFKt9(Oc`}e9?hCm=PC?Ni;PtE&VuZr^VWn_ zeXFEDcG3~1=OtnuW}>(1Rp;7G>9@JWiISl+6`bKD&c*yVh(4CAUf3Tal1t2LV=GWS z+f%A?NF~dsrnf%RQ2JnyFnVwbfGKhuyoFl0fBu;Dx} zMo6u_wpD_Mn8)i9@6R-N{?0@ace)~fLGbOUjV{ZOiImC77UC%a#UQ<7xamdy?&LND zJQ8M(t)|z+JHSw&N1NAQh#7{ab&cZ47@87v9jlN4g2uhNFLLRCDvF#HEucynbJ|+( zUi<8iVh@c5Q#%BMm2$(M;8kE~N`C=LA~=Or@Y`()5Ul&S`zO&-{DcMU{Th&5EU<7I zRMEj@=RM7k`2A6!YkX&Fa2K$+!c;{4j_kg z77_%8^5;`uN5?je=qT>lCW%{)w2vE2p$}Y|?oj(o#!Nk~T;#5ISF|l&!jpES6~3;H zQRYI2=X>g<|9TLs*96xU4)JO$2l^O|S<*lNUZi@LYq7OP8S+uzm zFx}h`NVMl8I@jv!R|M%{>JNcl@$uOmx!-%O>%r?=)KktkovVK#vBS@EPG9+6kw$c| zsqYi$g7wTniY3dCcJ-U*Q!h+Olo>r`+e|)KskdU$d||j1p9;jn`9vl$5FJEYXU=8v zr#uWyq!5}=Lzn2uZMpirc5RUP`JIjM9CVnD?;@>NPz2Xr$~SGnOUCwJJKxxI2MK~s z#n`org3BdJaf)#{7owa<{VpzLYQ@^8a#48arXE?4poViIMbAy-S=@fs2LM})Fmh9J zfd9ig7Iri3nFHYUZg*L7X5o8poWCkv(}bXR-i66i4ixx0(PwSek_;}I%PMNG-`RzniiR0nmC#d z^!vfNuOhaZ2GVMVMR4AP^8Mr^mxKfr0*4{T*r*rH2`zO zM&*r=?m|8c)^cZf53tfqkpY|A3WM0DN;?TnfK8udBYu&*+xaF4-3N^UtRn-`O{OtG zu;8{BR@elnA6KwuJUA~D%#^a8s{hy*jj)ZPLPQrI~G#?CtvHu;NbZdQ(u)%+7s&#OD07 z4D@oa@IjY@sh9O&DlD>|Q`aM(Ifw#${=rxBdSHf#moB9evSH-15jxPEahcT7y z%6_4KDh;OAU@lQPv)D(K(yL_qmb8J=Gv{@yXzkFfxn>W;ik z&`HYf0)|1Pb&eQlBXSb*J2HH=+?L3A{pBCW;?K0+m*T9b_-)2wCAP=E;2-LYMNdI0 zFNDiLe!p)2T}08qBMd}4-d@Tq_be98Mr~hF-OUQxoeZ!~F9}-tL9=Gm<`4LZF6;1o z|M^}Cu;C#fu!5YtCilhUU>&281VS>%%oYT4es<2Nep{Yt8Ug|L7W&5TbsZnKB53zX z@@XCo1b94%0M55YBkuo!!GLUnqz=La6t{q*zW3V}6#0uv9WERvpM+&|6l(|r>XszRMq%vG4x;PzDePBOo0aGnbX7{>b6Zv@hRAAEiY pMC~*v0fE2PARYbx{m=R_9bTNr&tpgO0=PHi-kpb Date: Sat, 14 Sep 2024 13:37:12 +0200 Subject: [PATCH 07/10] DOC: mention docs on classes init --- rocketpy/rocket/aero_surface/generic_surface.py | 3 +-- rocketpy/rocket/aero_surface/linear_generic_surface.py | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/rocketpy/rocket/aero_surface/generic_surface.py b/rocketpy/rocket/aero_surface/generic_surface.py index 051cb5f28..a48d6a129 100644 --- a/rocketpy/rocket/aero_surface/generic_surface.py +++ b/rocketpy/rocket/aero_surface/generic_surface.py @@ -35,8 +35,7 @@ def __init__( See Also -------- - For more information on how to create a custom aerodynamic surface, - check TODO: ADD LINK TO DOCUMENTATION + :ref:`genericsurfaces`. Parameters ---------- diff --git a/rocketpy/rocket/aero_surface/linear_generic_surface.py b/rocketpy/rocket/aero_surface/linear_generic_surface.py index 1c2befd7b..3fa0ef194 100644 --- a/rocketpy/rocket/aero_surface/linear_generic_surface.py +++ b/rocketpy/rocket/aero_surface/linear_generic_surface.py @@ -29,8 +29,7 @@ def __init__( See Also -------- - For more information on how to create a custom aerodynamic surface, - check TODO: ADD LINK TO DOCUMENTATION + :ref:`genericsurfaces`. Parameters ---------- From 9016ccef93336603bf93e9b382675718c8d7de28 Mon Sep 17 00:00:00 2001 From: MateusStano Date: Sat, 14 Sep 2024 14:44:07 +0200 Subject: [PATCH 08/10] DOC: address all review comments --- docs/user/index.rst | 1 - docs/user/rocket/generic_surface.rst | 50 +++++++++++++++++---------- docs/user/rocket/rocket.rst | 2 +- tests/fixtures/motor/aeroframe.png | Bin 32917 -> 0 bytes 4 files changed, 32 insertions(+), 21 deletions(-) delete mode 100644 tests/fixtures/motor/aeroframe.png diff --git a/docs/user/index.rst b/docs/user/index.rst index 6f28d2b4d..6b29c80f5 100644 --- a/docs/user/index.rst +++ b/docs/user/index.rst @@ -15,7 +15,6 @@ RocketPy's User Guide Positions and Coordinate Systems Motors Rocket - Environment .. toctree:: :maxdepth: 2 diff --git a/docs/user/rocket/generic_surface.rst b/docs/user/rocket/generic_surface.rst index 007fc5ec9..7629feec5 100644 --- a/docs/user/rocket/generic_surface.rst +++ b/docs/user/rocket/generic_surface.rst @@ -4,12 +4,19 @@ Generic Surfaces and Custom Aerodynamic Coefficients ==================================================== Generic aerodynamic surfaces can be used to model aerodynamic forces based on -force and moment coefficients. The :class:`rocketpy.GenericSurface` class is a -parent class for all generic surfaces. The :class:`rocketpy.LinearGenericSurface` -class is a child class of :class:`rocketpy.GenericSurface` and is used to model -aerodynamic forces based on linear force and moment coefficients. +force and moment coefficients. The :class:`rocketpy.GenericSurface` receives the +coefficients as functions of the angle of attack, side slip angle, Mach number, +Reynolds number, pitch rate, yaw rate, and roll rate. -Both classes base their coeffcient on the definition of the aerodynamic frame +The :class:`rocketpy.LinearGenericSurface` class model aerodynamic forces based +the force and moment coefficients derivatives. The coefficients are derivatives +of the force and moment coefficients with respect to the angle of attack, side +slip angle, Mach number, Reynolds number, pitch rate, yaw rate, and roll rate. + +These classes allows the user to be less dependent on the built-in aerodynamic +surfaces and to define their own aerodynamic coefficients. + +Both classes base their coefficient on the definition of the aerodynamic frame of reference. Aerodynamic Frame @@ -85,6 +92,7 @@ Where: - :math:`\bar{q}` is the dynamic pressure. - :math:`A_{ref}` is the reference area used to calculate the coefficients. + Commonly the rocket's cross-sectional area is used as the reference area. The moment coefficients can be defined as: @@ -100,6 +108,7 @@ And the moments from the coefficients are defined as: Where: - :math:`L_{ref}` is the reference length used to calculate the coefficients. + Commonly the rocket's diameter is used as the reference length. Aerodynamic angles @@ -107,11 +116,11 @@ Aerodynamic angles There aerodynamic angles are defined in two different ways in RocketPy: -- As the angle of attack (:math:`\alpha`) and the side slip - angle (:math:`\beta`), which are defined in the image above. These are used +- As the angle of attack (:math:`\alpha`) and the side slip \ + angle (:math:`\beta`), which are defined in the image above. These are used \ in the calculation of the generic surface forces and moments. -- As the total angle of attack (:math:`\alpha_{\text{tot}}`), defined as the - angle between the total velocity vector and the rocket's centerline. This is +- As the total angle of attack (:math:`\alpha_{\text{tot}}`), defined as the \ + angle between the total velocity vector and the rocket's centerline. This is \ used in the calculation of the standard aerodynamic surface forces and moments. The partial angles are calculated as: @@ -139,7 +148,7 @@ The total angle of attack is calculated as: Generic Surface Class --------------------- -The :class:`rocketpy.GenericSurface` class is used to define a aerodynamic +The :class:`rocketpy.GenericSurface` class is used to define an aerodynamic surface based on force and moment coefficients. A generic surface is defined as follows: @@ -151,10 +160,13 @@ as follows: .. code-block:: python from rocketpy import GenericSurface + + radius = 0.0635 + generic_surface = GenericSurface( - reference_area=np.pi * 0.0635**2, - reference_length=2 * 0.0635, - coeffcients={ + reference_area=np.pi * radius**2, + reference_length=2 * radius, + coefficients={ "cL": "cL.csv", "cQ": "cQ.csv", "cD": "cD.csv", @@ -165,7 +177,7 @@ as follows: name="Generic Surface", ) -The ``coeffcients`` argument is a dictionary containing the coefficients of the +The ``coefficients`` argument is a dictionary containing the coefficients of the generic surface. The keys of the dictionary are the coefficient names, and the values are the coefficients. The possible coefficient names are: @@ -224,7 +236,7 @@ These coefficients can be defined as a callable such as: In which any algorithm can be implemented to calculate the coefficient values. Otherwise, the coefficients can be defined as a ``.csv`` file. The file must -contain a header with at least one of the following columns representin the +contain a header with at least one of the following columns representing the independent variables: - ``alpha``: Angle of attack. @@ -280,13 +292,13 @@ The position of the generic surface is defined in the User Defined coordinate System, see :ref:`rocketaxes` for more information. .. tip:: - If defining the coeffcients of the entire rocket is desired, only a single + If defining the coefficients of the entire rocket is desired, only a single generic surface can be added to the rocket, positioned at the center of dry mass. This will be equivalent to defining the coefficients of the entire rocket. .. attention:: - If there generic is positioned **not** at the center of dry mass, the + If there generic surface is positioned **not** at the center of dry mass, the forces generated by the force coefficients (cL, cQ, cD) will generate a moment around the center of dry mass. This moment will be calculated and added to the moment generated by the moment coefficients (cm, cn, cl). @@ -298,7 +310,7 @@ Linear Generic Surface Class ---------------------------- The :class:`rocketpy.LinearGenericSurface` class is used to define a aerodynamic -surface based on the forces and moments coeffcient derivatives. A linear generic +surface based on the forces and moments coefficient derivatives. A linear generic surface will receive the derivatives of each coefficient with respect to the independent variables. The derivatives are defined as: @@ -368,7 +380,7 @@ shown below: linear_generic_surface = LinearGenericSurface( reference_area=np.pi * 0.0635**2, reference_length=2 * 0.0635, - coeffcients={ + coefficients={ "cL_0": "cL_0.csv", "cL_alpha": "cL_alpha.csv", "cL_beta": "cL_beta.csv", diff --git a/docs/user/rocket/rocket.rst b/docs/user/rocket/rocket.rst index 38b4ce4e6..956175409 100644 --- a/docs/user/rocket/rocket.rst +++ b/docs/user/rocket/rocket.rst @@ -17,5 +17,5 @@ Rocket Usage :maxdepth: 3 :caption: Generic Surfaces and Custom Aerodynamic Coefficients - Generic Surfaces and Custom Aerodynamic Coefficients + Generic Surfaces and Custom Aerodynamic Coefficients \ No newline at end of file diff --git a/tests/fixtures/motor/aeroframe.png b/tests/fixtures/motor/aeroframe.png deleted file mode 100644 index 33868d1e1be794a325d1111173ae9de0315a29c2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32917 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

|Pn?L>k;qj}Jyq#1+nf;fg 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 Date: Sun, 15 Sep 2024 00:19:41 +0200 Subject: [PATCH 09/10] DOC: typo --- docs/user/rocket/generic_surface.rst | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/user/rocket/generic_surface.rst b/docs/user/rocket/generic_surface.rst index 7629feec5..8b0784a42 100644 --- a/docs/user/rocket/generic_surface.rst +++ b/docs/user/rocket/generic_surface.rst @@ -154,7 +154,7 @@ as follows: .. seealso:: For more information on class initialization, see - :class:`rocketpy.GeericSurface.__init__` + :class:`rocketpy.GenericSurface.__init__` .. code-block:: python @@ -286,7 +286,7 @@ rocket's configuration: rocket = Rocket( ... ) - rocket.add_generic_surface(generic_surface, position=(0,0,0)) + rocket.add_surfaces(generic_surface, position=(0,0,0)) The position of the generic surface is defined in the User Defined coordinate System, see :ref:`rocketaxes` for more information. @@ -431,4 +431,6 @@ shown below: "cl_p": "cl_p.csv", }, ) + rocket.add_surfaces(linear_generic_surface, position=(0,0,0)) + From 2e4d34b8f6867981e21b78467ec59bba9eba9992 Mon Sep 17 00:00:00 2001 From: MateusStano Date: Sun, 15 Sep 2024 00:20:05 +0200 Subject: [PATCH 10/10] ENH: remove add_generic_surface --- rocketpy/rocket/rocket.py | 61 --------------------------------------- 1 file changed, 61 deletions(-) diff --git a/rocketpy/rocket/rocket.py b/rocketpy/rocket/rocket.py index e1dc7c6fc..a430149c2 100644 --- a/rocketpy/rocket/rocket.py +++ b/rocketpy/rocket/rocket.py @@ -1323,67 +1323,6 @@ def add_elliptical_fins( self.add_surfaces(fin_set, position) return fin_set - def add_generic_surface( - self, - generic_surface, - position, - angular_position=None, - radius=None, - ): - """Adds a generic surface to the rocket. A generic surface is used to - model any aerodynamic surface that does not fit the predefined classes - such as NoseCone, TrapezoidalFins, EllipticalFins, Tail, etc. - - Parameters - ---------- - generic_surface : GenericSurface, LinearGenericSurface - Generic surface object to be added to the rocket. Must be an - instance of the GenericSurface or LinearGenericSurface class. - position : int, float, tuple, list - Position of the application point of the forces and moments of the - surface. If a tuple or list is passed, it must be in the format - (x, y, z) where x, y, and z are defined in the rocket's user - defined coordinate system. If a single value is passed, it is - assumed to be along the z-axis (centerline) of the rocket's user - defined coordinate system and angular_position and radius must be - given. - angular_position : int, float, optional - Angular position of the surface along the rocket's user defined - coordinate system. By angular position, understand the angle, - measured from the y-axis, that the surface is located. Positive - values are defined as a positive rotation around the z-axis. Only - used if position is a single value. Default is None. - radius : int, float, optional - Fuselage radius where the surface is located. Only used if the - position is a single value. If None, which is default, the rocket - radius will be used. - - Returns - ------- - generic_surface : GenericSurface - Generic surface object created. - """ - radius = radius if radius is not None else self.radius - if isinstance(position, (tuple, list)): - position = Vector(position) - else: - position = Vector( - [ - radius * -math.sin(math.radians(angular_position)), - radius * math.cos(math.radians(angular_position)), - position, - ] - ) - self.add_surfaces(generic_surface, position) - - warnings.warn( - "Generic surfaces do not affect the center of pressure position, " - "static margin or stability margin calculations. This does not " - "affect the simulation accuracy.", - UserWarning, - ) - return generic_surface - def add_parachute( self, name, cd_s, trigger, sampling_rate=100, lag=0, noise=(0, 0, 0) ):