def setup(self): const = self.add_subsystem('const',IndepVarComp(),promotes_outputs=["*"]) const.add_output('W_fluids', val=20, units='kg') const.add_output('structural_fudge', val=1.6, units='m/m') self.add_subsystem('wing',WingWeight_SmallTurboprop(),promotes_inputs=["*"],promotes_outputs=["*"]) self.add_subsystem('empennage',EmpennageWeight_SmallTurboprop(),promotes_inputs=["*"],promotes_outputs=["*"]) self.add_subsystem('fuselage',FuselageWeight_SmallTurboprop(),promotes_inputs=["*"],promotes_outputs=["*"]) self.add_subsystem('nacelle',NacelleWeight_SmallSingleTurboprop(),promotes_inputs=["*"],promotes_outputs=["*"]) self.add_subsystem('gear',LandingGearWeight_SmallTurboprop(),promotes_inputs=["*"],promotes_outputs=["*"]) self.add_subsystem('fuelsystem', FuelSystemWeight_SmallTurboprop(), promotes_inputs=["*"],promotes_outputs=["*"]) self.add_subsystem('equipment',EquipmentWeight_SmallTurboprop(), promotes_inputs=["*"],promotes_outputs=["*"]) self.add_subsystem('structural',AddSubtractComp(output_name='W_structure',input_names=['W_wing','W_fuselage','W_nacelle','W_empennage','W_gear'], units='lb'),promotes_outputs=['*'],promotes_inputs=["*"]) self.add_subsystem('structural_fudge',ElementMultiplyDivideComp(output_name='W_structure_adjusted',input_names=['W_structure','structural_fudge'],input_units=['lb','m/m']),promotes_inputs=["*"],promotes_outputs=["*"]) self.add_subsystem('totalempty',AddSubtractComp(output_name='OEW',input_names=['W_structure_adjusted','W_fuelsystem','W_equipment','W_engine','W_propeller','W_fluids'], units='lb'),promotes_outputs=['*'],promotes_inputs=["*"])
def setup(self): nn = self.options['num_nodes'] flight_phase = self.options['flight_phase'] # any control variables other than throttle and braking need to be defined here controls = self.add_subsystem('controls', IndepVarComp(), promotes_outputs=['*']) controls.add_output('proprpm',val=np.ones((nn,))*2000, units='rpm') # assume TO happens on battery backup if flight_phase in ['climb', 'cruise','descent']: controls.add_output('hybridization',val=0.0) else: controls.add_output('hybridization',val=1.0) hybrid_factor = self.add_subsystem('hybrid_factor', LinearInterpolator(num_nodes=nn), promotes_inputs=[('start_val', 'hybridization'), ('end_val', 'hybridization')]) propulsion_promotes_outputs = ['fuel_flow','thrust'] propulsion_promotes_inputs = ["fltcond|*", "ac|propulsion|*", "throttle", "propulsor_active", "ac|weights*", 'duration'] self.add_subsystem('propmodel', TwinSeriesHybridElectricPropulsionSystem(num_nodes=nn), promotes_inputs=propulsion_promotes_inputs, promotes_outputs=propulsion_promotes_outputs) self.connect('proprpm', ['propmodel.prop1.rpm', 'propmodel.prop2.rpm']) self.connect('hybrid_factor.vec', 'propmodel.hybrid_split.power_split_fraction') # use a different drag coefficient for takeoff versus cruise if flight_phase not in ['v0v1', 'v1v0', 'v1vr', 'rotate']: cd0_source = 'ac|aero|polar|CD0_cruise' else: cd0_source = 'ac|aero|polar|CD0_TO' self.add_subsystem('drag', PolarDrag(num_nodes=nn), promotes_inputs=['fltcond|CL', 'ac|geom|*', ('CD0', cd0_source), 'fltcond|q', ('e', 'ac|aero|polar|e')], promotes_outputs=['drag']) self.add_subsystem('OEW',TwinSeriesHybridEmptyWeight(), promotes_inputs=[('P_TO','ac|propulsion|engine|rating'),'*'], promotes_outputs=['OEW']) self.connect('propmodel.propellers_weight', 'W_propeller') self.connect('propmodel.eng1.component_weight', 'W_engine') self.connect('propmodel.gen1.component_weight', 'W_generator') self.connect('propmodel.motors_weight', 'W_motors') intfuel = self.add_subsystem('intfuel', Integrator(num_nodes=nn, method='simpson', diff_units='s', time_setup='duration'), promotes_inputs=['*'], promotes_outputs=['*']) intfuel.add_integrand('fuel_used', rate_name='fuel_flow', val=1.0, units='kg') self.add_subsystem('weight', AddSubtractComp(output_name='weight', input_names=['ac|weights|MTOW', 'fuel_used'], units='kg', vec_size=[1, nn], scaling_factors=[1, -1]), promotes_inputs=['*'], promotes_outputs=['weight'])
def setup(self): # set fudge factors on structure / equipment weight const = self.add_subsystem('const', IndepVarComp(), promotes_outputs=["*"]) const.add_output('structural_fudge', val=1., units='m/m') const.add_output('equipment_fudge', val=1.0, units='m/m') # structural weight self.add_subsystem('wing', WingWeight_JetAirliner(), promotes_inputs=["*"], promotes_outputs=["*"]) self.add_subsystem('hstab', HorizontalTailWeight_JetAirliner(), promotes_inputs=["*"], promotes_outputs=["*"]) self.add_subsystem('vstab', VerticalTailWeight_JetAirliner(), promotes_inputs=["*"], promotes_outputs=["*"]) self.add_subsystem('fuselage', FuselageWeight_JetAirliner(), promotes_inputs=["*"], promotes_outputs=["*"]) self.add_subsystem('nacelle', NacelleWeight_JetAirliner(), promotes_inputs=["*"], promotes_outputs=["*"]) self.add_subsystem('gear', LandingGearWeight_JetAirliner(), promotes_inputs=["*"], promotes_outputs=["*"]) self.add_subsystem('structural', AddSubtractComp(output_name='W_structure',input_names=['W_wing','W_fuselage','W_nacelle','W_hstab','W_vstab','W_gear'], units='lb'),promotes_outputs=['*'],promotes_inputs=["*"]) self.add_subsystem('structural_fudge',ElementMultiplyDivideComp(output_name='W_structure_adjusted',input_names=['W_structure','structural_fudge'],input_units=['lb','m/m']),promotes_inputs=["*"],promotes_outputs=["*"]) # engine weight self.add_subsystem('engines', EngineWeight_TurbofanSystem(), promotes_inputs=["*"], promotes_outputs=["*"]) # other weights self.add_subsystem('equipment', EquipmentWeight_JetAirliner(), promotes_inputs=["*"], promotes_outputs=["*"]) self.add_subsystem('operation', OperationalWeight_JetAirliner(), promotes_inputs=["*"], promotes_outputs=["*"]) self.add_subsystem('equipment_fudge',ElementMultiplyDivideComp(output_name='W_equip_adjusted',input_names=['W_equip','equipment_fudge'],input_units=['lb','m/m']),promotes_inputs=["*"],promotes_outputs=["*"]) # sum self.add_subsystem('totalempty', AddSubtractComp(output_name='OEW',input_names=['W_structure_adjusted', 'W_engines', 'W_equip_adjusted', 'W_op'], units='lb'), promotes_outputs=['*'], promotes_inputs=["*"])
def setup(self): nn = self.options['num_nodes'] flight_phase = self.options['flight_phase'] # no control variables other than throttle and braking """ # propulsion system 1: simple turbofan (textbook formula) from examples.propulsion_layouts.simple_turbofan import TurbofanPropulsionSystem self.add_subsystem('propmodel', TurbofanPropulsionSystem(num_nodes=nn), promotes_inputs=['fltcond|*', 'ac|propulsion|max_thrust', 'throttle', 'ac|propulsion|num_engine'], promotes_outputs=['thrust', 'fuel_flow']) """ # propulsion system 2: pycycle surrogate from examples.propulsion_layouts.turbofan_surrogate import TurbofanPropulsionSystem self.add_subsystem('propmodel', TurbofanPropulsionSystem(num_nodes=nn), promotes_inputs=['fltcond|*', 'throttle'], promotes_outputs=['thrust', 'fuel_flow']) #""" # aerodynamic model if flight_phase not in ['v0v1', 'v1v0', 'v1vr', 'rotate']: cd0_source = 'ac|aero|polar|CD0_cruise' else: cd0_source = 'ac|aero|polar|CD0_TO' self.add_subsystem('drag', PolarDrag(num_nodes=nn), promotes_inputs=['fltcond|CL', 'ac|geom|*', ('CD0', cd0_source), 'fltcond|q', ('e', 'ac|aero|polar|e')], promotes_outputs=['drag']) # weight estimation models self.add_subsystem('OEW', JetAirlinerEmptyWeight(), promotes_inputs=['ac|geom|*', 'ac|weights|*', 'ac|propulsion|*', 'ac|misc|*'], promotes_outputs=['OEW']) # airplanes which consume fuel will need to integrate # fuel usage across the mission and subtract it from TOW nn_simpson = int((nn - 1) / 2) self.add_subsystem('intfuel', Integrator(num_intervals=nn_simpson, method='simpson', quantity_units='kg', diff_units='s', time_setup='duration'), promotes_inputs=[('dqdt', 'fuel_flow'), 'duration', ('q_initial', 'fuel_used_initial')], promotes_outputs=[('q', 'fuel_used'), ('q_final', 'fuel_used_final')]) self.add_subsystem('weight', AddSubtractComp(output_name='weight', input_names=['ac|weights|MTOW', 'fuel_used'], units='kg', vec_size=[1, nn], scaling_factors=[1, -1]), promotes_inputs=['*'], promotes_outputs=['weight'])
def setup(self): nn = self.options['num_nodes'] flight_phase = self.options['flight_phase'] # any control variables other than throttle and braking need to be defined here controls = self.add_subsystem('controls', IndepVarComp(), promotes_outputs=['*']) controls.add_output('prop|rpm', val=np.ones((nn, )) * 1900, units='rpm') # a propulsion system needs to be defined in order to provide thrust # information for the mission analysis code propulsion_promotes_outputs = ['fuel_flow', 'thrust'] propulsion_promotes_inputs = [ "fltcond|*", "ac|propulsion|*", "throttle", "propulsor_active" ] self.add_subsystem('propmodel', TwinTurbopropPropulsionSystem(num_nodes=nn), promotes_inputs=propulsion_promotes_inputs, promotes_outputs=propulsion_promotes_outputs) self.connect('prop|rpm', ['propmodel.prop1.rpm', 'propmodel.prop2.rpm']) # use a different drag coefficient for takeoff versus cruise if flight_phase not in ['v0v1', 'v1v0', 'v1vr', 'rotate']: cd0_source = 'ac|aero|polar|CD0_cruise' else: cd0_source = 'ac|aero|polar|CD0_TO' self.add_subsystem('drag', PolarDrag(num_nodes=nn), promotes_inputs=[ 'fltcond|CL', 'ac|geom|*', ('CD0', cd0_source), 'fltcond|q', ('e', 'ac|aero|polar|e') ], promotes_outputs=['drag']) # generally the weights module will be custom to each airplane self.add_subsystem( 'OEW', SingleTurboPropEmptyWeight(), promotes_inputs=['*', ('P_TO', 'ac|propulsion|engine|rating')], promotes_outputs=['OEW']) self.connect('propmodel.propellers_weight', 'W_propeller') self.connect('propmodel.engines_weight', 'W_engine') # airplanes which consume fuel will need to integrate # fuel usage across the mission and subtract it from TOW nn_simpson = int((nn - 1) / 2) self.add_subsystem('intfuel', Integrator(num_intervals=nn_simpson, method='simpson', quantity_units='kg', diff_units='s', time_setup='duration'), promotes_inputs=[('dqdt', 'fuel_flow'), 'duration', ('q_initial', 'fuel_used_initial') ], promotes_outputs=[('q', 'fuel_used'), ('q_final', 'fuel_used_final')]) self.add_subsystem('weight', AddSubtractComp( output_name='weight', input_names=['ac|weights|MTOW', 'fuel_used'], units='kg', vec_size=[1, nn], scaling_factors=[1, -1]), promotes_inputs=['*'], promotes_outputs=['weight'])
def setup(self): nn = self.options['num_nodes'] #define design variables that are independent of flight condition or control states dvlist = [ ['ac|propulsion|engine|rating', 'eng_rating', 260.0, 'kW'], ['ac|propulsion|propeller|diameter', 'prop_diameter', 2.5, 'm'], ['ac|propulsion|motor|rating', 'motor_rating', 240.0, 'kW'], ['ac|propulsion|generator|rating', 'gen_rating', 250.0, 'kW'], ['ac|weights|W_battery', 'batt_weight', 2000, 'kg'], [ 'ac|propulsion|thermal|hx|mdot_coolant', 'mdot_coolant', 0.1 * np.ones((nn, )), 'kg/s' ], [ 'ac|propulsion|thermal|hx|coolant_mass', 'coolant_mass', 10., 'kg' ], [ 'ac|propulsion|thermal|hx|channel_width', 'channel_width', 1., 'mm' ], [ 'ac|propulsion|thermal|hx|channel_height', 'channel_height', 20., 'mm' ], [ 'ac|propulsion|thermal|hx|channel_length', 'channel_length', 0.2, 'm' ], ['ac|propulsion|thermal|hx|n_parallel', 'n_parallel', 50, None], # ['ac|propulsion|thermal|duct|area_nozzle','area_nozzle',58.*np.ones((nn,)),'inch**2'], [ 'ac|propulsion|battery|specific_energy', 'specific_energy', 300, 'W*h/kg' ] ] self.add_subsystem('dvs', DVLabel(dvlist), promotes_inputs=["*"], promotes_outputs=["*"]) #introduce model components self.add_subsystem('motor1', SimpleMotor(efficiency=0.97, num_nodes=nn), promotes_inputs=['throttle']) self.add_subsystem('prop1', SimplePropeller(num_nodes=nn), promotes_inputs=["fltcond|*"]) self.connect('motor1.shaft_power_out', 'prop1.shaft_power_in') #propulsion models expect a high-level 'throttle' parameter and a 'propulsor_active' flag to set individual throttles failedengine = ElementMultiplyDivideComp() failedengine.add_equation('motor2throttle', input_names=['throttle', 'propulsor_active'], vec_size=nn) self.add_subsystem('failedmotor', failedengine, promotes_inputs=['throttle', 'propulsor_active']) self.add_subsystem('motor2', SimpleMotor(efficiency=0.97, num_nodes=nn)) self.add_subsystem('prop2', SimplePropeller(num_nodes=nn), promotes_inputs=["fltcond|*"]) self.connect('motor2.shaft_power_out', 'prop2.shaft_power_in') self.connect('failedmotor.motor2throttle', 'motor2.throttle') addpower = AddSubtractComp( output_name='motors_elec_load', input_names=['motor1_elec_load', 'motor2_elec_load'], units='kW', vec_size=nn) addpower.add_equation(output_name='thrust', input_names=['prop1_thrust', 'prop2_thrust'], units='N', vec_size=nn) self.add_subsystem('add_power', subsys=addpower, promotes_outputs=['*']) self.connect('motor1.elec_load', 'add_power.motor1_elec_load') self.connect('motor2.elec_load', 'add_power.motor2_elec_load') self.connect('prop1.thrust', 'add_power.prop1_thrust') self.connect('prop2.thrust', 'add_power.prop2_thrust') self.add_subsystem('hybrid_split', PowerSplit(rule='fraction', num_nodes=nn)) self.connect('motors_elec_load', 'hybrid_split.power_in') self.add_subsystem('eng1', SimpleTurboshaft(num_nodes=nn, weight_inc=0.14 / 1000, weight_base=104), promotes_outputs=["fuel_flow"]) self.add_subsystem('gen1', SimpleGenerator(efficiency=0.97, num_nodes=nn)) self.connect('eng1.shaft_power_out', 'gen1.shaft_power_in') self.add_subsystem('batt1', SOCBattery(num_nodes=nn, efficiency=0.97), promotes_inputs=["duration", 'specific_energy']) self.connect('hybrid_split.power_out_A', 'batt1.elec_load') # TODO set val= right number of nn self.add_subsystem( 'eng_throttle_set', BalanceComp(name='eng_throttle', val=np.ones((nn, )) * 0.5, units=None, eq_units='kW', rhs_name='gen_power_required', lhs_name='gen_power_available')) #need to use the optimizer to drive hybrid_split.power_out_B to the same value as gen1.elec_power_out self.connect('hybrid_split.power_out_B', 'eng_throttle_set.gen_power_required') self.connect('gen1.elec_power_out', 'eng_throttle_set.gen_power_available') self.connect('eng_throttle_set.eng_throttle', 'eng1.throttle') adder = AddSubtractComp(output_name='motors_weight', input_names=['motor1_weight', 'motor2_weight'], units='kg') adder.add_equation(output_name='propellers_weight', input_names=['prop1_weight', 'prop2_weight'], units='kg') adder.add_equation(output_name='motors_heat', input_names=['motor1_heat', 'motor2_heat'], vec_size=nn, units='W') self.add_subsystem('adder', subsys=adder, promotes_inputs=['*'], promotes_outputs=['*']) relabel = [[ 'hybrid_split_A_in', 'battery_load', np.ones(nn) * 260.0, 'kW' ]] self.add_subsystem('relabel', DVLabel(relabel), promotes_outputs=["battery_load"]) self.connect('hybrid_split.power_out_A', 'relabel.hybrid_split_A_in') self.connect('motor1.component_weight', 'motor1_weight') self.connect('motor2.component_weight', 'motor2_weight') self.connect('prop1.component_weight', 'prop1_weight') self.connect('prop2.component_weight', 'prop2_weight') self.connect('motor1.heat_out', 'motor1_heat') self.connect('motor2.heat_out', 'motor2_heat') #connect design variables to model component inputs self.connect('eng_rating', 'eng1.shaft_power_rating') self.connect('prop_diameter', ['prop1.diameter', 'prop2.diameter']) self.connect('motor_rating', ['motor1.elec_power_rating', 'motor2.elec_power_rating']) self.connect('motor_rating', ['prop1.power_rating', 'prop2.power_rating']) self.connect('gen_rating', 'gen1.elec_power_rating') self.connect('batt_weight', 'batt1.battery_weight') iv = self.add_subsystem('iv', IndepVarComp(), promotes_outputs=['*']) iv.add_output('rho_coolant', val=997 * np.ones((nn, )), units='kg/m**3') lc_promotes = ['duration', 'channel_*', 'n_parallel'] self.add_subsystem('batteryheatsink', LiquidCooledComp(num_nodes=nn, quasi_steady=False), promotes_inputs=lc_promotes) self.connect('batt1.heat_out', 'batteryheatsink.q_in') self.connect('batt_weight', 'batteryheatsink.mass') self.add_subsystem('motorheatsink', LiquidCooledComp(num_nodes=nn, quasi_steady=False), promotes_inputs=lc_promotes) self.connect('motors_heat', 'motorheatsink.q_in') self.connect('motors_weight', 'motorheatsink.mass') self.add_subsystem('duct', ExplicitIncompressibleDuct(num_nodes=nn), promotes_inputs=['fltcond|*']) iv.add_output('ac|propulsion|thermal|duct|area_nozzle', val=58. * np.ones((nn, )), units='inch**2') self.connect('ac|propulsion|thermal|duct|area_nozzle', 'duct.area_nozzle') self.add_subsystem('hx', HXGroup(num_nodes=nn), promotes_inputs=[ 'ac|*', ('T_in_cold', 'fltcond|T'), ('rho_cold', 'fltcond|rho') ]) self.connect('duct.mdot', 'hx.mdot_cold') self.connect('hx.delta_p_cold', 'duct.delta_p_hex') self.connect('motorheatsink.T_out', 'hx.T_in_hot') self.connect('rho_coolant', 'hx.rho_hot') self.add_subsystem( 'reservoir', CoolantReservoir(num_nodes=nn), promotes_inputs=['duration', ('mass', 'coolant_mass')]) self.connect('hx.T_out_hot', 'reservoir.T_in') self.connect('reservoir.T_out', 'batteryheatsink.T_in') self.connect('batteryheatsink.T_out', 'motorheatsink.T_in') self.connect('mdot_coolant', [ 'batteryheatsink.mdot_coolant', 'motorheatsink.mdot_coolant', 'hx.mdot_hot', 'reservoir.mdot_coolant' ])
def setup(self): n_int_per_seg = self.options['n_int_per_seg'] track_battery = self.options['track_battery'] track_fuel = self.options['track_fuel'] nn = (n_int_per_seg * 2 + 1) nn_tot = 3 * nn + 2 #===Some of the generic components take "weight" as an input. Need to re-label Takeoff Weight (TOW) as just weight dvlist = [['ac|weights|MTOW', 'weight', 2000.0, 'kg'], ['mission|takeoff|v1', 'v1', 40, 'm/s'], ['mission|takeoff|vr', 'vr', 45, 'm/s'], ['ac|aero|polar|CD0_TO', 'CD0', 0.005, None], ['ac|aero|polar|e', 'e', 0.95, None], [ 'fltcond|takeoff|q', 'fltcond|q', 100 * np.ones(nn_tot), 'Pa' ]] self.add_subsystem('dvs', DVLabel(dvlist), promotes_inputs=["*"], promotes_outputs=["*"]) #===We assume the takeoff starts at 1 m/s to avoid singularities at v=0 const = self.add_subsystem('const', IndepVarComp()) const.add_output('v0', val=1, units='m/s') const.add_output('reaction_time', val=2, units='s') #===Lift and Drag Calculations feed the EOMs for the takeoff roll=== self.add_subsystem( 'CL', TakeoffCLs(n_int_per_seg=n_int_per_seg), promotes_inputs=["ac|geom|*", "fltcond|*", "weight"], ) self.add_subsystem( 'drag', PolarDrag(num_nodes=nn_tot), promotes_inputs=['ac|geom|*', 'fltcond|q', 'CD0', 'e'], promotes_outputs=['drag']) self.add_subsystem('lift', Lift(num_nodes=nn_tot), promotes_inputs=['ac|geom|*', 'fltcond|q'], promotes_outputs=['lift']) self.connect('CL.CL_takeoff', 'drag.fltcond|CL') self.connect('CL.CL_takeoff', 'lift.fltcond|CL') #==The takeoff distance integrator numerically integrates the quantity (speed / acceleration) with respect to speed to obtain distance. Obtain this quantity: self.add_subsystem( 'accel', TakeoffAccels(n_int_per_seg=n_int_per_seg), promotes_inputs=['lift', 'drag', 'takeoff|thrust', 'weight'], promotes_outputs=['_inverse_accel']) self.add_subsystem( 'mult', ElementMultiplyDivideComp( output_name='_rate_to_integrate', input_names=['_inverse_accel', 'fltcond|takeoff|Utrue'], vec_size=nn_tot, input_units=['s**2/m', 'm/s']), promotes_inputs=['*'], promotes_outputs=['_*']) if track_fuel: self.add_subsystem( 'fuelflowmult', ElementMultiplyDivideComp( output_name='_fuel_flow_rate_to_integrate', input_names=['_inverse_accel', 'takeoff|fuel_flow'], vec_size=nn_tot, input_units=['s**2/m', 'kg/s']), promotes_inputs=['*'], promotes_outputs=['_*']) if track_battery: self.add_subsystem( 'batterymult', ElementMultiplyDivideComp( output_name='_battery_rate_to_integrate', input_names=['_inverse_accel', 'takeoff|battery_load'], vec_size=nn_tot, input_units=['s**2/m', 'J / s']), promotes_inputs=['*'], promotes_outputs=['*']) #==The following boilerplate splits the input flight conditions, thrusts, and fuel flows into the takeoff segments for further analysis inputs_to_split = [ '_rate_to_integrate', 'fltcond|takeoff|Utrue', 'takeoff|thrust', 'drag', 'lift' ] units = ['s', 'm/s', 'N', 'N', 'N'] if track_battery: inputs_to_split.append('_battery_rate_to_integrate') units.append('J*s/m') if track_fuel: inputs_to_split.append('_fuel_flow_rate_to_integrate') units.append('kg*s/m') segments_to_split_into = ['v0v1', 'v1vr', 'v1v0', 'vtrans', 'v2'] nn_each_segment = [nn, nn, nn, 1, 1] split_inst = VectorSplitComp() for kth, input_name in enumerate(inputs_to_split): output_names_list = [] for segment in segments_to_split_into: output_names_list.append(input_name + '_' + segment) split_inst.add_relation(output_names=output_names_list, input_name=input_name, vec_sizes=nn_each_segment, units=units[kth]) splitter = self.add_subsystem('splitter', subsys=split_inst, promotes_inputs=["*"], promotes_outputs=["*"]) #==Now integrate the three continuous segments: 0 to v1, v1 to rotation with reduced power if applicable, and hard braking self.add_subsystem( 'v0v1_dist', IntegrateQuantity(num_intervals=n_int_per_seg, quantity_units='m', diff_units='m/s', force_signs=True)) self.add_subsystem( 'v1vr_dist', IntegrateQuantity(num_intervals=n_int_per_seg, quantity_units='m', diff_units='m/s', force_signs=True)) self.add_subsystem( 'v1v0_dist', IntegrateQuantity(num_intervals=n_int_per_seg, quantity_units='m', diff_units='m/s', force_signs=True)) self.connect('_rate_to_integrate_v0v1', 'v0v1_dist.rate') self.connect('_rate_to_integrate_v1vr', 'v1vr_dist.rate') self.connect('_rate_to_integrate_v1v0', 'v1v0_dist.rate') self.connect('const.v0', 'v0v1_dist.lower_limit') self.connect('v1', 'v0v1_dist.upper_limit') self.connect('v1', 'v1v0_dist.lower_limit') self.connect('const.v0', 'v1v0_dist.upper_limit') self.connect('v1', 'v1vr_dist.lower_limit') self.connect('vr', 'v1vr_dist.upper_limit') if track_fuel: self.add_subsystem( 'v0v1_fuel', IntegrateQuantity(num_intervals=n_int_per_seg, quantity_units='kg', diff_units='m/s', force_signs=True)) self.add_subsystem( 'v1vr_fuel', IntegrateQuantity(num_intervals=n_int_per_seg, quantity_units='kg', diff_units='m/s', force_signs=True)) self.connect('_fuel_flow_rate_to_integrate_v0v1', 'v0v1_fuel.rate') self.connect('_fuel_flow_rate_to_integrate_v1vr', 'v1vr_fuel.rate') self.connect('const.v0', 'v0v1_fuel.lower_limit') self.connect('v1', ['v0v1_fuel.upper_limit', 'v1vr_fuel.lower_limit']) self.connect('vr', 'v1vr_fuel.upper_limit') if track_battery: self.add_subsystem( 'v0v1_battery', IntegrateQuantity(num_intervals=n_int_per_seg, quantity_units='J', diff_units='m/s', force_signs=True)) self.add_subsystem( 'v1vr_battery', IntegrateQuantity(num_intervals=n_int_per_seg, quantity_units='J', diff_units='m/s', force_signs=True)) self.connect('_battery_rate_to_integrate_v0v1', 'v0v1_battery.rate') self.connect('_battery_rate_to_integrate_v1vr', 'v1vr_battery.rate') self.connect('const.v0', 'v0v1_battery.lower_limit') self.connect( 'v1', ['v0v1_battery.upper_limit', 'v1vr_battery.lower_limit']) self.connect('vr', 'v1vr_battery.upper_limit') #==Next compute the transition and climb phase to the specified clearance height. First, need the steady climb-out angle at v2 speed self.add_subsystem( 'gamma', TakeoffV2ClimbAngle(), promotes_inputs=["drag_v2", "takeoff|thrust_v2", "weight"], promotes_outputs=["takeoff|climb|gamma"]) self.add_subsystem('transition', TakeoffTransition(), promotes_inputs=[ "fltcond|takeoff|Utrue_vtrans", "takeoff|climb|gamma" ], promotes_outputs=["h_transition", "s_transition"]) self.add_subsystem( 'climb', TakeoffClimb(), promotes_inputs=["takeoff|climb|gamma", "h_transition"], promotes_outputs=["s_climb"]) self.add_subsystem('reaction', ElementMultiplyDivideComp( output_name='s_reaction', input_names=['v1', 'reaction_time'], vec_size=1, input_units=['m/s', 's']), promotes_inputs=['v1']) self.connect('const.reaction_time', 'reaction.reaction_time') self.add_subsystem('total_to_distance_continue', AddSubtractComp(output_name='takeoff|distance', input_names=[ 's_v0v1', 's_v1vr', 's_reaction', 's_transition', 's_climb' ], vec_size=1, units='m'), promotes_outputs=["*"]) self.add_subsystem('total_to_distance_abort', AddSubtractComp( output_name='takeoff|distance_abort', input_names=['s_v0v1', 's_v1v0', 's_reaction'], vec_size=1, units='m'), promotes_outputs=["*"]) self.connect('reaction.s_reaction', 'total_to_distance_continue.s_reaction') self.connect('reaction.s_reaction', 'total_to_distance_abort.s_reaction') self.connect('v0v1_dist.delta_quantity', [ 'total_to_distance_continue.s_v0v1', 'total_to_distance_abort.s_v0v1' ]) self.connect('v1vr_dist.delta_quantity', 'total_to_distance_continue.s_v1vr') self.connect('s_transition', 'total_to_distance_continue.s_transition') self.connect('s_climb', 'total_to_distance_continue.s_climb') self.connect('v1v0_dist.delta_quantity', 'total_to_distance_abort.s_v1v0') if track_battery: self.add_subsystem( 'total_battery', AddSubtractComp(output_name='takeoff|total_battery_energy', input_names=['battery_v0v1', 'battery_v1vr'], units='J'), promotes_outputs=["*"]) self.connect('v0v1_battery.delta_quantity', 'total_battery.battery_v0v1') self.connect('v1vr_battery.delta_quantity', 'total_battery.battery_v1vr') if track_fuel: self.add_subsystem('total_fuel', AddSubtractComp( output_name='takeoff|total_fuel', input_names=['fuel_v0v1', 'fuel_v1vr'], units='kg', scaling_factors=[-1, -1]), promotes_outputs=["*"]) self.connect('v0v1_fuel.delta_quantity', 'total_fuel.fuel_v0v1') self.connect('v1vr_fuel.delta_quantity', 'total_fuel.fuel_v1vr') self.add_subsystem( 'climb_weight', AddSubtractComp(output_name='weight_after_takeoff', input_names=['weight', 'takeoff|total_fuel'], units='kg', scaling_factors=[1, -1]), promotes_inputs=["*"], promotes_outputs=["*"])
def setup(self): #define design variables that are independent of flight condition or control states dvlist = [ ['ac|propulsion|engine|rating', 'eng_rating', 750, 'hp'], ['ac|propulsion|propeller|diameter', 'prop_diameter', 2.28, 'm'], ] self.add_subsystem('dvs', DVLabel(dvlist), promotes_inputs=["*"], promotes_outputs=["*"]) nn = self.options['num_nodes'] #introduce model components self.add_subsystem('eng1', SimpleTurboshaft(num_nodes=nn, weight_inc=0.14 / 1000, weight_base=104), promotes_inputs=['throttle']) self.add_subsystem('prop1', SimplePropeller(num_nodes=nn, num_blades=4, design_J=2.2, design_cp=0.55), promotes_inputs=["fltcond|*"]) self.add_subsystem( 'eng2', SimpleTurboshaft(num_nodes=nn, weight_inc=0.14 / 1000, weight_base=104)) self.add_subsystem('prop2', SimplePropeller(num_nodes=nn, num_blades=4, design_J=2.2, design_cp=0.55), promotes_inputs=["fltcond|*"]) #connect design variables to model component inputs self.connect('eng_rating', 'eng1.shaft_power_rating') self.connect('eng_rating', 'eng2.shaft_power_rating') self.connect('eng_rating', 'prop1.power_rating') self.connect('eng_rating', 'prop2.power_rating') self.connect('prop_diameter', 'prop1.diameter') self.connect('prop_diameter', 'prop2.diameter') #propulsion models expect a high-level 'throttle' parameter and a 'propulsor_active' flag to set individual throttles failedengine = ElementMultiplyDivideComp() failedengine.add_equation('eng2throttle', input_names=['throttle', 'propulsor_active'], vec_size=nn) self.add_subsystem('failedengine', failedengine, promotes_inputs=['throttle', 'propulsor_active']) self.connect('failedengine.eng2throttle', 'eng2.throttle') #connect components to each other self.connect('eng1.shaft_power_out', 'prop1.shaft_power_in') self.connect('eng2.shaft_power_out', 'prop2.shaft_power_in') #add up the weights, thrusts and fuel flows add1 = AddSubtractComp( output_name='fuel_flow', input_names=['eng1_fuel_flow', 'eng2_fuel_flow'], vec_size=nn, units='kg/s') add1.add_equation(output_name='thrust', input_names=['prop1_thrust', 'prop2_thrust'], vec_size=nn, units='N') add1.add_equation(output_name='engines_weight', input_names=['eng1_weight', 'eng2_weight'], units='kg') add1.add_equation(output_name='propellers_weight', input_names=['prop1_weight', 'prop2_weight'], units='kg') self.add_subsystem('adder', subsys=add1, promotes_inputs=["*"], promotes_outputs=["*"]) self.connect('prop1.thrust', 'prop1_thrust') self.connect('prop2.thrust', 'prop2_thrust') self.connect('eng1.fuel_flow', 'eng1_fuel_flow') self.connect('eng2.fuel_flow', 'eng2_fuel_flow') self.connect('prop1.component_weight', 'prop1_weight') self.connect('prop2.component_weight', 'prop2_weight') self.connect('eng1.component_weight', 'eng1_weight') self.connect('eng2.component_weight', 'eng2_weight')
def setup(self): #define design variables that are independent of flight condition or control states dvlist = [ ['ac|propulsion|engine|rating', 'eng_rating', 260.0, 'kW'], ['ac|propulsion|propeller|diameter', 'prop_diameter', 2.5, 'm'], ['ac|propulsion|motor|rating', 'motor_rating', 240.0, 'kW'], ['ac|propulsion|generator|rating', 'gen_rating', 250.0, 'kW'], ['ac|weights|W_battery', 'batt_weight', 2000, 'kg'] ] self.add_subsystem('dvs', DVLabel(dvlist), promotes_inputs=["*"], promotes_outputs=["*"]) nn = self.options['num_nodes'] e_b = self.options['specific_energy'] #introduce model components self.add_subsystem('motor1', SimpleMotor(efficiency=0.97, num_nodes=nn)) self.add_subsystem('prop1', SimplePropeller(num_nodes=nn), promotes_inputs=["fltcond|*"]) self.connect('motor1.shaft_power_out', 'prop1.shaft_power_in') self.add_subsystem('motor2', SimpleMotor(efficiency=0.97, num_nodes=nn)) self.add_subsystem('prop2', SimplePropeller(num_nodes=nn), promotes_inputs=["fltcond|*"]) self.connect('motor2.shaft_power_out', 'prop2.shaft_power_in') addpower = AddSubtractComp( output_name='motors_elec_load', input_names=['motor1_elec_load', 'motor2_elec_load'], units='kW', vec_size=nn) addpower.add_equation(output_name='thrust', input_names=['prop1_thrust', 'prop2_thrust'], units='N', vec_size=nn) self.add_subsystem('add_power', subsys=addpower, promotes_outputs=['*']) self.connect('motor1.elec_load', 'add_power.motor1_elec_load') self.connect('motor2.elec_load', 'add_power.motor2_elec_load') self.connect('prop1.thrust', 'add_power.prop1_thrust') self.connect('prop2.thrust', 'add_power.prop2_thrust') self.add_subsystem('hybrid_split', PowerSplit(rule='fraction', num_nodes=nn)) self.connect('motors_elec_load', 'hybrid_split.power_in') self.add_subsystem('eng1', SimpleTurboshaft(num_nodes=nn, weight_inc=0.14 / 1000, weight_base=104), promotes_outputs=["fuel_flow"]) self.add_subsystem('gen1', SimpleGenerator(efficiency=0.97, num_nodes=nn)) self.connect('eng1.shaft_power_out', 'gen1.shaft_power_in') self.add_subsystem('batt1', SimpleBattery(num_nodes=nn, specific_energy=e_b)) self.connect('hybrid_split.power_out_A', 'batt1.elec_load') self.add_subsystem( 'eng_gen_resid', AddSubtractComp( output_name='eng_gen_residual', input_names=['gen_power_available', 'gen_power_required'], vec_size=nn, units='kW', scaling_factors=[1, -1])) #need to use the optimizer to drive hybrid_split.power_out_B to the same value as gen1.elec_power_out self.connect('hybrid_split.power_out_B', 'eng_gen_resid.gen_power_required') self.connect('gen1.elec_power_out', 'eng_gen_resid.gen_power_available') addweights = AddSubtractComp( output_name='motors_weight', input_names=['motor1_weight', 'motor2_weight'], units='kg') addweights.add_equation(output_name='propellers_weight', input_names=['prop1_weight', 'prop2_weight'], units='kg') self.add_subsystem('add_weights', subsys=addweights, promotes_inputs=['*'], promotes_outputs=['*']) relabel = [[ 'hybrid_split_A_in', 'battery_load', np.ones(nn) * 260.0, 'kW' ]] self.add_subsystem('relabel', DVLabel(relabel), promotes_outputs=["battery_load"]) self.connect('hybrid_split.power_out_A', 'relabel.hybrid_split_A_in') self.connect('motor1.component_weight', 'motor1_weight') self.connect('motor2.component_weight', 'motor2_weight') self.connect('prop1.component_weight', 'prop1_weight') self.connect('prop2.component_weight', 'prop2_weight') #connect design variables to model component inputs self.connect('eng_rating', 'eng1.shaft_power_rating') self.connect('prop_diameter', ['prop1.diameter', 'prop2.diameter']) self.connect('motor_rating', ['motor1.elec_power_rating', 'motor2.elec_power_rating']) self.connect('motor_rating', ['prop1.power_rating', 'prop2.power_rating']) self.connect('gen_rating', 'gen1.elec_power_rating') self.connect('batt_weight', 'batt1.battery_weight')
def setup(self): nn = self.options['num_nodes'] e_b = self.options['specific_energy'] # define design variables that are independent of flight condition or control states dvlist = [ ['ac|propulsion|engine|rating', 'eng_rating', 260.0, 'kW'], ['ac|propulsion|propeller|diameter', 'prop_diameter', 2.5, 'm'], ['ac|propulsion|motor|rating', 'motor_rating', 240.0, 'kW'], ['ac|propulsion|generator|rating', 'gen_rating', 250.0, 'kW'], ['ac|weights|W_battery', 'batt_weight', 2000, 'kg'] ] self.add_subsystem('dvs', DVLabel(dvlist), promotes_inputs=["*"], promotes_outputs=["*"]) # introduce model components self.add_subsystem('motor1', SimpleMotor(efficiency=0.97, num_nodes=nn)) self.add_subsystem('prop1', SimplePropeller(num_nodes=nn), promotes_inputs=["fltcond|*"], promotes_outputs=['thrust']) self.connect('motor1.shaft_power_out', 'prop1.shaft_power_in') self.add_subsystem('hybrid_split', PowerSplit(rule='fraction', num_nodes=nn)) self.connect('motor1.elec_load', 'hybrid_split.power_in') self.add_subsystem('eng1', SimpleTurboshaft(num_nodes=nn, weight_inc=0.14 / 1000, weight_base=104), promotes_outputs=["fuel_flow"]) self.add_subsystem('gen1', SimpleGenerator(efficiency=0.97, num_nodes=nn)) self.connect('eng1.shaft_power_out', 'gen1.shaft_power_in') self.add_subsystem('batt1', SimpleBattery(num_nodes=nn, specific_energy=e_b)) self.connect('hybrid_split.power_out_A', 'batt1.elec_load') # need to use the optimizer to drive hybrid_split.power_out_B to the # same value as gen1.elec_power_out. # create a residual equation for power in vs power out from the generator self.add_subsystem( 'eng_gen_resid', AddSubtractComp( output_name='eng_gen_residual', input_names=['gen_power_available', 'gen_power_required'], vec_size=nn, units='kW', scaling_factors=[1, -1])) self.connect('hybrid_split.power_out_B', 'eng_gen_resid.gen_power_required') self.connect('gen1.elec_power_out', 'eng_gen_resid.gen_power_available') # add the weights of all the motors and props # (forward-compatibility for twin series hybrid layout) addweights = AddSubtractComp(output_name='motors_weight', input_names=['motor1_weight'], units='kg') addweights.add_equation(output_name='propellers_weight', input_names=['prop1_weight'], units='kg') self.add_subsystem('add_weights', subsys=addweights, promotes_inputs=['*'], promotes_outputs=['*']) self.connect('motor1.component_weight', 'motor1_weight') self.connect('prop1.component_weight', 'prop1_weight') #connect design variables to model component inputs self.connect('eng_rating', 'eng1.shaft_power_rating') self.connect('prop_diameter', ['prop1.diameter']) self.connect('motor_rating', ['motor1.elec_power_rating']) self.connect('motor_rating', ['prop1.power_rating']) self.connect('gen_rating', 'gen1.elec_power_rating') self.connect('batt_weight', 'batt1.battery_weight')
def setup(self): nn = self.options['num_nodes'] #define design variables that are independent of flight condition or control states dvlist = [ ['ac|propulsion|engine|rating', 'eng_rating', 260.0, 'kW'], ['ac|propulsion|propeller|diameter', 'prop_diameter', 2.5, 'm'], ['ac|propulsion|motor|rating', 'motor_rating', 240.0, 'kW'], ['ac|propulsion|generator|rating', 'gen_rating', 250.0, 'kW'], ['ac|weights|W_battery', 'batt_weight', 2000, 'kg'], [ 'ac|propulsion|thermal|hx|mdot_coolant', 'mdot_coolant', 0.1 * np.ones((nn, )), 'kg/s' ], [ 'ac|propulsion|thermal|hx|coolant_mass', 'coolant_mass', 10., 'kg' ], [ 'ac|propulsion|thermal|hx|channel_width', 'channel_width', 1., 'mm' ], [ 'ac|propulsion|thermal|hx|channel_height', 'channel_height', 20., 'mm' ], [ 'ac|propulsion|thermal|hx|channel_length', 'channel_length', 0.2, 'm' ], ['ac|propulsion|thermal|hx|n_parallel', 'n_parallel', 50, None], # ['ac|propulsion|thermal|duct|area_nozzle','area_nozzle',58.*np.ones((nn,)),'inch**2'], [ 'ac|propulsion|battery|specific_energy', 'specific_energy', 300, 'W*h/kg' ] ] self.add_subsystem('dvs', DVLabel(dvlist), promotes_inputs=["*"], promotes_outputs=["*"]) #introduce model components self.add_subsystem('motor1', SimpleMotor(efficiency=0.97, num_nodes=nn), promotes_inputs=['throttle']) self.add_subsystem('prop1', SimplePropeller(num_nodes=nn), promotes_inputs=["fltcond|*"]) self.connect('motor1.shaft_power_out', 'prop1.shaft_power_in') #propulsion models expect a high-level 'throttle' parameter and a 'propulsor_active' flag to set individual throttles failedengine = ElementMultiplyDivideComp() failedengine.add_equation('motor2throttle', input_names=['throttle', 'propulsor_active'], vec_size=nn) self.add_subsystem('failedmotor', failedengine, promotes_inputs=['throttle', 'propulsor_active']) self.add_subsystem('motor2', SimpleMotor(efficiency=0.97, num_nodes=nn)) self.add_subsystem('prop2', SimplePropeller(num_nodes=nn), promotes_inputs=["fltcond|*"]) self.connect('motor2.shaft_power_out', 'prop2.shaft_power_in') self.connect('failedmotor.motor2throttle', 'motor2.throttle') addpower = AddSubtractComp(output_name='total_elec_load', input_names=[ 'motor1_elec_load', 'motor2_elec_load', 'refrig_elec_load' ], units='kW', vec_size=nn) addpower.add_equation(output_name='thrust', input_names=['prop1_thrust', 'prop2_thrust'], units='N', vec_size=nn) self.add_subsystem('add_power', subsys=addpower, promotes_outputs=['*']) self.connect('motor1.elec_load', 'add_power.motor1_elec_load') self.connect('motor2.elec_load', 'add_power.motor2_elec_load') self.connect('prop1.thrust', 'add_power.prop1_thrust') self.connect('prop2.thrust', 'add_power.prop2_thrust') self.add_subsystem('hybrid_split', PowerSplit(rule='fraction', num_nodes=nn)) self.connect('total_elec_load', 'hybrid_split.power_in') self.add_subsystem('eng1', SimpleTurboshaft(num_nodes=nn, weight_inc=0.14 / 1000, weight_base=104), promotes_outputs=["fuel_flow"]) self.add_subsystem('gen1', SimpleGenerator(efficiency=0.97, num_nodes=nn)) self.connect('eng1.shaft_power_out', 'gen1.shaft_power_in') self.add_subsystem('batt1', SOCBattery(num_nodes=nn, efficiency=0.97), promotes_inputs=["duration", 'specific_energy']) self.connect('hybrid_split.power_out_A', 'batt1.elec_load') # TODO set val= right number of nn self.add_subsystem( 'eng_throttle_set', BalanceComp(name='eng_throttle', val=np.ones((nn, )) * 0.5, units=None, eq_units='kW', rhs_name='gen_power_required', lhs_name='gen_power_available')) #need to use the optimizer to drive hybrid_split.power_out_B to the same value as gen1.elec_power_out self.connect('hybrid_split.power_out_B', 'eng_throttle_set.gen_power_required') self.connect('gen1.elec_power_out', 'eng_throttle_set.gen_power_available') self.connect('eng_throttle_set.eng_throttle', 'eng1.throttle') adder = AddSubtractComp(output_name='motors_weight', input_names=['motor1_weight', 'motor2_weight'], units='kg') adder.add_equation(output_name='propellers_weight', input_names=['prop1_weight', 'prop2_weight'], units='kg') adder.add_equation(output_name='motors_heat', input_names=['motor1_heat', 'motor2_heat'], vec_size=nn, units='W') self.add_subsystem('adder', subsys=adder, promotes_inputs=['*'], promotes_outputs=['*']) relabel = [[ 'hybrid_split_A_in', 'battery_load', np.ones(nn) * 260.0, 'kW' ]] self.add_subsystem('relabel', DVLabel(relabel), promotes_outputs=["battery_load"]) self.connect('hybrid_split.power_out_A', 'relabel.hybrid_split_A_in') self.connect('motor1.component_weight', 'motor1_weight') self.connect('motor2.component_weight', 'motor2_weight') self.connect('prop1.component_weight', 'prop1_weight') self.connect('prop2.component_weight', 'prop2_weight') self.connect('motor1.heat_out', 'motor1_heat') self.connect('motor2.heat_out', 'motor2_heat') #connect design variables to model component inputs self.connect('eng_rating', 'eng1.shaft_power_rating') self.connect('prop_diameter', ['prop1.diameter', 'prop2.diameter']) self.connect('motor_rating', ['motor1.elec_power_rating', 'motor2.elec_power_rating']) self.connect('motor_rating', ['prop1.power_rating', 'prop2.power_rating']) self.connect('gen_rating', 'gen1.elec_power_rating') self.connect('batt_weight', 'batt1.battery_weight') iv = self.add_subsystem('iv', IndepVarComp(), promotes_outputs=['*']) rho_coolant = 997. # kg/m^3 iv.add_output('rho_coolant', val=rho_coolant * np.ones((nn, )), units='kg/m**3') lc_promotes = ['duration', 'channel_*', 'n_parallel'] # Add the refrigerators electrical load to the splitter with the two motors # so it pulls power from both the battery and turboshaft at the hybridization ratio self.add_subsystem( 'refrig', HeatPumpWithIntegratedCoolantLoop( num_nodes=nn, hot_side_balance_param_units='inch**2', hot_side_balance_param_lower=1e-10, hot_side_balance_param_upper=1e3)) self.connect('refrig.Wdot', 'add_power.refrig_elec_load') iv.add_output('refrig_eff_factor', val=0.4, shape=None, units=None) iv.add_output('refrig_T_h_set', val=450., shape=(nn, ), units='K') iv.add_output('refrig_T_c_set', val=280., shape=(nn, ), units='K') iv.add_output('bypass_refrig', val=np.zeros((nn, )), shape=(nn, ), units=None) self.connect('refrig_eff_factor', 'refrig.eff_factor') self.connect('refrig_T_h_set', 'refrig.T_h_set') self.connect('refrig_T_c_set', 'refrig.T_c_set') self.connect('bypass_refrig', 'refrig.bypass_heat_pump') # Coolant loop on electrical component side (cooling side of refrigerator) # ,---> battery ---> motor ---, # | | # '---- refrig cold side <----' self.add_subsystem('batteryheatsink', LiquidCooledComp(num_nodes=nn, quasi_steady=False), promotes_inputs=lc_promotes) self.connect('batt1.heat_out', 'batteryheatsink.q_in') self.connect('batt_weight', 'batteryheatsink.mass') self.connect('refrig.T_out_cold', 'batteryheatsink.T_in') self.add_subsystem('motorheatsink', LiquidCooledComp(num_nodes=nn, quasi_steady=False), promotes_inputs=lc_promotes) self.connect('motors_heat', 'motorheatsink.q_in') self.connect('motors_weight', 'motorheatsink.mass') self.connect('motorheatsink.T_out', 'refrig.T_in_cold') self.connect('batteryheatsink.T_out', 'motorheatsink.T_in') self.connect('mdot_coolant', [ 'batteryheatsink.mdot_coolant', 'motorheatsink.mdot_coolant', 'refrig.mdot_coolant_cold' ]) # Coolant loop on hot side of refrigerator to reject heat # ,----> refrigerator hot side -----, # | | # '----- heat exchanger/duct <------' self.add_subsystem('duct', ExplicitIncompressibleDuct(num_nodes=nn), promotes_inputs=['fltcond|*']) self.add_subsystem('hx', HXGroup(num_nodes=nn), promotes_inputs=[ 'ac|*', ('rho_cold', 'fltcond|rho'), ('T_in_cold', 'fltcond|T') ]) self.connect('duct.mdot', 'hx.mdot_cold') self.connect('hx.delta_p_cold', 'duct.delta_p_hex') self.connect('rho_coolant', 'hx.rho_hot') self.connect('refrig.T_out_hot', 'hx.T_in_hot') self.connect('hx.T_out_hot', 'refrig.T_in_hot') # Modulate the duct inlet area to maintain the desired temperature on the hot side of the refrig self.connect('refrig.hot_side_balance_param', 'duct.area_nozzle') self.connect('mdot_coolant', ['refrig.mdot_coolant_hot', 'hx.mdot_hot'])
def setup(self): nn = self.options['num_nodes'] #define design variables that are independent of flight condition or control states dvlist = [ ['ac|propulsion|engine|rating', 'eng_rating', 260.0, 'kW'], ['ac|propulsion|propeller|diameter', 'prop_diameter', 2.5, 'm'], ['ac|propulsion|motor|rating', 'motor_rating', 240.0, 'kW'], ['ac|propulsion|generator|rating', 'gen_rating', 250.0, 'kW'], ['ac|weights|W_battery', 'batt_weight', 2000, 'kg'], [ 'ac|propulsion|battery|specific_energy', 'specific_energy', 300, 'W*h/kg' ] ] self.add_subsystem('dvs', DVLabel(dvlist), promotes_inputs=["*"], promotes_outputs=["*"]) #introduce model components self.add_subsystem('motor1', SimpleMotor(efficiency=0.97, num_nodes=nn), promotes_inputs=['throttle']) self.add_subsystem('prop1', SimplePropeller(num_nodes=nn), promotes_inputs=["fltcond|*"]) self.connect('motor1.shaft_power_out', 'prop1.shaft_power_in') #propulsion models expect a high-level 'throttle' parameter and a 'propulsor_active' flag to set individual throttles failedengine = ElementMultiplyDivideComp() failedengine.add_equation('motor2throttle', input_names=['throttle', 'propulsor_active'], vec_size=nn) self.add_subsystem('failedmotor', failedengine, promotes_inputs=['throttle', 'propulsor_active']) self.add_subsystem('motor2', SimpleMotor(efficiency=0.97, num_nodes=nn)) self.add_subsystem('prop2', SimplePropeller(num_nodes=nn), promotes_inputs=["fltcond|*"]) self.connect('motor2.shaft_power_out', 'prop2.shaft_power_in') self.connect('failedmotor.motor2throttle', 'motor2.throttle') addpower = AddSubtractComp( output_name='motors_elec_load', input_names=['motor1_elec_load', 'motor2_elec_load'], units='kW', vec_size=nn) addpower.add_equation(output_name='thrust', input_names=['prop1_thrust', 'prop2_thrust'], units='N', vec_size=nn) self.add_subsystem('add_power', subsys=addpower, promotes_outputs=['*']) self.connect('motor1.elec_load', 'add_power.motor1_elec_load') self.connect('motor2.elec_load', 'add_power.motor2_elec_load') self.connect('prop1.thrust', 'add_power.prop1_thrust') self.connect('prop2.thrust', 'add_power.prop2_thrust') self.add_subsystem('hybrid_split', PowerSplit(rule='fraction', num_nodes=nn)) self.connect('motors_elec_load', 'hybrid_split.power_in') self.add_subsystem('eng1', SimpleTurboshaft(num_nodes=nn, weight_inc=0.14 / 1000, weight_base=104), promotes_outputs=["fuel_flow"]) self.add_subsystem('gen1', SimpleGenerator(efficiency=0.97, num_nodes=nn)) self.connect('eng1.shaft_power_out', 'gen1.shaft_power_in') self.add_subsystem('batt1', SOCBattery(num_nodes=nn, efficiency=0.97), promotes_inputs=["duration", "specific_energy"]) self.connect('hybrid_split.power_out_A', 'batt1.elec_load') # TODO set val= right number of nn self.add_subsystem( 'eng_throttle_set', BalanceComp(name='eng_throttle', val=np.ones((nn, )) * 0.5, units=None, eq_units='kW', rhs_name='gen_power_required', lhs_name='gen_power_available')) #need to use the optimizer to drive hybrid_split.power_out_B to the same value as gen1.elec_power_out self.connect('hybrid_split.power_out_B', 'eng_throttle_set.gen_power_required') self.connect('gen1.elec_power_out', 'eng_throttle_set.gen_power_available') self.connect('eng_throttle_set.eng_throttle', 'eng1.throttle') addweights = AddSubtractComp( output_name='motors_weight', input_names=['motor1_weight', 'motor2_weight'], units='kg') addweights.add_equation(output_name='propellers_weight', input_names=['prop1_weight', 'prop2_weight'], units='kg') self.add_subsystem('add_weights', subsys=addweights, promotes_inputs=['*'], promotes_outputs=['*']) relabel = [[ 'hybrid_split_A_in', 'battery_load', np.ones(nn) * 260.0, 'kW' ]] self.add_subsystem('relabel', DVLabel(relabel), promotes_outputs=["battery_load"]) self.connect('hybrid_split.power_out_A', 'relabel.hybrid_split_A_in') self.connect('motor1.component_weight', 'motor1_weight') self.connect('motor2.component_weight', 'motor2_weight') self.connect('prop1.component_weight', 'prop1_weight') self.connect('prop2.component_weight', 'prop2_weight') #connect design variables to model component inputs self.connect('eng_rating', 'eng1.shaft_power_rating') self.connect('prop_diameter', ['prop1.diameter', 'prop2.diameter']) self.connect('motor_rating', ['motor1.elec_power_rating', 'motor2.elec_power_rating']) self.connect('motor_rating', ['prop1.power_rating', 'prop2.power_rating']) self.connect('gen_rating', 'gen1.elec_power_rating') self.connect('batt_weight', 'batt1.battery_weight')