Пример #1
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'])
Пример #2
0
    def setup(self):
        nn = self.options['num_nodes']
        flight_phase = self.options['flight_phase']


        # 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|*", "throttle"]

        self.add_subsystem('propmodel', CFM56(num_nodes=nn, plot=False),
                           promotes_inputs=propulsion_promotes_inputs)

        doubler = om.ExecComp(['thrust=2*thrust_in', 'fuel_flow=2*fuel_flow_in'], 
                  thrust_in={'val': 1.0*np.ones((nn,)),
                     'units': 'kN'},
                  thrust={'val': 1.0*np.ones((nn,)),
                       'units': 'kN'},
                  fuel_flow={'val': 1.0*np.ones((nn,)),
                     'units': 'kg/s',
                     'tags': ['integrate', 'state_name:fuel_used', 'state_units:kg', 'state_val:1.0', 'state_promotes:True']},
                  fuel_flow_in={'val': 1.0*np.ones((nn,)),
                       'units': 'kg/s'})
        
        self.add_subsystem('doubler', doubler, promotes_outputs=['*'])
        self.connect('propmodel.thrust', 'doubler.thrust_in')
        self.connect('propmodel.fuel_flow', 'doubler.fuel_flow_in')

        # 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
        passthru = om.ExecComp('OEW=x',
                  x={'val': 1.0,
                     'units': 'kg'},
                  OEW={'val': 1.0,
                       'units': 'kg'})
        self.add_subsystem('OEW', passthru,
                           promotes_inputs=[('x', 'ac|weights|OEW')],
                           promotes_outputs=['OEW'])

        self.add_subsystem('weight', oc.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):
        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'])
Пример #4
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', om.IndepVarComp(), promotes_outputs=['*'])
        controls.add_output('prop1rpm', val=np.ones((nn,)) * 2000, 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"]

        self.add_subsystem('propmodel', TurbopropPropulsionSystem(num_nodes=nn),
                           promotes_inputs=propulsion_promotes_inputs,
                           promotes_outputs=propulsion_promotes_outputs)
        self.connect('prop1rpm', 'propmodel.prop1.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.prop1.component_weight', 'W_propeller')
        self.connect('propmodel.eng1.component_weight', 'W_engine')

        # airplanes which consume fuel will need to integrate
        # fuel usage across the mission and subtract it from TOW
        intfuel = self.add_subsystem('intfuel', oc.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', oc.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']
        iv = self.add_subsystem('conditions',
                                IndepVarComp(),
                                promotes_outputs=['*'])
        self.add_subsystem('polardrag',
                           PolarDrag(num_nodes=nn),
                           promotes_inputs=['*'],
                           promotes_outputs=['*'])

        iv.add_output('fltcond|CL', val=np.linspace(0, 1.5, nn))
        iv.add_output('fltcond|q',
                      val=np.ones(nn) * 0.5 * 1.225 * 70**2,
                      units='N * m**-2')
        iv.add_output('ac|geom|wing|S_ref', val=30, units='m**2')
        iv.add_output('ac|geom|wing|AR', val=15)
        iv.add_output('CD0', val=0.02)
        iv.add_output('e', val=0.8)
Пример #6
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('prop1rpm',
                            val=np.ones((nn, )) * 2000,
                            units='rpm')

        propulsion_promotes_outputs = ['thrust']
        propulsion_promotes_inputs = [
            "fltcond|*", "ac|propulsion|*", "throttle", "ac|weights|*",
            "duration"
        ]

        self.add_subsystem(
            'propmodel',
            AllElectricSinglePropulsionSystemWithThermal_Compressible(
                num_nodes=nn),
            promotes_inputs=propulsion_promotes_inputs,
            promotes_outputs=propulsion_promotes_outputs)
        self.connect('prop1rpm', 'propmodel.prop1.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'])
        self.add_subsystem('weight',
                           LinearInterpolator(num_nodes=nn, units='kg'),
                           promotes_inputs=[('start_val', 'ac|weights|MTOW'),
                                            ('end_val', 'ac|weights|MTOW')],
                           promotes_outputs=[('vec', 'weight')])
