def setup_energy_opt(num_seg, order, q_tank, q_hx1, q_hx2, opt_burn=False):
    """
    Helper function to set up and return a problem instance for an energy minimization
    of a simple thermal system.

    Parameters
    ----------
    num_seg : int
        The number of ODE segments to use when discretizing the problem.
    order : int
        The order for the polynomial interpolation for the collocation methods.
    q_tank : float
        The amount of inputted heat to the fuel tank. Positive is heat inputted
        to the fuel tank.
    q_hx1 : float
        A measure of the amount of heat added to the fuel at the first heat exchanger.
        This mimics the fuel handling the heat generated from a thermal load,
        such as avionicsc or air conditioners.
    q_hx2 : float
        A measure of the amount of heat added to the fuel at the second heat exchanger.
        This may be a negative number, which means that heat is being taken out
        of the fuel in some way.
    opt_burn : boolean
        If true, we allow the optimizer to control the amount of fuel burned
        in the system. This mimics the cost of the fuel needed in the plane
        to provide thrust.
    """

    # Instantiate the problem and set the optimizer
    p = Problem(model=Group())
    p.driver = pyOptSparseDriver()
    p.driver.options['optimizer'] = 'SNOPT'
    p.driver.opt_settings['Major iterations limit'] = 2000
    p.driver.opt_settings['Major feasibility tolerance'] = 1.0E-7
    p.driver.opt_settings['Major optimality tolerance'] = 1.0E-7
    p.driver.opt_settings['Verify level'] = -1

    # Set up the phase for the defined ODE function, can be LGR or LGL
    phase = Phase('gauss-lobatto',
                  ode_class=SimpleHeatODE,
                  ode_init_kwargs={
                      'q_tank': q_tank,
                      'q_hx1': q_hx1,
                      'q_hx2': q_hx2
                  },
                  num_segments=num_seg,
                  transcription_order=order)

    # Do not allow the time to vary during the optimization
    phase.set_time_options(opt_initial=False, opt_duration=False)

    # Set the state options for mass, temperature, and energy.
    phase.set_state_options('m', lower=1., upper=10., fix_initial=True)
    phase.set_state_options('T', fix_initial=True, defect_scaler=.01)
    phase.set_state_options('energy', fix_initial=True)

    # Minimize the energy used to pump the fuel
    phase.set_objective('energy', loc='final')

    # Allow the optimizer to vary the fuel flow
    phase.add_control('m_flow',
                      opt=True,
                      lower=0.,
                      upper=5.,
                      rate_continuity=True)

    # Optimize the burned fuel amount, if selected
    if opt_burn:
        phase.add_control('m_burn',
                          opt=opt_burn,
                          lower=.2,
                          upper=5.,
                          dynamic=False)
    else:
        phase.add_control('m_burn', opt=opt_burn)

    # Constrain the temperature, 2nd derivative of fuel mass in the tank, and make
    # sure that the amount recirculated is at least 0, otherwise we'd burn
    # more fuel than we pumped.
    phase.add_path_constraint('T', upper=1.)
    phase.add_path_constraint('m_flow_rate', upper=0.)
    phase.add_path_constraint('m_flow', upper=1.)
    phase.add_path_constraint('fuel_burner.m_recirculated', lower=0.)

    # Add the phase to the problem and set it up
    p.model.add_subsystem('phase', phase)
    p.driver.add_recorder(SqliteRecorder('out.db'))
    p.setup(check=True, force_alloc_complex=True)

    # Give initial values for the phase states, controls, and time
    p['phase.states:m'] = 10.
    p['phase.states:T'] = 1.
    p['phase.states:energy'] = 0.
    p['phase.controls:m_flow'] = .5
    p['phase.controls:m_burn'] = .1
    p['phase.t_initial'] = 0.
    p['phase.t_duration'] = 2.

    return p
