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
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