Пример #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):
        nn = self.options['num_nodes']
        flight_phase = self.options['flight_phase']

        #=============AERODYNAMICS======================
        # 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('airframe_drag',
                           PolarDrag(num_nodes=nn),
                           promotes_inputs=[
                               'fltcond|CL', 'ac|geom|*', ('CD0', cd0_source),
                               'fltcond|q', ('e', 'ac|aero|polar|e')
                           ])
        self.promote_add(sources=[
            'airframe_drag.drag', 'variable_duct.force.F_net',
            'motor_duct.force.F_net'
        ],
                         prom_name='drag',
                         factors=[1.0, -2.0, -2.0],
                         vec_size=nn,
                         units='N')

        #=============PROPULSION=======================
        # Hybrid propulsion motor (model one side only)
        self.add_subsystem('hybrid_throttle',
                           LinearInterpolator(num_nodes=nn, units=None),
                           promotes_inputs=[
                               ('start_val', 'hybrid_throttle_start'),
                               ('end_val', 'hybrid_throttle_end')
                           ])

        self.add_subsystem('hybrid_motor',
                           SimpleMotor(num_nodes=nn, efficiency=0.95),
                           promotes_inputs=[('elec_power_rating',
                                             'ac|propulsion|motor|rating')])
        self.connect('hybrid_motor.shaft_power_out', 'engine.hybrid_power')
        self.connect('hybrid_throttle.vec', 'hybrid_motor.throttle')
        # Add a surrogate model for the engine. Inputs are Mach, Alt, Throttle, Hybrid power
        self.add_subsystem('engine',
                           N3Hybrid(num_nodes=nn, plot=False),
                           promotes_inputs=["fltcond|*", "throttle"])

        # double the thrust and fuel flow of the engine and integrate fuel flow
        self.promote_mult('engine.thrust',
                          prom_name='thrust',
                          factor=2.0,
                          vec_size=nn,
                          units='kN')
        self.promote_mult('engine.fuel_flow',
                          prom_name='fuel_flow',
                          factor=2.0,
                          vec_size=nn,
                          units='kg/s',
                          tags=[
                              'integrate', 'state_name:fuel_used',
                              'state_units:kg', 'state_val:1.0',
                              'state_promotes:True'
                          ])
        # Hybrid propulsion battery
        self.add_subsystem('battery',
                           SOCBattery(num_nodes=nn,
                                      efficiency=0.95,
                                      specific_energy=400),
                           promotes_inputs=[('battery_weight',
                                             'ac|propulsion|battery|weight')])
        self.promote_add(sources=[
            'hybrid_motor.elec_load', 'refrig.elec_load',
            'motorfaultprot.elec_load', 'battery_coolant_pump.elec_load',
            'motor_coolant_pump.elec_load'
        ],
                         prom_name='elec_load',
                         factors=[1.0, 1.0, 1.0, 1.0, 1.0],
                         vec_size=nn,
                         val=1.0,
                         units='kW')
        self.connect('elec_load', 'battery.elec_load')

        #=============THERMAL======================
        thermal_params = self.add_subsystem('thermal_params',
                                            om.IndepVarComp(),
                                            promotes_outputs=['*'])
        # properties
        thermal_params.add_output('rho_coolant',
                                  val=1020 * np.ones((nn, )),
                                  units='kg/m**3')
        # controls
        thermal_params.add_output('mdot_coolant_battery',
                                  val=4.8 * np.ones((nn, )),
                                  units='kg/s')
        thermal_params.add_output('mdot_coolant_motor',
                                  val=1.2 * np.ones((nn, )),
                                  units='kg/s')
        # fault protection needs separate cooler because it needs 40C inflow temp at 3gpm
        thermal_params.add_output('mdot_coolant_fault_prot',
                                  val=0.19 * np.ones((nn, )),
                                  units='kg/s')

        thermal_params.add_output('bypass_heat_pump', val=np.ones((nn, )))
        thermal_params.add_output('variable_duct_nozzle_area_start',
                                  val=20,
                                  units='inch**2')
        thermal_params.add_output('variable_duct_nozzle_area_end',
                                  val=20,
                                  units='inch**2')
        thermal_params.add_output('heat_pump_specific_power',
                                  val=200.,
                                  units='W/kg')
        thermal_params.add_output('heat_pump_eff_factor', val=0.4, units=None)

        self.add_subsystem('li_battery',
                           LinearInterpolator(num_nodes=nn, units='inch**2'),
                           promotes_outputs=[('vec',
                                              'variable_duct_nozzle_area')])
        self.connect('variable_duct_nozzle_area_start', 'li_battery.start_val')
        self.connect('variable_duct_nozzle_area_end', 'li_battery.end_val')
        self.add_subsystem(
            'li_motor',
            LinearInterpolator(num_nodes=nn, units='inch**2'),
            promotes_inputs=[
                ('start_val', 'ac|propulsion|thermal|hx_motor|nozzle_area'),
                ('end_val', 'ac|propulsion|thermal|hx_motor|nozzle_area')
            ],
            promotes_outputs=[('vec', 'motor_duct_area_nozzle_in')])

        hx_design_vars = [
            'ac|propulsion|thermal|hx|n_wide_cold',
            'ac|propulsion|thermal|hx|n_long_cold',
            'ac|propulsion|thermal|hx|n_tall'
        ]

        #===========MOTOR LOOP=======================

        self.add_subsystem('motorheatsink',
                           LiquidCooledMotor(num_nodes=nn,
                                             case_cooling_coefficient=2100.,
                                             quasi_steady=False),
                           promotes_inputs=[('power_rating',
                                             'ac|propulsion|motor|rating')])
        self.connect('hybrid_motor.heat_out', 'motorheatsink.q_in')
        self.connect('hybrid_motor.component_weight',
                     'motorheatsink.motor_weight')

        self.add_subsystem('motorfaultprot',
                           MotorFaultProtection(num_nodes=nn))
        self.connect('hybrid_motor.elec_load', 'motorfaultprot.motor_power')

        self.add_subsystem('hx_motor',
                           HXGroup(num_nodes=nn),
                           promotes_inputs=[(a, a.replace('hx', 'hx_motor'))
                                            for a in hx_design_vars])
        self.connect('rho_coolant', 'hx_motor.rho_hot')
        fault_prot_promotes = [
            ('ac|propulsion|thermal|hx|n_wide_cold',
             'ac|propulsion|thermal|hx_motor|n_wide_cold'),
            ('ac|propulsion|thermal|hx|n_long_cold',
             'ac|propulsion|thermal|hx_fault_prot|n_long_cold'),
            ('ac|propulsion|thermal|hx|n_tall',
             'ac|propulsion|thermal|hx_motor|n_tall')
        ]
        self.add_subsystem('hx_fault_prot',
                           HXGroup(num_nodes=nn),
                           promotes_inputs=fault_prot_promotes)
        self.connect('rho_coolant', [
            'hx_fault_prot.rho_hot', 'motor_hose.rho_coolant',
            'motor_coolant_pump.rho_coolant'
        ])

        self.add_subsystem('motor_duct',
                           ImplicitCompressibleDuct_ExternalHX(num_nodes=nn,
                                                               cfg=0.90),
                           promotes_inputs=[('p_inf', 'fltcond|p'),
                                            ('T_inf', 'fltcond|T'),
                                            ('Utrue', 'fltcond|Utrue'),
                                            ('area_nozzle_in',
                                             'motor_duct_area_nozzle_in')])

        self.add_subsystem('motor_hose',
                           SimpleHose(num_nodes=nn),
                           promotes_inputs=[
                               ('hose_length',
                                'ac|geom|thermal|hx_to_motor_length'),
                               ('hose_diameter',
                                'ac|geom|thermal|hx_to_motor_diameter')
                           ])
        self.add_subsystem(
            'motor_coolant_pump',
            SimplePump(num_nodes=nn),
            promotes_inputs=[
                ('power_rating',
                 'ac|propulsion|thermal|hx_motor|pump_power_rating')
            ])
        self.promote_add(
            sources=['motor_hose.delta_p', 'hx_motor.delta_p_hot'],
            prom_name='pressure_drop_motor_loop',
            factors=[1.0, -1.0],
            vec_size=nn,
            units='Pa')
        self.connect('pressure_drop_motor_loop', 'motor_coolant_pump.delta_p')

        # in to HXGroup:
        self.connect('motor_duct.sta2.T', 'hx_fault_prot.T_in_cold')
        self.connect('hx_fault_prot.T_out_cold', 'hx_motor.T_in_cold')

        self.connect('motor_duct.sta2.rho',
                     ['hx_motor.rho_cold', 'hx_fault_prot.rho_cold'])
        self.connect('motor_duct.mdot',
                     ['hx_motor.mdot_cold', 'hx_fault_prot.mdot_cold'])
        self.connect('motorheatsink.T_out', 'hx_motor.T_in_hot')
        self.connect('motorfaultprot.T_out', 'hx_fault_prot.T_in_hot')
        self.connect('mdot_coolant_motor', [
            'motorheatsink.mdot_coolant', 'hx_motor.mdot_hot',
            'motor_hose.mdot_coolant', 'motor_coolant_pump.mdot_coolant'
        ])
        self.connect('mdot_coolant_fault_prot',
                     ['motorfaultprot.mdot_coolant', 'hx_fault_prot.mdot_hot'])

        #out from HXGroup
        self.connect('hx_motor.frontal_area',
                     ['motor_duct.area_2', 'motor_duct.area_3'])
        self.connect('hx_motor.delta_p_cold',
                     'motorfaultprot.delta_p_motor_hx')
        self.connect('hx_fault_prot.delta_p_cold',
                     'motorfaultprot.delta_p_fault_prot_hx')
        self.connect('motorfaultprot.delta_p_stack', 'motor_duct.sta3.delta_p')

        self.connect('hx_motor.heat_transfer',
                     'motorfaultprot.heat_transfer_motor_hx')
        self.connect('hx_fault_prot.heat_transfer',
                     'motorfaultprot.heat_transfer_fault_prot_hx')
        self.connect('motorfaultprot.heat_transfer', 'motor_duct.sta3.heat_in')

        self.connect('hx_motor.T_out_hot', 'motorheatsink.T_in')
        self.connect('hx_fault_prot.T_out_hot', 'motorfaultprot.T_in')

        #=========BATTERY LOOP=====================
        self.add_subsystem('batteryheatsink',
                           LiquidCooledBattery(num_nodes=nn,
                                               quasi_steady=False),
                           promotes_inputs=[('battery_weight',
                                             'ac|propulsion|battery|weight')])
        self.connect('battery.heat_out', 'batteryheatsink.q_in')

        # self.connect('mdot_coolant_battery', ['batteryheatsink.mdot_coolant', 'hx_battery.mdot_hot', 'battery_hose.mdot_coolant', 'battery_coolant_pump.mdot_coolant'])
        self.connect('mdot_coolant_battery', [
            'batteryheatsink.mdot_coolant', 'refrig.mdot_coolant',
            'hx_battery.mdot_hot', 'battery_hose.mdot_coolant',
            'battery_coolant_pump.mdot_coolant'
        ])

        self.connect('batteryheatsink.T_out', 'refrig.T_in_cold')
        self.connect('refrig.T_out_cold', 'batteryheatsink.T_in')

        self.add_subsystem('hx_battery',
                           HXGroup(num_nodes=nn),
                           promotes_inputs=hx_design_vars)
        self.connect('rho_coolant', [
            'hx_battery.rho_hot', 'battery_hose.rho_coolant',
            'battery_coolant_pump.rho_coolant'
        ])

        # Hot side balance param will be set to the cooling duct nozzle area
        self.add_subsystem('refrig',
                           HeatPumpWithIntegratedCoolantLoop(num_nodes=nn),
                           promotes_inputs=[
                               ('power_rating',
                                'ac|propulsion|thermal|heatpump|power_rating')
                           ])
        self.connect('heat_pump_eff_factor', 'refrig.eff_factor')
        self.connect('heat_pump_specific_power', 'refrig.specific_power')

        self.add_subsystem('variable_duct',
                           ImplicitCompressibleDuct_ExternalHX(num_nodes=nn,
                                                               cfg=0.95),
                           promotes_inputs=[('p_inf', 'fltcond|p'),
                                            ('T_inf', 'fltcond|T'),
                                            ('Utrue', 'fltcond|Utrue')])
        self.connect('variable_duct_nozzle_area',
                     'variable_duct.area_nozzle_in')

        self.add_subsystem('battery_hose',
                           SimpleHose(num_nodes=nn),
                           promotes_inputs=[
                               ('hose_length',
                                'ac|geom|thermal|hx_to_battery_length'),
                               ('hose_diameter',
                                'ac|geom|thermal|hx_to_battery_diameter')
                           ])
        self.add_subsystem('battery_coolant_pump',
                           SimplePump(num_nodes=nn),
                           promotes_inputs=[
                               ('power_rating',
                                'ac|propulsion|thermal|hx|pump_power_rating')
                           ])
        self.promote_add(
            sources=['battery_hose.delta_p', 'hx_battery.delta_p_hot'],
            prom_name='pressure_drop_battery_loop',
            factors=[1.0, -1.0],
            vec_size=nn,
            units='Pa')
        self.connect('pressure_drop_battery_loop',
                     'battery_coolant_pump.delta_p')

        # in to HXGroup:
        self.connect('variable_duct.sta2.T', 'hx_battery.T_in_cold')
        self.connect('variable_duct.sta2.rho', 'hx_battery.rho_cold')
        self.connect('variable_duct.mdot', 'hx_battery.mdot_cold')

        #out from HXGroup
        self.connect('hx_battery.frontal_area',
                     ['variable_duct.area_2', 'variable_duct.area_3'])
        self.connect('hx_battery.delta_p_cold', 'variable_duct.sta3.delta_p')
        self.connect('hx_battery.heat_transfer', 'variable_duct.sta3.heat_in')
        self.connect('hx_battery.T_out_hot', 'refrig.T_in_hot')
        self.connect('refrig.T_out_hot', 'hx_battery.T_in_hot')

        # self.connect('hx_battery.T_out_hot', 'batteryheatsink.T_in')
        # self.connect('batteryheatsink.T_out', 'hx_battery.T_in_hot')

        #=============WEIGHTS======================

        # generally the weights module will be custom to each airplane

        # Motor, Battery, TMS, N+3 weight delta
        self.promote_add(sources=[
            'refrig.component_weight', 'hx_motor.component_weight',
            'hx_battery.component_weight', 'battery_hose.component_weight',
            'battery_coolant_pump.component_weight',
            'motor_hose.component_weight',
            'motor_coolant_pump.component_weight',
            'hx_fault_prot.component_weight', 'hybrid_motor.component_weight'
        ],
                         promoted_sources=['ac|weights|OEW'],
                         prom_name='OEW',
                         factors=[
                             2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 1.0
                         ],
                         vec_size=1,
                         units='kg')

        self.add_subsystem('weight',
                           oc.AddSubtractComp(output_name='weight',
                                              input_names=[
                                                  'ac|design_mission|TOW',
                                                  'fuel_used'
                                              ],
                                              units='kg',
                                              vec_size=[1, nn],
                                              scaling_factors=[1, -1]),
                           promotes_inputs=['*'],
                           promotes_outputs=['weight'])

        self.promote_add(sources=[],
                         promoted_sources=[
                             'ac|design_mission|TOW', 'OEW', 'fuel_used_final',
                             'ac|propulsion|battery|weight'
                         ],
                         prom_name='margin',
                         factors=[1.0, -1.0, -1.0, -2.0],
                         vec_size=1,
                         units='kg')