def test_hyper_sensitive_for_docs(self): import openmdao.api as om from openmdao.utils.assert_utils import assert_rel_error import dymos as dm from dymos.examples.plotting import plot_results from dymos.examples.hyper_sensitive.hyper_sensitive_ode import HyperSensitiveODE # Initialize the problem and assign the driver p = om.Problem(model=om.Group()) p.driver = om.pyOptSparseDriver() p.driver.options['optimizer'] = 'SLSQP' p.driver.declare_coloring() # Setup the trajectory and its phase traj = p.model.add_subsystem('traj', dm.Trajectory()) transcription = dm.Radau(num_segments=30, order=3, compressed=False) phase = traj.add_phase('phase0', dm.Phase(ode_class=HyperSensitiveODE, transcription=transcription)) phase.set_time_options(fix_initial=True, fix_duration=True) phase.add_state('x', fix_initial=True, fix_final=False, rate_source='x_dot', targets=['x']) phase.add_state('xL', fix_initial=True, fix_final=False, rate_source='L', targets=['xL']) phase.add_control('u', opt=True, targets=['u']) phase.add_boundary_constraint('x', loc='final', equals=1) phase.add_objective('xL', loc='final') p.setup(check=True) p.set_val('traj.phase0.states:x', phase.interpolate(ys=[1.5, 1], nodes='state_input')) p.set_val('traj.phase0.states:xL', phase.interpolate(ys=[0, 1], nodes='state_input')) p.set_val('traj.phase0.t_initial', 0) p.set_val('traj.phase0.t_duration', tf) p.set_val('traj.phase0.controls:u', phase.interpolate(ys=[-0.6, 2.4], nodes='control_input')) # # Solve the problem. # dm.run_problem(p) # # Verify that the results are correct. # ui, uf, J = solution() assert_rel_error(self, p.get_val('traj.phase0.timeseries.controls:u')[0], ui, tolerance=1.5e-2) assert_rel_error(self, p.get_val('traj.phase0.timeseries.controls:u')[-1], uf, tolerance=1.5e-2) assert_rel_error(self, p.get_val('traj.phase0.timeseries.states:xL')[-1], J, tolerance=1e-2) # # Simulate the explicit solution and plot the results. # exp_out = traj.simulate() plot_results([('traj.phase0.timeseries.time', 'traj.phase0.timeseries.states:x', 'time (s)', 'x $(m)$'), ('traj.phase0.timeseries.time', 'traj.phase0.timeseries.controls:u', 'time (s)', 'u $(m/s^2)$')], title='Hyper Sensitive Problem Solution\nRadau Pseudospectral Method', p_sol=p, p_sim=exp_out) plt.show()
def test_connect_control_to_parameter(self): """ Test that the final value of a control in one phase can be connected as the value of a parameter in a subsequent phase. """ import openmdao.api as om from openmdao.utils.assert_utils import assert_near_equal import dymos as dm from dymos.examples.cannonball.size_comp import CannonballSizeComp p = om.Problem(model=om.Group()) p.driver = om.pyOptSparseDriver() p.driver.options['optimizer'] = 'SLSQP' p.driver.declare_coloring() external_params = p.model.add_subsystem('external_params', om.IndepVarComp()) external_params.add_output('radius', val=0.10, units='m') external_params.add_output('dens', val=7.87, units='g/cm**3') external_params.add_design_var('radius', lower=0.01, upper=0.10, ref0=0.01, ref=0.10) p.model.add_subsystem('size_comp', CannonballSizeComp()) traj = p.model.add_subsystem('traj', dm.Trajectory()) transcription = dm.Radau(num_segments=5, order=3, compressed=True) ascent = dm.Phase(ode_class=CannonballODEVectorCD, transcription=transcription) ascent = traj.add_phase('ascent', ascent) # All initial states except flight path angle are fixed # Final flight path angle is fixed (we will set it to zero so that the phase ends at apogee) ascent.set_time_options(fix_initial=True, duration_bounds=(1, 100), duration_ref=100, units='s') ascent.add_state('r', fix_initial=True, fix_final=False, rate_source='r_dot', units='m') ascent.add_state('h', fix_initial=True, fix_final=False, units='m', rate_source='h_dot') ascent.add_state('gam', fix_initial=False, fix_final=True, units='rad', rate_source='gam_dot') ascent.add_state('v', fix_initial=False, fix_final=False, units='m/s', rate_source='v_dot') ascent.add_parameter('S', targets=['S'], units='m**2', static_target=True) ascent.add_parameter('mass', targets=['m'], units='kg', static_target=True) ascent.add_control('CD', targets=['CD'], opt=False, val=0.05) # Limit the muzzle energy ascent.add_boundary_constraint('ke', loc='initial', upper=400000, lower=0, ref=100000) # Second Phase (descent) transcription = dm.GaussLobatto(num_segments=5, order=3, compressed=True) descent = dm.Phase(ode_class=CannonballODEVectorCD, transcription=transcription) traj.add_phase('descent', descent) # All initial states and time are free (they will be linked to the final states of ascent. # Final altitude is fixed (we will set it to zero so that the phase ends at ground impact) descent.set_time_options(initial_bounds=(.5, 100), duration_bounds=(.5, 100), duration_ref=100, units='s') descent.add_state('r', units='m', rate_source='r_dot') descent.add_state('h', units='m', rate_source='h_dot', fix_initial=False, fix_final=True) descent.add_state('gam', units='rad', rate_source='gam_dot', fix_initial=False, fix_final=False) descent.add_state('v', units='m/s', rate_source='v_dot', fix_initial=False, fix_final=False) descent.add_parameter('S', targets=['S'], units='m**2', static_target=True) descent.add_parameter('mass', targets=['m'], units='kg', static_target=True) descent.add_parameter('CD', targets=['CD'], val=0.01) descent.add_objective('r', loc='final', scaler=-1.0) # Add externally-provided design parameters to the trajectory. # In this case, we connect 'm' to pre-existing input parameters named 'mass' in each phase. traj.add_parameter('m', units='kg', val=1.0, targets={ 'ascent': 'mass', 'descent': 'mass' }, static_target=True) # In this case, by omitting targets, we're connecting these parameters to parameters # with the same name in each phase. traj.add_parameter('S', units='m**2', val=0.005, static_target=True) # Link Phases (link time and all state variables) traj.link_phases(phases=['ascent', 'descent'], vars=['*']) # Issue Connections p.model.connect('external_params.radius', 'size_comp.radius') p.model.connect('external_params.dens', 'size_comp.dens') p.model.connect('size_comp.mass', 'traj.parameters:m') p.model.connect('size_comp.S', 'traj.parameters:S') traj.connect('ascent.timeseries.controls:CD', 'descent.parameters:CD', src_indices=[-1], flat_src_indices=True) # A linear solver at the top level can improve performance. p.model.linear_solver = om.DirectSolver() # Finish Problem Setup p.setup() # Set Initial Guesses p.set_val('external_params.radius', 0.05, units='m') p.set_val('external_params.dens', 7.87, units='g/cm**3') p.set_val('traj.ascent.controls:CD', 0.5) p.set_val('traj.ascent.t_initial', 0.0) p.set_val('traj.ascent.t_duration', 10.0) p.set_val('traj.ascent.states:r', ascent.interp('r', [0, 100])) p.set_val('traj.ascent.states:h', ascent.interp('h', [0, 100])) p.set_val('traj.ascent.states:v', ascent.interp('v', [200, 150])) p.set_val('traj.ascent.states:gam', ascent.interp('gam', [25, 0]), units='deg') p.set_val('traj.descent.t_initial', 10.0) p.set_val('traj.descent.t_duration', 10.0) p.set_val('traj.descent.states:r', descent.interp('r', [100, 200])) p.set_val('traj.descent.states:h', descent.interp('h', [100, 0])) p.set_val('traj.descent.states:v', descent.interp('v', [150, 200])) p.set_val('traj.descent.states:gam', descent.interp('gam', [0, -45]), units='deg') dm.run_problem(p, simulate=True, make_plots=True) assert_near_equal(p.get_val('traj.descent.states:r')[-1], 3183.25, tolerance=1.0E-2) assert_near_equal( p.get_val('traj.ascent.timeseries.controls:CD')[-1], p.get_val('traj.descent.timeseries.parameters:CD')[0])
def test_two_phase_cannonball_for_docs(self): import openmdao.api as om from openmdao.utils.assert_utils import assert_near_equal import dymos as dm from dymos.examples.cannonball.size_comp import CannonballSizeComp from dymos.examples.cannonball.cannonball_phase import CannonballPhase p = om.Problem(model=om.Group()) p.driver = om.pyOptSparseDriver() p.driver.options['optimizer'] = 'SLSQP' p.driver.declare_coloring() external_params = p.model.add_subsystem('external_params', om.IndepVarComp()) external_params.add_output('radius', val=0.10, units='m') external_params.add_output('dens', val=7.87, units='g/cm**3') external_params.add_design_var('radius', lower=0.01, upper=0.10, ref0=0.01, ref=0.10) p.model.add_subsystem('size_comp', CannonballSizeComp()) traj = p.model.add_subsystem('traj', dm.Trajectory()) transcription = dm.Radau(num_segments=5, order=3, compressed=True) ascent = CannonballPhase(transcription=transcription) ascent = traj.add_phase('ascent', ascent) # All initial states except flight path angle are fixed # Final flight path angle is fixed (we will set it to zero so that the phase ends at apogee) ascent.set_time_options(fix_initial=True, duration_bounds=(1, 100), duration_ref=100, units='s') ascent.set_state_options('r', fix_initial=True, fix_final=False) ascent.set_state_options('h', fix_initial=True, fix_final=False) ascent.set_state_options('gam', fix_initial=False, fix_final=True) ascent.set_state_options('v', fix_initial=False, fix_final=False) ascent.add_parameter('S', targets=['aero.S'], units='m**2') ascent.add_parameter('mass', targets=['eom.m', 'kinetic_energy.m'], units='kg') # Limit the muzzle energy ascent.add_boundary_constraint('kinetic_energy.ke', loc='initial', units='J', upper=400000, lower=0, ref=100000, shape=(1,)) # Second Phase (descent) transcription = dm.GaussLobatto(num_segments=5, order=3, compressed=True) descent = CannonballPhase(transcription=transcription) traj.add_phase('descent', descent) # All initial states and time are free (they will be linked to the final states of ascent. # Final altitude is fixed (we will set it to zero so that the phase ends at ground impact) descent.set_time_options(initial_bounds=(.5, 100), duration_bounds=(.5, 100), duration_ref=100, units='s') descent.add_state('r', ) descent.add_state('h', fix_initial=False, fix_final=True) descent.add_state('gam', fix_initial=False, fix_final=False) descent.add_state('v', fix_initial=False, fix_final=False) descent.add_parameter('S', targets=['aero.S'], units='m**2') descent.add_parameter('mass', targets=['eom.m', 'kinetic_energy.m'], units='kg') descent.add_objective('r', loc='final', scaler=-1.0) # Add internally-managed design parameters to the trajectory. traj.add_parameter('CD', targets={'ascent': ['aero.CD'], 'descent': ['aero.CD']}, val=0.5, units=None, opt=False) traj.add_parameter('CL', targets={'ascent': ['aero.CL'], 'descent': ['aero.CL']}, val=0.0, units=None, opt=False) traj.add_parameter('T', targets={'ascent': ['eom.T'], 'descent': ['eom.T']}, val=0.0, units='N', opt=False) traj.add_parameter('alpha', targets={'ascent': ['eom.alpha'], 'descent': ['eom.alpha']}, val=0.0, units='deg', opt=False) # Add externally-provided design parameters to the trajectory. # In this case, we connect 'm' to pre-existing input parameters named 'mass' in each phase. traj.add_parameter('m', units='kg', val=1.0, targets={'ascent': 'mass', 'descent': 'mass'}) # In this case, by omitting targets, we're connecting these parameters to parameters # with the same name in each phase. traj.add_parameter('S', units='m**2', val=0.005) # Link Phases (link time and all state variables) traj.link_phases(phases=['ascent', 'descent'], vars=['*']) # Issue Connections p.model.connect('external_params.radius', 'size_comp.radius') p.model.connect('external_params.dens', 'size_comp.dens') p.model.connect('size_comp.mass', 'traj.parameters:m') p.model.connect('size_comp.S', 'traj.parameters:S') # A linear solver at the top level can improve performance. p.model.linear_solver = om.DirectSolver() # Finish Problem Setup p.setup() # Set Initial Guesses p.set_val('external_params.radius', 0.05, units='m') p.set_val('external_params.dens', 7.87, units='g/cm**3') p.set_val('traj.parameters:CD', 0.5) p.set_val('traj.parameters:CL', 0.0) p.set_val('traj.parameters:T', 0.0) p.set_val('traj.ascent.t_initial', 0.0) p.set_val('traj.ascent.t_duration', 10.0) p.set_val('traj.ascent.states:r', ascent.interpolate(ys=[0, 100], nodes='state_input')) p.set_val('traj.ascent.states:h', ascent.interpolate(ys=[0, 100], nodes='state_input')) p.set_val('traj.ascent.states:v', ascent.interpolate(ys=[200, 150], nodes='state_input')) p.set_val('traj.ascent.states:gam', ascent.interpolate(ys=[25, 0], nodes='state_input'), units='deg') p.set_val('traj.descent.t_initial', 10.0) p.set_val('traj.descent.t_duration', 10.0) p.set_val('traj.descent.states:r', descent.interpolate(ys=[100, 200], nodes='state_input')) p.set_val('traj.descent.states:h', descent.interpolate(ys=[100, 0], nodes='state_input')) p.set_val('traj.descent.states:v', descent.interpolate(ys=[150, 200], nodes='state_input')) p.set_val('traj.descent.states:gam', descent.interpolate(ys=[0, -45], nodes='state_input'), units='deg') dm.run_problem(p) assert_near_equal(p.get_val('traj.descent.states:r')[-1], 3183.25, tolerance=1.0E-2) exp_out = traj.simulate() print('optimal radius: {0:6.4f} m '.format(p.get_val('external_params.radius', units='m')[0])) print('cannonball mass: {0:6.4f} kg '.format(p.get_val('size_comp.mass', units='kg')[0])) print('launch angle: {0:6.4f} ' 'deg '.format(p.get_val('traj.ascent.timeseries.states:gam', units='deg')[0, 0])) print('maximum range: {0:6.4f} ' 'm '.format(p.get_val('traj.descent.timeseries.states:r')[-1, 0])) fig, axes = plt.subplots(nrows=1, ncols=1, figsize=(10, 6)) time_imp = {'ascent': p.get_val('traj.ascent.timeseries.time'), 'descent': p.get_val('traj.descent.timeseries.time')} time_exp = {'ascent': exp_out.get_val('traj.ascent.timeseries.time'), 'descent': exp_out.get_val('traj.descent.timeseries.time')} r_imp = {'ascent': p.get_val('traj.ascent.timeseries.states:r'), 'descent': p.get_val('traj.descent.timeseries.states:r')} r_exp = {'ascent': exp_out.get_val('traj.ascent.timeseries.states:r'), 'descent': exp_out.get_val('traj.descent.timeseries.states:r')} h_imp = {'ascent': p.get_val('traj.ascent.timeseries.states:h'), 'descent': p.get_val('traj.descent.timeseries.states:h')} h_exp = {'ascent': exp_out.get_val('traj.ascent.timeseries.states:h'), 'descent': exp_out.get_val('traj.descent.timeseries.states:h')} axes.plot(r_imp['ascent'], h_imp['ascent'], 'bo') axes.plot(r_imp['descent'], h_imp['descent'], 'ro') axes.plot(r_exp['ascent'], h_exp['ascent'], 'b--') axes.plot(r_exp['descent'], h_exp['descent'], 'r--') axes.set_xlabel('range (m)') axes.set_ylabel('altitude (m)') fig, axes = plt.subplots(nrows=4, ncols=1, figsize=(10, 6)) states = ['r', 'h', 'v', 'gam'] for i, state in enumerate(states): x_imp = {'ascent': p.get_val('traj.ascent.timeseries.states:{0}'.format(state)), 'descent': p.get_val('traj.descent.timeseries.states:{0}'.format(state))} x_exp = {'ascent': exp_out.get_val('traj.ascent.timeseries.states:{0}'.format(state)), 'descent': exp_out.get_val('traj.descent.timeseries.states:{0}'.format(state))} axes[i].set_ylabel(state) axes[i].plot(time_imp['ascent'], x_imp['ascent'], 'bo') axes[i].plot(time_imp['descent'], x_imp['descent'], 'ro') axes[i].plot(time_exp['ascent'], x_exp['ascent'], 'b--') axes[i].plot(time_exp['descent'], x_exp['descent'], 'r--') params = ['CL', 'CD', 'T', 'alpha', 'mass', 'S'] fig, axes = plt.subplots(nrows=6, ncols=1, figsize=(12, 6)) for i, param in enumerate(params): p_imp = { 'ascent': p.get_val('traj.ascent.timeseries.parameters:{0}'.format(param)), 'descent': p.get_val('traj.descent.timeseries.parameters:{0}'.format(param))} p_exp = {'ascent': exp_out.get_val('traj.ascent.timeseries.' 'parameters:{0}'.format(param)), 'descent': exp_out.get_val('traj.descent.timeseries.' 'parameters:{0}'.format(param))} axes[i].set_ylabel(param) axes[i].plot(time_imp['ascent'], p_imp['ascent'], 'bo') axes[i].plot(time_imp['descent'], p_imp['descent'], 'ro') axes[i].plot(time_exp['ascent'], p_exp['ascent'], 'b--') axes[i].plot(time_exp['descent'], p_exp['descent'], 'r--') plt.show()
class Build_Pack(): """ Use Dymos to minimize the thickness of the insulator, while still maintaining a temperature below 100degC after a 45 second transient """ p = om.Problem(model=om.Group()) model = p.model nn = 1 p.driver = om.ScipyOptimizeDriver() p.driver = om.pyOptSparseDriver(optimizer='SLSQP') # p.driver.opt_settings['iSumm'] = 6 # record_file = 'geometry.sql' # p.add_recorder(om.SqliteRecorder(record_file)) # p.recording_options['includes'] = ['*'] # p.recording_options['record_objectives'] = True # p.recording_options['record_constraints'] = True # p.recording_options['record_desvars'] = True # p.recording_options['record_inputs'] = True p.driver.declare_coloring() traj = p.model.add_subsystem('traj', dm.Trajectory()) phase = traj.add_phase( 'phase0', dm.Phase(ode_class=tempODE, transcription=dm.GaussLobatto(num_segments=20, order=3, compressed=False))) phase.set_time_options(fix_initial=True, fix_duration=True) phase.add_state('T', rate_source='Tdot', units='K', ref=333.15, defect_ref=333.15, fix_initial=True, fix_final=False, solve_segments=False) phase.add_boundary_constraint('T', loc='final', units='K', upper=333.15, lower=293.15, shape=(1, )) phase.add_parameter('d', opt=True, lower=0.001, upper=0.5, val=0.001, units='m', ref0=0, ref=1) phase.add_objective('d', loc='final', ref=1) model.add_subsystem('sizing', SizingGroup(num_nodes=nn), promotes=['*']) p.model.connect('traj.phase0.timeseries.parameters:d', 'cell_s_w', src_indices=[-1]) # connect final value of d with cell_s_w # model.add_design_var('sizing.L', lower=-1, upper=1) # model.add_objective('OD1.Eff') p.model.linear_solver = om.DirectSolver() p.setup(force_alloc_complex=True) p['traj.phase0.t_initial'] = 0.0 p['traj.phase0.t_duration'] = 45 p['traj.phase0.states:T'] = phase.interpolate(ys=[293.15, 333.15], nodes='state_input') p['traj.phase0.parameters:d'] = 0.001 # p.set_val('DESIGN.rot_ir' , 60) p.run_model() # cpd = p.check_partials(method='cs', compact_print=True) #check partial derivatives # assert_check_partials(cpd) # quit() dm.run_problem(p) # p.record('final') #trigger problem record (or call run_driver if it's attached to the driver) # p.run_driver() # p.cleanup() model.list_inputs(prom_name=True) model.list_outputs(prom_name=True) # p.check_partials(compact_print=True, method='cs') print("num of cells: ", p['n_cells']) print("flux: ", p['flux']) print("PCM mass: ", p['PCM_tot_mass']) print("PCM thickness (mm): ", p['t_PCM']) print("OHP mass: ", p['mass_OHP']) print("packaging mass: ", p['p_mass']) print("total mass: ", p['tot_mass']) print("package mass fraction: ", p['mass_frac']) print("pack energy density: ", p['energy'] / (p['tot_mass'])) print("cell energy density: ", (p['q_max'] * p['v_n_c']) / (p['cell_mass'])) print("pack energy (kWh): ", p['energy'] / 1000.) print("pack cost ($K): ", p['n_cells'] * 0.4)
def test_doc_ssto_polynomial_control(self): import numpy as np import matplotlib.pyplot as plt import openmdao.api as om from openmdao.utils.assert_utils import assert_near_equal import dymos as dm g = 1.61544 # lunar gravity, m/s**2 class LaunchVehicle2DEOM(om.ExplicitComponent): """ Simple 2D Cartesian Equations of Motion for a launch vehicle subject to thrust and drag. """ def initialize(self): self.options.declare('num_nodes', types=int) def setup(self): nn = self.options['num_nodes'] # Inputs self.add_input('vx', val=np.zeros(nn), desc='x velocity', units='m/s') self.add_input('vy', val=np.zeros(nn), desc='y velocity', units='m/s') self.add_input('m', val=np.zeros(nn), desc='mass', units='kg') self.add_input('theta', val=np.zeros(nn), desc='pitch angle', units='rad') self.add_input('thrust', val=2100000 * np.ones(nn), desc='thrust', units='N') self.add_input('Isp', val=265.2 * np.ones(nn), desc='specific impulse', units='s') # Outputs self.add_output('xdot', val=np.zeros(nn), desc='velocity component in x', units='m/s') self.add_output('ydot', val=np.zeros(nn), desc='velocity component in y', units='m/s') self.add_output('vxdot', val=np.zeros(nn), desc='x acceleration magnitude', units='m/s**2') self.add_output('vydot', val=np.zeros(nn), desc='y acceleration magnitude', units='m/s**2') self.add_output('mdot', val=np.zeros(nn), desc='mass rate of change', units='kg/s') # Setup partials ar = np.arange(self.options['num_nodes']) self.declare_partials(of='xdot', wrt='vx', rows=ar, cols=ar, val=1.0) self.declare_partials(of='ydot', wrt='vy', rows=ar, cols=ar, val=1.0) self.declare_partials(of='vxdot', wrt='vx', rows=ar, cols=ar) self.declare_partials(of='vxdot', wrt='m', rows=ar, cols=ar) self.declare_partials(of='vxdot', wrt='theta', rows=ar, cols=ar) self.declare_partials(of='vxdot', wrt='thrust', rows=ar, cols=ar) self.declare_partials(of='vydot', wrt='m', rows=ar, cols=ar) self.declare_partials(of='vydot', wrt='theta', rows=ar, cols=ar) self.declare_partials(of='vydot', wrt='vy', rows=ar, cols=ar) self.declare_partials(of='vydot', wrt='thrust', rows=ar, cols=ar) self.declare_partials(of='mdot', wrt='thrust', rows=ar, cols=ar) self.declare_partials(of='mdot', wrt='Isp', rows=ar, cols=ar) def compute(self, inputs, outputs): theta = inputs['theta'] cos_theta = np.cos(theta) sin_theta = np.sin(theta) vx = inputs['vx'] vy = inputs['vy'] m = inputs['m'] F_T = inputs['thrust'] Isp = inputs['Isp'] outputs['xdot'] = vx outputs['ydot'] = vy outputs['vxdot'] = F_T * cos_theta / m outputs['vydot'] = F_T * sin_theta / m - g outputs['mdot'] = -F_T / (g * Isp) def compute_partials(self, inputs, jacobian): theta = inputs['theta'] cos_theta = np.cos(theta) sin_theta = np.sin(theta) m = inputs['m'] F_T = inputs['thrust'] Isp = inputs['Isp'] # jacobian['vxdot', 'vx'] = -CDA * rho * vx / m jacobian['vxdot', 'm'] = -(F_T * cos_theta) / m**2 jacobian['vxdot', 'theta'] = -(F_T / m) * sin_theta jacobian['vxdot', 'thrust'] = cos_theta / m # jacobian['vydot', 'vy'] = -CDA * rho * vy / m jacobian['vydot', 'm'] = -(F_T * sin_theta) / m**2 jacobian['vydot', 'theta'] = (F_T / m) * cos_theta jacobian['vydot', 'thrust'] = sin_theta / m jacobian['mdot', 'thrust'] = -1.0 / (g * Isp) jacobian['mdot', 'Isp'] = F_T / (g * Isp**2) class LaunchVehicleLinearTangentODE(om.Group): """ The LaunchVehicleLinearTangentODE for this case consists of a guidance component and the EOM. Guidance is simply an OpenMDAO ExecComp which computes the arctangent of the tan_theta variable. """ def initialize(self): self.options.declare( 'num_nodes', types=int, desc='Number of nodes to be evaluated in the RHS') def setup(self): nn = self.options['num_nodes'] self.add_subsystem( 'guidance', om.ExecComp('theta=arctan(tan_theta)', theta={ 'value': np.ones(nn), 'units': 'rad' }, tan_theta={'value': np.ones(nn)})) self.add_subsystem('eom', LaunchVehicle2DEOM(num_nodes=nn)) self.connect('guidance.theta', 'eom.theta') # # Setup and solve the optimal control problem # p = om.Problem(model=om.Group()) traj = p.model.add_subsystem('traj', dm.Trajectory()) phase = dm.Phase(ode_class=LaunchVehicleLinearTangentODE, transcription=dm.Radau(num_segments=20, order=3, compressed=False)) traj.add_phase('phase0', phase) phase.set_time_options(initial_bounds=(0, 0), duration_bounds=(10, 1000), units='s') # # Set the state options. We include rate_source, units, and targets here since the ODE # is not decorated with their default values. # phase.add_state('x', fix_initial=True, lower=0, rate_source='eom.xdot', units='m') phase.add_state('y', fix_initial=True, lower=0, rate_source='eom.ydot', units='m') phase.add_state('vx', fix_initial=True, lower=0, rate_source='eom.vxdot', units='m/s', targets=['eom.vx']) phase.add_state('vy', fix_initial=True, rate_source='eom.vydot', units='m/s', targets=['eom.vy']) phase.add_state('m', fix_initial=True, rate_source='eom.mdot', units='kg', targets=['eom.m']) # # The tangent of theta is modeled as a linear polynomial over the duration of the phase. # phase.add_polynomial_control('tan_theta', order=1, units=None, opt=True, targets=['guidance.tan_theta']) # # Parameters values for thrust and specific impulse are design parameters. They are # provided by an IndepVarComp in the phase, but with opt=False their values are not # design variables in the optimization problem. # phase.add_parameter('thrust', units='N', opt=False, val=3.0 * 50000.0 * 1.61544, targets=['eom.thrust']) phase.add_parameter('Isp', units='s', opt=False, val=1.0E6, targets=['eom.Isp']) # # Set the boundary constraints. These are all states which could also be handled # by setting fix_final=True and including the correct final value in the initial guess. # phase.add_boundary_constraint('y', loc='final', equals=1.85E5, linear=True) phase.add_boundary_constraint('vx', loc='final', equals=1627.0) phase.add_boundary_constraint('vy', loc='final', equals=0) phase.add_objective('time', index=-1, scaler=0.01) # # Add theta as a timeseries output since it's not included by default. # phase.add_timeseries_output('guidance.theta', units='deg') # # Set the optimizer # p.driver = om.pyOptSparseDriver() p.driver.options['optimizer'] = 'SLSQP' p.driver.declare_coloring() # # We don't strictly need to define a linear solver here since our problem is entirely # feed-forward with no iterative loops. It's good practice to add one, however, since # failing to do so can cause incorrect derivatives if iterative processes are ever # introduced to the system. # p.model.linear_solver = om.DirectSolver() p.setup(check=True) # # Assign initial guesses for the independent variables in the problem. # p['traj.phase0.t_initial'] = 0.0 p['traj.phase0.t_duration'] = 500.0 p['traj.phase0.states:x'] = phase.interpolate(ys=[0, 350000.0], nodes='state_input') p['traj.phase0.states:y'] = phase.interpolate(ys=[0, 185000.0], nodes='state_input') p['traj.phase0.states:vx'] = phase.interpolate(ys=[0, 1627.0], nodes='state_input') p['traj.phase0.states:vy'] = phase.interpolate(ys=[1.0E-6, 0], nodes='state_input') p['traj.phase0.states:m'] = phase.interpolate(ys=[50000, 50000], nodes='state_input') p['traj.phase0.polynomial_controls:tan_theta'] = [[0.5 * np.pi], [0.0]] # # Solve the problem. # dm.run_problem(p) # # Check the results. # assert_near_equal(p.get_val('traj.phase0.timeseries.time')[-1], 481, tolerance=0.01) # # Get the explitly simulated results # exp_out = traj.simulate() # # Plot the results # fig, axes = plt.subplots(nrows=2, ncols=1, figsize=(10, 8)) axes[0].plot(p.get_val('traj.phase0.timeseries.states:x'), p.get_val('traj.phase0.timeseries.states:y'), marker='o', ms=4, linestyle='None', label='solution') axes[0].plot(exp_out.get_val('traj.phase0.timeseries.states:x'), exp_out.get_val('traj.phase0.timeseries.states:y'), marker=None, linestyle='-', label='simulation') axes[0].set_xlabel('range (m)') axes[0].set_ylabel('altitude (m)') axes[0].set_aspect('equal') axes[1].plot(p.get_val('traj.phase0.timeseries.time'), p.get_val('traj.phase0.timeseries.theta'), marker='o', ms=4, linestyle='None') axes[1].plot(exp_out.get_val('traj.phase0.timeseries.time'), exp_out.get_val('traj.phase0.timeseries.theta'), linestyle='-', marker=None) axes[1].set_xlabel('time (s)') axes[1].set_ylabel('theta (deg)') plt.suptitle( 'Single Stage to Orbit Solution Using Polynomial Controls') fig.legend(loc='lower center', ncol=2) plt.show()
def test_simulate_options(self): import itertools import numpy as np import openmdao.api as om import dymos as dm for method, atol, first_step in itertools.product( ('RK23', 'RK45'), (0.01, 0.001), (None, 0.01, .1)): with self.subTest(): # # Define the OpenMDAO problem # p = om.Problem(model=om.Group()) # # Define a Trajectory object # traj = dm.Trajectory() p.model.add_subsystem('traj', subsys=traj) # # Define a Dymos Phase object with GaussLobatto Transcription # phase = dm.Phase(ode_class=BrachistochroneODE, transcription=dm.GaussLobatto(num_segments=10, order=3)) traj.add_phase(name='phase0', phase=phase) # # Set the time options # Time has no targets in our ODE. # We fix the initial time so that the it is not a design variable in the optimization. # The duration of the phase is allowed to be optimized, but is bounded on [0.5, 10]. # phase.set_time_options(fix_initial=True, duration_bounds=(0.5, 10.0), units='s') # # Set the time options # Initial values of positions and velocity are all fixed. # The final value of position are fixed, but the final velocity is a free variable. # The equations of motion are not functions of position, so 'x' and 'y' have no targets. # The rate source points to the output in the ODE which provides the time derivative of the # given state. phase.add_state('x', fix_initial=True, fix_final=True, rate_source='xdot') phase.add_state('y', fix_initial=True, fix_final=True, rate_source='ydot') phase.add_state('v', fix_initial=True, fix_final=False, rate_source='vdot') # Define theta as a control. phase.add_control(name='theta', units='rad', lower=0, upper=np.pi) # Minimize final time. phase.add_objective('time', loc='final') # Set the simulate options phase.set_simulate_options(method=method, atol=atol, first_step=first_step) # Set the driver. p.driver = om.ScipyOptimizeDriver() # Allow OpenMDAO to automatically determine our sparsity pattern. # Doing so can significant speed up the execution of Dymos. p.driver.declare_coloring() # Setup the problem p.setup(check=True) # Now that the OpenMDAO problem is setup, we can set the values of the states. p.set_val('traj.phase0.states:x', phase.interp('x', [0, 10]), units='m') p.set_val('traj.phase0.states:y', phase.interp('y', [10, 5]), units='m') p.set_val('traj.phase0.states:v', phase.interp('v', [0, 5]), units='m/s') p.set_val('traj.phase0.controls:theta', phase.interp('theta', [90, 90]), units='deg') # Run the driver to solve the problem dm.run_problem(p, run_driver=True, simulate=True)
def test_reentry_mixed_controls(self): p = om.Problem(model=om.Group()) p.driver = om.pyOptSparseDriver() p.driver.declare_coloring(tol=1.0E-12) p.driver.options['optimizer'] = 'IPOPT' p.driver.opt_settings['alpha_for_y'] = 'safer-min-dual-infeas' p.driver.opt_settings['print_level'] = 5 p.driver.opt_settings['nlp_scaling_method'] = 'gradient-based' p.driver.opt_settings['mu_strategy'] = 'monotone' traj = p.model.add_subsystem('traj', Trajectory()) phase0 = traj.add_phase( 'phase0', Phase(ode_class=ShuttleODE, transcription=Radau(num_segments=30, order=3))) phase0.set_time_options(fix_initial=True, units='s', duration_ref=200) phase0.add_state('h', fix_initial=True, fix_final=True, units='ft', rate_source='hdot', targets=['h'], lower=0, ref0=75000, ref=300000, defect_ref=1000) phase0.add_state('gamma', fix_initial=True, fix_final=True, units='rad', rate_source='gammadot', targets=['gamma'], lower=-89. * np.pi / 180, upper=89. * np.pi / 180) phase0.add_state('phi', fix_initial=True, fix_final=False, units='rad', rate_source='phidot', lower=0, upper=89. * np.pi / 180) phase0.add_state('psi', fix_initial=True, fix_final=False, units='rad', rate_source='psidot', targets=['psi'], lower=0, upper=90. * np.pi / 180) phase0.add_state('theta', fix_initial=True, fix_final=False, units='rad', rate_source='thetadot', targets=['theta'], lower=-89. * np.pi / 180, upper=89. * np.pi / 180) phase0.add_state('v', fix_initial=True, fix_final=True, units='ft/s', rate_source='vdot', targets=['v'], lower=500, ref0=2500, ref=25000) phase0.add_control('alpha', units='rad', opt=True, lower=-np.pi / 2, upper=np.pi / 2) phase0.add_polynomial_control('beta', order=9, units='rad', opt=True, lower=-89 * np.pi / 180, upper=1 * np.pi / 180) phase0.add_objective('theta', loc='final', ref=-0.01) phase0.add_path_constraint('q', lower=0, upper=70, ref=70) p.setup(check=True, force_alloc_complex=True) p.set_val('traj.phase0.states:h', phase0.interp('h', [260000, 80000]), units='ft') p.set_val('traj.phase0.states:gamma', phase0.interp('gamma', [-1 * np.pi / 180, -5 * np.pi / 180]), units='rad') p.set_val('traj.phase0.states:phi', phase0.interp('phi', [0, 75 * np.pi / 180]), units='rad') p.set_val('traj.phase0.states:psi', phase0.interp('psi', [90 * np.pi / 180, 10 * np.pi / 180]), units='rad') p.set_val('traj.phase0.states:theta', phase0.interp('theta', [0, 25 * np.pi / 180]), units='rad') p.set_val('traj.phase0.states:v', phase0.interp('v', [25600, 2500]), units='ft/s') p.set_val('traj.phase0.t_initial', 0, units='s') p.set_val('traj.phase0.t_duration', 2000, units='s') p.set_val('traj.phase0.controls:alpha', phase0.interp('alpha', [17.4, 17.4]), units='deg') p.set_val('traj.phase0.polynomial_controls:beta', phase0.interp('beta', [-20, 0]), units='deg') run_problem(p, simulate=True) sol = om.CaseReader('dymos_solution.db').get_case('final') sim = om.CaseReader('dymos_simulation.db').get_case('final') from scipy.interpolate import interp1d t_sol = sol.get_val('traj.phase0.timeseries.time') beta_sol = sol.get_val( 'traj.phase0.timeseries.polynomial_controls:beta', units='deg') t_sim = sim.get_val('traj.phase0.timeseries.time') beta_sim = sim.get_val( 'traj.phase0.timeseries.polynomial_controls:beta', units='deg') sol_interp = interp1d(t_sol.ravel(), beta_sol.ravel()) sim_interp = interp1d(t_sim.ravel(), beta_sim.ravel()) t = np.linspace(0, t_sol.ravel()[-1], 1000) assert_near_equal(sim_interp(t), sol_interp(t), tolerance=0.01) assert_near_equal(p.get_val('traj.phase0.timeseries.time')[-1], expected_results['constrained']['time'], tolerance=1e-2) assert_near_equal(p.get_val('traj.phase0.timeseries.states:theta', units='deg')[-1], expected_results['constrained']['theta'], tolerance=1e-2)
def test_balanced_field_length_for_docs(self): import matplotlib.pyplot as plt import openmdao.api as om from openmdao.utils.general_utils import set_pyoptsparse_opt from openmdao.utils.assert_utils import assert_near_equal import dymos as dm from dymos.examples.balanced_field.balanced_field_ode import BalancedFieldODEComp p = om.Problem() _, optimizer = set_pyoptsparse_opt('IPOPT', fallback=True) p.driver = om.pyOptSparseDriver() p.driver.declare_coloring() # Use IPOPT if available, with fallback to SLSQP p.driver.options['optimizer'] = optimizer p.driver.options['print_results'] = False if optimizer == 'IPOPT': p.driver.opt_settings['print_level'] = 5 p.driver.opt_settings['derivative_test'] = 'first-order' # First Phase: Brake release to V1 - both engines operable br_to_v1 = dm.Phase(ode_class=BalancedFieldODEComp, transcription=dm.Radau(num_segments=3), ode_init_kwargs={'mode': 'runway'}) br_to_v1.set_time_options(fix_initial=True, duration_bounds=(1, 1000), duration_ref=10.0) br_to_v1.add_state('r', fix_initial=True, lower=0, ref=1000.0, defect_ref=1000.0) br_to_v1.add_state('v', fix_initial=True, lower=0, ref=100.0, defect_ref=100.0) br_to_v1.add_parameter('alpha', val=0.0, opt=False, units='deg') br_to_v1.add_timeseries_output('*') # Second Phase: Rejected takeoff at V1 - no engines operable rto = dm.Phase(ode_class=BalancedFieldODEComp, transcription=dm.Radau(num_segments=3), ode_init_kwargs={'mode': 'runway'}) rto.set_time_options(fix_initial=False, duration_bounds=(1, 1000), duration_ref=1.0) rto.add_state('r', fix_initial=False, lower=0, ref=1000.0, defect_ref=1000.0) rto.add_state('v', fix_initial=False, lower=0, ref=100.0, defect_ref=100.0) rto.add_parameter('alpha', val=0.0, opt=False, units='deg') rto.add_timeseries_output('*') # Third Phase: V1 to Vr - single engine operable v1_to_vr = dm.Phase(ode_class=BalancedFieldODEComp, transcription=dm.Radau(num_segments=3), ode_init_kwargs={'mode': 'runway'}) v1_to_vr.set_time_options(fix_initial=False, duration_bounds=(1, 1000), duration_ref=1.0) v1_to_vr.add_state('r', fix_initial=False, lower=0, ref=1000.0, defect_ref=1000.0) v1_to_vr.add_state('v', fix_initial=False, lower=0, ref=100.0, defect_ref=100.0) v1_to_vr.add_parameter('alpha', val=0.0, opt=False, units='deg') v1_to_vr.add_timeseries_output('*') # Fourth Phase: Rotate - single engine operable rotate = dm.Phase(ode_class=BalancedFieldODEComp, transcription=dm.Radau(num_segments=3), ode_init_kwargs={'mode': 'runway'}) rotate.set_time_options(fix_initial=False, duration_bounds=(1.0, 5), duration_ref=1.0) rotate.add_state('r', fix_initial=False, lower=0, ref=1000.0, defect_ref=1000.0) rotate.add_state('v', fix_initial=False, lower=0, ref=100.0, defect_ref=100.0) rotate.add_polynomial_control('alpha', order=1, opt=True, units='deg', lower=0, upper=10, ref=10, val=[0, 10]) rotate.add_timeseries_output('*') # Fifth Phase: Climb to target speed and altitude at end of runway. climb = dm.Phase(ode_class=BalancedFieldODEComp, transcription=dm.Radau(num_segments=5), ode_init_kwargs={'mode': 'climb'}) climb.set_time_options(fix_initial=False, duration_bounds=(1, 100), duration_ref=1.0) climb.add_state('r', fix_initial=False, lower=0, ref=1000.0, defect_ref=1000.0) climb.add_state('h', fix_initial=True, lower=0, ref=1.0, defect_ref=1.0) climb.add_state('v', fix_initial=False, lower=0, ref=100.0, defect_ref=100.0) climb.add_state('gam', fix_initial=True, lower=0, ref=0.05, defect_ref=0.05) climb.add_control('alpha', opt=True, units='deg', lower=-10, upper=15, ref=10) climb.add_timeseries_output('*') # Instantiate the trajectory and add phases traj = dm.Trajectory() p.model.add_subsystem('traj', traj) traj.add_phase('br_to_v1', br_to_v1) traj.add_phase('rto', rto) traj.add_phase('v1_to_vr', v1_to_vr) traj.add_phase('rotate', rotate) traj.add_phase('climb', climb) # Add parameters common to multiple phases to the trajectory traj.add_parameter('m', val=174200., opt=False, units='lbm', desc='aircraft mass', targets={ 'br_to_v1': ['m'], 'v1_to_vr': ['m'], 'rto': ['m'], 'rotate': ['m'], 'climb': ['m'] }) traj.add_parameter('T_nominal', val=27000 * 2, opt=False, units='lbf', static_target=True, desc='nominal aircraft thrust', targets={'br_to_v1': ['T']}) traj.add_parameter('T_engine_out', val=27000, opt=False, units='lbf', static_target=True, desc='thrust under a single engine', targets={ 'v1_to_vr': ['T'], 'rotate': ['T'], 'climb': ['T'] }) traj.add_parameter( 'T_shutdown', val=0.0, opt=False, units='lbf', static_target=True, desc='thrust when engines are shut down for rejected takeoff', targets={'rto': ['T']}) traj.add_parameter('mu_r_nominal', val=0.03, opt=False, units=None, static_target=True, desc='nominal runway friction coefficient', targets={ 'br_to_v1': ['mu_r'], 'v1_to_vr': ['mu_r'], 'rotate': ['mu_r'] }) traj.add_parameter('mu_r_braking', val=0.3, opt=False, units=None, static_target=True, desc='runway friction coefficient under braking', targets={'rto': ['mu_r']}) traj.add_parameter('h_runway', val=0., opt=False, units='ft', desc='runway altitude', targets={ 'br_to_v1': ['h'], 'v1_to_vr': ['h'], 'rto': ['h'], 'rotate': ['h'] }) traj.add_parameter('rho', val=1.225, opt=False, units='kg/m**3', static_target=True, desc='atmospheric density', targets={ 'br_to_v1': ['rho'], 'v1_to_vr': ['rho'], 'rto': ['rho'], 'rotate': ['rho'] }) traj.add_parameter('S', val=124.7, opt=False, units='m**2', static_target=True, desc='aerodynamic reference area', targets={ 'br_to_v1': ['S'], 'v1_to_vr': ['S'], 'rto': ['S'], 'rotate': ['S'], 'climb': ['S'] }) traj.add_parameter( 'CD0', val=0.03, opt=False, units=None, static_target=True, desc='zero-lift drag coefficient', targets={ f'{phase}': ['CD0'] for phase in ['br_to_v1', 'v1_to_vr', 'rto', 'rotate' 'climb'] }) traj.add_parameter( 'AR', val=9.45, opt=False, units=None, static_target=True, desc='wing aspect ratio', targets={ f'{phase}': ['AR'] for phase in ['br_to_v1', 'v1_to_vr', 'rto', 'rotate' 'climb'] }) traj.add_parameter( 'e', val=801, opt=False, units=None, static_target=True, desc='Oswald span efficiency factor', targets={ f'{phase}': ['e'] for phase in ['br_to_v1', 'v1_to_vr', 'rto', 'rotate' 'climb'] }) traj.add_parameter( 'span', val=35.7, opt=False, units='m', static_target=True, desc='wingspan', targets={ f'{phase}': ['span'] for phase in ['br_to_v1', 'v1_to_vr', 'rto', 'rotate' 'climb'] }) traj.add_parameter( 'h_w', val=1.0, opt=False, units='m', static_target=True, desc='height of wing above CG', targets={ f'{phase}': ['h_w'] for phase in ['br_to_v1', 'v1_to_vr', 'rto', 'rotate' 'climb'] }) traj.add_parameter( 'CL0', val=0.5, opt=False, units=None, static_target=True, desc='zero-alpha lift coefficient', targets={ f'{phase}': ['CL0'] for phase in ['br_to_v1', 'v1_to_vr', 'rto', 'rotate' 'climb'] }) traj.add_parameter( 'CL_max', val=2.0, opt=False, units=None, static_target=True, desc='maximum lift coefficient for linear fit', targets={ f'{phase}': ['CL_max'] for phase in ['br_to_v1', 'v1_to_vr', 'rto', 'rotate' 'climb'] }) traj.add_parameter( 'alpha_max', val=10.0, opt=False, units='deg', static_target=True, desc='angle of attack at maximum lift', targets={ f'{phase}': ['alpha_max'] for phase in ['br_to_v1', 'v1_to_vr', 'rto', 'rotate' 'climb'] }) # Standard "end of first phase to beginning of second phase" linkages # Alpha changes from being a parameter in v1_to_vr to a polynomial control # in rotate, to a dynamic control in `climb`. traj.link_phases(['br_to_v1', 'v1_to_vr'], vars=['time', 'r', 'v']) traj.link_phases(['v1_to_vr', 'rotate'], vars=['time', 'r', 'v', 'alpha']) traj.link_phases(['rotate', 'climb'], vars=['time', 'r', 'v', 'alpha']) traj.link_phases(['br_to_v1', 'rto'], vars=['time', 'r', 'v']) # Less common "final value of r must be the match at ends of two phases". traj.add_linkage_constraint(phase_a='rto', var_a='r', loc_a='final', phase_b='climb', var_b='r', loc_b='final', ref=1000) # Define the constraints and objective for the optimal control problem v1_to_vr.add_boundary_constraint('v_over_v_stall', loc='final', lower=1.2, ref=100) rto.add_boundary_constraint('v', loc='final', equals=0., ref=100, linear=True) rotate.add_boundary_constraint('F_r', loc='final', equals=0, ref=100000) climb.add_boundary_constraint('h', loc='final', equals=35, ref=35, units='ft', linear=True) climb.add_boundary_constraint('gam', loc='final', equals=5, ref=5, units='deg', linear=True) climb.add_path_constraint('gam', lower=0, upper=5, ref=5, units='deg') climb.add_boundary_constraint('v_over_v_stall', loc='final', lower=1.25, ref=1.25) rto.add_objective('r', loc='final', ref=1.0) # # Setup the problem and set the initial guess # p.setup(check=True) p.set_val('traj.br_to_v1.t_initial', 0) p.set_val('traj.br_to_v1.t_duration', 35) p.set_val('traj.br_to_v1.states:r', br_to_v1.interp('r', [0, 2500.0])) p.set_val('traj.br_to_v1.states:v', br_to_v1.interp('v', [0, 100.0])) p.set_val('traj.br_to_v1.parameters:alpha', 0, units='deg') p.set_val('traj.v1_to_vr.t_initial', 35) p.set_val('traj.v1_to_vr.t_duration', 35) p.set_val('traj.v1_to_vr.states:r', v1_to_vr.interp('r', [2500, 300.0])) p.set_val('traj.v1_to_vr.states:v', v1_to_vr.interp('v', [100, 110.0])) p.set_val('traj.v1_to_vr.parameters:alpha', 0.0, units='deg') p.set_val('traj.rto.t_initial', 35) p.set_val('traj.rto.t_duration', 35) p.set_val('traj.rto.states:r', rto.interp('r', [2500, 5000.0])) p.set_val('traj.rto.states:v', rto.interp('v', [110, 0])) p.set_val('traj.rto.parameters:alpha', 0.0, units='deg') p.set_val('traj.rotate.t_initial', 70) p.set_val('traj.rotate.t_duration', 5) p.set_val('traj.rotate.states:r', rotate.interp('r', [1750, 1800.0])) p.set_val('traj.rotate.states:v', rotate.interp('v', [80, 85.0])) p.set_val('traj.rotate.polynomial_controls:alpha', 0.0, units='deg') p.set_val('traj.climb.t_initial', 75) p.set_val('traj.climb.t_duration', 15) p.set_val('traj.climb.states:r', climb.interp('r', [5000, 5500.0]), units='ft') p.set_val('traj.climb.states:v', climb.interp('v', [160, 170.0]), units='kn') p.set_val('traj.climb.states:h', climb.interp('h', [0, 35.0]), units='ft') p.set_val('traj.climb.states:gam', climb.interp('gam', [0, 5.0]), units='deg') p.set_val('traj.climb.controls:alpha', 5.0, units='deg') dm.run_problem(p, run_driver=True, simulate=True) # Test this example in Dymos' continuous integration assert_near_equal(p.get_val('traj.rto.states:r')[-1], 2188.2, tolerance=0.01) sim_case = om.CaseReader('dymos_solution.db').get_case('final') fig, axes = plt.subplots(2, 1, sharex=True, gridspec_kw={'top': 0.92}) for phase in ['br_to_v1', 'rto', 'v1_to_vr', 'rotate', 'climb']: r = sim_case.get_val(f'traj.{phase}.timeseries.states:r', units='ft') v = sim_case.get_val(f'traj.{phase}.timeseries.states:v', units='kn') t = sim_case.get_val(f'traj.{phase}.timeseries.time', units='s') axes[0].plot(t, r, '-', label=phase) axes[1].plot(t, v, '-', label=phase) fig.suptitle('Balanced Field Length') axes[1].set_xlabel('time (s)') axes[0].set_ylabel('range (ft)') axes[1].set_ylabel('airspeed (kts)') axes[0].grid(True) axes[1].grid(True) tv1 = sim_case.get_val('traj.br_to_v1.timeseries.time', units='s')[-1, 0] v1 = sim_case.get_val('traj.br_to_v1.timeseries.states:v', units='kn')[-1, 0] tf_rto = sim_case.get_val('traj.rto.timeseries.time', units='s')[-1, 0] rf_rto = sim_case.get_val('traj.rto.timeseries.states:r', units='ft')[-1, 0] axes[0].annotate(f'field length = {r[-1, 0]:5.1f} ft', xy=(t[-1, 0], r[-1, 0]), xycoords='data', xytext=(0.7, 0.5), textcoords='axes fraction', arrowprops=dict(arrowstyle='->'), horizontalalignment='center', verticalalignment='top') axes[0].annotate(f'', xy=(tf_rto, rf_rto), xycoords='data', xytext=(0.7, 0.5), textcoords='axes fraction', arrowprops=dict(arrowstyle='->'), horizontalalignment='center', verticalalignment='top') axes[1].annotate(f'$v1$ = {v1:5.1f} kts', xy=(tv1, v1), xycoords='data', xytext=(0.5, 0.5), textcoords='axes fraction', arrowprops=dict(arrowstyle='->'), horizontalalignment='center', verticalalignment='top') plt.legend() if SHOW_PLOTS: plt.show()
def _test_static_ode_output(self, transcription): # # Define the OpenMDAO problem # p = om.Problem(model=om.Group()) # # Define a Trajectory object # traj = dm.Trajectory() p.model.add_subsystem('traj', subsys=traj) # # Define a Dymos Phase object with GaussLobatto Transcription # phase = dm.Phase(ode_class=BrachODEStaticOutput, transcription=transcription(num_segments=10, order=3)) traj.add_phase(name='phase0', phase=phase) # # Set the time options # Time has no targets in our ODE. # We fix the initial time so that the it is not a design variable in the optimization. # The duration of the phase is allowed to be optimized, but is bounded on [0.5, 10]. # phase.set_time_options(fix_initial=True, duration_bounds=(1.0, 10.0), units='s') # # Set the time options # Initial values of positions and velocity are all fixed. # The final value of position are fixed, but the final velocity is a free variable. # The equations of motion are not functions of position, so 'x' and 'y' have no targets. # The rate source points to the output in the ODE which provides the time derivative of the # given state. phase.add_state('x', fix_initial=True, fix_final=True) phase.add_state('y', fix_initial=True, fix_final=True) phase.add_state('v', fix_initial=True) # Define theta as a control. phase.add_control(name='theta', units='rad', lower=0.01, upper=np.pi - 0.01, shape=(1, )) phase.add_timeseries_output('*') # Minimize final time. phase.add_objective('time', loc='final') # Set the driver. p.driver = om.ScipyOptimizeDriver() # Allow OpenMDAO to automatically determine our sparsity pattern. # Doing so can significant speed up the execution of Dymos. p.driver.declare_coloring() # Setup the problem with warnings.catch_warnings(record=True) as ctx: warnings.simplefilter('always') p.setup(check=True) expected_warning = "Cannot add ODE output foo to the timeseries output. It is sized " \ "such that its first dimension != num_nodes." self.assertIn(expected_warning, [str(w.message) for w in ctx]) # Now that the OpenMDAO problem is setup, we can set the values of the states. p.set_val('traj.phase0.t_initial', 0.0, units='s') p.set_val('traj.phase0.t_duration', 5.0, units='s') p.set_val('traj.phase0.states:x', phase.interpolate(ys=[0, 10], nodes='state_input'), units='m') p.set_val('traj.phase0.states:y', phase.interpolate(ys=[10, 5], nodes='state_input'), units='m') p.set_val('traj.phase0.states:v', phase.interpolate(ys=[0, 5], nodes='state_input'), units='m/s') p.set_val('traj.phase0.controls:theta', phase.interpolate(ys=[0.01, 90], nodes='control_input'), units='deg') # Run the driver to solve the problem with warnings.catch_warnings(record=True) as ctx: warnings.simplefilter('always') dm.run_problem(p, simulate=True, make_plots=False) expected_warning = "Cannot add ODE output foo to the timeseries output. It is sized " \ "such that its first dimension != num_nodes." self.assertIn(expected_warning, [str(w.message) for w in ctx]) sol = om.CaseReader('dymos_solution.db').get_case('final') sim = om.CaseReader('dymos_simulation.db').get_case('final') with self.assertRaises(expected_exception=KeyError) as e: sol.get_val('traj.phase0.timeseries.foo') self.assertEqual( str(e.exception), "'Variable name \"traj.phase0.timeseries.foo\" not found.'") with self.assertRaises(expected_exception=KeyError) as e: sim.get_val('traj.phase0.timeseries.foo') self.assertEqual( str(e.exception), "'Variable name \"traj.phase0.timeseries.foo\" not found.'")
fix_final=True, solve_segments=False) phase.add_boundary_constraint('T', loc='final', units='K', upper=60, lower=20, shape=(1, )) phase.add_parameter('d', opt=True, lower=0.00001, upper=0.1) phase.add_objective('d', loc='final', scaler=1) p.model.linear_solver = om.DirectSolver() p.setup() p['traj.phase0.t_initial'] = 0.0 p['traj.phase0.states:T'] = phase.interpolate(ys=[20, 60], nodes='state_input') dm.run_problem(p) exp_out = traj.simulate() plot_results([('traj.phase0.timeseries.time', 'traj.phase0.timeseries.states:T', 'time (s)', 'temp (K)')], title='Temps', p_sol=p, p_sim=exp_out) plt.show() # t = np.linspace(0, 45, 101) # def dT_calc(Ts,t): # Tf = Ts[0] # Tc = Ts[1] # K = 0.03 # W/mk
def ex_aircraft_steady_flight(optimizer='SLSQP', solve_segments=False, use_boundary_constraints=False, compressed=False): p = om.Problem(model=om.Group()) p.driver = om.pyOptSparseDriver() _, optimizer = set_pyoptsparse_opt(optimizer, fallback=False) p.driver.options['optimizer'] = optimizer p.driver.declare_coloring() if optimizer == 'SNOPT': p.driver.opt_settings['Major iterations limit'] = 20 p.driver.opt_settings['Major feasibility tolerance'] = 1.0E-6 p.driver.opt_settings['Major optimality tolerance'] = 1.0E-6 p.driver.opt_settings["Linesearch tolerance"] = 0.10 p.driver.opt_settings['iSumm'] = 6 if optimizer == 'SLSQP': p.driver.opt_settings['MAXIT'] = 50 num_seg = 15 seg_ends, _ = lgl(num_seg + 1) phase = dm.Phase(ode_class=AircraftODE, transcription=dm.Radau(num_segments=num_seg, segment_ends=seg_ends, order=3, compressed=compressed, solve_segments=solve_segments)) # Pass Reference Area from an external source assumptions = p.model.add_subsystem('assumptions', om.IndepVarComp()) assumptions.add_output('S', val=427.8, units='m**2') assumptions.add_output('mass_empty', val=1.0, units='kg') assumptions.add_output('mass_payload', val=1.0, units='kg') p.model.add_subsystem('phase0', phase) phase.set_time_options(initial_bounds=(0, 0), duration_bounds=(300, 10000), duration_ref=5600) fix_final = True if use_boundary_constraints: fix_final = False phase.add_boundary_constraint('mass_fuel', loc='final', units='lbm', equals=1e-3, linear=False) phase.add_boundary_constraint('alt', loc='final', units='kft', equals=10.0, linear=False) phase.add_state('range', units='NM', rate_source='range_rate_comp.dXdt:range', fix_initial=True, fix_final=False, ref=1e-3, defect_ref=1e-3, lower=0, upper=2000) phase.add_state('mass_fuel', units='lbm', rate_source='propulsion.dXdt:mass_fuel', fix_initial=True, fix_final=fix_final, upper=1.5E5, lower=0.0, ref=1e2, defect_ref=1e2) phase.add_state('alt', units='kft', rate_source='climb_rate', fix_initial=True, fix_final=fix_final, lower=0.0, upper=60, ref=1e-3, defect_ref=1e-3) phase.add_control('climb_rate', units='ft/min', opt=True, lower=-3000, upper=3000, targets=['gam_comp.climb_rate'], rate_continuity=True, rate2_continuity=False) phase.add_control('mach', targets=['tas_comp.mach', 'aero.mach'], opt=False) phase.add_parameter( 'S', targets=['aero.S', 'flight_equilibrium.S', 'propulsion.S'], units='m**2') phase.add_parameter('mass_empty', targets=['mass_comp.mass_empty'], units='kg') phase.add_parameter('mass_payload', targets=['mass_comp.mass_payload'], units='kg') phase.add_path_constraint('propulsion.tau', lower=0.01, upper=2.0, shape=(1, )) p.model.connect('assumptions.S', 'phase0.parameters:S') p.model.connect('assumptions.mass_empty', 'phase0.parameters:mass_empty') p.model.connect('assumptions.mass_payload', 'phase0.parameters:mass_payload') phase.add_objective('range', loc='final', ref=-1.0e-4) p.setup() p['phase0.t_initial'] = 0.0 p['phase0.t_duration'] = 3600.0 p['phase0.states:range'][:] = phase.interpolate(ys=(0, 724.0), nodes='state_input') p['phase0.states:mass_fuel'][:] = phase.interpolate(ys=(30000, 1e-3), nodes='state_input') p['phase0.states:alt'][:] = 10.0 p['phase0.controls:mach'][:] = 0.8 p['assumptions.S'] = 427.8 p['assumptions.mass_empty'] = 0.15E6 p['assumptions.mass_payload'] = 84.02869 * 400 dm.run_problem(p) return p
def _test_transcription(self, transcription=dm.GaussLobatto): # # Define the OpenMDAO problem # p = om.Problem(model=om.Group()) # # Define a Trajectory object # traj = dm.Trajectory() p.model.add_subsystem('traj', subsys=traj) # # Define a Dymos Phase object with GaussLobatto Transcription # phase = dm.Phase(ode_class=BrachistochroneODE, transcription=transcription(num_segments=10, order=3, solve_segments='forward')) traj.add_phase(name='phase0', phase=phase) # # Set the time options # Time has no targets in our ODE. # We fix the initial time so that the it is not a design variable in the optimization. # The duration of the phase is allowed to be optimized, but is bounded on [0.5, 10]. # phase.set_time_options(fix_initial=True, fix_duration=True, units='s') # # Set the time options # Initial values of positions and velocity are all fixed. # The final value of position are fixed, but the final velocity is a free variable. # The equations of motion are not functions of position, so 'x' and 'y' have no targets. # The rate source points to the output in the ODE which provides the time derivative of the # given state. phase.add_state('x', fix_initial=True) phase.add_state('y', fix_initial=True) phase.add_state('v', fix_initial=True) phase.add_state('int_one', fix_initial=True, rate_source='one') phase.add_state('int_time', fix_initial=True, rate_source='time') phase.add_state('int_time_phase', fix_initial=True, rate_source='time_phase') phase.add_state('int_int_one', fix_initial=True, rate_source='int_one') # Define theta as a control. phase.add_control(name='theta', units='rad', lower=0, upper=np.pi) # With no targets we must explicitly assign units and shape to this parameter. # Its only purpose is to be integrated as the rate source for a state. phase.add_parameter(name='one', opt=False, units=None, shape=(1, )) # Minimize final time. phase.add_objective('time', loc='final') # Set the driver. p.driver = om.ScipyOptimizeDriver() # Allow OpenMDAO to automatically determine our sparsity pattern. # Doing so can significant speed up the execution of Dymos. p.driver.declare_coloring() # Setup the problem p.setup(check=True) # Now that the OpenMDAO problem is setup, we can set the values of the states. p.set_val('traj.phase0.t_initial', 0.0, units='s') p.set_val('traj.phase0.t_duration', 5.0, units='s') p.set_val('traj.phase0.parameters:one', 1.0) p.set_val('traj.phase0.states:x', phase.interp('x', [0, 10]), units='m') p.set_val('traj.phase0.states:y', phase.interp('y', [10, 5]), units='m') p.set_val('traj.phase0.states:v', phase.interp('v', [0, 5]), units='m/s') p.set_val('traj.phase0.controls:theta', phase.interp('theta', [0.1, 45], nodes='control_input'), units='deg') # Additional states to test rate sources p.set_val('traj.phase0.states:int_one', phase.interp('int_one', [0, 10]), units='s') p.set_val('traj.phase0.states:int_time', phase.interp('int_time', [0, 10]), units='s**2') p.set_val('traj.phase0.states:int_time_phase', phase.interp('int_time_phase', [0, 10]), units='s**2') p.set_val('traj.phase0.states:int_int_one', phase.interp('int_int_one', [0, 10]), units='s**2') # Run the driver to solve the problem dm.run_problem(p, simulate=True) time_sol = p.get_val('traj.phase0.timeseries.time') time_phase_sol = p.get_val('traj.phase0.timeseries.time_phase') int_one_sol = p.get_val('traj.phase0.timeseries.states:int_one') int_time_sol = p.get_val('traj.phase0.timeseries.states:int_time') int_time_phase_sol = p.get_val( 'traj.phase0.timeseries.states:int_time_phase') int_int_one_sol = p.get_val( 'traj.phase0.timeseries.states:int_int_one') time_sim = p.get_val('traj.phase0.timeseries.time') time_phase_sim = p.get_val('traj.phase0.timeseries.time_phase') int_one_sim = p.get_val('traj.phase0.timeseries.states:int_one') int_time_sim = p.get_val('traj.phase0.timeseries.states:int_time') int_time_phase_sim = p.get_val( 'traj.phase0.timeseries.states:int_time_phase') int_int_one_sim = p.get_val( 'traj.phase0.timeseries.states:int_int_one') # Integral of one should match time and time_phase in this case. assert_near_equal(int_one_sol, time_sol, tolerance=1.0E-12) assert_near_equal(int_one_sol, time_phase_sol, tolerance=1.0E-12) assert_near_equal(int_one_sim, time_sim, tolerance=1.0E-12) assert_near_equal(int_one_sim, time_phase_sim, tolerance=1.0E-12) # Integral of time and time_phase should be t**2/2 assert_near_equal(time_sol, time_phase_sol, tolerance=1.0E-12) assert_near_equal(int_time_sol, time_sol**2 / 2, tolerance=1.0E-12) assert_near_equal(int_time_phase_sol, time_phase_sol**2 / 2, tolerance=1.0E-12) assert_near_equal(time_sim, time_phase_sim, tolerance=1.0E-12) assert_near_equal(int_time_sim, time_sim**2 / 2, tolerance=1.0E-12) assert_near_equal(int_time_phase_sim, time_phase_sim**2 / 2, tolerance=1.0E-12) # Double integral of one should be the same as the integral of time assert_near_equal(int_int_one_sol, int_time_sol, tolerance=1.0E-12) assert_near_equal(int_int_one_sim, int_time_sim, tolerance=1.0E-12) assert_timeseries_near_equal(time_sol, int_int_one_sol, time_sim, int_int_one_sim)
def _test_integrate_polynomial_control_rate2(self, transcription): # # Define the OpenMDAO problem # p = om.Problem(model=om.Group()) # # Define a Trajectory object # traj = dm.Trajectory() p.model.add_subsystem('traj', subsys=traj) # # Define a Dymos Phase object with GaussLobatto Transcription # phase = dm.Phase(ode_class=BrachistochroneODE, transcription=transcription(num_segments=20, order=3)) traj.add_phase(name='phase0', phase=phase) # # Set the time options # Time has no targets in our ODE. # We fix the initial time so that the it is not a design variable in the optimization. # The duration of the phase is allowed to be optimized, but is bounded on [0.5, 10]. # phase.set_time_options(fix_initial=True, duration_bounds=(1.0, 10.0), units='s') # # Set the time options # Initial values of positions and velocity are all fixed. # The final value of position are fixed, but the final velocity is a free variable. # The equations of motion are not functions of position, so 'x' and 'y' have no targets. # The rate source points to the output in the ODE which provides the time derivative of the # given state. phase.add_state('x', fix_initial=True, fix_final=True) phase.add_state('y', fix_initial=True, fix_final=True) phase.add_state('v', fix_initial=True) phase.add_state('int_theta_dot', fix_initial=False, rate_source='theta_rate2') phase.add_state('int_theta', fix_initial=False, rate_source='int_theta_dot', targets=['theta']) # Define theta as a control. phase.add_polynomial_control(name='theta', order=11, units='rad', shape=(1, ), targets=None) # Force the initial value of the theta polynomial control to equal the initial value of the theta state. traj.add_linkage_constraint(phase_a='phase0', phase_b='phase0', var_a='theta', var_b='int_theta', loc_a='initial', loc_b='initial') traj.add_linkage_constraint(phase_a='phase0', phase_b='phase0', var_a='int_theta_dot', var_b='theta_rate', loc_a='initial', loc_b='initial', units='rad/s') # Minimize final time. phase.add_objective('time', loc='final') # Set the driver. p.driver = om.pyOptSparseDriver(optimizer='SLSQP') # Allow OpenMDAO to automatically determine our sparsity pattern. # Doing so can significant speed up the execution of Dymos. p.driver.declare_coloring() # Setup the problem p.setup(check=True) # Now that the OpenMDAO problem is setup, we can set the values of the states. p.set_val('traj.phase0.t_initial', 0.0, units='s') p.set_val('traj.phase0.t_duration', 5.0, units='s') p.set_val('traj.phase0.states:x', phase.interp('x', [0, 10]), units='m') p.set_val('traj.phase0.states:y', phase.interp('y', [10, 5]), units='m') p.set_val('traj.phase0.states:v', phase.interp('v', [0, 5]), units='m/s') p.set_val('traj.phase0.states:int_theta', phase.interp('int_theta', [0.1, 45]), units='deg') p.set_val('traj.phase0.states:int_theta_dot', phase.interp('int_theta_dot', [0, 0]), units='deg/s') p.set_val('traj.phase0.polynomial_controls:theta', 45.0, units='deg') # Run the driver to solve the problem dm.run_problem(p, simulate=True, make_plots=True) sol = om.CaseReader('dymos_solution.db').get_case('final') sim = om.CaseReader('dymos_simulation.db').get_case('final') t_sol = sol.get_val('traj.phase0.timeseries.time') t_sim = sim.get_val('traj.phase0.timeseries.time') x_sol = sol.get_val('traj.phase0.timeseries.states:x') x_sim = sim.get_val('traj.phase0.timeseries.states:x') y_sol = sol.get_val('traj.phase0.timeseries.states:y') y_sim = sim.get_val('traj.phase0.timeseries.states:y') v_sol = sol.get_val('traj.phase0.timeseries.states:v') v_sim = sim.get_val('traj.phase0.timeseries.states:v') int_theta_sol = sol.get_val('traj.phase0.timeseries.states:int_theta') int_theta_sim = sim.get_val('traj.phase0.timeseries.states:int_theta') theta_sol = sol.get_val( 'traj.phase0.timeseries.polynomial_controls:theta') theta_sim = sim.get_val( 'traj.phase0.timeseries.polynomial_controls:theta') assert_timeseries_near_equal(t_sol, x_sol, t_sim, x_sim, tolerance=1.0E-2) assert_timeseries_near_equal(t_sol, y_sol, t_sim, y_sim, tolerance=1.0E-2) assert_timeseries_near_equal(t_sol, v_sol, t_sim, v_sim, tolerance=1.0E-2) assert_timeseries_near_equal(t_sol, int_theta_sol, t_sim, int_theta_sim, tolerance=1.0E-2)
def test_ivp_driver_run_problem(self): import openmdao.api as om import dymos as dm import matplotlib.pyplot as plt plt.switch_backend('Agg') # disable plotting to the screen from dymos.examples.oscillator.doc.oscillator_ode import OscillatorODE # Instantiate an OpenMDAO Problem instance. prob = om.Problem() # We need an optimization driver. To solve this simple problem ScipyOptimizerDriver will work. prob.driver = om.ScipyOptimizeDriver() # Instantiate a Dymos Trajectory and add it to the Problem model. traj = dm.Trajectory() prob.model.add_subsystem('traj', traj) # Instantiate a Phase and add it to the Trajectory. phase = dm.Phase(ode_class=OscillatorODE, transcription=dm.Radau(num_segments=4)) traj.add_phase('phase0', phase) # Tell Dymos that the duration of the phase is bounded. phase.set_time_options(fix_initial=True, fix_duration=True) # Tell Dymos the states to be propagated using the given ODE. phase.add_state('x', fix_initial=True, rate_source='v', targets=['x'], units='m') phase.add_state('v', fix_initial=True, rate_source='v_dot', targets=['v'], units='m/s') # The spring constant, damping coefficient, and mass are inputs to the system that are # constant throughout the phase. phase.add_parameter('k', units='N/m', targets=['k']) phase.add_parameter('c', units='N*s/m', targets=['c']) phase.add_parameter('m', units='kg', targets=['m']) # Since we're using an optimization driver, an objective is required. We'll minimize # the final time in this case. phase.add_objective('time', loc='final') # Setup the OpenMDAO problem prob.setup() # Assign values to the times and states prob.set_val('traj.phase0.t_initial', 0.0) prob.set_val('traj.phase0.t_duration', 15.0) prob.set_val('traj.phase0.states:x', 10.0) prob.set_val('traj.phase0.states:v', 0.0) prob.set_val('traj.phase0.parameters:k', 1.0) prob.set_val('traj.phase0.parameters:c', 0.5) prob.set_val('traj.phase0.parameters:m', 1.0) # Now we're using the optimization driver to iteratively run the model and vary the # phase duration until the final y value is 0. dm.run_problem(prob) # Perform an explicit simulation of our ODE from the initial conditions. sim_out = traj.simulate(times_per_seg=50) # Plot the state values obtained from the phase timeseries objects in the simulation output. t_sol = prob.get_val('traj.phase0.timeseries.time') t_sim = sim_out.get_val('traj.phase0.timeseries.time') states = ['x', 'v'] fig, axes = plt.subplots(len(states), 1) for i, state in enumerate(states): sol = axes[i].plot( t_sol, prob.get_val(f'traj.phase0.timeseries.states:{state}'), 'o') sim = axes[i].plot( t_sim, sim_out.get_val(f'traj.phase0.timeseries.states:{state}'), '-') axes[i].set_ylabel(state) axes[-1].set_xlabel('time (s)') fig.legend((sol[0], sim[0]), ('solution', 'simulation'), 'lower right', ncol=2) plt.tight_layout() plt.show()
def test_racecar_for_docs(self): import numpy as np import openmdao.api as om from openmdao.utils.assert_utils import assert_near_equal import dymos as dm import matplotlib.pyplot as plt import matplotlib as mpl from dymos.examples.racecar.combinedODE import CombinedODE from dymos.examples.racecar.spline import ( get_spline, get_track_points, get_gate_normals, reverse_transform_gates, set_gate_displacements, transform_gates, ) from dymos.examples.racecar.linewidthhelper import linewidth_from_data_units from dymos.examples.racecar.tracks import ovaltrack # track curvature imports # change track here and in curvature.py. Tracks are defined in tracks.py track = ovaltrack # generate nodes along the centerline for curvature calculation (different # than collocation nodes) points = get_track_points(track) # fit the centerline spline. finespline, gates, gatesd, curv, slope = get_spline(points, s=0.0) # by default 10000 points s_final = track.get_total_length() # Define the OpenMDAO problem p = om.Problem(model=om.Group()) # Define a Trajectory object traj = dm.Trajectory() p.model.add_subsystem('traj', subsys=traj) # Define a Dymos Phase object with GaussLobatto Transcription phase = dm.Phase(ode_class=CombinedODE, transcription=dm.GaussLobatto(num_segments=50, order=3, compressed=True)) traj.add_phase(name='phase0', phase=phase) # Set the time options, in this problem we perform a change of variables. So 'time' is # actually 's' (distance along the track centerline) # This is done to fix the collocation nodes in space, which saves us the calculation of # the rate of change of curvature. # The state equations are written with respect to time, the variable change occurs in # timeODE.py phase.set_time_options(fix_initial=True, fix_duration=True, duration_val=s_final, targets=['curv.s'], units='m', duration_ref=s_final, duration_ref0=10) # Define states phase.add_state('t', fix_initial=True, fix_final=False, units='s', lower=0, rate_source='dt_ds', ref=100) # time phase.add_state( 'n', fix_initial=False, fix_final=False, units='m', upper=4.0, lower=-4.0, rate_source='dn_ds', targets=['n'], ref=4.0 ) # normal distance to centerline. The bounds on n define the # width of the track phase.add_state('V', fix_initial=False, fix_final=False, units='m/s', ref=40, ref0=5, rate_source='dV_ds', targets=['V']) # velocity phase.add_state( 'alpha', fix_initial=False, fix_final=False, units='rad', rate_source='dalpha_ds', targets=['alpha'], ref=0.15) # vehicle heading angle with respect to centerline phase.add_state( 'lambda', fix_initial=False, fix_final=False, units='rad', rate_source='dlambda_ds', targets=['lambda'], ref=0.01 ) # vehicle slip angle, or angle between the axis of the vehicle # and velocity vector (all cars drift a little) phase.add_state('omega', fix_initial=False, fix_final=False, units='rad/s', rate_source='domega_ds', targets=['omega'], ref=0.3) # yaw rate phase.add_state('ax', fix_initial=False, fix_final=False, units='m/s**2', rate_source='dax_ds', targets=['ax'], ref=8) # longitudinal acceleration phase.add_state('ay', fix_initial=False, fix_final=False, units='m/s**2', rate_source='day_ds', targets=['ay'], ref=8) # lateral acceleration # Define Controls phase.add_control(name='delta', units='rad', lower=None, upper=None, fix_initial=False, fix_final=False, targets=['delta'], ref=0.04) # steering angle phase.add_control( name='thrust', units=None, fix_initial=False, fix_final=False, targets=['thrust'] ) # the thrust controls the longitudinal force of the rear tires and is # positive while accelerating, negative while braking # Performance Constraints pmax = 960000 # W phase.add_path_constraint('power', upper=pmax, ref=100000) # engine power limit # The following four constraints are the tire friction limits, with 'rr' designating the # rear right wheel etc. This limit is computed in tireConstraintODE.py phase.add_path_constraint('c_rr', upper=1) phase.add_path_constraint('c_rl', upper=1) phase.add_path_constraint('c_fr', upper=1) phase.add_path_constraint('c_fl', upper=1) # Some of the vehicle design parameters are available to set here. Other parameters can # be found in their respective ODE files. phase.add_parameter( 'M', val=800.0, units='kg', opt=False, targets=['car.M', 'tire.M', 'tireconstraint.M', 'normal.M'], dynamic=False) # vehicle mass phase.add_parameter('beta', val=0.62, units=None, opt=False, targets=['tire.beta'], dynamic=False) # brake bias phase.add_parameter('CoP', val=1.6, units='m', opt=False, targets=['normal.CoP'], dynamic=False) # center of pressure location phase.add_parameter('h', val=0.3, units='m', opt=False, targets=['normal.h'], dynamic=False) # center of gravity height phase.add_parameter('chi', val=0.5, units=None, opt=False, targets=['normal.chi'], dynamic=False) # roll stiffness phase.add_parameter('ClA', val=4.0, units='m**2', opt=False, targets=['normal.ClA'], dynamic=False) # downforce coefficient*area phase.add_parameter('CdA', val=2.0, units='m**2', opt=False, targets=['car.CdA'], dynamic=False) # drag coefficient*area # Minimize final time. # note that we use the 'state' time instead of Dymos 'time' phase.add_objective('t', loc='final') # Add output timeseries phase.add_timeseries_output('*') # Link the states at the start and end of the phase in order to ensure a continous lap traj.link_phases( phases=['phase0', 'phase0'], vars=['V', 'n', 'alpha', 'omega', 'lambda', 'ax', 'ay'], locs=('final', 'initial')) # Set the driver. IPOPT or SNOPT are recommended but SLSQP might work. p.driver = om.pyOptSparseDriver(optimizer='IPOPT') p.driver.opt_settings['mu_init'] = 1e-3 p.driver.opt_settings['max_iter'] = 500 p.driver.opt_settings['acceptable_tol'] = 1e-3 p.driver.opt_settings['constr_viol_tol'] = 1e-3 p.driver.opt_settings['compl_inf_tol'] = 1e-3 p.driver.opt_settings['acceptable_iter'] = 0 p.driver.opt_settings['tol'] = 1e-3 p.driver.opt_settings['nlp_scaling_method'] = 'none' p.driver.opt_settings['print_level'] = 5 p.driver.opt_settings[ 'nlp_scaling_method'] = 'gradient-based' # for faster convergence p.driver.options['print_results'] = False # Allow OpenMDAO to automatically determine our sparsity pattern. # Doing so can significant speed up the execution of Dymos. p.driver.declare_coloring() # Setup the problem p.setup(check=True) # force_alloc_complex=True # Now that the OpenMDAO problem is setup, we can set the values of the states. # States p.set_val( 'traj.phase0.states:V', phase.interpolate(ys=[20, 20], nodes='state_input'), units='m/s' ) # non-zero velocity in order to protect against 1/0 errors. p.set_val('traj.phase0.states:lambda', phase.interpolate(ys=[0.0, 0.0], nodes='state_input'), units='rad') # all other states start at 0 p.set_val('traj.phase0.states:omega', phase.interpolate(ys=[0.0, 0.0], nodes='state_input'), units='rad/s') p.set_val('traj.phase0.states:alpha', phase.interpolate(ys=[0.0, 0.0], nodes='state_input'), units='rad') p.set_val('traj.phase0.states:ax', phase.interpolate(ys=[0.0, 0.0], nodes='state_input'), units='m/s**2') p.set_val('traj.phase0.states:ay', phase.interpolate(ys=[0.0, 0.0], nodes='state_input'), units='m/s**2') p.set_val('traj.phase0.states:n', phase.interpolate(ys=[0.0, 0.0], nodes='state_input'), units='m') p.set_val('traj.phase0.states:t', phase.interpolate(ys=[0.0, 100.0], nodes='state_input'), units='s') # initial guess for what the final time should be # Controls p.set_val('traj.phase0.controls:delta', phase.interpolate(ys=[0.0, 0.0], nodes='control_input'), units='rad') p.set_val( 'traj.phase0.controls:thrust', phase.interpolate(ys=[0.1, 0.1], nodes='control_input'), units=None) # a small amount of thrust can speed up convergence dm.run_problem(p, run_driver=True) print('Optimization finished') # Test this example in Dymos' continuous integration process assert_near_equal(p.get_val('traj.phase0.timeseries.states:t')[-1], 22.2657, tolerance=0.01) # Get optimized time series n = p.get_val('traj.phase0.timeseries.states:n') s = p.get_val('traj.phase0.timeseries.time') V = p.get_val('traj.phase0.timeseries.states:V') thrust = p.get_val('traj.phase0.timeseries.controls:thrust') delta = p.get_val('traj.phase0.timeseries.controls:delta') power = p.get_val('traj.phase0.timeseries.power', units='W') print("Plotting") # We know the optimal distance from the centerline (n). To transform this into the racing # line we fit a spline to the displaced points. This will let us plot the racing line in # x/y coordinates normals = get_gate_normals(finespline, slope) newgates = [] newnormals = [] newn = [] for i in range(len(n)): index = ((s[i] / s_final) * np.array(finespline).shape[1]).astype( int) # interpolation to find the appropriate index if index[0] == np.array(finespline).shape[1]: index[0] = np.array(finespline).shape[1] - 1 if i > 0 and s[i] == s[i - 1]: continue else: newgates.append( [finespline[0][index[0]], finespline[1][index[0]]]) newnormals.append(normals[index[0]]) newn.append(n[i][0]) newgates = reverse_transform_gates(newgates) displaced_gates = set_gate_displacements(newn, newgates, newnormals) displaced_gates = np.array((transform_gates(displaced_gates))) npoints = 1000 # fit the racing line spline to npoints displaced_spline, gates, gatesd, curv, slope = get_spline( displaced_gates, 1 / npoints, 0) plt.rcParams.update({'font.size': 12}) def plot_track_with_data(state, s): # this function plots the track state = np.array(state)[:, 0] s = np.array(s)[:, 0] s_new = np.linspace(0, s_final, npoints) # Colormap and norm of the track plot cmap = mpl.cm.get_cmap('viridis') norm = mpl.colors.Normalize(vmin=np.amin(state), vmax=np.amax(state)) fig, ax = plt.subplots(figsize=(15, 6)) # establishes the figure axis limits needed for plotting the track below plt.plot(displaced_spline[0], displaced_spline[1], linewidth=0.1, solid_capstyle="butt") plt.axis('equal') # the linewidth is set in order to match the width of the track plt.plot(finespline[0], finespline[1], 'k', linewidth=linewidth_from_data_units(8.5, ax), solid_capstyle="butt") plt.plot(finespline[0], finespline[1], 'w', linewidth=linewidth_from_data_units(8, ax), solid_capstyle="butt" ) # 8 is the width, and the 8.5 wide line draws 'kerbs' plt.xlabel('x (m)') plt.ylabel('y (m)') # plot spline with color for i in range(1, len(displaced_spline[0])): s_spline = s_new[i] index_greater = np.argwhere(s >= s_spline)[0][0] index_less = np.argwhere(s < s_spline)[-1][0] x = s_spline xp = np.array([s[index_less], s[index_greater]]) fp = np.array([state[index_less], state[index_greater]]) interp_state = np.interp( x, xp, fp) # interpolate the given state to calculate the color # calculate the appropriate color state_color = norm(interp_state) color = cmap(state_color) color = mpl.colors.to_hex(color) # the track plot consists of thousands of tiny lines: point = [displaced_spline[0][i], displaced_spline[1][i]] prevpoint = [ displaced_spline[0][i - 1], displaced_spline[1][i - 1] ] if i <= 5 or i == len(displaced_spline[0]) - 1: plt.plot([point[0], prevpoint[0]], [point[1], prevpoint[1]], color, linewidth=linewidth_from_data_units(1.5, ax), solid_capstyle="butt", antialiased=True) else: plt.plot([point[0], prevpoint[0]], [point[1], prevpoint[1]], color, linewidth=linewidth_from_data_units(1.5, ax), solid_capstyle="projecting", antialiased=True) clb = plt.colorbar(mpl.cm.ScalarMappable(norm=norm, cmap=cmap), fraction=0.02, pad=0.04) # add colorbar if np.array_equal(state, V[:, 0]): clb.set_label('Velocity (m/s)') elif np.array_equal(state, thrust[:, 0]): clb.set_label('Thrust') elif np.array_equal(state, delta[:, 0]): clb.set_label('Delta') plt.tight_layout() plt.grid() # Create the plots plot_track_with_data(V, s) plot_track_with_data(thrust, s) plot_track_with_data(delta, s) # Plot the main vehicle telemetry fig, axes = plt.subplots(nrows=4, ncols=1, figsize=(15, 8)) # Velocity vs s axes[0].plot(s, p.get_val('traj.phase0.timeseries.states:V'), label='solution') axes[0].set_xlabel('s (m)') axes[0].set_ylabel('V (m/s)') axes[0].grid() axes[0].set_xlim(0, s_final) # n vs s axes[1].plot(s, p.get_val('traj.phase0.timeseries.states:n', units='m'), label='solution') axes[1].set_xlabel('s (m)') axes[1].set_ylabel('n (m)') axes[1].grid() axes[1].set_xlim(0, s_final) # throttle vs s axes[2].plot(s, thrust) axes[2].set_xlabel('s (m)') axes[2].set_ylabel('thrust') axes[2].grid() axes[2].set_xlim(0, s_final) # delta vs s axes[3].plot(s, p.get_val('traj.phase0.timeseries.controls:delta', units=None), label='solution') axes[3].set_xlabel('s (m)') axes[3].set_ylabel('delta') axes[3].grid() axes[3].set_xlim(0, s_final) plt.tight_layout() # Performance constraint plot. Tire friction and power constraints fig, axes = plt.subplots(nrows=1, ncols=1, figsize=(15, 4)) plt.subplots_adjust(right=0.82, bottom=0.14, top=0.97, left=0.07) axes.plot(s, p.get_val('traj.phase0.timeseries.c_fl', units=None), label='c_fl') axes.plot(s, p.get_val('traj.phase0.timeseries.c_fr', units=None), label='c_fr') axes.plot(s, p.get_val('traj.phase0.timeseries.c_rl', units=None), label='c_rl') axes.plot(s, p.get_val('traj.phase0.timeseries.c_rr', units=None), label='c_rr') axes.plot(s, power / pmax, label='Power') axes.legend(bbox_to_anchor=(1.04, 0.5), loc="center left") axes.set_xlabel('s (m)') axes.set_ylabel('Performance constraints') axes.grid() axes.set_xlim(0, s_final) plt.show()
def brachistochrone_min_time(transcription='gauss-lobatto', num_segments=8, transcription_order=3, compressed=True, optimizer='SLSQP', run_driver=True, force_alloc_complex=False, solve_segments=False): p = om.Problem(model=om.Group()) p.driver = om.pyOptSparseDriver() p.driver.options['optimizer'] = optimizer p.driver.declare_coloring(tol=1.0E-12) if transcription == 'gauss-lobatto': t = dm.GaussLobatto(num_segments=num_segments, order=transcription_order, compressed=compressed) elif transcription == 'radau-ps': t = dm.Radau(num_segments=num_segments, order=transcription_order, compressed=compressed) elif transcription == 'runge-kutta': t = dm.RungeKutta(num_segments=num_segments, order=transcription_order, compressed=compressed) traj = dm.Trajectory() phase = dm.Phase(ode_class=BrachistochroneODE, transcription=t) p.model.add_subsystem('traj0', traj) traj.add_phase('phase0', phase) # p.model.add_subsystem('traj0', traj) phase.set_time_options(fix_initial=True, duration_bounds=(.5, 10)) phase.add_state('x', rate_source=BrachistochroneODE.states['x']['rate_source'], units=BrachistochroneODE.states['x']['units'], fix_initial=True, fix_final=False, solve_segments=solve_segments) phase.add_state('y', rate_source=BrachistochroneODE.states['y']['rate_source'], units=BrachistochroneODE.states['y']['units'], fix_initial=True, fix_final=False, solve_segments=solve_segments) # Note that by omitting the targets here Dymos will automatically attempt to connect # to a top-level input named 'v' in the ODE, and connect to nothing if it's not found. phase.add_state('v', rate_source=BrachistochroneODE.states['v']['rate_source'], units=BrachistochroneODE.states['v']['units'], fix_initial=True, fix_final=False, solve_segments=solve_segments) phase.add_control( 'theta', targets=BrachistochroneODE.parameters['theta']['targets'], continuity=True, rate_continuity=True, units='deg', lower=0.01, upper=179.9) phase.add_parameter('g', targets=BrachistochroneODE.parameters['g']['targets'], units='m/s**2', val=9.80665) phase.add_timeseries('timeseries2', transcription=dm.Radau(num_segments=num_segments * 5, order=transcription_order, compressed=compressed), subset='control_input') phase.add_boundary_constraint('x', loc='final', equals=10) phase.add_boundary_constraint('y', loc='final', equals=5) # Minimize time at the end of the phase phase.add_objective('time_phase', loc='final', scaler=10) p.setup(check=['unconnected_inputs'], force_alloc_complex=force_alloc_complex) p['traj0.phase0.t_initial'] = 0.0 p['traj0.phase0.t_duration'] = 2.0 p['traj0.phase0.states:x'] = phase.interpolate(ys=[0, 10], nodes='state_input') p['traj0.phase0.states:y'] = phase.interpolate(ys=[10, 5], nodes='state_input') p['traj0.phase0.states:v'] = phase.interpolate(ys=[0, 9.9], nodes='state_input') p['traj0.phase0.controls:theta'] = phase.interpolate(ys=[5, 100], nodes='control_input') p['traj0.phase0.parameters:g'] = 9.80665 dm.run_problem(p, run_driver=run_driver) # Plot results if SHOW_PLOTS: exp_out = traj.simulate() fig, ax = plt.subplots() fig.suptitle('Brachistochrone Solution') x_imp = p.get_val('traj0.phase0.timeseries.states:x') y_imp = p.get_val('traj0.phase0.timeseries.states:y') x_exp = exp_out.get_val('traj0.phase0.timeseries.states:x') y_exp = exp_out.get_val('traj0.phase0.timeseries.states:y') ax.plot(x_imp, y_imp, 'ro', label='implicit') ax.plot(x_exp, y_exp, 'b-', label='explicit') ax.set_xlabel('x (m)') ax.set_ylabel('y (m)') ax.grid(True) ax.legend(loc='upper right') fig, ax = plt.subplots() fig.suptitle('Brachistochrone Solution') x_imp = p.get_val('traj0.phase0.timeseries.time_phase') y_imp = p.get_val('traj0.phase0.timeseries.controls:theta') x_exp = exp_out.get_val('traj0.phase0.timeseries.time_phase') y_exp = exp_out.get_val('traj0.phase0.timeseries.controls:theta') ax.plot(x_imp, y_imp, 'ro', label='implicit') ax.plot(x_exp, y_exp, 'b-', label='explicit') ax.set_xlabel('time (s)') ax.set_ylabel('theta (rad)') ax.grid(True) ax.legend(loc='lower right') plt.show() return p
def test_min_time_climb_for_docs_partial_coloring(self): import openmdao.api as om from openmdao.utils.assert_utils import assert_near_equal import dymos as dm from dymos.examples.min_time_climb.doc.min_time_climb_ode_partial_coloring import MinTimeClimbODE for fd in (False, True): if fd: pc_options = (False, True) else: pc_options = (False,) for pc in pc_options: with self.subTest(f'Finite Differencing: {fd} Partial Coloring: {pc}'): print(f'Finite Differencing: {fd} Partial Coloring: {pc}') # # Instantiate the problem and configure the optimization driver # p = om.Problem(model=om.Group()) p.driver = om.pyOptSparseDriver() p.driver.options['optimizer'] = 'IPOPT' p.driver.declare_coloring(tol=1.0E-12) p.driver.opt_settings['max_iter'] = 500 p.driver.opt_settings['alpha_for_y'] = 'safer-min-dual-infeas' # # Instantiate the trajectory and phase # traj = dm.Trajectory() phase = dm.Phase(ode_class=MinTimeClimbODE, ode_init_kwargs={'fd': fd, 'partial_coloring': pc}, transcription=dm.GaussLobatto(num_segments=30)) traj.add_phase('phase0', phase) p.model.add_subsystem('traj', traj) # # Set the options on the optimization variables # phase.set_time_options(fix_initial=True, duration_bounds=(50, 400), duration_ref=100.0) phase.add_state('r', fix_initial=True, lower=0, upper=1.0E6, ref=1.0E3, defect_ref=1.0E3, units='m', rate_source='flight_dynamics.r_dot') phase.add_state('h', fix_initial=True, lower=0, upper=20000.0, ref=1.0E2, defect_ref=1.0E2, units='m', rate_source='flight_dynamics.h_dot') phase.add_state('v', fix_initial=True, lower=10.0, ref=1.0E2, defect_ref=1.0E2, units='m/s', rate_source='flight_dynamics.v_dot') phase.add_state('gam', fix_initial=True, lower=-1.5, upper=1.5, ref=1.0, defect_ref=1.0, units='rad', rate_source='flight_dynamics.gam_dot') phase.add_state('m', fix_initial=True, lower=10.0, upper=1.0E5, ref=1.0E3, defect_ref=1.0E3, units='kg', rate_source='prop.m_dot') phase.add_control('alpha', units='deg', lower=-8.0, upper=8.0, scaler=1.0, rate_continuity=True, rate_continuity_scaler=100.0, rate2_continuity=False, targets=['alpha']) phase.add_parameter('S', val=49.2386, units='m**2', opt=False, targets=['S']) phase.add_parameter('Isp', val=1600.0, units='s', opt=False, targets=['Isp']) phase.add_parameter('throttle', val=1.0, opt=False, targets=['throttle']) # # Setup the boundary and path constraints # phase.add_boundary_constraint('h', loc='final', equals=20000, scaler=1.0E-3, units='m') phase.add_boundary_constraint('aero.mach', loc='final', equals=1.0, shape=(1,)) phase.add_boundary_constraint('gam', loc='final', equals=0.0, units='rad') phase.add_path_constraint(name='h', lower=100.0, upper=20000, ref=20000) phase.add_path_constraint(name='aero.mach', lower=0.1, upper=1.8, shape=(1,)) # Minimize time at the end of the phase phase.add_objective('time', loc='final', ref=100.0) p.model.linear_solver = om.DirectSolver() # # Setup the problem and set the initial guess # p.setup() p['traj.phase0.t_initial'] = 0.0 p['traj.phase0.t_duration'] = 500 p['traj.phase0.states:r'] = phase.interpolate(ys=[0.0, 50000.0], nodes='state_input') p['traj.phase0.states:h'] = phase.interpolate(ys=[100.0, 20000.0], nodes='state_input') p['traj.phase0.states:v'] = phase.interpolate(ys=[135.964, 283.159], nodes='state_input') p['traj.phase0.states:gam'] = phase.interpolate(ys=[0.0, 0.0], nodes='state_input') p['traj.phase0.states:m'] = phase.interpolate(ys=[19030.468, 10000.], nodes='state_input') p['traj.phase0.controls:alpha'] = phase.interpolate(ys=[0.0, 0.0], nodes='control_input') # # Solve for the optimal trajectory # dm.run_problem(p) # # This code is intended to save the coloring plots for the documentation. # In practice, use the command line interface to view these files instead: # `openmdao view_coloring coloring_files/total_coloring.pkl --view` # stfd = '_fd' if fd else '' stpc = '_pc' if pc else '' coloring_dir = f'coloring_files{stfd}{stpc}' if fd or pc: if os.path.exists(coloring_dir): shutil.rmtree(coloring_dir) shutil.move('coloring_files', coloring_dir) _view_coloring(os.path.join(coloring_dir, 'total_coloring.pkl')) # # Test the results # assert_near_equal(p.get_val('traj.phase0.t_duration'), 321.0, tolerance=1.0E-1)
def test_brachistochrone_for_docs_coloring_demo_solve_segments(self): import openmdao.api as om from openmdao.utils.assert_utils import assert_near_equal import dymos as dm from dymos.examples.plotting import plot_results from dymos.examples.brachistochrone import BrachistochroneODE from openmdao.utils.general_utils import set_pyoptsparse_opt # # Initialize the Problem and the optimization driver # p = om.Problem(model=om.Group()) _, optimizer = set_pyoptsparse_opt('IPOPT', fallback=True) p.driver = om.pyOptSparseDriver(optimizer=optimizer) p.driver.opt_settings['print_level'] = 4 # p.driver.declare_coloring() # # Create a trajectory and add a phase to it # traj = p.model.add_subsystem('traj', dm.Trajectory()) # # In this case the phase has many segments to demonstrate the impact of coloring. # phase = traj.add_phase( 'phase0', dm.Phase(ode_class=BrachistochroneODE, transcription=dm.Radau(num_segments=100, solve_segments='forward'))) # # Set the variables # phase.set_time_options(fix_initial=True, duration_bounds=(.5, 10)) phase.add_state('x', fix_initial=True) phase.add_state('y', fix_initial=True) phase.add_state('v', fix_initial=True) phase.add_control('theta', continuity=True, rate_continuity=True, units='deg', lower=0.01, upper=179.9) phase.add_parameter('g', units='m/s**2', val=9.80665) # # Replace state terminal bounds with nonlinear constraints # phase.add_boundary_constraint('x', loc='final', equals=10) phase.add_boundary_constraint('y', loc='final', equals=5) # # Minimize time at the end of the phase # phase.add_objective('time', loc='final', scaler=10) p.model.linear_solver = om.DirectSolver() # # Setup the Problem # p.setup() # # Set the initial values # p['traj.phase0.t_initial'] = 0.0 p['traj.phase0.t_duration'] = 2.0 p['traj.phase0.states:x'] = phase.interpolate(ys=[0, 10], nodes='state_input') p['traj.phase0.states:y'] = phase.interpolate(ys=[10, 5], nodes='state_input') p['traj.phase0.states:v'] = phase.interpolate(ys=[0, 9.9], nodes='state_input') p['traj.phase0.controls:theta'] = phase.interpolate( ys=[5, 100.5], nodes='control_input') # # Solve for the optimal trajectory # dm.run_problem(p) # Test the results assert_near_equal(p.get_val('traj.phase0.timeseries.time')[-1], 1.8016, tolerance=1.0E-3) # Generate the explicitly simulated trajectory exp_out = traj.simulate() plot_results( [('traj.phase0.timeseries.states:x', 'traj.phase0.timeseries.states:y', 'x (m)', 'y (m)'), ('traj.phase0.timeseries.time', 'traj.phase0.timeseries.controls:theta', 'time (s)', 'theta (deg)')], title='Brachistochrone Solution\nRadau Pseudospectral Method', p_sol=p, p_sim=exp_out) plt.show()
def test_min_time_climb_for_docs_gauss_lobatto(self): import matplotlib.pyplot as plt import openmdao.api as om from openmdao.utils.assert_utils import assert_near_equal import dymos as dm from dymos.examples.min_time_climb.min_time_climb_ode import MinTimeClimbODE from dymos.examples.plotting import plot_results # # Instantiate the problem and configure the optimization driver # p = om.Problem(model=om.Group()) p.driver = om.pyOptSparseDriver() p.driver.options['optimizer'] = 'SLSQP' p.driver.declare_coloring() # # Instantiate the trajectory and phase # traj = dm.Trajectory() phase = dm.Phase(ode_class=MinTimeClimbODE, transcription=dm.GaussLobatto(num_segments=15, compressed=True)) traj.add_phase('phase0', phase) p.model.add_subsystem('traj', traj) # # Set the options on the optimization variables # phase.set_time_options(fix_initial=True, duration_bounds=(50, 400), duration_ref=100.0) phase.add_state('r', fix_initial=True, lower=0, upper=1.0E6, ref=1.0E3, defect_ref=1.0E3, units='m', rate_source='flight_dynamics.r_dot') phase.add_state('h', fix_initial=True, lower=0, upper=20000.0, ref=1.0E2, defect_ref=1.0E2, units='m', rate_source='flight_dynamics.h_dot', targets=['h']) phase.add_state('v', fix_initial=True, lower=10.0, ref=1.0E2, defect_ref=1.0E2, units='m/s', rate_source='flight_dynamics.v_dot', targets=['v']) phase.add_state('gam', fix_initial=True, lower=-1.5, upper=1.5, ref=1.0, defect_ref=1.0, units='rad', rate_source='flight_dynamics.gam_dot', targets=['gam']) phase.add_state('m', fix_initial=True, lower=10.0, upper=1.0E5, ref=1.0E3, defect_ref=1.0E3, units='kg', rate_source='prop.m_dot', targets=['m']) phase.add_control('alpha', units='deg', lower=-8.0, upper=8.0, scaler=1.0, rate_continuity=True, rate_continuity_scaler=100.0, rate2_continuity=False, targets=['alpha']) phase.add_design_parameter('S', val=49.2386, units='m**2', opt=False, targets=['S']) phase.add_design_parameter('Isp', val=1600.0, units='s', opt=False, targets=['Isp']) phase.add_design_parameter('throttle', val=1.0, opt=False, targets=['throttle']) # # Setup the boundary and path constraints # phase.add_boundary_constraint('h', loc='final', equals=20000, scaler=1.0E-3, units='m') phase.add_boundary_constraint('aero.mach', loc='final', equals=1.0, shape=(1,)) phase.add_boundary_constraint('gam', loc='final', equals=0.0, units='rad') phase.add_path_constraint(name='h', lower=100.0, upper=20000, ref=20000) phase.add_path_constraint(name='aero.mach', lower=0.1, upper=1.8, shape=(1,)) # Minimize time at the end of the phase phase.add_objective('time', loc='final', ref=1.0) p.model.linear_solver = om.DirectSolver() # # Setup the problem and set the initial guess # p.setup(check=True) p['traj.phase0.t_initial'] = 0.0 p['traj.phase0.t_duration'] = 500 p['traj.phase0.states:r'] = phase.interpolate(ys=[0.0, 50000.0], nodes='state_input') p['traj.phase0.states:h'] = phase.interpolate(ys=[100.0, 20000.0], nodes='state_input') p['traj.phase0.states:v'] = phase.interpolate(ys=[135.964, 283.159], nodes='state_input') p['traj.phase0.states:gam'] = phase.interpolate(ys=[0.0, 0.0], nodes='state_input') p['traj.phase0.states:m'] = phase.interpolate(ys=[19030.468, 10000.], nodes='state_input') p['traj.phase0.controls:alpha'] = phase.interpolate(ys=[0.0, 0.0], nodes='control_input') # # Solve for the optimal trajectory # dm.run_problem(p) # # Test the results # assert_near_equal(p.get_val('traj.phase0.t_duration'), 321.0, tolerance=1.0E-1) # # Get the explicitly simulated solution and plot the results # exp_out = traj.simulate() plot_results([('traj.phase0.timeseries.time', 'traj.phase0.timeseries.states:h', 'time (s)', 'altitude (m)'), ('traj.phase0.timeseries.time', 'traj.phase0.timeseries.controls:alpha', 'time (s)', 'alpha (deg)')], title='Supersonic Minimum Time-to-Climb Solution', p_sol=p, p_sim=exp_out) plt.show()
def test_brachistochrone_for_docs_gauss_lobatto(self): import openmdao.api as om from openmdao.utils.assert_utils import assert_near_equal import dymos as dm from dymos.examples.plotting import plot_results from dymos.examples.brachistochrone import BrachistochroneODE import matplotlib.pyplot as plt # # Initialize the Problem and the optimization driver # p = om.Problem(model=om.Group()) p.driver = om.ScipyOptimizeDriver() p.driver.declare_coloring() # # Create a trajectory and add a phase to it # traj = p.model.add_subsystem('traj', dm.Trajectory()) phase = traj.add_phase( 'phase0', dm.Phase(ode_class=BrachistochroneODE, transcription=dm.GaussLobatto(num_segments=10))) # # Set the variables # phase.set_time_options(fix_initial=True, duration_bounds=(.5, 10)) phase.add_state('x', fix_initial=True, fix_final=True) phase.add_state('y', fix_initial=True, fix_final=True) phase.add_state('v', fix_initial=True, fix_final=False) phase.add_control('theta', continuity=True, rate_continuity=True, units='deg', lower=0.01, upper=179.9) phase.add_parameter('g', units='m/s**2', val=9.80665) # # Minimize time at the end of the phase # phase.add_objective('time', loc='final', scaler=10) p.model.linear_solver = om.DirectSolver() # # Setup the Problem # p.setup() # # Set the initial values # p['traj.phase0.t_initial'] = 0.0 p['traj.phase0.t_duration'] = 2.0 p['traj.phase0.states:x'] = phase.interpolate(ys=[0, 10], nodes='state_input') p['traj.phase0.states:y'] = phase.interpolate(ys=[10, 5], nodes='state_input') p['traj.phase0.states:v'] = phase.interpolate(ys=[0, 9.9], nodes='state_input') p['traj.phase0.controls:theta'] = phase.interpolate( ys=[5, 100.5], nodes='control_input') # # Solve for the optimal trajectory # dm.run_problem(p) # Test the results assert_near_equal(p.get_val('traj.phase0.timeseries.time')[-1], 1.8016, tolerance=1.0E-3) # Generate the explicitly simulated trajectory exp_out = traj.simulate() plot_results( [('traj.phase0.timeseries.states:x', 'traj.phase0.timeseries.states:y', 'x (m)', 'y (m)'), ('traj.phase0.timeseries.time', 'traj.phase0.timeseries.controls:theta', 'time (s)', 'theta (deg)')], title='Brachistochrone Solution\nHigh-Order Gauss-Lobatto Method', p_sol=p, p_sim=exp_out) plt.show()
def test_double_integrator_for_docs(self): import matplotlib.pyplot as plt import openmdao.api as om from openmdao.utils.assert_utils import assert_near_equal import dymos as dm from dymos.examples.plotting import plot_results from dymos.examples.double_integrator.double_integrator_ode import DoubleIntegratorODE # Initialize the problem and assign the driver p = om.Problem(model=om.Group()) p.driver = om.pyOptSparseDriver() p.driver.options['optimizer'] = 'SLSQP' p.driver.declare_coloring() # Setup the trajectory and its phase traj = p.model.add_subsystem('traj', dm.Trajectory()) transcription = dm.Radau(num_segments=30, order=3, compressed=False) phase = traj.add_phase( 'phase0', dm.Phase(ode_class=DoubleIntegratorODE, transcription=transcription)) # # Set the options for our variables. # phase.set_time_options(fix_initial=True, fix_duration=True, units='s') phase.add_state('v', fix_initial=True, fix_final=True, rate_source='u', units='m/s') phase.add_state('x', fix_initial=True, rate_source='v', units='m') phase.add_control('u', units='m/s**2', scaler=0.01, continuity=False, rate_continuity=False, rate2_continuity=False, shape=(1, ), lower=-1.0, upper=1.0) # # Maximize distance travelled. # phase.add_objective('x', loc='final', scaler=-1) p.model.linear_solver = om.DirectSolver() # # Setup the problem and set our initial values. # p.setup(check=True) p['traj.phase0.t_initial'] = 0.0 p['traj.phase0.t_duration'] = 1.0 p.set_val('traj.phase0.states:x', phase.interp('x', ys=[0, 0.25])) p.set_val('traj.phase0.states:v', phase.interp('v', ys=[0, 0])) p.set_val('traj.phase0.controls:u', phase.interp('u', ys=[1, -1])) # # Solve the problem. # dm.run_problem(p) # # Verify that the results are correct. # x = p.get_val('traj.phase0.timeseries.states:x') v = p.get_val('traj.phase0.timeseries.states:v') assert_near_equal(x[0], 0.0, tolerance=1.0E-4) assert_near_equal(x[-1], 0.25, tolerance=1.0E-4) assert_near_equal(v[0], 0.0, tolerance=1.0E-4) assert_near_equal(v[-1], 0.0, tolerance=1.0E-4) # # Simulate the explicit solution and plot the results. # exp_out = traj.simulate() plot_results( [('traj.phase0.timeseries.time', 'traj.phase0.timeseries.states:x', 'time (s)', 'x $(m)$'), ('traj.phase0.timeseries.time', 'traj.phase0.timeseries.states:v', 'time (s)', 'v $(m/s)$'), ('traj.phase0.timeseries.time', 'traj.phase0.timeseries.controls:u', 'time (s)', 'u $(m/s^2)$')], title='Double Integrator Solution\nRadau Pseudospectral Method', p_sol=p, p_sim=exp_out) plt.show()
def test_brachistochrone_upstream_state(self): import openmdao.api as om from openmdao.utils.assert_utils import assert_near_equal import dymos as dm import matplotlib.pyplot as plt plt.switch_backend('Agg') from dymos.examples.brachistochrone.brachistochrone_ode import BrachistochroneODE # # Define the OpenMDAO problem # p = om.Problem(model=om.Group()) # Instantiate the transcription so we can get the number of nodes from it while # building the problem. tx = dm.GaussLobatto(num_segments=10, order=3) # Add an indep var comp to provide the external control values ivc = p.model.add_subsystem('states_ivc', om.IndepVarComp(), promotes_outputs=['*']) # Add the output to provide the values of theta at the control input nodes of the transcription. ivc.add_output('x0', shape=(1, ), units='m') # Connect x0 to the state error component so we can constrain the given value of x0 # to be equal to the value chosen in the phase. p.model.connect('x0', 'state_error_comp.x0_target') p.model.connect('traj.phase0.timeseries.states:x', 'state_error_comp.x0_actual', src_indices=[0], flat_src_indices=True) # # Define a Trajectory object # traj = dm.Trajectory() p.model.add_subsystem('traj', subsys=traj) p.model.add_subsystem( 'state_error_comp', om.ExecComp('x0_error = x0_target - x0_actual', x0_error={'units': 'm'}, x0_target={'units': 'm'}, x0_actual={'units': 'm'})) p.model.add_constraint('state_error_comp.x0_error', equals=0.0) # # Define a Dymos Phase object with GaussLobatto Transcription # phase = dm.Phase(ode_class=BrachistochroneODE, transcription=tx) traj.add_phase(name='phase0', phase=phase) # # Set the time options # Time has no targets in our ODE. # We fix the initial time so that the it is not a design variable in the optimization. # The duration of the phase is allowed to be optimized, but is bounded on [0.5, 10]. # phase.set_time_options(fix_initial=True, duration_bounds=(0.5, 10.0), units='s') # # Set the time options # Initial values of positions and velocity are all fixed. # The final value of position are fixed, but the final velocity is a free variable. # The equations of motion are not functions of position, so 'x' and 'y' have no targets. # The rate source points to the output in the ODE which provides the time derivative of the # given state. phase.add_state('x', fix_initial=False, fix_final=True, units='m', rate_source='xdot') phase.add_state('y', fix_initial=True, fix_final=True, units='m', rate_source='ydot') phase.add_state('v', fix_initial=True, fix_final=False, units='m/s', rate_source='vdot', targets=['v']) # Define theta as a control. # Use opt=False to allow it to be connected to an external source. # Arguments lower and upper are no longer valid for an input control. phase.add_control(name='theta', units='rad', targets=['theta']) # Minimize final time. phase.add_objective('time', loc='final') # Set the driver. p.driver = om.ScipyOptimizeDriver() # Allow OpenMDAO to automatically determine our sparsity pattern. # Doing so can significant speed up the execution of Dymos. p.driver.declare_coloring() # Setup the problem p.setup(check=True) # Now that the OpenMDAO problem is setup, we can set the values of the states. p.set_val('x0', 0.0, units='m') # Here we're intentially setting the intiial x value to something other than zero, just # to demonstrate that the optimizer brings it back in line with the value of x0 set above. p.set_val('traj.phase0.states:x', phase.interp('x', [1, 10]), units='m') p.set_val('traj.phase0.states:y', phase.interp('y', [10, 5]), units='m') p.set_val('traj.phase0.states:v', phase.interp('v', [0, 5]), units='m/s') p.set_val('traj.phase0.controls:theta', phase.interp('theta', [90, 90]), units='deg') # Run the driver to solve the problem dm.run_problem(p, make_plots=True) # Test the results assert_near_equal(p.get_val('traj.phase0.timeseries.time')[-1], 1.8016, tolerance=1.0E-3) # Check the validity of our results by using scipy.integrate.solve_ivp to # integrate the solution. sim_out = traj.simulate() # Plot the results fig, axes = plt.subplots(nrows=1, ncols=2, figsize=(12, 4.5)) axes[0].plot(p.get_val('traj.phase0.timeseries.states:x'), p.get_val('traj.phase0.timeseries.states:y'), 'ro', label='solution') axes[0].plot(sim_out.get_val('traj.phase0.timeseries.states:x'), sim_out.get_val('traj.phase0.timeseries.states:y'), 'b-', label='simulation') axes[0].set_xlabel('x (m)') axes[0].set_ylabel('y (m/s)') axes[0].legend() axes[0].grid() axes[1].plot(p.get_val('traj.phase0.timeseries.time'), p.get_val('traj.phase0.timeseries.controls:theta', units='deg'), 'ro', label='solution') axes[1].plot(sim_out.get_val('traj.phase0.timeseries.time'), sim_out.get_val('traj.phase0.timeseries.controls:theta', units='deg'), 'b-', label='simulation') axes[1].set_xlabel('time (s)') axes[1].set_ylabel(r'$\theta$ (deg)') axes[1].legend() axes[1].grid() plt.show()
def test_doc_ssto_earth(self): import matplotlib.pyplot as plt import openmdao.api as om import dymos as dm # # Setup and solve the optimal control problem # p = om.Problem(model=om.Group()) p.driver = om.pyOptSparseDriver() p.driver.declare_coloring(tol=1.0E-12) from dymos.examples.ssto.launch_vehicle_ode import LaunchVehicleODE # # Initialize our Trajectory and Phase # traj = dm.Trajectory() phase = dm.Phase(ode_class=LaunchVehicleODE, transcription=dm.GaussLobatto(num_segments=12, order=3, compressed=False)) traj.add_phase('phase0', phase) p.model.add_subsystem('traj', traj) # # Set the options for the variables # phase.set_time_options(fix_initial=True, duration_bounds=(10, 500)) phase.add_state('x', fix_initial=True, ref=1.0E5, defect_ref=10000.0, rate_source='xdot') phase.add_state('y', fix_initial=True, ref=1.0E5, defect_ref=10000.0, rate_source='ydot') phase.add_state('vx', fix_initial=True, ref=1.0E3, defect_ref=1000.0, rate_source='vxdot') phase.add_state('vy', fix_initial=True, ref=1.0E3, defect_ref=1000.0, rate_source='vydot') phase.add_state('m', fix_initial=True, ref=1.0E3, defect_ref=100.0, rate_source='mdot') phase.add_control('theta', units='rad', lower=-1.57, upper=1.57, targets=['theta']) phase.add_parameter('thrust', units='N', opt=False, val=2100000.0, targets=['thrust']) # # Set the options for our constraints and objective # phase.add_boundary_constraint('y', loc='final', equals=1.85E5, linear=True) phase.add_boundary_constraint('vx', loc='final', equals=7796.6961) phase.add_boundary_constraint('vy', loc='final', equals=0) phase.add_objective('time', loc='final', scaler=0.01) p.model.linear_solver = om.DirectSolver() # # Setup and set initial values # p.setup(check=True) p.set_val('traj.phase0.t_initial', 0.0) p.set_val('traj.phase0.t_duration', 150.0) p.set_val('traj.phase0.states:x', phase.interp('x', [0, 1.15E5])) p.set_val('traj.phase0.states:y', phase.interp('y', [0, 1.85E5])) p.set_val('traj.phase0.states:vy', phase.interp('vx', [1.0E-6, 0])) p.set_val('traj.phase0.states:m', phase.interp('vy', [117000, 1163])) p.set_val('traj.phase0.controls:theta', phase.interp('theta', [1.5, -0.76])) p.set_val('traj.phase0.parameters:thrust', 2.1, units='MN') # # Solve the Problem # dm.run_problem(p) assert_near_equal(p.get_val('traj.phase0.timeseries.time')[-1], 143, tolerance=0.05) assert_near_equal( p.get_val('traj.phase0.timeseries.states:y')[-1], 1.85E5, 1e-4) assert_near_equal( p.get_val('traj.phase0.timeseries.states:vx')[-1], 7796.6961, 1e-4) assert_near_equal( p.get_val('traj.phase0.timeseries.states:vy')[-1], 0, 1e-4) # # Get the explicitly simulated results # exp_out = traj.simulate() # # Plot the results # fig, axes = plt.subplots(nrows=2, ncols=1, figsize=(10, 8)) axes[0].plot(p.get_val('traj.phase0.timeseries.states:x'), p.get_val('traj.phase0.timeseries.states:y'), marker='o', ms=4, linestyle='None', label='solution') axes[0].plot(exp_out.get_val('traj.phase0.timeseries.states:x'), exp_out.get_val('traj.phase0.timeseries.states:y'), marker=None, linestyle='-', label='simulation') axes[0].set_xlabel('range (m)') axes[0].set_ylabel('altitude (m)') axes[0].set_aspect('equal') axes[1].plot(p.get_val('traj.phase0.timeseries.time'), p.get_val('traj.phase0.timeseries.controls:theta'), marker='o', ms=4, linestyle='None') axes[1].plot(exp_out.get_val('traj.phase0.timeseries.time'), exp_out.get_val('traj.phase0.timeseries.controls:theta'), linestyle='-', marker=None) axes[1].set_xlabel('time (s)') axes[1].set_ylabel('theta (deg)') plt.suptitle( 'Single Stage to Orbit Solution Using Linear Tangent Guidance') fig.legend(loc='lower center', ncol=2) plt.show()
def test_brachistochrone_static_gravity(self): import openmdao.api as om from openmdao.utils.assert_utils import assert_near_equal import dymos as dm import matplotlib.pyplot as plt from dymos.examples.brachistochrone.brachistochrone_ode import BrachistochroneODE # # Initialize the Problem and the optimization driver # p = om.Problem(model=om.Group()) p.driver = om.ScipyOptimizeDriver() p.driver.declare_coloring() # # Create a trajectory and add a phase to it # traj = p.model.add_subsystem('traj', dm.Trajectory()) phase = traj.add_phase( 'phase0', dm.Phase(ode_class=BrachistochroneODE, ode_init_kwargs={'static_gravity': True}, transcription=dm.GaussLobatto(num_segments=10))) # # Set the variables # phase.set_time_options(initial_bounds=(0, 0), duration_bounds=(.5, 10)) phase.add_state('x', rate_source='xdot', targets=None, units='m', fix_initial=True, fix_final=True, solve_segments=False) phase.add_state('y', rate_source='ydot', targets=None, units='m', fix_initial=True, fix_final=True, solve_segments=False) phase.add_state('v', rate_source='vdot', targets=['v'], units='m/s', fix_initial=True, fix_final=False, solve_segments=False) phase.add_control('theta', targets=['theta'], continuity=True, rate_continuity=True, units='deg', lower=0.01, upper=179.9) phase.add_parameter('g', targets=['g'], dynamic=False, opt=False) # # Minimize time at the end of the phase # phase.add_objective('time', loc='final', scaler=10) # # Setup the Problem # p.setup() # # Set the initial values # The initial time is fixed, and we set that fixed value here. # The optimizer is allowed to modify t_duration, but an initial guess is provided here. # p.set_val('traj.phase0.t_initial', 0.0) p.set_val('traj.phase0.t_duration', 2.0) # Guesses for states are provided at all state_input nodes. # We use the phase.interpolate method to linearly interpolate values onto the state input nodes. # Since fix_initial=True for all states and fix_final=True for x and y, the initial or final # values of the interpolation provided here will not be changed by the optimizer. p.set_val('traj.phase0.states:x', phase.interpolate(ys=[0, 10], nodes='state_input')) p.set_val('traj.phase0.states:y', phase.interpolate(ys=[10, 5], nodes='state_input')) p.set_val('traj.phase0.states:v', phase.interpolate(ys=[0, 9.9], nodes='state_input')) # Guesses for controls are provided at all control_input node. # Here phase.interpolate is used to linearly interpolate values onto the control input nodes. p.set_val('traj.phase0.controls:theta', phase.interpolate(ys=[5, 100.5], nodes='control_input')) # Set the value for gravitational acceleration. p.set_val('traj.phase0.parameters:g', 9.80665) # # Solve for the optimal trajectory # dm.run_problem(p, simulate=True) # Test the results assert_near_equal(p.get_val('traj.phase0.timeseries.time')[-1], 1.8016, tolerance=1.0E-3) # Generate the explicitly simulated trajectory exp_out = om.CaseReader('dymos_simulation.db').get_case('final') # Extract the timeseries from the implicit solution and the explicit simulation x = p.get_val('traj.phase0.timeseries.states:x') y = p.get_val('traj.phase0.timeseries.states:y') t = p.get_val('traj.phase0.timeseries.time') theta = p.get_val('traj.phase0.timeseries.controls:theta') x_exp = exp_out.get_val('traj.phase0.timeseries.states:x') y_exp = exp_out.get_val('traj.phase0.timeseries.states:y') t_exp = exp_out.get_val('traj.phase0.timeseries.time') theta_exp = exp_out.get_val('traj.phase0.timeseries.controls:theta') fig, axes = plt.subplots(nrows=2, ncols=1) axes[0].plot(x, y, 'o') axes[0].plot(x_exp, y_exp, '-') axes[0].set_xlabel('x (m)') axes[0].set_ylabel('y (m)') axes[1].plot(t, theta, 'o') axes[1].plot(t_exp, theta_exp, '-') axes[1].set_xlabel('time (s)') axes[1].set_ylabel(r'$\theta$ (deg)') plt.show()
def test_finite_burn_orbit_raise(self): import numpy as np import matplotlib.pyplot as plt import openmdao.api as om from openmdao.utils.assert_utils import assert_near_equal import dymos as dm from dymos.examples.finite_burn_orbit_raise.finite_burn_eom import FiniteBurnODE p = om.Problem(model=om.Group()) p.driver = om.pyOptSparseDriver() p.driver.options['optimizer'] = 'IPOPT' p.driver.declare_coloring() traj = dm.Trajectory() traj.add_parameter('c', opt=False, val=1.5, units='DU/TU', targets={'burn1': ['c'], 'coast': ['c'], 'burn2': ['c']}) # First Phase (burn) burn1 = dm.Phase(ode_class=FiniteBurnODE, transcription=dm.GaussLobatto(num_segments=5, order=3, compressed=False)) burn1 = traj.add_phase('burn1', burn1) burn1.set_time_options(fix_initial=True, duration_bounds=(.5, 10), units='TU') burn1.add_state('r', fix_initial=True, fix_final=False, defect_scaler=100.0, rate_source='r_dot', units='DU') burn1.add_state('theta', fix_initial=True, fix_final=False, defect_scaler=100.0, rate_source='theta_dot', units='rad') burn1.add_state('vr', fix_initial=True, fix_final=False, defect_scaler=100.0, rate_source='vr_dot', units='DU/TU') burn1.add_state('vt', fix_initial=True, fix_final=False, defect_scaler=100.0, rate_source='vt_dot', units='DU/TU') burn1.add_state('accel', fix_initial=True, fix_final=False, rate_source='at_dot', units='DU/TU**2') burn1.add_state('deltav', fix_initial=True, fix_final=False, rate_source='deltav_dot', units='DU/TU') burn1.add_control('u1', rate_continuity=True, rate2_continuity=True, units='deg', scaler=0.01, rate_continuity_scaler=0.001, rate2_continuity_scaler=0.001, lower=-30, upper=30) # Second Phase (Coast) coast = dm.Phase(ode_class=FiniteBurnODE, transcription=dm.GaussLobatto(num_segments=5, order=3, compressed=False)) coast.set_time_options(initial_bounds=(0.5, 20), duration_bounds=(.5, 50), duration_ref=50, units='TU') coast.add_state('r', fix_initial=False, fix_final=False, defect_scaler=100.0, rate_source='r_dot', targets=['r'], units='DU') coast.add_state('theta', fix_initial=False, fix_final=False, defect_scaler=100.0, rate_source='theta_dot', targets=['theta'], units='rad') coast.add_state('vr', fix_initial=False, fix_final=False, defect_scaler=100.0, rate_source='vr_dot', targets=['vr'], units='DU/TU') coast.add_state('vt', fix_initial=False, fix_final=False, defect_scaler=100.0, rate_source='vt_dot', targets=['vt'], units='DU/TU') coast.add_state('accel', fix_initial=True, fix_final=True, rate_source='at_dot', targets=['accel'], units='DU/TU**2') coast.add_state('deltav', fix_initial=False, fix_final=False, rate_source='deltav_dot', units='DU/TU') coast.add_parameter('u1', opt=False, val=0.0, units='deg', targets=['u1']) # Third Phase (burn) burn2 = dm.Phase(ode_class=FiniteBurnODE, transcription=dm.GaussLobatto(num_segments=5, order=3, compressed=False)) traj.add_phase('coast', coast) traj.add_phase('burn2', burn2) burn2.set_time_options(initial_bounds=(0.5, 50), duration_bounds=(.5, 10), initial_ref=10, units='TU') burn2.add_state('r', fix_initial=False, fix_final=True, defect_scaler=100.0, rate_source='r_dot', units='DU') burn2.add_state('theta', fix_initial=False, fix_final=False, defect_scaler=100.0, rate_source='theta_dot', units='rad') burn2.add_state('vr', fix_initial=False, fix_final=True, defect_scaler=1000.0, rate_source='vr_dot', units='DU/TU') burn2.add_state('vt', fix_initial=False, fix_final=True, defect_scaler=1000.0, rate_source='vt_dot', units='DU/TU') burn2.add_state('accel', fix_initial=False, fix_final=False, defect_scaler=1.0, rate_source='at_dot', units='DU/TU**2') burn2.add_state('deltav', fix_initial=False, fix_final=False, defect_scaler=1.0, rate_source='deltav_dot', units='DU/TU') burn2.add_objective('deltav', loc='final', scaler=100.0) burn2.add_control('u1', rate_continuity=True, rate2_continuity=True, units='deg', scaler=0.01, lower=-90, upper=90) burn1.add_timeseries_output('pos_x') coast.add_timeseries_output('pos_x') burn2.add_timeseries_output('pos_x') burn1.add_timeseries_output('pos_y') coast.add_timeseries_output('pos_y') burn2.add_timeseries_output('pos_y') # Link Phases traj.link_phases(phases=['burn1', 'coast', 'burn2'], vars=['time', 'r', 'theta', 'vr', 'vt', 'deltav']) traj.link_phases(phases=['burn1', 'burn2'], vars=['accel']) p.model.add_subsystem('traj', subsys=traj) # Finish Problem Setup # Needed to move the direct solver down into the phases for use with MPI. # - After moving down, used fewer iterations (about 30 less) p.driver.add_recorder(om.SqliteRecorder('two_burn_orbit_raise_example.db')) p.setup(check=True, mode='fwd') # Set Initial Guesses p.set_val('traj.parameters:c', value=1.5, units='DU/TU') burn1 = p.model.traj.phases.burn1 burn2 = p.model.traj.phases.burn2 coast = p.model.traj.phases.coast p.set_val('traj.burn1.t_initial', value=0.0) p.set_val('traj.burn1.t_duration', value=2.25) p.set_val('traj.burn1.states:r', value=burn1.interp('r', [1, 1.5])) p.set_val('traj.burn1.states:theta', value=burn1.interp('theta', [0, 1.7])) p.set_val('traj.burn1.states:vr', value=burn1.interp('vr', [0, 0])) p.set_val('traj.burn1.states:vt', value=burn1.interp('vt', [1, 1])) p.set_val('traj.burn1.states:accel', value=burn1.interp('accel', [0.1, 0])) p.set_val('traj.burn1.states:deltav', value=burn1.interp('deltav', [0, 0.1])) p.set_val('traj.burn1.controls:u1', value=burn1.interp('u1', [-3.5, 13.0])) p.set_val('traj.coast.t_initial', value=2.25) p.set_val('traj.coast.t_duration', value=3.0) p.set_val('traj.coast.states:r', value=coast.interp('r', [1.3, 1.5])) p.set_val('traj.coast.states:theta', value=coast.interp('theta', [2.1767, 1.7])) p.set_val('traj.coast.states:vr', value=coast.interp('vr', [0.3285, 0])) p.set_val('traj.coast.states:vt', value=coast.interp('vt', [0.97, 1])) p.set_val('traj.coast.states:accel', value=coast.interp('accel', [0, 0])) p.set_val('traj.burn2.t_initial', value=5.25) p.set_val('traj.burn2.t_duration', value=1.75) p.set_val('traj.burn2.states:r', value=burn2.interp('r', [1, 3.])) p.set_val('traj.burn2.states:theta', value=burn2.interp('theta', [0, 4.0])) p.set_val('traj.burn2.states:vr', value=burn2.interp('vr', [0, 0])) p.set_val('traj.burn2.states:vt', value=burn2.interp('vt', [1, np.sqrt(1 / 3.)])) p.set_val('traj.burn2.states:deltav', value=burn2.interp('deltav', [0.1, 0.2])) p.set_val('traj.burn2.states:accel', value=burn2.interp('accel', [0.1, 0])) p.set_val('traj.burn2.controls:u1', value=burn2.interp('u1', [0, 0])) dm.run_problem(p) assert_near_equal(p.get_val('traj.burn2.states:deltav')[-1], 0.3995, tolerance=2.0E-3) # # Plot results # traj = p.model.traj exp_out = traj.simulate() fig = plt.figure(figsize=(8, 4)) fig.suptitle('Two Burn Orbit Raise Solution') ax_u1 = plt.subplot2grid((2, 2), (0, 0)) ax_deltav = plt.subplot2grid((2, 2), (1, 0)) ax_xy = plt.subplot2grid((2, 2), (0, 1), rowspan=2) span = np.linspace(0, 2 * np.pi, 100) ax_xy.plot(np.cos(span), np.sin(span), 'k--', lw=1) ax_xy.plot(3 * np.cos(span), 3 * np.sin(span), 'k--', lw=1) ax_xy.set_xlim(-4.5, 4.5) ax_xy.set_ylim(-4.5, 4.5) ax_xy.set_xlabel('x ($R_e$)') ax_xy.set_ylabel('y ($R_e$)') ax_u1.set_xlabel('time ($TU$)') ax_u1.set_ylabel('$u_1$ ($deg$)') ax_u1.grid(True) ax_deltav.set_xlabel('time ($TU$)') ax_deltav.set_ylabel('${\Delta}v$ ($DU/TU$)') ax_deltav.grid(True) t_sol = dict((phs, p.get_val('traj.{0}.timeseries.time'.format(phs))) for phs in ['burn1', 'coast', 'burn2']) x_sol = dict((phs, p.get_val('traj.{0}.timeseries.pos_x'.format(phs))) for phs in ['burn1', 'coast', 'burn2']) y_sol = dict((phs, p.get_val('traj.{0}.timeseries.pos_y'.format(phs))) for phs in ['burn1', 'coast', 'burn2']) dv_sol = dict((phs, p.get_val('traj.{0}.timeseries.states:deltav'.format(phs))) for phs in ['burn1', 'coast', 'burn2']) u1_sol = dict((phs, p.get_val('traj.{0}.timeseries.controls:u1'.format(phs), units='deg')) for phs in ['burn1', 'burn2']) t_exp = dict((phs, exp_out.get_val('traj.{0}.timeseries.time'.format(phs))) for phs in ['burn1', 'coast', 'burn2']) x_exp = dict((phs, exp_out.get_val('traj.{0}.timeseries.pos_x'.format(phs))) for phs in ['burn1', 'coast', 'burn2']) y_exp = dict((phs, exp_out.get_val('traj.{0}.timeseries.pos_y'.format(phs))) for phs in ['burn1', 'coast', 'burn2']) dv_exp = dict((phs, exp_out.get_val('traj.{0}.timeseries.states:deltav'.format(phs))) for phs in ['burn1', 'coast', 'burn2']) u1_exp = dict((phs, exp_out.get_val('traj.{0}.timeseries.controls:u1'.format(phs), units='deg')) for phs in ['burn1', 'burn2']) for phs in ['burn1', 'coast', 'burn2']: try: ax_u1.plot(t_exp[phs], u1_exp[phs], '-', marker=None, color='C0') ax_u1.plot(t_sol[phs], u1_sol[phs], 'o', mfc='C1', mec='C1', ms=3) except KeyError: pass ax_deltav.plot(t_exp[phs], dv_exp[phs], '-', marker=None, color='C0') ax_deltav.plot(t_sol[phs], dv_sol[phs], 'o', mfc='C1', mec='C1', ms=3) ax_xy.plot(x_exp[phs], y_exp[phs], '-', marker=None, color='C0', label='explicit') ax_xy.plot(x_sol[phs], y_sol[phs], 'o', mfc='C1', mec='C1', ms=3, label='implicit') plt.show()
def test_tandem_phases_for_docs(self): p = om.Problem(model=om.Group()) p.driver = om.pyOptSparseDriver() p.driver.options['optimizer'] = 'SLSQP' p.driver.declare_coloring() # # First Phase: Standard Brachistochrone # num_segments = 10 transcription_order = 3 compressed = False tx0 = dm.GaussLobatto(num_segments=num_segments, order=transcription_order, compressed=compressed) tx1 = dm.Radau(num_segments=num_segments*2, order=transcription_order*3, compressed=compressed) phase0 = dm.Phase(ode_class=BrachistochroneODE, transcription=tx0) p.model.add_subsystem('phase0', phase0) phase0.set_time_options(fix_initial=True, duration_bounds=(.5, 10)) phase0.add_state('x', rate_source=BrachistochroneODE.states['x']['rate_source'], units=BrachistochroneODE.states['x']['units'], fix_initial=True, fix_final=False, solve_segments=False) phase0.add_state('y', rate_source=BrachistochroneODE.states['y']['rate_source'], units=BrachistochroneODE.states['y']['units'], fix_initial=True, fix_final=False, solve_segments=False) phase0.add_state('v', rate_source=BrachistochroneODE.states['v']['rate_source'], targets=BrachistochroneODE.states['v']['targets'], units=BrachistochroneODE.states['v']['units'], fix_initial=True, fix_final=False, solve_segments=False) phase0.add_control('theta', continuity=True, rate_continuity=True, targets=BrachistochroneODE.parameters['theta']['targets'], units='deg', lower=0.01, upper=179.9) phase0.add_input_parameter('g', targets=BrachistochroneODE.parameters['g']['targets'], units='m/s**2', val=9.80665) phase0.add_boundary_constraint('x', loc='final', equals=10) phase0.add_boundary_constraint('y', loc='final', equals=5) # Add alternative timeseries output to provide control inputs for the next phase phase0.add_timeseries('timeseries2', transcription=tx1, subset='control_input') # # Second Phase: Integration of ArcLength # phase1 = dm.Phase(ode_class=BrachistochroneArclengthODE, transcription=tx1) p.model.add_subsystem('phase1', phase1) phase1.set_time_options(fix_initial=True, input_duration=True) phase1.add_state('S', fix_initial=True, fix_final=False, rate_source='Sdot', units='m') phase1.add_control('theta', opt=False, units='deg', targets='theta') phase1.add_control('v', opt=False, units='m/s', targets='v') # # Connect the two phases # p.model.connect('phase0.t_duration', 'phase1.t_duration') p.model.connect('phase0.timeseries2.controls:theta', 'phase1.controls:theta') p.model.connect('phase0.timeseries2.states:v', 'phase1.controls:v') # Minimize time at the end of the phase # phase1.add_objective('time', loc='final', scaler=1) # phase1.add_boundary_constraint('S', loc='final', upper=12) phase1.add_objective('S', loc='final', ref=1) p.model.linear_solver = om.DirectSolver() p.setup(check=True) p['phase0.t_initial'] = 0.0 p['phase0.t_duration'] = 2.0 p['phase0.states:x'] = phase0.interpolate(ys=[0, 10], nodes='state_input') p['phase0.states:y'] = phase0.interpolate(ys=[10, 5], nodes='state_input') p['phase0.states:v'] = phase0.interpolate(ys=[0, 9.9], nodes='state_input') p['phase0.controls:theta'] = phase0.interpolate(ys=[5, 100], nodes='control_input') p['phase0.input_parameters:g'] = 9.80665 p['phase1.states:S'] = 0.0 dm.run_problem(p) expected = np.sqrt((10-0)**2 + (10 - 5)**2) assert_near_equal(p['phase1.timeseries.states:S'][-1], expected, tolerance=1.0E-3) fig, (ax0, ax1) = plt.subplots(2, 1) fig.tight_layout() ax0.plot(p.get_val('phase0.timeseries.states:x'), p.get_val('phase0.timeseries.states:y'), '.') ax0.set_xlabel('x (m)') ax0.set_ylabel('y (m)') ax1.plot(p.get_val('phase1.timeseries.time'), p.get_val('phase1.timeseries.states:S'), '+') ax1.set_xlabel('t (s)') ax1.set_ylabel('S (m)') plt.show()
def test_reentry(self): import openmdao.api as om from openmdao.utils.assert_utils import assert_near_equal import dymos as dm from dymos.examples.shuttle_reentry.shuttle_ode import ShuttleODE from dymos.examples.plotting import plot_results # Instantiate the problem, add the driver, and allow it to use coloring p = om.Problem(model=om.Group()) p.driver = om.pyOptSparseDriver() p.driver.declare_coloring() p.driver.options['optimizer'] = 'SLSQP' # Instantiate the trajectory and add a phase to it traj = p.model.add_subsystem('traj', dm.Trajectory()) phase0 = traj.add_phase( 'phase0', dm.Phase(ode_class=ShuttleODE, transcription=dm.Radau(num_segments=15, order=3))) phase0.set_time_options(fix_initial=True, units='s', duration_ref=200) phase0.add_state('h', fix_initial=True, fix_final=True, units='ft', rate_source='hdot', lower=0, ref0=75000, ref=300000, defect_ref=1000) phase0.add_state('gamma', fix_initial=True, fix_final=True, units='rad', rate_source='gammadot', lower=-89. * np.pi / 180, upper=89. * np.pi / 180) phase0.add_state('phi', fix_initial=True, fix_final=False, units='rad', rate_source='phidot', lower=0, upper=89. * np.pi / 180) phase0.add_state('psi', fix_initial=True, fix_final=False, units='rad', rate_source='psidot', lower=0, upper=90. * np.pi / 180) phase0.add_state('theta', fix_initial=True, fix_final=False, units='rad', rate_source='thetadot', lower=-89. * np.pi / 180, upper=89. * np.pi / 180) phase0.add_state('v', fix_initial=True, fix_final=True, units='ft/s', rate_source='vdot', lower=0, ref0=2500, ref=25000) phase0.add_control( 'alpha', units='rad', opt=True, lower=-np.pi / 2, upper=np.pi / 2, ) phase0.add_control( 'beta', units='rad', opt=True, lower=-89 * np.pi / 180, upper=1 * np.pi / 180, ) # The original implementation by Betts includes a heating rate path constraint. # This will work with the SNOPT optimizer but SLSQP has difficulty converging the solution. # phase0.add_path_constraint('q', lower=0, upper=70, units='Btu/ft**2/s', ref=70) phase0.add_timeseries_output('q', shape=(1, ), units='Btu/ft**2/s') phase0.add_objective('theta', loc='final', ref=-0.01) p.setup(check=True) p.set_val('traj.phase0.states:h', phase0.interpolate(ys=[260000, 80000], nodes='state_input'), units='ft') p.set_val('traj.phase0.states:gamma', phase0.interpolate(ys=[-1, -5], nodes='state_input'), units='deg') p.set_val('traj.phase0.states:phi', phase0.interpolate(ys=[0, 75], nodes='state_input'), units='deg') p.set_val('traj.phase0.states:psi', phase0.interpolate(ys=[90, 10], nodes='state_input'), units='deg') p.set_val('traj.phase0.states:theta', phase0.interpolate(ys=[0, 25], nodes='state_input'), units='deg') p.set_val('traj.phase0.states:v', phase0.interpolate(ys=[25600, 2500], nodes='state_input'), units='ft/s') p.set_val('traj.phase0.t_initial', 0, units='s') p.set_val('traj.phase0.t_duration', 2000, units='s') p.set_val('traj.phase0.controls:alpha', phase0.interpolate(ys=[17.4, 17.4], nodes='control_input'), units='deg') p.set_val('traj.phase0.controls:beta', phase0.interpolate(ys=[-75, 0], nodes='control_input'), units='deg') # Run the driver dm.run_problem(p) # Check the validity of the solution assert_near_equal(p.get_val('traj.phase0.timeseries.time')[-1], 2008.59, tolerance=1e-3) assert_near_equal(p.get_val('traj.phase0.timeseries.states:theta', units='deg')[-1], 34.1412, tolerance=1e-3) # assert_near_equal(p.get_val('traj.phase0.timeseries.time')[-1], 2181.90371131, tolerance=1e-3) # assert_near_equal(p.get_val('traj.phase0.timeseries.states:theta')[-1], .53440626, tolerance=1e-3) # Run the simulation to check if the model is physically valid sim_out = traj.simulate() # Plot the results plot_results([ ('traj.phase0.timeseries.time', 'traj.phase0.timeseries.controls:alpha', 'time (s)', 'alpha (rad)'), ('traj.phase0.timeseries.time', 'traj.phase0.timeseries.controls:beta', 'time (s)', 'beta (rad)'), ('traj.phase0.timeseries.time', 'traj.phase0.timeseries.states:theta', 'time (s)', 'theta (rad)'), ('traj.phase0.timeseries.time', 'traj.phase0.timeseries.q', 'time (s)', 'q (btu/ft/ft/s') ], title='Reentry Solution', p_sol=p, p_sim=sim_out) plt.show()
def test_brachistochrone_tandem_phases(self): from dymos.examples.brachistochrone.brachistochrone_ode import BrachistochroneODE import numpy as np import matplotlib.pyplot as plt plt.switch_backend('Agg') import openmdao.api as om import dymos as dm from openmdao.utils.assert_utils import assert_near_equal p = om.Problem(model=om.Group()) p.driver = om.pyOptSparseDriver() p.driver.options['optimizer'] = 'SLSQP' p.driver.declare_coloring() # The transcription of the first phase tx0 = dm.GaussLobatto(num_segments=10, order=3, compressed=False) # The transcription for the second phase (and the secondary timeseries outputs from the first phase) tx1 = dm.Radau(num_segments=20, order=9, compressed=False) # # First Phase: Integrate the standard brachistochrone ODE # phase0 = dm.Phase(ode_class=BrachistochroneODE, transcription=tx0) p.model.add_subsystem('phase0', phase0) phase0.set_time_options(fix_initial=True, duration_bounds=(.5, 10)) phase0.add_state('x', fix_initial=True, fix_final=False) phase0.add_state('y', fix_initial=True, fix_final=False) phase0.add_state('v', fix_initial=True, fix_final=False) phase0.add_control('theta', continuity=True, rate_continuity=True, units='deg', lower=0.01, upper=179.9) phase0.add_parameter('g', units='m/s**2', val=9.80665) phase0.add_boundary_constraint('x', loc='final', equals=10) phase0.add_boundary_constraint('y', loc='final', equals=5) # Add alternative timeseries output to provide control inputs for the next phase phase0.add_timeseries('timeseries2', transcription=tx1, subset='control_input') # # Second Phase: Integration of ArcLength # phase1 = dm.Phase(ode_class=BrachistochroneArclengthODE, transcription=tx1) p.model.add_subsystem('phase1', phase1) phase1.set_time_options(fix_initial=True, input_duration=True) phase1.add_state('S', fix_initial=True, fix_final=False, rate_source='Sdot', units='m') phase1.add_control('theta', opt=False, units='deg', targets='theta') phase1.add_control('v', opt=False, units='m/s', targets='v') # # Connect the two phases # p.model.connect('phase0.t_duration', 'phase1.t_duration') p.model.connect('phase0.timeseries2.controls:theta', 'phase1.controls:theta') p.model.connect('phase0.timeseries2.states:v', 'phase1.controls:v') # Minimize arclength at the end of the second phase phase1.add_objective('S', loc='final', ref=1) p.model.linear_solver = om.DirectSolver() p.setup(check=True) p['phase0.t_initial'] = 0.0 p['phase0.t_duration'] = 2.0 p['phase0.states:x'] = phase0.interpolate(ys=[0, 10], nodes='state_input') p['phase0.states:y'] = phase0.interpolate(ys=[10, 5], nodes='state_input') p['phase0.states:v'] = phase0.interpolate(ys=[0, 9.9], nodes='state_input') p['phase0.controls:theta'] = phase0.interpolate(ys=[5, 100], nodes='control_input') p['phase0.parameters:g'] = 9.80665 p['phase1.states:S'] = 0.0 dm.run_problem(p) expected = np.sqrt((10 - 0)**2 + (10 - 5)**2) assert_near_equal(p.get_val('phase1.timeseries.states:S')[-1], expected, tolerance=1.0E-3) fig, (ax0, ax1) = plt.subplots(2, 1) fig.tight_layout() ax0.plot(p.get_val('phase0.timeseries.states:x'), p.get_val('phase0.timeseries.states:y'), '.') ax0.set_xlabel('x (m)') ax0.set_ylabel('y (m)') ax1.plot(p.get_val('phase1.timeseries.time'), p.get_val('phase1.timeseries.states:S'), '+') ax1.set_xlabel('t (s)') ax1.set_ylabel('S (m)') plt.show()
def test_steady_aircraft_for_docs(self): import matplotlib.pyplot as plt import openmdao.api as om from openmdao.utils.assert_utils import assert_near_equal import dymos as dm from dymos.examples.aircraft_steady_flight.aircraft_ode import AircraftODE from dymos.examples.plotting import plot_results from dymos.utils.lgl import lgl p = om.Problem(model=om.Group()) p.driver = om.pyOptSparseDriver() p.driver.options['optimizer'] = 'SLSQP' p.driver.declare_coloring() num_seg = 15 seg_ends, _ = lgl(num_seg + 1) traj = p.model.add_subsystem('traj', dm.Trajectory()) phase = traj.add_phase( 'phase0', dm.Phase(ode_class=AircraftODE, transcription=dm.Radau(num_segments=num_seg, segment_ends=seg_ends, order=3, compressed=False))) # Pass Reference Area from an external source assumptions = p.model.add_subsystem('assumptions', om.IndepVarComp()) assumptions.add_output('S', val=427.8, units='m**2') assumptions.add_output('mass_empty', val=1.0, units='kg') assumptions.add_output('mass_payload', val=1.0, units='kg') phase.set_time_options(fix_initial=True, duration_bounds=(300, 10000), duration_ref=5600) phase.add_state('range', units='NM', rate_source='range_rate_comp.dXdt:range', fix_initial=True, fix_final=False, ref=1e-3, defect_ref=1e-3, lower=0, upper=2000) phase.add_state('mass_fuel', units='lbm', rate_source='propulsion.dXdt:mass_fuel', fix_initial=True, fix_final=True, upper=1.5E5, lower=0.0, ref=1e2, defect_ref=1e2) phase.add_state('alt', units='kft', rate_source='climb_rate', fix_initial=True, fix_final=True, lower=0.0, upper=60, ref=1e-3, defect_ref=1e-3) phase.add_control('climb_rate', units='ft/min', opt=True, lower=-3000, upper=3000, targets=['gam_comp.climb_rate'], rate_continuity=True, rate2_continuity=False) phase.add_control('mach', targets=['tas_comp.mach', 'aero.mach'], units=None, opt=False) phase.add_parameter( 'S', targets=['aero.S', 'flight_equilibrium.S', 'propulsion.S'], units='m**2') phase.add_parameter('mass_empty', targets=['mass_comp.mass_empty'], units='kg') phase.add_parameter('mass_payload', targets=['mass_comp.mass_payload'], units='kg') phase.add_path_constraint('propulsion.tau', lower=0.01, upper=2.0) p.model.connect('assumptions.S', 'traj.phase0.parameters:S') p.model.connect('assumptions.mass_empty', 'traj.phase0.parameters:mass_empty') p.model.connect('assumptions.mass_payload', 'traj.phase0.parameters:mass_payload') phase.add_objective('range', loc='final', ref=-1.0e-4) phase.add_timeseries_output('aero.CL') phase.add_timeseries_output('aero.CD') p.setup() p['traj.phase0.t_initial'] = 0.0 p['traj.phase0.t_duration'] = 3600.0 p['traj.phase0.states:range'][:] = phase.interpolate( ys=(0, 724.0), nodes='state_input') p['traj.phase0.states:mass_fuel'][:] = phase.interpolate( ys=(30000, 1e-3), nodes='state_input') p['traj.phase0.states:alt'][:] = 10.0 p['traj.phase0.controls:mach'][:] = 0.8 p['assumptions.S'] = 427.8 p['assumptions.mass_empty'] = 0.15E6 p['assumptions.mass_payload'] = 84.02869 * 400 dm.run_problem(p) assert_near_equal(p.get_val('traj.phase0.timeseries.states:range', units='NM')[-1], 726.85, tolerance=1.0E-2) exp_out = traj.simulate() assert_near_equal(p.get_val('traj.phase0.timeseries.CL')[0], exp_out.get_val('traj.phase0.timeseries.CL')[0], tolerance=1.0E-9) assert_near_equal(p.get_val('traj.phase0.timeseries.CD')[0], exp_out.get_val('traj.phase0.timeseries.CD')[0], tolerance=1.0E-9) plot_results( [('traj.phase0.timeseries.states:range', 'traj.phase0.timeseries.states:alt', 'range (NM)', 'altitude (kft)'), ('traj.phase0.timeseries.time', 'traj.phase0.timeseries.states:mass_fuel', 'time (s)', 'fuel mass (lbm)'), ('traj.phase0.timeseries.time', 'traj.phase0.timeseries.CL', 'time (s)', 'lift coefficient'), ('traj.phase0.timeseries.time', 'traj.phase0.timeseries.CD', 'time (s)', 'drag coefficient')], title='Commercial Aircraft Optimization', p_sol=p, p_sim=exp_out) plt.show()
def test_vanderpol_simulate_true(self): # simulate true p = vanderpol(transcription='radau-ps', num_segments=30, transcription_order=3, compressed=True, optimizer='SLSQP', delay=0.005, distrib=True, use_pyoptsparse=True) dm.run_problem(p, run_driver=True, simulate=True)