def shevell(weight_landing, number_of_engines, thrust_sea_level, thrust_landing): #process baseline_noise = 101. thrust_percentage = (thrust_sea_level/ Units.force_pound)/25000 * 100. thrust_reduction = thrust_landing/thrust_sea_level * 100. noise_increase_due_to_thrust = - 0.0002193 * thrust_percentage ** 2. + 0.09454 * thrust_percentage - 7.30116 noise_landing = - 0.0015766 * thrust_reduction ** 2. + 0.34882 * thrust_reduction -19.2569 takeoff_distance_noise = -4. # 1500 ft altitude at 6500m from start of take-off sideline_distance_noise = -6.5 # 1476 ft (450m) from centerline (effective distance = 1476*1.25 = 1845ft) landing_distance_noise = 9.1 # 370 ft altitude at 6562 ft (2000m) from runway takeoff = 10. * np.log10(10. ** (baseline_noise/10.) * number_of_engines) - 4. \ + takeoff_distance_noise + noise_increase_due_to_thrust side_line = 10. * np.log10(10. ** (baseline_noise/10.) * number_of_engines) - 4. \ + sideline_distance_noise + noise_increase_due_to_thrust landing = 10. * np.log10(10. ** (baseline_noise/10.) * number_of_engines) - 5. \ + landing_distance_noise + noise_increase_due_to_thrust + noise_landing airframe = 40. + 10. * np.log10(weight_landing / Units.lbs) output = Data() output.takeoff = takeoff output.side_line = side_line output.landing = 10. * np.log10(10. ** (airframe/10.) + 10. ** (landing/10.)) return output
class Aircraft(Data): def __defaults__(self): self.tag = 'aircraft' self.wings = Data() self.bodies = Data() def append_wing(self,wing): # assert database type if not isinstance(wing,Wing): raise Component_Exception, 'input component must be of type AVL.Data.Wing()' # store data self.wings.append(wing) return def append_body(self,body): # assert database type if not isinstance(body,Body): raise Component_Exception, 'input component must be of type AVL.Data.Body()' # store data self.bodies.append(body) return
def evaluate_field_length(vehicle): # --------------------------- # Check field performance # --------------------------- # define takeoff and landing configuration #print ' Defining takeoff and landing configurations' takeoff_config,landing_config = define_field_configs(vehicle) # define airport to be evaluated airport = SUAVE.Attributes.Airports.Airport() airport.altitude = 0.0 * Units.ft airport.delta_isa = 0.0 airport.atmosphere = SUAVE.Attributes.Atmospheres.Earth.US_Standard_1976() # evaluate takeoff / landing #print ' Estimating takeoff performance' TOFL = estimate_take_off_field_length(vehicle,takeoff_config,airport) #print ' Estimating landing performance' LFL = estimate_landing_field_length(vehicle,landing_config,airport) fldlength = Data() fldlength.TOFL = TOFL fldlength.LFL = LFL return fldlength
def compute_forces(self,conditions): # unpack q = conditions.freestream.dynamic_pressure Sref = self.geometry.reference_area # CL = conditions.aerodynamics.lift_coefficient CD = conditions.aerodynamics.drag_coefficient N = q.shape[0] L = np.zeros([N,3]) D = np.zeros([N,3]) L[:,2] = ( -CL * q * Sref )[:,0] D[:,0] = ( -CD * q * Sref )[:,0] results = Data() results.lift_force_vector = L results.drag_force_vector = D return results
def append_control_deflection(self,control_tag,deflection): """ Adds a control deflection case Assumptions: None Source: None Inputs: None Outputs: None Properties Used: N/A """ control_deflection = Data() control_deflection.tag = control_tag control_deflection.deflection = deflection if self.stability_and_control.control_deflections is None: self.stability_and_control.control_deflections = Data() self.stability_and_control.control_deflections.append(control_deflection) return
def do_this(input1,input2=None): """ SUAVE.Attributes.Attribute.do_this(input1,input2=None) conditions data for some useful purpose Inputs: input1 - description [units] input2 - description [units] Outpus: output1 - description output2 - description >> try to minimize outputs >> pack up outputs into Data() if needed Assumptions: if needed """ # unpack inputs var1 = input1.var1 var2 = inputs.var2 # setup var3 = var1 * var2 # process magic = np.log(var3) # packup outputs output = Data() output.magic = magic output.var3 = var3 return output
def fuel_for_missions(interface): # unpack data config = interface.configs.cruise analyses = interface.analyses mission = interface.analyses.missions.fuel.mission mission_payload = interface.analyses.missions.fuel.payload # determine maximum range based in tow short_field from SUAVE.Methods.Performance import size_mission_range_given_weights # unpack cruise_segment_tag = 'cruise' weight_max = config.mass_properties.max_takeoff weight_min = config.mass_properties.operating_empty + 0.10 * mission_payload # 10% takeoff_weight_vec = np.linspace(weight_min,weight_max,3) distance_vec = np.zeros_like(takeoff_weight_vec) fuel_vec = np.zeros_like(takeoff_weight_vec) # call function distance_vec,fuel_vec = size_mission_range_given_weights(config,mission,cruise_segment_tag,mission_payload,takeoff_weight_vec) # pack results = Data() results.tag = 'missions_fuel' results.weights = takeoff_weight_vec results.distances = distance_vec results.fuels = fuel_vec ## print results return results
def main(): # setup the interface interface = setup_interface() # quick test inputs = Data() inputs.projected_span = 36. inputs.fuselage_length = 58. # evalute! results = interface.evaluate(inputs) """ VEHICLE EVALUATION 1 INPUTS <data object 'SUAVE.Core.Data'> projected_span : 36.0 fuselage_length : 58.0 RESULTS <data object 'SUAVE.Core.Data'> fuel_burn : 15700.3830236 weight_empty : 62746.4 """ return
def short_field(interface): # unpack data results_field = interface.results.takeoff_field_length results_fuel = interface.results.fuel_for_missions available_tofl = interface.analyses.missions.short_field.mission.airport.available_tofl tofl_vec = results_field.takeoff_field_length weight_vec_tofl = results_field.takeoff_weights range_vec = results_fuel.distances weight_vec_fuel = results_fuel.weights fuel_vec = results_fuel.fuels # evaluate maximum allowable takeoff weight from a given airfield tow_short_field = np.interp(available_tofl,tofl_vec,weight_vec_tofl) # determine maximum range/fuel based in tow short_field range_short_field = np.interp(tow_short_field,weight_vec_fuel,range_vec) fuel_short_field = np.interp(tow_short_field,weight_vec_fuel,fuel_vec) # pack results = Data() results.tag = 'short_field' results.takeoff_weight = tow_short_field results.range = range_short_field results.fuel = fuel_short_field return results
def main(): # Setup and pack inputs, test several cases conditions = Data() conditions.frames = Data() conditions.frames.body = Data() conditions.frames.planet = Data() conditions.frames.inertial = Data() conditions.freestream = Data() conditions.frames.body.inertial_rotations = np.zeros((4,3)) conditions.frames.planet.start_time = time.strptime("Thu, Mar 20 12:00:00 2014", "%a, %b %d %H:%M:%S %Y",) conditions.frames.planet.latitude = np.array([[0.0],[35],[70],[0.0]]) conditions.frames.planet.longitude = np.array([[0.0],[0.0],[0.0],[0.0]]) conditions.frames.body.inertial_rotations[:,0] = np.array([0.0,np.pi/10,np.pi/5,0.0]) # Phi conditions.frames.body.inertial_rotations[:,1] = np.array([0.0,np.pi/10,np.pi/5,0.0]) # Theta conditions.frames.body.inertial_rotations[:,2] = np.array([0.0,np.pi/2,np.pi,0.0]) # Psi conditions.freestream.altitude = np.array([[600000.0],[0.0],[60000],[1000]]) conditions.frames.inertial.time = np.array([[0.0],[0.0],[0.0],[43200]]) # Call solar radiation rad = SUAVE.Components.Energy.Processes.Solar_Radiation() fluxes = rad.solar_radiation(conditions) print('Solar Fluxes') print fluxes truth_fluxes = [[ 1365.96369614],[ 853.74651524],[ 820.78323974],[ 0. ]] max_error = np.max(np.abs(fluxes-truth_fluxes)) assert( max_error < 1e-5 ) return
def __defaults__(self): """This sets the default for wing segments in SUAVE. Assumptions: None Source: N/A Inputs: None Outputs: None Properties Used: N/A """ self.tag = 'segment' self.percent_span_location = 0.0 self.twist = 0.0 self.root_chord_percent = 0.0 self.dihedral_outboard = 0.0 self.sweeps = Data() self.sweeps.quarter_chord = 0.0 self.sweeps.leading_edge = 0.0 self.Airfoil = Data() self.control_surfaces = Data()
def sample_training(self): # unpack geometry = self.geometry settings = self.settings training = self.training AoA = training.angle_of_attack CL = np.zeros_like(AoA) # condition input, local, do not keep konditions = Data() konditions.aerodynamics = Data() # calculate aerodynamics for table for i,_ in enumerate(AoA): # overriding conditions, thus the name mangling konditions.aerodynamics.angle_of_attack = AoA[i] # these functions are inherited from Aerodynamics() or overridden CL[i] = calculate_lift_vortex_lattice(konditions, settings, geometry) # store training data training.lift_coefficient = CL return
def main(): # ------------------------------------------------------------------ # Testing # Using NACA 2410 # ------------------------------------------------------------------ camber = 0.02 camber_loc = 0.4 thickness = 0.10 npoints = 10 upper,lower = compute_naca_4series(camber, camber_loc, thickness,npoints) truth_upper = ([[ 0. , 0. ], [ 0.08654358, 0.04528598], [ 0.25116155, 0.06683575], [ 0.46508794, 0.06561866], [ 0.71656695, 0.04370913], [ 1. , 0. ]]) truth_lower = ([[ 0. , 0. ], [ 0.09234186, -0.02939744], [ 0.25480288, -0.03223931], [ 0.46442806, -0.02608462], [ 0.71451656, -0.01477209], [ 1. , 0. ]]) # Compute Errors error = Data() error.upper = np.abs(upper-truth_upper) error.lower = np.abs(lower-truth_lower) for k,v in list(error.items()): assert np.any(np.abs(v)<1e-6)
class Wing(Data): def __defaults__(self): self.tag = 'wing' self.symmetric = True self.vertical = False self.origin = [0.,0.,0.] self.sweep = 0.0 self.dihedral = 0.0 self.sections = Data() self.configuration = Data() self.control_surfaces = Data() self.configuration.nspanwise = 10 self.configuration.nchordwise = 5 self.configuration.sspace = 1.0 self.configuration.cspace = 1.0 def append_section(self,section): """ adds a segment to the wing """ # assert database type if not isinstance(section,Data): raise Component_Exception, 'input component must be of type Data()' # store data self.sections.append(section) return
def simple_method(input1,input2=None): """ SUAVE.Methods.SimpleMethod(input1,input2=None) does something useful Inputs: input1 - description [units] input2 - description [units] Outputs: output1 - description output2 - description >> try to minimize outputs >> pack up outputs into Data() if needed Assumptions: if needed """ # unpack inputs var1 = input1.var1 var2 = inputs.var2 # setup var3 = var1 * var2 # process magic = np.log(var3) # packup outputs output = Data() output.magic = magic output.var3 = var3 return output
def estimate_hourly_rates(year): """Estimates the hourly rate according to a trend line. Assumptions: None Source: Trends in hourly rates according to "Fundamentals of Aircraft Design", vol 1, Nicolai Figure 24.4. Inputs: year [-] Outputs: hourly_rates. engineering [$/hr] tooling [$/hr] manufacturing [$/hr] quality_control [$/hr] Properties Used: N/A """ # Unpack reference_year = year hourly_rates = Data() hourly_rates.engineering = 2.576 * reference_year - 5058. hourly_rates.tooling = 2.883 * reference_year - 5666. hourly_rates.manufacturing = 2.316 * reference_year - 4552. hourly_rates.quality_control = 2.600 * reference_year - 5112. return hourly_rates
def read_results(avl_object): results = Data() for case in avl_object.current_status.cases: num_ctrl = case.stability_and_control.control_deflections.size with open(case.result_filename,'r') as res_file: case_res = Results() case_res.tag = case.tag lines = res_file.readlines() case_res.aerodynamics.roll_moment_coefficient = float(lines[19][32:42].strip()) case_res.aerodynamics.pitch_moment_coefficient = float(lines[20][32:42].strip()) case_res.aerodynamics.yaw_moment_coefficient = float(lines[21][32:42].strip()) case_res.aerodynamics.total_lift_coefficient = float(lines[23][10:20].strip()) #case_res.aerodynamics.total_drag_coefficient = float(lines[24][10:20].strip()) case_res.aerodynamics.induced_drag_coefficient = float(lines[25][32:42].strip()) case_res.aerodynamics.span_efficiency_factor = float(lines[27][32:42].strip()) case_res.stability.alpha_derivatives.lift_curve_slope = float(lines[36+num_ctrl][25:35].strip()) case_res.stability.alpha_derivatives.side_force_derivative = float(lines[37+num_ctrl][25:35].strip()) case_res.stability.alpha_derivatives.roll_moment_derivative = float(lines[38+num_ctrl][25:35].strip()) case_res.stability.alpha_derivatives.pitch_moment_derivative = float(lines[39+num_ctrl][25:35].strip()) case_res.stability.alpha_derivatives.yaw_moment_derivative = float(lines[40+num_ctrl][25:35].strip()) case_res.stability.beta_derivatives.lift_coefficient_derivative = float(lines[36+num_ctrl][44:55].strip()) case_res.stability.beta_derivatives.side_force_derivative = float(lines[37+num_ctrl][44:55].strip()) case_res.stability.beta_derivatives.roll_moment_derivative = float(lines[38+num_ctrl][44:55].strip()) case_res.stability.beta_derivatives.pitch_moment_derivative = float(lines[39+num_ctrl][44:55].strip()) case_res.stability.beta_derivatives.yaw_moment_derivative = float(lines[40+num_ctrl][44:55].strip()) case_res.stability.neutral_point = float(lines[50+13*(num_ctrl>0)][22:33].strip()) results.append(case_res) return results
def main(): #only do calculation for 747 from Boeing_747 import vehicle_setup, configs_setup vehicle = vehicle_setup() configs = configs_setup(vehicle) Mach = np.array([0.198]) segment = SUAVE.Analyses.Mission.Segments.Segment() segment.freestream = Data() segment.freestream.mach_number = Mach segment.atmosphere = SUAVE.Analyses.Atmospheric.US_Standard_1976() altitude = 0.0 * Units.feet conditions = segment.atmosphere.compute_values(altitude / Units.km) segment.a = conditions.speed_of_sound segment.freestream.density = conditions.density segment.freestream.dynamic_viscosity = conditions.dynamic_viscosity segment.freestream.velocity = segment.freestream.mach_number * segment.a #Method Test cn_b = taw_cnbeta(vehicle,segment,configs.base) expected = 0.09427599 # Should be 0.184 error = Data() error.cn_b_747 = (cn_b-expected)/expected print(error) for k,v in list(error.items()): assert(np.abs(v)<1e-6) return
def evaluate(self,state,settings=None,geometry=None): # unpack aoa = state.conditions.aerodynamics.angle_of_attack Sref = self.geometry.reference_area # evaluate surrogates CL = self.surrogates.lift_coefficient(aoa) CDi = self.surrogates.induced_drag_coefficient(aoa) Cm = self.surrogates.pitch_moment_coefficient(aoa) # pack conditions state.conditions.aerodynamics.lift_coefficient = CL state.conditions.aerodynamics.drag_coefficient = CDi state.conditions.aerodynamics.pitch_moment_coefficient = Cm # pack results results = Data() results.lift_coefficient = CL results.drag_coefficient = CDi results.induced_drag_coefficient = CDi results.pitch_moment_coefficient = Cm #results.update( self.compute_forces(state.conditions) ) return results
def Empty(vehicle): """ Structural Weight correlation from all 415 samples of fixed-wing UAVs and sailplanes Equation 3.16 from 'Design of Solar Powered Airplanes for Continuous Flight' by Andre Noth Relatively valid for a wide variety of vehicles, may be optimistic Assumes a 'main wing' is attached """ # Unpack S = vehicle.reference_area AR = vehicle.wings['main_wing'].aspect_ratio Earth = SUAVE.Attributes.Planets.Earth() g = Earth.sea_level_gravity # Airframe weight Waf = (5.58*(S**1.59)*(AR**0.71))/g # All Samples #Waf = (0.44*(S**1.55)*(AR**1.30))/g # Top 19 # Pack weight = Data() weight.empty = Waf return weight
def evaluate_range_from_short_field(vehicle,mission,results): # unpack airport_short_field = mission.airport_short_field tofl = airport_short_field.field_lenght takeoff_config = vehicle.configs.takeoff from SUAVE.Methods.Performance import find_takeoff_weight_given_tofl # evaluate maximum allowable takeoff weight from a short field tow_short_field = find_takeoff_weight_given_tofl(vehicle,takeoff_config,airport_short_field,tofl) # determine maximum range based in tow short_field from SUAVE.Methods.Performance import size_mission_range_given_weights # unpack cruise_segment_tag = 'Cruise' mission_payload = vehicle.mass_properties.payload # call function distance,fuel = size_mission_range_given_weights(vehicle,mission,cruise_segment_tag,mission_payload,tow_short_field) # pack short_field = Data() short_field.tag = 'short_field' short_field.takeoff_weight = tow_short_field short_field.range = distance short_field.fuel = fuel results.short_field = short_field return results
def compute_ducted_fan_geometry(ducted_fan, conditions): """ SUAVE.Methods.Geometry.Two_Dimensional.Planform.wing_fuel_volume(wing): Estimates wing fuel capacity based in correlation methods. """ # unpack thrust = ducted_fan.thrust fan_nozzle = ducted_fan.fan_nozzle mass_flow = thrust.mass_flow_rate_design #evaluate engine at these conditions state=Data() state.conditions=conditions state.numerics= Data() ducted_fan.evaluate_thrust(state) #determine geometry U0 = conditions.freestream.velocity rho0 = conditions.freestream.density Ue = fan_nozzle.outputs.velocity rhoe = fan_nozzle.outputs.density Ae = mass_flow[0][0]/(rhoe[0][0]*Ue[0][0]) #ducted fan nozzle exit area A0 = (mass_flow/(rho0*U0))[0][0] ducted_fan.areas.maximum = 1.2*Ae/fan_nozzle.outputs.area_ratio[0][0] ducted_fan.nacelle_diameter = 2.1*((ducted_fan.areas.maximum/np.pi)**.5) ducted_fan.engine_length = 1.5*ducted_fan.nacelle_diameter ducted_fan.areas.wetted = ducted_fan.nacelle_diameter*ducted_fan.engine_length*np.pi
def main(): problem = setup() n_des_var = 13 var = np.zeros(n_des_var) var = [134.6,9.6105641082,35.0,0.123,49200.0,70000.0,0.75,6.6,30.0,70000.0,70000.0,11.5,283.0] input_vec = var / problem.optimization_problem.inputs[:,3] problem.objective(input_vec) objectives = problem.objective() * problem.optimization_problem.objective[:,1] noise_cumulative_margin = objectives[0] actual = Data() actual.noise_cumulative_margin = 19.7810544184 error = Data() error.noise_cumulative_margin = abs(actual.noise_cumulative_margin - noise_cumulative_margin)/actual.noise_cumulative_margin print 'noise_cumulative_margin=', noise_cumulative_margin print error.noise_cumulative_margin print error for k,v in error.items(): assert(np.abs(v)<1e-6) return
def main(): configs, analyses = full_setup() simple_sizing(configs) configs.finalize() analyses.finalize() # weight analysis weights = analyses.configs.base.weights breakdown = weights.evaluate() # mission analysis mission = analyses.missions.base results = mission.evaluate() # print weight breakdown print_weight_breakdown(configs.base,filename = 'weight_breakdown.dat') # print engine data into file print_engine_data(configs.base,filename = 'B737_engine_data.dat') # print parasite drag data into file # define reference condition for parasite drag ref_condition = Data() ref_condition.mach_number = 0.3 ref_condition.reynolds_number = 12e6 print_parasite_drag(ref_condition,configs.cruise,analyses,'B737_parasite_drag.dat') # print compressibility drag data into file print_compress_drag(configs.cruise,analyses,filename = 'B737_compress_drag.dat') # print mission breakdown print_mission_breakdown(results,filename='B737_mission_breakdown.dat') # load older results #save_results(results) old_results = load_results() # plt the old results plot_mission(results) plot_mission(old_results,'k-') # check the results check_results(results,old_results) ## # print some results, for check agaist Aviation paper ## end_segment = results.segments[-1] ## time = end_segment.conditions.frames.inertial.time[-1,0] / Units.min ## distance = end_segment.conditions.frames.inertial.position_vector[-1,0] / Units.nautical_miles ## mass_end = end_segment.conditions.weights.total_mass[-1,0] ## mass_begin = results.segments[0].conditions.weights.total_mass[0,0] ## fuelburn = (mass_begin- mass_end) / Units.lbs ## ## print 'Time: {:5.0f} min , Distance: {:5.0f} nm ; FuelBurn: {:5.0f} lbs'.format(time,distance,fuelburn) return
def evaluate(self,conditions): """ process vehicle to setup geometry, condititon and settings Inputs: conditions - DataDict() of aerodynamic conditions Outputs: CL - array of lift coefficients, same size as alpha CD - array of drag coefficients, same size as alpha Assumptions: linear intperolation surrogate model on Mach, Angle of Attack and Reynolds number locations outside the surrogate's table are held to nearest data no changes to initial geometry or settings """ # unpack settings = self.settings geometry = self.geometry surrogates = self.surrogates q = conditions.freestream.dynamic_pressure AoA = conditions.aerodynamics.angle_of_attack Sref = geometry.reference_area wings_lift_model = surrogates.lift_coefficient # inviscid lift of wings only inviscid_wings_lift = wings_lift_model(AoA) conditions.aerodynamics.lift_breakdown.inviscid_wings_lift = inviscid_wings_lift # lift needs to compute first, updates data needed for drag CL = compute_aircraft_lift(conditions,settings,geometry) # drag computes second CD = compute_aircraft_drag(conditions,settings,geometry) # pack conditions conditions.aerodynamics.lift_coefficient = CL conditions.aerodynamics.drag_coefficient = CD # pack results results = Data() results.lift_coefficient = CL results.drag_coefficient = CD N = q.shape[0] L = np.zeros([N,3]) D = np.zeros([N,3]) L[:,2] = ( -CL * q * Sref )[:,0] D[:,0] = ( -CD * q * Sref )[:,0] results.lift_force_vector = L results.drag_force_vector = D return results
def summarize(interface): vehicle = interface.configs.base results = interface.results mission_profile = results.missions.base # merge all segment conditions def stack_condition(a,b): if isinstance(a,np.ndarray): return np.vstack([a,b]) else: return None conditions = None for segment in mission_profile.segments: if conditions is None: conditions = segment.conditions continue conditions = conditions.do_recursive(stack_condition,segment.conditions) # pack summary = SUAVE.Core.Results() summary.weight_empty = vehicle.mass_properties.operating_empty summary.fuel_burn = max(conditions.weights.total_mass[:,0]) - min(conditions.weights.total_mass[:,0]) #results.output.max_usable_fuel = vehicle.mass_properties.max_usable_fuel summary.noise = results.noise summary.mission_time_min = max(conditions.frames.inertial.time[:,0] / Units.min) summary.max_altitude_km = max(conditions.freestream.altitude[:,0] / Units.km) summary.range_nmi = mission_profile.segments[-1].conditions.frames.inertial.position_vector[-1,0] / Units.nmi summary.field_length = results.field_length summary.stability = Data() summary.stability.cm_alpha = max(conditions.stability.static.cm_alpha[:,0]) summary.stability.cn_beta = max(conditions.stability.static.cn_beta[:,0]) #summary.conditions = conditions #TODO: revisit how this is calculated summary.second_segment_climb_rate = mission_profile.segments[1].climb_rate printme = Data() printme.fuel_burn = summary.fuel_burn printme.weight_empty = summary.weight_empty print "RESULTS" print printme return summary
def sample_training(self): """Call methods to run vortex lattice for sample point evaluation. Assumptions: None Source: N/A Inputs: see properties used Outputs: self.training. lift_coefficient [-] wing_lift_coefficients [-] (wing specific) Properties Used: self.geometry.wings.*.tag self.settings (passed to calculate vortex lattice) self.training.angle_of_attack [radians] """ # unpack geometry = self.geometry settings = self.settings training = self.training AoA = training.angle_of_attack CL = np.zeros_like(AoA) wing_CLs = Data.fromkeys(geometry.wings.keys(), np.zeros_like(AoA)) # The above performs the function of: #wing_CLs = Data() #for wing in geometry.wings.values(): # wing_CLs[wing.tag] = np.zeros_like(AoA) # condition input, local, do not keep konditions = Data() konditions.aerodynamics = Data() # calculate aerodynamics for table for i,_ in enumerate(AoA): # overriding conditions, thus the name mangling konditions.aerodynamics.angle_of_attack = AoA[i] # these functions are inherited from Aerodynamics() or overridden CL[i], wing_lifts = calculate_lift_vortex_lattice(konditions, settings, geometry) for wing in geometry.wings.values(): wing_CLs[wing.tag][i] = wing_lifts[wing.tag] # store training data training.lift_coefficient = CL training.wing_lift_coefficients = wing_CLs return
def main(): data = Data() data.x = 'x' data.y = 'y' data.sub = Data() data.sub.z = 'z' data.sub.a = 1 IO.D3JS.save_tree(data,'tree.json',root_name='data')
def main(): output = Data() output.hello = Data() output.hello.world = 'watsup' output.hello.now = None output.hello['we are'] = None output.hello.rockin = ['a','list','of','items'] IO.FreeMind.save(output,'output.mm')
def append_control_deflection(self,control_tag,deflection): """ adds a control deflection case """ control_deflection = Data() control_deflection.tag = control_tag control_deflection.deflection = deflection if self.stability_and_control.control_deflections is None: self.stability_and_control.control_deflections = Data() self.stability_and_control.control_deflections.append(control_deflection) return
def mission_setup(analyses): # ------------------------------------------------------------------ # Initialize the Mission # ------------------------------------------------------------------ mission = SUAVE.Analyses.Mission.Sequential_Segments() mission.tag = 'the_mission' #airport airport = SUAVE.Attributes.Airports.Airport() airport.altitude = 0.0 * Units.ft airport.delta_isa = 0.0 airport.atmosphere = SUAVE.Attributes.Atmospheres.Earth.US_Standard_1976() mission.airport = airport # unpack Segments module Segments = SUAVE.Analyses.Mission.Segments # base segment base_segment = Segments.Segment() ones_row = base_segment.state.ones_row 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.process.iterate.unknowns.network = analyses.vehicle.propulsors.turboprop.unpack_unknowns base_segment.process.iterate.residuals.network = analyses.vehicle.propulsors.turboprop.residuals base_segment.state.unknowns.pitch_command = ones_row(1) * 0. * Units.deg base_segment.state.conditions.propulsion = Data() base_segment.state.conditions.propulsion.rpm = 1200 * ones_row(1) base_segment.state.residuals.net = 0. * ones_row(1) base_segment.state.numerics.number_control_points = 10 base_segment.state.conditions.freestream = Data() base_segment.state.conditions.freestream.delta_ISA = airport.delta_isa * ones_row( 1) climb_segment = deepcopy(base_segment) base_segment.state.unknowns.throttle = ones_row(1) climb_segment.state.numerics.number_control_points = 3 # ------------------------------------------------------------------ # First Climb Segment: Constant Speed, Constant Rate # ------------------------------------------------------------------ segment = Segments.Climb.Constant_Throttle_Constant_Speed(climb_segment) segment.tag = "climb_1" segment.analyses.extend(analyses.takeoff) segment.altitude_start = 0.0 * Units.ft segment.altitude_end = 1500.0 * Units.ft segment.air_speed = 173.0 * Units.knots segment.throttle = 1. segment.state.conditions.propulsion.gas_turbine_rating = 'MCL' # add to misison mission.append_segment(segment) # ------------------------------------------------------------------ # Second Climb Segment: Constant Speed, Constant Rate # ------------------------------------------------------------------ segment = Segments.Climb.Constant_Throttle_Constant_Speed(climb_segment) segment.tag = "climb_2" segment.analyses.extend(analyses.cruise) segment.altitude_start = 1500.0 * Units.ft segment.altitude_end = 4000.0 * Units.ft segment.air_speed = 180.0 * Units.knots segment.throttle = 1. # segment.climb_rate = 915. * Units['ft/min'] segment.state.conditions.propulsion.gas_turbine_rating = 'MCL' # add to misison mission.append_segment(segment) # ------------------------------------------------------------------ # Third Climb Segment: Constant Speed, Constant Rate # ------------------------------------------------------------------ segment = Segments.Climb.Constant_Throttle_Constant_Speed(climb_segment) segment.tag = "climb_3" segment.analyses.extend(analyses.cruise) segment.altitude_start = 4000.0 * Units.ft segment.altitude_end = 7000.0 * Units.ft segment.air_speed = 187.0 * Units.knots segment.throttle = 1. segment.state.conditions.propulsion.gas_turbine_rating = 'MCL' # add to misison mission.append_segment(segment) # ------------------------------------------------------------------ # Fourth Climb Segment: Constant Speed, Constant Rate # ------------------------------------------------------------------ segment = Segments.Climb.Constant_Throttle_Constant_Speed(climb_segment) segment.tag = "climb_4" segment.analyses.extend(analyses.cruise) segment.altitude_start = 7000.0 * Units.ft segment.altitude_end = 10000.0 * Units.ft segment.air_speed = 195.0 * Units.knots segment.throttle = 1. segment.state.conditions.propulsion.gas_turbine_rating = 'MCL' # add to misison mission.append_segment(segment) # ------------------------------------------------------------------ # Fifth Climb Segment: Constant Speed, Constant Rate # ------------------------------------------------------------------ segment = Segments.Climb.Constant_Throttle_Constant_Speed(climb_segment) segment.tag = "climb_5" segment.analyses.extend(analyses.takeoff) segment.altitude_start = 10000.0 * Units.ft segment.altitude_end = 12000.0 * Units.ft segment.air_speed = 203.5 * Units.knots segment.throttle = 1. segment.state.conditions.propulsion.gas_turbine_rating = 'MCL' # add to misison mission.append_segment(segment) # ------------------------------------------------------------------ # Sixth Climb Segment: Constant Speed, Constant Rate # ------------------------------------------------------------------ segment = Segments.Climb.Constant_Throttle_Constant_Speed(climb_segment) segment.tag = "climb_6" segment.analyses.extend(analyses.cruise) segment.altitude_start = 12000.0 * Units.ft segment.altitude_end = 14000.0 * Units.ft segment.air_speed = 210.0 * Units.knots segment.throttle = 1. segment.state.conditions.propulsion.gas_turbine_rating = 'MCL' # add to misison mission.append_segment(segment) # ------------------------------------------------------------------ # Seventh Climb Segment: Constant Speed, Constant Rate # ------------------------------------------------------------------ segment = Segments.Climb.Constant_Throttle_Constant_Speed(climb_segment) segment.tag = "climb_7" segment.analyses.extend(analyses.cruise) segment.altitude_start = 14000.0 * Units.ft segment.altitude_end = 16000.0 * Units.ft segment.air_speed = 217.0 * Units.knots segment.throttle = 1. segment.state.conditions.propulsion.gas_turbine_rating = 'MCL' # add to misison mission.append_segment(segment) # ------------------------------------------------------------------ # Eighth Climb Segment: Constant Speed, Constant Rate # ------------------------------------------------------------------ segment = Segments.Climb.Constant_Throttle_Constant_Speed(climb_segment) segment.tag = "climb_8" segment.analyses.extend(analyses.cruise) segment.altitude_start = 16000.0 * Units.ft segment.altitude_end = 18000.0 * Units.ft segment.air_speed = 225.0 * Units.knots segment.throttle = 1. segment.state.conditions.propulsion.gas_turbine_rating = 'MCL' # add to misison mission.append_segment(segment) # ------------------------------------------------------------------ # Ninth Climb Segment: Constant Speed, Constant Rate # ------------------------------------------------------------------ segment = Segments.Climb.Constant_Throttle_Constant_Speed(climb_segment) segment.tag = "climb_9" segment.analyses.extend(analyses.cruise) segment.altitude_start = 18000.0 * Units.ft segment.altitude_end = 21000.0 * Units.ft segment.air_speed = 235.0 * Units.knots segment.throttle = 1. segment.state.conditions.propulsion.gas_turbine_rating = 'MCL' # add to misison mission.append_segment(segment) # ------------------------------------------------------------------ # Cruise Segment: Constant Speed, Constant Altitude # ------------------------------------------------------------------ segment = Segments.Cruise.Constant_Speed_Constant_Altitude(base_segment) segment.tag = "cruise" segment.analyses.extend(analyses.cruise) segment.air_speed = 260. * Units.knots segment.distance = 86. * Units.nautical_miles segment.state.conditions.propulsion.gas_turbine_rating = 'MCR' # add to mission mission.append_segment(segment) # ------------------------------------------------------------------ # First Descent Segment: Constant Speed, Constant Rate # ------------------------------------------------------------------ segment = Segments.Descent.Constant_Speed_Constant_Rate(base_segment) segment.tag = "descent_1" segment.analyses.extend(analyses.cruise) segment.altitude_end = 18000. * Units.ft segment.air_speed = 283.0 * Units.knots segment.descent_rate = 2000. * Units['ft/min'] segment.state.conditions.propulsion.gas_turbine_rating = 'MCR' # add to mission mission.append_segment(segment) # ------------------------------------------------------------------ # Second Descent Segment: Constant Speed, Constant Rate # ------------------------------------------------------------------ segment = Segments.Descent.Constant_Speed_Constant_Rate(base_segment) segment.tag = "descent_2" segment.analyses.extend(analyses.cruise) segment.altitude_end = 10000. * Units.ft segment.air_speed = 275.0 * Units.knots segment.descent_rate = 2000. * Units['ft/min'] segment.state.conditions.propulsion.gas_turbine_rating = 'MCR' # add to mission mission.append_segment(segment) # ------------------------------------------------------------------ # Third Descent Segment: Constant Speed, Constant Rate # ------------------------------------------------------------------ segment = Segments.Descent.Constant_Speed_Constant_Rate(base_segment) segment.tag = "descent_3" segment.analyses.extend(analyses.landing) segment.altitude_end = 0. * Units.ft segment.air_speed = 260.0 * Units.knots segment.descent_rate = 2000. * Units['ft/min'] segment.state.conditions.propulsion.gas_turbine_rating = 'MCR' # add to mission mission.append_segment(segment) # ------------------------------------------------------------------ # Mission definition complete # ------------------------------------------------------------------ # #################################################################### # ------------------------------------------------------------------ # MISSION TO ESTIMATE TYPICAL RESERVES - 100 nm + 45 min (900 kg) # ------------------------------------------------------------------ # # ------------------------------------------------------------------ # First Climb Segment: Constant Speed, Constant Rate # # ------------------------------------------------------------------ # # segment = Segments.Climb.Constant_Speed_Constant_Rate(base_segment) # segment.tag = "climb_1_reserve" # # segment.analyses.extend(analyses.takeoff) # # segment.altitude_start = 0.0 * Units.ft # segment.altitude_end = 5000.0 * Units.ft # segment.air_speed = 180.0 * Units.knots # segment.climb_rate = 1220. * Units['ft/min'] # # segment.state.conditions.propulsion.gas_turbine_rating = 'MCL' # # add to misison # mission.append_segment(segment) # # # ------------------------------------------------------------------ # # Second Climb Segment: Constant Speed, Constant Rate # # ------------------------------------------------------------------ # # segment = Segments.Climb.Constant_Speed_Constant_Rate(base_segment) # segment.tag = "climb_2_reserve" # # segment.analyses.extend(analyses.cruise) # # segment.altitude_start = 5000.0 * Units.ft # segment.altitude_end = 10000.0 * Units.ft # segment.air_speed = 197.0 * Units.knots # segment.climb_rate = 915. * Units['ft/min'] # # segment.state.conditions.propulsion.gas_turbine_rating = 'MCL' # # add to misison # mission.append_segment(segment) # # ------------------------------------------------------------------ # # Cruise Segment: Constant Speed, Constant Altitude # # ------------------------------------------------------------------ # # # segment = Segments.Cruise.Constant_Speed_Constant_Altitude(base_segment) # # segment.tag = "cruise_reserve_Dist" # # # # segment.analyses.extend(analyses.cruise) # # # # segment.air_speed = 170. * Units.knots # # segment.distance = 20. * Units.nautical_miles # # # # segment.state.conditions.propulsion.gas_turbine_rating = 'MCR' # # # add to mission # mission.append_segment(segment) # # # ------------------------------------------------------------------ # # Cruise Segment: Constant Speed, Constant Altitude # # ------------------------------------------------------------------ # # segment = Segments.Cruise.Constant_Speed_Constant_Altitude(base_segment) # segment.tag = "cruise_reserve_time" # # segment.analyses.extend(analyses.cruise) # # segment.air_speed = 178. * Units.knots # segment.distance = 127. * Units.nautical_miles # # Considering 45 min as being more 127 nm for 178 KTAS # segment.state.conditions.propulsion.gas_turbine_rating = 'MCR' # # # add to mission # mission.append_segment(segment) # # # ------------------------------------------------------------------ # # Third Descent Segment: Constant Speed, Constant Rate # # ------------------------------------------------------------------ # # segment = Segments.Descent.Constant_Speed_Constant_Rate(base_segment) # segment.tag = "descent_3_reserve" # # segment.analyses.extend(analyses.landing) # # segment.altitude_end = 0. * Units.ft # segment.air_speed = 240.0 * Units.knots # segment.descent_rate = 1500. * Units['ft/min'] # # # segment.state.conditions.propulsion.gas_turbine_rating = 'FID' # segment.state.conditions.propulsion.gas_turbine_rating = 'MCR' # # add to mission # mission.append_segment(segment) return mission
def main(): #------------------------------------------------------------------ # Propulsor #------------------------------------------------------------------ # build network net = Solar_Low_Fidelity() net.number_of_engines = 1. net.nacelle_diameter = 0.05 net.areas = Data() net.areas.wetted = 0.01*(2*np.pi*0.01/2) net.engine_length = 0.01 # Component 1 the Sun sun = SUAVE.Components.Energy.Processes.Solar_Radiation() net.solar_flux = sun # Component 2 the solar panels panel = SUAVE.Components.Energy.Converters.Solar_Panel() panel.ratio = 0.9 panel.area = 1.0 * panel.ratio panel.efficiency = 0.25 panel.mass_properties.mass = panel.area*(0.60 * Units.kg) net.solar_panel = panel # Component 3 the ESC esc = SUAVE.Components.Energy.Distributors.Electronic_Speed_Controller() esc.efficiency = 0.95 # Gundlach for brushless motors net.esc = esc # Component 5 the Propeller prop = SUAVE.Components.Energy.Converters.Propeller_Lo_Fid() prop.propulsive_efficiency = 0.825 net.propeller = prop # Component 4 the Motor motor = SUAVE.Components.Energy.Converters.Motor_Lo_Fid() motor.speed_constant = 800. * Units['rpm/volt'] # RPM/volt is standard motor = size_from_kv(motor) motor.gear_ratio = 1. # Gear ratio, no gearbox motor.gearbox_efficiency = 1. # Gear box efficiency, no gearbox motor.motor_efficiency = 0.825; net.motor = motor # Component 6 the Payload payload = SUAVE.Components.Energy.Peripherals.Payload() payload.power_draw = 0. #Watts payload.mass_properties.mass = 0.0 * Units.kg net.payload = payload # Component 7 the Avionics avionics = SUAVE.Components.Energy.Peripherals.Avionics() avionics.power_draw = 10. #Watts net.avionics = avionics # Component 8 the Battery bat = SUAVE.Components.Energy.Storages.Batteries.Constant_Mass.Lithium_Ion() bat.mass_properties.mass = 5.0 * Units.kg bat.specific_energy = 250. *Units.Wh/Units.kg bat.resistance = 0.003 bat.iters = 0 initialize_from_mass(bat,bat.mass_properties.mass) net.battery = bat #Component 9 the system logic controller and MPPT logic = SUAVE.Components.Energy.Distributors.Solar_Logic() logic.system_voltage = 18.5 logic.MPPT_efficiency = 0.95 net.solar_logic = logic # Setup the conditions to run the network state = Data() state.conditions = SUAVE.Analyses.Mission.Segments.Conditions.Aerodynamics() state.numerics = SUAVE.Analyses.Mission.Segments.Conditions.Numerics() conditions = state.conditions numerics = state.numerics # Calculate atmospheric properties atmosphere = SUAVE.Analyses.Atmospheric.US_Standard_1976() atmosphere_conditions = atmosphere.compute_values(1000.*Units.ft) rho = atmosphere_conditions.density[0,:] a = atmosphere_conditions.speed_of_sound[0,:] mu = atmosphere_conditions.dynamic_viscosity[0,:] T = atmosphere_conditions.temperature[0,:] conditions.propulsion.throttle = np.array([[1.0],[1.0]]) conditions.freestream.velocity = np.array([[1.0],[1.0]]) conditions.freestream.density = np.array([rho,rho]) conditions.freestream.dynamic_viscosity = np.array([mu, mu]) conditions.freestream.speed_of_sound = np.array([a, a]) conditions.freestream.altitude = np.array([[1000.0],[1000.0]]) conditions.propulsion.battery_energy = bat.max_energy*np.ones_like(conditions.freestream.altitude) conditions.frames.body.inertial_rotations = np.zeros([2,3]) conditions.frames.inertial.time = np.array([[0.0],[1.0]]) numerics.time.integrate = np.array([[0, 0],[0, 1]]) numerics.time.differentiate = np.array([[0, 0],[0, 1]]) conditions.frames.planet.start_time = time.strptime("Sat, Jun 21 06:00:00 2014", "%a, %b %d %H:%M:%S %Y",) conditions.frames.planet.latitude = np.array([[0.0],[0.0]]) conditions.frames.planet.longitude = np.array([[0.0],[0.0]]) conditions.freestream.temperature = np.array([T, T]) conditions.frames.body.transform_to_inertial = np.array([[[ 1., 0., 0.], [ 0., 1., 0.], [ 0., 0., 1.]], [[ 1., 0., 0.], [ 0., 1., 0.], [ 0., 0., 1.]]]) # Run the network and print the results results = net(state) F = results.thrust_force_vector # Truth results truth_F = [[ 68.78277813 ], [ 68.78277813 ]] truth_i = [[ 5.75011436 ], [ 5.75011436 ]] truth_rpm = [[ 14390.30435183], [ 14390.30435183 ]] truth_bat = [[ 4500000. ], [ 4499883.5041616 ]] error = Data() error.Thrust = np.max(np.abs(F[:,0]-truth_F)) error.RPM = np.max(np.abs(conditions.propulsion.propeller_rpm-truth_rpm)) error.Current = np.max(np.abs(conditions.propulsion.battery_current-truth_i)) error.Battery = np.max(np.abs(bat.current_energy-truth_bat)) print(error) for k,v in list(error.items()): assert(np.abs(v)<1e-6) return
def setup(): nexus = Nexus() problem = Data() nexus.optimization_problem = problem # ------------------------------------------------------------------- # Inputs # ------------------------------------------------------------------- # [ tag , initial, (lb,ub) , scaling , units ] problem.inputs = np.array([ #[ 'wing_span' , 9.4 , ( 8.0 , 15. ) , 9.4 , Units.meter], #[ 'wing_root_chord' , 2.0 , ( 1. , 4. ) , 2.0 , Units.meter], #[ 'wing_thickness_to_chord' , 0.15 , ( 0.1 , 0.5 ) , 0.15 , Units.less], #[ 'wing_taper_ratio' , 0.9 , ( 0.55 , 1.0 ) , 0.9 , Units.less], #[ 'cruise_distance' , 150. , ( 50. , 1000. ) , 150. , Units.nautical_miles], #[ 'cruise_airspeed' , 200. , ( 90. , 300. ) , 200. , Units['m/s']], #[ 'climb1_airspeed' , 125.0 , ( 50.0 , 250.0 ) , 125.0 , Units['m/s']], #[ 'climb2_airspeed' , 190.0 , ( 70.0 , 270.0 ) , 190.0 , Units['m/s']], #[ 'descent1_airspeed' , 180.0 , ( 100. , 270. ) , 180.0 , Units['m/s']], #[ 'descent2_airspeed' , 145.0 , ( 50. , 200.0 ) , 145.0 , Units['m/s']], #[ 'climb1_altitude' , 3. , ( 1. , 5. ) , 3. , Units.km], #[ 'climb2_altitude' , 8.0 , ( 7. , 15. ) , 8.0 , Units.km], #[ 'descent1_altitude' , 3.0 , ( 2. , 7. ) , 3.0 , Units.km], #[ 'climb1_rate' , 6.0 , ( 3. , 17. ) , 6.0 , Units['m/s']], #[ 'climb2_rate' , 3.0 , ( 2. , 12. ) , 3.0 , Units['m/s']], #[ 'descent1_rate' , 4.5 , ( 3. , 15. ) , 4.5 , Units['m/s']], #[ 'descent2_rate' , 3.0 , ( 2. , 12. ) , 3.0 , Units['m/s']], ['voltage', 470., (30., 10000.), 470., Units['volt']], ['motor_kv_cruise', 181., (10., 500.), 181., Units['rpm/volt']], ['motor_kv_HL', 91., (5., 300.), 91., Units['rpm/volt']], ['bat_spec_energy', 6.1, (3., 10000.), 6.1, Units.Wh / Units.kg], ['bat_spec_power', 0.833, (0.5, 1000.), 0.833, Units.kW / Units.kg], ['bat_max_voltage', 60.1, (0.5, 100000.), 60.1, Units['volt']], ['bat_resistance', 0.0151, (0.0001, 100.), 0.0151, Units['ohm']], ['payload_draw', 50.1, (5., 50000.), 50.1, Units.watts], ['avionics_draw', 50.1, (5., 50000.), 50.1, Units.watts], ]) # ------------------------------------------------------------------- # Objective # ------------------------------------------------------------------- # throw an error if the user isn't specific about wildcards # [ tag, scaling, units ] problem.objective = np.array([ #[ 'range', -540.9079921321364, Units.nautical_miles ], ['battery_energy_max', 8476560.0, Units.Wh], ]) # ------------------------------------------------------------------- # Constraints # ------------------------------------------------------------------- # [ tag, sense, edge, scaling, units ] problem.constraints = np.array([ #[ 'energy_constraint', '=', 0.0, 1.0, Units.less], ['battery_energy_min', '>', 0.0, 111153486.035, Units.Wh], #[ 'battery_draw' , '>', -0.1, 1.0, Units.kW ], #[ 'CL' , '>', 0.0, 1.0, Units.less], ['Throttle_cruise', '>', -0.1, 1.0, Units.less], ['Throttle_HL', '>', -0.1, 1.0, Units.less], ['Throttle_cruise', '<', 1.5, 1.0, Units.less], ['Throttle_HL', '<', 1.5, 1.0, Units.less], ['HL_rpm_min', '>', -0.1, 1.0, Units['rpm']], ['HL_rpm_max', '<', 15000, 1.0, Units['rpm']], ['cruise_rpm_min', '>', -0.1, 1.0, Units['rpm']], ['cruise_rpm_max', '<', 50000, 1.0, Units['rpm']], #[ 'lift_coefficient' , '>', -0.1, 1.0, Units.less], ['mach_number', '<', 1.5, 1.0, Units.less], ]) # ------------------------------------------------------------------- # Aliases # ------------------------------------------------------------------- # [ 'alias' , ['data.path1.name','data.path2.name'] ] problem.aliases = [ #[ 'wing_span' , 'vehicle_configurations.*.wings.main_wing.spans.projected'], #[ 'wing_root_chord' , 'vehicle_configurations.*.wings.main_wing.chords.root' ], #[ 'wing_thickness_to_chord' , 'vehicle_configurations.*.wings.main_wing.thickness_to_chord' ], #[ 'wing_taper_ratio' , 'vehicle_configurations.*.wings.main_wing.taper' ], #[ 'cruise_distance' , 'missions.mission.segments.cruise.distance' ], #[ 'cruise_airspeed' , 'missions.mission.segments.cruise.air_speed' ], #[ 'climb1_airspeed' , 'missions.mission.segments.climb_1.air_speed' ], #[ 'climb2_airspeed' , 'missions.mission.segments.climb_2.air_speed' ], #[ 'descent1_airspeed' , 'missions.mission.segments.descent_1.air_speed' ], #[ 'descent2_airspeed' , 'missions.mission.segments.descent_2.air_speed' ], #[ 'climb1_altitude' , 'missions.mission.segments.climb_1.altitude_end' ], #[ 'climb2_altitude' , 'missions.mission.segments.climb_2.altitude_end' ], #[ 'descent1_altitude' , 'missions.mission.segments.descent_1.altitude_end' ], #[ 'climb1_rate' , 'missions.mission.segments.climb_1.climb_rate' ], #[ 'climb2_rate' , 'missions.mission.segments.climb_2.climb_rate' ], #[ 'descent1_rate' , 'missions.mission.segments.descent_1.descent_rate' ], #[ 'descent2_rate' , 'missions.mission.segments.descent_2.descent_rate' ], ['voltage', 'vehicle_configurations.*.propulsors.propulsor.voltage'], [ 'motor_kv_cruise', 'vehicle_configurations.*.propulsors.propulsor.motor_forward.speed_constant' ], [ 'motor_kv_HL', 'vehicle_configurations.*.propulsors.propulsor.motor_lift.speed_constant' ], [ 'bat_spec_energy', 'vehicle_configurations.*.propulsors.propulsor.battery.specific_energy' ], [ 'bat_spec_power', 'vehicle_configurations.*.propulsors.propulsor.battery.specific_power' ], [ 'bat_max_voltage', 'vehicle_configurations.*.propulsors.propulsor.battery.max_voltage' ], [ 'bat_resistance', 'vehicle_configurations.*.propulsors.propulsor.battery.resistance' ], [ 'payload_draw', 'vehicle_configurations.*.propulsors.propulsor.payload.power_draw' ], [ 'avionics_draw', 'vehicle_configurations.*.propulsors.propulsor.avionics.power_draw' ], #[ 'range' , 'summary.total_range' ], #[ 'max_lift' , 'summary.max_lift_coefficient_all_segments' ], ['battery_energy_max', 'summary.max_battery_energy_all_segments'], ['battery_energy_min', 'summary.min_battery_energy_all_segments'], ['battery_draw', 'summary.battery_charging_power_all_segments'], ['Throttle_cruise', 'summary.throttle_all_segments'], ['Throttle_HL', 'summary.lift_throttle_all_segments'], ['HL_rpm_min', 'summary.min_rpm_lift_all_segments'], ['HL_rpm_max', 'summary.max_rpm_lift_all_segments'], ['cruise_rpm_min', 'summary.min_rpm_forward_all_segments'], ['cruise_rpm_max', 'summary.max_rpm_forward_all_segments'], ['lift_coefficient', 'summary.lift_coefficient_all_segments'], ['mach_number', 'summary.mach_number_all_segments'], ['AoA_min', 'summary.min_aoa_all_segments'], ] # ------------------------------------------------------------------- # Vehicles # ------------------------------------------------------------------- nexus.vehicle_configurations = Vehicles.setup() # ------------------------------------------------------------------- # Analyses # ------------------------------------------------------------------- nexus.analyses = Analyses.setup(nexus.vehicle_configurations) # ------------------------------------------------------------------- # Missions # ------------------------------------------------------------------- nexus.missions = Missions.setup(nexus.analyses, nexus.vehicle_configurations) # ------------------------------------------------------------------- # Procedure # ------------------------------------------------------------------- nexus.procedure = Procedure.setup() # ------------------------------------------------------------------- # Summary # ------------------------------------------------------------------- #nexus.summary = Data() nexus.total_number_of_iterations = 0 return nexus
def energy_network(): # ------------------------------------------------------------------ # Evaluation Conditions # ------------------------------------------------------------------ # --- Conditions ones_1col = np.ones([1, 1]) # setup conditions conditions = SUAVE.Analyses.Mission.Segments.Conditions.Aerodynamics() ''' conditions.frames = Data() conditions.freestream = Data() conditions.aerodynamics = Data() conditions.propulsion = Data() conditions.weights = Data() conditions.energies = Data() ''' # self.conditions = conditions # freestream conditions conditions.freestream.mach_number = ones_1col * 0.8 conditions.freestream.pressure = ones_1col * 20000. conditions.freestream.temperature = ones_1col * 215. conditions.freestream.density = ones_1col * 0.8 conditions.freestream.dynamic_viscosity = ones_1col * 0.000001475 conditions.freestream.altitude = ones_1col * 10. conditions.freestream.gravity = ones_1col * 9.81 conditions.freestream.gamma = ones_1col * 1.4 conditions.freestream.Cp = 1.4 * 287.87 / (1.4 - 1) conditions.freestream.R = 287.87 conditions.M = conditions.freestream.mach_number conditions.T = conditions.freestream.temperature conditions.p = conditions.freestream.pressure conditions.freestream.speed_of_sound = ones_1col * np.sqrt( conditions.freestream.Cp / (conditions.freestream.Cp - conditions.freestream.R) * conditions.freestream.R * conditions.freestream.temperature) #300. conditions.freestream.velocity = conditions.M * conditions.freestream.speed_of_sound conditions.velocity = conditions.M * conditions.freestream.speed_of_sound conditions.q = 0.5 * conditions.freestream.density * conditions.velocity**2 conditions.g0 = conditions.freestream.gravity # propulsion conditions conditions.propulsion.throttle = ones_1col * 1.0 # ------------------------------------------------------------------ # Design/sizing conditions # ------------------------------------------------------------------ # --- Conditions ones_1col = np.ones([1, 1]) # setup conditions conditions_sizing = SUAVE.Analyses.Mission.Segments.Conditions.Aerodynamics( ) ''' conditions_sizing.frames = Data() conditions_sizing.freestream = Data() conditions_sizing.aerodynamics = Data() conditions_sizing.propulsion = Data() conditions_sizing.weights = Data() conditions_sizing.energies = Data() ''' # self.conditions = conditions # freestream conditions conditions_sizing.freestream.mach_number = ones_1col * 0.8 conditions_sizing.freestream.pressure = ones_1col * 26499.73156529 conditions_sizing.freestream.temperature = ones_1col * 223.25186491 conditions_sizing.freestream.density = ones_1col * 0.41350854 conditions_sizing.freestream.dynamic_viscosity = ones_1col * 1.45766126e-05 #*1.789*10**(-5) conditions_sizing.freestream.altitude = ones_1col * 10000. #* 0.5 conditions_sizing.freestream.gravity = ones_1col * 9.81 conditions_sizing.freestream.gamma = ones_1col * 1.4 conditions_sizing.freestream.Cp = 1.4 * 287.87 / (1.4 - 1) conditions_sizing.freestream.R = 287.87 conditions_sizing.freestream.speed_of_sound = 299.53150968 conditions_sizing.freestream.velocity = conditions_sizing.freestream.mach_number * conditions_sizing.freestream.speed_of_sound # propulsion conditions conditions_sizing.propulsion.throttle = ones_1col * 1.0 state_sizing = Data() state_sizing.numerics = Data() state_sizing.conditions = conditions_sizing state_off_design = Data() state_off_design.numerics = Data() state_off_design.conditions = conditions # ------------------------------------------------------------------ # Turbofan Network # ------------------------------------------------------------------ #instantiate the gas turbine network turbofan = SUAVE.Components.Energy.Networks.Turbofan() turbofan.tag = 'turbo_fan' # setup turbofan.bypass_ratio = 5.4 turbofan.number_of_engines = 2.0 turbofan.engine_length = 2.5 turbofan.nacelle_diameter = 1.580 # working fluid turbofan.working_fluid = SUAVE.Attributes.Gases.Air() # ------------------------------------------------------------------ # Component 1 - Ram # to convert freestream static to stagnation quantities # instantiate ram = SUAVE.Components.Energy.Converters.Ram() ram.tag = 'ram' # add to the network turbofan.append(ram) # ------------------------------------------------------------------ # Component 2 - Inlet Nozzle # instantiate inlet_nozzle = SUAVE.Components.Energy.Converters.Compression_Nozzle() inlet_nozzle.tag = 'inlet_nozzle' # setup inlet_nozzle.polytropic_efficiency = 0.98 inlet_nozzle.pressure_ratio = 0.98 # add to network turbofan.append(inlet_nozzle) # ------------------------------------------------------------------ # Component 3 - Low Pressure Compressor # instantiate compressor = SUAVE.Components.Energy.Converters.Compressor() compressor.tag = 'low_pressure_compressor' # setup compressor.polytropic_efficiency = 0.91 compressor.pressure_ratio = 1.14 # add to network turbofan.append(compressor) # ------------------------------------------------------------------ # Component 4 - High Pressure Compressor # instantiate compressor = SUAVE.Components.Energy.Converters.Compressor() compressor.tag = 'high_pressure_compressor' # setup compressor.polytropic_efficiency = 0.91 compressor.pressure_ratio = 13.415 # add to network turbofan.append(compressor) # ------------------------------------------------------------------ # Component 5 - Low Pressure Turbine # instantiate turbine = SUAVE.Components.Energy.Converters.Turbine() turbine.tag = 'low_pressure_turbine' # setup turbine.mechanical_efficiency = 0.99 turbine.polytropic_efficiency = 0.93 # add to network turbofan.append(turbine) # ------------------------------------------------------------------ # Component 6 - High Pressure Turbine # instantiate turbine = SUAVE.Components.Energy.Converters.Turbine() turbine.tag = 'high_pressure_turbine' # setup turbine.mechanical_efficiency = 0.99 turbine.polytropic_efficiency = 0.93 # add to network turbofan.append(turbine) # ------------------------------------------------------------------ # Component 7 - Combustor # instantiate combustor = SUAVE.Components.Energy.Converters.Combustor() combustor.tag = 'combustor' # setup combustor.efficiency = 0.99 combustor.alphac = 1.0 combustor.turbine_inlet_temperature = 1450 combustor.pressure_ratio = 0.95 combustor.fuel_data = SUAVE.Attributes.Propellants.Jet_A() # add to network turbofan.append(combustor) # ------------------------------------------------------------------ # Component 8 - Core Nozzle # instantiate nozzle = SUAVE.Components.Energy.Converters.Expansion_Nozzle() nozzle.tag = 'core_nozzle' # setup nozzle.polytropic_efficiency = 0.95 nozzle.pressure_ratio = 0.99 # add to network turbofan.append(nozzle) # ------------------------------------------------------------------ # Component 9 - Fan Nozzle # instantiate nozzle = SUAVE.Components.Energy.Converters.Expansion_Nozzle() nozzle.tag = 'fan_nozzle' # setup nozzle.polytropic_efficiency = 0.95 nozzle.pressure_ratio = 0.99 # add to network turbofan.append(nozzle) # ------------------------------------------------------------------ # Component 10 - Fan # instantiate fan = SUAVE.Components.Energy.Converters.Fan() fan.tag = 'fan' # setup fan.polytropic_efficiency = 0.93 fan.pressure_ratio = 1.7 # add to network turbofan.append(fan) # ------------------------------------------------------------------ # Component 10 - Thrust # to compute thrust # instantiate thrust = SUAVE.Components.Energy.Processes.Thrust() thrust.tag = 'thrust' # setup thrust.total_design = 42383.01818423 # add to network turbofan.thrust = thrust #bypass ratio closer to fan numerics = Data() eta = 1.0 #size the turbofan turbofan_sizing(turbofan, 0.8, 10000.0) print "Design thrust ", turbofan.design_thrust print "Sealevel static thrust ", turbofan.sealevel_static_thrust results_design = turbofan(state_sizing) results_off_design = turbofan(state_off_design) F = results_design.thrust_force_vector mdot = results_design.vehicle_mass_rate F_off_design = results_off_design.thrust_force_vector mdot_off_design = results_off_design.vehicle_mass_rate #Test the model #Specify the expected values expected = Data() expected.thrust = 42383.01818423 expected.mdot = 0.7657905 #error data function error = Data() error.thrust_error = (F[0][0] - expected.thrust) / expected.thrust error.mdot_error = (mdot[0][0] - expected.mdot) / expected.mdot print error for k, v in error.items(): assert (np.abs(v) < 1e-4) return
def __defaults__(self): """ This sets the default values for the component to function. Assumptions: None Source: N/A Inputs: None Outputs: None Properties Used: None """ self.tag = 'fuselage' self.origin = [[0.0,0.0,0.0]] self.aerodynamic_center = [0.0,0.0,0.0] self.Sections = Lofted_Body.Section.Container() self.Segments = ContainerOrdered() self.number_coach_seats = 0.0 self.seats_abreast = 0.0 self.seat_pitch = 1.0 self.areas = Data() self.areas.front_projected = 0.0 self.areas.side_projected = 0.0 self.areas.wetted = 0.0 self.effective_diameter = 0.0 self.width = 0.0 self.heights = Data() self.heights.maximum = 0.0 self.heights.at_quarter_length = 0.0 self.heights.at_three_quarters_length = 0.0 self.heights.at_wing_root_quarter_chord = 0.0 self.x_rotation = 0.0 self.y_rotation = 0.0 self.z_rotation = 0.0 self.lengths = Data() self.lengths.nose = 0.0 self.lengths.tail = 0.0 self.lengths.total = 0.0 self.lengths.cabin = 0.0 self.lengths.fore_space = 0.0 self.lengths.aft_space = 0.0 self.fineness = Data() self.fineness.nose = 0.0 self.fineness.tail = 0.0 self.differential_pressure = 0.0 # for BWB self.aft_centerbody_area = 0.0 self.aft_centerbody_taper = 0.0 self.cabin_area = 0.0 self.Fuel_Tanks = Container() # For VSP self.vsp_data = Data() self.vsp_data.xsec_surf_id = '' # There is only one XSecSurf in each VSP geom. self.vsp_data.xsec_num = None # Number if XSecs in fuselage geom. self.Segments = SUAVE.Core.ContainerOrdered()
def evaluate_thrust(self, state): """ Calculate thrust given the current state of the vehicle Assumptions: Caps the throttle at 110% and linearly interpolates thrust off that Source: N/A Inputs: state [state()] Outputs: results.thrust_force_vector [Newtons] results.vehicle_mass_rate [kg/s] conditions.propulsion: rpm_lift [radians/sec] rpm _forward [radians/sec] current_lift [amps] current_forward [amps] battery_draw [watts] battery_energy [joules] voltage_open_circuit [volts] voltage_under_load [volts] motor_torque_lift [N-M] motor_torque_forward [N-M] propeller_torque_lift [N-M] propeller_torque_forward [N-M] Properties Used: Defaulted values """ # unpack conditions = state.conditions numerics = state.numerics motor_lift = self.motor_lift motor_forward = self.motor_forward propeller_lift = self.propeller_lift propeller_forward = self.propeller_forward esc_lift = self.esc_lift esc_forward = self.esc_forward avionics = self.avionics payload = self.payload battery = self.battery num_lift = self.number_of_engines_lift num_forward = self.number_of_engines_forward ### # Setup batteries and ESC's ### # Set battery energy battery.current_energy = conditions.propulsion.battery_energy volts = state.unknowns.battery_voltage_under_load volts[volts > self.voltage] = self.voltage # ESC Voltage esc_lift.inputs.voltagein = volts esc_forward.inputs.voltagein = volts ### # Evaluate thrust from the forward propulsors ### # Throttle the voltage esc_forward.voltageout(conditions) # link motor_forward.inputs.voltage = esc_forward.outputs.voltageout # Run the motor motor_forward.omega(conditions) # link propeller_forward.inputs.omega = motor_forward.outputs.omega propeller_forward.thrust_angle = self.thrust_angle_forward # Run the propeller F_forward, Q_forward, P_forward, Cp_forward = propeller_forward.spin( conditions) # Check to see if magic thrust is needed, the ESC caps throttle at 1.1 already eta = conditions.propulsion.throttle[:, 0, None] P_forward[eta > 1.0] = P_forward[eta > 1.0] * eta[eta > 1.0] F_forward[eta > 1.0] = F_forward[eta > 1.0] * eta[eta > 1.0] # Run the motor for current motor_forward.current(conditions) # link esc_forward.inputs.currentout = motor_forward.outputs.current # Run the esc esc_forward.currentin() ### # Evaluate thrust from the lift propulsors ### # Make a new set of konditions, since there are differences for the esc and motor konditions = Data() konditions.propulsion = Data() konditions.freestream = Data() konditions.frames = Data() konditions.frames.inertial = Data() konditions.frames.body = Data() konditions.propulsion.throttle = conditions.propulsion.lift_throttle * 1. konditions.propulsion.propeller_power_coefficient = conditions.propulsion.propeller_power_coefficient_lift * 1. konditions.freestream.density = conditions.freestream.density * 1. konditions.freestream.velocity = conditions.freestream.velocity * 1. konditions.freestream.dynamic_viscosity = conditions.freestream.dynamic_viscosity * 1. konditions.freestream.speed_of_sound = conditions.freestream.speed_of_sound * 1. konditions.freestream.temperature = conditions.freestream.temperature * 1. konditions.frames.inertial.velocity_vector = conditions.frames.inertial.velocity_vector * 1. konditions.frames.body.transform_to_inertial = conditions.frames.body.transform_to_inertial # Throttle the voltage esc_lift.voltageout(konditions) # link motor_lift.inputs.voltage = esc_lift.outputs.voltageout # Run the motor motor_lift.omega(konditions) # link propeller_lift.inputs.omega = motor_lift.outputs.omega propeller_lift.thrust_angle = self.thrust_angle_lift # Run the propeller F_lift, Q_lift, P_lift, Cp_lift = propeller_lift.spin(konditions) # Check to see if magic thrust is needed, the ESC caps throttle at 1.1 already eta = state.conditions.propulsion.lift_throttle P_lift[eta > 1.0] = P_lift[eta > 1.0] * eta[eta > 1.0] F_lift[eta > 1.0] = F_lift[eta > 1.0] * eta[eta > 1.0] # Run the motor for current motor_lift.current(conditions) # link esc_lift.inputs.currentout = motor_lift.outputs.current # Run the esc esc_lift.currentin() ### # Combine the thrusts and powers ### # Run the avionics avionics.power() # Run the payload payload.power() # Calculate avionics and payload power avionics_payload_power = avionics.outputs.power + payload.outputs.power # Calculate avionics and payload current i_avionics_payload = avionics_payload_power / state.unknowns.battery_voltage_under_load # Add up the power usages i_lift = esc_lift.outputs.currentin * num_lift i_forward = esc_forward.outputs.currentin * num_forward current_total = i_lift + i_forward + i_avionics_payload power_total = current_total * state.unknowns.battery_voltage_under_load battery.inputs.current = current_total battery.inputs.power_in = -power_total # Run the battery battery.energy_calc(numerics) # Pack the conditions rpm_lift = motor_lift.outputs.omega * 60. / (2. * np.pi) rpm_forward = motor_forward.outputs.omega * 60. / (2. * np.pi) battery_draw = battery.inputs.power_in battery_energy = battery.current_energy voltage_open_circuit = battery.voltage_open_circuit voltage_under_load = battery.voltage_under_load conditions.propulsion.rpm_lift = rpm_lift conditions.propulsion.rpm_forward = rpm_forward conditions.propulsion.current_lift = i_lift conditions.propulsion.current_forward = i_forward conditions.propulsion.motor_torque_lift = motor_lift.outputs.torque conditions.propulsion.motor_torque_forward = motor_forward.outputs.torque conditions.propulsion.propeller_torque_lift = Q_lift conditions.propulsion.propeller_torque_forward = Q_forward conditions.propulsion.battery_draw = battery_draw conditions.propulsion.battery_energy = battery_energy conditions.propulsion.voltage_open_circuit = voltage_open_circuit conditions.propulsion.voltage_under_load = voltage_under_load # Calculate the thrust and mdot F_lift_total = F_lift * num_lift * [ np.cos(self.thrust_angle_lift), 0, -np.sin(self.thrust_angle_lift) ] F_forward_total = F_forward * num_forward * [ np.cos(self.thrust_angle_forward), 0, -np.sin(self.thrust_angle_forward) ] F_total = F_lift_total + F_forward_total mdot = np.zeros_like(F_total) results = Data() results.thrust_force_vector = F_total results.vehicle_mass_rate = mdot return results
def main(): vehicle = vehicle_setup() # Create the vehicle for testing for wing in vehicle.wings: wing.areas.wetted = 2.0 * wing.areas.reference wing.areas.exposed = 0.8 * wing.areas.wetted wing.areas.affected = 0.6 * wing.areas.wetted aerodynamics = SUAVE.Analyses.Aerodynamics.Supersonic_Zero() aerodynamics.geometry = vehicle aerodynamics.settings.drag_coefficient_increment = 0.0000 vehicle.aerodynamics_model = aerodynamics vehicle.aerodynamics_model.initialize() test_num = 11 # Length of arrays used in this test # -------------------------------------------------------------------- # Test Lift Surrogate # -------------------------------------------------------------------- AoA = np.linspace(-.174, .174, test_num)[:, None] # +- 10 degrees # Cruise conditions (except Mach number) state = SUAVE.Analyses.Mission.Segments.Conditions.State() state.conditions = SUAVE.Analyses.Mission.Segments.Conditions.Aerodynamics( ) state.expand_rows(test_num) # -------------------------------------------------------------------- # Initialize variables needed for CL and CD calculations # Use a seeded random order for values # -------------------------------------------------------------------- random.seed(1) Mc = np.linspace(0.05, 0.9, test_num) random.shuffle(Mc) AoA = AoA.reshape(test_num, 1) Mc = Mc.reshape(test_num, 1) rho = np.linspace(0.3, 1.3, test_num) random.shuffle(rho) rho = rho.reshape(test_num, 1) mu = np.linspace(5 * 10**-6, 20 * 10**-6, test_num) random.shuffle(mu) mu = mu.reshape(test_num, 1) T = np.linspace(200, 300, test_num) random.shuffle(T) T = T.reshape(test_num, 1) pressure = np.linspace(10**5, 10**6, test_num) pressure = pressure.reshape(test_num, 1) state.conditions.freestream.mach_number = Mc state.conditions.freestream.density = rho state.conditions.freestream.dynamic_viscosity = mu state.conditions.freestream.temperature = T state.conditions.freestream.pressure = pressure state.conditions.aerodynamics.angle_of_attack = AoA # -------------------------------------------------------------------- # Surrogate # -------------------------------------------------------------------- #call the aero model results = aerodynamics.evaluate(state) #build a polar for the markup aero polar = Data() CL = results.lift.total CD = results.drag.total polar.lift = CL polar.drag = CD # -------------------------------------------------------------------- # Test compute Lift # -------------------------------------------------------------------- #compute_aircraft_lift(conditions, configuration, geometry) lift = state.conditions.aerodynamics.lift_coefficient # Truth value lift_r = np.array([ -2.42489437, -0.90696416, -0.53991953, -0.3044834, -0.03710598, 0.31061936, 0.52106899, 0.77407765, 1.22389024, 1.86240501, 1.54587835 ])[:, None] lift_r = lift_r.reshape(test_num, 1) lift_test = np.abs((lift - lift_r) / lift) print '\nCompute Lift Test Results\n' print lift_test assert (np.max(lift_test) < 1e-4), 'Supersonic Aero regression failed at compute lift test' # -------------------------------------------------------------------- # Test compute drag # -------------------------------------------------------------------- #compute_aircraft_drag(conditions, configuration, geometry) # Pull calculated values drag_breakdown = state.conditions.aerodynamics.drag_breakdown # Only one wing is evaluated since they rely on the same function cd_c = drag_breakdown.compressible['main_wing'].compressibility_drag cd_i = drag_breakdown.induced.total cd_m = drag_breakdown.miscellaneous.total cd_m_fuse_base = drag_breakdown.miscellaneous.fuselage_base cd_m_fuse_up = drag_breakdown.miscellaneous.fuselage_upsweep cd_m_nac_base = drag_breakdown.miscellaneous.nacelle_base['turbo_fan'] cd_m_ctrl = drag_breakdown.miscellaneous.control_gaps cd_p_fuse = drag_breakdown.parasite['fuselage'].parasite_drag_coefficient cd_p_wing = drag_breakdown.parasite['main_wing'].parasite_drag_coefficient cd_tot = drag_breakdown.total print cd_c print cd_i print cd_m print cd_m_fuse_base print cd_m_fuse_up print cd_m_nac_base print cd_m_ctrl print cd_p_fuse print cd_p_wing print cd_tot # Truth values (cd_c_r, cd_i_r, cd_m_r, cd_m_fuse_base_r, cd_m_fuse_up_r, cd_m_nac_base_r, cd_m_ctrl_r, cd_p_fuse_r, cd_p_wing_r, cd_tot_r) = reg_values() cd_c_r = cd_c_r.reshape(test_num, 1) cd_i_r = cd_i_r.reshape(test_num, 1) cd_m_r = cd_m_r.reshape(test_num, 1) cd_m_fuse_base_r = cd_m_fuse_base_r.reshape(test_num, 1) cd_m_fuse_up_r = cd_m_fuse_up_r.reshape(test_num, 1) cd_m_nac_base_r = cd_m_nac_base_r.reshape(test_num, 1) cd_m_ctrl_r = cd_m_ctrl_r.reshape(test_num, 1) cd_p_fuse_r = cd_p_fuse_r.reshape(test_num, 1) cd_p_wing_r = cd_p_wing_r.reshape(test_num, 1) cd_tot_r = cd_tot_r.reshape(test_num, 1) drag_tests = Data() drag_tests.cd_c = np.abs((cd_c - cd_c_r) / cd_c) drag_tests.cd_i = np.abs((cd_i - cd_i_r) / cd_i) drag_tests.cd_m = np.abs((cd_m - cd_m_r) / cd_m) # Line below is not normalized since regression values are 0, insert commented line if this changes drag_tests.cd_m_fuse_base = np.abs( (cd_m_fuse_base - cd_m_fuse_base_r )) # np.abs((cd_m_fuse_base-cd_m_fuse_base_r)/cd_m_fuse_base) drag_tests.cd_m_fuse_up = np.abs( (cd_m_fuse_up - cd_m_fuse_up_r) / cd_m_fuse_up) drag_tests.cd_m_ctrl = np.abs((cd_m_ctrl - cd_m_ctrl_r) / cd_m_ctrl) drag_tests.cd_p_fuse = np.abs((cd_p_fuse - cd_p_fuse_r) / cd_p_fuse) drag_tests.cd_p_wing = np.abs((cd_p_wing - cd_p_wing_r) / cd_p_wing) drag_tests.cd_tot = np.abs((cd_tot - cd_tot_r) / cd_tot) print '\nCompute Drag Test Results\n' #print drag_tests for i, tests in drag_tests.items(): assert (np.max(tests) < 1e-4), 'Supersonic Aero regression test failed at ' + i
def simple_sizing(nexus): configs = nexus.vehicle_configurations analyses = nexus.analyses base = configs.base m_guess = nexus.m_guess #take in sizing inputs base.mass_properties.max_takeoff = m_guess #find conditions air_speed = nexus.missions.base.segments['cruise'].air_speed altitude = nexus.missions.base.segments['climb_3'].altitude_end atmosphere = SUAVE.Analyses.Atmospheric.US_Standard_1976() freestream = atmosphere.compute_values(altitude) freestream0 = atmosphere.compute_values(6000.*Units.ft) #cabin altitude diff_pressure = np.max(freestream0.pressure-freestream.pressure,0) fuselage = base.fuselages['fuselage'] fuselage.differential_pressure = diff_pressure #now size engine mach_number = air_speed/freestream.speed_of_sound #now add to freestream data object freestream.velocity = air_speed freestream.mach_number = mach_number freestream.gravity = 9.81 conditions = SUAVE.Analyses.Mission.Segments.Conditions.Aerodynamics() #assign conditions in form for propulsor sizing conditions.freestream = freestream nose_load_fraction = .06 #now evaluate all of the vehicle configurations for config in configs: config.wings.horizontal_stabilizer.areas.reference = (26.0/92.0)*config.wings.main_wing.areas.reference for wing in config.wings: wing = SUAVE.Methods.Geometry.Two_Dimensional.Planform.wing_planform(wing) wing.areas.exposed = 0.8 * wing.areas.wetted wing.areas.affected = 0.6 * wing.areas.reference fuselage = config.fuselages['fuselage'] fuselage.differential_pressure = diff_pressure #now evaluate weights # diff the new data config.mass_properties.max_takeoff = m_guess #take in parameters config.mass_properties.takeoff = m_guess config.mass_properties.max_zero_fuel = base.mass_properties.max_zero_fuel config.store_diff() #now evaluate the weights weights = analyses.base.weights.evaluate() #base.weights.evaluate() #update zfw empty_weight = base.mass_properties.operating_empty payload = base.mass_properties.max_payload zfw = empty_weight + payload base.max_zero_fuel = zfw base.store_diff() for config in configs: config.pull_base() # ------------------------------------------------------------------ # Landing Configuration # ------------------------------------------------------------------ landing = nexus.vehicle_configurations.landing landing_conditions = Data() landing_conditions.freestream = Data() # landing weight landing.mass_properties.landing = base.mass_properties.max_zero_fuel # Landing CL_max altitude = nexus.missions.base.segments[-1].altitude_end atmosphere = SUAVE.Analyses.Atmospheric.US_Standard_1976() freestream = atmosphere.compute_values(altitude) mu = freestream.dynamic_viscosity rho = freestream.density landing_conditions.freestream.velocity = nexus.missions.base.segments['descent_3'].air_speed landing_conditions.freestream.density = rho landing_conditions.freestream.dynamic_viscosity = mu/rho CL_max_landing,CDi = compute_max_lift_coeff(landing,landing_conditions) landing.maximum_lift_coefficient = CL_max_landing # diff the new data landing.store_diff() #Takeoff CL_max takeoff = nexus.vehicle_configurations.takeoff takeoff_conditions = Data() takeoff_conditions.freestream = Data() altitude = nexus.missions.base.airport.altitude atmosphere = SUAVE.Analyses.Atmospheric.US_Standard_1976() freestream = atmosphere.compute_values(altitude) mu = freestream.dynamic_viscosity rho = freestream.density takeoff_conditions.freestream.velocity = nexus.missions.base.segments.climb_1.air_speed takeoff_conditions.freestream.density = rho takeoff_conditions.freestream.dynamic_viscosity = mu/rho max_CL_takeoff,CDi = compute_max_lift_coeff(takeoff,takeoff_conditions) takeoff.maximum_lift_coefficient = max_CL_takeoff takeoff.store_diff() #Base config CL_max base = nexus.vehicle_configurations.base base_conditions = Data() base_conditions.freestream = Data() altitude = nexus.missions.base.airport.altitude atmosphere = SUAVE.Analyses.Atmospheric.US_Standard_1976() freestream = atmosphere.compute_values(altitude) mu = freestream.dynamic_viscosity rho = freestream.density base_conditions.freestream.velocity = nexus.missions.base.segments.climb_1.air_speed base_conditions.freestream.density = rho base_conditions.freestream.dynamic_viscosity = mu/rho max_CL_base,CDi = compute_max_lift_coeff(base,base_conditions) base.maximum_lift_coefficient = max_CL_base base.store_diff() # done! return nexus
def simple_sizing(nexus): configs = nexus.vehicle_configurations base = configs.base #find conditions air_speed = nexus.missions.base.segments['cruise'].air_speed altitude = nexus.missions.base.segments['climb_5'].altitude_end atmosphere = SUAVE.Analyses.Atmospheric.US_Standard_1976() freestream = atmosphere.compute_values(altitude) freestream0 = atmosphere.compute_values(6000. * Units.ft) #cabin altitude diff_pressure = np.max(freestream0.pressure - freestream.pressure, 0) fuselage = base.fuselages['fuselage'] fuselage.differential_pressure = diff_pressure #now size engine mach_number = air_speed / freestream.speed_of_sound #now add to freestream data object freestream.velocity = air_speed freestream.mach_number = mach_number freestream.gravity = 9.81 conditions = SUAVE.Analyses.Mission.Segments.Conditions.Aerodynamics( ) #assign conditions in form for propulsor sizing conditions.freestream = freestream for config in configs: config.wings.horizontal_stabilizer.areas.reference = ( 26.0 / 92.0) * config.wings.main_wing.areas.reference for wing in config.wings: wing = SUAVE.Methods.Geometry.Two_Dimensional.Planform.wing_planform( wing) wing.areas.exposed = 0.8 * wing.areas.wetted wing.areas.affected = 0.6 * wing.areas.reference fuselage = config.fuselages['fuselage'] fuselage.differential_pressure = diff_pressure turbofan_sizing(config.propulsors['turbofan'], mach_number=mach_number, altitude=altitude) compute_turbofan_geometry(config.propulsors['turbofan'], conditions) # ------------------------------------------------------------------ # Landing Configuration # ------------------------------------------------------------------ landing = nexus.vehicle_configurations.landing landing_conditions = Data() landing_conditions.freestream = Data() # landing weight landing.mass_properties.landing = 0.85 * config.mass_properties.takeoff # Landing CL_max altitude = nexus.missions.base.segments[-1].altitude_end atmosphere = SUAVE.Analyses.Atmospheric.US_Standard_1976() freestream_landing = atmosphere.compute_values(0.) landing_conditions.freestream.velocity = nexus.missions.base.segments[ 'descent_3'].air_speed landing_conditions.freestream.density = freestream_landing.density landing_conditions.freestream.dynamic_viscosity = freestream_landing.dynamic_viscosity CL_max_landing, CDi = compute_max_lift_coeff(landing, landing_conditions) landing.maximum_lift_coefficient = CL_max_landing #Takeoff CL_max takeoff = nexus.vehicle_configurations.takeoff takeoff_conditions = Data() takeoff_conditions.freestream = Data() altitude = nexus.missions.base.airport.altitude freestream_takeoff = atmosphere.compute_values(altitude) takeoff_conditions.freestream.velocity = nexus.missions.base.segments.climb_1.air_speed takeoff_conditions.freestream.density = freestream_takeoff.density takeoff_conditions.freestream.dynamic_viscosity = freestream_takeoff.dynamic_viscosity max_CL_takeoff, CDi = compute_max_lift_coeff(takeoff, takeoff_conditions) takeoff.maximum_lift_coefficient = max_CL_takeoff #Base config CL_max base = nexus.vehicle_configurations.base base_conditions = Data() base_conditions.freestream = takeoff_conditions.freestream max_CL_base, CDi = compute_max_lift_coeff(base, base_conditions) base.maximum_lift_coefficient = max_CL_base return nexus
def check_cruise_altitude(analyses): # ------------------------------------------------------------------ # Initialize the Mission # ------------------------------------------------------------------ mission = SUAVE.Analyses.Mission.Sequential_Segments() mission.tag = 'the_dummy_mission' # airport airport = SUAVE.Attributes.Airports.Airport() airport.altitude = 0.0 * Units.ft airport.delta_isa = 0.0 airport.atmosphere = SUAVE.Analyses.Atmospheric.US_Standard_1976() mission.airport = airport # unpack Segments module Segments = SUAVE.Analyses.Mission.Segments # base segment base_segment = Segments.Segment() ones_row = base_segment.state.ones_row 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.process.iterate.unknowns.network = analyses.vehicle.propulsors.turboprop.unpack_unknowns base_segment.process.iterate.residuals.network = analyses.vehicle.propulsors.turboprop.residuals base_segment.state.unknowns.pitch_command = ones_row(1) * 0. * Units.deg base_segment.state.conditions.propulsion = Data() base_segment.state.conditions.propulsion.rpm = 1200 * ones_row(1) base_segment.state.residuals.net = 0. * ones_row(1) base_segment.state.numerics.number_control_points = 10 base_segment.state.conditions.freestream = Data() base_segment.state.conditions.freestream.delta_ISA = airport.delta_isa * ones_row(1) base_segment.state.numerics.number_control_points = 20 planet = SUAVE.Attributes.Planets.Earth() atmosphere = SUAVE.Attributes.Atmospheres.Earth.US_Standard_1976() # ------------------------------------------------------------------ # First Climb Segment: Constant Speed, Constant Rate # ------------------------------------------------------------------ segment = Segments.Climb.Constant_EAS_Constant_Rate(base_segment) segment.tag = "climb_dummy" # connect vehicle configuration segment.analyses.extend(analyses.base) # define segment attributes segment.atmosphere = atmosphere segment.planet = planet segment.altitude_start = 0.0 * Units.ft segment.altitude_end = 21000. * Units.ft segment.equivalent_air_speed = 168.2981 * Units.knots segment.climb_rate = 300. * Units['ft/min'] segment.state.conditions.propulsion.gas_turbine_rating = 'MCL' # add to mission mission.append_segment(segment) results = mission.evaluate() cruise_altitude = 21000. for segment in results.segments.values(): altitude = segment.conditions.freestream.altitude[:, 0] / Units.ft eta = segment.conditions.propulsion.throttle[:, 0] for i in range(len(altitude)): if eta[i] > 1.: cruise_altitude = altitude[i - 1] elif eta[i] < 1. and i == len(altitude) - 1: cruise_altitude = altitude[i] print ('Cruise altitude: ' + str(int((cruise_altitude+1)/100.)*100.)+' ft') cruise_altitude = int(cruise_altitude/100.)*100. * Units.ft return cruise_altitude
def vehicle_setup(): # ------------------------------------------------------------------ # Initialize the Vehicle # ------------------------------------------------------------------ vehicle = SUAVE.Vehicle() vehicle.tag = 'Tecnam_P2006TElectric' # ------------------------------------------------------------------ # Vehicle-level Properties # ------------------------------------------------------------------ # mass properties vehicle.mass_properties.max_takeoff = 1400 * Units.kilogram vehicle.mass_properties.takeoff = 1400 * Units.kilogram vehicle.mass_properties.operating_empty = 1000 * Units.kilogram vehicle.mass_properties.max_zero_fuel = 1400 * Units.kilogram vehicle.mass_properties.cargo = 80 * Units.kilogram # envelope properties vehicle.envelope.ultimate_load = 5.7 vehicle.envelope.limit_load = 3.8 # basic parameters vehicle.reference_area = 64.4 * Units['meters**2'] vehicle.passengers = 4 vehicle.systems.control = "fully powered" vehicle.systems.accessories = "medium range" # ------------------------------------------------------------------ # Landing Gear # ------------------------------------------------------------------ # used for noise calculations landing_gear = SUAVE.Components.Landing_Gear.Landing_Gear() landing_gear.tag = "main_landing_gear" landing_gear.main_tire_diameter = 0.423 * Units.m landing_gear.nose_tire_diameter = 0.3625 * Units.m landing_gear.main_strut_length = 0.4833 * Units.m landing_gear.nose_strut_length = 0.3625 * Units.m landing_gear.main_units = 2 #number of main landing gear units landing_gear.nose_units = 1 #number of nose landing gear landing_gear.main_wheels = 1 #number of wheels on the main landing gear landing_gear.nose_wheels = 1 #number of wheels on the nose landing gear vehicle.landing_gear = landing_gear # ------------------------------------------------------------------ # Fuselage # ------------------------------------------------------------------ fuselage = SUAVE.Components.Fuselages.Fuselage() fuselage.tag = 'fuselage' fuselage.number_coach_seats = vehicle.passengers fuselage.seats_abreast = 2 fuselage.seat_pitch = 0.995 * Units.meter fuselage.fineness.nose = 1.27 fuselage.fineness.tail = 1 #3.31 fuselage.lengths.nose = 1.16 * Units.meter fuselage.lengths.tail = 4.637 * Units.meter fuselage.lengths.cabin = 2.653 * Units.meter fuselage.lengths.total = 8.45 * Units.meter fuselage.lengths.fore_space = 0.0 * Units.meter fuselage.lengths.aft_space = 0.0 * Units.meter fuselage.width = 1.22 * Units.meter fuselage.heights.maximum = 1.41 * Units.meter fuselage.effective_diameter = 2 * Units.meter fuselage.areas.side_projected = 7.46 * Units['meters**2'] fuselage.areas.wetted = 25.0 * Units['meters**2'] fuselage.areas.front_projected = 1.54 * Units['meters**2'] fuselage.differential_pressure = 0.0 * Units.pascal # Maximum differential pressure fuselage.heights.at_quarter_length = 1.077 * Units.meter fuselage.heights.at_three_quarters_length = 0.5 * Units.meter #0.621 * Units.meter fuselage.heights.at_wing_root_quarter_chord = 1.41 * Units.meter # add to vehicle vehicle.append_component(fuselage) # ------------------------------------------------------------------ # Main Wing # ------------------------------------------------------------------ wing = SUAVE.Components.Wings.Main_Wing() wing.tag = 'main_wing' wing.thickness_to_chord = 0.15 wing.taper = 0.7016 wing.spans.projected = 9.631680 * Units.meter wing.chords.root = 0.7559040 * Units.meter wing.chords.tip = wing.chords.root * wing.taper wing.chords.mean_aerodynamic = 0.6 * Units.meter wing.areas.reference = (wing.chords.root + wing.chords.tip) * wing.spans.projected / 2 wing.twists.root = 0. * Units.degrees wing.twists.tip = 0. * Units.degrees wing.dihedral = 1. * Units.degrees wing.origin = [2.986, 0, 1.077] # meters wing.sweeps.leading_edge = 1.9 * Units.deg wing.aspect_ratio = (wing.spans.projected * wing.spans.projected) / wing.areas.reference wing.span_efficiency = 0.99 * ( 1 - 0.0407 * (fuselage.width / wing.spans.projected) - 1.792 * ((fuselage.width / wing.spans.projected)**2)) wing.vertical = False wing.symmetric = True wing.high_lift = True wing.dynamic_pressure_ratio = 1.0 # ------------------------------------------------------------------ # Flaps # ------------------------------------------------------------------ wing.flaps.chord = 0.20 wing.flaps.span_start = 0.1053 wing.flaps.span_end = 0.6842 wing.flaps.type = 'single_slotted' # add to vehicle vehicle.append_component(wing) # ------------------------------------------------------------------ # Horizontal Stabilizer # ------------------------------------------------------------------ wing = SUAVE.Components.Wings.Wing() wing.tag = 'horizontal_stabilizer' wing.aspect_ratio = 4.193 wing.sweeps.quarter_chord = 0.0 * Units.deg wing.thickness_to_chord = 0.12 wing.taper = 1.0 wing.span_efficiency = 0.733 wing.spans.projected = 3.3 * Units.meter wing.chords.root = 0.787 * Units.meter wing.chords.tip = 0.787 * Units.meter wing.chords.mean_aerodynamic = (wing.chords.root * (2.0 / 3.0) * ((1.0 + wing.taper + wing.taper**2.0) / (1.0 + wing.taper))) * Units.meter wing.areas.reference = 2.5971 * Units['meters**2'] wing.areas.exposed = 4.0 * Units['meters**2'] wing.areas.wetted = 4.0 * Units['meters**2'] wing.twists.root = 0.0 * Units.degrees wing.twists.tip = 0.0 * Units.degrees wing.origin = [7.789, 0.0, 0.3314] # meters wing.vertical = False wing.symmetric = True wing.dynamic_pressure_ratio = 0.9 # add to vehicle vehicle.append_component(wing) # ------------------------------------------------------------------ # Vertical Stabilizer # ------------------------------------------------------------------ wing = SUAVE.Components.Wings.Wing() wing.tag = 'vertical_stabilizer' wing.aspect_ratio = 1.407 wing.sweeps.quarter_chord = 38.75 * Units.deg wing.thickness_to_chord = 0.12 wing.taper = 1.0 wing.span_efficiency = -0.107 wing.spans.projected = 1.574 * Units.meter wing.chords.root = 1.2 * Units.meter wing.chords.tip = 0.497 * Units.meter wing.chords.mean_aerodynamic = (wing.chords.root * (2.0 / 3.0) * ((1.0 + wing.taper + wing.taper**2.0) / (1.0 + wing.taper))) * Units.meter wing.areas.reference = 1.761 * Units['meters**2'] wing.twists.root = 0.0 * Units.degrees wing.twists.tip = 0.0 * Units.degrees wing.origin = [7.25, 0, 0.497] # meters wing.vertical = True wing.symmetric = False wing.t_tail = False wing.dynamic_pressure_ratio = 1.0 # add to vehicle vehicle.append_component(wing) # ------------------------------------------------------------------ # Propellers Powered By Batteries # ------------------------------------------------------------------ # build network net = SUAVE.Components.Energy.Networks.Lift_Forward_Propulsor_Network_Lo_Fi( ) net.nacelle_diameter_lift = 0.08 * Units.meters net.nacelle_diameter_forward = 0.1732 * Units.meters net.engine_length_lift = 0.47244 * Units.meters net.engine_length_forward = 1.2 * Units.meters net.number_of_engines_lift = 12 net.number_of_engines_forward = 2 net.thrust_angle_lift = 0.0 * Units.degrees net.thrust_angle_forward = 0.0 * Units.degrees net.voltage = 461. net.areas_forward = Data() net.areas_forward.wetted = 1.1 * np.pi * net.nacelle_diameter_forward * net.engine_length_forward net.areas_lift = Data() net.areas_lift.wetted = 1.1 * np.pi * net.nacelle_diameter_forward * net.engine_length_lift # Component 1 - Tip ESC esc = SUAVE.Components.Energy.Distributors.Electronic_Speed_Controller() esc.efficiency = 0.95 # Gundlach for brushless motors net.esc_forward = esc # Component 1 - High Lift ESC esc = SUAVE.Components.Energy.Distributors.Electronic_Speed_Controller() esc.efficiency = 0.95 # Gundlach for brushless motors net.esc_lift = esc # Component 2 Tip Propeller prop = SUAVE.Components.Energy.Converters.Propeller_Lo_Fid() prop.propulsive_efficiency = 0.95 prop.tip_radius = 0.762 * Units.meter net.propeller_forward = prop # Component 2 High Lift Propeller prop = SUAVE.Components.Energy.Converters.Propeller_Lo_Fid() prop.propulsive_efficiency = 0.95 prop.tip_radius = 0.2880360 * Units.meter net.propeller_lift = prop # Component 3 Tip Motor motor = SUAVE.Components.Energy.Converters.Motor_Lo_Fid() kv = 300. * Units['rpm/volt'] # RPM/volt is standard motor.expected_current = 1000.0 motor = size_from_kv(motor, kv) motor.gear_ratio = 1. # Gear ratio, no gearbox motor.gearbox_efficiency = 1. # Gear box efficiency, no gearbox motor.motor_efficiency = 0.95 net.motor_forward = motor # Component 3 High Lift Motor motor = SUAVE.Components.Energy.Converters.Motor_Lo_Fid() kv = 3691. * Units['rpm/volt'] # RPM/volt is standard motor.expected_current = 1000.0 motor = size_from_kv(motor, kv) motor.gear_ratio = 1. # Gear ratio, no gearbox motor.gearbox_efficiency = 1. # Gear box efficiency, no gearbox motor.motor_efficiency = 0.95 net.motor_lift = motor # Component 4 - the Payload payload = SUAVE.Components.Energy.Peripherals.Payload() payload.power_draw = 50. * Units.watts payload.mass_properties.mass = 5.0 * Units.kg net.payload = payload # Component 5 - the Avionics avionics = SUAVE.Components.Energy.Peripherals.Avionics() avionics.power_draw = 50. * Units.watts net.avionics = avionics # Component 6 - the Battery bat = SUAVE.Components.Energy.Storages.Batteries.Constant_Mass.Lithium_Ion( ) bat.mass_properties.mass = 358.33 * Units.kg bat.specific_energy = 192.84 * Units.Wh / Units.kg bat.specific_power = 0.837 * Units.kW / Units.kg bat.resistance = 0.0153 bat.max_voltage = 11.078 initialize_from_mass(bat, bat.mass_properties.mass) net.battery = bat # ------------------------------------------------------------------ # Vehicle Definition Complete # ------------------------------------------------------------------ # add the energy network to the vehicle vehicle.append_component(net) #print vehicle return vehicle
def simulation_settings(vehicle): # grid conditions for downstream propeller grid_settings = Data() grid_settings.radius = vehicle.networks.prop_net.propeller.tip_radius grid_settings.hub_radius = vehicle.networks.prop_net.propeller.hub_radius grid_settings.Nr = 40 grid_settings.Na = 40 # cartesian grid specs grid_settings.Ny = 50 grid_settings.Nz = 50 grid_settings.grid_mode = 'cartesian' VLM_settings = Data() VLM_settings.number_spanwise_vortices = 10 VLM_settings.number_chordwise_vortices = 4 VLM_settings.use_surrogate = True VLM_settings.propeller_wake_model = False VLM_settings.model_fuselage = False VLM_settings.spanwise_cosine_spacing = True VLM_settings.number_of_wake_timesteps = 0. VLM_settings.leading_edge_suction_multiplier = 1. VLM_settings.initial_timestep_offset = 0. VLM_settings.wake_development_time = 0.5 return grid_settings, VLM_settings
def main(): weight_landing = 300000 * Units.lbs number_of_engines = 3. thrust_sea_level = 40000 * Units.force_pounds thrust_landing = 0.45 * thrust_sea_level noise = shevell(weight_landing, number_of_engines, thrust_sea_level, thrust_landing) actual = Data() actual.takeoff = 99.982372547196633 actual.side_line = 97.482372547196633 actual.landing = 105.69577388532885 error = Data() error.takeoff = (actual.takeoff - noise.takeoff) / actual.takeoff error.side_line = (actual.side_line - noise.side_line) / actual.side_line error.landing = (actual.landing - noise.landing) / actual.landing for k, v in list(error.items()): assert (np.abs(v) < 1e-6) return
def vehicle_setup(): # ------------------------------------------------------------------ # Initialize the Vehicle # ------------------------------------------------------------------ vehicle = SUAVE.Vehicle() vehicle.tag = 'Tecnam_P2006TElectric' # ------------------------------------------------------------------ # Vehicle-level Properties # ------------------------------------------------------------------ # mass properties vehicle.mass_properties.max_takeoff = 1400 * Units.kilogram vehicle.mass_properties.takeoff = 1400 * Units.kilogram vehicle.mass_properties.operating_empty = 1000 * Units.kilogram vehicle.mass_properties.max_zero_fuel = 1400 * Units.kilogram vehicle.mass_properties.cargo = 80 * Units.kilogram # envelope properties vehicle.envelope.ultimate_load = 5.7 vehicle.envelope.limit_load = 3.8 # basic parameters vehicle.reference_area = 64.4 * Units['meters**2'] vehicle.passengers = 4 vehicle.systems.control = "fully powered" vehicle.systems.accessories = "medium range" # ------------------------------------------------------------------ # Landing Gear # ------------------------------------------------------------------ # used for noise calculations #landing_gear = SUAVE.Components.Landing_Gear.Landing_Gear() #landing_gear.tag = "main_landing_gear" #landing_gear.main_tire_diameter = 0.423 * Units.m #landing_gear.nose_tire_diameter = 0.3625 * Units.m #landing_gear.main_strut_length = 0.4833 * Units.m #landing_gear.nose_strut_length = 0.3625 * Units.m #landing_gear.main_units = 2 #number of main landing gear units #landing_gear.nose_units = 1 #number of nose landing gear #landing_gear.main_wheels = 1 #number of wheels on the main landing gear #landing_gear.nose_wheels = 1 #number of wheels on the nose landing gear #vehicle.landing_gear = landing_gear # ------------------------------------------------------------------ # Fuselage # ------------------------------------------------------------------ fuselage = SUAVE.Components.Fuselages.Fuselage() fuselage.tag = 'fuselage' aux = time.time() fuselage.time = aux fuselage.number_coach_seats = vehicle.passengers fuselage.seats_abreast = 2 fuselage.seat_pitch = 0.995 * Units.meter fuselage.fineness.nose = 1.27 fuselage.fineness.tail = 1 #3.31 fuselage.lengths.nose = 1.16 * Units.meter fuselage.lengths.tail = 4.637 * Units.meter fuselage.lengths.cabin = 2.653 * Units.meter fuselage.lengths.total = 8.45 * Units.meter fuselage.lengths.fore_space = 0.0 * Units.meter fuselage.lengths.aft_space = 0.0 * Units.meter fuselage.width = 1.1 * Units.meter #1.22 fuselage.heights.maximum = 1.41 * Units.meter fuselage.effective_diameter = 2 * Units.meter fuselage.areas.side_projected = 7.46 * Units['meters**2'] fuselage.areas.wetted = 25.0 * Units['meters**2'] fuselage.areas.front_projected = 1.54 * Units['meters**2'] fuselage.differential_pressure = 10**5 * Units.pascal # Maximum differential pressure fuselage.heights.at_quarter_length = 1.077 * Units.meter fuselage.heights.at_three_quarters_length = 0.5 * Units.meter #0.621 * Units.meter fuselage.heights.at_wing_root_quarter_chord = 1.41 * Units.meter ## OpenVSP Design fuselage.OpenVSP_values = Data() # VSP uses degrees directly #MidFuselage1 Section fuselage.OpenVSP_values.midfus1 = Data() fuselage.OpenVSP_values.midfus1.z_pos = 0.03 #MidFuselage2 Section fuselage.OpenVSP_values.midfus2 = Data() fuselage.OpenVSP_values.midfus2.z_pos = 0.06 #MidFuselage3 Section fuselage.OpenVSP_values.midfus3 = Data() fuselage.OpenVSP_values.midfus3.z_pos = 0.04 #Tail Section fuselage.OpenVSP_values.tail = Data() fuselage.OpenVSP_values.tail.bottom = Data() fuselage.OpenVSP_values.tail.z_pos = 0.039 fuselage.OpenVSP_values.tail.bottom.angle = -20.0 fuselage.OpenVSP_values.tail.bottom.strength = 1 # add to vehicle vehicle.append_component(fuselage) # ------------------------------------------------------------------ # Main Wing # ------------------------------------------------------------------ wing = SUAVE.Components.Wings.Main_Wing() wing.tag = 'main_wing' wing.thickness_to_chord = 0.15 wing.taper = 0.9 wing.spans.projected = 9.4 * Units.meter wing.chords.root = 2. * Units.meter wing.chords.tip = wing.chords.root * wing.taper wing.chords.mean_aerodynamic = (wing.chords.root * (2.0 / 3.0) * ((1.0 + wing.taper + wing.taper**2.0) / (1.0 + wing.taper))) wing.areas.reference = (wing.chords.root + wing.chords.tip) * wing.spans.projected / 2 print wing.areas.reference basearea = wing.areas.reference wing.areas.wetted = 2.0 * wing.areas.reference wing.areas.exposed = wing.areas.wetted wing.areas.affected = wing.areas.wetted wing.twists.root = 0. * Units.degrees wing.twists.tip = 0. * Units.degrees wing.dihedral = 1. * Units.degrees wing.origin = [2.986, 0, 1.077] # meters wing.sweeps.leading_edge = 1.9 * Units.deg wing.aspect_ratio = (wing.spans.projected * wing.spans.projected) / wing.areas.reference wing.span_efficiency = 0.95 wing.vertical = False wing.symmetric = True wing.high_lift = True wing.dynamic_pressure_ratio = 1.0 ## Wing Segments # Root Segment #segment = SUAVE.Components.Wings.Segment() #segment.tag = 'root' #segment.percent_span_location = 0.0 #segment.twist = 0. * Units.deg #segment.root_chord_percent = 1. #segment.dihedral_outboard = 1. * Units.degrees #segment.sweeps.quarter_chord = 0. * Units.degrees #segment.thickness_to_chord = 0.15 #airfoil = SUAVE.Components.Wings.Airfoils.Airfoil() #airfoil.coordinate_file = '/Users/Bruno/Documents/Delft/Courses/2016-2017/Thesis/Code/Airfoils/naca642415.dat' #segment.append_airfoil(airfoil) #wing.Segments.append(segment) # Tip Segment #segment = SUAVE.Components.Wings.Segment() #segment.tag = 'tip' #segment.percent_span_location = 1.0 #segment.twist = 0. * Units.deg #segment.root_chord_percent = 1. #segment.dihedral_outboard = 1. * Units.degrees #segment.sweeps.quarter_chord = 0. * Units.degrees #segment.thickness_to_chord = 0.15 #airfoil = SUAVE.Components.Wings.Airfoils.Airfoil() #airfoil.coordinate_file = '/Users/Bruno/Documents/Delft/Courses/2016-2017/Thesis/Code/Airfoils/naca642415.dat' #segment.append_airfoil(airfoil) #wing.Segments.append(segment) # ------------------------------------------------------------------ # Flaps # ------------------------------------------------------------------ wing.flaps.chord = wing.chords.root * 0.15 wing.flaps.span_start = 0.3 * wing.spans.projected wing.flaps.span_end = 0.8 * wing.spans.projected wing.flaps.area = wing.flaps.chord * (wing.flaps.span_end - wing.flaps.span_start) wing.flaps.type = 'single_slotted' # add to vehicle vehicle.append_component(wing) # ------------------------------------------------------------------ # Horizontal Stabilizer # ------------------------------------------------------------------ wing = SUAVE.Components.Wings.Wing() wing.tag = 'horizontal_stabilizer' wing.aspect_ratio = 4.193 wing.areas.reference = 0.145 * basearea wing.sweeps.quarter_chord = 0.0 * Units.deg wing.thickness_to_chord = 0.12 wing.taper = 1.0 wing.span_efficiency = 0.97 wing.spans.projected = np.sqrt(wing.aspect_ratio * wing.areas.reference) wing.chords.root = wing.areas.reference / wing.spans.projected wing.chords.tip = wing.chords.root wing.chords.mean_aerodynamic = (wing.chords.root * (2.0 / 3.0) * ((1.0 + wing.taper + wing.taper**2.0) / (1.0 + wing.taper))) wing.areas.wetted = 2.0 * wing.areas.reference wing.areas.exposed = wing.areas.wetted wing.areas.affected = wing.areas.wetted wing.twists.root = 0.0 * Units.degrees wing.twists.tip = 0.0 * Units.degrees wing.origin = [7.789, 0.0, 0.3314] # meters wing.vertical = False wing.symmetric = True wing.dynamic_pressure_ratio = 0.9 # add to vehicle vehicle.append_component(wing) # ------------------------------------------------------------------ # Vertical Stabilizer # ------------------------------------------------------------------ wing = SUAVE.Components.Wings.Wing() wing.tag = 'vertical_stabilizer' wing.areas.reference = 0.099 * basearea wing.areas.wetted = 2.0 * wing.areas.reference wing.areas.exposed = wing.areas.wetted wing.areas.affected = wing.areas.wetted wing.aspect_ratio = 1.407 wing.sweeps.quarter_chord = 38.75 * Units.deg wing.thickness_to_chord = 0.12 wing.taper = 0.4142 wing.span_efficiency = 0.97 wing.spans.projected = np.sqrt(wing.aspect_ratio * wing.areas.reference) wing.chords.root = (2.0 * wing.areas.reference) / (wing.spans.projected * (1 + wing.taper)) wing.chords.tip = wing.chords.root * wing.taper wing.chords.mean_aerodynamic = (wing.chords.root * (2.0 / 3.0) * ((1.0 + wing.taper + wing.taper**2.0) / (1.0 + wing.taper))) wing.twists.root = 0.0 * Units.degrees wing.twists.tip = 0.0 * Units.degrees wing.origin = [7.25, 0, 0.497] # meters wing.vertical = True wing.symmetric = False wing.t_tail = False wing.dynamic_pressure_ratio = 1.0 # add to vehicle vehicle.append_component(wing) # ------------------------------------------------------------------ # Propellers Powered By Batteries # ------------------------------------------------------------------ # build network net = SUAVE.Components.Energy.Networks.Lift_Forward_Propulsor() net.nacelle_diameter_lift = 0.08 * Units.meters net.nacelle_diameter_forward = 0.1732 * Units.meters net.engine_length_lift = 0.47244 * Units.meters net.engine_length_forward = 1.2 * Units.meters net.number_of_engines_lift = 14 net.number_of_engines_forward = 2 net.thrust_angle_lift = 0.0 * Units.degrees net.thrust_angle_forward = 0.0 * Units.degrees net.voltage = 461. * Units['volt'] #461. net.areas_forward = Data() net.areas_forward.wetted = 1.1 * np.pi * net.nacelle_diameter_forward * net.engine_length_forward net.areas_lift = Data() net.areas_lift.wetted = 1.1 * np.pi * net.nacelle_diameter_forward * net.engine_length_lift net.number_of_engines = 1 net.nacelle_diameter = net.nacelle_diameter_forward net.areas = Data() net.areas.wetted = net.areas_lift.wetted net.engine_length = 1. # Component 1 - Tip ESC esc = SUAVE.Components.Energy.Distributors.Electronic_Speed_Controller() esc.efficiency = 0.95 # Gundlach for brushless motors net.esc_forward = esc # Component 1 - High Lift ESC esc = SUAVE.Components.Energy.Distributors.Electronic_Speed_Controller() esc.efficiency = 0.95 # Gundlach for brushless motors net.esc_lift = esc # Component 2 - Tip Propeller # Design the Propeller #prop_attributes = Data() #prop_attributes = propeller_design(prop_attributes) prop_forward = SUAVE.Components.Energy.Converters.Propeller() prop_forward.number_blades = 3.0 prop_forward.propulsive_efficiency = 0.85 prop_forward.freestream_velocity = 50.0 * Units['m/s'] # freestream m/s prop_forward.angular_velocity = 27500. * Units['rpm'] # For 20x10 prop prop_forward.tip_radius = 0.5 * Units.meter prop_forward.hub_radius = 0.085 * Units.meter prop_forward.design_Cl = 0.8 prop_forward.design_altitude = 14.0 * Units.km prop_forward.design_thrust = 0.0 * Units.newton prop_forward.design_power = 60000. * Units.watts prop_forward = propeller_design(prop_forward) net.propeller_forward = prop_forward # Component 2 - High Lift Propeller # Design the Propeller #prop_attributes = Data() prop_lift = SUAVE.Components.Energy.Converters.Propeller() prop_lift.number_blades = 5.0 prop_lift.propulsive_efficiency = 0.85 prop_lift.freestream_velocity = 1. * Units['m/s'] # freestream m/s prop_lift.angular_velocity = 2750 * Units['rpm'] # For 20x10 prop prop_lift.tip_radius = 0.26 * Units.meter #0.2880360 prop_lift.hub_radius = 0.07772400 * Units.meter prop_lift.design_Cl = 0.8 prop_lift.design_altitude = 0.0 * Units.meter prop_lift.design_thrust = 0.0 * Units.newton prop_lift.design_power = 10500. * Units.watts prop_lift = propeller_design(prop_lift) net.propeller_lift = prop_lift # Component 3 - Tip Motor motor = SUAVE.Components.Energy.Converters.Motor_Lo_Fid() #motor.resistance = 1. #motor.no_load_current = 7. * Units.ampere #motor.speed_constant = 11.9999 * Units['rpm/volt'] # RPM/volt converted to (rad/s)/volt #motor.propeller_radius = prop_forward.tip_radius #motor.propeller_Cp = prop_forward.Cp[0] #motor.gear_ratio = 12. # Gear ratio #motor.gearbox_efficiency = .98 # Gear box efficiency #motor.expected_current = 160. # Expected current #motor.mass_properties.mass = 9.0 * Units.kg #net.motor_forward = motor kv = 180. * Units['rpm/volt'] # RPM/volt is standard #motor.speed_constant = 0.0 motor = size_from_kv(motor, kv) motor.gear_ratio = 1. # Gear ratio, no gearbox motor.gearbox_efficiency = .98 # Gear box efficiency, no gearbox motor.motor_efficiency = 0.825 net.motor_forward = motor # Component 3 - High Lift Motor motor = SUAVE.Components.Energy.Converters.Motor_Lo_Fid() #motor.resistance = 0.008 #motor.no_load_current = 4.5 * Units.ampere #motor.speed_constant = 5800. * Units['rpm/volt'] # RPM/volt converted to (rad/s)/volt #motor.propeller_radius = prop_lift.tip_radius #motor.propeller_Cp = prop_lift.Cp[0] #motor.gear_ratio = 12. # Gear ratio #motor.gearbox_efficiency = .98 # Gear box efficiency #motor.expected_current = 25. # Expected current #motor.mass_properties.mass = 6.0 * Units.kg #net.motor_lift = motor kv = 90. * Units['rpm/volt'] # RPM/volt is standard #motor.speed_constant = 0.0 motor = size_from_kv(motor, kv) motor.gear_ratio = 1. # Gear ratio, no gearbox motor.gearbox_efficiency = .98 # Gear box efficiency, no gearbox motor.motor_efficiency = 0.825 net.motor_lift = motor # Component 4 - the Payload payload = SUAVE.Components.Energy.Peripherals.Payload() payload.power_draw = 50. * Units.watts payload.mass_properties.mass = 5.0 * Units.kg net.payload = payload # Component 5 - the Avionics avionics = SUAVE.Components.Energy.Peripherals.Avionics() avionics.power_draw = 50. * Units.watts net.avionics = avionics # Component 6 - the Battery bat = SUAVE.Components.Energy.Storages.Batteries.Constant_Mass.Lithium_Ion( ) bat.mass_properties.mass = 386.0 * Units.kg bat.specific_energy = 121.8 * Units.Wh / Units.kg #192.84 bat.specific_power = 0.312 * Units.kW / Units.kg #0.837 bat.resistance = 0.32 bat.max_voltage = 538. * Units['volt'] #10000. initialize_from_mass(bat, bat.mass_properties.mass) print bat.max_energy net.battery = bat # ------------------------------------------------------------------ # Vehicle Definition Complete # ------------------------------------------------------------------ # add the energy network to the vehicle vehicle.append_component(net) #now add weights objects vehicle.landing_gear = SUAVE.Components.Landing_Gear.Landing_Gear() vehicle.control_systems = SUAVE.Components.Physical_Component() vehicle.electrical_systems = SUAVE.Components.Physical_Component() vehicle.avionics = SUAVE.Components.Energy.Peripherals.Avionics() vehicle.passenger_weights = SUAVE.Components.Physical_Component() #vehicle.furnishings = SUAVE.Components.Physical_Component() #vehicle.apu = SUAVE.Components.Physical_Component() #vehicle.hydraulics = SUAVE.Components.Physical_Component() vehicle.optionals = SUAVE.Components.Physical_Component() vehicle.wings[ 'vertical_stabilizer'].rudder = SUAVE.Components.Physical_Component() #print vehicle return vehicle
def __defaults__(self): self.tag = 'structures' self.features = Data() self.settings = Data()
def evaluate_thrust(self, state): """ Calculate thrust given the current state of the vehicle Assumptions: None Source: N/A Inputs: state [state()] Outputs: results.thrust_force_vector [newtons] results.vehicle_mass_rate [kg/s] conditions.propulsion.primary_battery_draw [watts] conditions.propulsion.primary_battery_energy [joules] conditions.propulsion.auxiliary_battery_draw [watts] conditions.propulsion.auxiliary_battery_energy [joules] Properties Used: Defaulted values """ # unpack propulsor = self.propulsor primary_battery = self.primary_battery auxiliary_battery = self.auxiliary_battery conditions = state.conditions numerics = state.numerics results = propulsor.evaluate_thrust(state) Pe = results.power try: initial_energy = conditions.propulsion.primary_battery_energy initial_energy_auxiliary = conditions.propulsion.auxiliary_battery_energy if initial_energy[0][ 0] == 0: #beginning of segment; initialize battery primary_battery.current_energy = primary_battery.current_energy[ -1] * np.ones_like(initial_energy) auxiliary_battery.current_energy = auxiliary_battery.current_energy[ -1] * np.ones_like(initial_energy) except AttributeError: #battery energy not initialized, e.g. in takeoff primary_battery.current_energy = np.transpose( np.array( [primary_battery.current_energy[-1] * np.ones_like(Pe)])) auxiliary_battery.current_energy = np.transpose( np.array( [auxiliary_battery.current_energy[-1] * np.ones_like(Pe)])) pbat = -Pe / self.motor_efficiency pbat_primary = copy.copy(pbat) #prevent deep copy nonsense pbat_auxiliary = np.zeros_like(pbat) #print 'pbat=', pbat/10**6. #print 'max_power prim=', primary_battery.max_power/10**6. #print 'max_power aux=', auxiliary_battery.max_power/10**6. for i in range(len(pbat)): if pbat[i] < -primary_battery.max_power: #limit power output of primary_battery pbat_primary[ i] = -primary_battery.max_power #-power means discharge pbat_auxiliary[i] = pbat[i] - pbat_primary[i] elif pbat[ i] > primary_battery.max_power: #limit charging rate of battery pbat_primary[i] = primary_battery.max_power pbat_auxiliary[i] = pbat[i] - pbat_primary[i] if pbat_primary[ i] > 0: #don't allow non-rechargable battery to charge pbat_primary[i] = 0 pbat_auxiliary[i] = pbat[i] primary_battery_logic = Data() primary_battery_logic.power_in = pbat_primary primary_battery_logic.current = 90. #use 90 amps as a default for now; will change this for higher fidelity methods auxiliary_battery_logic = copy.copy(primary_battery_logic) auxiliary_battery_logic.power_in = pbat_auxiliary primary_battery.inputs = primary_battery_logic auxiliary_battery.inputs = auxiliary_battery_logic tol = 1e-6 primary_battery.energy_calc(numerics) auxiliary_battery.energy_calc(numerics) #allow for mass gaining batteries try: mdot_primary = find_mass_gain_rate( primary_battery, -(pbat_primary - primary_battery.resistive_losses)) except AttributeError: mdot_primary = np.zeros_like(results.thrust_force_vector[:, 0]) try: mdot_auxiliary = find_mass_gain_rate( auxiliary_battery, -(pbat_auxiliary - auxiliary_battery.resistive_losses)) except AttributeError: mdot_auxiliary = np.zeros_like(results.thrust_force_vector[:, 0]) mdot = mdot_primary + mdot_auxiliary mdot = np.reshape(mdot, np.shape(conditions.freestream.velocity)) #Pack the conditions for outputs primary_battery_draw = primary_battery.inputs.power_in primary_battery_energy = primary_battery.current_energy auxiliary_battery_draw = auxiliary_battery.inputs.power_in auxiliary_battery_energy = auxiliary_battery.current_energy conditions.propulsion.primary_battery_draw = primary_battery_draw conditions.propulsion.primary_battery_energy = primary_battery_energy conditions.propulsion.auxiliary_battery_draw = auxiliary_battery_draw conditions.propulsion.auxiliary_battery_energy = auxiliary_battery_energy results.vehicle_mass_rate = mdot return results
pressure = np.linspace(10**5, 10**6, test_num) pressure = pressure.reshape(test_num, 1) #specify the conditions at which to perform the aerodynamic analysis state.conditions.aerodynamics.angle_of_attack = AoA #angle_of_attacks state.conditions.freestream.mach_number = Mc #np.array([0.8]*test_num) state.conditions.freestream.density = rho #np.array([0.3804534]*test_num) state.conditions.freestream.dynamic_viscosity = mu #np.array([1.43408227e-05]*test_num) state.conditions.freestream.temperature = T #np.array([218.92391647]*test_num) state.conditions.freestream.pressure = pressure #np.array([23908.73408391]*test_num) #call the aero model results = aerodynamics.evaluate(state) #build a polar for the markup aero polar = Data() CL = results.lift.total CD = results.drag.total polar.lift = CL polar.drag = CD ##load old results #old_polar = SUAVE.Input_Output.load('polar_M8.pkl') #('polar_old2.pkl') #CL_old = old_polar.lift #CD_old = old_polar.drag #plot the results plt.figure("Drag Polar") axes = plt.gca() axes.plot(CD, CL, 'bo-') #,CD_old,CL_old,'*') axes.set_xlabel('$C_D$')
def empty(config, contingency_factor=1.1, speed_of_sound=340.294, max_tip_mach=0.65, disk_area_factor=1.15, safety_factor=1.5, max_thrust_to_weight_ratio=1.1, max_g_load=3.8, motor_efficiency=0.85 * 0.98): """mass = SUAVE.Methods.Weights.Buildups.EVTOL.empty( config, speed_of_sound = 340.294, max_tip_mach = 0.65, disk_area_factor = 1.15, max_thrust_to_weight_ratio = 1.1, motor_efficiency = 0.85 * 0.98) Calculates the empty vehicle mass for an EVTOL-type aircraft including seats, avionics, servomotors, ballistic recovery system, rotor and hub assembly, transmission, and landing gear. Incorporates the results of the following common-use buildups: fuselage.py prop.py wing.py wiring.py Sources: Project Vahana Conceptual Trade Study https://github.com/VahanaOpenSource Inputs: config: SUAVE Config Data Stucture speed_of_sound: Local Speed of Sound [m/s] max_tip_mach: Allowable Tip Mach Number [Unitless] disk_area_factor: Inverse of Disk Area Efficiency [Unitless] max_thrust_to_weight_ratio: Allowable Thrust to Weight Ratio [Unitless] motor_efficiency: Motor Efficiency [Unitless] Outpus: outputs: Data Dictionary of Component Masses [kg] Output data dictionary has the following book-keeping hierarchical structure: Output Total. Empty. Structural. Fuselage Wings Landing Gear Rotors Hubs Seats Battery Motors Servo Systems. Avionics ECS - Environmental Control System BRS - Ballistic Recovery System Wiring - Aircraft Electronic Wiring Payload """ # Set up data structures for SUAVE weight methods output = Data() output.lift_rotors = 0.0 output.propellers = 0.0 output.lift_rotor_motors = 0.0 output.propeller_motors = 0.0 output.battery = 0.0 output.payload = 0.0 output.servos = 0.0 output.hubs = 0.0 output.BRS = 0.0 config.payload.passengers = SUAVE.Components.Physical_Component() config.payload.baggage = SUAVE.Components.Physical_Component() config.payload.cargo = SUAVE.Components.Physical_Component() control_systems = SUAVE.Components.Physical_Component() electrical_systems = SUAVE.Components.Physical_Component() furnishings = SUAVE.Components.Physical_Component() air_conditioner = SUAVE.Components.Physical_Component() fuel = SUAVE.Components.Physical_Component() apu = SUAVE.Components.Physical_Component() hydraulics = SUAVE.Components.Physical_Component() avionics = SUAVE.Components.Energy.Peripherals.Avionics() optionals = SUAVE.Components.Physical_Component() # assign components to vehicle config.systems.control_systems = control_systems config.systems.electrical_systems = electrical_systems config.systems.avionics = avionics config.systems.furnishings = furnishings config.systems.air_conditioner = air_conditioner config.systems.fuel = fuel config.systems.apu = apu config.systems.hydraulics = hydraulics config.systems.optionals = optionals #------------------------------------------------------------------------------- # Fixed Weights #------------------------------------------------------------------------------- MTOW = config.mass_properties.max_takeoff output.seats = config.passengers * 15. * Units.kg output.passengers = config.passengers * 70. * Units.kg output.avionics = 15. * Units.kg output.landing_gear = MTOW * 0.02 * Units.kg output.ECS = config.passengers * 7. * Units.kg # Inputs and other constants tipMach = max_tip_mach k = disk_area_factor ToverW = max_thrust_to_weight_ratio eta = motor_efficiency rho_ref = 1.225 maxVTip = speed_of_sound * tipMach # Prop Tip Velocity maxLift = MTOW * ToverW * 9.81 # Maximum Thrust AvgBladeCD = 0.012 # Average Blade CD # Select a length scale depending on what kind of vehicle this is length_scale = 1. nose_length = 0. # Check if there is a fuselage C = SUAVE.Components if len(config.fuselages) == 0.: for w in config.wings: if isinstance(w, C.Wings.Main_Wing): b = w.chords.root if b > length_scale: length_scale = b nose_length = 0.25 * b else: for fuse in config.fuselages: nose = fuse.lengths.nose length = fuse.lengths.total if length > length_scale: length_scale = length nose_length = nose #------------------------------------------------------------------------------- # Environmental Control System #------------------------------------------------------------------------------- config.systems.air_conditioner.origin[0][0] = 0.51 * length_scale config.systems.air_conditioner.mass_properties.mass = output.ECS #------------------------------------------------------------------------------- # Network Weight #------------------------------------------------------------------------------- for network in config.networks: #------------------------------------------------------------------------------- # Battery Weight #------------------------------------------------------------------------------- network.battery.origin[0][0] = 0.51 * length_scale network.battery.mass_properties.center_of_gravity[0][0] = 0.0 output.battery += network.battery.mass_properties.mass * Units.kg #------------------------------------------------------------------------------- # Payload Weight #------------------------------------------------------------------------------- network.payload.origin[0][0] = 0.51 * length_scale network.payload.mass_properties.center_of_gravity[0][0] = 0.0 output.payload += network.payload.mass_properties.mass * Units.kg #------------------------------------------------------------------------------- # Avionics Weight #------------------------------------------------------------------------------- network.avionics.origin[0][0] = 0.4 * nose_length network.avionics.mass_properties.center_of_gravity[0][0] = 0.0 network.avionics.mass_properties.mass = output.avionics #------------------------------------------------------------------------------- # Servo, Hub and BRS Weights #------------------------------------------------------------------------------- lift_rotor_hub_weight = 4. * Units.kg prop_hub_weight = MTOW * 0.04 * Units.kg lift_rotor_BRS_weight = 16. * Units.kg #------------------------------------------------------------------------------- # Rotor, Propeller, parameters for sizing #------------------------------------------------------------------------------- if isinstance(network, Lift_Cruise): # Total number of rotors and propellers nLiftRotors = network.number_of_lift_rotor_engines nThrustProps = network.number_of_propeller_engines props = network.propellers rots = network.lift_rotors prop_motors = network.propeller_motors rot_motors = network.lift_rotor_motors elif isinstance(network, Battery_Propeller): # Total number of rotors and propellers if network.number_of_lift_rotor_engines == None: nLiftRotors = 0 else: nLiftRotors = network.number_of_lift_rotor_engines rots = network.lift_rotors rot_motors = network.lift_rotor_motors if network.number_of_propeller_engines == None: nThrustProps = 0 else: nThrustProps = network.number_of_propeller_engines props = network.propellers prop_motors = network.propeller_motors else: raise NotImplementedError( """eVTOL weight buildup only supports the Battery Propeller and Lift Cruise energy networks.\n Weight buildup will not return information on propulsion system.""", RuntimeWarning) nProps = int(nLiftRotors + nThrustProps) if nProps > 1: prop_BRS_weight = 16. * Units.kg else: prop_BRS_weight = 0. * Units.kg prop_servo_weight = 0.0 if nThrustProps > 0: if network.identical_propellers: # Get reference properties for sizing from first propeller (assumes identical) proprotor = props[list(props.keys())[0]] propmotor = prop_motors[list(prop_motors.keys())[0]] rTip_ref = proprotor.tip_radius bladeSol_ref = proprotor.blade_solidity if proprotor.variable_pitch: prop_servo_weight = 5.2 * Units.kg # Compute and add propeller weights propeller_mass = prop(proprotor, maxLift / 5.) * Units.kg output.propellers += nThrustProps * propeller_mass output.propeller_motors += nThrustProps * propmotor.mass_properties.mass proprotor.mass_properties.mass = propeller_mass + prop_hub_weight + prop_servo_weight else: for idx, propeller in enumerate(network.propellers): proprotor = propeller propmotor = prop_motors[list(prop_motors.keys())[idx]] rTip_ref = proprotor.tip_radius bladeSol_ref = proprotor.blade_solidity if proprotor.variable_pitch: prop_servo_weight = 5.2 * Units.kg # Compute and add propeller weights propeller_mass = prop(proprotor, maxLift / 5.) * Units.kg output.propellers += propeller_mass output.propeller_motors += propmotor.mass_properties.mass proprotor.mass_properties.mass = propeller_mass + prop_hub_weight + prop_servo_weight lift_rotor_servo_weight = 0.0 if nLiftRotors > 0: if network.identical_lift_rotors: # Get reference properties for sizing from first lift_rotor (assumes identical) liftrotor = rots[list(rots.keys())[0]] liftmotor = rot_motors[list(rot_motors.keys())[0]] rTip_ref = liftrotor.tip_radius bladeSol_ref = liftrotor.blade_solidity if liftrotor.variable_pitch: lift_rotor_servo_weight = 0.65 * Units.kg # Compute and add lift_rotor weights lift_rotor_mass = prop( liftrotor, maxLift / max(nLiftRotors - 1, 1)) * Units.kg output.lift_rotors += nLiftRotors * lift_rotor_mass output.lift_rotor_motors += nLiftRotors * liftmotor.mass_properties.mass liftrotor.mass_properties.mass = lift_rotor_mass + lift_rotor_hub_weight + lift_rotor_servo_weight else: for idx, lift_rotor in enumerate(network.lift_rotors): liftrotor = lift_rotor liftmotor = rot_motors[list(rot_motors.keys())[idx]] rTip_ref = liftrotor.tip_radius bladeSol_ref = liftrotor.blade_solidity if liftrotor.variable_pitch: lift_rotor_servo_weight = 0.65 * Units.kg # Compute and add lift_rotor weights lift_rotor_mass = prop(liftrotor, maxLift / max(nLiftRotors - 1, 1)) * Units.kg output.lift_rotors += lift_rotor_mass output.lift_rotor_motors += liftmotor.mass_properties.mass liftrotor.mass_properties.mass = lift_rotor_mass + lift_rotor_hub_weight + lift_rotor_servo_weight # Add associated weights output.servos += (nLiftRotors * lift_rotor_servo_weight + nThrustProps * prop_servo_weight) output.hubs += (nLiftRotors * lift_rotor_hub_weight + nThrustProps * prop_hub_weight) output.BRS += (prop_BRS_weight + lift_rotor_BRS_weight) maxLiftPower = 1.15 * maxLift * ( k * np.sqrt(maxLift / (2 * rho_ref * np.pi * rTip_ref**2)) + bladeSol_ref * AvgBladeCD / 8 * maxVTip**3 / (maxLift / (rho_ref * np.pi * rTip_ref**2))) # Tail Rotor if nLiftRotors == 1: # this assumes that the vehicle is an electric helicopter with a tail rotor maxLiftOmega = maxVTip / rTip_ref maxLiftTorque = maxLiftPower / maxLiftOmega tailrotor = next(iter(network.lift_rotors)) output.tail_rotor = prop(tailrotor, 1.5 * maxLiftTorque / (1.25 * rTip_ref)) * 0.2 * Units.kg output.lift_rotors += output.tail_rotor # sum motor weight output.motors = output.lift_rotor_motors + output.propeller_motors #------------------------------------------------------------------------------- # Wing and Motor Wiring Weight #------------------------------------------------------------------------------- total_wing_weight = 0.0 total_wiring_weight = 0.0 output.wings = Data() output.wiring = Data() for w in config.wings: if w.symbolic: wing_weight = 0 else: wing_weight = wing(w, config, maxLift / 5, safety_factor=safety_factor, max_g_load=max_g_load) wing_tag = w.tag output.wings[wing_tag] = wing_weight w.mass_properties.mass = wing_weight total_wing_weight = total_wing_weight + wing_weight # wiring weight wiring_weight = wiring(w, config, maxLiftPower / (eta * nProps)) * Units.kg total_wiring_weight = total_wiring_weight + wiring_weight output.wiring = total_wiring_weight output.total_wing_weight = total_wing_weight #------------------------------------------------------------------------------- # Landing Gear Weight #------------------------------------------------------------------------------- if not hasattr(config.landing_gear, 'nose'): config.landing_gear.nose = SUAVE.Components.Landing_Gear.Nose_Landing_Gear( ) config.landing_gear.nose.mass = 0.0 if not hasattr(config.landing_gear, 'main'): config.landing_gear.main = SUAVE.Components.Landing_Gear.Main_Landing_Gear( ) config.landing_gear.main.mass = output.landing_gear #------------------------------------------------------------------------------- # Fuselage Weight #------------------------------------------------------------------------------- output.fuselage = fuselage(config) * Units.kg config.fuselages.fuselage.mass_properties.center_of_gravity[0][ 0] = .45 * config.fuselages.fuselage.lengths.total config.fuselages.fuselage.mass_properties.mass = output.fuselage + output.passengers + output.seats +\ output.wiring + output.BRS #------------------------------------------------------------------------------- # Pack Up Outputs #------------------------------------------------------------------------------- output.structural = (output.lift_rotors + output.propellers + output.hubs + output.fuselage + output.landing_gear + output.total_wing_weight) * Units.kg output.empty = (contingency_factor * (output.structural + output.seats + output.avionics +output.ECS +\ output.motors + output.servos + output.wiring + output.BRS) + output.battery) *Units.kg output.total = output.empty + output.payload + output.passengers return output
def __defaults__(self): """ This sets the default solver flow. Anything in here can be modified after initializing a segment. Assumptions: None Source: N/A Inputs: None Outputs: None Properties Used: None """ # -------------------------------------------------------------- # User inputs # -------------------------------------------------------------- self.ground_incline = 0.0 self.friction_coefficient = 0.04 self.throttle = None self.velocity_start = 0.0 self.velocity_end = 0.0 self.time = 0.01 # -------------------------------------------------------------- # State # -------------------------------------------------------------- # conditions self.state.conditions.update(Conditions.Aerodynamics()) # initials and unknowns ones_row = self.state.ones_row self.state.residuals.acceleration_x = ones_row(1) * 0.0 # Specific ground things self.state.conditions.ground = Data() self.state.conditions.ground.incline = ones_row(1) * 0.0 self.state.conditions.ground.friction_coefficient = ones_row(1) * 0.0 self.state.conditions.frames.inertial.ground_force_vector = ones_row( 3) * 0.0 # -------------------------------------------------------------- # The Solving Process # -------------------------------------------------------------- # -------------------------------------------------------------- # Initialize - before iteration # -------------------------------------------------------------- initialize = self.process.initialize initialize.expand_state = Methods.expand_state initialize.differentials = Methods.Common.Numerics.initialize_differentials_dimensionless initialize.conditions = Methods.Ground.Common.initialize_conditions # -------------------------------------------------------------- # Converge - starts iteration # -------------------------------------------------------------- converge = self.process.converge converge.converge_root = Methods.converge_root # -------------------------------------------------------------- # Iterate - this is iterated # -------------------------------------------------------------- iterate = self.process.iterate # Update Initials iterate.initials = Process() iterate.initials.time = Methods.Common.Frames.initialize_time iterate.initials.weights = Methods.Common.Weights.initialize_weights iterate.initials.inertial_position = Methods.Common.Frames.initialize_inertial_position iterate.initials.planet_position = Methods.Common.Frames.initialize_planet_position # Unpack Unknowns iterate.unknowns = Process() iterate.unknowns.mission = Methods.Ground.Common.unpack_unknowns # Update Conditions iterate.conditions = Process() iterate.conditions.differentials = Methods.Common.Numerics.update_differentials_time iterate.conditions.altitude = Methods.Common.Aerodynamics.update_altitude iterate.conditions.atmosphere = Methods.Common.Aerodynamics.update_atmosphere iterate.conditions.gravity = Methods.Common.Weights.update_gravity iterate.conditions.freestream = Methods.Common.Aerodynamics.update_freestream iterate.conditions.orientations = Methods.Common.Frames.update_orientations iterate.conditions.propulsion = Methods.Common.Energy.update_thrust iterate.conditions.aerodynamics = Methods.Common.Aerodynamics.update_aerodynamics iterate.conditions.stability = Methods.Common.Aerodynamics.update_stability iterate.conditions.weights = Methods.Common.Weights.update_weights iterate.conditions.forces_ground = Methods.Ground.Common.compute_ground_forces iterate.conditions.forces = Methods.Ground.Common.compute_forces iterate.conditions.planet_position = Methods.Common.Frames.update_planet_position # Solve Residuals iterate.residuals = Process() iterate.residuals.total_forces = Methods.Ground.Common.solve_residuals # -------------------------------------------------------------- # Finalize - after iteration # -------------------------------------------------------------- finalize = self.process.finalize # Post Processing finalize.post_process = Process() finalize.post_process.inertial_position = Methods.Common.Frames.integrate_inertial_horizontal_position finalize.post_process.stability = Methods.Common.Aerodynamics.update_stability return
def evaluate_thrust(self, state): """ Calculate thrust given the current state of the vehicle Assumptions: Source: N/A Inputs: state [state()] Outputs: results.thrust_force_vector [newtons] results.vehicle_mass_rate [kg/s] conditions.propulsion: rpm [radians/sec] propeller_torque [N-M] power [W] Properties Used: Defaulted values """ # unpack conditions = state.conditions engine = self.engine propeller = self.propeller num_engines = self.number_of_engines # Throttle the engine engine.inputs.speed = state.conditions.propulsion.rpm * Units.rpm conditions.propulsion.combustion_engine_throttle = conditions.propulsion.throttle # Run the engine engine.power(conditions) power_output = engine.outputs.power sfc = engine.outputs.power_specific_fuel_consumption mdot = engine.outputs.fuel_flow_rate torque = engine.outputs.torque # link propeller.inputs.omega = state.conditions.propulsion.rpm * Units.rpm propeller.thrust_angle = self.thrust_angle # step 4 F, Q, P, Cp, outputs, etap = propeller.spin(conditions) # Check to see if magic thrust is needed eta = conditions.propulsion.throttle P[eta > 1.0] = P[eta > 1.0] * eta[eta > 1.0] F[eta > 1.0] = F[eta > 1.0] * eta[eta > 1.0] # link propeller.outputs = outputs # Pack the conditions for outputs a = conditions.freestream.speed_of_sound R = propeller.tip_radius rpm = engine.inputs.speed / Units.rpm conditions.propulsion.rpm = rpm conditions.propulsion.propeller_torque = Q conditions.propulsion.power = P conditions.propulsion.propeller_tip_mach = (R * rpm * Units.rpm) / a conditions.propulsion.motor_torque = torque # noise outputs.number_of_engines = num_engines conditions.noise.sources.propeller = outputs # Create the outputs F = num_engines * F * [ np.cos(self.thrust_angle), 0, -np.sin(self.thrust_angle) ] F_mag = np.atleast_2d(np.linalg.norm(F, axis=1)) conditions.propulsion.disc_loading = (F_mag.T) / ( num_engines * np.pi * (R / Units.feet)**2) # N/m^2 conditions.propulsion.power_loading = (F_mag.T) / (P) # N/W results = Data() results.thrust_force_vector = F results.vehicle_mass_rate = mdot return results
def setup(): nexus = Nexus() problem = Data() nexus.optimization_problem = problem # ------------------------------------------------------------------- # Inputs # ------------------------------------------------------------------- # [ tag , initial, (lb,ub) , scaling , units ] problem.inputs = np.array([ ['wing_area', 95, (90., 130.), 100., Units.meter**2], ['cruise_altitude', 11, (9, 14.), 10., Units.km], ]) # ------------------------------------------------------------------- # Objective # ------------------------------------------------------------------- # throw an error if the user isn't specific about wildcards # [ tag, scaling, units ] problem.objective = np.array([['fuel_burn', 10000, Units.kg]]) # ------------------------------------------------------------------- # Constraints # ------------------------------------------------------------------- # [ tag, sense, edge, scaling, units ] problem.constraints = np.array([ ['design_range_fuel_margin', '>', 0., 1E-1, Units.less], #fuel margin defined here as fuel ]) # ------------------------------------------------------------------- # Aliases # ------------------------------------------------------------------- # [ 'alias' , ['data.path1.name','data.path2.name'] ] problem.aliases = [ [ 'wing_area', [ 'vehicle_configurations.*.wings.main_wing.areas.reference', 'vehicle_configurations.*.reference_area' ] ], ['cruise_altitude', 'missions.base.segments.climb_5.altitude_end'], ['fuel_burn', 'summary.base_mission_fuelburn'], ['design_range_fuel_margin', 'summary.max_zero_fuel_margin'], ] # ------------------------------------------------------------------- # Vehicles # ------------------------------------------------------------------- nexus.vehicle_configurations = Vehicles.setup() # ------------------------------------------------------------------- # Analyses # ------------------------------------------------------------------- nexus.analyses = Analyses.setup(nexus.vehicle_configurations) # ------------------------------------------------------------------- # Missions # ------------------------------------------------------------------- nexus.missions = Missions.setup(nexus.analyses) # ------------------------------------------------------------------- # Procedure # ------------------------------------------------------------------- nexus.procedure = Procedure.setup() # ------------------------------------------------------------------- # Summary # ------------------------------------------------------------------- nexus.summary = Data() nexus.total_number_of_iterations = 0 return nexus
def windmilling_drag(geometry, state): """Computes windmilling drag for turbofan engines Assumptions: None Source: http://www.dept.aoe.vt.edu/~mason/Mason_f/AskinThesis2002_13.pdf Inputs: geometry. max_mach_operational [Unitless] reference_area [m^2] wings.sref [m^2] propulsors. areas.wetted [m^2] nacelle_diameter [m^2] engine_length [m^2] Outputs: windmilling_drag_coefficient [Unitless] Properties Used: N/A """ # ============================================== # Unpack # ============================================== vehicle = geometry # Defining reference area if vehicle.reference_area: reference_area = vehicle.reference_area else: n_wing = 0 for wing in vehicle.wings: if not isinstance(wing, Wings.Main_Wing): continue n_wing = n_wing + 1 reference_area = wing.sref if n_wing > 1: print( ' More than one Main_Wing in the vehicle. Last one will be considered.' ) elif n_wing == 0: print('No Main_Wing defined! Using the 1st wing found') for wing in vehicle.wings: if not isinstance(wing, Wings.Wing): continue reference_area = wing.sref break # getting geometric data from engine (estimating when not available) for idx, propulsor in enumerate(vehicle.propulsors): try: swet_nac = propulsor.areas.wetted except: try: D_nac = propulsor.nacelle_diameter if propulsor.engine_length != 0.: l_nac = propulsor.engine_length else: try: MMO = vehicle.max_mach_operational except: MMO = 0.84 D_nac_in = D_nac / Units.inches l_nac = (2.36 * D_nac_in - 0.01 * (D_nac_in * MMO)**2) * Units.inches except AttributeError: print( 'Error calculating windmilling drag. Engine dimensions missing.' ) swet_nac = 5.62 * D_nac * l_nac # Compute windmilling_drag_coefficient = 0.007274 * swet_nac / reference_area # dump data to state windmilling_result = Data( wetted_area=swet_nac, windmilling_drag_coefficient=windmilling_drag_coefficient, ) state.conditions.aerodynamics.drag_breakdown.windmilling_drag = windmilling_result return windmilling_drag_coefficient
def evaluate_thrust(self, state): """ Calculate thrust given the current state of the vehicle Assumptions: Source: N/A Inputs: state [state()] Outputs: results.thrust_force_vector [newtons] results.vehicle_mass_rate [kg/s] conditions.propulsion: rpm [radians/sec] propeller_torque [N-M] power [W] Properties Used: Defaulted values """ # unpack conditions = state.conditions engines = self.engines propellers = self.propellers num_engines = self.number_of_engines rpm = conditions.propulsion.rpm # Unpack conditions a = conditions.freestream.speed_of_sound # How many evaluations to do if self.identical_propellers: n_evals = 1 factor = num_engines * 1 else: n_evals = int(num_engines) factor = 1. # Setup conditions ones_row = conditions.ones_row conditions.propulsion.disc_loading = ones_row(n_evals) conditions.propulsion.power_loading = ones_row(n_evals) conditions.propulsion.propeller_torque = ones_row(n_evals) conditions.propulsion.propeller_tip_mach = ones_row(n_evals) conditions.propulsion.combustion_engine_throttle = ones_row(n_evals) # Setup numbers for iteration total_thrust = 0. * state.ones_row(3) total_power = 0. mdot = 0. for ii in range(n_evals): # Unpack the engine and props engine_key = list(engines.keys())[ii] prop_key = list(propellers.keys())[ii] engine = self.engines[engine_key] prop = self.propellers[prop_key] # Run the propeller to get the power prop.inputs.pitch_command = conditions.propulsion.throttle - 0.5 prop.inputs.omega = rpm # step 4 F, Q, P, Cp, outputs, etap = prop.spin(conditions) # Run the engine to calculate the throttle setting and the fuel burn engine.inputs.power = P engine.calculate_throttle(conditions) # Create the outputs R = prop.tip_radius mdot = mdot + engine.outputs.fuel_flow_rate * factor F_mag = np.atleast_2d(np.linalg.norm(F, axis=1)) engine_throttle = engine.outputs.throttle total_thrust = total_thrust + F * factor total_power = total_power + P * factor # Pack the conditions conditions.propulsion.propeller_torque[:, ii] = Q[:, 0] conditions.propulsion.propeller_tip_mach[:, ii] = ( R * rpm[:, 0] * Units.rpm) / a[:, 0] conditions.propulsion.disc_loading[:, ii] = (F_mag[:, 0]) / ( np.pi * (R**2)) # N/m^2 conditions.propulsion.power_loading[:, ii] = (F_mag[:, 0]) / ( P[:, 0]) # N/W conditions.propulsion.combustion_engine_throttle = engine_throttle conditions.propulsion.propeller_efficiency = etap[:, 0] conditions.noise.sources.propellers[prop.tag] = outputs # Create the outputs conditions.propulsion.power = total_power results = Data() results.thrust_force_vector = F results.vehicle_mass_rate = mdot return results
def setup(): nexus = Nexus() problem = Data() nexus.optimization_problem = problem # ------------------------------------------------------------------- # Inputs # ------------------------------------------------------------------- # [ tag , initial, (lb,ub) , scaling , units] problem.inputs = np.array([ ['wing_area', 61., (61., 61.), 61., Units.meter**2], ['aspect_ratio', 12., (12., 12.), 12.0, Units.less], ['taper_ratio', 0.53, (0.53, 0.53), 0.53, Units.less], ['t_c_ratio', 0.15, (0.15, 0.15), 0.15, Units.less], ['sweep_angle', 3., (3.0, 3.0), 3.0, Units.deg], [ 'cruise_range', 331.5017418, (310., 350.), 331.3, Units.nautical_miles ], ['MTOW', 23009.2657571, (21000., 25000.), 23000., Units.kg], ['MZFW_ratio', 0.912696041, (0.8, 0.98), 1., Units.less], ]) # ------------------------------------------------------------------- # Objective # ------------------------------------------------------------------- # throw an error if the user isn't specific about wildcards # [ tag, scaling, units ] problem.objective = np.array([ # ['objective', 1., Units.less], ['Nothing', 1., Units.less], ]) # ------------------------------------------------------------------- # Constraints # ------------------------------------------------------------------- # [ tag, sense, edge, scaling, units ] # CONSTRAINTS ARE SET TO BE BIGGER THAN ZERO, SEE PROCEDURE (SciPy's SLSQP optimization algorithm assumes this form) problem.constraints = np.array([ ['mzfw_consistency', '=', 0., 1000., Units.kg], # MZFW consistency ['fuel_margin', '=', 0., 1000., Units.kg], # fuel margin defined here as fuel # ['Throttle_min', '>', 0., 1., Units.less], # ['Throttle_max', '>', 0., 1., Units.less], # ['tofl_mtow_margin', '>', 0., 100., Units.m], # take-off field length ['design_range_ub', '>', 0., 10., Units.nautical_miles], # Range consistency ['design_range_lb', '>', 0., 10., Units.nautical_miles], # Range consistency # ['time_to_climb', '>', 0., 10., Units.min], # Time to climb consistency # ['climb_gradient', '>', 0., 1., Units.less], # second segment climb gradient # ['lfl_mlw_margin', '>', 0., 100., Units.m], # landing field length # ['max_fuel_margin', '>', 0., 100., Units.kg], # max fuel margin # ['TOW_HH_margin', '>', 0., 100., Units.kg], # TOW for Hot and High ]) # ------------------------------------------------------------------- # Aliases # ------------------------------------------------------------------- # [ 'alias' , ['data.path1.name','data.path2.name'] ] problem.aliases = [ [ 'wing_area', [ 'vehicle_configurations.*.wings.main_wing.areas.reference', 'vehicle_configurations.*.reference_area' ] ], [ 'aspect_ratio', 'vehicle_configurations.*.wings.main_wing.aspect_ratio' ], [ 't_c_ratio', 'vehicle_configurations.*.wings.main_wing.thickness_to_chord' ], [ 'sweep_angle', 'vehicle_configurations.*.wings.main_wing.sweeps.quarter_chord' ], ['cruise_range', 'missions.base.segments.cruise.distance'], ['fuel_margin', 'summary.fuel_margin'], ['Throttle_min', 'summary.throttle_min'], ['Throttle_max', 'summary.throttle_max'], ['tofl_mtow_margin', 'summary.takeoff_field_length_margin'], ['mzfw_consistency', 'summary.mzfw_consistency'], ['design_range_ub', 'summary.design_range_ub'], ['design_range_lb', 'summary.design_range_lb'], ['time_to_climb', 'summary.time_to_climb'], ['climb_gradient', 'summary.climb_gradient'], ['lfl_mlw_margin', 'summary.lfl_mlw_margin'], ['max_fuel_margin', 'summary.max_fuel_margin'], ['TOW_HH_margin', 'summary.TOW_HH_margin'], [ 'MTOW', [ 'vehicle_configurations.*.mass_properties.max_takeoff', 'vehicle_configurations.*.mass_properties.takeoff' ] ], ['MZFW_ratio', 'summary.MZFW_ratio'], ['beta', 'vehicle_configurations.base.wings.main_wing.beta'], ['objective', 'summary.objective'], ['Nothing', 'summary.nothing'], ['taper_ratio', 'vehicle_configurations.*.wings.main_wing.taper'], ] # ------------------------------------------------------------------- # Vehicles # ------------------------------------------------------------------- nexus.vehicle_configurations = Vehicles.setup() # ------------------------------------------------------------------- # Analyses # ------------------------------------------------------------------- nexus.analyses = Analyses.setup(nexus.vehicle_configurations) nexus.analyses.vehicle = Data() nexus.analyses.vehicle = nexus.vehicle_configurations.base # ------------------------------------------------------------------- # Missions # ------------------------------------------------------------------- nexus.missions = Missions.setup(nexus.analyses) # ------------------------------------------------------------------- # Procedure # ------------------------------------------------------------------- nexus.procedure = Procedure.setup() # ------------------------------------------------------------------- # Summary # ------------------------------------------------------------------- nexus.summary = Data() nexus.total_number_of_iterations = 0 nexus.beta = Data() nexus.beta = 1. return nexus
# store to outputs self.outputs.power = output_power self.outputs.power_specific_fuel_consumption = BSFC self.outputs.fuel_flow_rate = fuel_flow_rate self.outputs.torque = torque return self.outputs if __name__ == '__main__': import numpy as np import pylab as plt import SUAVE from SUAVE.Core import Units, Data conditions = Data() atmo = SUAVE.Analyses.Atmospheric.US_Standard_1976() ICE = Internal_Combustion_Engine() ICE.sea_level_power = 250.0 * Units.horsepower ICE.flat_rate_altitude = 5000. * Units.ft ICE.speed = 2200. # rpm ICE.throttle = 1.0 PSLS = 1.0 delta_isa = 0.0 i = 0 altitude = list() rho = list() sigma = list() Pavailable = list() torque = list() for h in range(0, 25000, 500):
def estimate_take_off_field_length(vehicle, analyses, airport, compute_2nd_seg_climb=0): """ Computes the takeoff field length for a given vehicle configuration in a given airport. Also optionally computes the second segment climb gradient. Assumptions: For second segment climb gradient: One engine inoperative Only validated for two engine aircraft Source: http://adg.stanford.edu/aa241/AircraftDesign.html Inputs: analyses.base.atmosphere [SUAVE data type] airport. altitude [m] delta_isa [K] vehicle. mass_properties.takeoff [kg] reference_area [m^2] V2_VS_ratio (optional) [Unitless] maximum_lift_coefficient (optional) [Unitless] propulsors.*.number_of_engines [Unitless] Outputs: takeoff_field_length [m] Properties Used: N/A """ # ============================================== # Unpack # ============================================== atmo = analyses.atmosphere altitude = airport.altitude * Units.ft delta_isa = airport.delta_isa weight = vehicle.mass_properties.takeoff reference_area = vehicle.reference_area try: V2_VS_ratio = vehicle.V2_VS_ratio except: V2_VS_ratio = 1.20 # ============================================== # Computing atmospheric conditions # ============================================== atmo_values = atmo.compute_values(altitude, delta_isa) conditions = SUAVE.Analyses.Mission.Segments.Conditions.Aerodynamics() p = atmo_values.pressure T = atmo_values.temperature rho = atmo_values.density a = atmo_values.speed_of_sound mu = atmo_values.dynamic_viscosity sea_level_gravity = atmo.planet.sea_level_gravity # ============================================== # Determining vehicle maximum lift coefficient # ============================================== # Condition to CLmax calculation: 90KTAS @ airport state = Data() state.conditions = SUAVE.Analyses.Mission.Segments.Conditions.Aerodynamics( ) state.conditions.freestream = Data() state.conditions.freestream.density = rho state.conditions.freestream.velocity = 90. * Units.knots state.conditions.freestream.dynamic_viscosity = mu settings = analyses.aerodynamics.settings maximum_lift_coefficient, induced_drag_high_lift = compute_max_lift_coeff( state, settings, vehicle) # ============================================== # Computing speeds (Vs, V2, 0.7*V2) # ============================================== stall_speed = (2 * weight * sea_level_gravity / (rho * reference_area * maximum_lift_coefficient))**0.5 V2_speed = V2_VS_ratio * stall_speed speed_for_thrust = 0.70 * V2_speed # ============================================== # Determining vehicle number of engines # ============================================== engine_number = 0. for propulsor in vehicle.propulsors: # may have than one propulsor engine_number += propulsor.number_of_engines if engine_number == 0: raise ValueError("No engine found in the vehicle") # ============================================== # Getting engine thrust # ============================================== state = SUAVE.Analyses.Mission.Segments.Conditions.State() conditions = state.conditions conditions.update( SUAVE.Analyses.Mission.Segments.Conditions.Aerodynamics()) conditions.freestream.dynamic_pressure = np.array( np.atleast_1d(0.5 * rho * speed_for_thrust**2)) conditions.freestream.gravity = np.array( [np.atleast_1d(sea_level_gravity)]) conditions.freestream.velocity = np.array(np.atleast_1d(speed_for_thrust)) conditions.freestream.mach_number = np.array( np.atleast_1d(speed_for_thrust / a)) conditions.freestream.speed_of_sound = np.array(a) conditions.freestream.temperature = np.array(np.atleast_1d(T)) conditions.freestream.pressure = np.array(np.atleast_1d(p)) conditions.propulsion.throttle = np.array(np.atleast_2d([1.])) results = vehicle.propulsors.evaluate_thrust(state) # total thrust thrust = results.thrust_force_vector # ============================================== # Calculate takeoff distance # ============================================== # Defining takeoff distance equations coefficients try: takeoff_constants = vehicle.takeoff_constants # user defined except: # default values takeoff_constants = np.zeros(3) if engine_number == 2: takeoff_constants[0] = 857.4 takeoff_constants[1] = 2.476 takeoff_constants[2] = 0.00014 elif engine_number == 3: takeoff_constants[0] = 667.9 takeoff_constants[1] = 2.343 takeoff_constants[2] = 0.000093 elif engine_number == 4: takeoff_constants[0] = 486.7 takeoff_constants[1] = 2.282 takeoff_constants[2] = 0.0000705 elif engine_number > 4: takeoff_constants[0] = 486.7 takeoff_constants[1] = 2.282 takeoff_constants[2] = 0.0000705 print( 'The vehicle has more than 4 engines. Using 4 engine correlation. Result may not be correct.' ) else: takeoff_constants[0] = 857.4 takeoff_constants[1] = 2.476 takeoff_constants[2] = 0.00014 print( 'Incorrect number of engines: {0:.1f}. Using twin engine correlation.' .format(engine_number)) # Define takeoff index (V2^2 / (T/W) takeoff_index = V2_speed**2. / (thrust[0][0] / weight) # Calculating takeoff field length takeoff_field_length = 0. for idx, constant in enumerate(takeoff_constants): takeoff_field_length += constant * takeoff_index**idx takeoff_field_length = takeoff_field_length * Units.ft # calculating second segment climb gradient, if required by user input if compute_2nd_seg_climb: # Getting engine thrust at V2 (update only speed related conditions) state.conditions.freestream.dynamic_pressure = np.array( np.atleast_1d(0.5 * rho * V2_speed**2)) state.conditions.freestream.velocity = np.array( np.atleast_1d(V2_speed)) state.conditions.freestream.mach_number = np.array( np.atleast_1d(V2_speed / a)) state.conditions.freestream.dynamic_viscosity = np.array( np.atleast_1d(mu)) state.conditions.freestream.density = np.array(np.atleast_1d(rho)) results = vehicle.propulsors['turbofan'].engine_out(state) thrust = results.thrust_force_vector[0][0] # Compute windmilling drag windmilling_drag_coefficient = windmilling_drag(vehicle, state) # Compute asymmetry drag asymmetry_drag_coefficient = asymmetry_drag( state, vehicle, windmilling_drag_coefficient) # Compute l over d ratio for takeoff condition, NO engine failure l_over_d = estimate_2ndseg_lift_drag_ratio(state, settings, vehicle) # Compute L over D ratio for takeoff condition, WITH engine failure clv2 = maximum_lift_coefficient / (V2_VS_ratio)**2 cdv2_all_engine = clv2 / l_over_d cdv2 = cdv2_all_engine + asymmetry_drag_coefficient + windmilling_drag_coefficient l_over_d_v2 = clv2 / cdv2 # Compute 2nd segment climb gradient second_seg_climb_gradient = thrust / ( weight * sea_level_gravity) - 1. / l_over_d_v2 return takeoff_field_length[0][0], second_seg_climb_gradient[0][0] else: # return only takeoff_field_length return takeoff_field_length[0][0]
def __defaults__(self): self.tag = 'stability' self.geometry = Data() self.settings = Data()
def evaluate_thrust(self, state): """ Calculate thrust given the current state of the vehicle Assumptions: Caps the throttle at 110% and linearly interpolates thrust off that Source: N/A Inputs: state [state()] Outputs: results.thrust_force_vector [newtons] results.vehicle_mass_rate [kg/s] conditions.propulsion: rpm [radians/sec] current [amps] battery_draw [watts] battery_energy [joules] voltage_open_circuit [volts] voltage_under_load [volts] motor_torque [N-M] propeller_torque [N-M] Properties Used: Defaulted values """ # unpack conditions = state.conditions numerics = state.numerics motor = self.motor rotor = self.rotor esc = self.esc avionics = self.avionics payload = self.payload battery = self.battery num_engines = self.number_of_engines t_nondim = state.numerics.dimensionless.control_points # Set battery energy battery.current_energy = conditions.propulsion.battery_energy volts = state.unknowns.battery_voltage_under_load * 1. volts[volts > self.voltage] = self.voltage # ESC Voltage esc.inputs.voltagein = volts #--------------------------------------------------------------- # EVALUATE THRUST FROM PROPULSORS #--------------------------------------------------------------- # Step 2 esc.voltageout(conditions) # link motor.inputs.voltage = esc.outputs.voltageout # Run the motor motor.omega(conditions) # Define the thrust angle thrust_angle = self.thrust_angle # link rotor.inputs.omega = motor.outputs.omega rotor.thrust_angle = thrust_angle rotor.pitch_command = self.pitch_command # Run the rotor F, Q, P, Cp, outputs, etap = rotor.spin(conditions) # Check to see if magic thrust is needed, the ESC caps throttle at 1.1 already eta = conditions.propulsion.throttle[:, 0, None] P[eta > 1.0] = P[eta > 1.0] * eta[eta > 1.0] F[eta > 1.0] = F[eta > 1.0] * eta[eta > 1.0] # Run the avionics avionics.power() # Run the payload payload.power() # Run the motor for current i, etam = motor.current(conditions) # Fix the current for the throttle cap motor.outputs.current[ eta > 1.0] = motor.outputs.current[eta > 1.0] * eta[eta > 1.0] # link esc.inputs.currentout = motor.outputs.current # Run the esc esc.currentin(conditions) # Calculate avionics and payload power avionics_payload_power = avionics.outputs.power + payload.outputs.power # Calculate avionics and payload current avionics_payload_current = avionics_payload_power / self.voltage # link propeller_current = esc.outputs.currentin * num_engines total_current = propeller_current + avionics_payload_current battery.inputs.current = total_current battery.inputs.power_in = -( esc.outputs.voltageout * esc.outputs.currentin * num_engines + avionics_payload_power) battery.energy_calc(numerics) # Pack the conditions for outputs rpm = motor.outputs.omega / Units.rpm a = conditions.freestream.speed_of_sound R = rotor.tip_radius current = esc.outputs.currentin battery_draw = battery.inputs.power_in battery_energy = battery.current_energy voltage_open_circuit = battery.voltage_open_circuit voltage_under_load = battery.voltage_under_load state_of_charge = battery.state_of_charge conditions.propulsion.rpm = rpm conditions.propulsion.current = current conditions.propulsion.battery_draw = battery_draw conditions.propulsion.battery_energy = battery_energy conditions.propulsion.voltage_open_circuit = voltage_open_circuit conditions.propulsion.voltage_under_load = voltage_under_load conditions.propulsion.state_of_charge = state_of_charge conditions.propulsion.motor_torque = motor.outputs.torque conditions.propulsion.propeller_torque = Q conditions.propulsion.motor_efficiency = etam conditions.propulsion.acoustic_outputs[rotor.tag] = outputs conditions.propulsion.battery_specfic_power = -battery_draw / battery.mass_properties.mass #Wh/kg conditions.propulsion.electronics_efficiency = -( P * num_engines) / battery_draw conditions.propulsion.propeller_tip_mach = (R * rpm * Units.rpm) / a conditions.propulsion.battery_current = total_current conditions.propulsion.battery_efficiency = ( battery_draw + battery.resistive_losses) / battery_draw conditions.propulsion.payload_efficiency = ( battery_draw + (avionics.outputs.power + payload.outputs.power)) / battery_draw conditions.propulsion.propeller_power = P * num_engines conditions.propulsion.propeller_thrust_coefficient = Cp conditions.propulsion.propeller_efficiency = etap conditions.propulsion.propeller_thrust_coefficient = outputs.thrust_coefficient # Compute force vector F_vec = self.number_of_engines * F * [ np.cos(self.thrust_angle), 0, -np.sin(self.thrust_angle) ] F_mag = np.atleast_2d(np.linalg.norm(F_vec, axis=1)) conditions.propulsion.disc_loading = (F_mag.T) / (num_engines * np.pi * (R)**2) # N/m^2 conditions.propulsion.power_loading = (F_mag.T) / (P) # N/W mdot = state.ones_row(1) * 0.0 results = Data() results.thrust_force_vector = F_vec results.vehicle_mass_rate = mdot return results
def compute_propeller_wake_velocities(prop, grid_settings, grid_points, conditions, plot_velocities=True): x_plane = prop.origin[1, 0] #second propeller, x-location # generate the propeller wake distribution for the upstream propeller prop_copy = copy.deepcopy(prop) VD = Data() cpts = 1 # only testing one condition number_of_wake_timesteps = 100 init_timestep_offset = 0 time = 10 props = SUAVE.Core.Container() props.append(prop_copy) identical_props = True WD, dt, ts, B, Nr = generate_propeller_wake_distribution( props, identical_props, cpts, VD, init_timestep_offset, time, number_of_wake_timesteps, conditions) prop.start_angle = prop_copy.start_angle # compute the wake induced velocities: VD.YC = grid_points.ymesh VD.ZC = grid_points.zmesh VD.XC = x_plane * np.ones_like(VD.YC) VD.n_cp = np.size(VD.YC) V_ind = compute_wake_induced_velocity(WD, VD, cpts) u = V_ind[0, :, 0] v = V_ind[0, :, 1] w = V_ind[0, :, 2] propeller_wake = Data() propeller_wake.u_velocities = u propeller_wake.v_velocities = v propeller_wake.w_velocities = w propeller_wake.VD = VD if plot_velocities: # plot the velocities input to downstream propeller plot_propeller_disc_inflow(prop, propeller_wake, grid_points) return propeller_wake
class Segment(Lofted_Body.Segment): def __defaults__(self): """This sets the default for wing segments in SUAVE. Assumptions: None Source: N/A Inputs: None Outputs: None Properties Used: N/A """ self.tag = 'segment' self.percent_span_location = 0.0 self.twist = 0.0 self.root_chord_percent = 0.0 self.dihedral_outboard = 0.0 self.sweeps = Data() self.sweeps.quarter_chord = 0.0 self.sweeps.leading_edge = None self.areas = Data() self.areas.reference = 0.0 self.areas.exposed = 0.0 self.areas.wetted = 0.0 self.Airfoil = SUAVE.Core.ContainerOrdered() self.control_surfaces = Data() def append_airfoil(self, airfoil): """ Adds an airfoil to the segment Assumptions: None Source: N/A Inputs: None Outputs: None Properties Used: N/A """ # assert database type if not isinstance(airfoil, Data): raise Exception('input component must be of type Data()') # store data self.Airfoil.append(airfoil) def append_control_surface(self, control_surface): """ Adds an control_surface to the segment Assumptions: None Source: N/A Inputs: None Outputs: None Properties Used: N/A """ # assert database type if not isinstance(control_surface, Data): raise Exception('input component must be of type Data()') # store data self.control_surfaces.append(control_surface) return