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') # 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('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()
def new_descent_phase(transcription): descent = CannonballPhase(transcription=transcription) # All initial states and time are free (they will be linked to the final states of ballistic_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=10, 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, units='deg') descent.add_state('v', fix_initial=False, fix_final=False) descent.add_input_parameter('S', targets=['aero.S'], units='m**2') descent.add_input_parameter('mass', targets=['eom.m', 'kinetic_energy.m'], units='kg') return descent
def new_propelled_ascent_phase(transcription): propelled_ascent = CannonballPhase(ode_class=WaterPropulsionODE, transcription=transcription) # Add states specific for the propelled ascent propelled_ascent.add_state('p', units='bar', rate_source='water_engine.pdot', targets=['water_engine.p']) propelled_ascent.add_state('V_w', units='m**3', ref=1e-3, rate_source='water_engine.Vdot', targets=['water_engine.V_w', 'mass_adder.V_w']) # All initial states except flight path angle and water volume are fixed # Final flight path angle is fixed (we will set it to zero so that the phase ends at apogee) # Final water volume is fixed (we will set it to zero so that phase ends when bottle empties) propelled_ascent.set_time_options(fix_initial=True, duration_bounds=(0, 0.5), duration_ref=0.1, units='s') propelled_ascent.set_state_options('r', fix_initial=True, fix_final=False) propelled_ascent.set_state_options('h', fix_initial=True, fix_final=False) propelled_ascent.set_state_options('gam', fix_initial=False, fix_final=False, lower=0, upper=89, units='deg') propelled_ascent.set_state_options('v', fix_initial=True, fix_final=False) propelled_ascent.set_state_options('V_w', fix_initial=False, fix_final=True) propelled_ascent.set_state_options('p', fix_initial=True, fix_final=False) propelled_ascent.add_input_parameter('S', targets=['aero.S'], units='m**2') propelled_ascent.add_input_parameter('m_empty', targets=['mass_adder.m_empty'], units='kg') propelled_ascent.add_input_parameter('V_b', targets=['water_engine.V_b'], units='m**3') propelled_ascent.add_timeseries_output('water_engine.F', 'T', units='N') return propelled_ascent
def new_ballistic_ascent_phase(transcription): ballistic_ascent = CannonballPhase(transcription=transcription) # All initial states are free (they will be linked to the final stages of propelled_ascent). # Final flight path angle is fixed (we will set it to zero so that the phase ends at apogee) ballistic_ascent.set_time_options(fix_initial=False, initial_bounds=(0, 1), duration_bounds=(0, 10), duration_ref=1, units='s') ballistic_ascent.set_state_options('r', fix_initial=False, fix_final=False) ballistic_ascent.set_state_options('h', fix_initial=False, fix_final=False) ballistic_ascent.set_state_options('gam', fix_initial=False, fix_final=True, upper=89, units='deg') ballistic_ascent.set_state_options('v', fix_initial=False, fix_final=False) ballistic_ascent.add_input_parameter('S', targets=['aero.S'], units='m**2') ballistic_ascent.add_input_parameter('m_empty', targets=['eom.m'], units='kg') return ballistic_ascent
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.')