Пример #2
0
def escort_problem(optimizer='SLSQP',
                   num_seg=3,
                   transcription_order=5,
                   transcription='gauss-lobatto',
                   meeting_altitude=14000.,
                   climb_time=350.,
                   starting_mass=19030.468):

    p = Problem(model=Group())

    p.driver = pyOptSparseDriver()
    p.driver.options['optimizer'] = optimizer
    if optimizer == 'SNOPT':
        p.driver.opt_settings['Major iterations limit'] = 1000
        p.driver.opt_settings['Iterations limit'] = 100000000
        p.driver.opt_settings['iSumm'] = 6
        p.driver.opt_settings['Major feasibility tolerance'] = 1.0E-6
        p.driver.opt_settings['Major optimality tolerance'] = 1.0E-5
        p.driver.opt_settings['Verify level'] = -1
        p.driver.opt_settings['Function precision'] = 1.0E-6
        p.driver.opt_settings['Linesearch tolerance'] = 0.10
        p.driver.opt_settings['Major step limit'] = 0.5

    phase_class = _phase_map[transcription]

    climb = Phase('gauss-lobatto',
                  ode_class=MinTimeClimbODE,
                  num_segments=num_seg,
                  transcription_order=transcription_order)

    climb.set_time_options(duration_bounds=(50, climb_time),
                           duration_ref=100.0)

    climb.set_state_options('r',
                            fix_initial=True,
                            lower=0,
                            upper=1.0E6,
                            scaler=1.0E-3,
                            defect_scaler=1.0E-2,
                            units='m')

    climb.set_state_options('h',
                            fix_initial=True,
                            lower=0,
                            upper=20000.0,
                            scaler=1.0E-3,
                            defect_scaler=1.0E-3,
                            units='m')

    climb.set_state_options('v',
                            fix_initial=True,
                            lower=10.0,
                            scaler=1.0E-2,
                            defect_scaler=1.0E-2,
                            units='m/s')

    climb.set_state_options('gam',
                            fix_initial=True,
                            lower=-1.5,
                            upper=1.5,
                            ref=1.0,
                            defect_scaler=1.0,
                            units='rad')

    climb.set_state_options('m',
                            fix_initial=True,
                            lower=10.0,
                            upper=1.0E5,
                            scaler=1.0E-3,
                            defect_scaler=1.0E-3)

    climb.add_control('alpha',
                      units='deg',
                      lower=-8.0,
                      upper=8.0,
                      scaler=1.0,
                      rate_continuity=True)

    climb.add_control('S', val=49.2386, units='m**2', dynamic=False, opt=False)
    climb.add_control('Isp', val=5000.0, units='s', dynamic=False, opt=False)
    climb.add_control('throttle', val=1.0, dynamic=False, opt=False)

    climb.add_boundary_constraint('h',
                                  loc='final',
                                  equals=meeting_altitude,
                                  scaler=1.0E-3,
                                  units='m')
    climb.add_boundary_constraint('aero.mach',
                                  loc='final',
                                  equals=1.0,
                                  units=None)
    climb.add_boundary_constraint('gam', loc='final', equals=0.0, units='rad')

    # climb.add_boundary_constraint('time', loc='final', upper=climb_time, units='s')

    climb.add_path_constraint(name='h', lower=100.0, upper=20000, ref=20000)
    climb.add_path_constraint(name='aero.mach', lower=0.1, upper=1.8)

    # Minimize time at the end of the climb
    climb.set_objective('time', loc='final', ref=100.0)

    p.model.add_subsystem('climb', climb)

    escort = Phase('gauss-lobatto',
                   ode_class=MinTimeClimbODE,
                   num_segments=num_seg * 2,
                   transcription_order=transcription_order)

    escort.set_time_options(duration_bounds=(50, 10000),
                            opt_initial=True,
                            duration_ref=100.0)

    escort.set_state_options('r',
                             lower=0,
                             upper=1.0E6,
                             scaler=1.0E-3,
                             defect_scaler=1.0E-2,
                             units='m')

    escort.set_state_options('h',
                             lower=0,
                             upper=20000.0,
                             scaler=1.0E-3,
                             defect_scaler=1.0E-3,
                             units='m')

    escort.set_state_options('v',
                             lower=10.0,
                             scaler=1.0E-2,
                             defect_scaler=1.0E-2,
                             units='m/s')

    escort.set_state_options('gam',
                             lower=-1.5,
                             upper=1.5,
                             ref=1.0,
                             defect_scaler=1.0,
                             units='rad')

    escort.set_state_options('m',
                             lower=10.0,
                             upper=1.0E5,
                             scaler=1.0E-3,
                             defect_scaler=1.0E-3)

    escort.add_control('alpha',
                       units='deg',
                       lower=-8.0,
                       upper=8.0,
                       scaler=1.0,
                       rate_continuity=True)

    escort.add_control('S',
                       val=49.2386,
                       units='m**2',
                       dynamic=False,
                       opt=False)
    escort.add_control('Isp', val=1600.0, units='s', dynamic=False, opt=False)

    escort.add_control('throttle', val=1.0, lower=0., upper=1., opt=True)
    # escort.add_control('throttle', val=1.0, dynamic=False, opt=False)

    # escort.add_boundary_constraint('h', loc='final', equals=20000, scaler=1.0E-3, units='m')
    # escort.add_boundary_constraint('aero.mach', loc='final', equals=1.0, units=None)
    # escort.add_boundary_constraint('gam', loc='final', equals=0.0, units='rad')
    escort.add_boundary_constraint('m',
                                   loc='final',
                                   equals=15000.0,
                                   units='kg')

    escort.add_path_constraint(name='h',
                               lower=meeting_altitude,
                               upper=meeting_altitude,
                               ref=meeting_altitude)
    escort.add_path_constraint(name='aero.mach', equals=1.0)

    # Maximize distance at the end of the escort
    # escort.set_objective('r', loc='final', ref=-1e5)

    p.model.add_subsystem('escort', escort)

    # Connect the phases
    linkage_comp = PhaseLinkageComp()
    linkage_comp.add_linkage(name='L01',
                             vars=['t'],
                             units='s',
                             equals=0.0,
                             linear=True)
    linkage_comp.add_linkage(name='L01',
                             vars=['r'],
                             units='m',
                             equals=0.0,
                             linear=True)
    linkage_comp.add_linkage(name='L01',
                             vars=['h'],
                             units='m',
                             equals=0.0,
                             linear=True)
    linkage_comp.add_linkage(name='L01',
                             vars=['v'],
                             units='m/s',
                             equals=0.0,
                             linear=True)
    linkage_comp.add_linkage(name='L01',
                             vars=['gam'],
                             units='rad',
                             equals=0.0,
                             linear=True)
    linkage_comp.add_linkage(name='L01',
                             vars=['m'],
                             units='kg',
                             equals=0.0,
                             linear=True)
    linkage_comp.add_linkage(name='L01',
                             vars=['alpha'],
                             units='rad',
                             equals=0.0,
                             linear=True)
    linkage_comp.add_linkage(name='L01',
                             vars=['throttle'],
                             equals=0.0,
                             linear=True)

    p.model.connect('climb.time++', 'linkages.L01_t:lhs')
    p.model.connect('escort.time--', 'linkages.L01_t:rhs')

    p.model.connect('climb.states:r++', 'linkages.L01_r:lhs')
    p.model.connect('escort.states:r--', 'linkages.L01_r:rhs')

    p.model.connect('climb.states:h++', 'linkages.L01_h:lhs')
    p.model.connect('escort.states:h--', 'linkages.L01_h:rhs')
    #
    p.model.connect('climb.states:v++', 'linkages.L01_v:lhs')
    p.model.connect('escort.states:v--', 'linkages.L01_v:rhs')
    #
    p.model.connect('climb.states:gam++', 'linkages.L01_gam:lhs')
    p.model.connect('escort.states:gam--', 'linkages.L01_gam:rhs')

    p.model.connect('climb.states:m++', 'linkages.L01_m:lhs')
    p.model.connect('escort.states:m--', 'linkages.L01_m:rhs')

    p.model.connect('climb.controls:alpha++', 'linkages.L01_alpha:lhs')
    p.model.connect('escort.controls:alpha--', 'linkages.L01_alpha:rhs')

    p.model.connect('climb.controls:throttle++', 'linkages.L01_throttle:lhs')
    p.model.connect('escort.controls:throttle--', 'linkages.L01_throttle:rhs')

    p.model.add_subsystem('linkages', linkage_comp)

    p.model.linear_solver = DirectSolver(assemble_jac=True)
    p.model.options['assembled_jac_type'] = 'csc'

    # p.driver.add_recorder(SqliteRecorder('escort.db'))

    p.setup(mode='fwd', check=True)

    p['climb.t_initial'] = 0.0
    p['climb.t_duration'] = 200.
    p['climb.states:r'] = climb.interpolate(ys=[0.0, 111319.54], nodes='disc')
    p['climb.states:h'] = climb.interpolate(ys=[100.0, 20000.0], nodes='disc')
    p['climb.states:v'] = climb.interpolate(ys=[135.964, 283.159],
                                            nodes='disc')
    p['climb.states:gam'] = climb.interpolate(ys=[0.0, 0.0], nodes='disc')
    p['climb.states:m'] = climb.interpolate(ys=[starting_mass, 16841.431],
                                            nodes='disc')
    p['climb.controls:alpha'] = climb.interpolate(ys=[0.0, 0.0], nodes='all')

    p['escort.t_initial'] = 200.
    p['escort.t_duration'] = 1000.
    p['escort.states:r'] = escort.interpolate(ys=[111319.54, 400000.],
                                              nodes='disc')
    p['escort.states:h'] = escort.interpolate(
        ys=[meeting_altitude, meeting_altitude], nodes='disc')
    p['escort.states:v'] = escort.interpolate(ys=[250., 250.], nodes='disc')
    p['escort.states:gam'] = escort.interpolate(ys=[0.0, 0.0], nodes='disc')
    p['escort.states:m'] = escort.interpolate(ys=[17000., 15000.],
                                              nodes='disc')
    p['escort.controls:alpha'] = escort.interpolate(ys=[0.0, 0.0], nodes='all')

    return p