diff --git a/regression/automatic_regression.py b/regression/automatic_regression.py index cb42b0c79d..68da628918 100644 --- a/regression/automatic_regression.py +++ b/regression/automatic_regression.py @@ -37,7 +37,6 @@ modules = [ # ----------------------- Regression List -------------------------- - 'scripts/segments/transition_segment_test.py', 'scripts/aerodynamics/aerodynamics.py', 'scripts/aerodynamics/all_moving_surfaces_vlm.py', 'scripts/aerodynamics/control_surfaces_vlm.py', @@ -45,8 +44,7 @@ 'scripts/aerodynamics/sideslip_and_rotation_vlm.py', 'scripts/airfoil_import/airfoil_import_test.py', 'scripts/airfoil_import/airfoil_interpolation_test.py', - 'scripts/airfoil_analysis/airfoil_panel_method_test.py', - 'scripts/airfoil_analysis/airfoil_polar_surrogate_test.py', + 'scripts/airfoil_analysis/airfoil_panel_method_test.py', 'scripts/atmosphere/atmosphere.py', 'scripts/atmosphere/constant_temperature.py', 'scripts/AVL/test_AVL.py', @@ -96,6 +94,7 @@ 'scripts/rocket_network/Rocketdyne_F1.py', 'scripts/rocket_network/Rocketdyne_J2.py', 'scripts/segments/segment_test.py', + 'scripts/segments/transition_segment_test.py', 'scripts/slipstream/slipstream_test.py', 'scripts/slipstream/propeller_interactions.py', 'scripts/solar_network/solar_network.py', diff --git a/regression/scripts/VTOL/test_Tiltwing.py b/regression/scripts/VTOL/test_Tiltwing.py index f2368d3d4a..e3774ca45d 100644 --- a/regression/scripts/VTOL/test_Tiltwing.py +++ b/regression/scripts/VTOL/test_Tiltwing.py @@ -56,7 +56,7 @@ def main(): # RPM check during hover RPM = results.segments.departure.conditions.propulsion.propeller_rpm[0][0] - RPM_true = 1847.7091031178145 + RPM_true = 1922.161595958687 print(RPM) diff_RPM = np.abs(RPM - RPM_true) @@ -66,16 +66,13 @@ def main(): # lift Coefficient Check During Cruise lift_coefficient = results.segments.climb.conditions.aerodynamics.lift_coefficient[0][0] - lift_coefficient_true = 1.030344423712622 + lift_coefficient_true = 1.0303444240760393 print(lift_coefficient) diff_CL = np.abs(lift_coefficient - lift_coefficient_true) print('CL difference') print(diff_CL) - assert np.abs((lift_coefficient - lift_coefficient_true)/lift_coefficient_true) < 1e-3 - - - return - + assert np.abs((lift_coefficient - lift_coefficient_true)/lift_coefficient_true) < 1e-3 + return # ---------------------------------------------------------------------- # Setup diff --git a/regression/scripts/Vehicles/Additional_Fuel_Aircraft.py b/regression/scripts/Vehicles/Additional_Fuel_Aircraft.py index fbc2660796..c00ca3f65a 100644 --- a/regression/scripts/Vehicles/Additional_Fuel_Aircraft.py +++ b/regression/scripts/Vehicles/Additional_Fuel_Aircraft.py @@ -272,21 +272,21 @@ def vehicle_setup(): # ------------------------------------------------------------------ # Nacelles # ------------------------------------------------------------------ - nacelle = SUAVE.Components.Nacelles.Nacelle() - nacelle.tag = 'nacelle_1' - nacelle.length = 2.71 - nacelle.inlet_diameter = 1.90 - nacelle.diameter = 2.05 - nacelle.areas.wetted = 1.1*np.pi*nacelle.diameter*nacelle.length - nacelle.origin = [[13.72, -4.86,-1.9]] - nacelle.flow_through = True - nacelle_airfoil = SUAVE.Components.Airfoils.Airfoil() - nacelle_airfoil.naca_4_series_airfoil = '2410' - nacelle.append_airfoil(nacelle_airfoil) - - nacelle_2 = deepcopy(nacelle) - nacelle_2.tag = 'nacelle_2' - nacelle_2.origin = [[13.72, 4.86,-1.9]] + nacelle = SUAVE.Components.Nacelles.Nacelle() + nacelle.tag = 'nacelle_1' + nacelle.length = 2.71 + nacelle.inlet_diameter = 1.90 + nacelle.diameter = 2.05 + nacelle.areas.wetted = 1.1*np.pi*nacelle.diameter*nacelle.length + nacelle.origin = [[13.72, -4.86,-1.9]] + nacelle.flow_through = True + nacelle_airfoil = SUAVE.Components.Airfoils.Airfoil() + nacelle_airfoil.NACA_4_series_flag = True + nacelle_airfoil.coordinate_file = '2410' + nacelle.append_airfoil(nacelle_airfoil) + nacelle_2 = deepcopy(nacelle) + nacelle_2.tag = 'nacelle_2' + nacelle_2.origin = [[13.72, 4.86,-1.9]] vehicle.append_component(nacelle) vehicle.append_component(nacelle_2) diff --git a/regression/scripts/Vehicles/Boeing_737.py b/regression/scripts/Vehicles/Boeing_737.py index b868eb1225..0ec861dd5e 100644 --- a/regression/scripts/Vehicles/Boeing_737.py +++ b/regression/scripts/Vehicles/Boeing_737.py @@ -506,19 +506,19 @@ def vehicle_setup(): # ------------------------------------------------------------------ # Nacelles # ------------------------------------------------------------------ - nacelle = SUAVE.Components.Nacelles.Nacelle() - nacelle.tag = 'nacelle_1' - nacelle.length = 2.71 - nacelle.inlet_diameter = 1.90 - nacelle.diameter = 2.05 - nacelle.areas.wetted = 1.1*np.pi*nacelle.diameter*nacelle.length - nacelle.origin = [[13.72, -4.86,-1.9]] - nacelle.flow_through = True - nacelle.Airfoil.naca_4_series_airfoil = '2410' - - nacelle_2 = deepcopy(nacelle) - nacelle_2.tag = 'nacelle_2' - nacelle_2.origin = [[13.72, 4.86,-1.9]] + nacelle = SUAVE.Components.Nacelles.Nacelle() + nacelle.tag = 'nacelle_1' + nacelle.length = 2.71 + nacelle.inlet_diameter = 1.90 + nacelle.diameter = 2.05 + nacelle.areas.wetted = 1.1*np.pi*nacelle.diameter*nacelle.length + nacelle.origin = [[13.72, -4.86,-1.9]] + nacelle.flow_through = True + nacelle.Airfoil.NACA_4_series_flag = True + nacelle.Airfoil.coordinate_file = '2410' + nacelle_2 = deepcopy(nacelle) + nacelle_2.tag = 'nacelle_2' + nacelle_2.origin = [[13.72, 4.86,-1.9]] vehicle.append_component(nacelle) vehicle.append_component(nacelle_2) diff --git a/regression/scripts/Vehicles/Cessna_172.py b/regression/scripts/Vehicles/Cessna_172.py index c42dfd9dca..4a41fcbfcc 100644 --- a/regression/scripts/Vehicles/Cessna_172.py +++ b/regression/scripts/Vehicles/Cessna_172.py @@ -20,8 +20,6 @@ from SUAVE.Methods.Geometry.Three_Dimensional.compute_span_location_from_chord_length import compute_span_location_from_chord_length from SUAVE.Methods.Flight_Dynamics.Static_Stability.Approximations.datcom import datcom from SUAVE.Methods.Flight_Dynamics.Static_Stability.Approximations.Supporting_Functions.trapezoid_ac_x import trapezoid_ac_x -from SUAVE.Methods.Geometry.Two_Dimensional.Cross_Section.Airfoil.import_airfoil_geometry import import_airfoil_geometry -from SUAVE.Methods.Geometry.Two_Dimensional.Cross_Section.Airfoil.compute_airfoil_polars import compute_airfoil_polars from SUAVE.Methods.Propulsion import propeller_design def vehicle_setup(): @@ -246,18 +244,16 @@ def vehicle_setup(): prop.design_Cl = 0.8 prop.design_altitude = 12000. * Units.feet prop.design_power = .64 * 180. * Units.horsepower - prop.variable_pitch = True - - airfoil_geometry = ['../Vehicles/Airfoils/NACA_4412.txt'] - airfoil_polars = [['../Vehicles//Airfoils/Polars/NACA_4412_polar_Re_50000.txt' , - '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_100000.txt' , - '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_200000.txt' , - '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_500000.txt' , - '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_1000000.txt' ]] - - prop.airfoil_geometry_data = import_airfoil_geometry(airfoil_geometry) - prop.airfoil_polar_data = compute_airfoil_polars(airfoil_polars, prop.airfoil_geometry_data) - prop.airfoil_polar_stations = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] + prop.variable_pitch = True + airfoil = SUAVE.Components.Airfoils.Airfoil() + airfoil.coordinate_file = '../Vehicles/Airfoils/NACA_4412.txt' + airfoil.polar_files = ['../Vehicles//Airfoils/Polars/NACA_4412_polar_Re_50000.txt' , + '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_100000.txt' , + '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_200000.txt' , + '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_500000.txt' , + '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_1000000.txt' ] + prop.append_airfoil(airfoil) + prop.airfoil_polar_stations = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] prop = propeller_design(prop) net.propellers.append(prop) diff --git a/regression/scripts/Vehicles/Electric_Multicopter.py b/regression/scripts/Vehicles/Electric_Multicopter.py index 597157e789..8ee2891024 100644 --- a/regression/scripts/Vehicles/Electric_Multicopter.py +++ b/regression/scripts/Vehicles/Electric_Multicopter.py @@ -15,8 +15,6 @@ from SUAVE.Methods.Center_of_Gravity.compute_component_centers_of_gravity import compute_component_centers_of_gravity from SUAVE.Methods.Propulsion.electric_motor_sizing import size_optimal_motor from SUAVE.Methods.Weights.Correlations.Propulsion import nasa_motor, hts_motor , air_cooled_motor -from SUAVE.Methods.Geometry.Two_Dimensional.Cross_Section.Airfoil.import_airfoil_geometry import import_airfoil_geometry -from SUAVE.Methods.Geometry.Two_Dimensional.Cross_Section.Airfoil.compute_airfoil_polars import compute_airfoil_polars import numpy as np from copy import deepcopy @@ -241,18 +239,17 @@ def vehicle_setup(): lift_rotor.angular_velocity = (design_tip_mach*speed_of_sound)/lift_rotor.tip_radius lift_rotor.design_Cl = 0.7 lift_rotor.design_altitude = 1000 * Units.feet - lift_rotor.design_thrust = Hover_Load/(net.number_of_propeller_engines-1) # contingency for one-engine-inoperative condition - - airfoil_geometry = ['../Vehicles/Airfoils/NACA_4412.txt'] - airfoil_polars = [['../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_50000.txt' , - '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_100000.txt' , - '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_200000.txt' , - '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_500000.txt' , - '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_1000000.txt' ]] - lift_rotor.airfoil_geometry_data = import_airfoil_geometry(airfoil_geometry) - lift_rotor.airfoil_polar_data = compute_airfoil_polars(airfoil_polars, lift_rotor.airfoil_geometry_data) - lift_rotor.airfoil_polar_stations = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] - lift_rotor = propeller_design(lift_rotor) + lift_rotor.design_thrust = Hover_Load/(net.number_of_propeller_engines-1) # contingency for one-engine-inoperative condition + airfoil = SUAVE.Components.Airfoils.Airfoil() + airfoil.coordinate_file = '../Vehicles/Airfoils/NACA_4412.txt' + airfoil.polar_files = ['../Vehicles//Airfoils/Polars/NACA_4412_polar_Re_50000.txt' , + '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_100000.txt' , + '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_200000.txt' , + '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_500000.txt' , + '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_1000000.txt' ] + lift_rotor.append_airfoil(airfoil) + lift_rotor.airfoil_polar_stations = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] + lift_rotor = propeller_design(lift_rotor) # Appending rotors with different origins origins = [[ 0.,2.,1.4],[ 0.0,-2.,1.4], diff --git a/regression/scripts/Vehicles/Embraer_190.py b/regression/scripts/Vehicles/Embraer_190.py index 18b2f6599b..c23b270088 100644 --- a/regression/scripts/Vehicles/Embraer_190.py +++ b/regression/scripts/Vehicles/Embraer_190.py @@ -256,9 +256,9 @@ def vehicle_setup(): nacelle.inlet_diameter = 2.0 nacelle.origin = [[12.0,4.38,-2.1]] Awet = 1.1*np.pi*nacelle.diameter*nacelle.length # 1.1 is simple coefficient - nacelle.areas.wetted = Awet - nacelle.Airfoil.naca_4_series_airfoil = '2410' - + nacelle.areas.wetted = Awet + nacelle.Airfoil.NACA_4_series_flag = True + nacelle.Airfoil.coordinate_file = '2410' nacelle_2 = deepcopy(nacelle) nacelle_2.tag = 'nacelle_2' nacelle_2.origin = [[12.0,-4.38,-2.1]] diff --git a/regression/scripts/Vehicles/Propellers/APC_10x7_thin_electric.py b/regression/scripts/Vehicles/Propellers/APC_10x7_thin_electric.py index 674cc3860a..ccf7d7a609 100644 --- a/regression/scripts/Vehicles/Propellers/APC_10x7_thin_electric.py +++ b/regression/scripts/Vehicles/Propellers/APC_10x7_thin_electric.py @@ -8,8 +8,11 @@ import SUAVE from SUAVE.Core import Data, Units import numpy as np -from SUAVE.Methods.Geometry.Two_Dimensional.Cross_Section.Airfoil.compute_airfoil_polars import compute_airfoil_polars -from SUAVE.Methods.Geometry.Two_Dimensional.Cross_Section.Airfoil.import_airfoil_geometry import import_airfoil_geometry +from SUAVE.Methods.Geometry.Two_Dimensional.Cross_Section.Airfoil.import_airfoil_geometry\ + import import_airfoil_geometry +from SUAVE.Methods.Geometry.Two_Dimensional.Cross_Section.Airfoil.compute_airfoil_properties import ( + compute_airfoil_properties, +) import os @@ -93,34 +96,25 @@ def propeller_geometry(): ] ) - prop.pitch_command = 0.0* Units.deg - prop.twist_distribution = beta * Units.deg - prop.chord_distribution = c_R * prop.tip_radius - prop.radius_distribution = r_R * prop.tip_radius - prop.max_thickness_distribution = 0.12 - - prop.number_azimuthal_stations = 24 - prop.number_radial_stations = len(r_R) - - # Distance from mid chord to the line axis out of the center of the blade - In this case the 1/4 chords are all aligned - MCA = prop.chord_distribution/4. - prop.chord_distribution[0]/4. - prop.mid_chord_alignment = MCA - - airfoils_path = os.path.join(os.path.dirname(__file__), "../Airfoils/") - polars_path = os.path.join(os.path.dirname(__file__), "../Airfoils/Polars/") - airfoil_geometry = [airfoils_path + "Clark_y.txt"] - airfoil_polars = [ - [ polars_path + "Clark_y_polar_Re_50000.txt", - polars_path + "Clark_y_polar_Re_100000.txt", - polars_path + "Clark_y_polar_Re_200000.txt", - polars_path + "Clark_y_polar_Re_500000.txt", - polars_path + "Clark_y_polar_Re_1000000.txt", - ], - ] - prop.airfoil_geometry_data = import_airfoil_geometry(airfoil_geometry) - prop.airfoil_polar_data = compute_airfoil_polars(airfoil_polars, prop.airfoil_geometry_data) - prop.airfoil_polar_stations = np.zeros(len(r_R)) - prop.airfoil_polar_stations = list(prop.airfoil_polar_stations.astype(int)) - - + prop.pitch_command = 0.0* Units.deg + prop.twist_distribution = beta * Units.deg + prop.chord_distribution = c_R * prop.tip_radius + prop.radius_distribution = r_R * prop.tip_radius + prop.max_thickness_distribution = 0.12 + prop.number_azimuthal_stations = 24 + prop.number_radial_stations = len(r_R) + prop.mid_chord_alignment = prop.chord_distribution/4. - prop.chord_distribution[0]/4. + airfoils_path = os.path.join(os.path.dirname(__file__), "../Airfoils/") + polars_path = os.path.join(os.path.dirname(__file__), "../Airfoils/Polars/") + airfoil = SUAVE.Components.Airfoils.Airfoil() + airfoil.coordinate_file = airfoils_path + "Clark_y.txt" + airfoil.polar_files = [ polars_path + "Clark_y_polar_Re_50000.txt", + polars_path + "Clark_y_polar_Re_100000.txt", + polars_path + "Clark_y_polar_Re_200000.txt", + polars_path + "Clark_y_polar_Re_500000.txt", + polars_path + "Clark_y_polar_Re_1000000.txt"] + airfoil.geometry = import_airfoil_geometry(airfoil.coordinate_file,airfoil.number_of_points) + airfoil.polars = compute_airfoil_properties(airfoil.geometry,airfoil.polar_files) + prop.append_airfoil(airfoil) + prop.airfoil_polar_stations = list(np.zeros(len(r_R)).astype(int)) return prop diff --git a/regression/scripts/Vehicles/Propellers/APC_11x4_Propeller.py b/regression/scripts/Vehicles/Propellers/APC_11x4_Propeller.py new file mode 100644 index 0000000000..37cc76c0f7 --- /dev/null +++ b/regression/scripts/Vehicles/Propellers/APC_11x4_Propeller.py @@ -0,0 +1,71 @@ +# APC_11x4_Propeller.py +# +# Created: Oct 2022, M. Clarke + +# Imports +import SUAVE +from SUAVE.Core import Units, Data +from SUAVE.Methods.Geometry.Two_Dimensional.Cross_Section.Airfoil.compute_airfoil_properties import compute_airfoil_properties +from SUAVE.Methods.Geometry.Two_Dimensional.Cross_Section.Airfoil.import_airfoil_geometry \ + import import_airfoil_geometry +from scipy.interpolate import interp1d +import os +import numpy as np + +# design propeller + +def APC_11x4_Propeller(): + prop = SUAVE.Components.Energy.Converters.Rotor() + prop.inputs = Data() + prop.inputs.pitch_command = 0 + prop.inputs.y_axis_rotation = 0. + prop.tag = 'APC_11x4_Propeller' + prop.tip_radius = (11/2)*Units.inches + prop.hub_radius = prop.tip_radius*0.15 + prop.number_of_blades = 2 + prop.thrust_angle = 0. + dimensionless_radius_chord = np.array([[0.1552847467328054, 0.11186397023752742 ],[0.2053658303920633, 0.13710483640179336 ],[0.2554278355432604, 0.15993608699799672 ], + [0.3030954879328435, 0.1803605838023466 ],[0.3531145664409043, 0.19777019937040918 ],[0.4054803014404272, 0.21156252981016876 ], + [0.4554326051702757, 0.22053849089001235 ],[0.505361060765048 , 0.2265024325097777 ],[0.5552656682247448, 0.2294543546694648 ], + [0.6027568444147668, 0.22758990746923585 ],[0.6549651817227891, 0.2215029094724792 ],[0.7024038920156443, 0.21301201946007814 ], + [0.7545454545454544, 0.1984913669751025 ],[0.8042831250596203, 0.18035915291424204 ],[0.8539826385576648, 0.15740770771725646 ], + [0.9012877992940951, 0.13204950872841742 ],[0.953167032338071 , 0.0843966421825813 ],[0.99 , 0.03493942573690739 ]]) + dimensionless_radius_twist = np.array([[0.1452380952380954, 19.68937875751503 ],[0.1976190476190478, 21.793587174348698 ],[0.25, 22.494989979959918 ], + [0.2952380952380953, 21.8436873747495 ],[0.3500000000000001, 20.741482965931866 ],[0.4023809523809523, 19.138276553106213 ], + [0.44761904761904736, 17.434869739478955 ],[0.5, 15.6312625250501 ],[0.5500000000000003, 14.07815631262525 ], + [0.6000000000000001, 12.725450901803606 ],[0.6499999999999999, 11.57314629258517 ],[0.6976190476190478, 10.470941883767534 ], + [0.7476190476190476, 9.569138276553108 ], [0.7976190476190479, 8.667334669338675 ],[0.8500000000000001, 7.665330661322642 ], + [0.8999999999999999, 6.6633266533066156 ],[0.9500000000000002, 5.360721442885772 ],[0.99, 3.9579158316633247 ]]) + + r_R = dimensionless_radius_chord[:,0] + b_R = dimensionless_radius_chord[:,1] + beta = dimensionless_radius_twist[:,1] + + # estimate thickness + r_R_data = np.array([0.155,0.275,0.367,0.449,0.5,0.55, + 0.6,0.65,0.7,0.75,0.8,0.85,0.9,0.95,0.99]) + t_b_data = np.array([0.122,0.105,0.077,0.061,0.055,0.049,0.045,0.041,0.038 + ,0.035,0.033,0.031,0.029,0.027,0.026]) + b_D_data = np.array([0.14485,0.14587,0.1481, + 0.1499,0.15061,0.15058,0.14981,0.14831,0.1468,0.14529,0.14268, + 0.13764,0.12896,0.11304,0.085]) + func_max_thickness_distribution = interp1d(r_R_data, t_b_data*b_D_data*2*prop.tip_radius, kind='cubic') + prop.max_thickness_distribution = func_max_thickness_distribution(r_R) + prop.twist_distribution = beta*Units.degrees + prop.chord_distribution = b_R*prop.tip_radius + prop.radius_distribution = r_R*prop.tip_radius + prop.mid_chord_alignment = np.zeros_like(prop.chord_distribution) + prop.thickness_to_chord = prop.max_thickness_distribution/prop.chord_distribution + ospath = os.path.abspath(__file__) + separator = os.path.sep + rel_path = os.path.dirname(ospath) + separator + airfoil = SUAVE.Components.Airfoils.Airfoil() + airfoil.coordinate_file = rel_path +'../Airfoils/Clark_y.txt' + airfoil.polar_files = [rel_path +'../Airfoils/Polars/Clark_y_polar_Re_50000.txt', + rel_path +'../Airfoils/Polars/Clark_y_polar_Re_100000.txt',rel_path +'../Airfoils/Polars/Clark_y_polar_Re_200000.txt', + rel_path +'../Airfoils/Polars/Clark_y_polar_Re_500000.txt',rel_path +'../Airfoils/Polars/Clark_y_polar_Re_1000000.txt'] + airfoil.geometry = import_airfoil_geometry(airfoil.coordinate_file,airfoil.number_of_points) + airfoil.polars = compute_airfoil_properties(airfoil.geometry,airfoil.polar_files) + prop.append_airfoil(airfoil) + prop.airfoil_polar_stations = list(np.zeros(len(prop.radius_distribution)).astype(int)) + return prop \ No newline at end of file diff --git a/regression/scripts/Vehicles/Propellers/F8745_D4_Propeller.py b/regression/scripts/Vehicles/Propellers/F8745_D4_Propeller.py new file mode 100644 index 0000000000..ab7dcdb9a6 --- /dev/null +++ b/regression/scripts/Vehicles/Propellers/F8745_D4_Propeller.py @@ -0,0 +1,58 @@ +# F8745_D4_Propeller.py +# +# Created: Oct 2022, M. Clarke + +# Imports +import SUAVE +from SUAVE.Core import Units, Data +from SUAVE.Methods.Geometry.Two_Dimensional.Cross_Section.Airfoil.compute_airfoil_properties import compute_airfoil_properties +from SUAVE.Methods.Geometry.Two_Dimensional.Cross_Section.Airfoil.import_airfoil_geometry \ + import import_airfoil_geometry +from scipy.interpolate import interp1d +import os +import numpy as np + +# design propeller +def F8745_D4_Propeller(): + prop = SUAVE.Components.Energy.Converters.Propeller() + prop.inputs = Data() + prop.inputs.pitch_command = 0 + prop.inputs.y_axis_rotation = 0. + prop.tag = 'F8745_D4_Propeller' + prop.tip_radius = 2.03/2 + prop.hub_radius = prop.tip_radius*0.20 + prop.number_of_blades = 2 + r_R_data = np.array([ 0.2,0.300,0.450,0.601,0.747,0.901,0.950,0.975,0.998 ]) + t_c_data = np.array([ 0.3585,0.1976,0.1148,0.0834,0.0648,0.0591,0.0562,0.0542,0.0533 ]) + b_R_data = np.array([0.116,0.143,0.163,0.169,0.166,0.148,0.135,0.113,0.075 ]) + beta_data = np.array([ 0.362,0.286,0.216,0.170,0.135,0.112,0.105,0.101,0.098 ])* 100 + + num_sec = 30 + new_radius_distribution = np.linspace(0.2,0.98 ,num_sec) + func_twist_distribution = interp1d(r_R_data, (beta_data)* Units.degrees , kind='cubic') + func_chord_distribution = interp1d(r_R_data, b_R_data * prop.tip_radius , kind='cubic') + func_radius_distribution = interp1d(r_R_data, r_R_data * prop.tip_radius , kind='cubic') + func_max_thickness_distribution = interp1d(r_R_data, t_c_data * b_R_data , kind='cubic') + + prop.twist_distribution = func_twist_distribution(new_radius_distribution) + prop.chord_distribution = func_chord_distribution(new_radius_distribution) + prop.radius_distribution = func_radius_distribution(new_radius_distribution) + prop.max_thickness_distribution = func_max_thickness_distribution(new_radius_distribution) + prop.thickness_to_chord = prop.max_thickness_distribution/prop.chord_distribution + ospath = os.path.abspath(__file__) + separator = os.path.sep + rel_path = os.path.dirname(ospath) + separator + airfoil = SUAVE.Components.Airfoils.Airfoil() + airfoil.coordinate_file = rel_path +'../Airfoils/Clark_y.txt' + airfoil.polar_files = [rel_path +'../Airfoils/Polars/Clark_y_polar_Re_50000.txt' , + rel_path +'../Airfoils/Polars/Clark_y_polar_Re_100000.txt', + rel_path +'../Airfoils/Polars/Clark_y_polar_Re_200000.txt', + rel_path +'../Airfoils/Polars/Clark_y_polar_Re_500000.txt', + rel_path +'../Airfoils/Polars/Clark_y_polar_Re_1000000.txt'] + airfoil.geometry = import_airfoil_geometry(airfoil.coordinate_file,airfoil.number_of_points) + airfoil.polars = compute_airfoil_properties(airfoil.geometry,airfoil.polar_files) + prop.append_airfoil(airfoil) + prop.airfoil_polar_stations = list(np.zeros(num_sec).astype(int)) + prop.mid_chord_alignment = np.zeros_like(prop.chord_distribution) + + return prop \ No newline at end of file diff --git a/regression/scripts/Vehicles/Stopped_Rotor.py b/regression/scripts/Vehicles/Stopped_Rotor.py index 280d1cc158..5910e31782 100644 --- a/regression/scripts/Vehicles/Stopped_Rotor.py +++ b/regression/scripts/Vehicles/Stopped_Rotor.py @@ -16,8 +16,7 @@ from SUAVE.Methods.Weights.Buildups.eVTOL.empty import empty from SUAVE.Methods.Center_of_Gravity.compute_component_centers_of_gravity import compute_component_centers_of_gravity from SUAVE.Methods.Geometry.Two_Dimensional.Planform import wing_segmented_planform -from SUAVE.Methods.Geometry.Two_Dimensional.Cross_Section.Airfoil.import_airfoil_geometry import import_airfoil_geometry -from SUAVE.Methods.Geometry.Two_Dimensional.Cross_Section.Airfoil.compute_airfoil_polars import compute_airfoil_polars + import numpy as np import pylab as plt @@ -109,9 +108,9 @@ def vehicle_setup(): segment.sweeps.quarter_chord = 0.0 * Units.degrees segment.thickness_to_chord = 0.12 wing.Segments.append(segment) - + # Fill out more segment properties automatically - wing = wing_segmented_planform(wing) + wing = wing_segmented_planform(wing) # add to vehicle vehicle.append_component(wing) @@ -160,7 +159,7 @@ def vehicle_setup(): wing.twists.root = 0. * Units.degrees wing.twists.tip = 0. * Units.degrees wing.origin = [[4.0, 4.0*0.3048 , 0.205 ]] - wing.aerodynamic_center = [4.2,0,0] + wing.aerodynamic_center = [4.2,0,0] wing.winglet_fraction = 0.0 wing.vertical = True wing.symmetric = False @@ -187,7 +186,7 @@ def vehicle_setup(): wing.twists.root = 0.0 * Units.degrees wing.twists.tip = 0.0 * Units.degrees wing.origin = [[4.0, -4.0*0.3048 , 0.205 ]] - wing.aerodynamic_center = [4.2,0,0] + wing.aerodynamic_center = [4.2,0,0] wing.winglet_fraction = 0.0 wing.vertical = True wing.symmetric = False @@ -442,9 +441,9 @@ def vehicle_setup(): #------------------------------------------------------------------ # Nacelles - #------------------------------------------------------------------ + #------------------------------------------------------------------ rotor_nacelle = SUAVE.Components.Nacelles.Nacelle() - rotor_nacelle.tag = 'rotor_nacelle' + rotor_nacelle.tag = 'rotor_nacelle' rotor_nacelle_origins = [[0.543, 1.63 , -0.126] ,[0.543, -1.63 , -0.126 ] , [3.843, 1.63 , -0.126] ,[3.843, -1.63 , -0.126] , [0.543, 2.826 , -0.126] ,[0.543, -2.826 , -0.126 ] , @@ -453,52 +452,52 @@ def vehicle_setup(): [3.843, 4.022 , -0.126] ,[3.843, -4.022 , -0.126 ]] rotor_nacelle.length = 0.25 rotor_nacelle.diameter = 0.25 - rotor_nacelle.orientation_euler_angles = [0,-90*Units.degrees,0.] - rotor_nacelle.flow_through = False - + rotor_nacelle.orientation_euler_angles = [0,-90*Units.degrees,0.] + rotor_nacelle.flow_through = False + nac_segment = SUAVE.Components.Lofted_Body_Segment.Segment() nac_segment.tag = 'segment_1' - nac_segment.percent_x_location = 0.0 + nac_segment.percent_x_location = 0.0 nac_segment.height = 0.2 nac_segment.width = 0.2 - rotor_nacelle.append_segment(nac_segment) - + rotor_nacelle.append_segment(nac_segment) + nac_segment = SUAVE.Components.Lofted_Body_Segment.Segment() nac_segment.tag = 'segment_2' - nac_segment.percent_x_location = 0.25 + nac_segment.percent_x_location = 0.25 nac_segment.height = 0.25 nac_segment.width = 0.25 - rotor_nacelle.append_segment(nac_segment) - + rotor_nacelle.append_segment(nac_segment) + nac_segment = SUAVE.Components.Lofted_Body_Segment.Segment() nac_segment.tag = 'segment_3' - nac_segment.percent_x_location = 0.5 + nac_segment.percent_x_location = 0.5 nac_segment.height = 0.3 nac_segment.width = 0.3 - rotor_nacelle.append_segment(nac_segment) + rotor_nacelle.append_segment(nac_segment) nac_segment = SUAVE.Components.Lofted_Body_Segment.Segment() nac_segment.tag = 'segment_4' nac_segment.percent_x_location = 0.75 nac_segment.height = 0.25 nac_segment.width = 0.25 - rotor_nacelle.append_segment(nac_segment) + rotor_nacelle.append_segment(nac_segment) nac_segment = SUAVE.Components.Lofted_Body_Segment.Segment() nac_segment.tag = 'segment_5' nac_segment.percent_x_location = 1.0 nac_segment.height = 0.2 nac_segment.width = 0.2 - rotor_nacelle.append_segment(nac_segment) - - rotor_nacelle.areas.wetted = np.pi*rotor_nacelle.diameter*rotor_nacelle.length + 0.5*np.pi*rotor_nacelle.diameter**2 + rotor_nacelle.append_segment(nac_segment) + + rotor_nacelle.areas.wetted = np.pi*rotor_nacelle.diameter*rotor_nacelle.length + 0.5*np.pi*rotor_nacelle.diameter**2 for idx in range(12): nacelle = deepcopy(rotor_nacelle) nacelle.tag = 'nacelle_' + str(idx) - nacelle.origin = [rotor_nacelle_origins[idx]] - vehicle.append_component(nacelle) - + nacelle.origin = [rotor_nacelle_origins[idx]] + vehicle.append_component(nacelle) + #------------------------------------------------------------------ # network #------------------------------------------------------------------ @@ -540,18 +539,18 @@ def vehicle_setup(): #------------------------------------------------------------------ # Design Battery #------------------------------------------------------------------ - bat = SUAVE.Components.Energy.Storages.Batteries.Constant_Mass.Lithium_Ion_LiNiMnCoO2_18650() - bat.mass_properties.mass = 500. * Units.kg - bat.max_voltage = net.voltage + bat = SUAVE.Components.Energy.Storages.Batteries.Constant_Mass.Lithium_Ion_LiNiMnCoO2_18650() + bat.mass_properties.mass = 500. * Units.kg + bat.max_voltage = net.voltage initialize_from_mass(bat) - + # Here we, are going to assume a battery pack module shape. This step is optional but # required for thermal analysis of tge pack number_of_modules = 10 bat.module_config.total = int(np.ceil(bat.pack_config.total/number_of_modules)) bat.module_config.normal_count = int(np.ceil(bat.module_config.total/bat.pack_config.series)) bat.module_config.parallel_count = int(np.ceil(bat.module_config.total/bat.pack_config.parallel)) - net.battery = bat + net.battery = bat #------------------------------------------------------------------ # Design Rotors and Propellers @@ -583,21 +582,17 @@ def vehicle_setup(): propeller.design_Cl = 0.7 propeller.design_altitude = 1000 * Units.feet propeller.design_thrust = (Drag*2.5)/net.number_of_propeller_engines - propeller.variable_pitch = True - - airfoil_geometry = ['../Vehicles/Airfoils/NACA_4412.txt'] - airfoil_polars = [['../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_100000.txt' , - '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_200000.txt' , - '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_500000.txt' , - '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_1000000.txt', - '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_5000000.txt', - '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_7500000.txt' ]] - - - propeller.airfoil_geometry_data = import_airfoil_geometry(airfoil_geometry) - propeller.airfoil_polar_data = compute_airfoil_polars(airfoil_polars, propeller.airfoil_geometry_data) + propeller.variable_pitch = True + airfoil = SUAVE.Components.Airfoils.Airfoil() + airfoil.coordinate_file = '../Vehicles/Airfoils/NACA_4412.txt' + airfoil.polar_files = ['../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_50000.txt' , + '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_100000.txt' , + '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_200000.txt' , + '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_500000.txt' , + '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_1000000.txt' ] + propeller.append_airfoil(airfoil) propeller.airfoil_polar_stations = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] - propeller = propeller_design(propeller) + propeller = propeller_design(propeller) propeller.origin = [[16.*0.3048 , 0. ,2.02*0.3048 ]] net.propellers.append(propeller) @@ -613,21 +608,17 @@ def vehicle_setup(): lift_rotor.design_Cl = 0.7 lift_rotor.design_altitude = 20 * Units.feet lift_rotor.design_thrust = Hover_Load/(net.number_of_lift_rotor_engines-1) # contingency for one-engine-inoperative condition - lift_rotor.variable_pitch = True - - airfoil_geometry = ['../Vehicles/Airfoils/NACA_4412.txt'] - airfoil_polars = [['../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_100000.txt' , - '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_200000.txt' , - '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_500000.txt' , - '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_1000000.txt', - '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_5000000.txt', - '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_7500000.txt' ]] - - - lift_rotor.airfoil_geometry_data = import_airfoil_geometry(airfoil_geometry) - lift_rotor.airfoil_polar_data = compute_airfoil_polars(airfoil_polars, lift_rotor.airfoil_geometry_data) + lift_rotor.variable_pitch = True + airfoil = SUAVE.Components.Airfoils.Airfoil() + airfoil.coordinate_file = '../Vehicles/Airfoils/NACA_4412.txt' + airfoil.polar_files = ['../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_50000.txt' , + '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_100000.txt' , + '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_200000.txt' , + '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_500000.txt' , + '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_1000000.txt' ] + lift_rotor.append_airfoil(airfoil) lift_rotor.airfoil_polar_stations = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] - lift_rotor = propeller_design(lift_rotor) + lift_rotor = propeller_design(lift_rotor) # Appending rotors with different origins rotations = [1,-1,1,-1,1,-1,1,-1,1,-1,1,-1] diff --git a/regression/scripts/Vehicles/Tiltrotor.py b/regression/scripts/Vehicles/Tiltrotor.py index a6cc9f45da..84bd877b1e 100644 --- a/regression/scripts/Vehicles/Tiltrotor.py +++ b/regression/scripts/Vehicles/Tiltrotor.py @@ -16,8 +16,7 @@ from SUAVE.Methods.Power.Battery.Sizing import initialize_from_mass from SUAVE.Methods.Propulsion.electric_motor_sizing import size_optimal_motor from SUAVE.Methods.Geometry.Two_Dimensional.Planform import wing_segmented_planform -from SUAVE.Methods.Geometry.Two_Dimensional.Cross_Section.Airfoil.compute_airfoil_polars import compute_airfoil_polars -from SUAVE.Methods.Geometry.Two_Dimensional.Cross_Section.Airfoil.import_airfoil_geometry import import_airfoil_geometry + import numpy as np from copy import deepcopy @@ -416,21 +415,17 @@ def vehicle_setup(): prop.sol_tolerance = 1e-4 prop.symmetry = True prop.variable_pitch = True - airfoil_geometry = ['../Vehicles/Airfoils/NACA_4412.txt'] - airfoil_polars = [['../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_50000.txt' , - #'../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_100000.txt' , - '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_200000.txt' , - '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_500000.txt' , - '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_1000000.txt', - '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_5000000.txt', - '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_7500000.txt' ]] - - prop.airfoil_geometry_data = import_airfoil_geometry(airfoil_geometry) - prop.airfoil_polar_data = compute_airfoil_polars(airfoil_polars, prop.airfoil_geometry_data, use_pre_stall_data=False) - prop.airfoil_polar_stations = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] - prop = propeller_design(prop) - prop.number_of_airfoil_section_points = 102 - + airfoil = SUAVE.Components.Airfoils.Airfoil() + airfoil.coordinate_file = '../Vehicles/Airfoils/NACA_4412.txt' + airfoil.polar_files = ['../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_50000.txt' , + '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_100000.txt' , + '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_200000.txt' , + '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_500000.txt' , + '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_1000000.txt' ] + prop.append_airfoil(airfoil) + prop.airfoil_polar_stations = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] + prop = propeller_design(prop) + prop_left = deepcopy(prop) prop_left.tag = 'propeller_2' prop_left.origin = [[2.,-5.7,0.784]] @@ -523,8 +518,6 @@ def configs_setup(vehicle): # ------------------------------------------------------------------ config = SUAVE.Components.Configs.Config(base_config) config.tag = 'transition_1' - #vector_angle = 0.0 * Units.degrees - #config.networks.battery_propeller.y_axis_rotation = vector_angle config.networks.battery_propeller.pitch_command = 0. * Units.degrees configs.append(config) diff --git a/regression/scripts/Vehicles/Tiltwing.py b/regression/scripts/Vehicles/Tiltwing.py index 027392e4f9..e8927c12c6 100644 --- a/regression/scripts/Vehicles/Tiltwing.py +++ b/regression/scripts/Vehicles/Tiltwing.py @@ -17,8 +17,6 @@ from SUAVE.Methods.Weights.Buildups.eVTOL.empty import empty from SUAVE.Methods.Weights.Buildups.eVTOL.converge_evtol_weight import converge_evtol_weight from SUAVE.Methods.Center_of_Gravity.compute_component_centers_of_gravity import compute_component_centers_of_gravity -from SUAVE.Methods.Geometry.Two_Dimensional.Cross_Section.Airfoil.import_airfoil_geometry import import_airfoil_geometry -from SUAVE.Methods.Geometry.Two_Dimensional.Cross_Section.Airfoil.compute_airfoil_polars import compute_airfoil_polars from copy import deepcopy import numpy as np @@ -270,24 +268,19 @@ def vehicle_setup(): prop.angular_velocity = prop.design_tip_mach*speed_of_sound/prop.tip_radius prop.design_Cl = 0.7 prop.design_altitude = 500 * Units.feet - Hover_Load = vehicle.mass_properties.takeoff*9.81 + Hover_Load = vehicle.mass_properties.takeoff*9.81 prop.design_thrust = Hover_Load/(net.number_of_propeller_engines-1) # contingency for one-engine-inoperative condition - - airfoil_geometry = ['../Vehicles/Airfoils/NACA_4412.txt'] - airfoil_polars = [['../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_50000.txt' , - '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_100000.txt' , - '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_200000.txt' , - '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_500000.txt' , - '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_1000000.txt' ]] - - # Generate and store airfoil geometry and polar data from files - prop.airfoil_geometry_data = import_airfoil_geometry(airfoil_geometry) - prop.airfoil_polar_data = compute_airfoil_polars(airfoil_polars, prop.airfoil_geometry_data) - prop.airfoil_polar_stations = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] - - # Design propeller radial properties - prop = propeller_design(prop) - prop.rotation = 1 + airfoil = SUAVE.Components.Airfoils.Airfoil() + airfoil.coordinate_file = '../Vehicles/Airfoils/NACA_4412.txt' + airfoil.polar_files = ['../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_50000.txt' , + '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_100000.txt' , + '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_200000.txt' , + '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_500000.txt' , + '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_1000000.txt' ] + prop.append_airfoil(airfoil) + prop.airfoil_polar_stations = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] + prop = propeller_design(prop) + prop.rotation = 1 # Front Rotors Locations diff --git a/regression/scripts/Vehicles/X57_Maxwell_Mod2.py b/regression/scripts/Vehicles/X57_Maxwell_Mod2.py index 63b3f67736..8094ec332f 100644 --- a/regression/scripts/Vehicles/X57_Maxwell_Mod2.py +++ b/regression/scripts/Vehicles/X57_Maxwell_Mod2.py @@ -17,8 +17,7 @@ from SUAVE.Methods.Power.Battery.Sizing import initialize_from_mass from SUAVE.Methods.Propulsion.electric_motor_sizing import size_optimal_motor from SUAVE.Methods.Geometry.Two_Dimensional.Planform import wing_segmented_planform -from SUAVE.Methods.Geometry.Two_Dimensional.Cross_Section.Airfoil.import_airfoil_geometry import import_airfoil_geometry -from SUAVE.Methods.Geometry.Two_Dimensional.Cross_Section.Airfoil.compute_airfoil_polars import compute_airfoil_polars + import numpy as np from copy import deepcopy import os @@ -420,31 +419,30 @@ def vehicle_setup(): # Component 2 the Propeller prop = SUAVE.Components.Energy.Converters.Propeller() - prop.tag = 'propeller_1' - prop.number_of_blades = 3.0 - prop.freestream_velocity = 150. * Units.knots - prop.angular_velocity = 2400. * Units.rpm - prop.tip_radius = 1.72/2 - prop.hub_radius = 10. * Units.inches - prop.design_Cl = 0.8 - prop.design_altitude = 9000. * Units.feet - prop.design_power = 98 * 0.65 * Units.hp # assume 65 BHP at cruise - prop.origin = [[2.,2.5,0.784]] - prop.rotation = -1 - prop.symmetry = True - prop.variable_pitch = True - - airfoil_geometry = [base + '/Airfoils/NACA_4412.txt'] - airfoil_polars = [[base + '/Airfoils/Polars/NACA_4412_polar_Re_50000.txt' , - base + '/Airfoils/Polars/NACA_4412_polar_Re_100000.txt' , - base + '/Airfoils/Polars/NACA_4412_polar_Re_200000.txt' , - base + '/Airfoils/Polars/NACA_4412_polar_Re_500000.txt' , - base + '/Airfoils/Polars/NACA_4412_polar_Re_1000000.txt' ]] - - prop.airfoil_geometry_data = import_airfoil_geometry(airfoil_geometry) - prop.airfoil_polar_data = compute_airfoil_polars(airfoil_polars, prop.airfoil_geometry_data) - prop.airfoil_polar_stations = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] - prop = propeller_design(prop) + prop.tag = 'propeller_1' + prop.number_of_blades = 3.0 + prop.freestream_velocity = 150. * Units.knots + prop.angular_velocity = 2400. * Units.rpm + prop.tip_radius = 1.72/2 + prop.hub_radius = 10. * Units.inches + prop.design_Cl = 0.8 + prop.design_altitude = 9000. * Units.feet + prop.design_power = 98 * 0.65 * Units.hp # assume 65 BHP at cruise + prop.origin = [[2.,2.5,0.784]] + prop.rotation = -1 + prop.symmetry = True + prop.variable_pitch = True + airfoil = SUAVE.Components.Airfoils.Airfoil() + airfoil.number_of_points = 102 + airfoil.coordinate_file = '../Vehicles/Airfoils/NACA_4412.txt' + airfoil.polar_files = ['../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_50000.txt' , + '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_100000.txt' , + '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_200000.txt' , + '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_500000.txt' , + '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_1000000.txt' ] + prop.append_airfoil(airfoil) + prop.airfoil_polar_stations = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] + prop = propeller_design(prop) prop_left = deepcopy(prop) prop_left.tag = 'propeller_2' @@ -519,10 +517,8 @@ def configs_setup(vehicle): configs = SUAVE.Components.Configs.Config.Container() base_config = SUAVE.Components.Configs.Config(vehicle) - base_config.tag = 'base' - base_config.networks.battery_propeller.pitch_command = 0 - configs.append(base_config) - + base_config.tag = 'base' + configs.append(base_config) # done! return configs diff --git a/regression/scripts/airfoil_analysis/Clark_y_cd_sweep.npy b/regression/scripts/airfoil_analysis/Clark_y_cd_sweep.npy deleted file mode 100644 index 456ff88721..0000000000 Binary files a/regression/scripts/airfoil_analysis/Clark_y_cd_sweep.npy and /dev/null differ diff --git a/regression/scripts/airfoil_analysis/Clark_y_cl_sweep.npy b/regression/scripts/airfoil_analysis/Clark_y_cl_sweep.npy deleted file mode 100644 index 6a568df612..0000000000 Binary files a/regression/scripts/airfoil_analysis/Clark_y_cl_sweep.npy and /dev/null differ diff --git a/regression/scripts/airfoil_analysis/Clark_y_xc.npy b/regression/scripts/airfoil_analysis/Clark_y_xc.npy deleted file mode 100644 index 29524abab6..0000000000 Binary files a/regression/scripts/airfoil_analysis/Clark_y_xc.npy and /dev/null differ diff --git a/regression/scripts/airfoil_analysis/airfoil_panel_method_test.py b/regression/scripts/airfoil_analysis/airfoil_panel_method_test.py index f2b52355e1..3dfdc58f8f 100644 --- a/regression/scripts/airfoil_analysis/airfoil_panel_method_test.py +++ b/regression/scripts/airfoil_analysis/airfoil_panel_method_test.py @@ -6,7 +6,7 @@ #---------------------------------------------------------------------- # Imports # --------------------------------------------------------------------- -from SUAVE.Core import Units, Data +from SUAVE.Core import Units from SUAVE.Methods.Aerodynamics.Airfoil_Panel_Method.airfoil_analysis import airfoil_analysis import matplotlib.pyplot as plt from SUAVE.Methods.Geometry.Two_Dimensional.Cross_Section.Airfoil.compute_naca_4series \ @@ -21,75 +21,74 @@ # Main # ---------------------------------------------------------------------- -def main(): - # Define Panelization - npanel = 200 - +def main(): # ----------------------------------------------- # Batch analysis of single airfoil - NACA 2410 # ----------------------------------------------- - Re_batch = np.atleast_2d(np.array([1E5,2E5])).T - AoA_batch = np.atleast_2d(np.linspace(-5,10,4)*Units.degrees).T - airfoil_geometry = compute_naca_4series(0.02,0.4,0.1,npoints=npanel) - airfoil_properties_1 = airfoil_analysis(airfoil_geometry,AoA_batch,Re_batch, npanel, batch_analysis = True) + Re_vals = np.atleast_2d(np.array([1E5,1E5,1E5,1E5])) + AoA_vals = np.atleast_2d(np.linspace(-5,10,4)*Units.degrees) + airfoil_file_1 = '2412' + airfoil_geometry_1 = compute_naca_4series(airfoil_file_1,npoints = 200) + airfoil_properties_1 = airfoil_analysis(airfoil_geometry_1,AoA_vals,Re_vals) # Plots - plot_airfoil_analysis_polars(airfoil_properties_1,show_legend = True ) - plot_airfoil_analysis_surface_forces(airfoil_properties_1,show_legend = True ) - plot_airfoil_analysis_boundary_layer_properties(airfoil_properties_1,show_legend = True ) + plot_airfoil_surface_forces(airfoil_properties_1,show_legend = True ) + plot_airfoil_boundary_layer_properties(airfoil_properties_1,show_legend = True ) - # XFOIL Validation - Source : - xfoil_data_Cl = 0.7922 - xfoil_data_Cd = 0.01588 - xfoil_data_Cm = -0.0504 + # XFOIL Validation - Source + xfoil_data_cl = 0.803793 + xfoil_data_cd = 0.017329 + xfoil_data_cm = -0.053745 - diff_CL = np.abs(airfoil_properties_1.Cl[2,0] - xfoil_data_Cl) - expected_Cl_error = -0.01801472136594917 + diff_CL = np.abs(airfoil_properties_1.cl[0,2] - xfoil_data_cl) + expected_cl_error = 0.0051685845759998905 print('\nCL difference') print(diff_CL) - assert np.abs(((airfoil_properties_1.Cl[2,0]- expected_Cl_error) - xfoil_data_Cl)/xfoil_data_Cl) < 1e-6 + assert np.abs(((airfoil_properties_1.cl[0,2] - expected_cl_error) - xfoil_data_cl)/xfoil_data_cl) < 1e-6 - diff_CD = np.abs(airfoil_properties_1.Cd[2,0] - xfoil_data_Cd) - expected_Cd_error = -0.00012125071270768784 - print('\nCD difference') + + diff_CD = np.abs(airfoil_properties_1.cd[0,2] - xfoil_data_cd) + expected_cd_error = 0.00022154375589937547 + print('\nCDpi difference') print(diff_CD) - assert np.abs(((airfoil_properties_1.Cd[2,0]- expected_Cd_error) - xfoil_data_Cd)/xfoil_data_Cd) < 1e-6 - - diff_CM = np.abs(airfoil_properties_1.Cm[2,0] - xfoil_data_Cm) - expected_Cm_error = -0.002782281182096287 + assert np.abs(((airfoil_properties_1.cd[0,2]- expected_cd_error) - xfoil_data_cd)/xfoil_data_cd) < 1e-6 + + + diff_CM = np.abs(airfoil_properties_1.cm[0,2] - xfoil_data_cm) + expected_cm_error = -0.00566693670655781 print('\nCM difference') print(diff_CM) - assert np.abs(((airfoil_properties_1.Cm[2,0]- expected_Cm_error) - xfoil_data_Cm)/xfoil_data_Cm) < 1e-6 + assert np.abs(((airfoil_properties_1.cm[0,2]- expected_cm_error) - xfoil_data_cm)/xfoil_data_cm) < 1e-6 + # ----------------------------------------------- # Single Condition Analysis of multiple airfoils # ----------------------------------------------- - ospath = os.path.abspath(__file__) - separator = os.path.sep - rel_path = ospath.split('airfoil_analysis' + separator + 'airfoil_panel_method_test.py')[0] + 'Vehicles' + separator + 'Airfoils' + separator - Re_vals = np.atleast_2d(np.array([1E5,1E5,1E5,1E5,1E5,1E5])).T - AoA_vals = np.atleast_2d(np.array([2,2,2,2,2,2])*Units.degrees).T - airfoil_stations = [0,1,0,1,0,1] - airfoils = [rel_path + 'NACA_4412.txt',rel_path +'Clark_y.txt'] - airfoil_geometry = import_airfoil_geometry(airfoils, npoints = (npanel + 2)) - airfoil_properties_2 = airfoil_analysis(airfoil_geometry,AoA_vals,Re_vals, npanel, batch_analysis = False, airfoil_stations = airfoil_stations) + ospath = os.path.abspath(__file__) + separator = os.path.sep + rel_path = ospath.split('airfoil_analysis' + separator + 'airfoil_panel_method_test.py')[0] + 'Vehicles' + separator + 'Airfoils' + separator + Re_vals = np.atleast_2d(np.array([[1E5,1E5,1E5,1E5,1E5,1E5],[2E5,2E5,2E5,2E5,2E5,2E5]])) + AoA_vals = np.atleast_2d(np.array([[0,1,2,3,4,5],[0,1,2,3,4,5]])*Units.degrees) + airfoil_file_2 = rel_path + 'NACA_4412.txt' + airfoil_geometry_2 = import_airfoil_geometry(airfoil_file_2,npoints = 200) + airfoil_properties_2 = airfoil_analysis(airfoil_geometry_2,AoA_vals,Re_vals) - True_Cls = np.array([[0.68788905],[0.60906619],[0.68788905],[0.60906619],[0.68788905],[0.60906619]]) - True_Cds = np.array([[0.01015395],[0.00846174],[0.01015395],[0.00846174],[0.01015395],[0.00846174]]) - True_Cms = np.array([[-0.10478782],[-0.08727453],[-0.10478782],[-0.08727453],[-0.10478782],[-0.08727453]]) + True_cls = np.array([0.43894783, 0.54740563, 0.65581723, 0.764182 , 0.87244463, 0.98056708]) + True_cd = np.array([0.01068774, 0.0114142 , 0.01224437, 0.01315219, 0.01419829, 0.01541621]) + True_cms = np.array([-0.09880519, -0.09893714, -0.09905913, -0.09922631, -0.09931107,-0.09937669]) print('\n\nSingle Point Validation') print('\nCL difference') - print(np.sum(np.abs((airfoil_properties_2.Cl - True_Cls)/True_Cls))) - assert np.sum(np.abs((airfoil_properties_2.Cl - True_Cls)/True_Cls)) < 1e-5 + print(np.sum(np.abs((airfoil_properties_2.cl[0] - True_cls)/True_cls))) + assert np.sum(np.abs((airfoil_properties_2.cl[0] - True_cls)/True_cls)) < 1e-5 print('\nCD difference') - print(np.sum(np.abs((airfoil_properties_2.Cd - True_Cds)/True_Cds))) - assert np.sum(np.abs((airfoil_properties_2.Cd - True_Cds)/True_Cds)) < 1e-5 + print(np.sum(np.abs((airfoil_properties_2.cd[0] - True_cd)/True_cd))) + assert np.sum(np.abs((airfoil_properties_2.cd[0] - True_cd)/True_cd)) < 1e-5 print('\nCM difference') - print(np.sum(np.abs((airfoil_properties_2.Cm - True_Cms)/True_Cms))) - assert np.sum(np.abs((airfoil_properties_2.Cm - True_Cms)/True_Cms)) < 1e-5 + print(np.sum(np.abs((airfoil_properties_2.cm[0] - True_cms)/True_cms))) + assert np.sum(np.abs((airfoil_properties_2.cm[0] - True_cms)/True_cms)) < 1e-5 return diff --git a/regression/scripts/airfoil_analysis/airfoil_polar_surrogate_test.py b/regression/scripts/airfoil_analysis/airfoil_polar_surrogate_test.py deleted file mode 100644 index 37b5888aa0..0000000000 --- a/regression/scripts/airfoil_analysis/airfoil_polar_surrogate_test.py +++ /dev/null @@ -1,75 +0,0 @@ -# airfoil_polar_surrogate_test.py -# -# Created: Aug 2022, R. Erhard -# Modified: -from SUAVE.Methods.Geometry.Two_Dimensional.Cross_Section.Airfoil.compute_airfoil_polars import compute_airfoil_polars -from SUAVE.Methods.Geometry.Two_Dimensional.Cross_Section.Airfoil.import_airfoil_geometry import import_airfoil_geometry -from SUAVE.Plots.Performance.Airfoil_Plots import plot_airfoil_polars, plot_raw_data_airfoil_polars -from SUAVE.Core import Units -import numpy as np -import os -import pylab as plt - -def main(): - """ - This script generates airfoil surrogates for three airfoils, and plots the - surrogate data over a range of alpha and Reynolds number sweeps. - """ - airfoils_path = os.path.dirname(os.path.abspath(__file__)) + "/../Vehicles/Airfoils/" - polars_path = os.path.dirname(os.path.abspath(__file__)) + "/../Vehicles/Airfoils/Polars/" - - airfoils = ["Clark_y.txt", "NACA_63_412.txt", "NACA_4412.txt"] - - airfoil_geometry_files = [] - airfoil_polar_files = [] - - for a in airfoils: - - airfoil_geometry_files.append(airfoils_path + a ) - aName = a[:-4] - - airfoil_polars = [] - Re_val = [] - for f in os.listdir(polars_path): - if aName in f and f.endswith('.txt'): - airfoil_polars.append(polars_path + f) - Re_val.append(float(f.split('_')[-1][:-4])) - - # sort by Re number of polars - sorted_ids = np.argsort(Re_val) - airfoil_polar_files.append([airfoil_polars[i] for i in sorted_ids]) - - airfoil_geometry_data = import_airfoil_geometry(airfoil_geometry_files) - airfoil_polar_data = compute_airfoil_polars(airfoil_polar_files, airfoil_geometry_data, use_pre_stall_data=False) - aoa_sweep = np.linspace(-20,20,100) * Units.deg - Re_sweep = np.array([0.05, 0.1, 0.2, 0.5, 1., 3.5, 5., 7.5]) * 1e6 - #Re_sweep = np.array([0.05, 0.1, 0.2, 0.5, 1.]) * 1e6 - plot_airfoil_polars(airfoil_polar_data, aoa_sweep, Re_sweep) - plot_raw_data_airfoil_polars(airfoil_polar_data.airfoil_names, airfoil_polar_files) - - - # ----------------------------------------------------------------------------------------- - # Regression comparison - # ----------------------------------------------------------------------------------------- - xc = airfoil_geometry_data.x_coordinates[airfoil_geometry_data.airfoil_names[0]] - cl_outputs = airfoil_polar_data.lift_coefficient_surrogates[airfoil_polar_data.airfoil_names[0]]((Re_sweep[0], aoa_sweep)) - cd_outputs = airfoil_polar_data.drag_coefficient_surrogates[airfoil_polar_data.airfoil_names[0]]((Re_sweep[0], aoa_sweep)) - - ## store new regression data (LEAVE COMMENTED OUT) - #np.save(airfoil_geometry_data.airfoil_names[0]+"_xc", airfoil_geometry_data.x_coordinates[airfoil_geometry_data.airfoil_names[0]]) - #np.save(airfoil_polar_data.airfoil_names[0]+"_cl_sweep", cl_outputs) - #np.save(airfoil_polar_data.airfoil_names[0]+"_cd_sweep", cd_outputs) - - # regress coordinate values - xc_true = np.load(airfoil_geometry_data.airfoil_names[0]+"_xc.npy") - cl_outputs_true = np.load(airfoil_polar_data.airfoil_names[0]+"_cl_sweep.npy") - cd_outputs_true = np.load(airfoil_polar_data.airfoil_names[0]+"_cd_sweep.npy") - assert(np.max(abs(xc-xc_true)) < 1e-6) - assert(np.max(abs(cl_outputs-cl_outputs_true)) < 1e-6) - assert(np.max(abs(cd_outputs-cd_outputs_true)) < 1e-6) - - plt.show() - return - -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/regression/scripts/airfoil_import/airfoil_import_test.py b/regression/scripts/airfoil_import/airfoil_import_test.py index 4bbd859dae..8a6beb8877 100644 --- a/regression/scripts/airfoil_import/airfoil_import_test.py +++ b/regression/scripts/airfoil_import/airfoil_import_test.py @@ -14,8 +14,8 @@ import matplotlib.pyplot as plt from SUAVE.Methods.Geometry.Two_Dimensional.Cross_Section.Airfoil.import_airfoil_geometry\ import import_airfoil_geometry -from SUAVE.Methods.Geometry.Two_Dimensional.Cross_Section.Airfoil.import_airfoil_polars \ - import import_airfoil_polars +from SUAVE.Methods.Geometry.Two_Dimensional.Cross_Section.Airfoil.compute_airfoil_properties \ + import compute_airfoil_properties from SUAVE.Plots.Performance.Airfoil_Plots import * import os import numpy as np @@ -24,42 +24,51 @@ # Main # ---------------------------------------------------------------------- -def main(): +def main(): + # ---------------------------------------------------------------------------------------------------------------- + # Define airfoil geometry and polar files + # ---------------------------------------------------------------------------------------------------------------- ospath = os.path.abspath(__file__) - separator = os.path.sep - + separator = os.path.sep rel_path = ospath.split('airfoil_import' + separator + 'airfoil_import_test.py')[0] + 'Vehicles' + separator + 'Airfoils' + separator airfoil_geometry_with_selig = [rel_path + 'NACA_4412.txt','airfoil_geometry_2.txt', 'airfoil_geometry_2-selig.txt'] - airfoil_geometry = [rel_path + 'NACA_4412.txt'] - airfoil_polar_names = [[rel_path + 'Polars' + separator + 'NACA_4412_polar_Re_50000.txt', + airfoil_geometry_files = rel_path + 'NACA_4412.txt' + airfoil_polar_files = [rel_path + 'Polars' + separator + 'NACA_4412_polar_Re_50000.txt', rel_path + 'Polars' + separator + 'NACA_4412_polar_Re_100000.txt', rel_path + 'Polars' + separator + 'NACA_4412_polar_Re_200000.txt', rel_path + 'Polars' + separator + 'NACA_4412_polar_Re_500000.txt', - rel_path + 'Polars' + separator + 'NACA_4412_polar_Re_1000000.txt']] - + rel_path + 'Polars' + separator + 'NACA_4412_polar_Re_1000000.txt'] - # plot airfoil polar data with and without surrogate - plot_raw_data_airfoil_polars(airfoil_geometry, airfoil_polar_names, display_plot=True) - airfoil_geometry_data = import_airfoil_geometry(airfoil_geometry_with_selig) - # Actual t/c values - airfoil_tc_actual = [0.12031526401402462, 0.11177063928743779, 0.11177063928743779] + # ---------------------------------------------------------------------------------------------------------------- + # Plot polar files + # ---------------------------------------------------------------------------------------------------------------- + # plot airfoil polar data with and without surrogate + airfoil_geometry_1 = import_airfoil_geometry(airfoil_geometry_files) + airfoil_polar_data_1 = compute_airfoil_properties(airfoil_geometry_1,airfoil_polar_files) + plot_airfoil_polar_files(airfoil_polar_data_1) + # ---------------------------------------------------------------------------------------------------------------- + # + # ---------------------------------------------------------------------------------------------------------------- + airfoil_geometry_2 = import_airfoil_geometry(airfoil_geometry_with_selig[0]) + airfoil_geometry_3 = import_airfoil_geometry(airfoil_geometry_with_selig[1]) + airfoil_geometry_4 = import_airfoil_geometry(airfoil_geometry_with_selig[2]) - # Check t/c calculation against previously calculated values + # Actual t/c values + airfoil_tc_actual = [0.12031526401402462, 0.11177619218206997, 0.11177619218206997] - aNames = airfoil_geometry_data.airfoil_names - for i in range(0, len(airfoil_geometry_with_selig)): - assert(np.abs(airfoil_tc_actual[i]-airfoil_geometry_data.thickness_to_chord[aNames[i]]) < 1E-8 ) - + # Check t/c calculation against previously calculated values + assert(np.abs(airfoil_tc_actual[0]-airfoil_geometry_2.thickness_to_chord) < 1E-8 ) + assert(np.abs(airfoil_tc_actual[1]-airfoil_geometry_3.thickness_to_chord) < 1E-8 ) + assert(np.abs(airfoil_tc_actual[2]-airfoil_geometry_4.thickness_to_chord) < 1E-8 ) - # Check that camber line comes back the same for the Lednicer and Selig formats - for j in range(0, len(airfoil_geometry_data.camber_coordinates[aNames[1]])): - assert( np.abs(airfoil_geometry_data.camber_coordinates[aNames[1]][j] - airfoil_geometry_data.camber_coordinates[aNames[2]][j]) < 1E-8 ) + # Check that camber line comes back the same for the Lednicer and Selig formats + for j in range(0, len(airfoil_geometry_3.camber_coordinates)): + assert( np.abs(airfoil_geometry_3.camber_coordinates[j] - airfoil_geometry_4.camber_coordinates[j]) < 1E-8 ) - plot_airfoil(airfoil_geometry_with_selig) - + plot_airfoil(airfoil_geometry_with_selig[0]) return diff --git a/regression/scripts/airfoil_import/airfoil_interpolation_test.py b/regression/scripts/airfoil_import/airfoil_interpolation_test.py index 9bfa899420..c1bf3205a6 100644 --- a/regression/scripts/airfoil_import/airfoil_interpolation_test.py +++ b/regression/scripts/airfoil_import/airfoil_interpolation_test.py @@ -18,21 +18,21 @@ def main(): a_labels = ["Clark_y", "E63"] nairfoils = 4 # number of total airfoils - a1 = airfoils_path + a_labels[0]+ ".txt" - a2 = airfoils_path + a_labels[1]+ ".txt" - new_files = generate_interpolated_airfoils(a1, a2, nairfoils,npoints=100,save_filename="Transition") + a1 = airfoils_path + a_labels[0]+ ".txt" + a2 = airfoils_path + a_labels[1]+ ".txt" + new_files = generate_interpolated_airfoils(a1, a2, nairfoils,npoints=100,save_filename="Transition") # import the new airfoil geometries and compare to the regression: - airfoil_data_1 = import_airfoil_geometry([new_files['a_1'].name],npoints=100) - airfoil_data_2 = import_airfoil_geometry([new_files['a_2'].name],npoints=100) - airfoil_data_1_r = import_airfoil_geometry(["Transition1_regression.txt"],npoints=100) - airfoil_data_2_r = import_airfoil_geometry(["Transition2_regression.txt"],npoints=100) + airfoil_data_1 = import_airfoil_geometry(new_files['a_1'].name,npoints=100) + airfoil_data_2 = import_airfoil_geometry(new_files['a_2'].name,npoints=100) + airfoil_data_1_r = import_airfoil_geometry("Transition1_regression.txt",npoints=100) + airfoil_data_2_r = import_airfoil_geometry("Transition2_regression.txt",npoints=100) # ensure coordinates are the same: - assert( max(abs(airfoil_data_1.x_coordinates[airfoil_data_1.airfoil_names[0]] - airfoil_data_1_r.x_coordinates[airfoil_data_1_r.airfoil_names[0]])) < 1e-5) - assert( max(abs(airfoil_data_2.x_coordinates[airfoil_data_2.airfoil_names[0]] - airfoil_data_2_r.x_coordinates[airfoil_data_2_r.airfoil_names[0]])) < 1e-5) - assert( max(abs(airfoil_data_1.y_coordinates[airfoil_data_1.airfoil_names[0]] - airfoil_data_1_r.y_coordinates[airfoil_data_1_r.airfoil_names[0]])) < 1e-5) - assert( max(abs(airfoil_data_2.y_coordinates[airfoil_data_2.airfoil_names[0]] - airfoil_data_2_r.y_coordinates[airfoil_data_2_r.airfoil_names[0]])) < 1e-5) + assert( max(abs(airfoil_data_1.x_coordinates - airfoil_data_1_r.x_coordinates)) < 1e-5) + assert( max(abs(airfoil_data_2.x_coordinates - airfoil_data_2_r.x_coordinates)) < 1e-5) + assert( max(abs(airfoil_data_1.y_coordinates - airfoil_data_1_r.y_coordinates)) < 1e-5) + assert( max(abs(airfoil_data_2.y_coordinates - airfoil_data_2_r.y_coordinates)) < 1e-5) plt.show() diff --git a/regression/scripts/battery/aircraft_discharge_comparisons.py b/regression/scripts/battery/aircraft_discharge_comparisons.py index a5e9d90bee..3c8f4113bf 100644 --- a/regression/scripts/battery/aircraft_discharge_comparisons.py +++ b/regression/scripts/battery/aircraft_discharge_comparisons.py @@ -40,14 +40,14 @@ def main(): # General Aviation Aircraft - GA_RPM_true = [2287.4598939481502,2287.4598939025564] - GA_lift_coefficient_true = [0.5474716961975736,0.5474716961975756] + GA_RPM_true = [2285.8179503746774,2285.8179503693987] + GA_lift_coefficient_true = [0.5474716961975739,0.5474716961975754] # EVTOL Aircraft - EVTOL_RPM_true = [2404.3825064145894,2404.3825064589055] + EVTOL_RPM_true = [2404.363170876128,2404.363170937451] - EVTOL_lift_coefficient_true = [0.8075309358248256,0.8075309358231033] + EVTOL_lift_coefficient_true = [0.8075309358253244,0.8075309358231044] for i in range(len(battery_chemistry)): @@ -401,8 +401,7 @@ def EVTOL_mission_setup(analyses,vehicle): base_segment.state.numerics.number_control_points = 3 base_segment.process.initialize.initialize_battery = SUAVE.Methods.Missions.Segments.Common.Energy.initialize_battery base_segment.process.iterate.conditions.planet_position = SUAVE.Methods.skip - ones_row = base_segment.state.ones_row - + # VSTALL Calculation m = vehicle.mass_properties.max_takeoff g = 9.81 @@ -432,6 +431,55 @@ def EVTOL_mission_setup(analyses,vehicle): # add to misison mission.append_segment(segment) + # ------------------------------------------------------------------ + # First Cruise Segment: Transition + # ------------------------------------------------------------------ + segment = Segments.Transition.Constant_Acceleration_Constant_Pitchrate_Constant_Altitude(base_segment) + segment.tag = "transition_1" + segment.analyses.extend( analyses.base ) + + segment.altitude = 40. * Units.ft + segment.air_speed_start = 500. * Units['ft/min'] + segment.air_speed_end = 0.8 * Vstall + segment.acceleration = 9.8/5 + segment.pitch_initial = 0.0 * Units.degrees + segment.pitch_final = 5. * Units.degrees + ones_row = segment.state.ones_row + segment.state.unknowns.throttle = 0.95 * ones_row(1) + segment.process.iterate.unknowns.mission = SUAVE.Methods.skip + segment.process.iterate.conditions.stability = SUAVE.Methods.skip + segment.process.finalize.post_process.stability = SUAVE.Methods.skip + segment = vehicle.networks.lift_cruise.add_transition_unknowns_and_residuals_to_segment(segment, + initial_prop_power_coefficient = 0.2, + initial_lift_rotor_power_coefficient = 0.01, + initial_throttle_lift = 0.9,) + + # add to misison + mission.append_segment(segment) + + # ------------------------------------------------------------------ + # First Cruise Segment: Transition + # ------------------------------------------------------------------ + segment = Segments.Transition.Constant_Acceleration_Constant_Angle_Linear_Climb(base_segment) + segment.tag = "transition_2" + segment.analyses.extend( analyses.base ) + segment.altitude_start = 40.0 * Units.ft + segment.altitude_end = 50.0 * Units.ft + segment.climb_angle = 1 * Units.degrees + segment.acceleration = 0.5 * Units['m/s/s'] + segment.pitch_initial = 5. * Units.degrees + segment.pitch_final = 7. * Units.degrees + segment.state.unknowns.throttle = 0.95 * ones_row(1) + segment.process.iterate.unknowns.mission = SUAVE.Methods.skip + segment.process.iterate.conditions.stability = SUAVE.Methods.skip + segment.process.finalize.post_process.stability = SUAVE.Methods.skip + segment = vehicle.networks.lift_cruise.add_transition_unknowns_and_residuals_to_segment(segment, + initial_prop_power_coefficient = 0.2, + initial_lift_rotor_power_coefficient = 0.01, + initial_throttle_lift = 0.9,) + + # add to misison + mission.append_segment(segment) # ------------------------------------------------------------------ diff --git a/regression/scripts/electric_performance/electric_V_h_diagram.py b/regression/scripts/electric_performance/electric_V_h_diagram.py index b1d23fefdc..b33942cd90 100644 --- a/regression/scripts/electric_performance/electric_V_h_diagram.py +++ b/regression/scripts/electric_performance/electric_V_h_diagram.py @@ -71,12 +71,11 @@ def main(): display_plot=True) - climb_rate_r = [[ 0. , 0. , 0. , 0. , 0. ], - [ 0. , 0. , 0. , 0. , 0. ], - [491.38813465, 0. , 0. , 0. , 0. ], - [771.51391686, 665.32566422, 561.13375595, 458.55507621, 357.1938555 ], - [447.04545605, 375.77807358, 305.6388056 , 236.26265718, 167.24776149]] - + climb_rate_r = [[ 0. , 0. , 0. , 0. , 0. ], + [ 0. , 0. , 0. , 0. , 0. ], + [487.63179542, 0. , 0. , 0. , 0. ], + [772.92643016, 666.52055528, 562.06865597, 459.0544259 , 356.95140061], + [447.06298061, 375.79491086, 305.65495126, 236.2785227 , 167.26496564]] assert (np.all(np.nan_to_num(np.abs(climb_rate-climb_rate_r)/climb_rate_r) < 1e-6)), "Electric V_h Diagram Regression Failed" diff --git a/regression/scripts/electric_performance/electric_payload_range.py b/regression/scripts/electric_performance/electric_payload_range.py index 5b8a06f9c0..f8dd37f1a4 100644 --- a/regression/scripts/electric_performance/electric_payload_range.py +++ b/regression/scripts/electric_performance/electric_payload_range.py @@ -35,8 +35,8 @@ def main(): payload_range = electric_payload_range(vehicle, mission, 'cruise', display_plot=True) - payload_range_r = [ 0. , 105275.60817101, 112026.18114827] - + payload_range_r = [ 0. , 105741.71003496, 112657.67272721] + assert (np.abs(payload_range.range[1] - payload_range_r[1]) / payload_range_r[1] < 1e-6), "Payload Range Regression Failed at Max Payload Test" assert (np.abs(payload_range.range[2] - payload_range_r[2]) / payload_range_r[2] < 1e-6), "Payload Range Regression Failed at Ferry Range Test" diff --git a/regression/scripts/electric_performance/propeller_single_point.py b/regression/scripts/electric_performance/propeller_single_point.py index 248fdc0ba3..19f11275e8 100644 --- a/regression/scripts/electric_performance/propeller_single_point.py +++ b/regression/scripts/electric_performance/propeller_single_point.py @@ -58,11 +58,11 @@ def test_1(): Cp = results.power_coefficient etap = results.efficiency - thrust_r = 636.0320347016154 - torque_r = 130.22463212998505 - power_r = 30001.60149183959 - Cp_r = 0.03829965692345541 - etap_r = 0.21199936105898068 + thrust_r = 642.341629214397 + torque_r = 133.88658084221808 + power_r = 30845.25391113234 + Cp_r = 0.03937665270417546 + etap_r = 0.20824650400513317 assert (np.abs(thrust - thrust_r) / thrust_r < 1e-6), "Propeller Single Point Regression Failed at Thrust Test" @@ -100,11 +100,11 @@ def test_2(): Cp = results.power_coefficient etap = results.efficiency - thrust_r = 640.629147594485 - torque_r = 129.3237931843859 - power_r = 29794.063097511713 - Cp_r = 0.038034716090101416 - etap_r = 0.2150190611793354 + thrust_r = 645.2923978453605 + torque_r = 132.6762848678586 + power_r = 30566.421735406424 + Cp_r = 0.03902069915041507 + etap_r = 0.21111152735875854 assert (np.abs(thrust - thrust_r) / thrust_r < 1e-6), "Propeller Single Point Regression Failed at Thrust Test" diff --git a/regression/scripts/geometry/NACA_airfoil_compute.py b/regression/scripts/geometry/NACA_airfoil_compute.py index c50dc7c14a..60e52b8b58 100644 --- a/regression/scripts/geometry/NACA_airfoil_compute.py +++ b/regression/scripts/geometry/NACA_airfoil_compute.py @@ -21,24 +21,21 @@ def main(): # ------------------------------------------------------------------ # Testing # Using NACA 2410 - # ------------------------------------------------------------------ - camber = 0.02 - camber_loc = 0.4 - thickness = 0.10 - npoints = 10 - airfoil_data = compute_naca_4series(camber, camber_loc, thickness,npoints) + # ------------------------------------------------------------------ + npoints = 11 + airfoil_data = compute_naca_4series('2410',npoints) - truth_upper_x = np.array([0. , 0.08654358, 0.25116155, 0.46508794, 0.71656695,1. ]) - truth_lower_x = np.array([0. , 0.09234186, 0.25480288, 0.46442806, 0.71451656,1.]) - truth_upper_y = np.array([0. , 0.04528598, 0.06683575, 0.06561866, 0.04370913,0.]) - truth_lower_y = np.array([0., -0.02939744, -0.03223931, -0.02608462, -0.01477209, 0.]) + truth_upper_x = np.array([0. , 0.06979572, 0.2563872 , 0.5193338 , 0.80322602, 1. ]) + truth_lower_x = np.array([0. , 0.06979572, 0.2563872 , 0.5193338 , 0.80322602, 1. ]) + truth_upper_y = np.array([0. , 0.04038307, 0.06705705, 0.06227549, 0.03252813, 0.00105 ]) + truth_lower_y = np.array([0. , -0.02764178, -0.03221321, -0.02385778, -0.01059382, -0.00105 ]) # Compute Errors error = Data() - error.upper_x = np.abs( airfoil_data.x_upper_surface[airfoil_data.airfoil_names[0]] - truth_upper_x) - error.lower_x = np.abs( airfoil_data.x_lower_surface[airfoil_data.airfoil_names[0]] - truth_lower_x) - error.upper_y = np.abs( airfoil_data.y_upper_surface[airfoil_data.airfoil_names[0]] - truth_upper_y) - error.lower_y = np.abs( airfoil_data.y_lower_surface[airfoil_data.airfoil_names[0]] - truth_lower_y) + error.upper_x = np.abs( airfoil_data.x_upper_surface[0] - truth_upper_x) + error.lower_x = np.abs( airfoil_data.x_lower_surface[0] - truth_lower_x) + error.upper_y = np.abs( airfoil_data.y_upper_surface[0] - truth_upper_y) + error.lower_y = np.abs( airfoil_data.y_lower_surface[0] - truth_lower_y) for k,v in list(error.items()): assert np.any(np.abs(v)<1e-6) diff --git a/regression/scripts/internal_combustion_propeller/ICE_CS_Test.py b/regression/scripts/internal_combustion_propeller/ICE_CS_Test.py index 1983d1bff5..d4628e2ca9 100644 --- a/regression/scripts/internal_combustion_propeller/ICE_CS_Test.py +++ b/regression/scripts/internal_combustion_propeller/ICE_CS_Test.py @@ -25,8 +25,7 @@ from Cessna_172 import vehicle_setup from SUAVE.Methods.Propulsion import propeller_design -from SUAVE.Methods.Geometry.Two_Dimensional.Cross_Section.Airfoil.import_airfoil_geometry import import_airfoil_geometry -from SUAVE.Methods.Geometry.Two_Dimensional.Cross_Section.Airfoil.compute_airfoil_polars import compute_airfoil_polars + # ---------------------------------------------------------------------- # Main # ---------------------------------------------------------------------- @@ -47,8 +46,8 @@ def main(): # evaluate results = mission.evaluate() - P_truth = 53598.69844995636 - mdot_truth = 0.004709304352257224 + P_truth = 53595.133857796805 + mdot_truth = 0.004708991159029469 P = results.segments.cruise.state.conditions.propulsion.power[-1,0] mdot = results.segments.cruise.state.conditions.weights.vehicle_mass_rate[-1,0] @@ -98,18 +97,16 @@ def ICE_CS(vehicle): prop.hub_radius = 8. * Units.inches prop.design_Cl = 0.8 prop.design_altitude = 12000. * Units.feet - prop.design_power = .64 * 180. * Units.horsepower - - airfoil_geometry = ['../Vehicles/Airfoils/NACA_4412.txt'] - airfoil_polars = [['../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_50000.txt' , - '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_100000.txt' , - '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_200000.txt' , - '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_500000.txt' , - '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_1000000.txt' ]] - - prop.airfoil_geometry_data = import_airfoil_geometry(airfoil_geometry) - prop.airfoil_polar_data = compute_airfoil_polars(airfoil_polars, prop.airfoil_geometry_data) - prop.airfoil_polar_stations = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] + prop.design_power = .64 * 180. * Units.horsepower + airfoil = SUAVE.Components.Airfoils.Airfoil() + airfoil.coordinate_file = '../Vehicles/Airfoils/NACA_4412.txt' + airfoil.polar_files = ['../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_50000.txt' , + '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_100000.txt' , + '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_200000.txt' , + '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_500000.txt' , + '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_1000000.txt' ] + prop.append_airfoil(airfoil) + prop.airfoil_polar_stations = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] prop = propeller_design(prop) net.propellers.append(prop) diff --git a/regression/scripts/internal_combustion_propeller/ICE_Test.py b/regression/scripts/internal_combustion_propeller/ICE_Test.py index 2663b7453c..77e1ee1f8a 100644 --- a/regression/scripts/internal_combustion_propeller/ICE_Test.py +++ b/regression/scripts/internal_combustion_propeller/ICE_Test.py @@ -43,8 +43,8 @@ def main(): h = 0.008757244664175039 - P_truth = 53543.63489042103 - mdot_truth = 0.004704466341856426 + P_truth = 53537.39729983448 + mdot_truth = 0.004703918292867424 P = results.segments.cruise.state.conditions.propulsion.power[-1,0] mdot = results.segments.cruise.state.conditions.weights.vehicle_mass_rate[-1,0] diff --git a/regression/scripts/motor/motor_test.py b/regression/scripts/motor/motor_test.py index f7f941a7c7..f2f4b269cc 100644 --- a/regression/scripts/motor/motor_test.py +++ b/regression/scripts/motor/motor_test.py @@ -14,8 +14,6 @@ from SUAVE.Core import ( Data, Container, ) -from SUAVE.Methods.Geometry.Two_Dimensional.Cross_Section.Airfoil.import_airfoil_geometry import import_airfoil_geometry -from SUAVE.Methods.Geometry.Two_Dimensional.Cross_Section.Airfoil.compute_airfoil_polars import compute_airfoil_polars from SUAVE.Methods.Propulsion.electric_motor_sizing import size_from_mass , size_optimal_motor from SUAVE.Methods.Propulsion import propeller_design import numpy as np @@ -35,17 +33,16 @@ def main(): prop.design_altitude = 0.0 * Units.km prop.design_thrust = 2271.2220451593753 - airfoil_geometry = ['../Vehicles/Airfoils/NACA_4412.txt'] - airfoil_polars = [['../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_50000.txt' , - '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_100000.txt' , - '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_200000.txt' , - '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_500000.txt' , - '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_1000000.txt' ]] - - prop.airfoil_geometry_data = import_airfoil_geometry(airfoil_geometry) - prop.airfoil_polar_data = compute_airfoil_polars(airfoil_polars, prop.airfoil_geometry_data) - prop.airfoil_polar_stations = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] - prop = propeller_design(prop) + airfoil = SUAVE.Components.Airfoils.Airfoil() + airfoil.coordinate_file = '../Vehicles/Airfoils/NACA_4412.txt' + airfoil.polar_files = ['../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_50000.txt' , + '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_100000.txt' , + '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_200000.txt' , + '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_500000.txt' , + '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_1000000.txt' ] + prop.append_airfoil(airfoil) + prop.airfoil_polar_stations = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] + prop = propeller_design(prop) # Motor #------------------------------------------------------------------ @@ -153,12 +150,12 @@ def main(): current = i[0][0] # Truth values - omega_1_truth = 197.3149623 - torque_1_truth = 934.4429475025574 - current_2_truth = 572.2417638433878 - torque_3_truth = 803.2824292690289 - voltage_4_truth = 417.0794861805669 - current_4_truth = 665.3512388410677 + omega_1_truth = 197.31166405767365 + torque_1_truth = 934.4117082048164 + current_2_truth = 572.2006877833089 + torque_3_truth = 803.2247688894328 + voltage_4_truth = 417.0841366120573 + current_4_truth = 665.3288953823401 power_out_truth = 1960.0 error = Data() diff --git a/regression/scripts/multifidelity/f_data.npy b/regression/scripts/multifidelity/f_data.npy index a258f5615c..973e7e1628 100644 Binary files a/regression/scripts/multifidelity/f_data.npy and b/regression/scripts/multifidelity/f_data.npy differ diff --git a/regression/scripts/multifidelity/x_samples.npy b/regression/scripts/multifidelity/x_samples.npy index 796e1dc32c..e9095495a9 100644 Binary files a/regression/scripts/multifidelity/x_samples.npy and b/regression/scripts/multifidelity/x_samples.npy differ diff --git a/regression/scripts/noise_fidelity_one/aircraft_noise.py b/regression/scripts/noise_fidelity_one/aircraft_noise.py index 3ae035eda8..3070eca8cd 100644 --- a/regression/scripts/noise_fidelity_one/aircraft_noise.py +++ b/regression/scripts/noise_fidelity_one/aircraft_noise.py @@ -55,7 +55,7 @@ def main(): print('\n\n SUAVE Frequency Domain Propeller Aircraft Noise Model') X57_SPL = X57_results.segments.departure_end_of_runway.conditions.noise.total_SPL_dBA[0][0] - X57_SPL_true = 66.26047007599651 + X57_SPL_true = 66.26373125376338 print(X57_SPL) X57_diff_SPL = np.abs(X57_SPL - X57_SPL_true) diff --git a/regression/scripts/noise_fidelity_one/propeller_noise.py b/regression/scripts/noise_fidelity_one/propeller_noise.py index 84fca9e588..5d88958bf9 100644 --- a/regression/scripts/noise_fidelity_one/propeller_noise.py +++ b/regression/scripts/noise_fidelity_one/propeller_noise.py @@ -3,18 +3,24 @@ # Imports import SUAVE from SUAVE.Core import Units, Data -from SUAVE.Components.Energy.Networks.Battery_Propeller import Battery_Propeller -from SUAVE.Methods.Geometry.Two_Dimensional.Cross_Section.Airfoil.compute_airfoil_polars import compute_airfoil_polars -from SUAVE.Methods.Geometry.Two_Dimensional.Cross_Section.Airfoil.import_airfoil_geometry import import_airfoil_geometry -from SUAVE.Methods.Noise.Fidelity_One.Propeller.propeller_mid_fidelity import propeller_mid_fidelity -from SUAVE.Analyses.Mission.Segments.Conditions import Aerodynamics , Conditions -from SUAVE.Analyses.Mission.Segments.Segment import Segment +from SUAVE.Components.Energy.Networks.Battery_Propeller import Battery_Propeller +from SUAVE.Methods.Geometry.Two_Dimensional.Cross_Section.Airfoil.compute_airfoil_properties import compute_airfoil_properties +from SUAVE.Methods.Geometry.Two_Dimensional.Cross_Section.Airfoil.compute_naca_4series import compute_naca_4series +from SUAVE.Methods.Noise.Fidelity_One.Propeller.propeller_mid_fidelity import propeller_mid_fidelity +from SUAVE.Analyses.Mission.Segments.Conditions import Aerodynamics , Conditions +from SUAVE.Analyses.Mission.Segments.Segment import Segment from scipy.interpolate import interp1d -# Python Imports -import os +# Python Imports +import sys import numpy as np -import matplotlib.pyplot as plt +import matplotlib.pyplot as plt + +sys.path.append('../Vehicles/Propellers') +# the analysis functions + +from F8745_D4_Propeller import F8745_D4_Propeller +from APC_11x4_Propeller import APC_11x4_Propeller # ---------------------------------------------------------------------- # Main # ---------------------------------------------------------------------- @@ -22,11 +28,50 @@ def main(): '''This regression script is for validation and verification of the mid-fidelity acoustics analysis routine. "Experimental Data is obtained from Comparisons of predicted propeller noise with windtunnel ..." by Weir, D and Powers, J. - ''' + ''' + + + # ---------------------------------------------------------------------------------------------------------------------------------------- + # Plots + # ---------------------------------------------------------------------------------------------------------------------------------------- + # Universal Plot Settings + plt.rcParams.update({'font.size': 12}) + plt.rcParams['axes.linewidth'] = 1. + + PP = Data( + lw = 1, # line_width + m = 5, # markersize + lf = 10, # legend_font_size + Slc = ['black','dimgray','silver' ], # SUAVE_line_colors + Slm = '^', # SUAVE_line_markers + Sls = '-', # SUAVE_line_styles + Elc = ['darkred','red','tomato'], # Experimental_line_colors + Elm = 's', # Experimental_line_markers + Els = '', # Experimental_line_styles + Rlc = ['darkblue','blue','cyan'], # Ref_Code_line_colors + Rlm = 'o', # Ref_Code_line_markers + Rls = ':', # Ref_Code_line_styles + ) + + + # harmonic noise test + Hararmonic_Noise_Validation(PP) + + # broadband nosie test function + Broadband_Noise_Validation(PP) + + return + + + +# ------------------------------------------------------------------ +# Harmonic Noise Validation +# ------------------------------------------------------------------ +def Hararmonic_Noise_Validation(PP): net = Battery_Propeller() net.number_of_propeller_engines = 1 - prop = design_F8745D4_prop() + prop = F8745_D4_Propeller() net.identical_propellers = True net.propellers.append(prop) @@ -69,7 +114,7 @@ def main(): conditions.frames.body.transform_to_inertial = np.array([[[1., 0., 0.],[0., 1., 0.],[0., 0., 1.]]]) prop.inputs.omega = np.atleast_2d(test_omega).T - prop.inputs.y_axis_rotation *= np.ones_like(prop.inputs.omega) + prop.inputs.y_axis_rotation = np.ones_like(prop.inputs.omega) # Run Propeller model F, Q, P, Cp , noise_data , etap = prop.spin(conditions) @@ -148,159 +193,288 @@ def main(): 109.924,109.129,108.725,107.342,106.743,105.164, 104.369,102.593,101.210,100.021,98.6401,96.6674]) - - # ---------------------------------------------------------------------------------------------------------------------------------------- - # Plots - # ---------------------------------------------------------------------------------------------------------------------------------------- - # Universal Plot Settings - plt.rcParams["font.family"] = "Times New Roman" - plt.rcParams.update({'font.size': 18}) - plt.rcParams['axes.linewidth'] = 1. - - PP = Data( - lw = 2, # line_width - m = 10, # markersize - lf = 10, # legend_font_size - Slc = ['black','dimgray','silver' ], # SUAVE_line_colors - Slm = '^', # SUAVE_line_markers - Sls = '-', # SUAVE_line_styles - Elc = ['darkred','red','tomato'], # Experimental_line_colors - Elm = 's', # Experimental_line_markers - Els = '', # Experimental_line_styles - Rlc = ['darkblue','blue','cyan'], # Ref_Code_line_colors - Rlm = 'o', # Ref_Code_line_markers - Rls = ':', # Ref_Code_line_styles - ) - # plot results - fig31 = plt.figure('Test_Case_3_p1') - fig31.set_size_inches(16, 5) - axes = fig31.add_subplot(1,3,1) + fig = plt.figure('Harmonic Test') + fig.set_size_inches(12, 8) + axes = fig.add_subplot(2,3,1) axes.plot(harmonics, F8745D4_SPL_harmonic_bpf_spectrum[0,6,:][:len(harmonics)] , color = PP.Slc[0] , linestyle = PP.Sls, marker = PP.Slm , markersize = PP.m , linewidth = PP.lw, label = 'SUAVE') axes.plot(harmonics, ANOPP_PAS_Case_1_60deg , color = PP.Rlc[0] , linestyle = PP.Rls, marker = PP.Rlm , markersize = PP.m , linewidth = PP.lw, label = 'ANOPP PAS') axes.plot(harmonics, Exp_Test_Case_1_60deg , color = PP.Elc[0] , linestyle = PP.Els, marker = PP.Elm , markersize = PP.m , linewidth = PP.lw, label = 'Exp.') axes.set_title('Case 1, $C_P$ = ' + str(round(Cp[0,0],3))) - axes.set_ylabel('SPL (dB)') - axes.set_xlabel('Harmonic #') + axes.set_ylabel('SPL (dB)') axes.minorticks_on() #plt.ylim((80,125)) # Test Case 2 - axes = fig31.add_subplot(1,3,2) + axes = fig.add_subplot(2,3,2) axes.plot(harmonics, F8745D4_SPL_harmonic_bpf_spectrum[1,6,:][:len(harmonics)] , color = PP.Slc[0] , linestyle = PP.Sls, marker = PP.Slm , markersize = PP.m , linewidth = PP.lw, label = 'SUAVE') axes.plot(harmonics, ANOPP_PAS_Case_2_60deg , color = PP.Rlc[0] , linestyle = PP.Rls, marker = PP.Rlm , markersize = PP.m , linewidth = PP.lw, label = 'ANOPP PAS') axes.plot(harmonics, Exp_Test_Case_2_60deg , color = PP.Elc[0] , linestyle = PP.Els, marker = PP.Elm , markersize = PP.m , linewidth = PP.lw, label = 'Exp.') #axes.set_ylabel('SPL (dB)') - axes.set_title('Case 2, $C_P$ = ' + str(round(Cp[1,0],3))) - axes.set_xlabel('Harmonic #') - axes.minorticks_on() - axes.legend(loc='upper center', prop={'size': PP.lf} , bbox_to_anchor=(0.5, -0.2), ncol= 3 ) + axes.set_title('60 deg. 60 deg. Case 2, $C_P$ = ' + str(round(Cp[1,0],3))) + axes.minorticks_on() # Test Case 3 - axes = fig31.add_subplot(1,3,3) + axes = fig.add_subplot(2,3,3) axes.plot(harmonics, F8745D4_SPL_harmonic_bpf_spectrum[2,6,:][:len(harmonics)] , color = PP.Slc[0] , linestyle = PP.Sls, marker = PP.Slm , markersize = PP.m , linewidth = PP.lw, label = 'SUAVE') axes.plot(harmonics, ANOPP_PAS_Case_3_60deg , color = PP.Rlc[0] , linestyle = PP.Rls, marker = PP.Rlm , markersize = PP.m , linewidth = PP.lw, label = 'ANOPP PAS') axes.plot(harmonics, Exp_Test_Case_3_60deg , color = PP.Elc[0] , linestyle = PP.Els, marker = PP.Elm , markersize = PP.m , linewidth = PP.lw, label = 'Exp.') - axes.set_title('Case 3, $C_P$ = ' + str(round(Cp[2,0],3))) - axes.set_xlabel('Harmonic #') + axes.set_title('60 deg. Case 3, $C_P$ = ' + str(round(Cp[2,0],3))) axes.minorticks_on() plt.tight_layout() - - fig32 = plt.figure('Test_Case_3_p2') - fig32.set_size_inches(16, 5) - axes = fig32.add_subplot(1,3,1) + + axes = fig.add_subplot(2,3,4) axes.plot(harmonics, F8745D4_SPL_harmonic_bpf_spectrum[0,9,:][:len(harmonics)] , color = PP.Slc[0] , linestyle = PP.Sls, marker = PP.Slm , markersize = PP.m , linewidth = PP.lw, label = 'SUAVE') axes.plot(harmonics, ANOPP_PAS_Case_1_90deg , color = PP.Rlc[0] , linestyle = PP.Rls, marker = PP.Rlm , markersize = PP.m , linewidth = PP.lw, label = 'ANOPP PAS') axes.plot(harmonics, Exp_Test_Case_1_90deg , color = PP.Elc[0] , linestyle = PP.Els, marker = PP.Elm , markersize = PP.m , linewidth = PP.lw, label = 'Exp.') - axes.set_title('Case 1, $C_P$ = ' + str(round(Cp[0,0],3))) + axes.set_title('90 deg. Case 1, $C_P$ = ' + str(round(Cp[0,0],3))) axes.set_ylabel('SPL (dB)') axes.set_xlabel('Harmonic #') axes.minorticks_on() - axes = fig32.add_subplot(1,3,2) + axes = fig.add_subplot(2,3,5) axes.plot(harmonics, F8745D4_SPL_harmonic_bpf_spectrum[1,9,:][:len(harmonics)] , color = PP.Slc[0] , linestyle = PP.Sls, marker = PP.Slm , markersize = PP.m , linewidth = PP.lw, label = 'SUAVE') axes.plot(harmonics, ANOPP_PAS_Case_2_90deg , color = PP.Rlc[0] , linestyle = PP.Rls, marker = PP.Rlm , markersize = PP.m , linewidth = PP.lw, label = 'ANOPP PAS') axes.plot(harmonics, Exp_Test_Case_2_90deg , color = PP.Elc[0] , linestyle = PP.Els, marker = PP.Elm , markersize = PP.m , linewidth = PP.lw, label = 'Exp.') - axes.set_title('Case 2, $C_P$ = ' + str(round(Cp[1,0],3))) + axes.set_title('90 deg. Case 2, $C_P$ = ' + str(round(Cp[1,0],3))) axes.set_xlabel('Harmonic #') - axes.legend(loc='upper center', prop={'size': PP.lf} , bbox_to_anchor=(0.5, -0.2), ncol= 3 ) + axes.legend(loc='upper center', prop={'size': PP.lf} , bbox_to_anchor=(0.5, -0.4), ncol= 3 ) axes.minorticks_on() - axes = fig32.add_subplot(1,3,3) + axes = fig.add_subplot(2,3,6) axes.plot(harmonics, F8745D4_SPL_harmonic_bpf_spectrum[2,9,:][:len(harmonics)] , color = PP.Slc[0] , linestyle = PP.Sls, marker = PP.Slm , markersize = PP.m , linewidth = PP.lw, label = 'SUAVE') axes.plot(harmonics, ANOPP_PAS_Case_3_90deg , color = PP.Rlc[0] , linestyle = PP.Rls, marker = PP.Rlm , markersize = PP.m , linewidth = PP.lw, label = 'ANOPP PAS') axes.plot(harmonics, Exp_Test_Case_3_90deg , color = PP.Elc[0] , linestyle = PP.Els, marker = PP.Elm , markersize = PP.m , linewidth = PP.lw, label = 'Exp.') - axes.set_title('Case 3, $C_P$ = ' + str(round(Cp[2,0],3))) + axes.set_title('90 deg. Case 3, $C_P$ = ' + str(round(Cp[2,0],3))) axes.set_xlabel('Harmonic #') axes.minorticks_on() plt.tight_layout() # Polar plot of noise - fig33 = plt.figure('Test_Case_3_p3') - axis = fig33.add_subplot(111, projection='polar') - axis.plot(theta*Units.degrees,F8745D4_SPL[0,:] , color = PP.Slc[0] , linestyle = PP.Sls, marker = PP.Slm , markersize = PP.m , linewidth = PP.lw, label = 'Total' ) - axis.plot(-theta*Units.degrees,F8745D4_SPL[0,:] , color = PP.Slc[0] , linestyle = PP.Sls, marker = PP.Slm , markersize = PP.m , linewidth = PP.lw ) - axis.plot(theta*Units.degrees,F8745D4_SPL_harmonic[0,:] , color = PP.Slc[1] , linestyle = PP.Sls, marker = PP.Slm , markersize = PP.m , linewidth = PP.lw ) - axis.plot(-theta*Units.degrees,F8745D4_SPL_harmonic[0,:] , color = PP.Slc[1] , linestyle = PP.Sls, marker = PP.Slm , markersize = PP.m , linewidth = PP.lw, label = 'Harmonic' ) - axis.plot(theta*Units.degrees,F8745D4_SPL_broadband[0,:] , color = PP.Slc[2] , linestyle = PP.Sls, marker = PP.Slm , markersize = PP.m , linewidth = PP.lw ) - axis.plot(-theta*Units.degrees,F8745D4_SPL_broadband[0,:] , color = PP.Slc[2] , linestyle = PP.Sls, marker = PP.Slm , markersize = PP.m , linewidth = PP.lw, label = 'Broadband' ) - axis.set_yticks(np.arange(50,150,25)) - axis.grid(True) + fig2 = plt.figure('Polar') + axis2 = fig2.add_subplot(111, projection='polar') + axis2.plot(theta*Units.degrees,F8745D4_SPL[0,:] , color = PP.Slc[0] , linestyle = PP.Sls, marker = PP.Slm , markersize = PP.m , linewidth = PP.lw, label = 'Total' ) + axis2.plot(-theta*Units.degrees,F8745D4_SPL[0,:] , color = PP.Slc[0] , linestyle = PP.Sls, marker = PP.Slm , markersize = PP.m , linewidth = PP.lw ) + axis2.plot(theta*Units.degrees,F8745D4_SPL_harmonic[0,:] , color = PP.Slc[1] , linestyle = PP.Sls, marker = PP.Slm , markersize = PP.m , linewidth = PP.lw ) + axis2.plot(-theta*Units.degrees,F8745D4_SPL_harmonic[0,:] , color = PP.Slc[1] , linestyle = PP.Sls, marker = PP.Slm , markersize = PP.m , linewidth = PP.lw, label = 'Harmonic' ) + axis2.plot(theta*Units.degrees,F8745D4_SPL_broadband[0,:] , color = PP.Slc[2] , linestyle = PP.Sls, marker = PP.Slm , markersize = PP.m , linewidth = PP.lw ) + axis2.plot(-theta*Units.degrees,F8745D4_SPL_broadband[0,:] , color = PP.Slc[2] , linestyle = PP.Sls, marker = PP.Slm , markersize = PP.m , linewidth = PP.lw, label = 'Broadband' ) + axis2.set_yticks(np.arange(50,150,25)) + axis2.grid(True) # Store errors error = Data() - error.SPL_Case_1_60deg = np.max(np.abs(F8745D4_SPL_harmonic_bpf_spectrum[0,6,:][:len(harmonics)] - Exp_Test_Case_1_60deg)) - error.SPL_Case_1_90deg = np.max(np.abs(F8745D4_SPL_harmonic_bpf_spectrum[0,9,:][:len(harmonics)] - Exp_Test_Case_1_90deg)) + error.SPL_Case_1_60deg = np.max(np.abs(F8745D4_SPL_harmonic_bpf_spectrum[0,6,:][:len(harmonics)] - Exp_Test_Case_1_60deg)/Exp_Test_Case_1_60deg) + error.SPL_Case_1_90deg = np.max(np.abs(F8745D4_SPL_harmonic_bpf_spectrum[0,9,:][:len(harmonics)] - Exp_Test_Case_1_90deg)/Exp_Test_Case_1_90deg) - print('Errors:') + print('Harmonic Noise Errors:') print(error) for k,v in list(error.items()): - assert(np.abs(v)<5E0) + assert(np.abs(v)<1E0) return - -def design_F8745D4_prop(): - prop = SUAVE.Components.Energy.Converters.Propeller() - prop.tag = 'F8745_D4_Propeller' - prop.tip_radius = 2.03/2 - prop.hub_radius = prop.tip_radius*0.20 - prop.number_of_blades = 2 - r_R_data = np.array([ 0.2,0.300,0.450,0.601,0.747,0.901,0.950,0.975,0.998 ]) - t_c_data = np.array([ 0.3585,0.1976,0.1148,0.0834,0.0648,0.0591,0.0562,0.0542,0.0533 ]) - b_R_data = np.array([0.116,0.143,0.163,0.169,0.166,0.148,0.135,0.113,0.075 ]) - beta_data = np.array([ 0.362,0.286,0.216,0.170,0.135,0.112,0.105,0.101,0.098])* 100 - - dim = 20 - new_radius_distribution = np.linspace(0.2,0.98 ,dim) - func_twist_distribution = interp1d(r_R_data, (beta_data)* Units.degrees , kind='cubic') - func_chord_distribution = interp1d(r_R_data, b_R_data * prop.tip_radius , kind='cubic') - func_radius_distribution = interp1d(r_R_data, r_R_data * prop.tip_radius , kind='cubic') - func_max_thickness_distribution = interp1d(r_R_data, t_c_data * b_R_data , kind='cubic') + +# ------------------------------------------------------------------ +# Broadband Noise Validation +# ------------------------------------------------------------------ +def Broadband_Noise_Validation(PP): + APC_SF = APC_11x4_Propeller() + APC_SF_inflow_ratio = 0.08 + + # Atmosheric conditions + a = 343 + density = 1.225 + dynamic_viscosity = 1.78899787e-05 + T = 286.16889478 + + # --------------------------------------------------------------------------------------------------------------------------- + # APC SF Rotor + # --------------------------------------------------------------------------------------------------------------------------- + # Define Network + net_APC_SF = Battery_Propeller() + net_APC_SF.number_of_propeller_engines = 1 + net_APC_SF.identical_propellers = True + net_APC_SF.propellers.append(APC_SF) + + # Run conditions + APC_SF_RPM = np.array([3600,4200,4800]) + APC_SF_omega_vector = APC_SF_RPM * Units.rpm + ctrl_pts = len(APC_SF_omega_vector) + velocity = APC_SF_inflow_ratio*APC_SF_omega_vector*APC_SF.tip_radius + theta = np.array([45., 67.5, 90.001, 112.5 , 135.]) # np.linspace(0.1,180,100) + S = 1.905 + + # Microphone Locations + positions = np.zeros((len(theta),3)) + for i in range(len(theta)): + if theta[i]*Units.degrees < np.pi/2: + positions[i][:] = [-S*np.cos(theta[i]*Units.degrees),-S*np.sin(theta[i]*Units.degrees), 0.0] + else: + positions[i][:] = [S*np.sin(theta[i]*Units.degrees- np.pi/2),-S*np.cos(theta[i]*Units.degrees - np.pi/2), 0.0] + + # Define conditions + APC_SF.thrust_angle = 0. * Units.degrees + APC_SF.inputs.omega = np.atleast_2d(APC_SF_omega_vector).T + APC_SF_conditions = Aerodynamics() + APC_SF_conditions.freestream.density = np.ones((ctrl_pts,1)) * density + APC_SF_conditions.freestream.dynamic_viscosity = np.ones((ctrl_pts,1)) * dynamic_viscosity + APC_SF_conditions.freestream.speed_of_sound = np.ones((ctrl_pts,1)) * a + APC_SF_conditions.freestream.temperature = np.ones((ctrl_pts,1)) * T + v_mat = np.zeros((ctrl_pts,3)) + v_mat[:,0] = velocity + APC_SF_conditions.frames.inertial.velocity_vector = v_mat + APC_SF_conditions.propulsion.throttle = np.ones((ctrl_pts,1)) * 1.0 + APC_SF_conditions.frames.body.transform_to_inertial = np.array([[[1., 0., 0.],[0., 1., 0.],[0., 0., 1.]]]) + + # Run Propeller BEMT new model + APC_SF_thrust, APC_SF_torque, APC_SF_power, APC_SF_Cp, acoustic_outputs , APC_SF_etap = APC_SF.spin(APC_SF_conditions) + + # Prepare Inputs for Noise Model + APC_SF_conditions.noise.total_microphone_locations = np.repeat(positions[ np.newaxis,:,: ],ctrl_pts,axis=0) + APC_SF_conditions.aerodynamics.angle_of_attack = np.ones((ctrl_pts,1))* 0. * Units.degrees + APC_SF_segment = Segment() + APC_SF_segment.state.conditions = APC_SF_conditions + APC_SF_segment.state.conditions.expand_rows(ctrl_pts) + APC_SF_settings = Data() + APC_SF_settings = setup_noise_settings(APC_SF_segment) + + # Run Noise Model + APC_SF_propeller_noise = propeller_mid_fidelity(net_APC_SF.propellers,acoustic_outputs,APC_SF_segment,APC_SF_settings ) + APC_SF_1_3_Spectrum = APC_SF_propeller_noise.SPL_1_3_spectrum + APC_SF_SPL_broadband_1_3_spectrum = APC_SF_propeller_noise.SPL_broadband_1_3_spectrum + + # ---------------------------------------------------------------------------------------------------------------------------------------- + # Experimental Data + # ---------------------------------------------------------------------------------------------------------------------------------------- + Exp_APC_SF_freqency_spectrum = np.array([100, 125, 160, 200, 250, 315, 400, 500, 630, 800, 1000, 1250, 1600, + 2000, 2500, 3150,4000, 5000, 6300, 8000, 10000]) + + Exp_APC_SF_1_3_Spectrum = np.array([[22.149, 42.242, 24.252, 22.616, 26.121, 24.953, 28.925, 29.158, 39.205 + , 42.943, 39.205, 38.971, 47.149, 45.280, 40.373, 38.738, 38.037, 39.906 + , 41.308, 45.981, 42.710, 39.205, 41.775, 37.570, 34.065, 33.598 ], + [17.943,46.214,46.214,22.850,27.056,27.990,31.495,31.261,37.336, + 42.242,50.186,40.373,45.280,42.476,45.747,43.878,43.878,48.084, + 48.317,49.252,49.018,49.018,46.214,42.242,40.140,39.205 ], + [ 19.345, 18.411, 54.859, 24.018, 26.355, 34.065, 33.130, 33.130, 36.635 + , 45.981, 45.046, 40.841, 42.710, 43.411, 44.813, 51.588, 45.981, 46.915 + , 52.289, 48.551, 50.186, 48.551, 48.551, 48.317, 43.177, 41.308]]) + + Exp_broadband_APC = np.array([[24.8571428,27.7142857,28.8571428,26.5714285,27.1428571,27.7142857,30.2857142,32,35.4285714, + 39.1428571,40.5714285,39.4285714,40.5714285,40,41.1428571,40.2857142,41.7142857,44,44.5714285, + 44.8571428,45.1428571], + [23.42857,26,27.14285,25.14285,25.71428,26.57142,28.28571,29.14285,34.28571,37.14285, + 37.99999,34.57142,39.14285,34,35.71428,34.85714,35.99999,43.42857,39.14285,41.14285, + 42.57142]]) + + + fig_size_width = 8 + fig_size_height = 6 + + # ---------------------------------------------------------------------------------------------------------------------------------------- + # Plots + # ---------------------------------------------------------------------------------------------------------------------------------------- + + # Figures 8 and 9 comparison + fig1 = plt.figure('Noise_Validation_Total_1_3_Spectrum_3600') + fig1.set_size_inches(fig_size_width,fig_size_height) + axes1 = fig1.add_subplot(1,1,1) + axes1.plot(Exp_APC_SF_freqency_spectrum , Exp_APC_SF_1_3_Spectrum[0,:-5] , color = PP.Elc[0] , linestyle = PP.Els, marker = PP.Elm , markersize = PP.m , linewidth = PP.lw, label = 'Exp. 3600 RPM') + axes1.plot(Exp_APC_SF_freqency_spectrum , APC_SF_1_3_Spectrum[0,0,8:] , color = PP.Slc[0] , linestyle = PP.Sls, marker = PP.Slm , markersize = PP.m , linewidth = PP.lw, label =' SUAVE 3600 RPM') + axes1.set_xscale('log') + axes1.set_ylabel(r'SPL$_{1/3}$ (dB)') + axes1.set_xlabel('Frequency (Hz)') + axes1.legend(loc='lower right') + axes1.set_ylim([15,60]) + + + fig2 = plt.figure('Noise_Validation_1_3_Spectrum_4800') + fig2.set_size_inches(fig_size_width,fig_size_height) + axes2 = fig2.add_subplot(1,1,1) + axes2.plot(Exp_APC_SF_freqency_spectrum , Exp_APC_SF_1_3_Spectrum[2,:-5] , color = PP.Elc[0] , linestyle = PP.Els, marker = PP.Elm , markersize = PP.m , linewidth = PP.lw, label = 'Exp. 4800 RPM') + axes2.plot(Exp_APC_SF_freqency_spectrum , APC_SF_1_3_Spectrum[2,0,8:] , color = PP.Slc[0] , linestyle = PP.Sls, marker = PP.Slm , markersize = PP.m , linewidth = PP.lw,label = ' SUAVE 4800 RPM') + axes2.set_xscale('log') + axes2.set_ylabel(r'SPL$_{1/3}$ (dB)') + axes2.set_xlabel('Frequency (Hz)') + axes2.legend(loc='lower right') + axes2.set_ylim([15,60]) + + + fig3 = plt.figure('Noise_Validation_Broadband_1_3_Spectrum_45_deg') + fig3.set_size_inches(fig_size_width,fig_size_height) + axes3 = fig3.add_subplot(1,1,1) + axes3.plot(Exp_APC_SF_freqency_spectrum , Exp_broadband_APC[0,:], color = PP.Elc[0] , linestyle = PP.Els, marker = PP.Elm , markersize = PP.m , linewidth = PP.lw, label = 'Exp. 45 $\degree$ mic.') + axes3.plot(Exp_APC_SF_freqency_spectrum , APC_SF_SPL_broadband_1_3_spectrum[1,4,8:] , color = PP.Slc[0] , linestyle = PP.Sls, marker = PP.Slm , markersize = PP.m , linewidth = PP.lw, label = 'SUAVE 45 $\degree$ mic') + axes3.set_xscale('log') + axes3.set_ylabel(r'SPL$_{1/3}$ (dB)') + axes3.set_xlabel('Frequency (Hz)') + axes3.legend(loc='lower right') + axes3.set_ylim([15,50]) + + + fig4 = plt.figure('Noise_Validation_Broadband_1_3_Spectrum_22_deg') + fig4.set_size_inches(fig_size_width,fig_size_height) + axes4 = fig4.add_subplot(1,1,1) + axes4.plot(Exp_APC_SF_freqency_spectrum , Exp_broadband_APC[1,:], color = PP.Elc[0] , linestyle = PP.Els, marker = PP.Elm , markersize = PP.m , linewidth = PP.lw, label = 'Exp. 22.5 $\degree$ mic.') + axes4.plot(Exp_APC_SF_freqency_spectrum , APC_SF_SPL_broadband_1_3_spectrum[1,3,8:] , color = PP.Slc[0] , linestyle = PP.Sls, marker = PP.Slm , markersize = PP.m , linewidth = PP.lw, label = 'SUAVE 22.5 $\degree$ mic.') + axes4.set_xscale('log') + axes4.set_ylabel(r'SPL$_{1/3}$ (dB)') + axes4.set_xlabel('Frequency (Hz)') + axes4.legend(loc='lower right') + axes4.set_ylim([15,50]) + + + fig1.tight_layout() + fig2.tight_layout() + fig3.tight_layout() + fig4.tight_layout() - prop.twist_distribution = func_twist_distribution(new_radius_distribution) - prop.chord_distribution = func_chord_distribution(new_radius_distribution) - prop.radius_distribution = func_radius_distribution(new_radius_distribution) - prop.max_thickness_distribution = func_max_thickness_distribution(new_radius_distribution) - prop.thickness_to_chord = prop.max_thickness_distribution/prop.chord_distribution - - ospath = os.path.abspath(__file__) - separator = os.path.sep - rel_path = ospath.split('noise_fidelity_one' + separator + 'propeller_noise.py')[0] + 'Vehicles/Airfoils' + separator - airfoil_geometry = [ rel_path +'Clark_y.txt'] - airfoil_polars = [[rel_path +'Polars/Clark_y_polar_Re_50000.txt' ,rel_path +'Polars/Clark_y_polar_Re_100000.txt',rel_path +'Polars/Clark_y_polar_Re_200000.txt', - rel_path +'Polars/Clark_y_polar_Re_500000.txt',rel_path +'Polars/Clark_y_polar_Re_1000000.txt']] - airfoil_polar_stations = np.zeros(dim) - prop.airfoil_polar_stations = list(airfoil_polar_stations.astype(int)) - prop.airfoil_flag = True - prop.mid_chord_alignment = np.zeros_like(prop.chord_distribution) - prop.number_of_airfoil_section_points = 102 - prop.airfoil_geometry_data = import_airfoil_geometry(airfoil_geometry, npoints = prop.number_of_airfoil_section_points) - prop.airfoil_polar_data = compute_airfoil_polars(airfoil_polars, prop.airfoil_geometry_data) + # Store errors + error = Data() + error.SPL_Case_1_60deg = np.max(np.abs(APC_SF_SPL_broadband_1_3_spectrum[1,3,8:] - Exp_broadband_APC[1,:])/Exp_broadband_APC[1,:]) + error.SPL_Case_1_90deg = np.max(np.abs(APC_SF_SPL_broadband_1_3_spectrum[1,4,8:] - Exp_broadband_APC[0,:])/Exp_broadband_APC[0,:]) + + print('Broadband Noise Errors:') + print(error) - return prop + for k,v in list(error.items()): + assert(np.abs(v)<1E0) + + return + +def setup_noise_settings(sts): + + sts.ground_microphone_phi_angles = np.array([30.,45.,60.,75.,89.9,90.1,105.,120.,135.,150.])*Units.degrees + sts.ground_microphone_theta_angles = np.array([89.9,89.9,89.9,89.9,89.9,89.9,89.9,89.9, 89.9,89.9 ])*Units.degrees + sts.center_frequencies = np.array([16,20,25,31.5,40, 50, 63, 80, 100, 125, 160, 200, 250, 315, 400, \ + 500, 630, 800, 1000, 1250, 1600, 2000, 2500, 3150, + 4000, 5000, 6300, 8000, 10000]) + sts.lower_frequencies = np.array([14,18,22.4,28,35.5,45,56,71,90,112,140,180,224,280,355,450,560,710,\ + 900,1120,1400,1800,2240,2800,3550,4500,5600,7100,9000 ]) + sts.upper_frequencies = np.array([18,22.4,28,35.5,45,56,71,90,112,140,180,224,280,355,450,560,710,900,1120,\ + 1400,1800,2240,2800,3550,4500,5600,7100,9000,11200 ]) + sts.harmonics = np.arange(1,30) + + + sts.broadband_spectrum_resolution = 301 + sts.floating_point_precision = np.float32 + sts.urban_canyon_microphone_z_resolution = 16 + sts.mic_x_position = 0 + sts.number_of_multiprocessing_workers = 8 + sts.parallel_computing = True # TO BE REMOVED + sts.lateral_ground_distance = 1000 * Units.feet + sts.level_ground_microphone_min_x = -50 + sts.level_ground_microphone_max_x = 1000 + sts.level_ground_microphone_min_y = -1000 * Units.feet + sts.level_ground_microphone_max_y = 1000 * Units.feet + sts.level_ground_microphone_x_resolution = 16 + sts.level_ground_microphone_y_resolution = 4 + return sts + if __name__ == '__main__': main() - plt.show() \ No newline at end of file + plt.show() + + \ No newline at end of file diff --git a/regression/scripts/nonuniform_propeller_inflow/nonuniform_propeller_inflow.py b/regression/scripts/nonuniform_propeller_inflow/nonuniform_propeller_inflow.py index 0ed131e8d0..5446c71b5f 100644 --- a/regression/scripts/nonuniform_propeller_inflow/nonuniform_propeller_inflow.py +++ b/regression/scripts/nonuniform_propeller_inflow/nonuniform_propeller_inflow.py @@ -9,8 +9,7 @@ from SUAVE.Plots.Performance.Propeller_Plots import * from SUAVE.Methods.Aerodynamics.Common.Fidelity_Zero.Lift.compute_wing_wake import compute_wing_wake from SUAVE.Methods.Aerodynamics.Common.Fidelity_Zero.Lift.compute_propeller_nonuniform_freestream import compute_propeller_nonuniform_freestream -from SUAVE.Methods.Geometry.Two_Dimensional.Cross_Section.Airfoil.import_airfoil_geometry import import_airfoil_geometry -from SUAVE.Methods.Geometry.Two_Dimensional.Cross_Section.Airfoil.compute_airfoil_polars import compute_airfoil_polars + from SUAVE.Analyses.Propulsion.Rotor_Wake_Fidelity_One import Rotor_Wake_Fidelity_One import numpy as np @@ -56,11 +55,11 @@ def case_1(vehicle, conditions): plot_propeller_disc_performance(prop,outputs,title='Case 1: Operating at Thrust Angle') thrust = np.linalg.norm(thrust) - thrust_r = 1738.662230458737 - torque_r = 746.75236032 - power_r = 101659.64159969 - Cp_r = 0.46809903 - etap_r = 0.7184538 + thrust_r = 1743.0258191335301 + torque_r = 748.87304348 + power_r = 101948.342247 + Cp_r = 0.46942838 + etap_r = 0.71821729 print('\nCase 1 Errors: \n') print('Thrust difference = ', np.abs(thrust - thrust_r) / thrust_r ) print('Torque difference = ', np.abs(torque - torque_r) / torque_r ) @@ -106,11 +105,11 @@ def case_2(vehicle,conditions, Na=24, Nr=101): # expected results thrust = np.linalg.norm(thrust) - thrust_r = 1147.4579297648168 - torque_r = 566.69857956 - power_r = 77147.8974213 - Cp_r = 0.35523297 - etap_r = 0.66490418 + thrust_r = 1150.8011515854673 + torque_r = 568.67821527 + power_r = 77417.3964781 + Cp_r = 0.3564739 + etap_r = 0.66452008 print('\nCase 2 Errors: \n') print('Thrust difference = ', np.abs(thrust - thrust_r) / thrust_r ) print('Torque difference = ', np.abs(torque - torque_r) / torque_r ) @@ -153,7 +152,7 @@ def case_3(vehicle,conditions): thrust, torque, power, Cp, outputs , etap = prop.spin(conditions) thrust = np.linalg.norm(thrust) - thrust_r, torque_r, power_r, Cp_r, etap_r = 1669.3167868463604, 741.13494845, 100894.91140539, 0.46457778, 0.73963232 + thrust_r, torque_r, power_r, Cp_r, etap_r = 1670.646434565442, 742.03162704, 101016.98135661, 0.46513986, 0.73932696 print('\nCase 3 Errors: \n') print('Thrust difference = ', np.abs(thrust - thrust_r) / thrust_r ) print('Torque difference = ', np.abs(torque - torque_r) / torque_r ) @@ -262,17 +261,16 @@ def basic_prop(Na=24, Nr=101): prop.number_azimuthal_stations = Na prop.rotation = 1 prop.symmetry = True - - airfoil_geometry = ['../Vehicles/Airfoils/NACA_4412.txt'] - airfoil_polars = [['../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_50000.txt' , - '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_100000.txt' , - '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_200000.txt' , - '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_500000.txt' , - '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_1000000.txt' ]] - prop.airfoil_geometry_data = import_airfoil_geometry(airfoil_geometry) - prop.airfoil_polar_data = compute_airfoil_polars(airfoil_polars, prop.airfoil_geometry_data) - prop.airfoil_polar_stations = list(np.zeros(Nr).astype(int)) - prop = propeller_design(prop,Nr) + airfoil = SUAVE.Components.Airfoils.Airfoil() + airfoil.coordinate_file = '../Vehicles/Airfoils/NACA_4412.txt' + airfoil.polar_files = ['../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_50000.txt' , + '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_100000.txt' , + '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_200000.txt' , + '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_500000.txt' , + '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_1000000.txt' ] + prop.append_airfoil(airfoil) + prop.airfoil_polar_stations = list(np.zeros(Nr).astype(int)) + prop = propeller_design(prop,Nr) return prop diff --git a/regression/scripts/propeller/propeller_test.py b/regression/scripts/propeller/propeller_test.py index afc6de0654..33272b3f9b 100644 --- a/regression/scripts/propeller/propeller_test.py +++ b/regression/scripts/propeller/propeller_test.py @@ -20,9 +20,6 @@ import copy, time from SUAVE.Methods.Propulsion import propeller_design from SUAVE.Components.Energy.Networks.Battery_Propeller import Battery_Propeller -from SUAVE.Methods.Geometry.Two_Dimensional.Cross_Section.Airfoil.import_airfoil_geometry import import_airfoil_geometry -from SUAVE.Methods.Geometry.Two_Dimensional.Cross_Section.Airfoil.compute_airfoil_polars import compute_airfoil_polars - def main(): @@ -56,71 +53,76 @@ def main(): bad_prop.design_tip_mach = 0.1 bad_prop.angular_velocity = gearbox.inputs.speed bad_prop.design_Cl = 0.7 - bad_prop.design_altitude = 1. * Units.km - bad_prop.airfoil_geometry = ['../Vehicles/Airfoils/NACA_4412.txt'] - - bad_prop.airfoil_polars = [[#'../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_50000.txt', - '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_100000.txt', - '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_200000.txt', - '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_500000.txt', - '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_1000000.txt', - '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_5000000.txt', - '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_7500000.txt']] - - bad_prop.airfoil_polar_stations = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] - bad_prop.design_thrust = 100000 - bad_prop = propeller_design(bad_prop) - - prop_a = SUAVE.Components.Energy.Converters.Propeller() - prop_a.tag = "Prop_W_Aifoil" - prop_a.number_of_blades = 3 - prop_a.number_of_engines = 1 - prop_a.freestream_velocity = 49.1744 - prop_a.tip_radius = 1.0668 - prop_a.hub_radius = 0.21336 - prop_a.design_tip_mach = 0.65 - prop_a.angular_velocity = gearbox.inputs.speed # 207.16160479940007 - prop_a.design_Cl = 0.7 - prop_a.design_altitude = 1. * Units.km - airfoil_geometry = ['../Vehicles/Airfoils/NACA_4412.txt','../Vehicles/Airfoils/Clark_y.txt'] - - airfoil_polars = [[#'../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_50000.txt', - '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_100000.txt', - '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_200000.txt', - '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_500000.txt', - '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_1000000.txt', - '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_5000000.txt', - '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_7500000.txt'], - ['../Vehicles/Airfoils/Polars/Clark_y_polar_Re_50000.txt', - '../Vehicles/Airfoils/Polars/Clark_y_polar_Re_100000.txt', - '../Vehicles/Airfoils/Polars/Clark_y_polar_Re_200000.txt', - '../Vehicles/Airfoils/Polars/Clark_y_polar_Re_500000.txt', - '../Vehicles/Airfoils/Polars/Clark_y_polar_Re_1000000.txt']] - prop_a.airfoil_geometry_data = import_airfoil_geometry(airfoil_geometry) - prop_a.airfoil_polar_data = compute_airfoil_polars(airfoil_polars, prop_a.airfoil_geometry_data) - - prop_a.airfoil_polar_stations = [0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1] - prop_a.design_thrust = 3054.4809132125697 - prop_a = propeller_design(prop_a) + bad_prop.design_altitude = 1. * Units.km + airfoil = SUAVE.Components.Airfoils.Airfoil() + airfoil.coordinate_file ='4412' + airfoil.NACA_4_series_flag = True + airfoil.polar_files = ['../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_50000.txt', + '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_100000.txt', + '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_200000.txt', + '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_500000.txt', + '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_1000000.txt'] + bad_prop.append_airfoil(airfoil) + bad_prop.airfoil_polar_stations = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] + bad_prop.design_thrust = 100000 + bad_prop = propeller_design(bad_prop) + prop_a = SUAVE.Components.Energy.Converters.Propeller() + prop_a.tag = "Prop_W_Aifoil" + prop_a.number_of_blades = 3 + prop_a.number_of_engines = 1 + prop_a.freestream_velocity = 49.1744 + prop_a.tip_radius = 1.0668 + prop_a.hub_radius = 0.21336 + prop_a.design_tip_mach = 0.65 + prop_a.angular_velocity = gearbox.inputs.speed # 207.16160479940007 + prop_a.design_Cl = 0.7 + prop_a.design_altitude = 1. * Units.km + + # define first airfoil + airfoil_1 = SUAVE.Components.Airfoils.Airfoil() + airfoil_1.tag = 'NACA_4412' + airfoil_1.coordinate_file = '../Vehicles/Airfoils/NACA_4412.txt' # absolute path + airfoil_1.polar_files = ['../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_50000.txt', + '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_100000.txt', + '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_200000.txt', + '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_500000.txt', + '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_1000000.txt'] + prop_a.append_airfoil(airfoil_1) # append first airfoil + + # define second airfoil + airfoil_2 = SUAVE.Components.Airfoils.Airfoil() + airfoil_2.tag = 'Clark_Y' + airfoil_2.coordinate_file = '../Vehicles/Airfoils/Clark_y.txt' + airfoil_2.polar_files = ['../Vehicles/Airfoils/Polars/Clark_y_polar_Re_50000.txt', + '../Vehicles/Airfoils/Polars/Clark_y_polar_Re_100000.txt', + '../Vehicles/Airfoils/Polars/Clark_y_polar_Re_200000.txt', + '../Vehicles/Airfoils/Polars/Clark_y_polar_Re_500000.txt', + '../Vehicles/Airfoils/Polars/Clark_y_polar_Re_1000000.txt'] + prop_a.append_airfoil(airfoil_2) # append second airfoil + + # define polar stations on rotor + prop_a.airfoil_polar_stations = [0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1] + prop_a.design_thrust = 3054.4809132125697 + prop_a = propeller_design(prop_a) # plot propeller plot_propeller(prop_a) # Design the Propeller with airfoil geometry defined - prop = SUAVE.Components.Energy.Converters.Propeller() - prop.tag = "Prop_No_Aifoil" - prop.number_of_blades = 3 - prop.number_of_engines = 1 - prop.freestream_velocity = 49.1744 - prop.tip_radius = 1.0668 - prop.hub_radius = 0.21336 - prop.design_tip_mach = 0.65 - prop.angular_velocity = gearbox.inputs.speed - prop.design_Cl = 0.7 - prop.design_altitude = 1. * Units.km - prop.origin = [[16.*0.3048 , 0. ,2.02*0.3048 ]] - prop.design_power = gearbox.outputs.power - prop = propeller_design(prop) + prop = SUAVE.Components.Energy.Converters.Propeller() + prop.tag = "Prop_No_Aifoil" + prop.number_of_blades = 3 + prop.number_of_engines = 1 + prop.freestream_velocity = 49.1744 + prop.tip_radius = 1.0668 + prop.hub_radius = 0.21336 + prop.design_tip_mach = 0.65 + prop.angular_velocity = gearbox.inputs.speed + prop.design_Cl = 0.7 + prop.design_altitude = 1. * Units.km + prop.origin = [[16.*0.3048 , 0. ,2.02*0.3048 ]] + prop.design_power = gearbox.outputs.power + prop = propeller_design(prop) # Design a Rotor with airfoil geometry defined rot_a = SUAVE.Components.Energy.Converters.Rotor() @@ -135,45 +137,37 @@ def main(): rot_a.angular_velocity = 258.9520059992501 rot_a.design_Cl = 0.7 rot_a.design_altitude = 20 * Units.feet - rot_a.design_thrust = 2271.2220451593753 - airfoil_geometry = ['../Vehicles/Airfoils/NACA_4412.txt'] - - airfoil_polars = [[#'../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_50000.txt', - '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_100000.txt', - '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_200000.txt', - '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_500000.txt', - '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_1000000.txt', - '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_5000000.txt', - '../Vehicles/Airfoils/Polars/NACA_4412_polar_Re_7500000.txt']] - - - rot_a.airfoil_geometry_data = import_airfoil_geometry(airfoil_geometry) - rot_a.airfoil_polar_data = compute_airfoil_polars(airfoil_polars, rot_a.airfoil_geometry_data) - rot_a.airfoil_polar_stations = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] + rot_a.design_thrust = 2271.2220451593753 + airfoil = SUAVE.Components.Airfoils.Airfoil() + airfoil.coordinate_file = '4412' + airfoil.NACA_4_series_flag = True + airfoil.number_of_points = 30 # for aero coefficients, panel code works better with fewer points + rot_a.append_airfoil(airfoil) + rot_a.airfoil_polar_stations = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] rot_a = propeller_design(rot_a) # Design a Rotor without airfoil geometry defined - rot = SUAVE.Components.Energy.Converters.Rotor() - rot.tag = "Rot_No_Aifoil" - rot.tip_radius = 2.8 * Units.feet - rot.hub_radius = 0.35 * Units.feet - rot.number_of_blades = 2 - rot.design_tip_mach = 0.65 - rot.number_of_engines = 12 - rot.disc_area = np.pi*(rot.tip_radius**2) - rot.freestream_velocity = 500. * Units['ft/min'] - rot.angular_velocity = 258.9520059992501 - rot.design_Cl = 0.7 - rot.design_altitude = 20 * Units.feet - rot.design_thrust = 2271.2220451593753 - rot = propeller_design(rot) + rot = SUAVE.Components.Energy.Converters.Rotor() + rot.tag = "Rot_No_Aifoil" + rot.tip_radius = 2.8 * Units.feet + rot.hub_radius = 0.35 * Units.feet + rot.number_of_blades = 2 + rot.design_tip_mach = 0.65 + rot.number_of_engines = 12 + rot.disc_area = np.pi*(rot.tip_radius**2) + rot.freestream_velocity = 500. * Units['ft/min'] + rot.angular_velocity = 258.9520059992501 + rot.design_Cl = 0.7 + rot.design_altitude = 20 * Units.feet + rot.design_thrust = 2271.2220451593753 + rot = propeller_design(rot) # Find the operating conditions - atmosphere = SUAVE.Analyses.Atmospheric.US_Standard_1976() - atmosphere_conditions = atmosphere.compute_values(rot.design_altitude) + atmosphere = SUAVE.Analyses.Atmospheric.US_Standard_1976() + atmosphere_conditions = atmosphere.compute_values(rot.design_altitude) - V = prop.freestream_velocity - Vr = rot.freestream_velocity + V = prop.freestream_velocity + Vr = rot.freestream_velocity conditions = SUAVE.Analyses.Mission.Segments.Conditions.Aerodynamics() conditions._size = 1 @@ -193,7 +187,7 @@ def main(): conditions_r.frames.inertial.velocity_vector = np.array([[0,Vr,0]]) # Create and attach this propeller - prop_a.inputs.omega = np.array(prop_a.angular_velocity,ndmin=2) + prop_a.inputs.omega = np.array(prop.angular_velocity,ndmin=2) prop.inputs.omega = np.array(prop.angular_velocity,ndmin=2) rot_a.inputs.omega = copy.copy(prop.inputs.omega) rot.inputs.omega = copy.copy(prop.inputs.omega) @@ -219,10 +213,10 @@ def main(): plot_results(outputr, rot,'black','-','P') # Truth values for propeller with airfoil geometry defined - F_a_truth = 3351.5941486151924 - Q_a_truth = 982.11712999 - P_a_truth = 203456.96074886 - Cplast_a_truth = 0.10486666 + F_a_truth = 3352.366469630676 + Q_a_truth = 978.76113592 + P_a_truth = 202761.72763161 + Cplast_a_truth = 0.10450832 # Truth values for propeller without airfoil geometry defined F_truth = 2629.013537561697 @@ -231,13 +225,13 @@ def main(): Cplast_truth = 0.08407389 # Truth values for rotor with airfoil geometry defined - Fr_a_truth = 1499.6262124628329 - Qr_a_truth = 141.00486033 - Pr_a_truth = 29210.79314946 - Cplastr_a_truth = 0.04594712 + Fr_a_truth = 1266.0906132241278 + Qr_a_truth = 107.01068456 + Pr_a_truth = 22168.50514504 + Cplastr_a_truth = 0.03486995 # Truth values for rotor without airfoil geometry defined - Fr_truth = 1250.1858821890885 + Fr_truth = 1250.185882189092 Qr_truth = 121.95416738 Pr_truth = 25264.22102656 Cplastr_truth = 0.03973936 @@ -248,17 +242,14 @@ def main(): error.Torque_a = np.max(np.abs(Q_a -Q_a_truth)) error.Power_a = np.max(np.abs(P_a -P_a_truth)) error.Cp_a = np.max(np.abs(Cplast_a -Cplast_a_truth)) - error.Thrust = np.max(np.abs(np.linalg.norm(F)-F_truth)) error.Torque = np.max(np.abs(Q-Q_truth)) error.Power = np.max(np.abs(P-P_truth)) error.Cp = np.max(np.abs(Cplast-Cplast_truth)) - error.Thrustr_a = np.max(np.abs(np.linalg.norm(Fr_a)-Fr_a_truth)) error.Torquer_a = np.max(np.abs(Qr_a-Qr_a_truth)) error.Powerr_a = np.max(np.abs(Pr_a-Pr_a_truth)) error.Cpr_a = np.max(np.abs(Cplastr_a-Cplastr_a_truth)) - error.Thrustr = np.max(np.abs(np.linalg.norm(Fr)-Fr_truth)) error.Torquer = np.max(np.abs(Qr-Qr_truth)) error.Powerr = np.max(np.abs(Pr-Pr_truth)) diff --git a/regression/scripts/segments/transition_segment_test.py b/regression/scripts/segments/transition_segment_test.py index e882a19d04..e4048b2dab 100644 --- a/regression/scripts/segments/transition_segment_test.py +++ b/regression/scripts/segments/transition_segment_test.py @@ -53,28 +53,26 @@ def main(): # Check throttles departure_throttle = results.segments.departure.conditions.propulsion.throttle[:,0] transition_1_throttle = results.segments.transition_1.conditions.propulsion.throttle[:,0] - transition_y_axis_rotations = results.segments.transition_1.conditions.propulsion.propeller_y_axis_rotation[:,0] - - # Truth values - departure_throttle_truth = np.array([0.65837632, 0.6586018 , 0.65906953, 0.65931179]) - transition_1_throttle_truth = np.array([0.95751959, 0.9463189 , 0.8110942 , 0.6151161 ]) - transition_y_axis_rotations_truth = np.array([1.35008476, 1.32586108, 1.16978417, 0.82700895]) + cruise_throttle = results.segments.cruise.conditions.propulsion.throttle[:,0] + + # Truth values + departure_throttle_truth = 0.651687547816525 + transition_1_throttle_truth = 0.601399797479313 + cruise_throttle_truth = 0.4649280739025156 # Store errors error = Data() - error.departure_throttle = np.max(np.abs(departure_throttle - departure_throttle_truth)) - #error.transition_1_throttle = np.max(np.abs(transition_1_throttle - transition_1_throttle_truth)) - #error.transition_y_axis_rotations = np.max(np.abs(transition_y_axis_rotations - transition_y_axis_rotations_truth)) - - #plt.show() - + error.departure_throttle = np.abs(departure_throttle[-1] - departure_throttle_truth) + error.transition_1_throttle = np.abs(transition_1_throttle[-1] - transition_1_throttle_truth) + error.cruise_throttle = np.abs(cruise_throttle[-1] - cruise_throttle_truth) + print('Errors:') print(error) - + for k,v in list(error.items()): assert(np.abs(v)<1e-6) - + plt.show() return @@ -222,15 +220,15 @@ def mission_setup(analyses,vehicle): segment.analyses.extend( analyses.transition_1 ) segment.altitude = 40.0 * Units.ft segment.acceleration = 2.3 * Units['m/s/s'] - segment.air_speed_start = 0.1 * Units.mph # starts from hover - segment.air_speed_end = 1. * V_stall # increases linearly in time to stall speed + segment.air_speed_start = 0.0 * Units.mph # starts from hover + segment.air_speed_end = 1.2 * V_stall # increases linearly in time to stall speed segment.pitch_initial = 0.0 * Units.degrees segment.pitch_final = 3.6 * Units.degrees segment.state.unknowns.throttle = 0.95 * ones_row(1) segment.process.iterate.conditions.stability = SUAVE.Methods.skip segment.process.finalize.post_process.stability = SUAVE.Methods.skip segment = vehicle.networks.battery_propeller.add_tiltrotor_transition_unknowns_and_residuals_to_segment(segment, - initial_power_coefficient = 0.15) + initial_power_coefficient = 0.03) # add to misison mission.append_segment(segment) @@ -243,15 +241,15 @@ def mission_setup(analyses,vehicle): segment.analyses.extend( analyses.transition_1 ) segment.altitude_start = 40.0 * Units.ft segment.altitude_end = 100.0 * Units.ft - segment.acceleration = 1.4 * Units['m/s/s'] - segment.climb_angle = 5. * Units.deg + segment.acceleration = 0.5 * Units['m/s/s'] + segment.climb_angle = 7. * Units.deg segment.pitch_initial = 3.6 * Units.degrees segment.pitch_final = 4.0 * Units.degrees segment.state.unknowns.throttle = 0.9 * ones_row(1) segment.process.iterate.conditions.stability = SUAVE.Methods.skip segment.process.finalize.post_process.stability = SUAVE.Methods.skip segment = vehicle.networks.battery_propeller.add_tiltrotor_transition_unknowns_and_residuals_to_segment(segment, - initial_power_coefficient = 0.1) + initial_power_coefficient = 0.03) # add to misison mission.append_segment(segment) @@ -263,19 +261,37 @@ def mission_setup(analyses,vehicle): segment.tag = "Transition_2b" segment.analyses.extend( analyses.transition_1 ) segment.altitude_start = 100.0 * Units.ft - segment.altitude_end = 90.0 * Units.ft - segment.acceleration = -0.1 * Units['m/s/s'] - segment.climb_angle = 1. * Units.deg + segment.altitude_end = 40.0 * Units.ft + segment.acceleration = -0.25 * Units['m/s/s'] + segment.climb_angle = 7. * Units.deg segment.pitch_initial = 4.0 * Units.degrees - segment.pitch_final = 3. * Units.degrees - segment.state.unknowns.throttle = 0.8 * ones_row(1) + segment.pitch_final = 3.6 * Units.degrees + segment.state.unknowns.throttle = 0.9 * ones_row(1) segment.process.iterate.conditions.stability = SUAVE.Methods.skip segment.process.finalize.post_process.stability = SUAVE.Methods.skip segment = vehicle.networks.battery_propeller.add_tiltrotor_transition_unknowns_and_residuals_to_segment(segment, - initial_power_coefficient = 0.05) + initial_power_coefficient = 0.03) # add to misison mission.append_segment(segment) + # ------------------------------------------------------------------ + # Segment 3: Mini Cruise; Constant Acceleration, Constant Altitude + # ------------------------------------------------------------------ + segment = Segments.Cruise.Constant_Speed_Constant_Altitude(base_segment) + segment.tag = "Cruise" + segment.analyses.extend(analyses.cruise) + segment.altitude = 40.0 * Units.ft + segment.air_speed = 1.2 * V_stall + segment.distance = 2. * Units.miles + segment.state.unknowns.throttle = 0.8 * ones_row(1) + segment.process.iterate.conditions.stability = SUAVE.Methods.skip + segment.process.finalize.post_process.stability = SUAVE.Methods.skip + segment = vehicle.networks.battery_propeller.add_unknowns_and_residuals_to_segment(segment) + + # add to mission + mission.append_segment(segment) + + # ------------------------------------------------------------------ # Mission definition complete # ------------------------------------------------------------------ diff --git a/regression/scripts/slipstream/propeller_interactions.py b/regression/scripts/slipstream/propeller_interactions.py index 3fdc2069ee..124db57306 100644 --- a/regression/scripts/slipstream/propeller_interactions.py +++ b/regression/scripts/slipstream/propeller_interactions.py @@ -64,7 +64,7 @@ def main(): T, Q, P, Cp, outputs , etap = run_downstream_propeller(prop, propeller_wake, conditions, plot_performance=plot_flag) # compare regression results: - T_iso_true, Q_iso_true, P_iso_true, Cp_iso_true, etap_iso_true = 3.20956040662288, 0.07275761, 49.52453651, 0.04625594, 0.57943072 + T_iso_true, Q_iso_true, P_iso_true, Cp_iso_true, etap_iso_true = 3.229745783054582, 0.07191487, 48.95089701,0.04572016, 0.5899077 assert(abs(np.linalg.norm(T_iso)-T_iso_true)<1e-6) assert(abs(Q_iso-Q_iso_true)<1e-6) @@ -72,7 +72,7 @@ def main(): assert(abs(Cp_iso-Cp_iso_true)<1e-6) assert(abs(etap_iso-etap_iso_true)<1e-6) - T_true, Q_true, P_true, Cp_true, etap_true = 3.4194138996561025, 0.07397641, 50.35414818, 0.0470308, 0.60714553 + T_true, Q_true, P_true, Cp_true, etap_true = 3.449059963023848,0.07231234, 49.22145028, 0.04597286, 0.62650237 assert(abs(np.linalg.norm(T)-T_true)<1e-6) assert(abs(Q-Q_true)<1e-6) diff --git a/regression/scripts/slipstream/slipstream_test.py b/regression/scripts/slipstream/slipstream_test.py index da04be03e3..0b4c755728 100644 --- a/regression/scripts/slipstream/slipstream_test.py +++ b/regression/scripts/slipstream/slipstream_test.py @@ -39,22 +39,23 @@ def main(): # fidelity zero wakes + print('Wake Fidelity Zero, Identical Props') t0=time.time() Propeller_Slipstream(wake_fidelity=0,identical_props=True) print((time.time()-t0)/60) # fidelity one wakes + print('Wake Fidelity One, Identical Props') t0=time.time() Propeller_Slipstream(wake_fidelity=1,identical_props=True) print((time.time()-t0)/60) + + print('Wake Fidelity One, Non-Identical Props') t0=time.time() Propeller_Slipstream(wake_fidelity=1,identical_props=False) print((time.time()-t0)/60) - # include slipstream - Lift_Rotor_Slipstream(wake_fidelity=0) - return @@ -71,9 +72,9 @@ def Propeller_Slipstream(wake_fidelity,identical_props): results = mission.evaluate() # check regression values - if wake_fidelity==0: + if wake_fidelity==0: regress_1a(results,configs) - elif wake_fidelity==1: + elif wake_fidelity==1: regress_1b(results, configs) return @@ -118,13 +119,13 @@ def regress_1b(results, configs): # lift coefficient and sectional lift coefficient check lift_coefficient_true = 0.6020199509181722 - sectional_lift_coeff_true = np.array([5.75823092e-01, 5.03698220e-01, 4.82884524e-01, 4.20652379e-01, - 8.18443409e-02, 5.81342816e-01, 5.26930095e-01, 4.91007309e-01, - 4.24081449e-01, 8.25233196e-02, 9.62043036e-03, 8.22649466e-03, - 6.21580138e-03, 5.35232779e-03, 3.67175516e-03, 5.36291909e-03, - 3.30114596e-03, 2.12812524e-03, 2.02058596e-03, 1.52908353e-03, - 3.87294195e-07, 2.14393032e-09, 1.67827738e-09, 4.26508872e-09, - 2.57863223e-09]) + sectional_lift_coeff_true = np.array([5.75825222e-01, 5.03609039e-01, 4.82878213e-01, 4.20669705e-01, + 8.18479211e-02, 5.81360461e-01, 5.26912066e-01, 4.91028606e-01, + 4.24111363e-01, 8.25293663e-02, 9.65711331e-03, 8.26280093e-03, + 6.24871066e-03, 5.37980377e-03, 3.68930535e-03, 5.38757509e-03, + 3.32372651e-03, 2.14969443e-03, 2.03870963e-03, 1.54056812e-03, + 3.89612810e-07, 2.15708160e-09, 1.68896860e-09, 4.29216200e-09, + 2.59497273e-09]) diff_CL = np.abs(lift_coefficient - lift_coefficient_true) print('CL difference') @@ -136,7 +137,7 @@ def regress_1b(results, configs): print(diff_Cl) assert np.abs(lift_coefficient - lift_coefficient_true) < 1e-6 - assert np.max(np.abs(sectional_lift_coeff - sectional_lift_coeff_true)) < 1e-6 + assert np.max(np.abs(sectional_lift_coeff - sectional_lift_coeff_true)) < 1e-6 # plot results, vehicle, and vortex distribution plot_mission(results,configs.base) @@ -144,83 +145,7 @@ def regress_1b(results, configs): plot_vehicle_vlm_panelization(configs.base, save_figure=False, plot_control_points=True) return - - -def Lift_Rotor_Slipstream(wake_fidelity): - # setup configs, analyses - vehicle = Stopped_Rotor_vehicle(wake_fidelity=wake_fidelity, identical_props=True) - - # evaluate single point - state = SUAVE.Analyses.Mission.Segments.Conditions.State() - state.conditions = SUAVE.Analyses.Mission.Segments.Conditions.Aerodynamics() - AoA = 4 * Units.deg*np.ones((1,1)) - state.conditions.freestream.mach_number = 0.15 * np.ones_like(AoA) - state.conditions.freestream.density = 1.21 * np.ones_like(AoA) - state.conditions.freestream.dynamic_viscosity = 1.79 * np.ones_like(AoA) - state.conditions.freestream.temperature = 288. * np.ones_like(AoA) - state.conditions.freestream.pressure = 99915.9 * np.ones_like(AoA) - state.conditions.freestream.reynolds_number = 3453930.8 * np.ones_like(AoA) - state.conditions.freestream.velocity = 51.1 * np.ones_like(AoA) - state.conditions.aerodynamics.angle_of_attack = AoA - state.conditions.frames = Data() - state.conditions.frames.inertial = Data() - state.conditions.frames.body = Data() - state.conditions.use_Blade_Element_Theory = False - state.conditions.frames.body.transform_to_inertial = np.array([[[1., 0., 0],[0., 1., 0.],[0., 0., 1.]]]) - state.conditions.propulsion.throttle = np.ones((1,1)) - velocity_vector = np.array([[51.1, 0. ,0.]]) - state.conditions.frames.inertial.velocity_vector = np.tile(velocity_vector,(1,1)) - - - settings = simulation_settings() - - # run propeller and rotor - - prop = vehicle.networks.lift_cruise.propellers.propeller - prop.inputs.omega = np.ones((1,1)) * 1200. - F, Q, P, Cp , outputs , etap = prop.spin(state.conditions) - prop.outputs = outputs - - rot = vehicle.networks.lift_cruise.lift_rotors.lift_rotor - rot.inputs.omega = np.ones((1,1)) * 250. - - # ========================================================================================================= - # Run Propeller model - # ========================================================================================================= - F, Q, P, Cp , outputs , etap = rot.spin(state.conditions) - - # append outputs for identical rotors - for r in vehicle.networks.lift_cruise.lift_rotors: - r.outputs = outputs - - # ========================================================================================================= - # Run VLM with slipstream - # ========================================================================================================= - results = VLM(state.conditions,settings,vehicle) - - # check regression values - regress_2(results) - - plot_vehicle(vehicle, save_figure = False, plot_control_points = False) - - return - - -def regress_2(results): - - CL_truth = 0.41644953 - CDi_truth = 0.00365652 - CM_truth = 0.06938676 - - CL = results.CL - CDi = results.CDi - CM = results.CM - - assert(np.abs(CL_truth - CL) < 1e-6) - assert(np.abs(CDi_truth - CDi) < 1e-6) - assert(np.abs(CM_truth - CM) < 1e-6) - - return + def plot_mission(results,vehicle): @@ -375,6 +300,7 @@ def X57_mission_setup(analyses,vehicle): base_segment.process.iterate.initials.initialize_battery = SUAVE.Methods.Missions.Segments.Common.Energy.initialize_battery base_segment.process.iterate.conditions.planet_position = SUAVE.Methods.skip base_segment.state.numerics.number_control_points = 2 + base_segment.state.numerics.tolerance_solution = 1e-10 # ------------------------------------------------------------------ # Cruise Segment: constant Speed, constant altitude @@ -424,12 +350,12 @@ def Stopped_Rotor_vehicle(wake_fidelity, identical_props): for p in props: p.rotation = -1 if wake_fidelity==1: - p.Wake = Rotor_Wake_Fidelity_One() + p.Wake = Rotor_Wake_Fidelity_One() p.Wake.wake_settings.number_rotor_rotations = 1 # reduced for regression speed for r in lift_rots: r.rotation = -1 if wake_fidelity==1: - r.Wake = Rotor_Wake_Fidelity_One() + r.Wake = Rotor_Wake_Fidelity_One() r.Wake.wake_settings.number_rotor_rotations = 1 # reduced for regression speed # test for non-identical propellers @@ -440,25 +366,6 @@ def Stopped_Rotor_vehicle(wake_fidelity, identical_props): return vehicle -def simulation_settings(): - settings = Vortex_Lattice().settings - settings.number_spanwise_vortices = 15 - settings.number_chordwise_vortices = 1 - settings.use_surrogate = False - settings.propeller_wake_model = True - settings.spanwise_cosine_spacing = True - settings.model_fuselage = True - settings.leading_edge_suction_multiplier = 1.0 - settings.oswald_efficiency_factor = None - settings.span_efficiency = None - settings.viscous_lift_dependent_drag_factor = 0.38 - settings.drag_coefficient_increment = 0.0000 - settings.spoiler_drag_increment = 0.00 - settings.maximum_lift_coefficient = np.inf - - return settings - - if __name__ == '__main__': main() plt.show() diff --git a/regression/scripts/solar_network/solar_network.py b/regression/scripts/solar_network/solar_network.py index 449f50a371..a9613870fe 100644 --- a/regression/scripts/solar_network/solar_network.py +++ b/regression/scripts/solar_network/solar_network.py @@ -248,8 +248,6 @@ def plot_mission(results,line_style='bo-'): return - - def load_results(): return SUAVE.Input_Output.SUAVE.load('solar_uav_mission.res') diff --git a/regression/scripts/variable_cruise_distance/variable_cruise_distance.py b/regression/scripts/variable_cruise_distance/variable_cruise_distance.py index 85d35be78f..339b46f3e5 100644 --- a/regression/scripts/variable_cruise_distance/variable_cruise_distance.py +++ b/regression/scripts/variable_cruise_distance/variable_cruise_distance.py @@ -64,7 +64,7 @@ def main(): results_SR = mission_SR.evaluate() results_SR = results_SR.merged() - distance_regression_SR = 102203.67525773334 + distance_regression_SR = 102657.56617749507 distance_calc_SR = results_SR.conditions.frames.inertial.position_vector[-1,0] print('distance_calc_SR = ', distance_calc_SR) diff --git a/trunk/SUAVE/Analyses/Propulsion/Rotor_Wake_Fidelity_One.py b/trunk/SUAVE/Analyses/Propulsion/Rotor_Wake_Fidelity_One.py index 72a222425d..82b3197fc7 100644 --- a/trunk/SUAVE/Analyses/Propulsion/Rotor_Wake_Fidelity_One.py +++ b/trunk/SUAVE/Analyses/Propulsion/Rotor_Wake_Fidelity_One.py @@ -12,9 +12,7 @@ from SUAVE.Components.Energy.Energy_Component import Energy_Component from SUAVE.Analyses.Propulsion.Rotor_Wake_Fidelity_Zero import Rotor_Wake_Fidelity_Zero from SUAVE.Methods.Propulsion.Rotor_Wake.Fidelity_One.fidelity_one_wake_convergence import fidelity_one_wake_convergence -from SUAVE.Methods.Propulsion.Rotor_Wake.Fidelity_One.compute_wake_induced_velocity import compute_wake_induced_velocity - -from SUAVE.Methods.Geometry.Two_Dimensional.Cross_Section.Airfoil.import_airfoil_geometry import import_airfoil_geometry +from SUAVE.Methods.Propulsion.Rotor_Wake.Fidelity_One.compute_wake_induced_velocity import compute_wake_induced_velocity from SUAVE.Methods.Aerodynamics.Common.Fidelity_Zero.Lift.extract_wing_VD import extract_wing_collocation_points # package imports @@ -74,7 +72,7 @@ def __defaults__(self): # flags for slipstream interaction self.slipstream = False - self.verbose = True + self.verbose = False def initialize(self,rotor,conditions): """ diff --git a/trunk/SUAVE/Analyses/Propulsion/Rotor_Wake_Fidelity_Two.py b/trunk/SUAVE/Analyses/Propulsion/Rotor_Wake_Fidelity_Two.py index 5fccd77714..0f88cceaf5 100644 --- a/trunk/SUAVE/Analyses/Propulsion/Rotor_Wake_Fidelity_Two.py +++ b/trunk/SUAVE/Analyses/Propulsion/Rotor_Wake_Fidelity_Two.py @@ -52,7 +52,7 @@ def __defaults__(self): self.external_inflow.disc_tangential_induced_velocity = None - def evaluate(self,rotor,U,Ua,Ut,PSI,omega,beta,c,r,R,B,a,nu,a_loc,a_geo,cl_sur,cd_sur,ctrl_pts,Nr,Na,tc,use_2d_analysis,conditions): + def evaluate(self,rotor,U,Ua,Ut,PSI,omega,beta,c,r,R,B,a,nu,airfoils,ctrl_pts,Nr,Na,tc,use_2d_analysis,conditions): """ Wake evaluation is performed using an externally-applied inflow field at the rotor. This requires an external solver to generate the inflow to the rotor, which must have been appended to the diff --git a/trunk/SUAVE/Components/Airfoils/Airfoil.py b/trunk/SUAVE/Components/Airfoils/Airfoil.py index a411c643c1..58b646dd83 100644 --- a/trunk/SUAVE/Components/Airfoils/Airfoil.py +++ b/trunk/SUAVE/Components/Airfoils/Airfoil.py @@ -10,7 +10,7 @@ # ---------------------------------------------------------------------- # Imports # ---------------------------------------------------------------------- - +from SUAVE.Core import Data from SUAVE.Components import Lofted_Body # ------------------------------------------------------------ @@ -38,9 +38,11 @@ def __defaults__(self): N/A """ - self.tag = 'Airfoil' - self.thickness_to_chord = 0.0 - self.naca_4_series_airfoil = None # string of 4 digits defining NACA 4 series airfoil" - self.coordinate_file = None # absolute path - self.points = [] + self.tag = 'Airfoil' + self.coordinate_file = None # absolute path + self.NACA_4_series_flag = False # Flag for NACA 4 series airfoil + self.geometry = None + self.polar_files = None + self.polars = None + self.number_of_points = 200 \ No newline at end of file diff --git a/trunk/SUAVE/Components/Energy/Converters/Rotor.py b/trunk/SUAVE/Components/Energy/Converters/Rotor.py index 229f8c9d06..10d7651325 100644 --- a/trunk/SUAVE/Components/Energy/Converters/Rotor.py +++ b/trunk/SUAVE/Components/Energy/Converters/Rotor.py @@ -18,6 +18,7 @@ # ---------------------------------------------------------------------- from SUAVE.Core import Data from SUAVE.Components.Energy.Energy_Component import Energy_Component +from SUAVE.Core import ContainerOrdered from SUAVE.Analyses.Propulsion.Rotor_Wake_Fidelity_Zero import Rotor_Wake_Fidelity_Zero from SUAVE.Analyses.Propulsion.Rotor_Wake_Fidelity_One import Rotor_Wake_Fidelity_One from SUAVE.Methods.Aerodynamics.Common.Fidelity_Zero.Lift.BET_calculations \ @@ -61,49 +62,80 @@ def __defaults__(self): None """ - self.tag = 'rotor' - 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.mid_chord_alignment = 0.0 - self.blade_solidity = 0.0 - self.design_power = None - self.design_thrust = None - self.airfoil_geometry_data = None - self.airfoil_polar_data = None - self.airfoil_polar_stations = None - self.radius_distribution = None - self.rotation = 1 - self.orientation_euler_angles = [0.,0.,0.] # This is X-direction thrust in vehicle frame - self.ducted = False - self.number_azimuthal_stations = 24 - self.vtk_airfoil_points = 40 - self.induced_power_factor = 1.48 # accounts for interference effects - self.profile_drag_coefficient = .03 - self.sol_tolerance = 1e-8 - self.design_power_coefficient = 0.01 - - - 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.tag = 'rotor' + 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.mid_chord_alignment = 0.0 + self.blade_solidity = 0.0 + self.design_power = None + self.design_thrust = None + self.radius_distribution = None + self.rotation = 1 + self.orientation_euler_angles = [0.,0.,0.] # This is X-direction thrust in vehicle frame + self.ducted = False + self.number_azimuthal_stations = 24 + self.vtk_airfoil_points = 40 + self.induced_power_factor = 1.48 # accounts for interference effects + self.profile_drag_coefficient = .03 + self.sol_tolerance = 1e-8 + self.design_power_coefficient = 0.01 - 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.inputs.y_axis_rotation = 0. - self.inputs.pitch_command = 0. - self.variable_pitch = False + self.Airfoils = ContainerOrdered() + self.airfoil_polar_stations = None + + 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.inputs.y_axis_rotation = 0. + self.inputs.pitch_command = 0. + self.variable_pitch = False # Initialize the default wake set to Fidelity Zero - self.Wake = Rotor_Wake_Fidelity_Zero() - + self.Wake = Rotor_Wake_Fidelity_Zero() + + def append_airfoil(self,airfoil): + """ Adds an airfoil to the rotor + + 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) + def spin(self,conditions): """Analyzes a general rotor given geometry and operating conditions. @@ -173,17 +205,16 @@ def spin(self,conditions): """ # Unpack rotor blade parameters - B = self.number_of_blades - R = self.tip_radius - beta_0 = self.twist_distribution - c = self.chord_distribution - sweep = self.sweep_distribution # quarter chord distance from quarter chord of root airfoil - r_1d = self.radius_distribution - tc = self.thickness_to_chord - - # Unpack rotor airfoil polar data - a_loc = self.airfoil_polar_stations - a_pol_data = self.airfoil_polar_data + B = self.number_of_blades + R = self.tip_radius + beta_0 = self.twist_distribution + c = self.chord_distribution + sweep = self.sweep_distribution # quarter chord distance from quarter chord of root airfoil + r_1d = self.radius_distribution + tc = self.thickness_to_chord + a_loc = self.airfoil_polar_stations + airfoils = self.Airfoils + # Unpack rotor inputs and conditions omega = self.inputs.omega @@ -346,8 +377,7 @@ def spin(self,conditions): # Total velocities Ut = omegar - ut U = np.sqrt(Ua*Ua + Ut*Ut + ur*ur) - - + #--------------------------------------------------------------------------- # COMPUTE WAKE-INDUCED INFLOW VELOCITIES AND RESULTING ROTOR PERFORMANCE #--------------------------------------------------------------------------- @@ -375,7 +405,7 @@ def spin(self,conditions): lamdaw, F, _ = compute_inflow_and_tip_loss(r,R,Wa,Wt,B) # Compute aerodynamic forces based on specified input airfoil or surrogate - Cl, Cdval, alpha, Ma,W = compute_airfoil_aerodynamics(beta,c,r,R,B,Wa,Wt,a,nu,a_loc,a_pol_data,ctrl_pts,Nr,Na,tc,use_2d_analysis) + Cl, Cdval, alpha, Ma,W = compute_airfoil_aerodynamics(beta,c,r,R,B,Wa,Wt,a,nu,airfoils,a_loc,ctrl_pts,Nr,Na,tc,use_2d_analysis) # compute HFW circulation at the blade @@ -655,4 +685,4 @@ def prop_vel_to_body(self): return rot_mat def vec_to_prop_body(self): - return self.prop_vel_to_body() + return self.prop_vel_to_body() \ No newline at end of file diff --git a/trunk/SUAVE/Components/Energy/Storages/Batteries/Constant_Mass/Lithium_Ion_LiNiMnCoO2_18650.py b/trunk/SUAVE/Components/Energy/Storages/Batteries/Constant_Mass/Lithium_Ion_LiNiMnCoO2_18650.py index 7ac22a762a..dc2f5628d6 100644 --- a/trunk/SUAVE/Components/Energy/Storages/Batteries/Constant_Mass/Lithium_Ion_LiNiMnCoO2_18650.py +++ b/trunk/SUAVE/Components/Energy/Storages/Batteries/Constant_Mass/Lithium_Ion_LiNiMnCoO2_18650.py @@ -521,9 +521,9 @@ def create_response_surface(processed_data): battery_map = Data() amps = np.linspace(0, 8, 5) temp = np.linspace(0, 50, 6) + 272.65 - SOC = np.linspace(0, 1, 15) - battery_map.Voltage = RegularGridInterpolator((amps, temp, SOC), processed_data.Voltage, bounds_error=False, fill_value=None) - battery_map.Temperature = RegularGridInterpolator((amps, temp, SOC), processed_data.Temperature, bounds_error=False, fill_value=None) + SOC = np.linspace(0, 1, 15) + battery_map.Voltage = RegularGridInterpolator((amps, temp, SOC), processed_data.Voltage,bounds_error=False,fill_value=None) + battery_map.Temperature = RegularGridInterpolator((amps, temp, SOC), processed_data.Temperature,bounds_error=False,fill_value=None) return battery_map diff --git a/trunk/SUAVE/Core/Utilities.py b/trunk/SUAVE/Core/Utilities.py new file mode 100644 index 0000000000..921238d551 --- /dev/null +++ b/trunk/SUAVE/Core/Utilities.py @@ -0,0 +1,56 @@ +## @ingroup Core +# Utilities.py +# +# Created: Oct 2022, M. Clarke + +# ---------------------------------------------------------------------- +# Imports +# ---------------------------------------------------------------------- +import numpy as np + +def interp2d(x,y,xp,yp,zp,fill_value= None): + """ + Bilinear interpolation on a grid. ``CartesianGrid`` is much faster if the data + lies on a regular grid. + Args: + x, y: 1D arrays of point at which to interpolate. Any out-of-bounds + coordinates will be clamped to lie in-bounds. + xp, yp: 1D arrays of points specifying grid points where function values + are provided. + zp: 2D array of function values. For a function `f(x, y)` this must + satisfy `zp[i, j] = f(xp[i], yp[j])` + Returns: + 1D array `z` satisfying `z[i] = f(x[i], y[i])`. + """ + #if xp.ndim != 1 or yp.ndim != 1: + #raise ValueError("xp and yp must be 1D arrays") + #if zp.shape != (xp.shape + yp.shape): + #raise ValueError("zp must be a 2D array with shape xp.shape + yp.shape") + + ix = np.clip(np.searchsorted(xp, x, side="right"), 1, len(xp) - 1) + iy = np.clip(np.searchsorted(yp, y, side="right"), 1, len(yp) - 1) + + # Using Wikipedia's notation (https://en.wikipedia.org/wiki/Bilinear_interpolation) + z_11 = zp[ix - 1, iy - 1] + z_21 = zp[ix, iy - 1] + z_12 = zp[ix - 1, iy] + z_22 = zp[ix, iy] + + z_xy1 = (xp[ix] - x) / (xp[ix] - xp[ix - 1]) * z_11 + (x - xp[ix - 1]) / ( + xp[ix] - xp[ix - 1] + ) * z_21 + z_xy2 = (xp[ix] - x) / (xp[ix] - xp[ix - 1]) * z_12 + (x - xp[ix - 1]) / ( + xp[ix] - xp[ix - 1] + ) * z_22 + + z = (yp[iy] - y) / (yp[iy] - yp[iy - 1]) * z_xy1 + (y - yp[iy - 1]) / ( + yp[iy] - yp[iy - 1] + ) * z_xy2 + + if fill_value is not None: + oob = np.logical_or( + x < xp[0], np.logical_or(x > xp[-1], np.logical_or(y < yp[0], y > yp[-1])) + ) + z = np.where(oob, fill_value, z) + + return z diff --git a/trunk/SUAVE/Core/__init__.py b/trunk/SUAVE/Core/__init__.py index 6ab49dda3f..a06a84c96d 100644 --- a/trunk/SUAVE/Core/__init__.py +++ b/trunk/SUAVE/Core/__init__.py @@ -8,5 +8,5 @@ from .Diffed_Data import Diffed_Data, diff from .Container import Container from .ContainerOrdered import ContainerOrdered - -from .Units import Units \ No newline at end of file +from .Utilities import * +from .Units import Units \ No newline at end of file diff --git a/trunk/SUAVE/Input_Output/OpenVSP/vsp_nacelle.py b/trunk/SUAVE/Input_Output/OpenVSP/vsp_nacelle.py index 2a8689e57d..3379683a5b 100644 --- a/trunk/SUAVE/Input_Output/OpenVSP/vsp_nacelle.py +++ b/trunk/SUAVE/Input_Output/OpenVSP/vsp_nacelle.py @@ -75,7 +75,7 @@ def write_vsp_nacelle(nacelle, OML_set_ind): num_segs = len(nacelle.Segments) if num_segs > 0: - if nacelle.Airfoil.naca_4_series_airfoil != None: + if nacelle.Airfoil.NACA_4_series_flag == True: raise AssertionError('Nacelle segments defined. Airfoil section will not be used.') nac_id = vsp.AddGeom( "STACK") vsp.SetGeomName(nac_id,nac_tag) @@ -160,16 +160,16 @@ def write_vsp_nacelle(nacelle, OML_set_ind): if ft_flag: vsp.SetParmVal(nac_id,"Mode","Design",0.0) else: - vsp.SetParmVal(nac_id,"Mode","Design",1.0) + vsp.SetParmVal(nac_id,"Mode","Design",1.0) - if nacelle.Airfoil.naca_4_series_airfoil != None: - if isinstance(nacelle.Airfoil.naca_4_series_airfoil, str) and len(nacelle.Airfoil.naca_4_series_airfoil) != 4: + if nacelle.Airfoil.NACA_4_series_flag == True: + if isinstance(nacelle.Airfoil.coordinate_file, str) and len(nacelle.Airfoil.coordinate_file) != 4: raise AssertionError('Nacelle cowling airfoil must be of type < string > and length < 4 >') else: angle = nacelle.cowling_airfoil_angle/Units.degrees - camber = float(nacelle.Airfoil.naca_4_series_airfoil[0])/100 - camber_loc = float(nacelle.Airfoil.naca_4_series_airfoil[1])/10 - thickness = float(nacelle.Airfoil.naca_4_series_airfoil[2:])/100 + camber = float(nacelle.Airfoil.coordinate_file[0])/100 + camber_loc = float(nacelle.Airfoil.coordinate_file[1])/10 + thickness = float(nacelle.Airfoil.coordinate_file[2:])/100 vsp.ChangeBORXSecShape(nac_id ,vsp.XS_FOUR_SERIES) vsp.Update() @@ -331,10 +331,11 @@ def read_vsp_nacelle(nacelle_id,vsp_nacelle_type, units_type='SI'): thickness = int(round(vsp.GetParmVal(nacelle_id, "ThickChord", "XSecCurve")*10,0)) camber = int(round(vsp.GetParmVal(nacelle_id, "Camber", "XSecCurve")*100,0)) camber_loc = int(round( vsp.GetParmVal(nacelle_id, "CamberLoc", "XSecCurve" )*10,0)) + airfoil = str(camber) + str(camber_loc) + str(thickness) + height = thickness - airfoil = str(camber) + str(camber_loc) + str(thickness) - height = thickness - naf.naca_4_series_airfoil = str(airfoil) + naf.coordinate_file = str(airfoil) + naf.NACA_4_series_flag = True naf.thickness_to_chord = thickness nacelle.append_airfoil(naf) diff --git a/trunk/SUAVE/Input_Output/OpenVSP/vsp_wing.py b/trunk/SUAVE/Input_Output/OpenVSP/vsp_wing.py index 1d06af93de..e5f9f8ffd3 100644 --- a/trunk/SUAVE/Input_Output/OpenVSP/vsp_wing.py +++ b/trunk/SUAVE/Input_Output/OpenVSP/vsp_wing.py @@ -13,7 +13,7 @@ # ---------------------------------------------------------------------- import SUAVE -from SUAVE.Core import Units +from SUAVE.Core import Units , Data from SUAVE.Components.Airfoils.Airfoil import Airfoil from SUAVE.Methods.Geometry.Two_Dimensional.Planform import wing_planform, wing_segmented_planform import numpy as np @@ -212,8 +212,9 @@ def read_vsp_wing(wing_id, units_type='SI', write_airfoil_file=True, use_scaling segment.root_chord_percent = (vsp.GetParmVal(wing_id, 'Tip_Chord', 'XSec_' + str(i-1))) * units_factor /root_chord - xsec_id = str(vsp.GetXSec(xsec_surf_id, jj)) - airfoil = Airfoil() + xsec_id = str(vsp.GetXSec(xsec_surf_id, jj)) + airfoil = Airfoil() + airfoil.geometry = Data() if vsp.GetXSecShape(xsec_id) == vsp.XS_FOUR_SERIES: # XSec shape: NACA 4-series camber = vsp.GetParmVal(wing_id, 'Camber', 'XSecCurve_' + str(jj)) @@ -222,7 +223,7 @@ def read_vsp_wing(wing_id, units_type='SI', write_airfoil_file=True, use_scaling else: camber_loc = vsp.GetParmVal(wing_id, 'CamberLoc', 'XSecCurve_' + str(jj)) - airfoil.thickness_to_chord = thick_cord + airfoil.geometry.thickness_to_chord = thick_cord camber_round = int(np.around(camber*100)) camber_loc_round = int(np.around(camber_loc*10)) thick_cord_round = int(np.around(thick_cord*100)) @@ -239,7 +240,7 @@ def read_vsp_wing(wing_id, units_type='SI', write_airfoil_file=True, use_scaling elif vsp.GetXSecShape(xsec_id) == vsp.XS_FILE_AIRFOIL: # XSec shape: 12 is type AF_FILE - airfoil.thickness_to_chord = thick_cord + airfoil.geometry.thickness_to_chord = thick_cord # VSP airfoil API calls get coordinates and write files with the final argument being the fraction of segment position, regardless of relative spans. # (Write the root airfoil with final arg = 0. Write 4th airfoil of 5 segments with final arg = .8) diff --git a/trunk/SUAVE/Methods/Aerodynamics/AVL/write_avl_airfoil_file.py b/trunk/SUAVE/Methods/Aerodynamics/AVL/write_avl_airfoil_file.py index ee7696863b..8c0668851d 100644 --- a/trunk/SUAVE/Methods/Aerodynamics/AVL/write_avl_airfoil_file.py +++ b/trunk/SUAVE/Methods/Aerodynamics/AVL/write_avl_airfoil_file.py @@ -48,8 +48,8 @@ def write_avl_airfoil_file(suave_airfoil_filename): airfoil_name = data_block[0].strip() # import airfoil coordinates - airfoil_geometry_data = import_airfoil_geometry([f_path]) - dim = len(airfoil_geometry_data.x_coordinates[airfoil_geometry_data.airfoil_names[0]]) + airfoil_geometry_data = import_airfoil_geometry(f_path) + dim = len(airfoil_geometry_data.x_coordinates) # write file with open(avl_airfoil_filename,'w') as afile: @@ -57,11 +57,11 @@ def write_avl_airfoil_file(suave_airfoil_filename): for i in range(dim - 1): if i == int(dim/2): pass - elif airfoil_geometry_data.y_coordinates[airfoil_geometry_data.airfoil_names[0]][i] < 0.0: - case_text = '\t' + format(airfoil_geometry_data.x_coordinates[airfoil_geometry_data.airfoil_names[0]][i], '.7f')+ " " + format(airfoil_geometry_data.y_coordinates[airfoil_geometry_data.airfoil_names[0]][i], '.7f') + "\n" + elif airfoil_geometry_data.y_coordinates[i] < 0.0: + case_text = '\t' + format(airfoil_geometry_data.x_coordinates[i], '.7f')+ " " + format(airfoil_geometry_data.y_coordinates[i], '.7f') + "\n" afile.write(case_text) else: - case_text = '\t' + format(airfoil_geometry_data.x_coordinates[airfoil_geometry_data.airfoil_names[0]][i], '.7f')+ " " + format(airfoil_geometry_data.y_coordinates[airfoil_geometry_data.airfoil_names[0]][i], '.7f') + "\n" + case_text = '\t' + format(airfoil_geometry_data.x_coordinates[i], '.7f')+ " " + format(airfoil_geometry_data.y_coordinates[i], '.7f') + "\n" afile.write(case_text) afile.close() return avl_airfoil_filename diff --git a/trunk/SUAVE/Methods/Aerodynamics/Airfoil_Panel_Method/aero_coeff.py b/trunk/SUAVE/Methods/Aerodynamics/Airfoil_Panel_Method/aero_coeff.py index 671f2c017f..5330f59cbc 100644 --- a/trunk/SUAVE/Methods/Aerodynamics/Airfoil_Panel_Method/aero_coeff.py +++ b/trunk/SUAVE/Methods/Aerodynamics/Airfoil_Panel_Method/aero_coeff.py @@ -2,6 +2,7 @@ # aero_coeff.py # Created: Mar 2021, M. Clarke +# Modified: Sep 2022, M. Clarke # ---------------------------------------------------------------------- # Imports @@ -44,22 +45,22 @@ def aero_coeff(x,y,cp,al,npanel): dy = y[1:]-y[:-1] xa = 0.5*(x[1:] +x[:-1])-0.25 ya = 0.5*(y[1:] +y[:-1]) - dcn = cp[:-1]*dx - dca = -cp[:-1]*dy + dcn = -cp[:-1]*dx + dca = cp[:-1]*dy # compute differential forces - cn = np.sum(dcn,axis=0) - ca = np.sum(dca,axis=0) - cm = np.sum((-dcn*xa + dca*ya),axis=0) + cn = np.sum(dcn,axis=0).T + ca = np.sum(dca,axis=0).T + cm = np.sum((-dcn*xa + dca*ya),axis=0).T # orient normal and axial forces cl = cn*np.cos(al) - ca*np.sin(al) - cd = cn*np.sin(al) + ca*np.cos(al) + cdpi = cn*np.sin(al) + ca*np.cos(al) # pack results AERO_RES = Data( - Cl = cl, - Cd = cd, - Cm = cm) + cl = cl, + cdpi = cdpi, + cm = cm) return AERO_RES \ No newline at end of file diff --git a/trunk/SUAVE/Methods/Aerodynamics/Airfoil_Panel_Method/airfoil_analysis.py b/trunk/SUAVE/Methods/Aerodynamics/Airfoil_Panel_Method/airfoil_analysis.py index 755f426c63..a2ff48f7db 100644 --- a/trunk/SUAVE/Methods/Aerodynamics/Airfoil_Panel_Method/airfoil_analysis.py +++ b/trunk/SUAVE/Methods/Aerodynamics/Airfoil_Panel_Method/airfoil_analysis.py @@ -2,6 +2,7 @@ # airfoil_analysis.py # Created: Mar 2021, M. Clarke +# Modified: Sep 2022, M. Clarke # ---------------------------------------------------------------------- # Imports @@ -21,13 +22,15 @@ # ---------------------------------------------------------------------- ## @ingroup Methods-Aerodynamics-Airfoil_Panel_Method -def airfoil_analysis(airfoil_data,alpha,Re_L,npanel = 100 , batch_analysis = True, airfoil_stations = [0], - initial_momentum_thickness=1E-5,tolerance = 1E0): +def airfoil_analysis(airfoil_geometry,alpha,Re_L,initial_momentum_thickness=1E-5,tolerance = 1E0,H_wake = 1.05,Ue_wake = 0.99): """This computes the aerodynamic polars as well as the boundary layer properties of an airfoil at a defined set of reynolds numbers and angle of attacks Assumptions: - Michel Criteria used for transition + Michel Criteria used for transition + + Squire-Young relation for total drag (exrapolates theta from end of wake). + However, since we do not have a wake we will assume H_wake = 1.05 and Ue_wake = 0.99 Source: N/A @@ -35,8 +38,7 @@ def airfoil_analysis(airfoil_data,alpha,Re_L,npanel = 100 , batch_analysis = Tru Inputs: airfoil_geometry - airfoil geometry points [unitless] alpha - angle of attacks [radians] - Re_L - Reynolds numbers [unitless] - npanel - number of airfoil panels [unitless] + Re_L - Reynolds numbers [unitless] batch_analysis - boolean : If True: the specified number of angle of attacks and Reynolds [boolean] numbers are used to create a table of 2-D results for each combination Note: Can only accomodate one airfoil @@ -71,32 +73,25 @@ def airfoil_analysis(airfoil_data,alpha,Re_L,npanel = 100 , batch_analysis = Tru Properties Used: N/A - """ - - nalpha = len(alpha) - nRe = len(Re_L) - x_coord = np.take(airfoil_data.x_coordinates.values(),airfoil_stations,axis=0).T - y_coord = np.take(airfoil_data.y_coordinates.values(),airfoil_stations,axis=0).T - x_coord = np.delete(x_coord[::-1], int(npanel/2),0) - y_coord = np.delete(y_coord[::-1], int(npanel/2),0) - - if batch_analysis: - x_coord_3d = np.repeat(np.repeat(np.atleast_2d(x_coord),nalpha,axis = 1)[:,:,np.newaxis],nRe, axis = 2) - y_coord_3d = np.repeat(np.repeat(np.atleast_2d(y_coord),nalpha,axis = 1)[:,:,np.newaxis],nRe, axis = 2) - else: - nairfoil = len(airfoil_stations) - if (nalpha != nRe) and ( nairfoil!= nalpha): - raise AssertionError('Dimension of angle of attacks,Reynolds numbers and airfoil stations must all be equal') - x_coord_3d = np.repeat(x_coord[:,:,np.newaxis],nRe, axis = 2) - y_coord_3d = np.repeat(y_coord[:,:,np.newaxis],nRe, axis = 2) + """ + ncases = len(alpha[0,:]) + ncpts = len(Re_L) + x_coord = airfoil_geometry.x_coordinates + y_coord = airfoil_geometry.y_coordinates + npanel = len(x_coord)-1 + + if (ncases != len(Re_L[0,:]) ): + raise AssertionError('Number of angle of attacks and Reynolds numbers must be equal') + x_coord_3d = np.tile(x_coord[:,None,None],(1,ncases,ncpts)) # number of points, number of cases, number of control points + y_coord_3d = np.tile(y_coord[:,None,None],(1,ncases,ncpts)) # number of points, number of cases, number of control points # Begin by solving for velocity distribution at airfoil surface using inviscid panel simulation # these are the locations (faces) where things are computed , len = n panel - # dimension of vt = npanel x nalpha x nRe - X,Y,vt,normals = hess_smith(x_coord_3d,y_coord_3d,alpha,Re_L,npanel,batch_analysis) + # dimension of vt = npanel x ncases x ncpts + X,Y,vt,normals = hess_smith(x_coord_3d,y_coord_3d,alpha,Re_L,npanel) # Reynolds number - RE_L_VALS = np.repeat(Re_L.T,nalpha, axis = 0) + RE_L_VALS = Re_L.T # --------------------------------------------------------------------- # Bottom surface of airfoil @@ -114,8 +109,8 @@ def airfoil_analysis(airfoil_data,alpha,Re_L,npanel = 100 , batch_analysis = Tru first_panel = list(prev_index.flatten()) last_panel = list((first_idx-1 + mask_count).flatten()) last_paneldve = list((first_idx-2 + mask_count).flatten()) - aoas = list(np.repeat(np.arange(nalpha),nRe)) - res = list(np.tile(np.arange(nRe),nalpha) ) + aoas = list(np.repeat(np.arange(ncases),ncpts)) + res = list(np.tile(np.arange(ncpts),ncases) ) X_BOT.mask[first_panel,aoas,res] = False # flow velocity and pressure of on botton surface @@ -135,7 +130,7 @@ def airfoil_analysis(airfoil_data,alpha,Re_L,npanel = 100 , batch_analysis = Tru L_BOT = X_BOT[-1,:,:] # laminar boundary layer properties using thwaites method - BOT_T_RESULTS = thwaites_method(npanel,nalpha,nRe, L_BOT , RE_L_VALS, X_BOT, VE_BOT, DVE_BOT,batch_analysis,tolerance, + BOT_T_RESULTS = thwaites_method(npanel,ncases,ncpts, L_BOT , RE_L_VALS, X_BOT, VE_BOT, DVE_BOT,tolerance, THETA_0=initial_momentum_thickness) X_T_BOT = BOT_T_RESULTS.X_T THETA_T_BOT = BOT_T_RESULTS.THETA_T @@ -152,20 +147,20 @@ def airfoil_analysis(airfoil_data,alpha,Re_L,npanel = 100 , batch_analysis = Tru mask_count = np.ma.count(CRITERION_BOT,axis = 0) mask_count[mask_count == npanel] = npanel-1 transition_panel = list(mask_count.flatten()) - aoas = list(np.repeat(np.arange(nalpha),nRe)) - res = list(np.tile(np.arange(nRe),nalpha)) + aoas = list(np.repeat(np.arange(ncases),ncpts)) + res = list(np.tile(np.arange(ncpts),ncases)) - X_TR_BOT = X_T_BOT[transition_panel,aoas,res].reshape(nalpha,nRe) - DELTA_STAR_TR_BOT = DELTA_STAR_T_BOT[transition_panel,aoas,res].reshape(nalpha,nRe) - THETA_TR_BOT = THETA_T_BOT[transition_panel,aoas,res].reshape(nalpha,nRe) - DELTA_TR_BOT = DELTA_T_BOT[transition_panel,aoas,res].reshape(nalpha,nRe) + X_TR_BOT = X_T_BOT[transition_panel,aoas,res].reshape(ncases,ncpts) + DELTA_STAR_TR_BOT = DELTA_STAR_T_BOT[transition_panel,aoas,res].reshape(ncases,ncpts) + THETA_TR_BOT = THETA_T_BOT[transition_panel,aoas,res].reshape(ncases,ncpts) + DELTA_TR_BOT = DELTA_T_BOT[transition_panel,aoas,res].reshape(ncases,ncpts) TURBULENT_SURF = L_BOT.data - X_TR_BOT TURBULENT_COORD = np.ma.masked_less(X_BOT.data - X_TR_BOT,0) # turbulent boundary layer properties using heads method - BOT_H_RESULTS = heads_method(npanel,nalpha,nRe,DELTA_TR_BOT ,THETA_TR_BOT , DELTA_STAR_TR_BOT, - TURBULENT_SURF, RE_L_VALS,TURBULENT_COORD, VE_BOT, DVE_BOT, batch_analysis,tolerance) + BOT_H_RESULTS = heads_method(npanel,ncases,ncpts,DELTA_TR_BOT ,THETA_TR_BOT , DELTA_STAR_TR_BOT, + TURBULENT_SURF, RE_L_VALS,TURBULENT_COORD, VE_BOT, DVE_BOT,tolerance) X_H_BOT = BOT_H_RESULTS.X_H THETA_H_BOT = BOT_H_RESULTS.THETA_H @@ -229,15 +224,14 @@ def airfoil_analysis(airfoil_data,alpha,Re_L,npanel = 100 , batch_analysis = Tru RE_X_BOT_SURF_2 = RE_X_BOT_SURF_1.data[~RE_X_BOT_SURF_1.mask] DELTA_BOT_SURF_2 = DELTA_BOT_SURF_1.data[~DELTA_BOT_SURF_1.mask] - X_BOT_SURF = X_BOT_SURF_2.reshape((npanel,nalpha,nRe),order = 'F') - Y_BOT_SURF = Y_BOT - THETA_BOT_SURF = THETA_BOT_SURF_2.reshape((npanel,nalpha,nRe),order = 'F') - DELTA_STAR_BOT_SURF = DELTA_STAR_BOT_SURF_2.reshape((npanel,nalpha,nRe),order = 'F') - H_BOT_SURF = H_BOT_SURF_2.reshape((npanel,nalpha,nRe),order = 'F') - CF_BOT_SURF = CF_BOT_SURF_2.reshape((npanel,nalpha,nRe),order = 'F') - RE_THETA_BOT_SURF = RE_THETA_BOT_SURF_2.reshape((npanel,nalpha,nRe),order = 'F') - RE_X_BOT_SURF = RE_X_BOT_SURF_2.reshape((npanel,nalpha,nRe),order = 'F') - DELTA_BOT_SURF = DELTA_BOT_SURF_2.reshape((npanel,nalpha,nRe),order = 'F') + X_BOT_SURF = X_BOT_SURF_2.reshape((npanel,ncases,ncpts),order = 'F') + THETA_BOT_SURF = THETA_BOT_SURF_2.reshape((npanel,ncases,ncpts),order = 'F') + DELTA_STAR_BOT_SURF = DELTA_STAR_BOT_SURF_2.reshape((npanel,ncases,ncpts),order = 'F') + H_BOT_SURF = H_BOT_SURF_2.reshape((npanel,ncases,ncpts),order = 'F') + CF_BOT_SURF = CF_BOT_SURF_2.reshape((npanel,ncases,ncpts),order = 'F') + RE_THETA_BOT_SURF = RE_THETA_BOT_SURF_2.reshape((npanel,ncases,ncpts),order = 'F') + RE_X_BOT_SURF = RE_X_BOT_SURF_2.reshape((npanel,ncases,ncpts),order = 'F') + DELTA_BOT_SURF = DELTA_BOT_SURF_2.reshape((npanel,ncases,ncpts),order = 'F') # ------------------------------------------------------------------------------------------------------ # Top surface of airfoil @@ -255,8 +249,8 @@ def airfoil_analysis(airfoil_data,alpha,Re_L,npanel = 100 , batch_analysis = Tru first_panel = list(prev_index.flatten()) last_panel = list((first_idx-1 + mask_count).flatten()) last_paneldve = list((first_idx-2 + mask_count).flatten()) - aoas = list(np.repeat(np.arange(nalpha),nRe)) - res = list(np.tile(np.arange(nRe),nalpha) ) + aoas = list(np.repeat(np.arange(ncases),ncpts)) + res = list(np.tile(np.arange(ncpts),ncases) ) X_TOP.mask[first_panel,aoas,res] = False # flow velocity and pressure of on botton surface @@ -276,7 +270,7 @@ def airfoil_analysis(airfoil_data,alpha,Re_L,npanel = 100 , batch_analysis = Tru L_TOP = X_TOP[-1,:,:] # laminar boundary layer properties using thwaites method - TOP_T_RESULTS = thwaites_method(npanel,nalpha,nRe, L_TOP , RE_L_VALS,X_TOP,VE_TOP, DVE_TOP,batch_analysis,tolerance, + TOP_T_RESULTS = thwaites_method(npanel,ncases,ncpts, L_TOP , RE_L_VALS,X_TOP,VE_TOP, DVE_TOP,tolerance, THETA_0=initial_momentum_thickness) X_T_TOP = TOP_T_RESULTS.X_T THETA_T_TOP = TOP_T_RESULTS.THETA_T @@ -293,20 +287,20 @@ def airfoil_analysis(airfoil_data,alpha,Re_L,npanel = 100 , batch_analysis = Tru mask_count = np.ma.count(CRITERION_TOP,axis = 0) mask_count[mask_count == npanel] = npanel-1 transition_panel = list(mask_count.flatten()) - aoas = list(np.repeat(np.arange(nalpha),nRe)) - res = list(np.tile(np.arange(nRe),nalpha) ) + aoas = list(np.repeat(np.arange(ncases),ncpts)) + res = list(np.tile(np.arange(ncpts),ncases) ) - X_TR_TOP = X_T_TOP[transition_panel,aoas,res].reshape(nalpha,nRe) - DELTA_STAR_TR_TOP = DELTA_STAR_T_TOP[transition_panel,aoas,res].reshape(nalpha,nRe) - THETA_TR_TOP = THETA_T_TOP[transition_panel,aoas,res].reshape(nalpha,nRe) - DELTA_TR_TOP = DELTA_T_TOP[transition_panel,aoas,res].reshape(nalpha,nRe) + X_TR_TOP = X_T_TOP[transition_panel,aoas,res].reshape(ncases,ncpts) + DELTA_STAR_TR_TOP = DELTA_STAR_T_TOP[transition_panel,aoas,res].reshape(ncases,ncpts) + THETA_TR_TOP = THETA_T_TOP[transition_panel,aoas,res].reshape(ncases,ncpts) + DELTA_TR_TOP = DELTA_T_TOP[transition_panel,aoas,res].reshape(ncases,ncpts) TURBULENT_SURF = L_TOP.data - X_TR_TOP TURBULENT_COORD = np.ma.masked_less( X_TOP.data - X_TR_TOP,0) # turbulent boundary layer properties using heads method - TOP_H_RESULTS = heads_method(npanel,nalpha,nRe,DELTA_TR_TOP ,THETA_TR_TOP , DELTA_STAR_TR_TOP, - TURBULENT_SURF, RE_L_VALS,TURBULENT_COORD, VE_TOP, DVE_TOP, batch_analysis,tolerance) + TOP_H_RESULTS = heads_method(npanel,ncases,ncpts,DELTA_TR_TOP ,THETA_TR_TOP , DELTA_STAR_TR_TOP, + TURBULENT_SURF, RE_L_VALS,TURBULENT_COORD, VE_TOP, DVE_TOP,tolerance) X_H_TOP = TOP_H_RESULTS.X_H THETA_H_TOP = TOP_H_RESULTS.THETA_H @@ -369,29 +363,26 @@ def airfoil_analysis(airfoil_data,alpha,Re_L,npanel = 100 , batch_analysis = Tru RE_X_TOP_SURF_2 = RE_X_TOP_SURF_1.data[~RE_X_TOP_SURF_1.mask] DELTA_TOP_SURF_2 = DELTA_TOP_SURF_1.data[~DELTA_TOP_SURF_1.mask] - X_TOP_SURF = X_TOP_SURF_2.reshape((npanel,nalpha,nRe),order = 'F') - Y_TOP_SURF = Y_TOP - THETA_TOP_SURF = THETA_TOP_SURF_2.reshape((npanel,nalpha,nRe),order = 'F') - DELTA_STAR_TOP_SURF = DELTA_STAR_TOP_SURF_2.reshape((npanel,nalpha,nRe),order = 'F') - H_TOP_SURF = H_TOP_SURF_2.reshape((npanel,nalpha,nRe),order = 'F') - CF_TOP_SURF = CF_TOP_SURF_2.reshape((npanel,nalpha,nRe),order = 'F') - RE_THETA_TOP_SURF = RE_THETA_TOP_SURF_2.reshape((npanel,nalpha,nRe),order = 'F') - RE_X_TOP_SURF = RE_X_TOP_SURF_2.reshape((npanel,nalpha,nRe),order = 'F') - DELTA_TOP_SURF = DELTA_TOP_SURF_2.reshape((npanel,nalpha,nRe),order = 'F') + X_TOP_SURF = X_TOP_SURF_2.reshape((npanel,ncases,ncpts),order = 'F') + THETA_TOP_SURF = THETA_TOP_SURF_2.reshape((npanel,ncases,ncpts),order = 'F') + DELTA_STAR_TOP_SURF = DELTA_STAR_TOP_SURF_2.reshape((npanel,ncases,ncpts),order = 'F') + H_TOP_SURF = H_TOP_SURF_2.reshape((npanel,ncases,ncpts),order = 'F') + CF_TOP_SURF = CF_TOP_SURF_2.reshape((npanel,ncases,ncpts),order = 'F') + RE_THETA_TOP_SURF = RE_THETA_TOP_SURF_2.reshape((npanel,ncases,ncpts),order = 'F') + RE_X_TOP_SURF = RE_X_TOP_SURF_2.reshape((npanel,ncases,ncpts),order = 'F') + DELTA_TOP_SURF = DELTA_TOP_SURF_2.reshape((npanel,ncases,ncpts),order = 'F') # ------------------------------------------------------------------------------------------------------ # concatenate lower and upper surfaces # ------------------------------------------------------------------------------------------------------ - X_PANEL = concatenate_surfaces(X_BOT,X_TOP,X_BOT_SURF,X_TOP_SURF,npanel,nalpha,nRe,batch_analysis) - Y_PANEL = concatenate_surfaces(X_BOT,X_TOP,Y_BOT_SURF,Y_TOP_SURF,npanel,nalpha,nRe,batch_analysis) - THETA = concatenate_surfaces(X_BOT,X_TOP,THETA_BOT_SURF,THETA_TOP_SURF,npanel,nalpha,nRe,batch_analysis) - DELTA_STAR = concatenate_surfaces(X_BOT,X_TOP,DELTA_STAR_BOT_SURF,DELTA_STAR_TOP_SURF,npanel,nalpha,nRe,batch_analysis) - H = concatenate_surfaces(X_BOT,X_TOP,H_BOT_SURF,H_TOP_SURF,npanel,nalpha,nRe,batch_analysis) - CF = concatenate_surfaces(X_BOT,X_TOP,CF_BOT_SURF,CF_TOP_SURF,npanel,nalpha,nRe,batch_analysis) - RE_THETA = concatenate_surfaces(X_BOT,X_TOP,RE_THETA_BOT_SURF,RE_THETA_TOP_SURF,npanel,nalpha,nRe,batch_analysis) - RE_X = concatenate_surfaces(X_BOT,X_TOP,RE_X_BOT_SURF,RE_X_TOP_SURF,npanel,nalpha,nRe,batch_analysis) - DELTA = concatenate_surfaces(X_BOT,X_TOP,DELTA_BOT_SURF,DELTA_TOP_SURF,npanel,nalpha,nRe,batch_analysis) + THETA = concatenate_surfaces(X_BOT,X_TOP,THETA_BOT_SURF,THETA_TOP_SURF,npanel,ncases,ncpts) + DELTA_STAR = concatenate_surfaces(X_BOT,X_TOP,DELTA_STAR_BOT_SURF,DELTA_STAR_TOP_SURF,npanel,ncases,ncpts) + H = concatenate_surfaces(X_BOT,X_TOP,H_BOT_SURF,H_TOP_SURF,npanel,ncases,ncpts) + CF = concatenate_surfaces(X_BOT,X_TOP,CF_BOT_SURF,CF_TOP_SURF,npanel,ncases,ncpts) + RE_THETA = concatenate_surfaces(X_BOT,X_TOP,RE_THETA_BOT_SURF,RE_THETA_TOP_SURF,npanel,ncases,ncpts) + RE_X = concatenate_surfaces(X_BOT,X_TOP,RE_X_BOT_SURF,RE_X_TOP_SURF,npanel,ncases,ncpts) + DELTA = concatenate_surfaces(X_BOT,X_TOP,DELTA_BOT_SURF,DELTA_TOP_SURF,npanel,ncases,ncpts) VE_VALS = np.ma.concatenate([np.flip(VE_BOT,axis = 0),VE_TOP ], axis = 0) DVE_VALS = np.ma.concatenate([np.flip(DVE_BOT,axis = 0),DVE_TOP], axis = 0) @@ -399,31 +390,26 @@ def airfoil_analysis(airfoil_data,alpha,Re_L,npanel = 100 , batch_analysis = Tru DVE_VALS_1 = DVE_VALS.flatten('F') VE_VALS_2 = VE_VALS_1.data[~VE_VALS_1.mask] DVE_VALS_2 = DVE_VALS_1.data[~DVE_VALS_1.mask] - VE = VE_VALS_2.reshape((npanel,nalpha,nRe),order = 'F') - DVE = DVE_VALS_2.reshape((npanel,nalpha,nRe),order = 'F') + VE = VE_VALS_2.reshape((npanel,ncases,ncpts),order = 'F') + DVE = DVE_VALS_2.reshape((npanel,ncases,ncpts),order = 'F') # ------------------------------------------------------------------------------------------------------ # Compute effective surface of airfoil with boundary layer and recompute aerodynamic properties # ------------------------------------------------------------------------------------------------------ DELTA = np.nan_to_num(DELTA) # make sure no nans - DELTA_PTS = np.concatenate((DELTA,DELTA[-1][np.newaxis,:,:]),axis = 0) - DELTA_PTS = np.concatenate((DELTA[0][np.newaxis,:,:],DELTA_PTS),axis = 0) - NORMALS_PTS = np.concatenate((normals,normals[-1][np.newaxis,:,:]),axis = 0) - NORMALS_PTS = np.concatenate((normals[0][np.newaxis,:,:],NORMALS_PTS),axis = 0) - POINT_NORMALS = 0.5*(NORMALS_PTS[1:] + NORMALS_PTS[:-1]) - POINT_BLS = 0.5*(DELTA_PTS[1:] + DELTA_PTS[:-1]) - y_coord_3d_bl = y_coord_3d+ POINT_BLS*POINT_NORMALS[:,1,:,:] - x_coord_3d_bl = x_coord_3d+ POINT_BLS*POINT_NORMALS[:,0,:,:] - - X_BL, Y_BL,vt_bl,normals_bl = hess_smith(x_coord_3d_bl,y_coord_3d_bl,alpha,Re_L,npanel,batch_analysis) + y_coord_3d_bl = Y + DELTA*normals[:,1,:,:] + x_coord_3d_bl = X + DELTA*normals[:,0,:,:] + npanel_mod = npanel-1 + + X_BL, Y_BL,vt_bl,normals_bl = hess_smith(x_coord_3d_bl,y_coord_3d_bl,alpha,Re_L,npanel_mod) # --------------------------------------------------------------------- # Bottom surface of airfoil with boundary layer # --------------------------------------------------------------------- VT_BL = np.ma.masked_greater(vt_bl,0 ) VT_BL_mask = np.ma.masked_greater(vt_bl,0 ).mask - X_BL_BOT_VALS = np.ma.array(X_BL, mask = VT_mask)[::-1] - Y_BL_BOT = np.ma.array(Y_BL, mask = VT_mask)[::-1] + X_BL_BOT_VALS = np.ma.array(X_BL, mask = VT_BL_mask)[::-1] + Y_BL_BOT = np.ma.array(Y_BL, mask = VT_BL_mask)[::-1] X_BL_BOT = np.zeros_like(X_BL_BOT_VALS) X_BL_BOT[1:] = np.cumsum(np.sqrt((X_BL_BOT_VALS[1:] - X_BL_BOT_VALS[:-1])**2 + (Y_BL_BOT[1:] - Y_BL_BOT[:-1])**2),axis = 0) first_idx = np.ma.count_masked(X_BL_BOT,axis = 0) @@ -432,8 +418,8 @@ def airfoil_analysis(airfoil_data,alpha,Re_L,npanel = 100 , batch_analysis = Tru first_panel = list(prev_index.flatten()) last_panel = list((first_idx-1 + mask_count).flatten()) last_paneldve = list((first_idx-2 + mask_count).flatten()) - aoas = list(np.repeat(np.arange(nalpha),nRe)) - res = list(np.tile(np.arange(nRe),nalpha) ) + aoas = list(np.repeat(np.arange(ncases),ncpts)) + res = list(np.tile(np.arange(ncpts),ncases) ) X_BL_BOT.mask[first_panel,aoas,res] = False # flow velocity and pressure of on botton surface @@ -443,21 +429,21 @@ def airfoil_analysis(airfoil_data,alpha,Re_L,npanel = 100 , batch_analysis = Tru # --------------------------------------------------------------------- # Top surface of airfoil with boundary layer # --------------------------------------------------------------------- - VT_BL = np.ma.masked_less(vt_bl,0 ) - VT_BL_mask = np.ma.masked_less(vt_bl,0 ).mask - X_BL_TOP_VALS = np.ma.array(X_BL, mask = VT_BL_mask) - Y_BL_TOP = np.ma.array(Y_BL, mask = VT_BL_mask) - - X_BL_TOP = np.zeros_like(X_BL_TOP_VALS) - X_BL_TOP[1:] = np.cumsum(np.sqrt((X_BL_TOP_VALS[1:] - X_BL_TOP_VALS[:-1])**2 + (Y_BL_TOP[1:] - Y_BL_TOP[:-1])**2),axis = 0) + VT_BL = np.ma.masked_less(vt_bl,0 ) + VT_BL_mask = np.ma.masked_less(vt_bl,0 ).mask + X_BL_TOP_VALS = np.ma.array(X_BL, mask = VT_BL_mask) + Y_BL_TOP = np.ma.array(Y_BL, mask = VT_BL_mask) + + X_BL_TOP = np.zeros_like(X_BL_TOP_VALS) + X_BL_TOP[1:] = np.cumsum(np.sqrt((X_BL_TOP_VALS[1:] - X_BL_TOP_VALS[:-1])**2 + (Y_BL_TOP[1:] - Y_BL_TOP[:-1])**2),axis = 0) first_idx = np.ma.count_masked(X_BL_TOP,axis = 0) mask_count = np.ma.count(X_BL_TOP,axis = 0) prev_index = first_idx-1 first_panel = list(prev_index.flatten()) last_panel = list((first_idx-1 + mask_count).flatten()) last_paneldve = list((first_idx-2 + mask_count).flatten()) - aoas = list(np.repeat(np.arange(nalpha),nRe)) - res = list(np.tile(np.arange(nRe),nalpha) ) + aoas = list(np.repeat(np.arange(ncases),ncpts)) + res = list(np.tile(np.arange(ncpts),ncases) ) X_BL_TOP.mask[first_panel,aoas,res] = False # flow velocity and pressure of on botton surface @@ -467,42 +453,43 @@ def airfoil_analysis(airfoil_data,alpha,Re_L,npanel = 100 , batch_analysis = Tru CP_BL_VALS = np.ma.concatenate([np.flip(CP_BL_BOT,axis = 0),CP_BL_TOP], axis = 0 ) CP_BL_VALS_1 = CP_BL_VALS.flatten('F') CP_BL_VALS_2 = CP_BL_VALS_1.data[~CP_BL_VALS_1.mask] - CP_BL = CP_BL_VALS_2.reshape((npanel,nalpha,nRe),order = 'F') - - AERO_RES_BL = aero_coeff(X,Y,-CP_BL,alpha,npanel) + CP_BL = CP_BL_VALS_2.reshape((npanel_mod,ncases,ncpts),order = 'F') + DCP_DX = np.diff(CP_BL,axis=0)/ np.diff(X_BL,axis=0) + AERO_RES_BL = aero_coeff(X_BL,Y_BL,CP_BL,alpha,npanel_mod) + + # Squire-Young relation for total drag + cd = 2.0*THETA[-1,:,:]*(Ue_wake)**((5+H_wake)/2.) + airfoil_properties = Data( - AoA = alpha, - Re = Re_L, - Cl = AERO_RES_BL.Cl, - Cd = AERO_RES_BL.Cd, - Cm = AERO_RES_BL.Cm, - normals = np.transpose(normals,(2,3,0,1)), - x = np.transpose(X,(1,2,0)), - y = np.transpose(Y,(1,2,0)), - x_bl = np.transpose(X_BL ,(1,2,0)), - y_bl = np.transpose(Y_BL ,(1,2,0)), - Cp = np.transpose(CP_BL,(1,2,0)), - Ue_Vinf = np.transpose(VE ,(1,2,0)), - dVe = np.transpose(DVE ,(1,2,0)), - theta = np.transpose(THETA,(1,2,0)), - delta_star = np.transpose(DELTA_STAR,(1,2,0)), - delta = np.transpose(DELTA,(1,2,0)), - Re_theta = np.transpose(RE_THETA,(1,2,0)), - Re_x = np.transpose(RE_X,(1,2,0)), - H = np.transpose(H,(1,2,0)), - Cf = np.transpose(CF,(1,2,0)), + AoA = alpha, + Re = Re_L, + cl = AERO_RES_BL.cl, + cdpi = AERO_RES_BL.cdpi, + cd = cd.T, + cm = AERO_RES_BL.cm, + normals = np.transpose(normals,(3,2,0,1)), + x = np.transpose(X,(2,1,0)), + y = np.transpose(Y,(2,1,0)), + x_bl = np.transpose(X_BL ,(2,1,0)), + y_bl = np.transpose(Y_BL ,(2,1,0)), + cp = np.transpose(CP_BL,(2,1,0)), + dcp_dx = np.transpose(DCP_DX,(2,1,0)), + Ue_Vinf = np.transpose(VE ,(2,1,0)), + dVe = np.transpose(DVE ,(2,1,0)), + theta = np.transpose(THETA,(2,1,0)), + delta_star = np.transpose(DELTA_STAR,(2,1,0)), + delta = np.transpose(DELTA,(2,1,0)), + Re_theta = np.transpose(RE_THETA,(2,1,0)), + Re_x = np.transpose(RE_X,(2,1,0)), + H = np.transpose(H,(2,1,0)), + cf = np.transpose(CF,(2,1,0)), ) - - if batch_analysis: - pass - else: - airfoil_properties = extract_values(airfoil_properties) return airfoil_properties -def concatenate_surfaces(X_BOT,X_TOP,FUNC_BOT_SURF,FUNC_TOP_SURF,npanel,nalpha,nRe,batch_analysis): +def concatenate_surfaces(X_BOT,X_TOP,FUNC_BOT_SURF,FUNC_TOP_SURF,npanel,ncases,ncpts): '''Interpolation of airfoil properties Assumptions: @@ -517,9 +504,8 @@ def concatenate_surfaces(X_BOT,X_TOP,FUNC_BOT_SURF,FUNC_TOP_SURF,npanel,nalpha,n FUNC_BOT_SURF - airfoil property computation discretization on bottom surface [multiple units] FUNC_TOP_SURF - airfoil property computation discretization on top surface [multiple units] npanel - number of panels [unitless] - nalpha - number of angle of attacks [unitless] - nRe - number of Reynolds numbers [unitless] - batch_analysis - batch analysis flag [boolean] + ncases - number of angle of attacks [unitless] + ncpts - number of Reynolds numbers [unitless] Outputs: FUNC - airfoil property in user specified discretization on entire @@ -528,65 +514,12 @@ def concatenate_surfaces(X_BOT,X_TOP,FUNC_BOT_SURF,FUNC_TOP_SURF,npanel,nalpha,n Properties Used: N/A ''' - FUNC = np.zeros((npanel,nalpha,nRe)) - - if batch_analysis: - N_ALPHA = nalpha - else: - N_ALPHA = 1 - - for a_i in range(N_ALPHA): - for Re_i in range(nRe): - if not batch_analysis: - a_i = Re_i - top_func = FUNC_TOP_SURF[:,a_i,Re_i][X_TOP[:,a_i,Re_i].mask == False] - bot_func = FUNC_BOT_SURF[:,a_i,Re_i][X_BOT[:,a_i,Re_i].mask == False] - FUNC[:,a_i,Re_i] = np.concatenate([bot_func[::-1],top_func]) - return FUNC - - -def extract_values(AP): - ''' - This funtion retrieves the aerodynamic and boundary layer properties of airfoils - that are not analyzed in a batch analysis. The results from an analysis which is - not in batch mode lie on the diagonal. - - Assumptions: - None - - Source: - None - - Inputs: - AP - Data structure of airfoil polars and boundary layer properties - - Outputs: - AP - Reshaped data structure of airfoil polars and boundary layer - properties - - Properties Used: - N/A - ''' - AP.Cl = np.atleast_2d(np.diagonal(AP.Cl,axis1 = 0, axis2 = 1 )).T - AP.Cd = np.atleast_2d(np.diagonal(AP.Cd,axis1 = 0, axis2 = 1 )).T - AP.Cm = np.atleast_2d(np.diagonal(AP.Cm,axis1 = 0, axis2 = 1 )).T - AP.normals = np.atleast_2d(np.diagonal(AP.normals,axis1 = 0, axis2 = 1)).T - AP.x = np.atleast_2d(np.diagonal(AP.x,axis1 = 0, axis2 = 1)).T - AP.y = np.atleast_2d(np.diagonal(AP.y,axis1 = 0, axis2 = 1)).T - AP.x_bl = np.atleast_2d(np.diagonal(AP.x_bl,axis1 = 0, axis2 = 1)).T - AP.y_bl = np.atleast_2d(np.diagonal(AP.y_bl,axis1 = 0, axis2 = 1)).T - AP.Cp = np.atleast_2d(np.diagonal(AP.Cp,axis1 = 0, axis2 = 1)).T - AP.Ue_Vinf = np.atleast_2d(np.diagonal(AP.Ue_Vinf,axis1 = 0, axis2 = 1)).T - AP.dVe = np.atleast_2d(np.diagonal(AP.dVe,axis1 = 0, axis2 = 1)).T - AP.theta = np.atleast_2d(np.diagonal(AP.theta,axis1 = 0, axis2 = 1)).T - AP.delta_star = np.atleast_2d(np.diagonal(AP.delta_star,axis1 = 0, axis2 = 1)).T - AP.delta = np.atleast_2d(np.diagonal(AP.delta,axis1 = 0, axis2 = 1)).T - AP.Re_theta = np.atleast_2d(np.diagonal(AP.Re_theta,axis1 = 0, axis2 = 1)).T - AP.Re_x = np.atleast_2d(np.diagonal(AP.Re_x,axis1 = 0, axis2 = 1)).T - AP.H = np.atleast_2d(np.diagonal(AP.H,axis1 = 0, axis2 = 1)).T - AP.Cf = np.atleast_2d(np.diagonal(AP.Cf,axis1 = 0, axis2 = 1)).T - - return AP - + FUNC = np.zeros((npanel,ncases,ncpts)) + for case in range(ncases): + for cpt in range(ncpts): + top_func = FUNC_TOP_SURF[:,case,cpt][X_TOP[:,case,cpt].mask == False] + bot_func = FUNC_BOT_SURF[:,case,cpt][X_BOT[:,case,cpt].mask == False] + FUNC[:,case,cpt] = np.concatenate([bot_func[::-1],top_func]) + return FUNC \ No newline at end of file diff --git a/trunk/SUAVE/Methods/Aerodynamics/Airfoil_Panel_Method/heads_method.py b/trunk/SUAVE/Methods/Aerodynamics/Airfoil_Panel_Method/heads_method.py index b23adcfa59..1e5c2ec370 100644 --- a/trunk/SUAVE/Methods/Aerodynamics/Airfoil_Panel_Method/heads_method.py +++ b/trunk/SUAVE/Methods/Aerodynamics/Airfoil_Panel_Method/heads_method.py @@ -1,6 +1,8 @@ ## @ingroup Methods-Aerodynamics-Airfoil_Panel_Method # heads_method.py + # Created: Mar 2021, M. Clarke +# Modified: Sep 2022, M. Clarke # ---------------------------------------------------------------------- # Imports @@ -13,8 +15,8 @@ # heads_method.py # ---------------------------------------------------------------------- ## @ingroup Methods-Aerodynamics-Airfoil_Panel_Method -def heads_method(npanel,nalpha,nRe,DEL_0,THETA_0,DELTA_STAR_0, TURBULENT_SURF,RE_L,TURBULENT_COORD, - VE_I, DVE_I,batch_analysis,tol): +def heads_method(npanel,ncases,ncpts,DEL_0,THETA_0,DELTA_STAR_0, TURBULENT_SURF,RE_L,TURBULENT_COORD, + VE_I, DVE_I,tol): """ Computes the boundary layer characteristics in turbulent flow pressure gradients @@ -26,9 +28,8 @@ def heads_method(npanel,nalpha,nRe,DEL_0,THETA_0,DELTA_STAR_0, TURBULENT_SURF,RE None Inputs: - nalpha - number of angle of attacks [unitless] - nRe - number of reynolds numbers [unitless] - batch_analysis - flag for batch analysis [boolean] + ncases - number of cases [unitless] + ncpts - number of control points [unitless] DEL_0 - intital bounday layer thickness [m] DELTA_STAR_0 - initial displacement thickness [m] THETA_0 - initial momentum thickness [m] @@ -56,7 +57,7 @@ def heads_method(npanel,nalpha,nRe,DEL_0,THETA_0,DELTA_STAR_0, TURBULENT_SURF,RE """ # Initialize vectors - X_H = np.zeros((npanel,nalpha,nRe)) + X_H = np.zeros((npanel,ncases,ncpts)) THETA_H = np.zeros_like(X_H) DELTA_STAR_H = np.zeros_like(X_H) H_H = np.zeros_like(X_H) @@ -64,28 +65,23 @@ def heads_method(npanel,nalpha,nRe,DEL_0,THETA_0,DELTA_STAR_0, TURBULENT_SURF,RE RE_THETA_H = np.zeros_like(X_H) RE_X_H = np.zeros_like(X_H) DELTA_H = np.zeros_like(X_H) - - if batch_analysis: - N_ALPHA = nalpha - else: - N_ALPHA = 1 - for a_i in range(N_ALPHA): - for re_i in range(nRe): - if not batch_analysis: - a_i = re_i + + + for case in range(ncases): + for cpt in range(ncpts): # length of tubulent surface - l = TURBULENT_SURF[a_i,re_i] + l = TURBULENT_SURF[case,cpt] if l == 0.0: pass else: - theta_0 = THETA_0[a_i,re_i] - Re_L = RE_L[a_i,re_i] + theta_0 = THETA_0[case,cpt] + Re_L = RE_L[case,cpt] nu = l/Re_L - x_i = TURBULENT_COORD.data[:,a_i,re_i][TURBULENT_COORD.mask[:,a_i,re_i] ==False] - Ve_i = VE_I.data[:,a_i,re_i][TURBULENT_COORD.mask[:,a_i,re_i] ==False] - dVe_i = DVE_I.data[:,a_i,re_i][TURBULENT_COORD.mask[:,a_i,re_i] ==False] - del_0 = DEL_0[a_i,re_i] - del_star_0 = DELTA_STAR_0[a_i,re_i] + x_i = TURBULENT_COORD.data[:,case,cpt][TURBULENT_COORD.mask[:,case,cpt] ==False] + Ve_i = VE_I.data[:,case,cpt][TURBULENT_COORD.mask[:,case,cpt] ==False] + dVe_i = DVE_I.data[:,case,cpt][TURBULENT_COORD.mask[:,case,cpt] ==False] + del_0 = DEL_0[case,cpt] + del_star_0 = DELTA_STAR_0[case,cpt] H_0 = del_star_0 / theta_0 H1_0 = getH1(np.atleast_1d(H_0))[0] if np.isnan(H1_0): @@ -94,8 +90,19 @@ def heads_method(npanel,nalpha,nRe,DEL_0,THETA_0,DELTA_STAR_0, TURBULENT_SURF,RE y = odeint(odefcn,y0,x_i,args=(Re_L/l, x_i, Ve_i, dVe_i)) # Compute momentum thickness, theta - theta = y[:,0] - Ve_theta_H1 = y[:,1] + theta = y[:,0] + ind = np.where(~np.isnan(theta))[0] + first, last = ind[0], ind[-1] + theta[:first] = theta[first] + dtheta_dx = (theta[last]-theta[last-1])/(x_i[last]-x_i[last-1]) + theta[last + 1:] = theta[last] + dtheta_dx*(x_i[last + 1:]-x_i[last]) + + Ve_theta_H1 = y[:,1] + ind = np.where(~np.isnan(Ve_theta_H1))[0] + first, last = ind[0], ind[-1] + Ve_theta_H1[:first] = Ve_theta_H1[first] + dVe_theta_H1_dx = (Ve_theta_H1[last]-Ve_theta_H1[last-1])/(x_i[last]-x_i[last-1]) + Ve_theta_H1[last + 1:] = Ve_theta_H1[last] + dVe_theta_H1_dx*(x_i[last + 1:]-x_i[last]) # find theta values that do not converge and replace them with neighbor idx1 = np.where(abs((theta[1:] - theta[:-1])/theta[:-1]) > tol )[0] @@ -130,23 +137,22 @@ def heads_method(npanel,nalpha,nRe,DEL_0,THETA_0,DELTA_STAR_0, TURBULENT_SURF,RE # Compute boundary layer thickness delta = theta*H1 + del_star - delta[0] = 0 # Reynolds number at x=0 cannot be negative (give nans) Re_x[0] = 1E-5 # Find where matrices are not masked - indices = np.where(TURBULENT_COORD.mask[:,a_i,re_i] == False) + indices = np.where(TURBULENT_COORD.mask[:,case,cpt] == False) # Store results - np.put(X_H[:,a_i,re_i],indices,x_i ) - np.put(THETA_H[:,a_i,re_i],indices,theta) - np.put(DELTA_STAR_H[:,a_i,re_i],indices,del_star) - np.put(H_H[:,a_i,re_i],indices,H) - np.put(CF_H[:,a_i,re_i],indices ,cf) - np.put(RE_THETA_H[:,a_i,re_i],indices,Re_theta) - np.put(RE_X_H[:,a_i,re_i],indices,Re_x) - np.put(DELTA_H[:,a_i,re_i],indices,delta) + np.put(X_H[:,case,cpt],indices,x_i ) + np.put(THETA_H[:,case,cpt],indices,theta) + np.put(DELTA_STAR_H[:,case,cpt],indices,del_star) + np.put(H_H[:,case,cpt],indices,H) + np.put(CF_H[:,case,cpt],indices ,cf) + np.put(RE_THETA_H[:,case,cpt],indices,Re_theta) + np.put(RE_X_H[:,case,cpt],indices,Re_x) + np.put(DELTA_H[:,case,cpt],indices,delta) RESULTS = Data( X_H = X_H, diff --git a/trunk/SUAVE/Methods/Aerodynamics/Airfoil_Panel_Method/hess_smith.py b/trunk/SUAVE/Methods/Aerodynamics/Airfoil_Panel_Method/hess_smith.py index 6a82bb6b54..4583e25c5f 100644 --- a/trunk/SUAVE/Methods/Aerodynamics/Airfoil_Panel_Method/hess_smith.py +++ b/trunk/SUAVE/Methods/Aerodynamics/Airfoil_Panel_Method/hess_smith.py @@ -1,6 +1,7 @@ ## @ingroup Methods-Aerodynamics-Airfoil_Panel_Method # hess_smith.py # Created: Mar 2021, M. Clarke +# Modified: Sep 2022, M. Clarke # ---------------------------------------------------------------------- # Imports @@ -18,7 +19,7 @@ # ---------------------------------------------------------------------- ## @ingroup Methods-Aerodynamics-Airfoil_Panel_Method -def hess_smith(x_coord,y_coord,alpha,Re,npanel,batch_analyis): +def hess_smith(x_coord,y_coord,alpha,Re,npanel): """Computes the incompressible, inviscid flow over an airfoil of arbitrary shape using the Hess-Smith panel method. Assumptions: @@ -30,8 +31,7 @@ def hess_smith(x_coord,y_coord,alpha,Re,npanel,batch_analyis): Inputs x - Vector of x coordinates of the surface [unitess] - y - Vector of y coordinates of the surface [unitess] - batch_analyis - flag for batch analysis [boolean] + y - Vector of y coordinates of the surface [unitess] alpha - Airfoil angle of attack [radians] npanel - Number of panels on the airfoil. The number of nodes [unitess] is equal to npanel+1, and the ith panel goes from node @@ -49,18 +49,18 @@ def hess_smith(x_coord,y_coord,alpha,Re,npanel,batch_analyis): N/A """ - nalpha = len(alpha) - nRe = len(Re) - alpha_2d = np.repeat(np.repeat(alpha,nRe, axis = 1)[np.newaxis,:, :], npanel, axis=0) + ncases = len(alpha[0,:]) + ncpts = len(Re) + alpha_2d = np.repeat(alpha.T[np.newaxis,:, :], npanel, axis=0) # generate panel geometry data for later use - l,st,ct,xbar,ybar,norm = panel_geometry(x_coord,y_coord,npanel,nalpha,nRe) + l,st,ct,xbar,ybar,norm = panel_geometry(x_coord,y_coord,npanel,ncases,ncpts) # compute matrix of aerodynamic influence coefficients - ainfl = infl_coeff(x_coord,y_coord,xbar,ybar,st,ct,npanel,nalpha,nRe,batch_analyis) # nalpha x nRe x npanel+1 x npanel+1 + ainfl = infl_coeff(x_coord,y_coord,xbar,ybar,st,ct,npanel,ncases,ncpts) # ncases x ncpts x npanel+1 x npanel+1 # compute right hand side vector for the specified angle of attack - b_2d = np.zeros((npanel+1,nalpha, nRe)) + b_2d = np.zeros((npanel+1,ncases, ncpts)) b_2d[:-1,:,:] = st*np.cos(alpha_2d) - np.sin(alpha_2d)*ct b_2d[-1,:,:] = -(ct[0,:,:]*np.cos(alpha_2d[-1,:,:]) + st[0,:,:]*np.sin(alpha_2d[-1,:,:]))-(ct[-1,:,:]*np.cos(alpha_2d[-1,:,:]) +st[-1,:,:]*np.sin(alpha_2d[-1,:,:])) @@ -69,6 +69,6 @@ def hess_smith(x_coord,y_coord,alpha,Re,npanel,batch_analyis): qg = np.swapaxes(qg_T.T,1,2) # compute the tangential velocity distribution at the midpoint of panels - vt = velocity_distribution(qg,x_coord,y_coord,xbar,ybar,st,ct,alpha,Re,npanel) + vt = velocity_distribution(qg,x_coord,y_coord,xbar,ybar,st,ct,alpha_2d,npanel) return xbar,ybar,vt,norm \ No newline at end of file diff --git a/trunk/SUAVE/Methods/Aerodynamics/Airfoil_Panel_Method/infl_coeff.py b/trunk/SUAVE/Methods/Aerodynamics/Airfoil_Panel_Method/infl_coeff.py index 2eaa8f2fbf..7c13b5d483 100644 --- a/trunk/SUAVE/Methods/Aerodynamics/Airfoil_Panel_Method/infl_coeff.py +++ b/trunk/SUAVE/Methods/Aerodynamics/Airfoil_Panel_Method/infl_coeff.py @@ -2,6 +2,7 @@ # infl_coeff.py # Created: Mar 2021, M. Clarke +# Modified: Sep 2022, M. Clarke # ---------------------------------------------------------------------- # Imports @@ -14,7 +15,7 @@ # infl_coeff.py # ---------------------------------------------------------------------- ## @ingroup Methods-Aerodynamics-Airfoil_Panel_Method -def infl_coeff(x,y,xbar,ybar,st,ct,npanel,nalpha,nRe,batch_analyis): +def infl_coeff(x,y,xbar,ybar,st,ct,npanel,ncases,ncpts): """Compute the matrix of aerodynamic influence coefficients for later use Assumptions: @@ -39,7 +40,7 @@ def infl_coeff(x,y,xbar,ybar,st,ct,npanel,nalpha,nRe,batch_analyis): N/A """ - ainfl = np.zeros((nalpha,nRe,npanel+1,npanel+1)) + ainfl = np.zeros((ncases,ncpts,npanel+1,npanel+1)) pi2inv = 1 / (2*np.pi) # convert 1d matrices to 4d @@ -62,9 +63,9 @@ def infl_coeff(x,y,xbar,ybar,st,ct,npanel,nalpha,nRe,batch_analyis): r_ratio = rij_dot_rij_plus_1/rij/rij_plus_1 r_ratio[r_ratio>1.0] = 1.0 # numerical noise betaij = np.real(anglesign*np.arccos(r_ratio)) - diag_indices = list(np.tile(np.repeat(np.arange(npanel),nalpha),nRe)) - aoas = list(np.tile(np.arange(nalpha),nRe*npanel)) - res = list(np.repeat(np.arange(nRe),nalpha*npanel)) + diag_indices = list(np.tile(np.repeat(np.arange(npanel),ncases),ncpts)) + aoas = list(np.tile(np.arange(ncases),ncpts*npanel)) + res = list(np.repeat(np.arange(ncpts),ncases*npanel)) betaij[aoas,res,diag_indices,diag_indices] = np.pi ainfl[:,:,:-1,:-1] = pi2inv*(sti_minus_j*np.log(rij_plus_1/rij) + cti_minus_j*betaij) diff --git a/trunk/SUAVE/Methods/Aerodynamics/Airfoil_Panel_Method/panel_geometry.py b/trunk/SUAVE/Methods/Aerodynamics/Airfoil_Panel_Method/panel_geometry.py index 5692dd883f..fc3857cf7b 100644 --- a/trunk/SUAVE/Methods/Aerodynamics/Airfoil_Panel_Method/panel_geometry.py +++ b/trunk/SUAVE/Methods/Aerodynamics/Airfoil_Panel_Method/panel_geometry.py @@ -2,6 +2,7 @@ # panel_geometry.py # Created: Mar 2021, M. Clarke +# Modified: Sep 2022, M. Clarke # --------------------------------------- #------------------------------- @@ -15,7 +16,7 @@ # panel_geometry.py # ---------------------------------------------------------------------- ## @ingroup Methods-Aerodynamics-Airfoil_Panel_Method -def panel_geometry(x,y,npanel,nalpha,nRe): +def panel_geometry(x,y,npanel,ncases,ncpts): """Computes airfoil surface panelization parameters for later use in the computation of the matrix of influence coefficients. @@ -48,7 +49,7 @@ def panel_geometry(x,y,npanel,nalpha,nRe): xbar = (x[1:] +x[:-1])/2 ybar = (y[1:] +y[:-1])/2 - norm = np.zeros((npanel,2,nalpha,nRe)) + norm = np.zeros((npanel,2,ncases,ncpts)) norm[:,0,:,:] = -st norm[:,1,:,:] = ct diff --git a/trunk/SUAVE/Methods/Aerodynamics/Airfoil_Panel_Method/thwaites_method.py b/trunk/SUAVE/Methods/Aerodynamics/Airfoil_Panel_Method/thwaites_method.py index 0b0191ee72..c0e1917bde 100644 --- a/trunk/SUAVE/Methods/Aerodynamics/Airfoil_Panel_Method/thwaites_method.py +++ b/trunk/SUAVE/Methods/Aerodynamics/Airfoil_Panel_Method/thwaites_method.py @@ -2,6 +2,7 @@ # thwaites_method.py # Created: Mar 2021, M. Clarke +# Modified: Sep 2022, M. Clarke # ---------------------------------------------------------------------- # Imports @@ -15,7 +16,7 @@ # thwaites_method # ---------------------------------------------------------------------- ## @ingroup Methods-Aerodynamics-Airfoil_Panel_Method -def thwaites_method(npanel,nalpha,nRe,L,RE_L,X_I,VE_I, DVE_I,batch_analysis,tol,THETA_0): +def thwaites_method(npanel,ncases,ncpts,L,RE_L,X_I,VE_I, DVE_I,tol,THETA_0): """ Computes the boundary layer characteristics in laminar flow pressure gradients @@ -28,8 +29,8 @@ def thwaites_method(npanel,nalpha,nRe,L,RE_L,X_I,VE_I, DVE_I,batch_analysis,tol, Inputs: npanel - number of points on surface [unitless] - nalpha - number of angle of attacks [unitless] - nRe - number of reynolds numbers [unitless] + ncases - number of cases [unitless] + ncpts - number of control points [unitless] batch_analysis - flag for batch analysis [boolean] THETA_0 - initial momentum thickness [m] L - normalized length of surface [unitless] @@ -55,7 +56,7 @@ def thwaites_method(npanel,nalpha,nRe,L,RE_L,X_I,VE_I, DVE_I,batch_analysis,tol, """ # Initialize vectors - X_T = np.zeros((npanel,nalpha,nRe)) + X_T = np.zeros((npanel,ncases,ncpts)) THETA_T = np.zeros_like(X_T) DELTA_STAR_T = np.zeros_like(X_T) H_T = np.zeros_like(X_T) @@ -63,24 +64,17 @@ def thwaites_method(npanel,nalpha,nRe,L,RE_L,X_I,VE_I, DVE_I,batch_analysis,tol, RE_THETA_T = np.zeros_like(X_T) RE_X_T = np.zeros_like(X_T) DELTA_T = np.zeros_like(X_T) - - if batch_analysis: - N_ALPHA = nalpha - else: - N_ALPHA = 1 - - for a_i in range(N_ALPHA): - for re_i in range(nRe): - if not batch_analysis: - a_i = re_i + + for case in range(ncases): + for cpt in range(ncpts): # compute laminar boundary layer properties - l = L[a_i,re_i] + l = L[case,cpt] theta_0 = THETA_0 - Re_L = RE_L[a_i,re_i] + Re_L = RE_L[case,cpt] nu = l/Re_L - x_i = X_I.data[:,a_i,re_i][X_I.mask[:,a_i,re_i] ==False] - Ve_i = VE_I.data[:,a_i,re_i][VE_I.mask[:,a_i,re_i] ==False] - dVe_i = DVE_I.data[:,a_i,re_i][DVE_I.mask[:,a_i,re_i] ==False] + x_i = X_I.data[:,case,cpt][X_I.mask[:,case,cpt] ==False] + Ve_i = VE_I.data[:,case,cpt][VE_I.mask[:,case,cpt] ==False] + dVe_i = DVE_I.data[:,case,cpt][DVE_I.mask[:,case,cpt] ==False] y0 = theta_0**2 * getVe(0,x_i,Ve_i)**6 theta2_Ve6 = odeint(odefcn, y0,x_i , args=(nu, x_i, Ve_i)) @@ -123,17 +117,17 @@ def thwaites_method(npanel,nalpha,nRe,L,RE_L,X_I,VE_I, DVE_I,batch_analysis,tol, Re_x[0] = 1E-5 # Find where matrices are not masked - indices = np.where(X_I.mask[:,a_i,re_i] == False) + indices = np.where(X_I.mask[:,case,cpt] == False) # Store results - np.put(X_T[:,a_i,re_i],indices,x_i) - np.put(THETA_T[:,a_i,re_i],indices,theta) - np.put(DELTA_STAR_T[:,a_i,re_i],indices,del_star) - np.put(H_T[:,a_i,re_i],indices,H) - np.put(CF_T[:,a_i,re_i],indices ,cf) - np.put(RE_THETA_T[:,a_i,re_i],indices,Re_theta) - np.put(RE_X_T[:,a_i,re_i],indices,Re_x) - np.put(DELTA_T[:,a_i,re_i],indices,delta) + np.put(X_T[:,case,cpt],indices,x_i) + np.put(THETA_T[:,case,cpt],indices,theta) + np.put(DELTA_STAR_T[:,case,cpt],indices,del_star) + np.put(H_T[:,case,cpt],indices,H) + np.put(CF_T[:,case,cpt],indices ,cf) + np.put(RE_THETA_T[:,case,cpt],indices,Re_theta) + np.put(RE_X_T[:,case,cpt],indices,Re_x) + np.put(DELTA_T[:,case,cpt],indices,delta) RESULTS = Data( X_T = X_T, diff --git a/trunk/SUAVE/Methods/Aerodynamics/Airfoil_Panel_Method/velocity_distribution.py b/trunk/SUAVE/Methods/Aerodynamics/Airfoil_Panel_Method/velocity_distribution.py index 6c3eb83efc..7447d22d4d 100644 --- a/trunk/SUAVE/Methods/Aerodynamics/Airfoil_Panel_Method/velocity_distribution.py +++ b/trunk/SUAVE/Methods/Aerodynamics/Airfoil_Panel_Method/velocity_distribution.py @@ -2,6 +2,7 @@ # velocity_distribution.py # # Created: Mar 2021, M. Clarke +# Modified: Sep 2022, M. Clarke # ---------------------------------------------------------------------- # Imports @@ -15,7 +16,7 @@ # ---------------------------------------------------------------------- ## @ingroup Methods-Aerodynamics-Airfoil_Panel_Method -def velocity_distribution(qg,x,y,xbar,ybar,st,ct,alpha,Re,npanel): +def velocity_distribution(qg,x,y,xbar,ybar,st,ct,alpha_2d,npanel): """Compute the tangential velocity distribution at the midpoint of each panel @@ -45,11 +46,11 @@ def velocity_distribution(qg,x,y,xbar,ybar,st,ct,alpha,Re,npanel): N/A """ - nalpha = len(alpha) - nRe = len(Re) + nalpha = len(alpha_2d[0,:,0]) + ncpts = len(alpha_2d[0,0,:]) # flow tangency boundary condition - source distribution - vt_2d = ct *np.cos(alpha) + st*np.sin(alpha) + vt_2d = ct *np.cos(alpha_2d) + st*np.sin(alpha_2d) gamma = np.repeat(qg[-1,:,:][np.newaxis,:,:],npanel, axis = 0) # convert 1d matrices to 2d @@ -73,9 +74,9 @@ def velocity_distribution(qg,x,y,xbar,ybar,st,ct,alpha,Re,npanel): r_ratio = rij_dot_rij_plus_1/rij/rij_plus_1 r_ratio[r_ratio>1.0] = 1.0 # numerical noise betaij = np.real(anglesign*np.arccos(r_ratio)) - diag_indices = list(np.tile(np.repeat(np.arange(npanel),nalpha),nRe)) - aoas = list(np.tile(np.arange(nalpha),nRe*npanel)) - res = list(np.repeat(np.arange(nRe),nalpha*npanel)) + diag_indices = list(np.tile(np.repeat(np.arange(npanel),nalpha),ncpts)) + aoas = list(np.tile(np.arange(nalpha),ncpts*npanel)) + res = list(np.repeat(np.arange(ncpts),nalpha*npanel)) betaij[aoas,res,diag_indices,diag_indices] = np.pi # swap axes diff --git a/trunk/SUAVE/Methods/Aerodynamics/Common/Fidelity_Zero/Lift/BET_calculations.py b/trunk/SUAVE/Methods/Aerodynamics/Common/Fidelity_Zero/Lift/BET_calculations.py index f2b82ceae1..5a9643191d 100644 --- a/trunk/SUAVE/Methods/Aerodynamics/Common/Fidelity_Zero/Lift/BET_calculations.py +++ b/trunk/SUAVE/Methods/Aerodynamics/Common/Fidelity_Zero/Lift/BET_calculations.py @@ -3,14 +3,14 @@ # # Created: Jan 2022, R. Erhard # Modified: - +from SUAVE.Core.Utilities import interp2d import numpy as np ## @ingroup Methods-Aerodynamics-Common-Fidelity_Zero-Lift -def compute_airfoil_aerodynamics(beta,c,r,R,B,Wa,Wt,a,nu,a_loc,a_pol_data,ctrl_pts,Nr,Na,tc,use_2d_analysis): +def compute_airfoil_aerodynamics(beta,c,r,R,B,Wa,Wt,a,nu,airfoils,a_loc,ctrl_pts,Nr,Na,tc,use_2d_analysis): """ Cl, Cdval = compute_airfoil_aerodynamics( beta,c,r,R,B, Wa,Wt,a,nu, - a_loc,a_geo,cl_sur,cd_sur, + airfoils,a_loc ctrl_pts,Nr,Na,tc,use_2d_analysis ) Computes the aerodynamic forces at sectional blade locations. If airfoil @@ -37,9 +37,7 @@ def compute_airfoil_aerodynamics(beta,c,r,R,B,Wa,Wt,a,nu,a_loc,a_pol_data,ctrl_p Wt tangential velocity [-] a speed of sound [-] nu viscosity [-] - - a_loc Locations of specified airfoils [-] - a_pol_data Airfoil polar data [-] + airfoil_data Data structure of airfoil polar information [-] ctrl_pts Number of control points [-] Nr Number of radial blade sections [-] Na Number of azimuthal blade stations [-] @@ -51,24 +49,23 @@ def compute_airfoil_aerodynamics(beta,c,r,R,B,Wa,Wt,a,nu,a_loc,a_pol_data,ctrl_p Cdval Drag Coefficients (before scaling) [-] alpha section local angle of attack [rad] - """ - + """ alpha = beta - np.arctan2(Wa,Wt) W = (Wa*Wa + Wt*Wt)**0.5 Ma = W/a Re = (W*c)/nu # If propeller airfoils are defined, use airfoil surrogate - if a_loc != None: - # Compute blade Cl and Cd distribution from the airfoil data - dim_sur = len(a_pol_data.lift_coefficient_surrogates) + if a_loc != None: + # Compute blade Cl and Cd distribution from the airfoil data if use_2d_analysis: # return the 2D Cl and CDval of shape (ctrl_pts, Nr, Na) Cl = np.zeros((ctrl_pts,Nr,Na)) Cdval = np.zeros((ctrl_pts,Nr,Na)) - for jj in range(dim_sur): - Cl_af = a_pol_data.lift_coefficient_surrogates[a_pol_data.airfoil_names[jj]]((Re,alpha)) - Cdval_af = a_pol_data.drag_coefficient_surrogates[a_pol_data.airfoil_names[jj]]((Re,alpha)) + for jj,airfoil in enumerate(airfoils): + pd = airfoil.polars + Cl_af = interp2d(Re,alpha,pd.reynolds_numbers, pd.angle_of_attacks, pd.lift_coefficients) + Cdval_af = interp2d(Re,alpha,pd.reynolds_numbers, pd.angle_of_attacks, pd.drag_coefficients) locs = np.where(np.array(a_loc) == jj ) Cl[:,locs,:] = Cl_af[:,locs,:] Cdval[:,locs,:] = Cdval_af[:,locs,:] @@ -77,9 +74,10 @@ def compute_airfoil_aerodynamics(beta,c,r,R,B,Wa,Wt,a,nu,a_loc,a_pol_data,ctrl_p Cl = np.zeros((ctrl_pts,Nr)) Cdval = np.zeros((ctrl_pts,Nr)) - for jj in range(dim_sur): - Cl_af = a_pol_data.lift_coefficient_surrogates[a_pol_data.airfoil_names[jj]]((Re,alpha)) - Cdval_af = a_pol_data.drag_coefficient_surrogates[a_pol_data.airfoil_names[jj]]((Re,alpha)) + for jj,airfoil in enumerate(airfoils): + pd = airfoil.polars + Cl_af = interp2d(Re,alpha,pd.reynolds_numbers, pd.angle_of_attacks, pd.lift_coefficients) + Cdval_af = interp2d(Re,alpha,pd.reynolds_numbers, pd.angle_of_attacks, pd.drag_coefficients) locs = np.where(np.array(a_loc) == jj ) Cl[:,locs] = Cl_af[:,locs] Cdval[:,locs] = Cdval_af[:,locs] @@ -145,6 +143,6 @@ def compute_inflow_and_tip_loss(r,R,Wa,Wt,B,et1=1,et2=1,et3=1): tipfactor = B/2.0*( (R/r)**et1 - 1 )**et2/lamdaw**et3 piece = np.exp(-tipfactor) - Ftip = 2.*np.arccos(piece)/np.pi + Ftip = 2.*np.arccos(piece)/np.pi return lamdaw, Ftip, piece \ No newline at end of file diff --git a/trunk/SUAVE/Methods/Aerodynamics/Common/Fidelity_Zero/Lift/generate_vortex_distribution.py b/trunk/SUAVE/Methods/Aerodynamics/Common/Fidelity_Zero/Lift/generate_vortex_distribution.py index 6d45f62140..f9819488cd 100644 --- a/trunk/SUAVE/Methods/Aerodynamics/Common/Fidelity_Zero/Lift/generate_vortex_distribution.py +++ b/trunk/SUAVE/Methods/Aerodynamics/Common/Fidelity_Zero/Lift/generate_vortex_distribution.py @@ -383,9 +383,9 @@ def generate_wing_vortex_distribution(VD,wing,n_cw,n_sw,spc,precision): # Get airfoil section VD if span_breaks[i_break].Airfoil: - airfoil_geo_data = import_airfoil_geometry([span_breaks[i_break].Airfoil.airfoil.coordinate_file]) - break_camber_zs.append(airfoil_geo_data.camber_coordinates[airfoil_geo_data.airfoil_names[0]]) - break_camber_xs.append(airfoil_geo_data.x_lower_surface[airfoil_geo_data.airfoil_names[0]]) + airfoil_geo_data = import_airfoil_geometry(span_breaks[i_break].Airfoil.airfoil.coordinate_file) + break_camber_zs.append(airfoil_geo_data.camber_coordinates) + break_camber_xs.append(airfoil_geo_data.x_lower_surface) else: break_camber_zs.append(np.zeros(30)) break_camber_xs.append(np.linspace(0,1,30)) diff --git a/trunk/SUAVE/Methods/Geometry/Two_Dimensional/Cross_Section/Airfoil/__init__.py b/trunk/SUAVE/Methods/Geometry/Two_Dimensional/Cross_Section/Airfoil/__init__.py index c03b03cf21..16525463af 100644 --- a/trunk/SUAVE/Methods/Geometry/Two_Dimensional/Cross_Section/Airfoil/__init__.py +++ b/trunk/SUAVE/Methods/Geometry/Two_Dimensional/Cross_Section/Airfoil/__init__.py @@ -2,8 +2,8 @@ # Geometry functions for two dimensional airfoils. # @ingroup Methods-Geometry-Two_Dimensional-Cross_Section -from .compute_naca_4series import compute_naca_4series -from .compute_airfoil_polars import compute_airfoil_polars -from .import_airfoil_dat import import_airfoil_dat -from .import_airfoil_geometry import import_airfoil_geometry -from .import_airfoil_polars import import_airfoil_polars \ No newline at end of file +from .compute_naca_4series import compute_naca_4series +from .compute_airfoil_properties import compute_airfoil_properties +from .import_airfoil_dat import import_airfoil_dat +from .import_airfoil_geometry import import_airfoil_geometry +from .import_airfoil_polars import import_airfoil_polars \ No newline at end of file diff --git a/trunk/SUAVE/Methods/Geometry/Two_Dimensional/Cross_Section/Airfoil/compute_airfoil_polars.py b/trunk/SUAVE/Methods/Geometry/Two_Dimensional/Cross_Section/Airfoil/compute_airfoil_polars.py deleted file mode 100644 index a171203a42..0000000000 --- a/trunk/SUAVE/Methods/Geometry/Two_Dimensional/Cross_Section/Airfoil/compute_airfoil_polars.py +++ /dev/null @@ -1,287 +0,0 @@ -## @ingroup Methods-Geometry-Two_Dimensional-Cross_Section-Airfoil -# compute_airfoil_polars.py -# -# Created: Mar 2019, M. Clarke -# Modified: Mar 2020, M. Clarke -# Jan 2021, E. Botero -# Jan 2021, R. Erhard -# Nov 2021, R. Erhard -# Aug 2022, R. Erhard - -# ---------------------------------------------------------------------- -# Imports -# ---------------------------------------------------------------------- - -import SUAVE -from SUAVE.Core import Data , Units -from SUAVE.Methods.Aerodynamics.AERODAS.pre_stall_coefficients import pre_stall_coefficients -from SUAVE.Methods.Aerodynamics.AERODAS.post_stall_coefficients import post_stall_coefficients -from .import_airfoil_polars import import_airfoil_polars -import numpy as np -from scipy.interpolate import RegularGridInterpolator - - -## @ingroup Methods-Geometry-Two_Dimensional-Cross_Section-Airfoil -def compute_airfoil_polars(a_polar, airfoil_geometry_data, npoints = 200, use_pre_stall_data=True): - """This computes the lift and drag coefficients of an airfoil in stall regimes using pre-stall - characterstics and AERODAS formation for post stall characteristics. This is useful for - obtaining a more accurate prediction of wing and blade loading. Pre stall characteristics - are obtained in the form of a text file of airfoil polar data. - - Assumptions: - Uses AERODAS formulation for post stall characteristics - - Source: - Models of Lift and Drag Coefficients of Stalled and Unstalled Airfoils in Wind Turbines and Wind Tunnels - by D Spera, 2008 - - Inputs: - a_geo - a_polar - use_pre_stall_data [Boolean] - - - Outputs: - airfoil_data. - cl_polars [unitless] - cd_polars [unitless] - aoa_sweep [unitless] - - Properties Used: - N/A - """ - - num_airfoils = len(airfoil_geometry_data.airfoil_names) - - # check number of polars per airfoil in batch - num_polars = 0 - for i in range(num_airfoils): - n_p = len(a_polar[i]) - if n_p < 3: - raise AttributeError('Provide three or more airfoil polars to compute surrogate') - - num_polars = max(num_polars, n_p) - - # Get all of the coefficients for AERODAS wings - AoA_sweep_deg = np.linspace(-90, 90, 180 * 4 + 1) - AoA_sweep_radians = AoA_sweep_deg*Units.degrees - CL = np.zeros((num_airfoils, num_polars, len(AoA_sweep_deg))) - CD = np.zeros((num_airfoils, num_polars, len(AoA_sweep_deg))) - aoa0 = np.zeros((num_airfoils, num_polars)) - cl0 = np.zeros((num_airfoils, num_polars)) - - # Create an infinite aspect ratio wing - geometry = SUAVE.Components.Wings.Wing() - geometry.aspect_ratio = np.inf - geometry.section = Data() - - # Create dummy settings and state - settings = Data() - state = Data() - state.conditions = Data() - state.conditions.aerodynamics = Data() - state.conditions.aerodynamics.pre_stall_coefficients = Data() - state.conditions.aerodynamics.post_stall_coefficients = Data() - - # read airfoil polars - airfoil_polar_data_raw = import_airfoil_polars(a_polar, airfoil_geometry_data.airfoil_names) - airfoil_polar_data = smooth_raw_polar_data(airfoil_polar_data_raw) - - # initialize new data - airfoil_polar_surrogate_data = Data() - airfoil_polar_surrogate_data.angle_of_attacks = Data() - airfoil_polar_surrogate_data.lift_coefficient_surrogates = Data() - airfoil_polar_surrogate_data.drag_coefficient_surrogates = Data() - - # AERODAS - aNames = airfoil_geometry_data.airfoil_names - for i in range(num_airfoils): - # Modify the "wing" slightly: - geometry.thickness_to_chord = airfoil_geometry_data.thickness_to_chord[aNames[i]] - - for j in range(len(a_polar[i])): - # Extract raw data from polars - airfoil_cl = np.array(airfoil_polar_data.lift_coefficients[i][j]) - airfoil_cd = np.array(airfoil_polar_data.drag_coefficients[i][j]) - airfoil_aoa = np.array(airfoil_polar_data.angle_of_attacks[i][j]) - - airfoil_cl, airfoil_cd, airfoil_aoa = remove_post_stall(airfoil_cl, airfoil_cd, airfoil_aoa) - - # computing approximate zero lift aoa - airfoil_cl_plus = airfoil_cl[airfoil_cl>0] - idx_zero_lift = np.where(airfoil_cl == min(airfoil_cl_plus))[0][0] - airfoil_cl_crossing = airfoil_cl[idx_zero_lift-1:idx_zero_lift+1] - airfoil_aoa_crossing = airfoil_aoa[idx_zero_lift-1:idx_zero_lift+1] - try: - A0 = np.interp(0,airfoil_cl_crossing, airfoil_aoa_crossing)* Units.deg - except: - A0 = airfoil_aoa[idx_zero_lift] * Units.deg - - # max lift coefficent and associated aoa - CL1max = np.max(airfoil_cl) - idx_aoa_max_prestall_cl = np.where(airfoil_cl == CL1max)[0][0] - ACL1 = airfoil_aoa[idx_aoa_max_prestall_cl] * Units.degrees - - # computing approximate lift curve slope - linear_idxs = [np.argmin(abs(airfoil_aoa)),np.argmin(abs(airfoil_aoa - 4))] - cl_range = airfoil_cl[linear_idxs] - aoa_range = airfoil_aoa[linear_idxs] * Units.degrees - S1 = (cl_range[1]-cl_range[0])/(aoa_range[1]-aoa_range[0]) - - # max drag coefficent and associated aoa - CD1max = np.max(airfoil_cd) - idx_aoa_max_prestall_cd = np.where(airfoil_cd == CD1max)[0][0] - ACD1 = airfoil_aoa[idx_aoa_max_prestall_cd] * Units.degrees - - # Find the point of lowest drag and the CD - idx_CD_min = np.where(airfoil_cd==min(airfoil_cd))[0][0] - ACDmin = airfoil_aoa[idx_CD_min] * Units.degrees - CDmin = airfoil_cd[idx_CD_min] - - # Setup data structures for this run - ones = np.ones_like(AoA_sweep_radians) - settings.section_zero_lift_angle_of_attack = A0 - state.conditions.aerodynamics.angle_of_attack = AoA_sweep_radians * ones - geometry.section.angle_attack_max_prestall_lift = ACL1 * ones - geometry.pre_stall_maximum_drag_coefficient_angle = ACD1 * ones - geometry.pre_stall_maximum_lift_coefficient = CL1max * ones - geometry.pre_stall_maximum_lift_drag_coefficient = CD1max * ones - geometry.section.minimum_drag_coefficient = CDmin * ones - geometry.section.minimum_drag_coefficient_angle_of_attack = ACDmin - geometry.pre_stall_lift_curve_slope = S1 - - # Get prestall coefficients - CL1, CD1 = pre_stall_coefficients(state,settings,geometry) - - # Get poststall coefficents - CL2, CD2 = post_stall_coefficients(state,settings,geometry) - - # Take the maxes - CL_ij = np.fmax(CL1,CL2) - CL_ij[AoA_sweep_radians<=A0] = np.fmin(CL1[AoA_sweep_radians<=A0],CL2[AoA_sweep_radians<=A0]) - - CD_ij = np.fmax(CD1,CD2) - - # Pack this loop - CL[i,j,:] = CL_ij - CD[i,j,:] = CD_ij - aoa0[i,j] = A0 - cl0[i,j] = np.interp(0,airfoil_aoa,airfoil_cl) - - if use_pre_stall_data == True: - CL[i,j,:], CD[i,j,:] = apply_pre_stall_data(AoA_sweep_deg, airfoil_aoa, airfoil_cl, airfoil_cd, CL[i,j,:], CD[i,j,:]) - - - # remove placeholder values (for airfoils that have different number of polars) - n_p = len(a_polar[i]) - RE_data = airfoil_polar_data.reynolds_number[aNames[i]] - - CL_sur = RegularGridInterpolator((RE_data, AoA_sweep_radians), CL[i,0:n_p,:],bounds_error=False,fill_value=None) - CD_sur = RegularGridInterpolator((RE_data, AoA_sweep_radians), CD[i,0:n_p,:],bounds_error=False,fill_value=None) - - - airfoil_polar_surrogate_data.lift_coefficient_surrogates[aNames[i]] = CL_sur - airfoil_polar_surrogate_data.drag_coefficient_surrogates[aNames[i]] = CD_sur - - airfoil_polar_surrogate_data.angle_of_attacks = AoA_sweep_radians - airfoil_polar_surrogate_data.airfoil_names = airfoil_geometry_data.airfoil_names - - return airfoil_polar_surrogate_data - -def remove_post_stall(airfoil_cl, airfoil_cd, airfoil_aoa): - cl_grad = np.gradient(airfoil_cl) - a0_idx = np.argmin(abs(airfoil_aoa)) - if np.any(cl_grad[:a0_idx] < 0): - negativeInflectionPoint = a0_idx - np.where( np.flip(cl_grad[:a0_idx]) < 0 )[0][0] # - else: - negativeInflectionPoint = 0 - if np.any(cl_grad[a0_idx:] < 0): - positiveInflectionPoint = a0_idx + np.where( cl_grad[a0_idx:] < 0)[0][0] - else: - positiveInflectionPoint = len(airfoil_aoa) - - airfoil_cl = airfoil_cl[negativeInflectionPoint:positiveInflectionPoint] - airfoil_cd = airfoil_cd[negativeInflectionPoint:positiveInflectionPoint] - airfoil_aoa = airfoil_aoa[negativeInflectionPoint:positiveInflectionPoint] - return airfoil_cl, airfoil_cd, airfoil_aoa - -def apply_pre_stall_data(AoA_sweep_deg, airfoil_aoa, airfoil_cl, airfoil_cd, CL, CD): - # Coefficients in pre-stall regime taken from experimental data: - aoa_locs = (AoA_sweep_deg>=airfoil_aoa[0]) * (AoA_sweep_deg<=airfoil_aoa[-1]) - aoa_in_data = AoA_sweep_deg[aoa_locs] - - # if the data is within experimental use it, if not keep the surrogate values - CL[aoa_locs] = airfoil_cl[abs(aoa_in_data[:,None] - airfoil_aoa[None,:]).argmin(axis=-1)] - CD[aoa_locs] = airfoil_cd[abs(aoa_in_data[:,None] - airfoil_aoa[None,:]).argmin(axis=-1)] - - # remove kinks/overlap between pre- and post-stall - data_lb = [i for i, v in enumerate(abs(CD-airfoil_cd[0])) if v == min(abs(CD-airfoil_cd[0]))][0]#np.where(CD == airfoil_cd[0])[0][0] - data_ub = [i for i, v in enumerate(abs(CD-airfoil_cd[-1])) if v == min(abs(CD-airfoil_cd[-1]))][-1]#np.where(CD == airfoil_cd[-1])[0][-1] - CD[0:data_lb] = np.maximum(CD[0:data_lb], CD[data_lb]*np.ones_like(CD[0:data_lb])) - CD[data_ub:] = np.maximum(CD[data_ub:], CD[data_ub]*np.ones_like(CD[data_ub:])) - - return CL, CD - - -def smooth_raw_polar_data(polar_data_raw): - # initialize new data structure - polar_data_new = Data() - polar_data_new.reynolds_number = polar_data_raw.reynolds_number - polar_data_new.mach_number = polar_data_raw.mach_number - polar_data_new.lift_coefficients = [] - polar_data_new.drag_coefficients = [] - polar_data_new.angle_of_attacks = [] - - # get information - num_airfoils = len(polar_data_raw.lift_coefficients) - for a in range(num_airfoils): - aName = polar_data_raw.airfoil_names[a] - num_polars = len(polar_data_raw.lift_coefficients[aName]) - cl_a = [] - cd_a = [] - aoa_a = [] - for i in range(num_polars): - # extract raw data - c_l_raw = np.array(polar_data_raw.lift_coefficients[aName][i]) - c_d_raw = np.array(polar_data_raw.drag_coefficients[aName][i]) - aoa_raw = np.array(polar_data_raw.angle_of_attacks[aName][i]) - - # smooth the data with rolling average - c_l_filtered = rollingAverage(aoa_raw, c_l_raw, window_size=9, order=1) - c_d_filtered = rollingAverage(aoa_raw, c_d_raw, window_size=9, order=1) - - ## remove post-stall regions - #c_l_grad = np.gradient(c_l_filtered) - #c_l_grad_target = c_l_grad[np.argmin(abs(aoa_raw))] - #pre_stalled_ids = np.where(c_l_grad > 0.4 * c_l_grad_target) - - cl_a.append(list(c_l_filtered)) #[pre_stalled_ids])) - cd_a.append(list(c_d_filtered)) #[pre_stalled_ids])) - aoa_a.append(list(aoa_raw)) #[pre_stalled_ids])) - polar_data_new.lift_coefficients.append(cl_a) - polar_data_new.drag_coefficients.append(cd_a) - polar_data_new.angle_of_attacks.append(aoa_a) - - return polar_data_new - -def rollingAverage(x, y, window_size=13, order=1): - N = len(x) - y_averaged = np.zeros_like(y) - for i in range(N): - # fit a polynomial of specified order to this window of data - - # At the start and end use half window size - if i < window_size // 2: - w_start = 0 - w_end = window_size // 2 - elif i > N - window_size // 2: - w_start = N - window_size // 2 - w_end = N - else: - w_start = i - window_size // 2 - w_end = i + window_size // 2 - - fitData = np.polyfit(x[w_start:w_end], y[w_start:w_end], deg=order) - y_averaged[i] = np.polyval(fitData, x[i]) - - return y_averaged \ No newline at end of file diff --git a/trunk/SUAVE/Methods/Geometry/Two_Dimensional/Cross_Section/Airfoil/compute_airfoil_properties.py b/trunk/SUAVE/Methods/Geometry/Two_Dimensional/Cross_Section/Airfoil/compute_airfoil_properties.py new file mode 100644 index 0000000000..64dbf3953e --- /dev/null +++ b/trunk/SUAVE/Methods/Geometry/Two_Dimensional/Cross_Section/Airfoil/compute_airfoil_properties.py @@ -0,0 +1,322 @@ +## @ingroup Methods-Geometry-Two_Dimensional-Cross_Section-Airfoil +# compute_airfoil_properties.py +# +# Created: Mar 2019, M. Clarke +# Modified: Mar 2020, M. Clarke +# Jan 2021, E. Botero +# Jan 2021, R. Erhard +# Nov 2021, R. Erhard + +# ---------------------------------------------------------------------- +# Imports +# ---------------------------------------------------------------------- + +import SUAVE +from SUAVE.Core import Data , Units +from SUAVE.Methods.Aerodynamics.AERODAS.pre_stall_coefficients import pre_stall_coefficients +from SUAVE.Methods.Aerodynamics.AERODAS.post_stall_coefficients import post_stall_coefficients +from SUAVE.Methods.Aerodynamics.Airfoil_Panel_Method.airfoil_analysis import airfoil_analysis +from SUAVE.Methods.Geometry.Two_Dimensional.Cross_Section.Airfoil.import_airfoil_polars import import_airfoil_polars +from SUAVE.Methods.Geometry.Two_Dimensional.Cross_Section.Airfoil.compute_naca_4series import compute_naca_4series +import numpy as np + +## @ingroup Methods-Geometry-Two_Dimensional-Cross_Section-Airfoil +def compute_airfoil_properties(airfoil_geometry, airfoil_polar_files = None,use_pre_stall_data=True): + """This computes the aerodynamic properties and coefficients of an airfoil in stall regimes using pre-stall + characterstics and AERODAS formation for post stall characteristics. This is useful for + obtaining a more accurate prediction of wing and blade loading as well as aeroacoustics. Pre stall characteristics + are obtained in the form of a text file of airfoil polar data obtained from airfoiltools.com + + Assumptions: + None + + Source + None + + Inputs: + airfoil_geometry + airfoil_polar_files + boundary_layer_files + use_pre_stall_data [Boolean] + Outputs: + airfoil_data. + cl_polars [unitless] + cd_polars [unitless] + aoa_sweep [unitless] + + # raw data [unitless] + theta_lower_surface [unitless] + delta_lower_surface [unitless] + delta_star_lower_surface [unitless] + sa_lower_surface [unitless] + ue_lower_surface [unitless] + cf_lower_surface [unitless] + dcp_dx_lower_surface [unitless] + Ret_lower_surface [unitless] + H_lower_surface [unitless] + theta_upper_surface [unitless] + delta_upper_surface [unitless] + delta_star_upper_surface [unitless] + sa_upper_surface [unitless] + ue_upper_surface [unitless] + cf_upper_surface [unitless] + dcp_dx_upper_surface [unitless] + Ret_upper_surface [unitless] + H_upper_surface [unitless] + + Properties Used: + N/A + """ + Airfoil_Data = Data() + + # ---------------------------------------------------------------------------------------- + # Compute airfoil boundary layers properties + # ---------------------------------------------------------------------------------------- + Airfoil_Data = compute_boundary_layer_properties(airfoil_geometry,Airfoil_Data) + num_polars = len(Airfoil_Data.re_from_polar) + + # ---------------------------------------------------------------------------------------- + # Compute extended cl and cd polars + # ---------------------------------------------------------------------------------------- + if airfoil_polar_files != None : + # check number of polars per airfoil in batch + num_polars = 0 + n_p = len(airfoil_polar_files) + if n_p < 3: + raise AttributeError('Provide three or more airfoil polars to compute surrogate') + num_polars = max(num_polars, n_p) + + # read in polars from files overwrite panel code + airfoil_file_data = import_airfoil_polars(airfoil_polar_files) + Airfoil_Data.aoa_from_polar = airfoil_file_data.aoa_from_polar + Airfoil_Data.re_from_polar = airfoil_file_data.re_from_polar + Airfoil_Data.lift_coefficients = airfoil_file_data.lift_coefficients + Airfoil_Data.drag_coefficients = airfoil_file_data.drag_coefficients + + # Get all of the coefficients for AERODAS wings + AoA_sweep_deg = np.linspace(-14,90,105) + AoA_sweep_rad = AoA_sweep_deg*Units.degrees + + # Create an infinite aspect ratio wing + geometry = SUAVE.Components.Wings.Wing() + geometry.aspect_ratio = np.inf + geometry.section = Data() + + # AERODAS + CL = np.zeros((num_polars,len(AoA_sweep_deg))) + CD = np.zeros((num_polars,len(AoA_sweep_deg))) + for j in range(num_polars): + airfoil_cl = Airfoil_Data.lift_coefficients[j,:] + airfoil_cd = Airfoil_Data.drag_coefficients[j,:] + airfoil_aoa = Airfoil_Data.aoa_from_polar[j,:]/Units.degrees + + # compute airfoil cl and cd for extended AoA range + CL[j,:],CD[j,:] = compute_extended_polars(airfoil_cl,airfoil_cd,airfoil_aoa,AoA_sweep_deg,geometry,use_pre_stall_data) + + # ---------------------------------------------------------------------------------------- + # Store data + # ---------------------------------------------------------------------------------------- + Airfoil_Data.reynolds_numbers = Airfoil_Data.re_from_polar + Airfoil_Data.angle_of_attacks = AoA_sweep_rad + Airfoil_Data.lift_coefficients = CL + Airfoil_Data.drag_coefficients = CD + + return Airfoil_Data + +def compute_extended_polars(airfoil_cl,airfoil_cd,airfoil_aoa,AoA_sweep_deg,geometry,use_pre_stall_data): + """ Computes the aerodynamic polars of an airfoil over an extended angle of attack range + + Assumptions: + Uses AERODAS formulation for post stall characteristics + Source: + Models of Lift and Drag Coefficients of Stalled and Unstalled Airfoils in Wind Turbines and Wind Tunnels + by D Spera, 2008 + + Inputs: + airfoil_cl [unitless] + airfoil_cd [unitless] + airfoil_aoa [degrees] + AoA_sweep_deg [unitless] + geometry [N/A] + use_pre_stall_data [boolean] + Outputs: + CL [unitless] + CD [unitless] + + Properties Used: + N/A + """ + + # Create dummy settings and state + settings = Data() + state = Data() + state.conditions = Data() + state.conditions.aerodynamics = Data() + state.conditions.aerodynamics.pre_stall_coefficients = Data() + state.conditions.aerodynamics.post_stall_coefficients = Data() + + # computing approximate zero lift aoa + AoA_sweep_radians = AoA_sweep_deg*Units.degrees + airfoil_cl_plus = airfoil_cl[airfoil_cl>0] + idx_zero_lift = np.where(airfoil_cl == min(airfoil_cl_plus))[0][0] + airfoil_cl_crossing = airfoil_cl[idx_zero_lift-1:idx_zero_lift+1] + airfoil_aoa_crossing = airfoil_aoa[idx_zero_lift-1:idx_zero_lift+1] + try: + A0 = np.interp(0,airfoil_cl_crossing, airfoil_aoa_crossing)* Units.deg + except: + A0 = airfoil_aoa[idx_zero_lift] * Units.deg + + # max lift coefficent and associated aoa + CL1max = np.max(airfoil_cl) + idx_aoa_max_prestall_cl = np.where(airfoil_cl == CL1max)[0][0] + ACL1 = airfoil_aoa[idx_aoa_max_prestall_cl] * Units.degrees + + # computing approximate lift curve slope + linear_idxs = [int(np.where(airfoil_aoa==0)[0]),int(np.where(airfoil_aoa==4)[0])] + cl_range = airfoil_cl[linear_idxs] + aoa_range = airfoil_aoa[linear_idxs] * Units.degrees + S1 = (cl_range[1]-cl_range[0])/(aoa_range[1]-aoa_range[0]) + + # max drag coefficent and associated aoa + CD1max = np.max(airfoil_cd) + idx_aoa_max_prestall_cd = np.where(airfoil_cd == CD1max)[0][0] + ACD1 = airfoil_aoa[idx_aoa_max_prestall_cd] * Units.degrees + + # Find the point of lowest drag and the CD + idx_CD_min = np.where(airfoil_cd==min(airfoil_cd))[0][0] + ACDmin = airfoil_aoa[idx_CD_min] * Units.degrees + CDmin = airfoil_cd[idx_CD_min] + + # Setup data structures for this run + ones = np.ones_like(AoA_sweep_radians) + settings.section_zero_lift_angle_of_attack = A0 + state.conditions.aerodynamics.angle_of_attack = AoA_sweep_radians* ones + geometry.section.angle_attack_max_prestall_lift = ACL1 * ones + geometry.pre_stall_maximum_drag_coefficient_angle = ACD1 * ones + geometry.pre_stall_maximum_lift_coefficient = CL1max * ones + geometry.pre_stall_maximum_lift_drag_coefficient = CD1max * ones + geometry.section.minimum_drag_coefficient = CDmin * ones + geometry.section.minimum_drag_coefficient_angle_of_attack = ACDmin + geometry.pre_stall_lift_curve_slope = S1 + + # Get prestall coefficients + CL1, CD1 = pre_stall_coefficients(state,settings,geometry) + + # Get poststall coefficents + CL2, CD2 = post_stall_coefficients(state,settings,geometry) + + # Take the maxes + CL_ij = np.fmax(CL1,CL2) + CL_ij[AoA_sweep_radians<=A0] = np.fmin(CL1[AoA_sweep_radians<=A0],CL2[AoA_sweep_radians<=A0]) + + CD_ij = np.fmax(CD1,CD2) + + # Pack this loop + CL = CL_ij + CD = CD_ij + + if use_pre_stall_data == True: + CL, CD = apply_pre_stall_data(AoA_sweep_deg, airfoil_aoa, airfoil_cl, airfoil_cd, CL, CD) + + return CL,CD + +def apply_pre_stall_data(AoA_sweep_deg, airfoil_aoa, airfoil_cl, airfoil_cd, CL, CD): + '''Applies prestall data to lift and drag curve slopes + + Assumptions: + None + + Source: + None + + Inputs: + AoA_sweep_deg [degrees] + airfoil_aoa [degrees] + airfoil_cl [unitless] + airfoil_cd [unitless] + CL [unitless] + CD [unitless] + + Outputs + CL [unitless] + CD [unitless] + + + Properties Used: + N/A + + ''' + + # Coefficients in pre-stall regime taken from experimental data: + aoa_locs = (AoA_sweep_deg>=airfoil_aoa[0]) * (AoA_sweep_deg<=airfoil_aoa[-1]) + aoa_in_data = AoA_sweep_deg[aoa_locs] + + # if the data is within experimental use it, if not keep the surrogate values + CL[aoa_locs] = airfoil_cl[abs(aoa_in_data[:,None] - airfoil_aoa[None,:]).argmin(axis=-1)] + CD[aoa_locs] = airfoil_cd[abs(aoa_in_data[:,None] - airfoil_aoa[None,:]).argmin(axis=-1)] + + # remove kinks/overlap between pre- and post-stall + data_lb = np.where(CD == airfoil_cd[0])[0][0] + data_ub = np.where(CD == airfoil_cd[-1])[0][-1] + CD[0:data_lb] = np.maximum(CD[0:data_lb], CD[data_lb]*np.ones_like(CD[0:data_lb])) + CD[data_ub:] = np.maximum(CD[data_ub:], CD[data_ub]*np.ones_like(CD[data_ub:])) + + return CL, CD + +## @ingroup Methods-Geometry-Two_Dimensional-Cross_Section-Airfoil +def compute_boundary_layer_properties(airfoil_geometry,Airfoil_Data): + '''Computes the boundary layer properties of an airfoil for a sweep of Reynolds numbers + and angle of attacks. + + Source: + None + + Assumptions: + None + + Inputs: + airfoil_geometry + Airfoil_Data + + Outputs: + Airfoil_Data + + Properties Used: + N/A + ''' + if airfoil_geometry == None: + print('No airfoil defined, NACA 0012 surrogates will be used') + a_names = ['0012'] + airfoil_geometry = compute_naca_4series(a_names, npoints= 100) + + AoA_sweep = np.array([-4,0,2,4,8,10,14])*Units.degrees + Re_sweep = np.array([1,5,10,30,50,75,100])*1E4 + AoA_vals = np.tile(AoA_sweep[None,:],(len(Re_sweep) ,1)) + Re_vals = np.tile(Re_sweep[:,None],(1, len(AoA_sweep))) + + # run airfoil analysis + af_res = airfoil_analysis(airfoil_geometry,AoA_vals,Re_vals) + + # store data + Airfoil_Data.aoa_from_polar = np.tile(AoA_sweep[None,:],(len(Re_sweep),1)) + Airfoil_Data.re_from_polar = Re_sweep + Airfoil_Data.lift_coefficients = af_res.cl + Airfoil_Data.drag_coefficients = af_res.cd + Airfoil_Data.cm = af_res.cm + Airfoil_Data.boundary_layer = Data() + Airfoil_Data.boundary_layer.angle_of_attacks = AoA_sweep + Airfoil_Data.boundary_layer.reynolds_numbers = Re_sweep + Airfoil_Data.boundary_layer.theta_lower_surface = af_res.theta + Airfoil_Data.boundary_layer.delta_lower_surface = af_res.delta + Airfoil_Data.boundary_layer.delta_star_lower_surface = af_res.delta_star + Airfoil_Data.boundary_layer.Ue_Vinf_lower_surface = af_res.Ue_Vinf + Airfoil_Data.boundary_layer.cf_lower_surface = af_res.cf + Airfoil_Data.boundary_layer.dcp_dx_lower_surface = af_res.dcp_dx + Airfoil_Data.boundary_layer.theta_upper_surface = af_res.theta + Airfoil_Data.boundary_layer.delta_upper_surface = af_res.delta + Airfoil_Data.boundary_layer.delta_star_upper_surface = af_res.delta_star + Airfoil_Data.boundary_layer.Ue_Vinf_upper_surface = af_res.Ue_Vinf + Airfoil_Data.boundary_layer.cf_upper_surface = af_res.cf + Airfoil_Data.boundary_layer.dcp_dx_upper_surface = af_res.dcp_dx + + return Airfoil_Data \ No newline at end of file diff --git a/trunk/SUAVE/Methods/Geometry/Two_Dimensional/Cross_Section/Airfoil/compute_naca_4series.py b/trunk/SUAVE/Methods/Geometry/Two_Dimensional/Cross_Section/Airfoil/compute_naca_4series.py index 07805beae7..acdd4271bb 100644 --- a/trunk/SUAVE/Methods/Geometry/Two_Dimensional/Cross_Section/Airfoil/compute_naca_4series.py +++ b/trunk/SUAVE/Methods/Geometry/Two_Dimensional/Cross_Section/Airfoil/compute_naca_4series.py @@ -1,14 +1,16 @@ ## @ingroup Methods-Geometry-Two_Dimensional-Cross_Section-Airfoil -# +# compute_naca_4series.py +# # Created: Aug 2015, SUAVE Team # Modified: Jul 2020, M.Clarke # Sep 2020, M. Clarke -from SUAVE.Core import Data +from SUAVE.Core import Data import numpy as np + ## @ingroup Methods-Geometry-Two_Dimensional-Cross_Section-Airfoil -def compute_naca_4series(camber,camber_loc,thickness,npoints=100,airfoilName='naca_4_series'): +def compute_naca_4series(airfoil_geometry_file,npoints= 200, leading_and_trailing_edge_resolution_factor = 1.5 ): """Computes the points of NACA 4-series airfoil Assumptions: @@ -18,147 +20,77 @@ def compute_naca_4series(camber,camber_loc,thickness,npoints=100,airfoilName='na None Inputs: - camber [-] Maximum camber as a fraction of chord - camber_loc [-] Maximum camber location as a fraction of chord - thickness [-] Maximum thickness as a fraction of chord - npoints [-] Total number of points + airfoils + npoints [unitless] + leading_and_trailing_edge_resolution_factor [unitless] Outputs: airfoil_data. - thickness_to_chord - x_coordinates - y_coordinates - x_upper_surface - x_lower_surface - y_upper_surface - y_lower_surface - camber_coordinates + thickness_to_chord [unitless] + x_coordinates [meters] + y_coordinates [meters] + x_upper_surface [meters] + x_lower_surface [meters] + y_upper_surface [meters] + y_lower_surface [meters] + camber_coordinates [meters] Properties Used: N/A - """ - - airfoil_data = Data() - airfoil_data.x_coordinates = Data() - airfoil_data.y_coordinates = Data() - airfoil_data.thickness_to_chord = Data() - airfoil_data.camber_coordinates = Data() - airfoil_data.x_upper_surface = Data() - airfoil_data.x_lower_surface = Data() - airfoil_data.y_upper_surface = Data() - airfoil_data.y_lower_surface = Data() - - aNames = [airfoilName] - half_pnts = int(npoints/2) - - upper = [] - lower = [] + """ + geometry = Data() + half_npoints = npoints/2 # number of points per side + airfoil_digits = [int(x) for x in airfoil_geometry_file] + camber = airfoil_digits[0]/100 # maximum camber as a fraction of chord + camber_loc = airfoil_digits[1]/10 # maximum camber location as a fraction of chord + thickness = airfoil_digits[2]/10 + airfoil_digits[3]/100 # maximum thickness as a fraction of chord - for i in range(1,half_pnts): - x = float(i) / float(half_pnts); - x = x*np.sqrt(x) - - # lines - zt,zc,th = compute_naca_4series_lines(x,camber,camber_loc,thickness) - - # upper surface - xu = x - zt*np.sin(th) - zu = zc + zt*np.cos(th) - upper.append([xu,zu]) - - # lower surface - xl = x + zt*np.sin(th) - zl = zc - zt*np.cos(th) - lower.append([xl,zl]) + x_us = np.linspace(0,1,int(np.ceil(half_npoints))+ (npoints%2 == 0)) + x_ls = np.linspace(0,1,int(np.ceil(half_npoints))) + if leading_and_trailing_edge_resolution_factor != None: + te = leading_and_trailing_edge_resolution_factor # points per side and trailing-edge bunching factor + x_us = 1 - (te+1)*x_us*(1-x_us)**te - (1-x_us)**(te+1) # bunched points, x, 0 to 1 + x_ls = 1 - (te+1)*x_ls*(1-x_ls)**te - (1-x_ls)**(te+1) # bunched points, x, 0 to 1 + + # normalized thickness, gap at trailing edge + t_us = .2969*np.sqrt(x_us) - 0.126*x_us - 0.3516*(x_us**2) + 0.2843*(x_us**3) - 0.1015*(x_us**4) + t_ls = .2969*np.sqrt(x_ls) - 0.126*x_ls - 0.3516*(x_ls**2) + 0.2843*(x_ls**3) - 0.1015*(x_ls**4) + t_us = t_us*thickness/.2 + t_ls = t_ls*thickness/.2 + m = camber + p = camber_loc + c_us = m/(1-p)**2 * ((1-2*p)+2*p*x_us-x_us**2) + c_ls = m/(1-p)**2 * ((1-2*p)+2*p*x_ls-x_ls**2) - upper = [[0.0,0.0]] + upper + [[1.0,0.0]] - lower = [[0.0,0.0]] + lower + [[1.0,0.0]] + if m == 0 and p == 0: + pass + else: + I_us = np.where(x_us=1.0 ): - zc = 0.0 - th = 0.0 - - else: - - # camber - if x < camber_loc: - zc = (camber/(camber_loc*camber_loc)) * \ - (2.0*camber_loc*x - xx) - else: - zc = (camber/((1.0 - camber_loc)*(1.0 - camber_loc))) * \ - (1.0 - 2.0*camber_loc + 2.0*camber_loc*x - xx) - - # finite difference theta - xo = x + 0.00001; - xoxo = xo*xo; - if xo < camber_loc: - zo = (camber/(camber_loc*camber_loc)) * \ - (2.0*camber_loc*xo - xoxo) - else: - zo = (camber/((1.0 - camber_loc)*(1.0 - camber_loc))) * \ - (1.0 - 2.0*camber_loc + 2.0*camber_loc*xo - xoxo) - - th = np.arctan( (zo - zc)/0.00001 ) - - return zt,zc,th \ No newline at end of file + return geometry \ No newline at end of file diff --git a/trunk/SUAVE/Methods/Geometry/Two_Dimensional/Cross_Section/Airfoil/generate_interpolated_airfoils.py b/trunk/SUAVE/Methods/Geometry/Two_Dimensional/Cross_Section/Airfoil/generate_interpolated_airfoils.py index 6e35ef0fd7..3790f27f97 100644 --- a/trunk/SUAVE/Methods/Geometry/Two_Dimensional/Cross_Section/Airfoil/generate_interpolated_airfoils.py +++ b/trunk/SUAVE/Methods/Geometry/Two_Dimensional/Cross_Section/Airfoil/generate_interpolated_airfoils.py @@ -28,36 +28,24 @@ def generate_interpolated_airfoils(a1, a2, nairfoils, npoints=200, save_filename """ - # import airfoil geometry for the two airfoils - airfoil_geo_files = [a1, a2] + # import airfoil geometry for the two airfoils a1_name = os.path.basename(a1) a2_name = os.path.basename(a2) - a_geo = import_airfoil_geometry(airfoil_geo_files,npoints) + a_geo_1 = import_airfoil_geometry(a1,npoints) + a_geo_2 = import_airfoil_geometry(a2,npoints) - # identify x and y coordinates of the two airfoils - x_upper = a_geo.x_upper_surface.values() - y_upper = a_geo.y_upper_surface.values() - x_lower = a_geo.x_lower_surface.values() - y_lower = a_geo.y_lower_surface.values() - - # identify points on airfoils to interpolate between - yairfoils_upper = np.array(y_upper).T - yairfoils_lower = np.array(y_lower).T - xairfoils_upper = np.array(x_upper).T - xairfoils_lower = np.array(x_lower).T - # for each point around the airfoil, interpolate between the two given airfoil coordinates z = np.linspace(0,1,nairfoils) - y_u_lb = yairfoils_upper[:,0] - y_u_ub = yairfoils_upper[:,1] - y_l_lb = yairfoils_lower[:,0] - y_l_ub = yairfoils_lower[:,1] - - x_u_lb = xairfoils_upper[:,0] - x_u_ub = xairfoils_upper[:,1] - x_l_lb = xairfoils_lower[:,0] - x_l_ub = xairfoils_lower[:,1] + y_u_lb = a_geo_1.y_upper_surface + y_u_ub = a_geo_2.y_upper_surface + y_l_lb = a_geo_1.y_lower_surface + y_l_ub = a_geo_2.y_lower_surface + + x_u_lb = a_geo_1.x_upper_surface + x_u_ub = a_geo_2.x_upper_surface + x_l_lb = a_geo_1.x_lower_surface + x_l_ub = a_geo_2.x_lower_surface # broadcasting interpolation y_n_upper = (z[None,...] * (y_u_ub[...,None] - y_u_lb[...,None]) + (y_u_lb[...,None])).T @@ -67,7 +55,6 @@ def generate_interpolated_airfoils(a1, a2, nairfoils, npoints=200, save_filename # save new airfoil geometry files: - new_files = {'a_{}'.format(i+1): [] for i in range(nairfoils-2)} airfoil_files = [] @@ -103,7 +90,9 @@ def generate_interpolated_airfoils(a1, a2, nairfoils, npoints=200, save_filename # plot new and original airfoils: airfoil_files.insert(0,a1) airfoil_files.append(a2) - plot_airfoil(airfoil_files,overlay=True) - plot_airfoil(airfoil_files,overlay=False) + + for airfoil_file in airfoil_files: + name = os.path.basename(airfoil_file) + plot_airfoil(airfoil_file,save_filename = name[:-4]) return new_files diff --git a/trunk/SUAVE/Methods/Geometry/Two_Dimensional/Cross_Section/Airfoil/import_airfoil_geometry.py b/trunk/SUAVE/Methods/Geometry/Two_Dimensional/Cross_Section/Airfoil/import_airfoil_geometry.py index 9b3e66caf2..a305d19df4 100644 --- a/trunk/SUAVE/Methods/Geometry/Two_Dimensional/Cross_Section/Airfoil/import_airfoil_geometry.py +++ b/trunk/SUAVE/Methods/Geometry/Two_Dimensional/Cross_Section/Airfoil/import_airfoil_geometry.py @@ -11,34 +11,29 @@ # May 2021, R. Erhard # Jun 2021, E. Botero # Aug 2021, M. Clarke -# Aug 2022, R. Erhard # ---------------------------------------------------------------------- # Imports -# ---------------------------------------------------------------------- +# ---------------------------------------------------------------------- from SUAVE.Core import Data -import scipy.interpolate as interp import numpy as np -import os +from scipy import interpolate ## @ingroup Methods-Geometry-Two_Dimensional-Cross_Section-Airfoil -def import_airfoil_geometry(airfoil_geometry_files, npoints = 200,surface_interpolation = 'cubic'): - """This imports an airfoil geometry from a text file and stores +def import_airfoil_geometry(airfoil_geometry_file, npoints = 200,surface_interpolation = 'cubic'): + """This imports an airfoil geometry from a text file and store the coordinates of upper and lower surfaces as well as the mean camberline Assumptions: Works for Selig and Lednicer airfoil formats. Automatically detects which format based off first line of data. Assumes it is one of those two. - Source: airfoiltools.com/airfoil/index - method for determining format and basic error checking - Inputs: airfoil_geometry_files surface_interpolation - type of interpolation used in the SciPy function. Preferable options are linear, quardratic and cubic. Full list of options can be found here : https://docs.scipy.org/doc/scipy/reference/generated/scipy.interpolate.interp1d.html#scipy.interpolate.interp1d - Outputs: airfoil_data. thickness_to_chord @@ -49,177 +44,188 @@ def import_airfoil_geometry(airfoil_geometry_files, npoints = 200,surface_inter y_upper_surface y_lower_surface camber_coordinates - Properties Used: N/A - """ - - if isinstance(airfoil_geometry_files,str): - print('import_airfoil_geometry was expecting a list of strings with absolute paths to airfoils') - print('Attempting to change path string to list') - airfoil_geometry_files = [airfoil_geometry_files] + """ + geometry = Data() + half_npoints = npoints//2 - num_airfoils = len(airfoil_geometry_files) - # unpack - - airfoil_geometry_data = Data() - airfoil_geometry_data.x_coordinates = Data() - airfoil_geometry_data.y_coordinates = Data() - airfoil_geometry_data.thickness_to_chord = Data() - airfoil_geometry_data.max_thickness = Data() - airfoil_geometry_data.camber_coordinates = Data() - airfoil_geometry_data.x_upper_surface = Data() - airfoil_geometry_data.x_lower_surface = Data() - airfoil_geometry_data.y_upper_surface = Data() - airfoil_geometry_data.y_lower_surface = Data() - - n_pts = npoints//2 - aNames = [] - for i in range(num_airfoils): - # New airfoil name - aNames.append(os.path.basename(airfoil_geometry_files[i])[:-4]) - - # Open file and read column names and data block - f = open(airfoil_geometry_files[i]) - - # Extract data - data_block = f.readlines() + # Open file and read column names and data block + f = open(airfoil_geometry_file) + + # Extract data + data_block = f.readlines() + try: + # Check for header block + first_element = float(data_block[0][0]) + if first_element == 1.: + lednicer_format = False + except: + # Check for format line and remove header block + format_line = data_block[1] + + # Check if it's a Selig or Lednicer file try: - # Check for header block - first_element = float(data_block[0][0]) - if first_element == 1.: - lednicer_format = False + format_flag = float(format_line.strip().split()[0]) except: - # Check for format line and remove header block - format_line = data_block[1] - - # Check if it's a Selig or Lednicer file - try: - format_flag = float(format_line.strip().split()[0]) - except: - format_flag = float(format_line.strip().split(',')[0]) - - if format_flag > 1.01: # Amount of wiggle room per airfoil tools - lednicer_format = True - # Remove header block - data_block = data_block[3:] - else: - lednicer_format = False - # Remove header block - data_block = data_block[1:] - - # Close the file - f.close() - - if lednicer_format: - x_up_surf = [] - y_up_surf = [] - x_lo_surf = [] - y_lo_surf = [] + format_flag = float(format_line.strip().split(',')[0]) - # Loop through each value: append to each column - upper_surface_flag = True - for line_count , line in enumerate(data_block): - #check for blank line which signifies the upper/lower surface division - line_check = data_block[line_count].strip() - if line_check == '': - upper_surface_flag = False - continue - if upper_surface_flag: - x_up_surf.append(float(data_block[line_count].strip().split()[0])) - y_up_surf.append(float(data_block[line_count].strip().split()[1])) - else: - x_lo_surf.append(float(data_block[line_count].strip().split()[0])) - y_lo_surf.append(float(data_block[line_count].strip().split()[1])) - + if format_flag > 1.01: # Amount of wiggle room per airfoil tools + lednicer_format = True + # Remove header block + data_block = data_block[3:] else: - x_up_surf_rev = [] - y_up_surf_rev = [] - x_lo_surf = [] - y_lo_surf = [] + lednicer_format = False + # Remove header block + data_block = data_block[1:] - # Loop through each value: append to each column - upper_surface_flag = True - for line_count , line in enumerate(data_block): - #check for line which starts with 0., which should be the split between upper and lower in selig - line_check = data_block[line_count].strip() - - # Remove any commas - line_check = line_check.replace(',','') - - if float(line_check.split()[0]) == 0.: - x_up_surf_rev.append(float(data_block[line_count].strip().replace(',','').split()[0])) - y_up_surf_rev.append(float(data_block[line_count].strip().replace(',','').split()[1])) - - x_lo_surf.append(float(data_block[line_count].strip().replace(',','').split()[0])) - y_lo_surf.append(float(data_block[line_count].strip().replace(',','').split()[1])) - - upper_surface_flag = False - continue - - if upper_surface_flag: - x_up_surf_rev.append(float(data_block[line_count].strip().replace(',','').split()[0])) - y_up_surf_rev.append(float(data_block[line_count].strip().replace(',','').split()[1])) - else: - x_lo_surf.append(float(data_block[line_count].strip().replace(',','').split()[0])) - y_lo_surf.append(float(data_block[line_count].strip().replace(',','').split()[1])) - - - if upper_surface_flag ==True: - # check if next line flips without x-coordinate going to 0 - next_line = data_block[line_count+1].strip() - next_line = next_line.replace(',','') - - if next_line.split()[0]>line_check.split()[0] and next_line.split()[0] !=0.: - upper_surface_flag = False - - # Upper surface values in Selig format are reversed from Lednicer format, so fix that - x_up_surf_rev.reverse() - y_up_surf_rev.reverse() - - x_up_surf = x_up_surf_rev - y_up_surf = y_up_surf_rev - - - # determine the thickness to chord ratio - note that the upper and lower surface - # may be of different lenghts so initial interpolation is required - # x coordinates - x_up_surf_old = np.array(x_up_surf) - arrx_up_interp= interp.interp1d(np.arange(x_up_surf_old.size),x_up_surf_old, kind=surface_interpolation) - x_up_surf_new = arrx_up_interp(np.linspace(0,x_up_surf_old.size-1,n_pts)) + # Close the file + f.close() + + if lednicer_format: + x_up_surf = [] + y_up_surf = [] + x_lo_surf = [] + y_lo_surf = [] - x_lo_surf_old = np.array(x_lo_surf) - arrx_lo_interp= interp.interp1d(np.arange(x_lo_surf_old.size),x_lo_surf_old, kind=surface_interpolation ) - x_lo_surf_new = arrx_lo_interp(np.linspace(0,x_lo_surf_old.size-1,n_pts)) + # Loop through each value: append to each column + upper_surface_flag = True + for line_count , line in enumerate(data_block): + #check for blank line which signifies the upper/lower surface division + line_check = data_block[line_count].strip() + if line_check == '': + upper_surface_flag = False + continue + if upper_surface_flag: + x_up_surf.append(float(data_block[line_count].strip().split()[0])) + y_up_surf.append(float(data_block[line_count].strip().split()[1])) + else: + x_lo_surf.append(float(data_block[line_count].strip().split()[0])) + y_lo_surf.append(float(data_block[line_count].strip().split()[1])) + + else: + x_up_surf_rev = [] + y_up_surf_rev = [] + x_lo_surf = [] + y_lo_surf = [] - # y coordinates - y_up_surf_old = np.array(y_up_surf) - arry_up_interp= interp.interp1d(np.arange(y_up_surf_old.size),y_up_surf_old, kind=surface_interpolation) - y_up_surf_new = arry_up_interp(np.linspace(0,y_up_surf_old.size-1,n_pts)) + # Loop through each value: append to each column + upper_surface_flag = True + for line_count , line in enumerate(data_block): + #check for line which starts with 0., which should be the split between upper and lower in selig + line_check = data_block[line_count].strip() + + # Remove any commas + line_check = line_check.replace(',','') + + if float(line_check.split()[0]) == 0.: + x_up_surf_rev.append(float(data_block[line_count].strip().replace(',','').split()[0])) + y_up_surf_rev.append(float(data_block[line_count].strip().replace(',','').split()[1])) + + x_lo_surf.append(float(data_block[line_count].strip().replace(',','').split()[0])) + y_lo_surf.append(float(data_block[line_count].strip().replace(',','').split()[1])) + + upper_surface_flag = False + continue + + if upper_surface_flag: + x_up_surf_rev.append(float(data_block[line_count].strip().replace(',','').split()[0])) + y_up_surf_rev.append(float(data_block[line_count].strip().replace(',','').split()[1])) + else: + x_lo_surf.append(float(data_block[line_count].strip().replace(',','').split()[0])) + y_lo_surf.append(float(data_block[line_count].strip().replace(',','').split()[1])) + + + if upper_surface_flag ==True: + # check if next line flips without x-coordinate going to 0 + next_line = data_block[line_count+1].strip() + next_line = next_line.replace(',','') + + if next_line.split()[0]>line_check.split()[0] and next_line.split()[0] !=0.: + upper_surface_flag = False + + # Upper surface values in Selig format are reversed from Lednicer format, so fix that + x_up_surf_rev.reverse() + y_up_surf_rev.reverse() + + x_up_surf = x_up_surf_rev + y_up_surf = y_up_surf_rev + + x_up_surf = np.array(x_up_surf) + x_lo_surf = np.array(x_lo_surf) + y_up_surf = np.array(y_up_surf) + y_lo_surf = np.array(y_lo_surf) + + # create custom spacing for more points and leading and trailing edge + t = np.linspace(0,4,npoints-1) + delta = 0.25 + A = 5 + f = 0.25 + smoothsq = 5 + (2*A/np.pi) *np.arctan(np.sin(2*np.pi*t*f + np.pi/2)/delta) + dim_spacing = np.append(0,np.cumsum(smoothsq)/sum(smoothsq)) + + # compute thickness, camber and concatenate coodinates + x_data = np.hstack((x_lo_surf[::-1], x_up_surf[1:])) + y_data = np.hstack((y_lo_surf[::-1], y_up_surf[1:])) + tck,u = interpolate.splprep([x_data,y_data],k=3,s=0) + out = interpolate.splev(dim_spacing,tck) + x_data = out[0] + y_data = out[1] + + # shift points to leading edge (x = 0, y = 0) + x_delta = min(x_data) + x_data = x_data - x_delta + + arg_min = np.argmin(x_data) + y_delta = y_data[arg_min] + y_data = y_data - y_delta + + if (x_data[arg_min] == 0) and (y_data[arg_min] == 0): + x_data[arg_min] = 0 + y_data[arg_min] = 0 + + # make sure points start and end at x = 1.0 + x_data[0] = 1.0 + x_data[-1] = 1.0 + + # make sure a small gap at trailing edge + if (y_data[0] == y_data[-1]): + y_data[0] = y_data[0] - 1E-4 + y_data[-1] = y_data[-1] + 1E-4 - y_lo_surf_old = np.array(y_lo_surf) - arry_lo_interp= interp.interp1d(np.arange(y_lo_surf_old.size),y_lo_surf_old, kind=surface_interpolation) - y_lo_surf_new = arry_lo_interp(np.linspace(0,y_lo_surf_old.size-1,n_pts)) + # thicknes and camber distributions require equal points + x_up_surf_old = np.array(x_up_surf) + arrx_up_interp = interpolate.interp1d(np.arange(x_up_surf_old.size),x_up_surf_old, kind=surface_interpolation) + x_up_surf_new = arrx_up_interp(np.linspace(0,x_up_surf_old.size-1,half_npoints)) + + x_lo_surf_old = np.array(x_lo_surf) + arrx_lo_interp = interpolate.interp1d(np.arange(x_lo_surf_old.size),x_lo_surf_old, kind=surface_interpolation ) + x_lo_surf_new = arrx_lo_interp(np.linspace(0,x_lo_surf_old.size-1,half_npoints)) + + # y coordinate s + y_up_surf_old = np.array(y_up_surf) + arry_up_interp = interpolate.interp1d(np.arange(y_up_surf_old.size),y_up_surf_old, kind=surface_interpolation) + y_up_surf_new = arry_up_interp(np.linspace(0,y_up_surf_old.size-1,half_npoints)) + + y_lo_surf_old = np.array(y_lo_surf) + arry_lo_interp = interpolate.interp1d(np.arange(y_lo_surf_old.size),y_lo_surf_old, kind=surface_interpolation) + y_lo_surf_new = arry_lo_interp(np.linspace(0,y_lo_surf_old.size-1,half_npoints)) + + # compute thickness, camber and concatenate coodinates + thickness = y_up_surf_new - y_lo_surf_new + camber = y_lo_surf_new + thickness/2 + max_t = np.max(thickness) + max_c = max(x_data) - min(x_data) + t_c = max_t/max_c + + geometry.thickness_to_chord = t_c + geometry.max_thickness = max_t + geometry.x_coordinates = x_data + geometry.y_coordinates = y_data + geometry.x_upper_surface = x_up_surf_new + geometry.x_lower_surface = x_lo_surf_new + geometry.y_upper_surface = y_up_surf_new + geometry.y_lower_surface = y_lo_surf_new + geometry.camber_coordinates = camber - # compute thickness, camber and concatenate coodinates - thickness = y_up_surf_new - y_lo_surf_new - camber = y_lo_surf_new + thickness/2 - x_data = np.concatenate([x_up_surf_new[::-1],x_lo_surf_new]) - y_data = np.concatenate([y_up_surf_new[::-1],y_lo_surf_new]) - - max_t = np.max(thickness) - max_c = max(x_data) - min(x_data) - t_c = max_t/max_c - - airfoil_geometry_data.thickness_to_chord[aNames[i]] = t_c - airfoil_geometry_data.max_thickness[aNames[i]] = max_t - airfoil_geometry_data.x_coordinates[aNames[i]] = x_data - airfoil_geometry_data.y_coordinates[aNames[i]] = y_data - airfoil_geometry_data.x_upper_surface[aNames[i]] = x_up_surf_new - airfoil_geometry_data.x_lower_surface[aNames[i]] = x_lo_surf_new - airfoil_geometry_data.y_upper_surface[aNames[i]] = y_up_surf_new - airfoil_geometry_data.y_lower_surface[aNames[i]] = y_lo_surf_new - airfoil_geometry_data.camber_coordinates[aNames[i]] = camber - - airfoil_geometry_data.airfoil_names = aNames - return airfoil_geometry_data \ No newline at end of file + return geometry \ No newline at end of file diff --git a/trunk/SUAVE/Methods/Geometry/Two_Dimensional/Cross_Section/Airfoil/import_airfoil_polars.py b/trunk/SUAVE/Methods/Geometry/Two_Dimensional/Cross_Section/Airfoil/import_airfoil_polars.py index abbb1bf9e9..433673c318 100644 --- a/trunk/SUAVE/Methods/Geometry/Two_Dimensional/Cross_Section/Airfoil/import_airfoil_polars.py +++ b/trunk/SUAVE/Methods/Geometry/Two_Dimensional/Cross_Section/Airfoil/import_airfoil_polars.py @@ -6,106 +6,90 @@ # Sep 2020, M. Clarke # May 2021, R. Erhard # Nov 2021, R. Erhard -# Jul 2022, R. Erhard # ---------------------------------------------------------------------- # Imports # ---------------------------------------------------------------------- -from SUAVE.Core import Data +from SUAVE.Core import Data, Units import numpy as np ## @ingroup Methods-Geometry-Two_Dimensional-Cross_Section-Airfoil -def import_airfoil_polars(airfoil_polar_files, airfoil_names): - """This imports airfoil polars from a text file output from XFOIL or from a - text file containing the (alpha, CL, CD) data from other sources. +def import_airfoil_polars(airfoil_polar_files,angel_of_attack_discretization = 89): + """This imports airfoil polars from a text file output from XFOIL or Airfoiltools.com Assumptions: Input airfoil polars file is obtained from XFOIL or from Airfoiltools.com - Source: - N/A - + http://airfoiltools.com/ Inputs: airfoil polar files - Outputs: data numpy array with airfoil data - Properties Used: N/A """ + + # number of airfoils + num_polars = 0 + n_p = len(airfoil_polar_files) + if n_p < 3: + raise AttributeError('Provide three or more airfoil polars to compute surrogate') + num_polars = max(num_polars, n_p) - # number of airfoils - num_airfoils = len(airfoil_polar_files) - - num_polars = 0 - for i in range(num_airfoils): - n_p = len(airfoil_polar_files[i]) - if n_p < 3: - raise AttributeError('Provide three or more airfoil polars to compute surrogate') - - num_polars = max(num_polars, n_p) + # create empty data structures + airfoil_data = Data() + AoA = np.zeros((num_polars,angel_of_attack_discretization)) + CL = np.zeros((num_polars,angel_of_attack_discretization)) + CD = np.zeros((num_polars,angel_of_attack_discretization)) + Re = np.zeros(num_polars) + Ma = np.zeros(num_polars) - # create empty data structures - airfoil_raw_polar_data = Data() - airfoil_raw_polar_data.angle_of_attacks = Data() - airfoil_raw_polar_data.reynolds_number = Data() - airfoil_raw_polar_data.mach_number = Data() - airfoil_raw_polar_data.lift_coefficients = Data() - airfoil_raw_polar_data.drag_coefficients = Data() + AoA_interp = np.linspace(-6,16,angel_of_attack_discretization) - for i in range(num_airfoils): - Re, Ma, CL, CD, AoA = [], [], [], [], [] - - for j in range(len(airfoil_polar_files[i])): + for j in range(len(airfoil_polar_files)): + # Open file and read column names and data block + f = open(airfoil_polar_files[j]) + data_block = f.readlines() + f.close() - # check for xfoil format - f = open(airfoil_polar_files[i][j]) - data_block = f.readlines() - f.close() + # Ignore header + for header_line in range(len(data_block)): + line = data_block[header_line] + if 'Re =' in line: + Re[j] = float(line[25:40].strip().replace(" ", "")) + if 'Mach =' in line: + Ma[j] = float(line[7:20].strip().replace(" ", "")) + if '---' in line: + data_block = data_block[header_line+1:] + break - if "XFOIL" in data_block[1]: - xfoilPolarFormat = True - header_idx = 10 - elif "xflr5" in data_block[0]: - xfoilPolarFormat = True - header_idx = 9 + # Remove any extra lines at end of file: + last_line = False + while last_line == False: + if data_block[-1]=='\n': + data_block = data_block[0:-1] else: - xfoilPolarFormat = False - - # Read data - if xfoilPolarFormat: - # get data, extract Re, Ma - headers = data_block[header_idx].split() - polarData = np.genfromtxt(airfoil_polar_files[i][j], encoding='UTF-8-sig', dtype=None, names=headers, skip_header=header_idx+2) - infoLine = list(filter(lambda x: 'Re = ' in x, data_block))[0] - - ReString = str(float(infoLine.split('Re =')[1].split('e 6')[0])) - MaString = str(float(infoLine.split('Mach =')[1].split(' Re')[0])) - else: - # get data, extract Re, Ma - polarData = np.genfromtxt(airfoil_polar_files[i][j], delimiter=" ", encoding='UTF-8-sig', dtype=None, names=True) - headers = polarData.dtype.names - - ReString = airfoil_polar_files[i][j].split('Re_',1)[1].split('e6',1)[0] - MaString = airfoil_polar_files[i][j].split('Ma_',1)[1].split('_',1)[0] - - airfoil_aoa = polarData[headers[np.where(np.array(headers) == 'alpha')[0][0]]] - airfoil_cl = polarData[headers[np.where(np.array(headers) == 'CL')[0][0]]] - airfoil_cd = polarData[headers[np.where(np.array(headers) == 'CD')[0][0]]] - - Re.append( float (ReString) * 1e6 ) - Ma.append( float (MaString) ) - CL.append( airfoil_cl ) - CD.append( airfoil_cd ) - AoA.append( airfoil_aoa ) - + last_line = True - airfoil_raw_polar_data.angle_of_attacks[airfoil_names[i]] = AoA - airfoil_raw_polar_data.reynolds_number[airfoil_names[i]] = Re - airfoil_raw_polar_data.mach_number[airfoil_names[i]] = Ma - airfoil_raw_polar_data.lift_coefficients[airfoil_names[i]] = CL - airfoil_raw_polar_data.drag_coefficients[airfoil_names[i]] = CD - airfoil_raw_polar_data.airfoil_names = airfoil_names - return airfoil_raw_polar_data - + data_len = len(data_block) + airfoil_aoa= np.zeros(data_len) + airfoil_cl = np.zeros(data_len) + airfoil_cd = np.zeros(data_len) + + # Loop through each value: append to each column + for line_count , line in enumerate(data_block): + airfoil_aoa[line_count] = float(data_block[line_count][0:8].strip()) + airfoil_cl[line_count] = float(data_block[line_count][10:17].strip()) + airfoil_cd[line_count] = float(data_block[line_count][20:27].strip()) + + AoA[j,:] = AoA_interp + CL[j,:] = np.interp(AoA_interp,airfoil_aoa,airfoil_cl) + CD[j,:] = np.interp(AoA_interp,airfoil_aoa,airfoil_cd) + + airfoil_data.aoa_from_polar = AoA*Units.degrees + airfoil_data.re_from_polar = Re + airfoil_data.mach_number = Ma + airfoil_data.lift_coefficients = CL + airfoil_data.drag_coefficients = CD + + return airfoil_data \ No newline at end of file diff --git a/trunk/SUAVE/Methods/Noise/Fidelity_One/Propeller/compute_broadband_noise.py b/trunk/SUAVE/Methods/Noise/Fidelity_One/Propeller/compute_broadband_noise.py index 121117cd67..4c08380ab6 100644 --- a/trunk/SUAVE/Methods/Noise/Fidelity_One/Propeller/compute_broadband_noise.py +++ b/trunk/SUAVE/Methods/Noise/Fidelity_One/Propeller/compute_broadband_noise.py @@ -3,20 +3,18 @@ # # Created: Mar 2021, M. Clarke # Modified: Feb 2022, M. Clarke +# Modified: Sep 2022, M. Clarke +# Modified: Sep 2022, M. Clarke # ---------------------------------------------------------------------- # Imports # ---------------------------------------------------------------------- import numpy as np - +from SUAVE.Core.Utilities import interp2d from SUAVE.Methods.Noise.Fidelity_One.Noise_Tools.dbA_noise import A_weighting from SUAVE.Methods.Noise.Fidelity_One.Noise_Tools.SPL_harmonic_to_third_octave import SPL_harmonic_to_third_octave from SUAVE.Methods.Noise.Fidelity_One.Noise_Tools.decibel_arithmetic import SPL_arithmetic -from SUAVE.Methods.Geometry.Two_Dimensional.Cross_Section.Airfoil.compute_naca_4series import compute_naca_4series -from SUAVE.Methods.Geometry.Two_Dimensional.Cross_Section.Airfoil.import_airfoil_geometry \ - import import_airfoil_geometry -from SUAVE.Methods.Aerodynamics.Airfoil_Panel_Method.airfoil_analysis import airfoil_analysis -from scipy.special import fresnel +from scipy.special import fresnel # ---------------------------------------------------------------------- # Frequency Domain Broadband Noise Computation @@ -81,12 +79,14 @@ def compute_broadband_noise(freestream,angle_of_attack,bspv, Vt_2d = aeroacoustic_data.disc_tangential_velocity Va_2d = aeroacoustic_data.disc_axial_velocity blade_chords = rotor.chord_distribution # blade chord - r = rotor.radius_distribution # radial location + r = rotor.radius_distribution # radial location + airfoils = rotor.Airfoils + a_loc = rotor.airfoil_polar_stations num_sec = len(r) - num_azi = len(aeroacoustic_data.disc_effective_angle_of_attack[0,0,:]) + num_azi = len(aeroacoustic_data.disc_effective_angle_of_attack[0,0,:]) U_blade = np.sqrt(Vt_2d**2 + Va_2d**2) - Re_blade = U_blade*np.repeat(np.repeat(blade_chords[np.newaxis,:],num_cpt,axis=0)[:,:,np.newaxis],num_azi,axis=2)*\ - np.repeat(np.repeat((rho/dyna_visc),num_sec,axis=1)[:,:,np.newaxis],num_azi,axis=2) + Re_blade = U_blade*np.repeat(np.repeat(blade_chords[np.newaxis,:],num_cpt,axis=0)[:,:,np.newaxis],num_azi,axis=2)/\ + np.repeat(np.repeat((kine_visc),num_sec,axis=1)[:,:,np.newaxis],num_azi,axis=2) rho_blade = np.repeat(np.repeat(rho,num_sec,axis=1)[:,:,np.newaxis],num_azi,axis=2) U_inf = np.atleast_2d(np.linalg.norm(velocity_vector,axis=1)).T M = U_inf/c_0 @@ -100,6 +100,9 @@ def compute_broadband_noise(freestream,angle_of_attack,bspv, delta_r[1:-1] = (del_r[:-1]+ del_r[1:])/2 + bstei = 1 # bottom surface trailing edge index + ustei = -bstei # upper surface trailing edge index + if np.all(Omega == 0): res.p_pref_broadband = np.zeros((num_cpt,num_mic,num_rot,num_cf)) res.SPL_prop_broadband_spectrum = np.zeros_like(res.p_pref_broadband) @@ -123,157 +126,157 @@ def compute_broadband_noise(freestream,angle_of_attack,bspv, lower_surface_delta_star = np.zeros_like(lower_surface_theta) lower_surface_cf = np.zeros_like(lower_surface_theta) lower_surface_Ue = np.zeros_like(lower_surface_theta) - lower_surface_H = np.zeros_like(lower_surface_theta) lower_surface_dp_dx = np.zeros_like(lower_surface_theta) upper_surface_theta = np.zeros_like(lower_surface_theta) upper_surface_delta = np.zeros_like(lower_surface_theta) upper_surface_delta_star = np.zeros_like(lower_surface_theta) upper_surface_cf = np.zeros_like(lower_surface_theta) upper_surface_Ue = np.zeros_like(lower_surface_theta) - upper_surface_H = np.zeros_like(lower_surface_theta) upper_surface_dp_dx = np.zeros_like(lower_surface_theta) - # ------------------------------------------------------------ - # ****** TRAILING EDGE BOUNDARY LAYER PROPERTY CALCULATIONS ****** - for i in range(num_cpt) : # lower surface is 0, upper surface is 1 - TE_idx = 4 # assume trailing edge is the forth from last panel + if rotor.nonuniform_freestream: - if rotor.nonuniform_freestream: - for i_azi in range(num_azi): - if rotor.airfoil_flag == True: - airfoil_data = rotor.airfoil_data - Re_batch = np.atleast_2d(Re_blade[i,:,0]).T - AoA_batch = np.atleast_2d(alpha_blade[i,:,0]).T - npanel = len(airfoil_data.x_coordinates[0]) - 2 - AP = airfoil_analysis(airfoil_data,AoA_batch,Re_batch, npanel, batch_analysis = False, airfoil_stations = rotor.airfoil_polar_stations) - else: - camber = 0.0 - camber_loc = 0.0 - thickness = 0.12 - default_airfoil_data = compute_naca_4series(camber, camber_loc, thickness,(rotor.number_of_airfoil_section_points*2 - 2)) - airfoil_polar_stations = np.zeros(num_sec) - default_airfoil_polar_stations = list(airfoil_polar_stations.astype(int) ) - Re_batch = np.atleast_2d(Re_blade[i,:,0]).T - AoA_batch = np.atleast_2d(alpha_blade[i,:,0]).T - npanel = len(default_airfoil_data.x_coordinates[0]) - 2 - AP = airfoil_analysis(default_airfoil_data,AoA_batch,Re_batch, npanel, batch_analysis = False, airfoil_stations = default_airfoil_polar_stations) + # return the 1D Cl and CDval of shape (ctrl_pts, Nr) + theta_ls = np.zeros((num_cpt,num_sec,num_azi)) + delta_ls = np.zeros((num_cpt,num_sec,num_azi)) + delta_star_ls = np.zeros((num_cpt,num_sec,num_azi)) + Ue_Vinf_ls = np.zeros((num_cpt,num_sec,num_azi)) + cf_ls = np.zeros((num_cpt,num_sec,num_azi)) + dcp_dx_ls = np.zeros((num_cpt,num_sec,num_azi)) + theta_us = np.zeros((num_cpt,num_sec,num_azi)) + delta_us = np.zeros((num_cpt,num_sec,num_azi)) + delta_star_us = np.zeros((num_cpt,num_sec,num_azi)) + Ue_Vinf_us = np.zeros((num_cpt,num_sec,num_azi)) + cf_us = np.zeros((num_cpt,num_sec,num_azi)) + dcp_dx_us = np.zeros((num_cpt,num_sec,num_azi)) - # extract properties - lower_surface_theta[:,i_azi] = AP.theta[:,TE_idx] - lower_surface_delta[:,i_azi] = AP.delta[:,TE_idx] - lower_surface_delta_star[:,i_azi] = AP.delta_star[:,TE_idx] - lower_surface_cf[:,i_azi] = AP.Cf[:,TE_idx] - lower_surface_Ue[:,i_azi] = AP.Ue_Vinf[:,TE_idx]*U_blade[i,:,i_azi] - lower_surface_H[:,i_azi] = AP.H[:,TE_idx] - surface_dcp_dx = (np.diff(AP.Cp,axis = 1)/np.diff(AP.x,axis = 1)) - lower_surface_dp_dx[:,i_azi] = abs(surface_dcp_dx[:,TE_idx]*(0.5*rho_blade[i,:,i_azi]*U_blade[i,:,i_azi]**2)/blade_chords) - upper_surface_theta[:,i_azi] = AP.theta[:,-TE_idx] - upper_surface_delta[:,i_azi] = AP.delta[:,-TE_idx] - upper_surface_delta_star[:,i_azi] = AP.delta_star[:,-TE_idx] - upper_surface_cf[:,i_azi] = AP.Cf[:,-TE_idx] - upper_surface_Ue[:,i_azi] = AP.Ue_Vinf[:,-TE_idx]*U_blade[i,:,i_azi] - upper_surface_H[:,i_azi] = AP.H[:,-TE_idx] - upper_surface_dp_dx[:,i_azi] = abs(surface_dcp_dx[:,-TE_idx]*(0.5*rho_blade[i,:,i_azi]*U_blade[i,:,i_azi]**2)/blade_chords) - else: - if rotor.airfoil_flag == True: - airfoil_geometry_data = rotor.airfoil_geometry_data - airfoil_names = airfoil_geometry_data.airfoil_names - Re_batch = np.atleast_2d(Re_blade[i,:,0]).T - AoA_batch = np.atleast_2d(alpha_blade[i,:,0]).T - npanel = len(airfoil_geometry_data.x_coordinates[airfoil_names[0]]) - 2 - AP = airfoil_analysis(airfoil_geometry_data,AoA_batch,Re_batch, npanel, batch_analysis = False, airfoil_stations = rotor.airfoil_polar_stations) - else: - camber = 0.0 - camber_loc = 0.0 - thickness = 0.12 - default_airfoil_data = compute_naca_4series(camber, camber_loc, thickness,(rotor.number_of_airfoil_section_points*2 - 2)) - airfoil_polar_stations = np.zeros(num_sec) - default_airfoil_polar_stations = list(airfoil_polar_stations.astype(int) ) - Re_batch = np.atleast_2d(Re_blade[i,:,0]).T - AoA_batch = np.atleast_2d(alpha_blade[i,:,0]).T - npanel = len(default_airfoil_data.x_coordinates[default_airfoil_data.airfoil_names[0]]) - 2 - AP = airfoil_analysis(default_airfoil_data,AoA_batch,Re_batch, npanel, batch_analysis = False, airfoil_stations = default_airfoil_polar_stations) + for i_azi in range(num_azi): + for jj,airfoil in enumerate(airfoils): + bl = airfoil.polars.boundary_layer + local_aoa = alpha_blade[:,:,i_azi] + local_Re = Re_blade[:,:,i_azi] - # extract properties - surface_dcp_dx = (np.diff(AP.Cp,axis = 1)/np.diff(AP.x,axis = 1)) - lower_surface_theta[:,:] = np.repeat(np.atleast_2d(AP.theta[:,TE_idx]).T,num_azi,axis = 1) - lower_surface_delta[:,:] = np.repeat(np.atleast_2d(AP.delta[:,TE_idx]).T,num_azi,axis = 1)*0.1 - lower_surface_delta_star[:,:] = np.repeat(np.atleast_2d(AP.delta_star[:,TE_idx]).T,num_azi,axis = 1) - lower_surface_cf[:,:] = np.repeat(np.atleast_2d(AP.Cf[:,TE_idx]).T,num_azi,axis = 1) - lower_surface_Ue[:,:] = np.repeat(np.atleast_2d(AP.Ue_Vinf[:,TE_idx]*U_blade[i,:,0]).T,num_azi,axis = 1) - lower_surface_H[:,:] = np.repeat(np.atleast_2d(AP.H[:,TE_idx]).T,num_azi,axis = 1) - lower_surface_dp_dx[:,:] = np.repeat(np.atleast_2d(surface_dcp_dx[:,TE_idx]*(0.5*rho_blade[i,:,0]*(U_blade[i,:,0]**2))/blade_chords).T,num_azi,axis = 1) - upper_surface_theta[:,:] = np.repeat(np.atleast_2d(AP.theta[:,-TE_idx]).T,num_azi,axis = 1) - upper_surface_delta[:,:] = np.repeat(np.atleast_2d(AP.delta[:,-TE_idx]).T,num_azi,axis = 1)*0.1 - upper_surface_delta_star[:,:] = np.repeat(np.atleast_2d(AP.delta_star[:,-TE_idx]).T,num_azi,axis = 1) - upper_surface_cf[:,:] = np.repeat(np.atleast_2d(AP.Cf[:,-TE_idx]).T,num_azi,axis = 1) - upper_surface_Ue[:,:] = np.repeat(np.atleast_2d(AP.Ue_Vinf[:,-TE_idx]*U_blade[i,:,0]).T,num_azi,axis = 1) - upper_surface_H[:,:] = np.repeat(np.atleast_2d(AP.H[:,-TE_idx]).T,num_azi,axis = 1) - upper_surface_dp_dx[:,:] = np.repeat(np.atleast_2d(surface_dcp_dx[:,-TE_idx]*(0.5*rho_blade[i,:,0]*(U_blade[i,:,0]**2))/blade_chords).T,num_azi,axis = 1) + theta_ls_data = interp2d(local_Re,local_aoa,bl.reynolds_numbers, bl.angle_of_attacks, bl.theta_lower_surface[:,:,bstei]) + delta_ls_data = interp2d(local_Re,local_aoa,bl.reynolds_numbers, bl.angle_of_attacks, bl.delta_lower_surface[:,:,bstei]) + delta_star_ls_data = interp2d(local_Re,local_aoa,bl.reynolds_numbers, bl.angle_of_attacks, bl.delta_star_lower_surface[:,:,bstei]) + Ue_Vinf_ls_data = interp2d(local_Re,local_aoa,bl.reynolds_numbers, bl.angle_of_attacks, bl.Ue_Vinf_lower_surface[:,:,bstei]) + cf_ls_data = interp2d(local_Re,local_aoa,bl.reynolds_numbers, bl.angle_of_attacks, bl.cf_lower_surface[:,:,bstei]) + dcp_dx_ls_data = interp2d(local_Re,local_aoa,bl.reynolds_numbers, bl.angle_of_attacks, bl.dcp_dx_lower_surface[:,:,bstei]) + theta_us_data = interp2d(local_Re,local_aoa,bl.reynolds_numbers, bl.angle_of_attacks, bl.theta_upper_surface[:,:,ustei]) + delta_us_data = interp2d(local_Re,local_aoa,bl.reynolds_numbers, bl.angle_of_attacks, bl.delta_upper_surface[:,:,ustei]) + delta_star_us_data = interp2d(local_Re,local_aoa,bl.reynolds_numbers, bl.angle_of_attacks, bl.delta_star_upper_surface[:,:,ustei]) + Ue_Vinf_us_data = interp2d(local_Re,local_aoa,bl.reynolds_numbers, bl.angle_of_attacks, bl.Ue_Vinf_upper_surface[:,:,ustei]) + cf_us_data = interp2d(local_Re,local_aoa,bl.reynolds_numbers, bl.angle_of_attacks, bl.cf_upper_surface[:,:,ustei]) + dcp_dx_us_data = interp2d(local_Re,local_aoa,bl.reynolds_numbers, bl.angle_of_attacks, bl.dcp_dx_upper_surface[:,:,ustei]) + locs = np.where(np.array(a_loc) == jj ) - # replace nans 0 with mean as a post post-processor - lower_surface_theta = np.nan_to_num(lower_surface_theta) - upper_surface_theta = np.nan_to_num(upper_surface_theta) - lower_surface_delta = np.nan_to_num(lower_surface_delta) - upper_surface_delta = np.nan_to_num(upper_surface_delta) - lower_surface_delta_star = np.nan_to_num(lower_surface_delta_star) - upper_surface_delta_star = np.nan_to_num(upper_surface_delta_star) - lower_surface_cf = np.nan_to_num(lower_surface_cf) - upper_surface_cf = np.nan_to_num(upper_surface_cf) - lower_surface_dp_dx = np.nan_to_num(lower_surface_dp_dx ) - upper_surface_dp_dx = np.nan_to_num(upper_surface_dp_dx ) - lower_surface_Ue = np.nan_to_num(lower_surface_Ue) - upper_surface_Ue = np.nan_to_num(upper_surface_Ue) - lower_surface_H = np.nan_to_num(lower_surface_H) - upper_surface_H = np.nan_to_num(upper_surface_H) - - # apply thresholds for non-converged boundary layer solutions form pandel code - lower_surface_theta[abs(lower_surface_theta)> 0.01 ] = 0.0 - upper_surface_theta[abs(upper_surface_theta)>0.01 ] = 0.0 - lower_surface_delta[abs(lower_surface_delta)> 0.1 ] = 0.0 - upper_surface_delta[abs(upper_surface_delta)> 0.1] = 0.0 - lower_surface_delta_star[abs(lower_surface_delta_star)>0.1 ] = 0.0 - upper_surface_delta_star[abs(upper_surface_delta_star)>0.1 ] = 0.0 - lower_surface_cf[abs(lower_surface_cf)>0.1 ] = 0.0 - upper_surface_cf[abs(upper_surface_cf)> 0.1] = 0.0 - lower_surface_dp_dx[abs(lower_surface_dp_dx)> 1E7] = 0.0 - upper_surface_dp_dx[abs(upper_surface_dp_dx)> 1E7] = 0.0 - lower_surface_Ue[abs(lower_surface_Ue)> 500.] = 0.0 - upper_surface_Ue[abs(upper_surface_Ue)> 500.] = 0.0 - lower_surface_H[abs(lower_surface_H)> 10.] = 0.0 - upper_surface_H[abs(upper_surface_H)> 10.] = 0.0 - - # replace null solutions with mean - lower_surface_theta[lower_surface_theta == 0] = np.mean(lower_surface_theta) - upper_surface_theta[upper_surface_theta == 0] = np.mean(upper_surface_theta) - lower_surface_delta[lower_surface_delta == 0] = np.mean(lower_surface_delta) - upper_surface_delta[upper_surface_delta == 0] = np.mean(upper_surface_delta) - lower_surface_delta_star[lower_surface_delta_star == 0] = np.mean(lower_surface_delta_star) - upper_surface_delta_star[upper_surface_delta_star== 0] = np.mean(upper_surface_delta_star) - lower_surface_cf[lower_surface_cf == 0] = np.mean(lower_surface_cf) - upper_surface_cf[upper_surface_cf == 0] = np.mean(upper_surface_cf) - lower_surface_dp_dx [lower_surface_dp_dx == 0] = np.mean(lower_surface_dp_dx ) - upper_surface_dp_dx [upper_surface_dp_dx == 0] = np.mean(upper_surface_dp_dx ) - lower_surface_Ue[lower_surface_Ue == 0] = np.mean(lower_surface_Ue) - upper_surface_Ue[upper_surface_Ue == 0] = np.mean(upper_surface_Ue) - lower_surface_H[lower_surface_H == 0] = np.mean(lower_surface_H) - upper_surface_H[upper_surface_H == 0] = np.mean(upper_surface_H) + theta_ls[:,locs,i_azi] = theta_ls_data[:,locs] + delta_ls[:,locs,i_azi] = delta_ls_data[:,locs] + delta_star_ls[:,locs,i_azi] = delta_star_ls_data[:,locs] + Ue_Vinf_ls[:,locs,i_azi] = Ue_Vinf_ls_data[:,locs] + cf_ls[:,locs,i_azi] = cf_ls_data[:,locs] + dcp_dx_ls[:,locs,i_azi] = dcp_dx_ls_data[:,locs] + theta_us[:,locs,i_azi] = theta_us_data[:,locs] + delta_us[:,locs,i_azi] = delta_us_data[:,locs] + delta_star_us[:,locs,i_azi] = delta_star_us_data[:,locs] + Ue_Vinf_us[:,locs,i_azi] = Ue_Vinf_us_data[:,locs] + cf_us[:,locs,i_azi] = cf_us_data[:,locs] + dcp_dx_us[:,locs,i_azi] = dcp_dx_us_data[:,locs] + + blade_chords_3d = np.tile(np.tile(blade_chords[None,:],(num_cpt,1))[:,:,None],(1,1,num_azi)) + dP_dX_ls = dcp_dx_ls*(0.5*rho_blade*U_blade**2)/blade_chords_3d + dP_dX_us = dcp_dx_us*(0.5*rho_blade*U_blade**2)/blade_chords_3d + + lower_surface_theta = theta_ls + lower_surface_delta = delta_ls + lower_surface_delta_star = delta_star_ls + lower_surface_cf = cf_ls + lower_surface_Ue = Ue_Vinf_ls*U_blade + lower_surface_dp_dx = dP_dX_ls + upper_surface_theta = theta_us + upper_surface_delta = delta_us + upper_surface_delta_star = delta_star_us + upper_surface_cf = cf_us + upper_surface_Ue = Ue_Vinf_us*U_blade + upper_surface_dp_dx = dP_dX_us - # ------------------------------------------------------------ - # ****** TRAILING EDGE BOUNDARY LAYER PROPERTY CALCULATIONS ****** + else: + theta_ls = np.zeros((num_cpt,num_sec)) + delta_ls = np.zeros((num_cpt,num_sec)) + delta_star_ls = np.zeros((num_cpt,num_sec)) + Ue_Vinf_ls = np.zeros((num_cpt,num_sec)) + cf_ls = np.zeros((num_cpt,num_sec)) + dcp_dx_ls = np.zeros((num_cpt,num_sec)) + theta_us = np.zeros((num_cpt,num_sec)) + delta_us = np.zeros((num_cpt,num_sec)) + delta_star_us = np.zeros((num_cpt,num_sec)) + Ue_Vinf_us = np.zeros((num_cpt,num_sec)) + cf_us = np.zeros((num_cpt,num_sec)) + dcp_dx_us = np.zeros((num_cpt,num_sec)) + local_aoa = alpha_blade[:,:,0] + local_Re = Re_blade[:,:,0] + + for jj,airfoil in enumerate(airfoils): + bl = airfoil.polars.boundary_layer + theta_ls_data = interp2d(local_Re,local_aoa,bl.reynolds_numbers, bl.angle_of_attacks, bl.theta_lower_surface[:,:,bstei]) + delta_ls_data = interp2d(local_Re,local_aoa,bl.reynolds_numbers, bl.angle_of_attacks, bl.delta_lower_surface[:,:,bstei]) + delta_star_ls_data = interp2d(local_Re,local_aoa,bl.reynolds_numbers, bl.angle_of_attacks, bl.delta_star_lower_surface[:,:,bstei]) + Ue_Vinf_ls_data = interp2d(local_Re,local_aoa,bl.reynolds_numbers, bl.angle_of_attacks, bl.Ue_Vinf_lower_surface[:,:,bstei]) + cf_ls_data = interp2d(local_Re,local_aoa,bl.reynolds_numbers, bl.angle_of_attacks, bl.cf_lower_surface[:,:,bstei]) + dcp_dx_ls_data = interp2d(local_Re,local_aoa,bl.reynolds_numbers, bl.angle_of_attacks, bl.dcp_dx_lower_surface[:,:,bstei]) + theta_us_data = interp2d(local_Re,local_aoa,bl.reynolds_numbers, bl.angle_of_attacks, bl.theta_upper_surface[:,:,ustei]) + delta_us_data = interp2d(local_Re,local_aoa,bl.reynolds_numbers, bl.angle_of_attacks, bl.delta_upper_surface[:,:,ustei]) + delta_star_us_data = interp2d(local_Re,local_aoa,bl.reynolds_numbers, bl.angle_of_attacks, bl.delta_star_upper_surface[:,:,ustei]) + Ue_Vinf_us_data = interp2d(local_Re,local_aoa,bl.reynolds_numbers, bl.angle_of_attacks, bl.Ue_Vinf_upper_surface[:,:,ustei]) + cf_us_data = interp2d(local_Re,local_aoa,bl.reynolds_numbers, bl.angle_of_attacks, bl.cf_upper_surface[:,:,ustei]) + dcp_dx_us_data = interp2d(local_Re,local_aoa,bl.reynolds_numbers, bl.angle_of_attacks, bl.dcp_dx_upper_surface[:,:,ustei]) + + locs = np.where(np.array(a_loc) == jj ) + + theta_ls[:,locs] = theta_ls_data[:,locs] + delta_ls[:,locs] = delta_ls_data[:,locs] + delta_star_ls[:,locs] = delta_star_ls_data[:,locs] + Ue_Vinf_ls[:,locs] = Ue_Vinf_ls_data[:,locs] + cf_ls[:,locs] = cf_ls_data[:,locs] + dcp_dx_ls[:,locs] = dcp_dx_ls_data[:,locs] + theta_us[:,locs] = theta_us_data[:,locs] + delta_us[:,locs] = delta_us_data[:,locs] + delta_star_us[:,locs] = delta_star_us_data[:,locs] + Ue_Vinf_us[:,locs] = Ue_Vinf_us_data[:,locs] + cf_us[:,locs] = cf_us_data[:,locs] + dcp_dx_us[:,locs] = dcp_dx_us_data[:,locs] + + blade_chords_2d = np.tile(blade_chords[None,:],(num_cpt,1)) + dP_dX_ls = dcp_dx_ls*(0.5*rho_blade[:,:,0]*(U_blade[:,:,0]**2))/blade_chords_2d + dP_dX_us = dcp_dx_us*(0.5*rho_blade[:,:,0]*(U_blade[:,:,0]**2))/blade_chords_2d + + lower_surface_theta = np.tile(theta_ls[:,:,None],(1,1,num_azi)) + lower_surface_delta = np.tile((blade_chords_2d*delta_ls)[:,:,None],(1,1,num_azi)) + lower_surface_delta_star = np.tile(delta_star_ls[:,:,None],(1,1,num_azi)) + lower_surface_cf = np.tile(cf_ls[:,:,None],(1,1,num_azi)) + lower_surface_Ue = np.tile(Ue_Vinf_ls[:,:,None],(1,1,num_azi))*U_blade + lower_surface_dp_dx = np.tile(dP_dX_ls[:,:,None],(1,1,num_azi)) + upper_surface_theta = np.tile(theta_us[:,:,None],(1,1,num_azi)) + upper_surface_delta = np.tile((blade_chords_2d*delta_us)[:,:,None],(1,1,num_azi)) + upper_surface_delta_star = np.tile(delta_star_us[:,:,None],(1,1,num_azi)) + upper_surface_cf = np.tile(cf_us[:,:,None],(1,1,num_azi)) + upper_surface_Ue = np.tile(Ue_Vinf_us[:,:,None],(1,1,num_azi))*U_blade + upper_surface_dp_dx = np.tile(dP_dX_us[:,:,None],(1,1,num_azi)) + # ------------------------------------------------------------ + # ****** TRAILING EDGE BOUNDARY LAYER PROPERTY CALCULATIONS ****** - delta[i,:,:,:,:,:,0] = np.tile(lower_surface_delta[None,None,:,:,None],(num_mic,num_rot,1,1,num_cf)) # lower surface boundary layer thickness - delta[i,:,:,:,:,:,1] = np.tile(upper_surface_delta[None,None,:,:,None],(num_mic,num_rot,1,1,num_cf)) # upper surface boundary layer thickness - delta_star[i,:,:,:,:,:,0] = np.tile(lower_surface_delta_star[None,None,:,:,None],(num_mic,num_rot,1,1,num_cf)) # lower surface displacement thickness - delta_star[i,:,:,:,:,:,1] = np.tile(upper_surface_delta_star[None,None,:,:,None],(num_mic,num_rot,1,1,num_cf)) # upper surface displacement thickness - dp_dx[i,:,:,:,:,:,0] = np.tile(lower_surface_dp_dx[None,None,:,:,None],(num_mic,num_rot,1,1,num_cf)) # lower surface pressure differential - dp_dx[i,:,:,:,:,:,1] = np.tile(upper_surface_dp_dx[None,None,:,:,None],(num_mic,num_rot,1,1,num_cf)) # upper surface pressure differential - Ue[i,:,:,:,:,:,0] = np.tile(lower_surface_Ue[None,None,:,:,None],(num_mic,num_rot,1,1,num_cf)) # lower surface boundary layer edge velocity - Ue[i,:,:,:,:,:,1] = np.tile(upper_surface_Ue[None,None,:,:,None],(num_mic,num_rot,1,1,num_cf)) # upper surface boundary layer edge velocity - tau_w[i,:,:,:,:,:,0] = np.tile((lower_surface_cf*(0.5*rho_blade[i]*(U_blade[i]**2)))[None,None,:,:,None],(num_mic,num_rot,1,1,num_cf)) # lower surface wall shear stress - tau_w[i:,:,:,:,:,:,1] = np.tile((upper_surface_cf*(0.5*rho_blade[i]*(U_blade[i]**2)))[None,None,:,:,None],(num_mic,num_rot,1,1,num_cf)) # upper surface wall shear stress - Theta[i,:,:,:,:,:,0] = np.tile(lower_surface_theta[None,None,:,:,None],(num_mic,num_rot,1,1,num_cf)) # lower surface momentum thickness - Theta[i,:,:,:,:,:,1] = np.tile(upper_surface_theta[None,None,:,:,None],(num_mic,num_rot,1,1,num_cf)) # upper surface momentum thickness + delta[:,:,:,:,:,:,0] = np.tile(lower_surface_delta[:,None,None,:,:,None],(1,num_mic,num_rot,1,1,num_cf)) # lower surface boundary layer thickness + delta[:,:,:,:,:,:,1] = np.tile(upper_surface_delta[:,None,None,:,:,None],(1,num_mic,num_rot,1,1,num_cf)) # upper surface boundary layer thickness + delta_star[:,:,:,:,:,:,0] = np.tile(lower_surface_delta_star[:,None,None,:,:,None],(1,num_mic,num_rot,1,1,num_cf)) # lower surface displacement thickness + delta_star[:,:,:,:,:,:,1] = np.tile(upper_surface_delta_star[:,None,None,:,:,None],(1,num_mic,num_rot,1,1,num_cf)) # upper surface displacement thickness + dp_dx[:,:,:,:,:,:,0] = np.tile(lower_surface_dp_dx[:,None,None,:,:,None],(1,num_mic,num_rot,1,1,num_cf)) # lower surface pressure differential + dp_dx[:,:,:,:,:,:,1] = np.tile(upper_surface_dp_dx[:,None,None,:,:,None],(1,num_mic,num_rot,1,1,num_cf)) # upper surface pressure differential + Ue[:,:,:,:,:,:,0] = np.tile(lower_surface_Ue[:,None,None,:,:,None],(1,num_mic,num_rot,1,1,num_cf)) # lower surface boundary layer edge velocity + Ue[:,:,:,:,:,:,1] = np.tile(upper_surface_Ue[:,None,None,:,:,None],(1,num_mic,num_rot,1,1,num_cf)) # upper surface boundary layer edge velocity + tau_w[:,:,:,:,:,:,0] = np.tile((lower_surface_cf*(0.5*rho_blade*(U_blade**2)))[:,None,None,:,:,None],(1,num_mic,num_rot,1,1,num_cf)) # lower surface wall shear stress + tau_w[:,:,:,:,:,:,1] = np.tile((upper_surface_cf*(0.5*rho_blade*(U_blade**2)))[:,None,None,:,:,None],(1,num_mic,num_rot,1,1,num_cf)) # upper surface wall shear stress + Theta[:,:,:,:,:,:,0] = np.tile(lower_surface_theta[:,None,None,:,:,None],(1,num_mic,num_rot,1,1,num_cf)) # lower surface momentum thickness + Theta[:,:,:,:,:,:,1] = np.tile(upper_surface_theta[:,None,None,:,:,None],(1,num_mic,num_rot,1,1,num_cf)) # upper surface momentum thickness # Update dimensions for computation r = np.tile(r[None,None,None,:,None,None],(num_cpt,num_mic,num_rot,1,num_azi,num_cf)) diff --git a/trunk/SUAVE/Methods/Noise/Fidelity_One/Propeller/propeller_mid_fidelity.py b/trunk/SUAVE/Methods/Noise/Fidelity_One/Propeller/propeller_mid_fidelity.py index c0769922d2..2befe7c1be 100644 --- a/trunk/SUAVE/Methods/Noise/Fidelity_One/Propeller/propeller_mid_fidelity.py +++ b/trunk/SUAVE/Methods/Noise/Fidelity_One/Propeller/propeller_mid_fidelity.py @@ -12,7 +12,7 @@ import numpy as np from SUAVE.Methods.Noise.Fidelity_One.Noise_Tools.decibel_arithmetic import SPL_spectra_arithmetic from SUAVE.Methods.Noise.Fidelity_One.Propeller.compute_source_coordinates import compute_point_source_coordinates -from SUAVE.Methods.Noise.Fidelity_One.Propeller.compute_source_coordinates import compute_blade_section_source_coordinates +from SUAVE.Methods.Noise.Fidelity_One.Propeller.compute_source_coordinates import compute_blade_section_source_coordinates from SUAVE.Methods.Noise.Fidelity_One.Propeller.compute_harmonic_noise import compute_harmonic_noise from SUAVE.Methods.Noise.Fidelity_One.Propeller.compute_broadband_noise import compute_broadband_noise @@ -33,7 +33,7 @@ def propeller_mid_fidelity(rotors,aeroacoustic_data,segment,settings): Inputs: rotors - data structure of rotors [None] segment - flight segment data structure [None] - aeroacoustic_data - data structure of acoustic data [None] + aeroacoustic_data - data structure of acoustic data [None] settings - accoustic settings [None] Outputs: diff --git a/trunk/SUAVE/Methods/Propulsion/Rotor_Wake/Fidelity_One/generate_fidelity_one_wake_shape.py b/trunk/SUAVE/Methods/Propulsion/Rotor_Wake/Fidelity_One/generate_fidelity_one_wake_shape.py index 4320998789..4570601b01 100644 --- a/trunk/SUAVE/Methods/Propulsion/Rotor_Wake/Fidelity_One/generate_fidelity_one_wake_shape.py +++ b/trunk/SUAVE/Methods/Propulsion/Rotor_Wake/Fidelity_One/generate_fidelity_one_wake_shape.py @@ -138,25 +138,28 @@ def generate_fidelity_one_wake_shape(wake,rotor): # put into velocity frame and find (y,z) components azi_y = np.sin(panel_azimuthal_positions) - azi_z = np.cos(panel_azimuthal_positions) - - airfoil_data = rotor.airfoil_geometry_data - a_secl = rotor.airfoil_polar_stations + azi_z = np.cos(panel_azimuthal_positions) - # trailing edge points in airfoil coordinates - xupper = np.take(airfoil_data.x_upper_surface.values(),a_secl,axis=0) - yupper = np.take(airfoil_data.y_upper_surface.values(),a_secl,axis=0) + # trailing edge points in airfoil coordinates + airfoils = rotor.Airfoils + af = airfoils[list(airfoils.keys())[0]] + a_loc = rotor.airfoil_polar_stations + xupper = np.zeros((Nr,len(af.geometry.x_upper_surface))) + yupper = np.zeros((Nr,len(af.geometry.x_upper_surface))) + for i,airfoil in enumerate(airfoils): + a_geo = airfoil.geometry + locs = np.where(np.array(a_loc) == i ) + xupper[locs] = a_geo.x_upper_surface + yupper[locs] = a_geo.y_upper_surface # Align the quarter chords of the airfoils (zero sweep) airfoil_le_offset = -c/2 xte_airfoils = xupper[:,-1]*c + airfoil_le_offset - yte_airfoils = yupper[:,-1]*c - - xle_airfoils = xupper[:,0]*c + airfoil_le_offset - yle_airfoils = yupper[:,0]*c - - x_c_4_airfoils = (xle_airfoils - xte_airfoils)/4 - airfoil_le_offset - y_c_4_airfoils = (yle_airfoils - yte_airfoils)/4 + yte_airfoils = yupper[:,-1]*c + xle_airfoils = xupper[:,0]*c + airfoil_le_offset + yle_airfoils = yupper[:,0]*c + x_c_4_airfoils = (xle_airfoils - xte_airfoils)/4 - airfoil_le_offset + y_c_4_airfoils = (yle_airfoils - yte_airfoils)/4 # apply blade twist rotation along rotor radius xte_twisted = np.cos(beta)*xte_airfoils - np.sin(beta)*yte_airfoils diff --git a/trunk/SUAVE/Methods/Propulsion/Rotor_Wake/Fidelity_Zero/fidelity_zero_wake_convergence.py b/trunk/SUAVE/Methods/Propulsion/Rotor_Wake/Fidelity_Zero/fidelity_zero_wake_convergence.py index 31dcf4442a..c2bcee921d 100644 --- a/trunk/SUAVE/Methods/Propulsion/Rotor_Wake/Fidelity_Zero/fidelity_zero_wake_convergence.py +++ b/trunk/SUAVE/Methods/Propulsion/Rotor_Wake/Fidelity_Zero/fidelity_zero_wake_convergence.py @@ -7,7 +7,6 @@ from SUAVE.Methods.Aerodynamics.Common.Fidelity_Zero.Lift.BET_calculations import compute_airfoil_aerodynamics,compute_inflow_and_tip_loss import numpy as np import scipy as sp -import copy ## @defgroup Methods-Propulsion-Rotor_Wake-Fidelity_Zero def fidelity_zero_wake_convergence(wake,rotor,wake_inputs): @@ -107,11 +106,11 @@ def iteration(PSI, wake_inputs, rotor): Na = wake_inputs.Na # Unpack rotor data - R = rotor.tip_radius - B = rotor.number_of_blades - tc = rotor.thickness_to_chord - a_loc = rotor.airfoil_polar_stations - a_pol_data = rotor.airfoil_polar_data + R = rotor.tip_radius + B = rotor.number_of_blades + tc = rotor.thickness_to_chord + airfoils = rotor.Airfoils + a_loc = rotor.airfoil_polar_stations # Reshape PSI because the solver gives it flat if wake_inputs.use_2d_analysis: @@ -127,7 +126,7 @@ def iteration(PSI, wake_inputs, rotor): vt = Ut - Wt # compute blade airfoil forces and properties - Cl, Cdval, alpha, Ma, W = compute_airfoil_aerodynamics(beta,c,r,R,B,Wa,Wt,a,nu,a_loc,a_pol_data,ctrl_pts,Nr,Na,tc,use_2d_analysis) + Cl, Cdval, alpha, Ma, W = compute_airfoil_aerodynamics(beta,c,r,R,B,Wa,Wt,a,nu,airfoils,a_loc,ctrl_pts,Nr,Na,tc,use_2d_analysis) # compute inflow velocity and tip loss factor lamdaw, F, piece = compute_inflow_and_tip_loss(r,R,Wa,Wt,B) diff --git a/trunk/SUAVE/Methods/Propulsion/propeller_design.py b/trunk/SUAVE/Methods/Propulsion/propeller_design.py index 5b66588ecb..b1ac557e3b 100644 --- a/trunk/SUAVE/Methods/Propulsion/propeller_design.py +++ b/trunk/SUAVE/Methods/Propulsion/propeller_design.py @@ -11,12 +11,18 @@ # ---------------------------------------------------------------------- # Imports # ---------------------------------------------------------------------- - import SUAVE +from SUAVE.Core import Data +from SUAVE.Core.Utilities import interp2d import numpy as np import scipy as sp from scipy.optimize import root - +from SUAVE.Methods.Geometry.Two_Dimensional.Cross_Section.Airfoil.compute_airfoil_properties \ + import compute_airfoil_properties +from SUAVE.Methods.Geometry.Two_Dimensional.Cross_Section.Airfoil.compute_naca_4series \ + import compute_naca_4series +from SUAVE.Methods.Geometry.Two_Dimensional.Cross_Section.Airfoil.import_airfoil_geometry\ + import import_airfoil_geometry # ---------------------------------------------------------------------- # Propeller Design # ---------------------------------------------------------------------- @@ -44,23 +50,20 @@ def propeller_design(prop,number_of_stations=20): Assumptions/ Source: Based on Design of Optimum Propellers by Adkins and Liebeck - """ - print('\nDesigning',prop.tag) - + """ # Unpack - N = number_of_stations # this number determines the discretization of the propeller into stations - B = prop.number_of_blades - R = prop.tip_radius - Rh = prop.hub_radius - omega = prop.angular_velocity # Rotation Rate in rad/s - V = prop.freestream_velocity # Freestream Velocity - Cl = prop.design_Cl # Design Lift Coefficient - alt = prop.design_altitude - Thrust = prop.design_thrust - Power = prop.design_power - a_pol_data = prop.airfoil_polar_data - a_geo_data = prop.airfoil_geometry_data - a_loc = prop.airfoil_polar_stations + N = number_of_stations # this number determines the discretization of the propeller into stations + B = prop.number_of_blades + R = prop.tip_radius + Rh = prop.hub_radius + omega = prop.angular_velocity # Rotation Rate in rad/s + V = prop.freestream_velocity # Freestream Velocity + Cl = prop.design_Cl # Design Lift Coefficient + alt = prop.design_altitude + Thrust = prop.design_thrust + Power = prop.design_power + airfoils = prop.Airfoils + a_loc = prop.airfoil_polar_stations if (Thrust == None) and (Power== None): raise AssertionError('Specify either design thrust or design power!') @@ -75,6 +78,7 @@ def propeller_design(prop,number_of_stations=20): atmosphere = SUAVE.Analyses.Atmospheric.US_Standard_1976() atmo_data = atmosphere.compute_values(alt) + p = atmo_data.pressure[0] T = atmo_data.temperature[0] rho = atmo_data.density[0] speed_of_sound = atmo_data.speed_of_sound[0] @@ -105,22 +109,27 @@ def propeller_design(prop,number_of_stations=20): x = omega*r/V # Nondimensional distance diff = 1.0 # Difference between zetas n = omega/(2*np.pi) # Cycles per second - D = 2.*R - - c = 0.2 * np.ones_like(chi) + D = 2.*R + c = 0.2 * np.ones_like(chi) # if user defines airfoil, check dimension of stations - if a_geo_data != None and a_loc != None: + num_airfoils = len(airfoils.keys()) + if num_airfoils>0: if len(a_loc) != N: - raise AssertionError('\nDimension of airfoil sections must be equal to number of stations on propeller') - airfoil_flag = True - else: - print('\nDefaulting to scaled DAE51') - airfoil_flag = False + raise AssertionError('\nDimension of airfoil sections must be equal to number of stations on propeller') + for _,airfoil in enumerate(airfoils): + if airfoil.geometry == None: # first, if airfoil geometry data not defined, import from geoemtry files + if airfoil.NACA_4_series_flag: # check if naca 4 series of airfoil from datafile + airfoil.geometry = compute_naca_4series(airfoil.coordinate_file,airfoil.number_of_points) + else: + airfoil.geometry = import_airfoil_geometry(airfoil.coordinate_file,airfoil.number_of_points) + + if airfoil.polars == None: # compute airfoil polars for airfoils + airfoil.polars = compute_airfoil_properties(airfoil.geometry, airfoil_polar_files= airfoil.polar_files) + else: + print('\nDefaulting to scaled DAE51') - # Step 4, determine epsilon and alpha from airfoil data - while diff>tol: # assign chord distribution prop.chord_distribution = c @@ -140,19 +149,19 @@ def propeller_design(prop,number_of_stations=20): Ma = Wc/speed_of_sound RE = Wc/nu - - if airfoil_flag: + if num_airfoils>0: # assign initial values alpha0 = np.ones(N)*0.05 # solve for optimal alpha to meet design Cl target - sol = root(objective, x0 = alpha0 , args=(a_pol_data, RE ,a_loc, Cl ,N)) + sol = root(objective, x0 = alpha0 , args=(airfoils,a_loc,RE,Cl,N)) alpha = sol.x # query surrogate for sectional Cls at stations Cdval = np.zeros_like(RE) - for j in range(len(a_pol_data.lift_coefficient_surrogates)): - Cdval_af = a_pol_data.drag_coefficient_surrogates[a_pol_data.airfoil_names[j]]((RE,alpha)) + for j,airfoil in enumerate(airfoils): + pd = airfoil.polars + Cdval_af = interp2d(RE,alpha,pd.reynolds_numbers, pd.angle_of_attacks, pd.drag_coefficients) locs = np.where(np.array(a_loc) == j ) Cdval[locs] = Cdval_af[locs] @@ -171,8 +180,7 @@ def propeller_design(prop,number_of_stations=20): epsilon = Cd/Cl #Step 6, determine a and a', and W - a = (zeta/2.)*(np.cos(phi)**2.)*(1.-epsilon*np.tan(phi)) - aprime = (zeta/(2.*x))*np.cos(phi)*np.sin(phi)*(1.+epsilon/np.tan(phi)) + a = (zeta/2.)*(np.cos(phi)**2.)*(1.-epsilon*np.tan(phi)) W = V*(1.+a)/np.sin(phi) #Step 7, compute the chord length and blade twist angle @@ -184,9 +192,7 @@ def propeller_design(prop,number_of_stations=20): Iprime2 = lamda*(Iprime1/(2.*chi))*(1.+epsilon/np.tan(phi) )*np.sin(phi)*np.cos(phi) Jprime1 = 4.*chi*G*(1.+epsilon/np.tan(phi)) - Jprime2 = (Jprime1/2.)*(1.-epsilon*np.tan(phi))*(np.cos(phi)**2.) - - dR = (r[1]-r[0])*np.ones_like(Jprime1) + Jprime2 = (Jprime1/2.)*(1.-epsilon*np.tan(phi))*(np.cos(phi)**2.) dchi = (chi[1]-chi[0])*np.ones_like(Jprime1) #Integrate derivatives from chi=chi0 to chi=1 @@ -240,20 +246,19 @@ def propeller_design(prop,number_of_stations=20): Cp = Power/(rho*(n*n*n)*(D*D*D*D*D)) # compute max thickness distribution - if airfoil_flag: - aNames = a_geo_data.airfoil_names - maxT = np.zeros(len(aNames)) - T_C = np.zeros(len(aNames)) - for j in range(len(aNames)): - maxT[j] = a_geo_data.max_thickness[aNames[j]] - T_C[j] = a_geo_data.thickness_to_chord[aNames[j]] - t_max = np.take(maxT,a_loc,axis=0)*c - t_c = np.take(T_C,a_loc,axis=0) + t_max = np.zeros(N) + t_c = np.zeros(N) + if num_airfoils>0: + for j,airfoil in enumerate(airfoils): + a_geo = airfoil.geometry + locs = np.where(np.array(a_loc) == j ) + t_max[locs] = a_geo.max_thickness*c[locs] + t_c[locs] = a_geo.thickness_to_chord else: - c_blade = np.repeat(np.atleast_2d(np.linspace(0,1,N)),N, axis = 0)* np.repeat(np.atleast_2d(c).T,N, axis = 1) - t = (5*c_blade)*(0.2969*np.sqrt(c_blade) - 0.1260*c_blade - 0.3516*(c_blade**2) + 0.2843*(c_blade**3) - 0.1015*(c_blade**4)) # local thickness distribution - t_max = np.max(t,axis = 1) - t_c = np.max(t,axis = 1) /c + c_blade = np.repeat(np.atleast_2d(np.linspace(0,1,N)),N, axis = 0)* np.repeat(np.atleast_2d(c).T,N, axis = 1) + t = (5*c_blade)*(0.2969*np.sqrt(c_blade) - 0.1260*c_blade - 0.3516*(c_blade**2) + 0.2843*(c_blade**3) - 0.1015*(c_blade**4)) # local thickness distribution + t_max = np.max(t,axis = 1) + t_c = np.max(t,axis = 1) /c # Nondimensional thrust if prop.design_power == None: @@ -266,27 +271,27 @@ def propeller_design(prop,number_of_stations=20): blade_area = sp.integrate.cumtrapz(B*c, r-r[0]) sigma = blade_area[-1]/(np.pi*R**2) - prop.design_torque = Power[0]/omega - prop.max_thickness_distribution = t_max - prop.twist_distribution = beta - prop.chord_distribution = c - prop.radius_distribution = r - prop.number_of_blades = int(B) - prop.design_power_coefficient = Cp - prop.design_thrust_coefficient = Ct - prop.mid_chord_alignment = MCA - prop.thickness_to_chord = t_c - prop.blade_solidity = sigma - prop.airfoil_flag = airfoil_flag + prop.design_torque = Power[0]/omega + prop.max_thickness_distribution = t_max + prop.twist_distribution = beta + prop.chord_distribution = c + prop.radius_distribution = r + prop.number_of_blades = int(B) + prop.design_power_coefficient = Cp + prop.design_thrust_coefficient = Ct + prop.mid_chord_alignment = MCA + prop.thickness_to_chord = t_c + prop.blade_solidity = sigma return prop -def objective(x, a_pol_data, RE ,a_loc, Cl ,N): +def objective(x,airfoils,a_loc,RE,Cl,N): # query surrogate for sectional Cls at stations - Cl_vals = np.zeros(N) - for j in range(len(a_pol_data.lift_coefficient_surrogates)): - Cl_af = a_pol_data.lift_coefficient_surrogates[a_pol_data.airfoil_names[j]]((RE,x)) + Cl_vals = np.zeros(N) + for j,airfoil in enumerate(airfoils): + pd = airfoil.polars + Cl_af = interp2d(RE,x,pd.reynolds_numbers, pd.angle_of_attacks, pd.lift_coefficients) locs = np.where(np.array(a_loc) == j ) Cl_vals[locs] = Cl_af[locs] diff --git a/trunk/SUAVE/Plots/Geometry/plot_airfoil.py b/trunk/SUAVE/Plots/Geometry/plot_airfoil.py index 63a5fa8945..6ce0b22b1e 100644 --- a/trunk/SUAVE/Plots/Geometry/plot_airfoil.py +++ b/trunk/SUAVE/Plots/Geometry/plot_airfoil.py @@ -14,7 +14,7 @@ import import_airfoil_geometry import os -def plot_airfoil(airfoil_paths, line_color = 'k-', overlay = False, save_figure = False, save_filename = "Airfoil_Geometry", file_type = ".png"): +def plot_airfoil(airfoil_paths,line_color = 'k-', save_figure = False, save_filename = "Airfoil_Geometry", file_type = ".png"): """This plots all airfoil defined in the list "airfoil_names" Assumptions: @@ -33,45 +33,15 @@ def plot_airfoil(airfoil_paths, line_color = 'k-', overlay = False, save_figure N/A """ # get airfoil coordinate geometry - airfoil_data = import_airfoil_geometry(airfoil_paths) + airfoil_geometry = import_airfoil_geometry(airfoil_paths) + + fig = plt.figure(save_filename) + fig.set_size_inches(10, 4) + axes = fig.add_subplot(1,1,1) + axes.plot(airfoil_geometry.x_coordinates,airfoil_geometry.y_coordinates , label=save_filename) - if overlay: - name = save_filename - fig = plt.figure(name) - fig.set_size_inches(10, 4) - axes = fig.add_subplot(1,1,1) - for i in range(len(airfoil_paths)): - # extract airfoil name from path - airfoil_name = airfoil_data.airfoil_names[i] - - # separate x and y coordinates - airfoil_x = airfoil_data.x_coordinates[airfoil_name] - airfoil_y = airfoil_data.y_coordinates[airfoil_name] - - # plot airfoil geometry - axes.plot(airfoil_x, airfoil_y , label=airfoil_name ) - - axes.set_title("Airfoil Geometry") - axes.legend(bbox_to_anchor=(1,1), loc='upper left', ncol=1) - if save_figure: - plt.savefig(name + file_type) - - else: - for i in range(len(airfoil_paths)): - # extract airfoil name from path - airfoil_name = airfoil_data.airfoil_names[i] - - # separate x and y coordinates - airfoil_x = airfoil_data.x_coordinates[airfoil_name] - airfoil_y = airfoil_data.y_coordinates[airfoil_name] - - name = save_filename + '_' + str(i) - fig = plt.figure(name) - axes = fig.add_subplot(1,1,1) - axes.set_title(airfoil_name) - axes.plot(airfoil_x, airfoil_y , line_color ) - axes.axis('equal') - if save_figure: - plt.savefig(name + file_type) + axes.set_title(save_filename) + if save_figure: + plt.savefig(save_filename.replace("_", " ") + file_type) return diff --git a/trunk/SUAVE/Plots/Geometry/plot_vehicle.py b/trunk/SUAVE/Plots/Geometry/plot_vehicle.py index 538148900d..e05976293e 100644 --- a/trunk/SUAVE/Plots/Geometry/plot_vehicle.py +++ b/trunk/SUAVE/Plots/Geometry/plot_vehicle.py @@ -1,14 +1,15 @@ ## @ingroup Plots-Geometry # plot_vehicle.py # -# Created: Mar 2020, M. Clarke -# Apr 2020, M. Clarke -# Jul 2020, M. Clarke -# Jul 2021, E. Botero -# Oct 2021, M. Clarke -# Dec 2021, M. Clarke -# Feb 2022, R. Erhard -# Mar 2022, R. Erhard +# Created : Mar 2020, M. Clarke +# Modified: Apr 2020, M. Clarke +# Modified: Jul 2020, M. Clarke +# Modified: Jul 2021, E. Botero +# Modified: Oct 2021, M. Clarke +# Modified: Dec 2021, M. Clarke +# Modified: Feb 2022, R. Erhard +# Modified: Mar 2022, R. Erhard +# Modified: Sep 2022, M. Clarke # ---------------------------------------------------------------------- # Imports @@ -18,8 +19,8 @@ import matplotlib.pyplot as plt from mpl_toolkits.mplot3d.art3d import Poly3DCollection from SUAVE.Methods.Geometry.Two_Dimensional.Cross_Section.Airfoil.import_airfoil_geometry import import_airfoil_geometry -from SUAVE.Methods.Geometry.Two_Dimensional.Cross_Section.Airfoil.compute_naca_4series import compute_naca_4series -from SUAVE.Methods.Aerodynamics.Common.Fidelity_Zero.Lift.generate_vortex_distribution import generate_vortex_distribution +from SUAVE.Methods.Geometry.Two_Dimensional.Cross_Section.Airfoil.compute_naca_4series import compute_naca_4series +from SUAVE.Methods.Aerodynamics.Common.Fidelity_Zero.Lift.generate_vortex_distribution import generate_vortex_distribution from SUAVE.Analyses.Aerodynamics import Vortex_Lattice ## @ingroup Plots-Geometry @@ -109,12 +110,13 @@ def plot_vehicle(vehicle, elevation_angle = 30,azimuthal_angle = 210, axis_limit # ------------------------------------------------------------------------- # PLOT ENGINE # ------------------------------------------------------------------------- - nacelle_face_color = 'darkred' - nacelle_edge_color = 'black' - nacelle_alpha = 1 + nacelle_face_color = 'darkred' + nacelle_edge_color = 'black' + nacelle_alpha = 1 + number_of_airfoil_points = 21 for nacelle in vehicle.nacelles: # Generate Nacelle Geoemtry - nac_geo = generate_nacelle_points(nacelle) + nac_geo = generate_nacelle_points(nacelle,number_of_airfoil_points) # Plot Nacelle Geometry plot_nacelle_geometry(axes,nac_geo,nacelle_face_color,nacelle_edge_color,nacelle_alpha ) @@ -123,11 +125,12 @@ def plot_vehicle(vehicle, elevation_angle = 30,azimuthal_angle = 210, axis_limit # ------------------------------------------------------------------------- # PLOT ENGINE # ------------------------------------------------------------------------- - network_face_color = 'darkred' - network_edge_color = 'red' - network_alpha = 1 + network_face_color = 'darkred' + network_edge_color = 'red' + network_alpha = 1 + number_of_airfoil_points = 21 for network in vehicle.networks: - plot_network(axes,network,network_face_color,network_edge_color,network_alpha ) + plot_network(axes,network,number_of_airfoil_points,network_face_color,network_edge_color,network_alpha ) axes.set_xlim(0,axis_limits*2) axes.set_ylim(-axis_limits,axis_limits) @@ -198,8 +201,7 @@ def plot_propeller_wake(axes, prop,face_color,edge_color,alpha,ctrl_pt=0): Properties Used: N/A """ - wVD = prop.Wake.vortex_distribution.reshaped_wake - num_cpts = len(wVD.XA1[0,:,0,0,0]) + wVD = prop.Wake.vortex_distribution.reshaped_wake num_B = len(wVD.XA1[0,0,:,0,0]) dim_R = len(wVD.XA1[0,0,0,:,0]) nts = len(wVD.XA1[0,0,0,0,:]) @@ -308,7 +310,7 @@ def plot_fuselage_geometry(axes,fus_pts, face_color,edge_color,alpha): return -def plot_network(axes,network,prop_face_color,prop_edge_color,prop_alpha): +def plot_network(axes,network,number_of_airfoil_points,prop_face_color,prop_edge_color,prop_alpha): """ This plots the 3D surface of the network Assumptions: @@ -332,18 +334,18 @@ def plot_network(axes,network,prop_face_color,prop_edge_color,prop_alpha): for prop in network.propellers: # Generate And Plot Propeller/Rotor Geometry - plot_propeller_geometry(axes,prop,0,prop_face_color,prop_edge_color,prop_alpha) + plot_propeller_geometry(axes,prop,0,number_of_airfoil_points,prop_face_color,prop_edge_color,prop_alpha) if ('lift_rotors' in network.keys()): for rotor in network.lift_rotors: # Generate and Plot Propeller/Rotor Geometry - plot_propeller_geometry(axes,rotor,0,prop_face_color,prop_edge_color,prop_alpha) + plot_propeller_geometry(axes,rotor,0,number_of_airfoil_points,prop_face_color,prop_edge_color,prop_alpha) return -def generate_nacelle_points(nac,tessellation = 24): +def generate_nacelle_points(nac,tessellation = 24,number_of_airfoil_points = 21): """ This generates the coordinate points on the surface of the nacelle Assumptions: @@ -352,36 +354,33 @@ def generate_nacelle_points(nac,tessellation = 24): Source: None - Inputs: + Inputs: + nac - Nacelle data structure + tessellation - azimuthal discretization of lofted body + number_of_airfoil_points - discretization of airfoil geometry + Properties Used: N/A """ num_nac_segs = len(nac.Segments.keys()) - theta = np.linspace(0,2*np.pi,tessellation) - n_points = 20 + theta = np.linspace(0,2*np.pi,tessellation) if num_nac_segs == 0: - num_nac_segs = int(n_points/2) + num_nac_segs = int(np.ceil(number_of_airfoil_points/2)) nac_pts = np.zeros((num_nac_segs,tessellation,3)) naf = nac.Airfoil - if naf.naca_4_series_airfoil != None: - # use mean camber surface of airfoil - camber = float(naf.naca_4_series_airfoil[0])/100 - camber_loc = float(naf.naca_4_series_airfoil[1])/10 - thickness = float(naf.naca_4_series_airfoil[2:])/100 - airfoil_data = compute_naca_4series(camber, camber_loc, thickness,(n_points - 2)) - xpts = np.repeat(np.atleast_2d(airfoil_data.x_lower_surface.values()).T,tessellation,axis = 1)*nac.length - zpts = np.repeat(np.atleast_2d(airfoil_data.camber_coordinates[airfoil_data.airfoil_names[0]]).T,tessellation,axis = 1)*nac.length + if naf.NACA_4_series_flag == True: + a_geo = compute_naca_4series(naf.coordinate_file,num_nac_segs) + xpts = np.repeat(np.atleast_2d(a_geo.x_coordinates[0]).T,tessellation,axis = 1)*nac.length + zpts = np.repeat(np.atleast_2d(a_geo.y_coordinates[0]).T,tessellation,axis = 1)*nac.length elif naf.coordinate_file != None: - a_sec = naf.coordinate_file - a_secl = [0] - airfoil_data = import_airfoil_geometry(a_sec,npoints=num_nac_segs) - xpts = np.repeat(np.atleast_2d(np.take(airfoil_data.x_coordinates.values(),a_secl,axis=0)).T,tessellation,axis = 1)*nac.length - zpts = np.repeat(np.atleast_2d(np.take(airfoil_data.y_coordinates.values(),a_secl,axis=0)).T,tessellation,axis = 1)*nac.length + a_geo = import_airfoil_geometry(naf.coordinate_file,num_nac_segs) + xpts = np.repeat(np.atleast_2d(np.take(a_geo.x_coordinates,[0],axis=0)).T,tessellation,axis = 1)*nac.length + zpts = np.repeat(np.atleast_2d(np.take(a_geo.y_coordinates,[0],axis=0)).T,tessellation,axis = 1)*nac.length else: # if no airfoil defined, use super ellipse as default @@ -433,6 +432,13 @@ def plot_nacelle_geometry(axes,NAC_SURF_PTS,face_color,edge_color,alpha): Source: None + + Inputs: + axes - plotting axes + NAC_SURF_PTS - nacelle surface points + face_color - face color of nacelle + edge_color - edge color of nacelle + alpha - transparency factor Properties Used: N/A @@ -463,7 +469,8 @@ def plot_nacelle_geometry(axes,NAC_SURF_PTS,face_color,edge_color,alpha): return -def plot_propeller_geometry(axes,prop,cpt=0,prop_face_color='red',prop_edge_color='darkred',prop_alpha=1): +def plot_propeller_geometry(axes,prop,cpt=0,number_of_airfoil_points = 21, + prop_face_color='red',prop_edge_color='darkred',prop_alpha=1): """ This plots a 3D surface of the propeller Assumptions: @@ -473,20 +480,21 @@ def plot_propeller_geometry(axes,prop,cpt=0,prop_face_color='red',prop_edge_colo None Inputs: - prop - SUAVE propeller for which to plot the geometry - cpt - control point at which to plot the propeller + axes - plotting axes + prop - SUAVE propeller for which to plot the geometry + cpt - control point at which to plot the propeller + number_of_airfoil_points - discretization of airfoil geometry Properties Used: N/A """ - num_B = prop.number_of_blades - n_points = 20 - af_pts = n_points-1 + num_B = prop.number_of_blades + af_pts = number_of_airfoil_points-1 dim = len(prop.radius_distribution) for i in range(num_B): - G = get_blade_coordinates(prop,dim,i) + G = get_blade_coordinates(prop,number_of_airfoil_points,dim,i) # ------------------------------------------------------------------------ # Plot Propeller Blade # ------------------------------------------------------------------------ @@ -512,7 +520,7 @@ def plot_propeller_geometry(axes,prop,cpt=0,prop_face_color='red',prop_edge_colo axes.add_collection3d(prop_collection) return -def get_blade_coordinates(prop,dim,i,aircraftRefFrame=True): +def get_blade_coordinates(prop,n_points,dim,i,aircraftRefFrame=True): """ This generates the coordinates of the blade surface for plotting in the aircraft frame (x-back, z-up) Assumptions: @@ -532,16 +540,16 @@ def get_blade_coordinates(prop,dim,i,aircraftRefFrame=True): N/A """ # unpack - num_B = prop.number_of_blades - airfoil_geometry_data = prop.airfoil_geometry_data - a_secl = prop.airfoil_polar_stations - beta = prop.twist_distribution + prop.inputs.pitch_command - a_o = prop.start_angle - b = prop.chord_distribution - r = prop.radius_distribution - MCA = prop.mid_chord_alignment - t = prop.max_thickness_distribution - origin = prop.origin + num_B = prop.number_of_blades + airfoils = prop.Airfoils + beta = prop.twist_distribution + prop.inputs.pitch_command + a_o = prop.start_angle + b = prop.chord_distribution + r = prop.radius_distribution + MCA = prop.mid_chord_alignment + t = prop.max_thickness_distribution + a_loc = prop.airfoil_polar_stations + origin = prop.origin if prop.rotation==1: # negative chord and twist to give opposite rotation direction @@ -552,37 +560,30 @@ def get_blade_coordinates(prop,dim,i,aircraftRefFrame=True): flip_1 = (np.pi/2) flip_2 = (np.pi/2) - # get airfoil coordinate geometry - if airfoil_geometry_data != None: - aNames = airfoil_geometry_data.airfoil_names - n_points = len(airfoil_geometry_data.x_coordinates[aNames[0]]) - xpts = np.zeros((len(a_secl), n_points)) - zpts = np.zeros((len(a_secl), n_points)) - max_t = np.zeros(len(a_secl)) - - for j in range(len(a_secl)): - xpts[j,:] = airfoil_geometry_data.x_coordinates[aNames[a_secl[j]]] - zpts[j,:] = airfoil_geometry_data.y_coordinates[aNames[a_secl[j]]] - max_t[j] = airfoil_geometry_data.thickness_to_chord[aNames[a_secl[j]]] - - else: - camber = 0.02 - camber_loc = 0.4 - thickness = 0.10 - n_points = 20 - airfoil_geometry_data = compute_naca_4series(camber, camber_loc, thickness,(n_points - 2)) - aNames = airfoil_geometry_data.airfoil_names - xpts = np.repeat(np.atleast_2d(airfoil_geometry_data.x_coordinates[aNames[0]]) ,dim,axis=0) - zpts = np.repeat(np.atleast_2d(airfoil_geometry_data.y_coordinates[aNames[0]]) ,dim,axis=0) - max_t = np.repeat(airfoil_geometry_data.thickness_to_chord[aNames[0]],dim,axis=0) - - # MCA_2d = np.repeat(np.atleast_2d(MCA).T,n_points,axis=1) b_2d = np.repeat(np.atleast_2d(b).T ,n_points,axis=1) t_2d = np.repeat(np.atleast_2d(t).T ,n_points,axis=1) r_2d = np.repeat(np.atleast_2d(r).T ,n_points,axis=1) airfoil_le_offset = np.repeat(b[:,None], n_points, axis=1)/2 - + + # get airfoil coordinate geometry + if len(airfoils.keys())>0: + xpts = np.zeros((dim,n_points)) + zpts = np.zeros((dim,n_points)) + max_t = np.zeros(dim) + for i,airfoil in enumerate(airfoils): + geometry = import_airfoil_geometry(airfoil.coordinate_file,n_points) + locs = np.where(np.array(a_loc) == i ) + xpts[locs] = geometry.x_coordinates + zpts[locs] = geometry.y_coordinates + max_t[locs] = geometry.thickness_to_chord + + else: + airfoil_data = compute_naca_4series('2410',n_points) + xpts = np.repeat(np.atleast_2d(airfoil_data.x_coordinates) ,dim,axis=0) + zpts = np.repeat(np.atleast_2d(airfoil_data.y_coordinates) ,dim,axis=0) + max_t = np.repeat(airfoil_data.thickness_to_chord,dim,axis=0) + # store points of airfoil in similar format as Vortex Points (i.e. in vertices) max_t2d = np.repeat(np.atleast_2d(max_t).T ,n_points,axis=1) diff --git a/trunk/SUAVE/Plots/Performance/Airfoil_Plots.py b/trunk/SUAVE/Plots/Performance/Airfoil_Plots.py index f83ee8d64a..81e261fc2a 100644 --- a/trunk/SUAVE/Plots/Performance/Airfoil_Plots.py +++ b/trunk/SUAVE/Plots/Performance/Airfoil_Plots.py @@ -4,193 +4,117 @@ # Created: Mar 2021, M. Clarke # Modified: Feb 2022, M. Clarke # Aug 2022, R. Erhard +# Sep 2022, M. Clarke # ---------------------------------------------------------------------- # Imports -# ---------------------------------------------------------------------- -from SUAVE.Core import Units -from SUAVE.Methods.Geometry.Two_Dimensional.Cross_Section.Airfoil.import_airfoil_polars import import_airfoil_polars +# ---------------------------------------------------------------------- +from SUAVE.Core.Utilities import interp2d +from SUAVE.Core import Units import numpy as np import matplotlib.pyplot as plt import matplotlib.cm as cm - +import os + ## @ingroup Plots-Performance -def plot_airfoil_analysis_boundary_layer_properties(ap,show_legend = True ): - """ This plots the boundary layer properties of an airfoil - or group of airfoils +def plot_airfoil_boundary_layer_properties(ap,show_legend = False ): + """Plots viscous distributions - Assumptions: - None - - Inputs: - ap - data stucture of airfoil boundary layer properties - - Outputs: - None - - Properties Used: - N/A - """ + Assumptions: + None - # determine dimension of angle of attack and reynolds number - nAoA = len(ap.AoA) - nRe = len(ap.Re) + Source: + None + + Inputs: + ap : data stucture of airfoil boundary layer properties + + Outputs: + Figures of quantity distributions - # create array of colors for difference reynolds numbers - colors = cm.rainbow(np.linspace(0, 1,nAoA)) - markers = ['o','v','s','P','p','^','D','X','*'] + Properties Used: + N/A + """ - fig1 = plt.figure('Airfoil Geometry',figsize=(8,6)) - axis1 = fig1.add_subplot(1,1,1) - axis1.set_xlabel('x') - axis1.set_ylabel('y') - axis1.set_ylim(-0.2, 0.2) + plot_quantity(ap, ap.Ue_Vinf, r'$U_{e}/U_{inv}}$' ,'inviscid edge velocity',show_legend) + plot_quantity(ap, ap.H, r'$H$' ,'kinematic shape parameter',show_legend) + plot_quantity(ap, ap.delta_star, r'$\delta*$' ,'displacement thickness',show_legend) + plot_quantity(ap, ap.delta , r'$\delta$' ,'boundary layer thickness',show_legend) + plot_quantity(ap, ap.theta, r'$\theta$' ,'momentum thickness',show_legend) + plot_quantity(ap, ap.cf, r'$c_f $' , 'skin friction coefficient',show_legend) + plot_quantity(ap, ap.Re_theta, r'$Re_{\theta}$' ,'theta Reynolds number',show_legend) - fig2 = plt.figure('Airfoil Boundary Layer Properties',figsize=(12,8)) - axis2 = fig2.add_subplot(2,3,1) - axis2.set_ylabel('$Ue/V_{inf}$') - - axis3 = fig2.add_subplot(2,3,2) - axis3.set_ylabel('$dV_e/dx$') - axis3.set_ylim(-1, 10) - - axis4 = fig2.add_subplot(2,3,3) - axis4.set_ylabel(r'$\theta$') - - axis5 = fig2.add_subplot(2,3,4) - axis5.set_xlabel('x') - axis5.set_ylabel('$H$') - - axis6 = fig2.add_subplot(2,3,5) - axis6.set_xlabel('x') - axis6.set_ylabel(r'$\delta$*') - - axis7 = fig2.add_subplot(2,3,6) - axis7.set_xlabel('x') - axis7.set_ylabel(r'$\delta$') - - fig3 = plt.figure('Airfoil Cp',figsize=(8,6)) - axis8 = fig3.add_subplot(1,1,1) - axis8.set_ylabel('$C_p$') - axis8.set_ylim(1.2,-7) - - mid = int(len(ap.x)/2) + + fig = plt.figure() + axis = fig.add_subplot(1,1,1) + n_cpts = len(ap.AoA[:,0]) + n_cases = len(ap.AoA[0,:]) + + # create array of colors for difference reynolds numbers + blues = cm.winter(np.linspace(0, 0.75,n_cases)) + reds = cm.autumn(np.linspace(0, 0.75,n_cases)) - for i in range(nAoA): - for j in range(nRe): - - tag = 'AoA: ' + str(round(ap.AoA[i][0]/Units.degrees,2)) + '$\degree$, Re: ' + str(round(ap.Re[j][0]/1000000,2)) + 'E6' - - axis1.plot(ap.x[:,j,i], ap.y[:,j,i],'k-') - axis1.plot(ap.x_bl[:,j,i],ap.y_bl[:,j,i],color = colors[j], linestyle = '-' ,marker = markers[j%9] , label = tag) - - axis2.plot(ap.x[:mid,j,i], abs(ap.Ue_Vinf)[:mid,j,i],color = colors[j], linestyle = '-' ,marker = markers[j%9] , label= tag ) - axis2.plot(ap.x[mid:,j,i], abs(ap.Ue_Vinf)[mid:,j,i],color = colors[j], linestyle = '--' ,marker = markers[j%9]) - - axis3.plot(ap.x[:mid,j,i], abs(ap.dVe)[:mid,j,i],color = colors[j], linestyle = '-' ,marker = markers[j%9] ) - axis3.plot(ap.x[mid:,j,i], abs(ap.dVe)[mid:,j,i],color = colors[j], linestyle = '--' ,marker = markers[j%9]) - - axis4.plot(ap.x[:mid,j,i], ap.theta[:mid,j,i],color = colors[j], linestyle = '-' ,marker = markers[j%9] ) - axis4.plot(ap.x[mid:,j,i], ap.theta[mid:,j,i],color = colors[j], linestyle = '--' ,marker = markers[j%9]) - - axis5.plot(ap.x[:mid,j,i], ap.H[:mid,j,i],color = colors[j], linestyle = '-' ,marker = markers[j%9] ) - axis5.plot(ap.x[mid:,j,i], ap.H[mid:,j,i],color = colors[j], linestyle = '--' ,marker = markers[j%9] ) - - axis6.plot(ap.x[:mid,j,i],ap.delta_star[:mid,j,i],color = colors[j], linestyle = '-' ,marker = markers[j%9] ) - axis6.plot(ap.x[mid:,j,i],ap.delta_star[mid:,j,i],color = colors[j], linestyle = '--' ,marker = markers[j%9]) + for i in range(n_cpts): + for j in range(n_cases): + case_label = 'AoA: ' + str(round(ap.AoA[i,j]/Units.degrees, 2)) + ', Re: ' + str(ap.Re[i,j]) + axis.plot(ap.x[i,j], ap.y[i,j], color = blues[j] , linewidth = 2, label = case_label ) + axis.plot(ap.x_bl[i,j], ap.y_bl[i,j], color = reds[j] , marker = 'o', linewidth = 2, label = case_label ) - axis7.plot(ap.x[:mid,j,i],ap.delta[:mid,j,i],color = colors[j], linestyle = '-' ,marker = markers[j%9] ) - axis7.plot(ap.x[mid:,j,i],ap.delta[mid:,j,i],color = colors[j], linestyle = '--' ,marker = markers[j%9]) - - axis8.plot(ap.x[:mid,j,i], ap.Cp[:mid,j,i] ,color = colors[j], linestyle = '-' ,marker = markers[j%9] , label= tag) - axis8.plot(ap.x[mid:,j,i], ap.Cp[ mid:,j,i],color = colors[j], linestyle = '--' ,marker = markers[j%9]) - - - # add legends for plotting - plt.tight_layout() + axis.set_title('Airfoil with Boundary Layers') + axis.set_ylabel(r'$y$') + axis.set_xlabel(r'$x$') if show_legend: - lines1, labels1 = fig2.axes[0].get_legend_handles_labels() - fig2.legend(lines1, labels1, loc='upper center', ncol=5) - plt.tight_layout() - axis8.legend(loc='upper right') - return + axis.legend(loc='upper left', ncol=1) + + + return ## @ingroup Plots-Performance -def plot_airfoil_analysis_polars(ap,show_legend = True): - """ This plots the polars of an airfoil or group of airfoils - - Assumptions: - None - - Inputs: - ap - data stucture of airfoil boundary layer properties and polars - - Outputs: - None - - Properties Used: - N/A - """ - - # determine dimension of angle of attack and reynolds number - nAoA = len(ap.AoA) - nRe = len(ap.Re) - - # create array of colors for difference reynolds numbers - colors = cm.rainbow(np.linspace(0, 1,nAoA)) - markers = ['o','v','s','P','p','^','D','X','*'] - - fig1 = plt.figure('Airfoil Geometry',figsize=(8,6)) - axis1 = fig1.add_subplot(1,1,1) - axis1.set_xlabel('x') - axis1.set_ylabel('y') - axis1.set_ylim(-0.2, 0.2) - +def plot_quantity(ap, q, qaxis, qname,show_legend): + """Plots a quantity q over lower/upper/wake surfaces - fig4 = plt.figure('Airfoil Polars',figsize=(12,5)) - axis12 = fig4.add_subplot(1,3,1) - axis12.set_title('Lift Coefficients') - axis12.set_xlabel('AoA') - axis12.set_ylabel(r'$C_l$') - axis12.set_ylim(-1,2) + Assumptions: + None - axis13 = fig4.add_subplot(1,3,2) - axis13.set_title('Drag Coefficient') - axis13.set_xlabel('AoA') - axis13.set_ylabel(r'$C_d$') - axis13.set_ylim(0,0.1) + Source: + None + + Inputs: + ap : data stucture of airfoil boundary layer properties + q : vector of values to plot, on all points (wake too if present) + qaxis : name of quantity, for axis labeling + qname : name of quantity, for title labeling + + Outputs: + Figure showing q versus x - axis14 = fig4.add_subplot(1,3,3) - axis14.set_title('Moment Coefficient') - axis14.set_xlabel('AoA') - axis14.set_ylabel(r'$C_m$') - axis14.set_ylim(-0.1,0.1) - - for i in range(nRe): - - Re_tag = 'Re: ' + str(round(ap.Re[i][0]/1000000,2)) + 'E6' - - # Lift Coefficient - axis12.plot(ap.AoA[:,0]/Units.degrees,ap.Cl[:,i],color = colors[i], linestyle = '-' ,marker = markers[i], label= Re_tag ) - - # Drag Coefficient - axis13.plot(ap.AoA[:,0]/Units.degrees,ap.Cd[:,i],color = colors[i], linestyle = '-',marker = markers[i], label = Re_tag) - - # Moment Coefficient - axis14.plot(ap.AoA[:,0]/Units.degrees, ap.Cm[:,i],color = colors[i], linestyle = '-',marker = markers[i], label = Re_tag) - plt.tight_layout() + Properties Used: + N/A + """ + + fig = plt.figure() + axis = fig.add_subplot(1,1,1) + n_cpts = len(ap.AoA[:,0]) + n_cases = len(ap.AoA[0,:]) + + # create array of colors for difference reynolds numbers + blues = cm.winter(np.linspace(0, 0.75,n_cases)) - # add legends for plotting + for i in range(n_cpts): + for j in range(n_cases): + case_label = 'AoA: ' + str(round(ap.AoA[i,j]/Units.degrees, 2)) + ', Re: ' + str(ap.Re[i,j]) + axis.plot(ap.x[i,j], q[i,j], color = blues[j] , marker = 'o', linewidth = 2, label = case_label ) + + axis.set_title(qname) + axis.set_ylabel(qaxis) + axis.set_xlabel(r'$x$') if show_legend: - axis12.legend(loc='upper left') - axis13.legend(loc='upper left') - axis14.legend(loc='upper left') - - return + axis.legend(loc='upper left', ncol=1) + return ## @ingroup Plots-Performance -def plot_airfoil_analysis_surface_forces(ap,show_legend= True,arrow_color = 'r'): +def plot_airfoil_surface_forces(ap,show_legend= True,arrow_color = 'r'): """ This plots the forces on an airfoil surface Assumptions: @@ -207,21 +131,21 @@ def plot_airfoil_analysis_surface_forces(ap,show_legend= True,arrow_color = 'r') """ # determine dimension of angle of attack and reynolds number - nAoA = len(ap.AoA) - nRe = len(ap.Re) - n_cpts = len(ap.x[0,0,:]) + n_cpts = len(ap.AoA[:,0]) + n_cases = len(ap.AoA[0,:]) + n_pts = len(ap.x[0,0,:])-1 - for i in range(nAoA): - for j in range(nRe): - label = '_AoA_' + str(round(ap.AoA[i][0]/Units.degrees,2)) + '_deg_Re_' + str(round(ap.Re[j][0]/1000000,2)) + 'E6' + for i in range(n_cpts): + for j in range(n_cases): + label = '_AoA_' + str(round(ap.AoA[i,j]/Units.degrees,2)) + '_deg_Re_' + str(round(ap.Re[i,j]/1000000,2)) + 'E6' fig = plt.figure('Airfoil_Pressure_Normals' + label ) axis = fig.add_subplot(1,1,1) axis.plot(ap.x[0,0,:], ap.y[0,0,:],'k-') - for k in range(n_cpts): - dx_val = ap.normals[i,j,k,0]*abs(ap.Cp[i,j,k])*0.1 - dy_val = ap.normals[i,j,k,1]*abs(ap.Cp[i,j,k])*0.1 - if ap.Cp[i,j,k] < 0: + for k in range(n_pts): + dx_val = ap.normals[i,j,k,0]*abs(ap.cp[i,j,k])*0.1 + dy_val = ap.normals[i,j,k,1]*abs(ap.cp[i,j,k])*0.1 + if ap.cp[i,j,k] < 0: plt.arrow(x= ap.x[i,j,k], y=ap.y[i,j,k] , dx= dx_val , dy = dy_val , fc=arrow_color, ec=arrow_color,head_width=0.005, head_length=0.01 ) else: @@ -230,11 +154,8 @@ def plot_airfoil_analysis_surface_forces(ap,show_legend= True,arrow_color = 'r') return - - ## @ingroup Plots-Performance -def plot_airfoil_polars(airfoil_polar_data, aoa_sweep, Re_sweep, display_plot = False, - save_figure = False, save_filename = "Airfoil_Polars", file_type = ".png"): +def plot_airfoil_polar_files(polar_data, line_color = 'k-', save_figure = False, save_filename = "Airfoil_Polars", file_type = ".png"): """This plots all airfoil polars in the list "airfoil_polar_paths" Assumptions: @@ -244,135 +165,104 @@ def plot_airfoil_polars(airfoil_polar_data, aoa_sweep, Re_sweep, display_plot = None Inputs: - airfoil_data - airfoil geometry and polar data (see outputs of compute_airfoil_polars.py) - aoa_sweep - angles over which to plot the polars [rad] - Re_sweep - Reynolds numbers over which to plot the polars [-] - + airfoil_polar_paths [list of strings] + Outputs: Plots Properties Used: N/A - """ - # Extract surrogates from airfoil data - airfoil_names = airfoil_polar_data.airfoil_names - airfoil_cl_surs = airfoil_polar_data.lift_coefficient_surrogates - airfoil_cd_surs = airfoil_polar_data.drag_coefficient_surrogates - - #---------------------------------------------------------------------------- - # plot airfoil polar surrogates - #---------------------------------------------------------------------------- - - col_raw = ['black','firebrick', 'darkorange', 'gold','forestgreen','teal','deepskyblue', 'blue', - 'blueviolet', 'fuchsia', 'deeppink', 'gray'] - for jj in range(len(airfoil_names)): - fig, ((ax,ax2),(ax3,ax4)) = plt.subplots(2,2) - fig.set_figheight(8) - fig.set_figwidth(12) - for ii in range(len(Re_sweep)): - cl_sur = airfoil_cl_surs[airfoil_names[jj]]( - (Re_sweep[ii], aoa_sweep) - ) - cd_sur = airfoil_cd_surs[airfoil_names[jj]]( - (Re_sweep[ii], aoa_sweep) - ) - ax.plot(aoa_sweep / Units.deg, cl_sur, col_raw[ii], label="Re="+str(Re_sweep[ii])) - ax.set_xlabel("Alpha (deg)") - ax.set_ylabel("Cl") - ax.legend() - ax.grid() - - ax2.plot(aoa_sweep / Units.deg, cd_sur, col_raw[ii]) - ax2.set_xlabel("Alpha (deg)") - ax2.set_ylabel("Cd") - ax2.set_title(airfoil_names[jj]) - ax2.grid() - - ax3.plot(cd_sur, cl_sur, col_raw[ii]) - ax3.set_xlabel("Cd") - ax3.set_ylabel("Cl") - ax3.grid() - - ax4.plot(aoa_sweep / Units.deg, cd_sur / cl_sur, col_raw[ii]) - ax4.set_xlabel("Alpha (deg)") - ax4.set_ylabel("Cd/Cl") - ax4.grid() + """ + + # Get raw data polars + CL = polar_data.lift_coefficients + CD = polar_data.drag_coefficients + alpha = polar_data.angle_of_attacks + Re_raw = polar_data.reynolds_numbers + n_Re = len(polar_data.re_from_polar) + cols = cm.winter(np.linspace(0, 0.75,n_Re)) - plt.tight_layout() - - if save_figure: - plt.savefig(save_filename + '_' + str(jj) + file_type) - if display_plot: - plt.show() + # plot all Reynolds number polars for ith airfoil + fig = plt.figure(save_filename, figsize=(8,2*n_Re)) + + for j in range(n_Re): + ax1 = fig.add_subplot(n_Re,2,1+2*j) + ax2 = fig.add_subplot(n_Re,2,2+2*j) + Re_val = str(round(Re_raw[j])/1e6)+'e6' + + ax1.plot(alpha/Units.degrees, CL[j,:], color= cols[j], linestyle = '--', label='Re='+Re_val) + ax2.plot(alpha/Units.degrees, CD[j,:], color= cols[j], linestyle = '--', label='Re='+Re_val) + + ax1.set_ylabel('$C_l$') + ax2.set_ylabel('$C_d$') + ax1.legend(loc='best') + + ax1.set_xlabel('AoA [deg]') + ax2.set_xlabel('AoA [deg]') + fig.tight_layout() + + if save_figure: + plt.savefig(save_filename.replace("_", " ") + file_type) return - ## @ingroup Plots-Performance -def plot_raw_data_airfoil_polars(airfoil_names, airfoil_polars_path, display_plot = False, - save_figure = False, save_filename = "Airfoil_Polars", file_type = ".png"): - """This plots all airfoil polars in the list "airfoil_polar_paths" - +def plot_airfoil_polars(polar_data, save_figure = False,save_filename = "Airfoil_Polars", file_type = ".png"): + """This plots all airfoil polars stored in the polar_data data_structure after AERODAS and smoothing corrections. + Assumptions: None - Source: None - Inputs: - airfoil_data - airfoil geometry and polar data (see outputs of compute_airfoil_polars.py) + polar_data - airfoil polar data aoa_sweep - angles over which to plot the polars [rad] Re_sweep - Reynolds numbers over which to plot the polars [-] Outputs: Plots - Properties Used: N/A - """ - airfoil_data = import_airfoil_polars(airfoil_polars_path, airfoil_names) - + """ #---------------------------------------------------------------------------- # plot airfoil polar surrogates - #---------------------------------------------------------------------------- - col_raw = ['black','firebrick', 'darkorange', 'gold','forestgreen','teal','deepskyblue', 'blue', - 'blueviolet', 'fuchsia', 'deeppink', 'gray'] - for jj in range(len(airfoil_names)): - Re_sweep = airfoil_data.reynolds_number[airfoil_names[jj]] - aoa_sweep = airfoil_data.angle_of_attacks[airfoil_names[jj]] - CL = airfoil_data.lift_coefficients[airfoil_names[jj]] - CD = airfoil_data.drag_coefficients[airfoil_names[jj]] + #---------------------------------------------------------------------------- + num_polars = len(polar_data.reynolds_numbers) + Re_sweep = polar_data.reynolds_numbers + aoa_sweep = polar_data.angle_of_attacks + CL = polar_data.lift_coefficients + CD = polar_data.drag_coefficients + + fig, ((ax,ax2),(ax3,ax4)) = plt.subplots(2,2) + fig.set_figheight(8) + fig.set_figwidth(12) + + fig.suptitle(save_filename + " (Raw Polar Data)") + col_raw = cm.jet(np.linspace(0, 1.0,num_polars)) + for ii in range(len(Re_sweep[:num_polars])): - fig, ((ax,ax2),(ax3,ax4)) = plt.subplots(2,2) - fig.set_figheight(8) - fig.set_figwidth(12) - for ii in range(len(Re_sweep)): - - ax.plot(np.array(aoa_sweep[ii]), np.array(CL[ii]), col_raw[ii], label='Re='+str(Re_sweep[ii])) - ax.set_xlabel("Alpha (deg)") - ax.set_ylabel("Cl") - ax.legend() - ax.grid() - - ax2.plot(np.array(aoa_sweep[ii]), np.array(CD[ii]), col_raw[ii]) - ax2.set_xlabel("Alpha (deg)") - ax2.set_ylabel("Cd") - ax2.set_title(airfoil_names[jj] + " (Raw Polar Data)") - ax2.grid() - - ax3.plot(np.array(CD[ii]), np.array(CL[ii]), col_raw[ii]) - ax3.set_xlabel("Cd") - ax3.set_ylabel("Cl") - ax3.grid() + ax.plot(aoa_sweep,CL[ii], color = col_raw[ii], label='Re='+str(Re_sweep[ii])) + ax.set_xlabel("Alpha (deg)") + ax.set_ylabel("Cl") + ax.legend() + ax.grid() + + ax2.plot(aoa_sweep, CD[ii], color = col_raw[ii]) + ax2.set_xlabel("Alpha (deg)") + ax2.set_ylabel("Cd") + ax2.grid() + + ax3.plot(CD[ii], CL[ii], color = col_raw[ii]) + ax3.set_xlabel("Cd") + ax3.set_ylabel("Cl") + ax3.grid() - ax4.plot(np.array(aoa_sweep[ii]), np.array(CD[ii]) / np.array(CL[ii]), col_raw[ii]) - ax4.set_xlabel("Alpha (deg)") - ax4.set_ylabel("Cd/Cl") - ax4.grid() - - plt.tight_layout() + ax4.plot(aoa_sweep, CD[ii]/CL[ii], color = col_raw[ii]) + ax4.set_xlabel("Alpha (deg)") + ax4.set_ylabel("Cd/Cl") + ax4.grid() + + plt.tight_layout() - if save_figure: - plt.savefig(save_filename +'_' + str(jj) + file_type) - if display_plot: - plt.show() + if save_figure: + plt.savefig(save_filename +'_' + file_type) return \ No newline at end of file diff --git a/trunk/SUAVE/Plots/Performance/Mission_Plots.py b/trunk/SUAVE/Plots/Performance/Mission_Plots.py index befdd17899..ba0691820c 100644 --- a/trunk/SUAVE/Plots/Performance/Mission_Plots.py +++ b/trunk/SUAVE/Plots/Performance/Mission_Plots.py @@ -2172,7 +2172,6 @@ def plot_flight_profile_noise_contours(results, line_color = 'bo-', save_figure title_x = 0.5, width = 750, height = 750, - font_family = "Times New Roman", font_size=18, scene_zaxis_range=[min_alt,max_alt], coloraxis=dict(colorscale='Jet', diff --git a/trunk/SUAVE/Plots/Performance/__init__.py b/trunk/SUAVE/Plots/Performance/__init__.py index ee8003b950..c1a37e96b1 100644 --- a/trunk/SUAVE/Plots/Performance/__init__.py +++ b/trunk/SUAVE/Plots/Performance/__init__.py @@ -25,10 +25,8 @@ from .Mission_Plots import plot_flight_profile_noise_contours from .Mission_Plots import plot_fuel_use -from .Airfoil_Plots import plot_airfoil_analysis_boundary_layer_properties -from .Airfoil_Plots import plot_airfoil_analysis_polars -from .Airfoil_Plots import plot_airfoil_analysis_surface_forces -from .Airfoil_Plots import plot_airfoil_polars +from .Airfoil_Plots import plot_airfoil_boundary_layer_properties +from .Airfoil_Plots import plot_airfoil_surface_forces from .Propeller_Plots import plot_propeller_disc_performance from .Propeller_Plots import plot_propeller_disc_inflow \ No newline at end of file