Source code for RCAIDE.Library.Components.Powertrain.Converters.Rotor

# RCAIDE/Library/Compoments/Powertrain/Converters/Rotor.py
# 
# 
# Created:  Mar 2024, M. Clarke

# ----------------------------------------------------------------------------------------------------------------------
#  IMPORT
# ---------------------------------------------------------------------------------------------------------------------- 

 # RCAIDE imports 
from RCAIDE.Framework.Core                              import Data , Units, Container
from RCAIDE.Library.Components                          import Component  
from RCAIDE.Library.Methods.Powertrain.Converters.Rotor.append_rotor_conditions import  append_rotor_conditions

# package imports
import numpy as np
import scipy as sp

# ---------------------------------------------------------------------------------------------------------------------- 
#  Generalized Rotor Class
# ---------------------------------------------------------------------------------------------------------------------- 
[docs] class Rotor(Component): """ A generalized rotor component model serving as the base class for various rotary propulsion devices. Attributes ---------- tag : str Identifier for the rotor. Default is 'rotor'. number_of_blades : float Number of rotor blades. Default is 0.0. tip_radius : float Outer radius of the rotor [m]. Default is 0.0. hub_radius : float Inner radius of the rotor at hub [m]. Default is 0.0. twist_distribution : float Blade twist angle distribution [rad]. Default is 0.0. sweep_distribution : float Quarter chord sweep distribution [m]. Default is 0.0. chord_distribution : float Blade chord length distribution [m]. Default is 0.0. thickness_to_chord : float Ratio of blade thickness to chord. Default is 0.0. max_thickness_distribution : float Maximum thickness distribution along blade [m]. Default is 0.0. radius_distribution : ndarray Radial stations along blade [m]. Default is None. mid_chord_alignment : float Mid-chord line alignment [m]. Default is 0.0. blade_solidity : float Ratio of total blade area to rotor disk area. Default is 0.0. flap_angle : float Blade flapping angle [rad]. Default is 0.0. number_azimuthal_stations : int Number of azimuthal calculation points. Default is 16. vtk_airfoil_points : int Number of points for VTK airfoil visualization. Default is 40. Airfoils : Airfoil_Container Container for blade airfoil definitions. Default is empty container. airfoil_polar_stations : ndarray Radial stations for airfoil polars. Default is None. induced_power_factor : float Factor accounting for non-ideal induced power. Default is 1.48. profile_drag_coefficient : float Mean blade profile drag coefficient. Default is 0.03. clockwise_rotation : bool Direction of rotation. Default is True. phase_offset_angle : float Initial blade phase angle [rad]. Default is 0.0. orientation_euler_angles : list Angles defining rotor orientation [rad]. Default is [0.,0.,0.]. blade_pitch_command : float Commanded blade pitch angle [rad]. Default is 0.0. ducted : bool Flag for ducted rotor configuration. Default is False. sol_tolerance : float Solution convergence tolerance. Default is 1e-8. use_2d_analysis : bool Flag for 2D aerodynamic analysis. Default is False. nonuniform_freestream : bool Flag for nonuniform inflow conditions. Default is False. Notes ----- The Rotor class provides a comprehensive framework for modeling rotary propulsion devices including: * Geometric definition of rotor and blades * Aerodynamic performance calculation * Wake modeling capabilities * Blade element analysis * Performance optimization * Acoustic analysis **Major Assumptions** * Rigid blade structure * Quasi-steady aerodynamics * Small angle approximations for flapping * Linear blade twist and taper * Uniform inflow (unless nonuniform_freestream is True) **Theory** The rotor model combines: * Blade Element Momentum Theory * Prescribed/Free Wake Analysis * Acoustic Propagation Models * Performance Optimization Methods **Definitions** 'Blade Solidity' Ratio of total blade area to rotor disk area 'Induced Power Factor' Correction for non-ideal induced power effects 'Phase Offset' Initial azimuthal position of reference blade See Also -------- RCAIDE.Library.Components.Powertrain.Converters.Propeller RCAIDE.Library.Components.Powertrain.Converters.Lift_Rotor RCAIDE.Library.Components.Powertrain.Converters.Prop_Rotor """ def __defaults__(self): """This sets the default values for the component to function. Assumptions: None Source: N/A Inputs: None Outputs: None Properties Used: None """ self.tag = 'rotor' # geometry properties self.number_of_blades = 0.0 self.tip_radius = 0.0 self.hub_radius = 0.0 self.twist_distribution = 0.0 self.sweep_distribution = 0.0 # quarter chord offset from quarter chord of root airfoil self.chord_distribution = 0.0 self.thickness_to_chord = 0.0 self.max_thickness_distribution = 0.0 self.radius_distribution = None self.blade_pitch_command = 0.0 self.mid_chord_alignment = 0.0 self.blade_solidity = 0.0 self.flap_angle = 0.0 self.number_azimuthal_stations = 16 self.vtk_airfoil_points = 40 self.airfoils = Airfoil_Container() self.airfoil_polar_stations = [] # Initialize the default wake set to Fidelity Zero self.fidelity = 'Blade_Element_Momentum_Theory_Helmholtz_Wake' # design flight conditions self.cruise = Data() self.cruise.design_power = None self.cruise.design_thrust = None self.cruise.design_torque = None self.cruise.design_power_coefficient = 0.01 self.cruise.design_thrust_coefficient = 0.01 self.cruise.design_torque_coefficient = 0.005 self.cruise.design_Cl = 0.7 self.cruise.design_efficiency = 0.86 self.cruise.design_angular_velocity = None self.cruise.design_tip_mach = None self.cruise.design_acoustics = None self.cruise.design_performance = None self.cruise.design_SPL_dBA = None self.cruise.design_blade_pitch_command = 0.0 # operating conditions self.induced_power_factor = 1.48 # accounts for interference effects self.profile_drag_coefficient = .03 self.clockwise_rotation = True self.phase_offset_angle = 0.0 self.orientation_euler_angles = [0.,0.,0.] # vector of angles defining default orientation of rotor self.ducted = False self.sol_tolerance = 1e-8 self.use_2d_analysis = False # True if rotor is at an angle relative to freestream or nonuniform freestream self.nonuniform_freestream = False self.axial_velocities_2d = None # user input for additional velocity influences at the rotor self.tangential_velocities_2d = None # user input for additional velocity influences at the rotor self.radial_velocities_2d = None # user input for additional velocity influences at the rotor self.start_angle = 0.0 # angle of first blade from vertical self.start_angle_idx = 0 # azimuthal index at which the blade is started self.electric_propulsion_fraction = 1.0 # blade optimization parameters self.optimization_parameters = Data() self.optimization_parameters.tip_mach_range = [0.3,0.7] self.optimization_parameters.multiobjective_aeroacoustic_weight = 1.0 self.optimization_parameters.multiobjective_performance_weight = 1.0 self.optimization_parameters.multiobjective_acoustic_weight = 1.0 self.optimization_parameters.noise_evaluation_angle = 135 * Units.degrees self.optimization_parameters.tolerance = 1E-4 self.optimization_parameters.ideal_SPL_dBA = 30 self.optimization_parameters.ideal_efficiency = 1.0 self.optimization_parameters.ideal_figure_of_merit = 1.0
[docs] def append_operating_conditions(rotor,segment,energy_conditions,noise_conditions=None): append_rotor_conditions(rotor,segment,energy_conditions,noise_conditions) return
[docs] def append_airfoil(self,airfoil): """ Adds an airfoil to the segment Assumptions: None Source: N/A Inputs: None Outputs: None Properties Used: N/A """ # assert database type if not isinstance(airfoil,Data): raise Exception('input component must be of type Data()') # See if the component exists, if it does modify the name keys = self.keys() if airfoil.tag in keys: string_of_keys = "".join(self.keys()) n_comps = string_of_keys.count(airfoil.tag) airfoil.tag = airfoil.tag + str(n_comps+1) # store data self.airfoils.append(airfoil)
[docs] def vec_to_vel(self): """ Rotates from the rotor's vehicle frame to the rotor's velocity frame. Returns ------- rot_mat : ndarray 3x3 rotation matrix transforming from vehicle frame to velocity frame. Notes ----- This method creates a rotation matrix for transforming coordinates between two reference frames of the rotor. When rotor is axially aligned with the vehicle body. Velocity frame: * X-axis points out the nose * Z-axis points towards the ground * Y-axis points out the right wing Vehicle frame: * X-axis points towards the tail * Z-axis points towards the ceiling * Y-axis points out the right wing **Major Assumptions** * The rotor's default orientation is aligned with the vehicle body * Right-handed coordinate system is used * Small angle approximations are not used * Rotation sequence is fixed """ rot_mat = sp.spatial.transform.Rotation.from_rotvec([0,np.pi,0]).as_matrix() return rot_mat
[docs] def body_to_prop_vel(self, commanded_thrust_vector): """ Rotates from the system's body frame to the rotor's velocity frame. Parameters ---------- commanded_thrust_vector : ndarray Vector of commanded thrust angles [rad] for each time step. Returns ------- rot_mat : ndarray 3x3 rotation matrix transforming from body frame to rotor velocity frame. rots : ndarray Array of rotation vectors including commanded thrust angles and orientation. Notes ----- This method performs a sequence of rotations to transform coordinates from the vehicle body frame to the rotor's velocity frame. The transformation sequence is: 1. Body to vehicle frame (π rotation about Y-axis) 2. Vehicle to rotor vehicle frame (includes thrust vector and orientation) 3. Rotor vehicle to rotor velocity frame **Theory** The complete transformation is computed as: .. math:: R_{total} = (R_{body2vehicle} R_{vehicle2rotor}) R_{rotor2vel} Where: * R_{body2vehicle} is a π rotation about Y-axis * R_{vehicle2rotor} includes orientation_euler_angles and thrust command * R_{rotor2vel} is from vec_to_vel() Velocity frame: * X-axis points out the nose * Z-axis points towards the ground * Y-axis points out the right wing Vehicle frame: * X-axis points towards the tail * Z-axis points towards the ceiling * Y-axis points out the right wing **Major Assumptions** * The rotor's default orientation is defined by orientation_euler_angles * Right-handed coordinate system is used * Thrust vector rotation is applied about the Y-axis * Matrix multiplication order preserves proper transformation sequence * Euler angle sequence is fixed """ # Go from velocity to vehicle frame body_2_vehicle = sp.spatial.transform.Rotation.from_rotvec([0,np.pi,0]).as_matrix() # Go from vehicle frame to propeller vehicle frame: rot 1 including the extra body rotation cpts = len(np.atleast_1d(commanded_thrust_vector)) rots = np.array(self.orientation_euler_angles) * 1. rots = np.repeat(rots[None,:], cpts, axis=0) rots[:,1] += commanded_thrust_vector[:,0] vehicle_2_prop_vec = sp.spatial.transform.Rotation.from_rotvec(rots).as_matrix() # GO from the propeller vehicle frame to the propeller velocity frame: rot 2 prop_vec_2_prop_vel = self.vec_to_vel() # Do all the matrix multiplies rot1 = np.matmul(body_2_vehicle,vehicle_2_prop_vec) rot_mat = np.matmul(rot1,prop_vec_2_prop_vel) return rot_mat , rots
[docs] def prop_vel_to_body(self, commanded_thrust_vector): """ Rotates from the rotor's velocity frame to the system's body frame. Parameters ---------- commanded_thrust_vector : ndarray Vector of commanded thrust angles [rad] for each time step. Returns ------- rot_mat : ndarray 3x3 rotation matrix transforming from rotor velocity frame to body frame. rots : ndarray Array of rotation vectors including commanded thrust angles and orientation. Notes ----- This method performs the inverse transformation sequence of body_to_prop_vel. The transformation sequence is: 1. Rotor velocity to rotor vehicle frame 2. Rotor vehicle to vehicle frame (includes thrust vector and orientation) 3. Vehicle to body frame (π rotation about Y-axis) **Theory** The complete transformation is computed as: .. math:: R_{total} = (R_{body2propvel})^{-1} Velocity frame: * X-axis points out the nose * Z-axis points towards the ground * Y-axis points out the right wing Vehicle frame: * X-axis points towards the tail * Z-axis points towards the ceiling * Y-axis points out the right wing **Major Assumptions** * The rotor's default orientation is defined by orientation_euler_angles * Right-handed coordinate system is used * Thrust vector rotation is applied about the Y-axis * Rotation matrices are orthogonal (inverse = transpose) * Euler angle sequence is fixed """ body2propvel,rots = self.body_to_prop_vel(commanded_thrust_vector) r = sp.spatial.transform.Rotation.from_matrix(body2propvel) r = r.inv() rot_mat = r.as_matrix() return rot_mat, rots
[docs] def vec_to_prop_body(self,commanded_thrust_vector): rot_mat, rots = self.prop_vel_to_body(commanded_thrust_vector) return rot_mat, rots
[docs] class Airfoil_Container(Container): """ Container for rotor airfoil Assumptions: None Source: N/A Inputs: None Outputs: None Properties Used: N/A """
[docs] def get_children(self): """ Returns the components that can go inside Assumptions: None Source: N/A Inputs: None Outputs: None Properties Used: N/A """ return []