def test_two_phase_cannonball_for_docs(self): import openmdao.api as om from openmdao.utils.assert_utils import assert_rel_error import dymos as dm from dymos.examples.cannonball.cannonball_ode import CannonballODE 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=CannonballODE, 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', units='m', rate_source='eom.r_dot', fix_initial=True, fix_final=False) ascent.add_state('h', units='m', rate_source='eom.h_dot', targets=['atmos.h'], fix_initial=True, fix_final=False) ascent.add_state('gam', units='rad', rate_source='eom.gam_dot', targets=['eom.gam'], fix_initial=False, fix_final=True) ascent.add_state( 'v', units='m/s', rate_source='eom.v_dot', targets=['dynamic_pressure.v', 'eom.v', 'kinetic_energy.v'], fix_initial=False, fix_final=False) # 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 = dm.Phase(ode_class=CannonballODE, 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='eom.r_dot', fix_initial=False, fix_final=False) descent.add_state('h', units='m', rate_source='eom.h_dot', targets=['atmos.h'], fix_initial=False, fix_final=True) descent.add_state('gam', units='rad', rate_source='eom.gam_dot', targets=['eom.gam'], fix_initial=False, fix_final=False) descent.add_state( 'v', units='m/s', rate_source='eom.v_dot', targets=['dynamic_pressure.v', 'eom.v', 'kinetic_energy.v'], fix_initial=False, fix_final=False) descent.add_objective('r', loc='final', scaler=-1.0) # Add internally-managed design parameters to the trajectory. traj.add_design_parameter('CD', custom_targets={ 'ascent': ['aero.CD'], 'descent': ['aero.CD'] }, val=0.5, units=None, opt=False) traj.add_design_parameter('CL', custom_targets={ 'ascent': ['aero.CL'], 'descent': ['aero.CL'] }, val=0.0, units=None, opt=False) traj.add_design_parameter('T', custom_targets={ 'ascent': ['eom.T'], 'descent': ['eom.T'] }, val=0.0, units='N', opt=False) traj.add_design_parameter('alpha', custom_targets={ 'ascent': ['eom.alpha'], 'descent': ['eom.alpha'] }, val=0.0, units='deg', opt=False) # Add externally-provided design parameters to the trajectory. traj.add_input_parameter('mass', units='kg', custom_targets={ 'ascent': ['eom.m', 'kinetic_energy.m'], 'descent': ['eom.m', 'kinetic_energy.m'] }, val=1.0) traj.add_input_parameter('S', units='m**2', custom_targets={ 'ascent': ['aero.S'], 'descent': ['aero.S'] }, 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.input_parameters:mass') p.model.connect('size_comp.S', 'traj.input_parameters:S') # Finish Problem Setup p.model.linear_solver = om.DirectSolver() p.driver.add_recorder(om.SqliteRecorder('ex_two_phase_cannonball.db')) p.setup(check=True) # 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.design_parameters:CD', 0.5) p.set_val('traj.design_parameters:CL', 0.0) p.set_val('traj.design_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') p.run_driver() assert_rel_error(self, 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.traj_parameters:{0}'.format( param)), 'descent': p.get_val('traj.descent.timeseries.traj_parameters:{0}'.format( param)) } p_exp = { 'ascent': exp_out.get_val('traj.ascent.timeseries.' 'traj_parameters:{0}'.format(param)), 'descent': exp_out.get_val('traj.descent.timeseries.' 'traj_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()
def test_two_phase_cannonball_ode_output_linkage(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_ode import CannonballODE p = om.Problem(model=om.Group()) p.driver = om.pyOptSparseDriver() p.driver.options['optimizer'] = 'SLSQP' p.driver.declare_coloring() p.model.add_subsystem('size_comp', CannonballSizeComp(), promotes_inputs=['radius', 'dens']) p.model.set_input_defaults('dens', val=7.87, units='g/cm**3') p.model.add_design_var('radius', lower=0.01, upper=0.10, ref0=0.01, ref=0.10, units='m') traj = p.model.add_subsystem('traj', dm.Trajectory()) transcription = dm.Radau(num_segments=5, order=3, compressed=True) ascent = dm.Phase(ode_class=CannonballODE, 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, units='m', rate_source='r_dot') 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) # 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=CannonballODE, 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', fix_initial=False, fix_final=True, units='m', rate_source='h_dot') descent.add_state('gam', fix_initial=False, fix_final=False, units='rad', rate_source='gam_dot') descent.add_state('v', fix_initial=False, fix_final=False, units='m/s', rate_source='v_dot') 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_objective('r', loc='final', scaler=-1.0) # Add internally-managed design parameters to the trajectory. traj.add_parameter('CD', targets={ 'ascent': ['CD'], 'descent': ['CD'] }, val=0.5, units=None, opt=False, static_target=True) # 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) # Note velocity is not included here. Doing so is equivalent to linking kinetic energy, # and causes a duplicate row in the constraint jacobian. traj.link_phases(phases=['ascent', 'descent'], vars=['time', 'r', 'h', 'gam'], connected=True) traj.add_linkage_constraint('ascent', 'descent', 'ke', 'ke', ref=100000, connected=False) p.model.connect('size_comp.mass', 'traj.parameters:m') p.model.connect('size_comp.S', 'traj.parameters:S') # Finish Problem Setup p.model.linear_solver = om.DirectSolver() p.driver.add_recorder(om.SqliteRecorder('ex_two_phase_cannonball.db')) p.setup() # Set Initial Guesses p.set_val('radius', 0.05, units='m') p.set_val('dens', 7.87, units='g/cm**3') p.set_val('traj.parameters: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) 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('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 = ['CD', 'mass', 'S'] fig, axes = plt.subplots(nrows=3, 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()
def test_traj_param_target_none(self): # Tests a bug where you couldn't specify None as a target for a specific 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 from dymos.examples.cannonball.cannonball_ode import CannonballODE p = om.Problem(model=om.Group()) p.driver = om.pyOptSparseDriver() p.driver.options['optimizer'] = 'SLSQP' p.driver.declare_coloring() p.model.add_subsystem('size_comp', CannonballSizeComp(), promotes_inputs=['radius', 'dens']) p.model.set_input_defaults('dens', val=7.87, units='g/cm**3') p.model.add_design_var('radius', lower=0.01, upper=0.10, ref0=0.01, ref=0.10, units='m') traj = p.model.add_subsystem('traj', dm.Trajectory()) transcription = dm.Radau(num_segments=5, order=3, compressed=True) ascent = dm.Phase(ode_class=CannonballODE, 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, units='m', rate_source='r_dot') 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) # Limit the muzzle energy ascent.add_boundary_constraint('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 = dm.Phase(ode_class=CannonballODE, 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', fix_initial=False, fix_final=True, units='m', rate_source='h_dot') descent.add_state('gam', fix_initial=False, fix_final=False, units='rad', rate_source='gam_dot') descent.add_state('v', fix_initial=False, fix_final=False, units='m/s', rate_source='v_dot') 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_objective('r', loc='final', scaler=-1.0) # Add internally-managed design parameters to the trajectory. traj.add_parameter('CD', targets={ 'ascent': ['CD'], 'descent': ['CD'] }, val=0.5, units=None, opt=False, static_target=True) # 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) # Note velocity is not included here. Doing so is equivalent to linking kinetic energy, # and causes a duplicate row in the constraint jacobian. traj.link_phases(phases=['ascent', 'descent'], vars=['time', 'r', 'h', 'gam'], connected=True) traj.add_linkage_constraint('ascent', 'descent', 'ke', 'ke', ref=100000, connected=False) p.model.connect('size_comp.mass', 'traj.parameters:m') p.model.connect('size_comp.S', 'traj.parameters:S') # Finish Problem Setup p.model.linear_solver = om.DirectSolver() p.driver.add_recorder(om.SqliteRecorder('ex_two_phase_cannonball.db')) p.setup() # Set Initial Guesses p.set_val('radius', 0.05, units='m') p.set_val('dens', 7.87, units='g/cm**3') p.set_val('traj.parameters: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) assert_near_equal(p.get_val('traj.descent.states:r')[-1], 3183.25, tolerance=1.0E-2)
def test_two_phase_cannonball_for_docs(self): from openmdao.api import Problem, Group, IndepVarComp, DirectSolver, SqliteRecorder, \ pyOptSparseDriver from openmdao.utils.assert_utils import assert_rel_error from dymos import Phase, Trajectory, load_simulation_results from dymos.examples.cannonball.cannonball_ode import CannonballODE from dymos.examples.cannonball.size_comp import CannonballSizeComp p = Problem(model=Group()) p.driver = pyOptSparseDriver() p.driver.options['optimizer'] = 'SLSQP' p.driver.options['dynamic_simul_derivs'] = True external_params = p.model.add_subsystem('external_params', 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', Trajectory()) # First Phase (ascent) ascent = Phase('radau-ps', ode_class=CannonballODE, num_segments=5, transcription_order=3, compressed=True) 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) 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) # Limit the muzzle energy ascent.add_boundary_constraint('kinetic_energy.ke', loc='initial', units='J', upper=400000, lower=0, ref=100000) # Second Phase (descent) descent = Phase('gauss-lobatto', ode_class=CannonballODE, num_segments=5, transcription_order=3, compressed=True) 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) descent.set_state_options('r', fix_initial=False, fix_final=False) descent.set_state_options('h', fix_initial=False, fix_final=True) descent.set_state_options('gam', fix_initial=False, fix_final=False) descent.set_state_options('v', fix_initial=False, fix_final=False) descent.add_objective('r', loc='final', scaler=-1.0) # Add internally-managed design parameters to the trajectory. traj.add_design_parameter('CD', val=0.5, units=None, opt=False) traj.add_design_parameter('CL', val=0.0, units=None, opt=False) traj.add_design_parameter('T', val=0.0, units='N', opt=False) traj.add_design_parameter('alpha', val=0.0, units='deg', opt=False) # Add externally-provided design parameters to the trajectory. traj.add_input_parameter('mass', targets={ 'ascent': 'm', 'descent': 'm' }, val=1.0, units='kg') traj.add_input_parameter('S', val=0.005, units='m**2') # 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.input_parameters:mass') p.model.connect('size_comp.S', 'traj.input_parameters:S') # Finish Problem Setup p.model.options['assembled_jac_type'] = 'csc' p.model.linear_solver = DirectSolver(assemble_jac=True) p.driver.add_recorder(SqliteRecorder('ex_two_phase_cannonball.db')) p.setup(check=True) # 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.design_parameters:CD', 0.5) p.set_val('traj.design_parameters:CL', 0.0) p.set_val('traj.design_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') p.run_driver() assert_rel_error(self, traj.get_values('r')['descent'][-1], 3191.83945861, tolerance=1.0E-2) exp_out = traj.simulate(times=100, record_file='ex_two_phase_cannonball_sim.db') # exp_out_loaded = load_simulation_results('ex_two_phase_cannonball_sim.db') 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])) fig, axes = plt.subplots(nrows=3, ncols=1, figsize=(8, 6)) axes[0].plot( traj.get_values('r')['ascent'], traj.get_values('h')['ascent'], 'bo') axes[0].plot( traj.get_values('r')['descent'], traj.get_values('h')['descent'], 'ro') axes[0].plot( exp_out.get_values('r')['ascent'], exp_out.get_values('h')['ascent'], 'b--') axes[0].plot( exp_out.get_values('r')['descent'], exp_out.get_values('h')['descent'], 'r--') axes[0].set_xlabel('range (m)') axes[0].set_ylabel('altitude (m)') # plt.suptitle('Kinetic Energy vs Time') axes[1].plot( traj.get_values('time')['ascent'], traj.get_values('kinetic_energy.ke')['ascent'], 'bo') axes[1].plot( traj.get_values('time')['descent'], traj.get_values('kinetic_energy.ke')['descent'], 'ro') axes[1].plot( exp_out.get_values('time')['ascent'], exp_out.get_values('kinetic_energy.ke')['ascent'], 'b--') axes[1].plot( exp_out.get_values('time')['descent'], exp_out.get_values('kinetic_energy.ke')['descent'], 'r--') # axes[1].plot(exp_out_loaded.get_values('time')['ascent'], # exp_out_loaded.get_values('kinetic_energy.ke')['ascent'], # 'b--') # # axes[1].plot(exp_out_loaded.get_values('time')['descent'], # exp_out_loaded.get_values('kinetic_energy.ke')['descent'], # 'r--') axes[1].set_xlabel('time (s)') axes[1].set_ylabel(r'kinetic energy (J)') # plt.figure() axes[2].plot( traj.get_values('time')['ascent'], traj.get_values('gam', units='deg')['ascent'], 'bo') axes[2].plot( traj.get_values('time')['descent'], traj.get_values('gam', units='deg')['descent'], 'ro') axes[2].plot( exp_out.get_values('time')['ascent'], exp_out.get_values('gam', units='deg')['ascent'], 'b--') axes[2].plot( exp_out.get_values('time')['descent'], exp_out.get_values('gam', units='deg')['descent'], 'r--') axes[2].set_xlabel('time (s)') axes[2].set_ylabel(r'flight path angle (deg)') 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 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') ascent.add_control('CD', targets=['aero.CD'], opt=False, val=0.05) # 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_parameter('CD', targets=['aero.CD'], val=0.01) descent.add_objective('r', loc='final', scaler=-1.0) # Add internally-managed design parameters to the trajectory. 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') traj.connect('ascent.timeseries.controls:CD', 'descent.parameters:CD', src_indices=[-1]) # 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.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, simulate=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_link_bounded_times_final_to_initial(self): """ Test that linking phases with times that are fixed at the linkage point raises an exception. """ import openmdao.api as om 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(initial_bounds=(0, 0), duration_bounds=(10, 10), 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=(10, 10), duration_bounds=(10, 10), 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) # Note velocity is not included here. Doing so is equivalent to linking kinetic energy, # and causes a duplicate row in the constraint jacobian. traj.link_phases(phases=['ascent', 'descent'], vars=['time', 'r', 'h', 'gam'], connected=False) traj.add_linkage_constraint('ascent', 'descent', 'kinetic_energy.ke', 'kinetic_energy.ke', ref=100000, connected=False) # 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') with self.assertRaises(ValueError) as e: p.setup() self.assertEqual( str(e.exception), 'Invalid linkage in Trajectory traj: Cannot link final ' 'value of "time" in ascent to initial value of "time" in ' 'descent. Values on both sides of the linkage are fixed.')