Пример #1
0
 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=["*"])
Пример #2
0
    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'])
Пример #3
0
 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=["*"])
Пример #4
0
    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'])
Пример #5
0
    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'
        ])
Пример #7
0
    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=["*"])
Пример #8
0
    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')
Пример #9
0
    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')
Пример #10
0
    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')
Пример #11
0
    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'])
Пример #12
0
    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')