def test_gauss_lobatto(self): p = om.Problem(model=om.Group()) p.driver = om.pyOptSparseDriver() p.driver.options['optimizer'] = 'SLSQP' p.driver.declare_coloring() t = dm.GaussLobatto(num_segments=20, order=[3, 5] * 10, compressed=True) phase = dm.Phase(ode_class=BrachistochroneODE, transcription=t) p.model.add_subsystem('phase0', phase) phase.set_time_options(fix_initial=True, duration_bounds=(.5, 10)) phase.add_state('x', fix_initial=True, fix_final=False) phase.add_state('y', fix_initial=True, fix_final=False) 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) 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.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'] = phase.interp('x', [0, 10]) p['phase0.states:y'] = phase.interp('y', [10, 5]) p['phase0.states:v'] = phase.interp('v', [0, 9.9]) p['phase0.controls:theta'] = phase.interp('theta', [5, 100]) p['phase0.parameters:g'] = 9.80665 p.run_driver() exp_out = phase.simulate() t_initial = p.get_val('phase0.timeseries.time')[0] tf = p.get_val('phase0.timeseries.time')[-1] x0 = p.get_val('phase0.timeseries.states:x')[0] xf = p.get_val('phase0.timeseries.states:x')[-1] y0 = p.get_val('phase0.timeseries.states:y')[0] yf = p.get_val('phase0.timeseries.states:y')[-1] v0 = p.get_val('phase0.timeseries.states:v')[0] vf = p.get_val('phase0.timeseries.states:v')[-1] g = p.get_val('phase0.timeseries.parameters:g')[0] thetaf = exp_out.get_val('phase0.timeseries.controls:theta')[-1] assert_almost_equal(t_initial, 0.0) assert_almost_equal(x0, 0.0) assert_almost_equal(y0, 10.0) assert_almost_equal(v0, 0.0) assert_almost_equal(tf, 1.8016, decimal=4) assert_almost_equal(xf, 10.0, decimal=3) assert_almost_equal(yf, 5.0, decimal=3) assert_almost_equal(vf, 9.902, decimal=3) assert_almost_equal(g, 9.80665, decimal=3) assert_almost_equal(thetaf, 100.12, decimal=0)
def test_brachistochrone_integrated_param_gauss_lobatto(self): import numpy as np import openmdao.api as om from openmdao.utils.assert_utils import assert_near_equal import dymos as dm p = om.Problem(model=om.Group()) p.driver = om.pyOptSparseDriver() p.driver.options['optimizer'] = 'SLSQP' p.driver.declare_coloring() phase = dm.Phase(ode_class=BrachistochroneODE, transcription=dm.GaussLobatto(num_segments=10)) p.model.add_subsystem('phase0', phase) phase.set_time_options(fix_initial=True, duration_bounds=(.5, 10), units='s') phase.add_state('x', fix_initial=True, fix_final=True, rate_source='xdot', units='m') phase.add_state('y', fix_initial=True, fix_final=True, rate_source='ydot', units='m') phase.add_state('v', fix_initial=True, rate_source='vdot', units='m/s') phase.add_state('theta', fix_initial=False, rate_source='theta_dot', lower=1E-3) # theta_dot has no target, therefore we need to explicitly set the units and shape. phase.add_parameter('theta_dot', units='deg/s', shape=(1,), opt=True, lower=0, upper=100) phase.add_parameter('g', units='m/s**2', opt=False, 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() p.setup() p['phase0.t_initial'] = 0.0 p['phase0.t_duration'] = 2.0 p['phase0.states:x'] = phase.interpolate(ys=[0, 10], nodes='state_input') p['phase0.states:y'] = phase.interpolate(ys=[10, 5], nodes='state_input') p['phase0.states:v'] = phase.interpolate(ys=[0, 9.9], nodes='state_input') p['phase0.states:theta'] = np.radians(phase.interpolate(ys=[0.05, 100.0], nodes='state_input')) p['phase0.parameters:theta_dot'] = 60.0 # Solve for the optimal trajectory dm.run_problem(p, refine_iteration_limit=5) # Test the results assert_near_equal(p.get_val('phase0.timeseries.time')[-1], 1.8016, tolerance=1.0E-3) sim_out = phase.simulate(times_per_seg=20) t_sol = p.get_val('phase0.timeseries.time') x_sol = p.get_val('phase0.timeseries.states:x') y_sol = p.get_val('phase0.timeseries.states:y') v_sol = p.get_val('phase0.timeseries.states:v') theta_sol = p.get_val('phase0.timeseries.states:theta') t_sim = sim_out.get_val('phase0.timeseries.time') x_sim = sim_out.get_val('phase0.timeseries.states:x') y_sim = sim_out.get_val('phase0.timeseries.states:y') v_sim = sim_out.get_val('phase0.timeseries.states:v') theta_sim = sim_out.get_val('phase0.timeseries.states:theta') assert_timeseries_near_equal(t_sol, x_sol, t_sim, x_sim, tolerance=5.0E-3) assert_timeseries_near_equal(t_sol, y_sol, t_sim, y_sim, tolerance=5.0E-3) assert_timeseries_near_equal(t_sol, v_sol, t_sim, v_sim, tolerance=5.0E-3) assert_timeseries_near_equal(t_sol, theta_sol, t_sim, theta_sim, tolerance=5.0E-3)
def test_two_burn_orbit_raise_gl_rk_gl_constrained(self): import numpy as np import matplotlib.pyplot as plt import openmdao.api as om from openmdao.utils.assert_utils import assert_near_equal from openmdao.utils.general_utils import set_pyoptsparse_opt import dymos as dm from dymos.examples.finite_burn_orbit_raise.finite_burn_eom import FiniteBurnODE traj = dm.Trajectory() p = om.Problem(model=om.Group()) p.model.add_subsystem('traj', traj) p.driver = om.pyOptSparseDriver() _, optimizer = set_pyoptsparse_opt('SNOPT', fallback=True) p.driver.options['optimizer'] = optimizer p.driver.declare_coloring() traj.add_design_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=10, order=3, compressed=True)) 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', targets=['r'], units='DU') burn1.add_state('theta', fix_initial=True, fix_final=False, defect_scaler=100.0, rate_source='theta_dot', targets=['theta'], units='rad') burn1.add_state('vr', fix_initial=True, fix_final=False, defect_scaler=100.0, rate_source='vr_dot', targets=['vr'], units='DU/TU') burn1.add_state('vt', fix_initial=True, fix_final=False, defect_scaler=100.0, rate_source='vt_dot', targets=['vt'], units='DU/TU') burn1.add_state('accel', fix_initial=True, fix_final=False, rate_source='at_dot', targets=['accel'], 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, targets=['u1']) # Second Phase (Coast) coast = dm.Phase(ode_class=FiniteBurnODE, transcription=dm.RungeKutta(num_segments=20)) traj.add_phase('coast', coast) coast.set_time_options(initial_bounds=(0.5, 20), duration_bounds=(.5, 10), duration_ref=10, units='TU') # TODO Moving add_state('theta'... after add_state('r',... causes the # coloring to be invalid with scipy > 1.0.1 coast.add_state('theta', fix_initial=False, fix_final=False, defect_scaler=100.0, units='rad', rate_source='theta_dot', targets=['theta']) 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('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=False, ref=1.0E-12, defect_ref=1.0E-12, 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_control('u1', targets=['u1'], opt=False, val=0.0, units='deg') # Third Phase (burn) burn2 = dm.Phase(ode_class=FiniteBurnODE, transcription=dm.GaussLobatto(num_segments=10, order=3, compressed=True)) traj.add_phase('burn2', burn2) burn2.set_time_options(initial_bounds=(0.5, 20), duration_bounds=(.5, 10), initial_ref=10, units='TU') burn2.add_state('r', fix_initial=False, fix_final=True, rate_source='r_dot', targets=['r'], units='DU') burn2.add_state('theta', fix_initial=False, fix_final=False, rate_source='theta_dot', targets=['theta'], units='rad') burn2.add_state('vr', fix_initial=False, fix_final=True, rate_source='vr_dot', targets=['vr'], units='DU/TU') burn2.add_state('vt', fix_initial=False, fix_final=True, rate_source='vt_dot', targets=['vt'], units='DU/TU') burn2.add_state('accel', fix_initial=False, fix_final=False, defect_ref=1.0E-6, rate_source='at_dot', targets=['accel'], units='DU/TU**2') burn2.add_state('deltav', fix_initial=False, fix_final=False, rate_source='deltav_dot', units='DU/TU') burn2.add_control('u1', targets=['u1'], rate_continuity=True, rate2_continuity=True, units='deg', scaler=0.01, lower=-30, upper=30) burn2.add_objective('deltav', loc='final', scaler=1.0) burn1.add_timeseries_output('pos_x', units='DU') coast.add_timeseries_output('pos_x', units='DU') burn2.add_timeseries_output('pos_x', units='DU') burn1.add_timeseries_output('pos_y', units='DU') coast.add_timeseries_output('pos_y', units='DU') burn2.add_timeseries_output('pos_y', units='DU') # Link Phases traj.link_phases(phases=['burn1', 'coast', 'burn2'], vars=['time', 'r', 'theta', 'vr', 'vt', 'deltav']) traj.link_phases(phases=['burn1', 'burn2'], vars=['accel']) # Finish Problem Setup p.model.linear_solver = om.DirectSolver() p.setup(check=True, force_alloc_complex=True) # Set Initial Guesses p.set_val('traj.design_parameters:c', value=1.5) 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.interpolate(ys=[1, 1.5], nodes='state_input')) p.set_val('traj.burn1.states:theta', value=burn1.interpolate(ys=[0, 1.7], nodes='state_input')) p.set_val('traj.burn1.states:vr', value=burn1.interpolate(ys=[0, 0], nodes='state_input')) p.set_val('traj.burn1.states:vt', value=burn1.interpolate(ys=[1, 1], nodes='state_input')) p.set_val('traj.burn1.states:accel', value=burn1.interpolate(ys=[0.1, 0], nodes='state_input')) p.set_val( 'traj.burn1.states:deltav', value=burn1.interpolate(ys=[0, 0.1], nodes='state_input'), ) p.set_val('traj.burn1.controls:u1', value=burn1.interpolate(ys=[-3.5, 13.0], nodes='control_input')) 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.interpolate(ys=[1.3, 1.5], nodes='state_input')) p.set_val('traj.coast.states:theta', value=coast.interpolate(ys=[2.1767, 1.7], nodes='state_input')) p.set_val('traj.coast.states:vr', value=coast.interpolate(ys=[0.3285, 0], nodes='state_input')) p.set_val('traj.coast.states:vt', value=coast.interpolate(ys=[0.97, 1], nodes='state_input')) p.set_val('traj.coast.states:accel', value=coast.interpolate(ys=[0, 0], nodes='state_input')) # p.set_val('traj.coast.controls:u1', # value=coast.interpolate(ys=[0, 0], nodes='control_input')) 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.interpolate(ys=[1.8, 3], nodes='state_input')) p.set_val('traj.burn2.states:theta', value=burn2.interpolate(ys=[3.2, 4.0], nodes='state_input')) p.set_val('traj.burn2.states:vr', value=burn2.interpolate(ys=[.5, 0], nodes='state_input')) p.set_val('traj.burn2.states:vt', value=burn2.interpolate(ys=[1, np.sqrt(1 / 3)], nodes='state_input')) p.set_val('traj.burn2.states:accel', value=burn2.interpolate(ys=[0.1, 0], nodes='state_input')) p.set_val('traj.burn2.states:deltav', value=burn2.interpolate(ys=[0.1, 0.2], nodes='state_input')) p.set_val('traj.burn2.controls:u1', value=burn2.interpolate(ys=[1, 1], nodes='control_input')) p.run_driver() assert_near_equal(p.get_val('traj.burn2.timeseries.states:deltav')[-1], 0.3995, tolerance=2.0E-3) # Plot results 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_sol[phs], u1_sol[phs], 'ro', ms=3) ax_u1.plot(t_exp[phs], u1_exp[phs], 'b-') except KeyError: pass ax_deltav.plot(t_sol[phs], dv_sol[phs], 'ro', ms=3) ax_deltav.plot(t_exp[phs], dv_exp[phs], 'b-') ax_xy.plot(x_sol[phs], y_sol[phs], 'ro', ms=3, label='implicit') ax_xy.plot(x_exp[phs], y_exp[phs], 'b-', label='explicit') plt.show()
def test_run_HS_problem_gl(self): p = om.Problem(model=om.Group()) p.driver = om.pyOptSparseDriver() p.driver.declare_coloring() optimizer = 'IPOPT' p.driver.options['optimizer'] = optimizer if optimizer == 'SNOPT': p.driver.opt_settings['Major iterations limit'] = 100 p.driver.opt_settings['Major feasibility tolerance'] = 1.0E-6 p.driver.opt_settings['Major optimality tolerance'] = 1.0E-6 elif optimizer == 'IPOPT': p.driver.opt_settings['hessian_approximation'] = 'limited-memory' # p.driver.opt_settings['nlp_scaling_method'] = 'user-scaling' p.driver.opt_settings['print_level'] = 5 p.driver.opt_settings['linear_solver'] = 'mumps' traj = p.model.add_subsystem('traj', dm.Trajectory()) phase0 = traj.add_phase('phase0', dm.Phase(ode_class=HyperSensitiveODE, transcription=dm.GaussLobatto(num_segments=30, order=3))) phase0.set_time_options(fix_initial=True, fix_duration=True) phase0.add_state('x', fix_initial=True, fix_final=False, rate_source='x_dot', targets=['x']) phase0.add_state('xL', fix_initial=True, fix_final=False, rate_source='L', targets=['xL']) phase0.add_control('u', opt=True, targets=['u'], rate_continuity=False) phase0.add_boundary_constraint('x', loc='final', equals=1) phase0.add_objective('xL', loc='final') phase0.set_refine_options(refine=True) p.setup(check=True) tf = 100 p.set_val('traj.phase0.states:x', phase0.interpolate(ys=[1.5, 1], nodes='state_input')) p.set_val('traj.phase0.states:xL', phase0.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', phase0.interpolate(ys=[-0.6, 2.4], nodes='control_input')) dm.run_problem(p, refine=True) sqrt_two = np.sqrt(2) val = sqrt_two * tf c1 = (1.5 * np.exp(-val) - 1) / (np.exp(-val) - np.exp(val)) c2 = (1 - 1.5 * np.exp(val)) / (np.exp(-val) - np.exp(val)) ui = c1 * (1 + sqrt_two) + c2 * (1 - sqrt_two) uf = c1 * (1 + sqrt_two) * np.exp(val) + c2 * (1 - sqrt_two) * np.exp(-val) J = 0.5 * (c1 ** 2 * (1 + sqrt_two) * np.exp(2 * val) + c2 ** 2 * (1 - sqrt_two) * np.exp(-2 * val) - (1 + sqrt_two) * c1 ** 2 - (1 - sqrt_two) * c2 ** 2) assert_near_equal(p.get_val('traj.phase0.timeseries.controls:u')[0], ui, tolerance=1e-2) assert_near_equal(p.get_val('traj.phase0.timeseries.controls:u')[-1], uf, tolerance=1e-2) assert_near_equal(p.get_val('traj.phase0.timeseries.states:xL')[-1], J, tolerance=5e-4)
def _make_problem(self, 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) traj = dm.Trajectory() phase = dm.Phase(ode_class=self.ode, transcription=t) p.model.add_subsystem('traj0', traj) traj.add_phase('phase0', phase) phase.set_time_options(fix_initial=True, duration_bounds=(.5, 10)) phase.add_state('x', fix_initial=True, fix_final=False, solve_segments=solve_segments, rate_source='xdot') phase.add_state('y', fix_initial=True, fix_final=False, solve_segments=solve_segments, rate_source='ydot') # 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', fix_initial=True, fix_final=False, solve_segments=solve_segments, rate_source='vdot') 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', static_target=True) 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.interp('x', [0, 10]) p['traj0.phase0.states:y'] = phase.interp('y', [10, 5]) p['traj0.phase0.states:v'] = phase.interp('v', [0, 9.9]) p['traj0.phase0.controls:theta'] = phase.interp('theta', [5, 100]) p['traj0.phase0.parameters:g'] = 9.80665 dm.run_problem(p, run_driver=run_driver, simulate=True) return p
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_input_parameter('S', targets=['aero.S'], units='m**2') ascent.add_input_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_input_parameter('S', targets=['aero.S'], units='m**2') descent.add_input_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_design_parameter('CD', targets={ 'ascent': ['aero.CD'], 'descent': ['aero.CD'] }, val=0.5, units=None, opt=False) traj.add_design_parameter('CL', targets={ 'ascent': ['aero.CL'], 'descent': ['aero.CL'] }, val=0.0, units=None, opt=False) traj.add_design_parameter('T', targets={ 'ascent': ['eom.T'], 'descent': ['eom.T'] }, val=0.0, units='N', opt=False) traj.add_design_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_input_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_input_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.input_parameters:m') 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() # 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') 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.input_parameters:{0}'.format( param)), 'descent': p.get_val( 'traj.descent.timeseries.input_parameters:{0}'.format( param)) } p_exp = { 'ascent': exp_out.get_val('traj.ascent.timeseries.' 'input_parameters:{0}'.format(param)), 'descent': exp_out.get_val('traj.descent.timeseries.' 'input_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_brachistochrone_undecorated_ode_gl(self): import numpy as np import matplotlib matplotlib.use('Agg') import matplotlib.pyplot as plt import openmdao.api as om from openmdao.utils.assert_utils import assert_near_equal import dymos as dm p = om.Problem(model=om.Group()) p.driver = om.ScipyOptimizeDriver() phase = dm.Phase(ode_class=BrachistochroneODE, transcription=dm.GaussLobatto(num_segments=10)) p.model.add_subsystem('phase0', phase) phase.set_time_options(fix_initial=True, duration_bounds=(.5, 10), units='s') 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, rate_source='vdot') phase.add_control('theta', units='deg', rate_continuity=False, lower=0.01, upper=179.9) phase.add_parameter('g', units='m/s**2', opt=False, val=9.80665, targets=['g']) # Minimize time at the end of the phase phase.add_objective('time', loc='final', scaler=10) p.model.linear_solver = om.DirectSolver() p.setup() p['phase0.t_initial'] = 0.0 p['phase0.t_duration'] = 2.0 p['phase0.states:x'] = phase.interpolate(ys=[0, 10], nodes='state_input') p['phase0.states:y'] = phase.interpolate(ys=[10, 5], nodes='state_input') p['phase0.states:v'] = phase.interpolate(ys=[0, 9.9], nodes='state_input') p['phase0.controls:theta'] = phase.interpolate(ys=[5, 100.5], nodes='control_input') # Solve for the optimal trajectory p.run_driver() # Test the results assert_near_equal(p.get_val('phase0.timeseries.time')[-1], 1.8016, tolerance=1.0E-3)
'solidity': num_blades * blade_chord / np.pi / prop_rad, # solidity 'omega': 136. / prop_rad, # angular rotation rate 'prop_CD0': 0.012, # CD0 for prop profile power 'k_elec': 0.9, # electrical and mechanical losses factor 'k_ind': 1.2, # induced-losses factor 'nB': num_blades, # number of blades per propeller 'bc': blade_chord, # representative blade chord 'n_props': num_props # number of propellers } p = om.Problem() traj = dm.Trajectory() p.model.add_subsystem('traj', traj) phase = dm.Phase(transcription=dm.GaussLobatto(num_segments=10, order=3, solve_segments=False, compressed=False), ode_class=DynamicsVectorized, ode_init_kwargs={'input_dict': input_dict}) traj.add_phase('phase0', phase) phase.set_time_options(fix_initial=True, duration_bounds=(5, 60), duration_ref=30) phase.add_state('x', fix_initial=True, rate_source='x_dot', ref0=0, ref=900, defect_ref=100) phase.add_state('y', fix_initial=True, rate_source='y_dot', ref0=0, ref=300, defect_ref=300) phase.add_state('vx', fix_initial=True, rate_source='a_x', ref0=0, ref=10) phase.add_state('vy', fix_initial=True, rate_source='a_y', ref0=0, ref=10) phase.add_state('energy', fix_initial=True, rate_source='energy_dot', ref0=0, ref=1E7, defect_ref=1E5) phase.add_control('power', lower=1e3, upper=311000, ref0=1e3, ref=311000, rate_continuity=False) phase.add_control('theta', lower=0., upper=3 * np.pi / 4, ref0=0, ref=3 * np.pi / 4, rate_continuity=False)
def test_doc_ssto_linear_tangent_guidance(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.plotting import plot_results 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 LinearTangentGuidanceComp(om.ExplicitComponent): """ Compute pitch angle from static controls governing linear expression for pitch angle tangent as function of time. """ def initialize(self): self.options.declare('num_nodes', types=int) def setup(self): nn = self.options['num_nodes'] self.add_input('a_ctrl', val=np.zeros(nn), desc='linear tangent slope', units='1/s') self.add_input('b_ctrl', val=np.zeros(nn), desc='tangent of theta at t=0', units=None) self.add_input('time_phase', val=np.zeros(nn), desc='time', units='s') self.add_output('theta', val=np.zeros(nn), desc='pitch angle', units='rad') # Setup partials arange = np.arange(self.options['num_nodes']) self.declare_partials(of='theta', wrt='a_ctrl', rows=arange, cols=arange, val=1.0) self.declare_partials(of='theta', wrt='b_ctrl', rows=arange, cols=arange, val=1.0) self.declare_partials(of='theta', wrt='time_phase', rows=arange, cols=arange, val=1.0) def compute(self, inputs, outputs): a = inputs['a_ctrl'] b = inputs['b_ctrl'] t = inputs['time_phase'] outputs['theta'] = np.arctan(a * t + b) def compute_partials(self, inputs, jacobian): a = inputs['a_ctrl'] b = inputs['b_ctrl'] t = inputs['time_phase'] x = a * t + b denom = x**2 + 1.0 jacobian['theta', 'a_ctrl'] = t / denom jacobian['theta', 'b_ctrl'] = 1.0 / denom jacobian['theta', 'time_phase'] = a / denom 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', LinearTangentGuidanceComp(num_nodes=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()) p.driver = om.pyOptSparseDriver() p.driver.declare_coloring() traj = dm.Trajectory() p.model.add_subsystem('traj', traj) phase = dm.Phase(ode_class=LaunchVehicleLinearTangentODE, transcription=dm.GaussLobatto(num_segments=10, order=5, compressed=True)) traj.add_phase('phase0', phase) phase.set_time_options(fix_initial=True, duration_bounds=(10, 1000), targets=['guidance.time_phase']) 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', targets=['eom.vx'], units='m/s') phase.add_state('vy', fix_initial=True, rate_source='eom.vydot', targets=['eom.vy'], units='m/s') phase.add_state('m', fix_initial=True, rate_source='eom.mdot', targets=['eom.m'], units='kg') 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_parameter('a_ctrl', units='1/s', opt=True, targets=['guidance.a_ctrl']) phase.add_parameter('b_ctrl', units=None, opt=True, targets=['guidance.b_ctrl']) 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']) phase.add_objective('time', index=-1, scaler=0.01) p.model.linear_solver = om.DirectSolver() phase.add_timeseries_output('guidance.theta', units='deg') p.setup(check=True) p['traj.phase0.t_initial'] = 0.0 p['traj.phase0.t_duration'] = 500.0 p['traj.phase0.states:x'] = phase.interp('x', [0, 350000.0]) p['traj.phase0.states:y'] = phase.interp('y', [0, 185000.0]) p['traj.phase0.states:vx'] = phase.interp('vx', [0, 1627.0]) p['traj.phase0.states:vy'] = phase.interp('vy', [1.0E-6, 0]) p['traj.phase0.states:m'] = phase.interp('m', [50000, 50000]) p['traj.phase0.parameters:a_ctrl'] = -0.01 p['traj.phase0.parameters:b_ctrl'] = 3.0 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 # plot_results( [('traj.phase0.timeseries.states:x', 'traj.phase0.timeseries.states:y', 'range (m)', 'altitude (m)'), ('traj.phase0.timeseries.time', 'traj.phase0.timeseries.theta', 'range (m)', 'altitude (m)')], title= 'Single Stage to Orbit Solution Using Linear Tangent Guidance', p_sol=p, p_sim=exp_out) 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.interpolate(ys=[1, 1.5], nodes='state_input')) p.set_val('traj.burn1.states:theta', value=burn1.interpolate(ys=[0, 1.7], nodes='state_input')) p.set_val('traj.burn1.states:vr', value=burn1.interpolate(ys=[0, 0], nodes='state_input')) p.set_val('traj.burn1.states:vt', value=burn1.interpolate(ys=[1, 1], nodes='state_input')) p.set_val('traj.burn1.states:accel', value=burn1.interpolate(ys=[0.1, 0], nodes='state_input')) p.set_val('traj.burn1.states:deltav', value=burn1.interpolate(ys=[0, 0.1], nodes='state_input')) p.set_val('traj.burn1.controls:u1', value=burn1.interpolate(ys=[-3.5, 13.0], nodes='control_input')) 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.interpolate(ys=[1.3, 1.5], nodes='state_input')) p.set_val('traj.coast.states:theta', value=coast.interpolate(ys=[2.1767, 1.7], nodes='state_input')) p.set_val('traj.coast.states:vr', value=coast.interpolate(ys=[0.3285, 0], nodes='state_input')) p.set_val('traj.coast.states:vt', value=coast.interpolate(ys=[0.97, 1], nodes='state_input')) p.set_val('traj.coast.states:accel', value=coast.interpolate(ys=[0, 0], nodes='state_input')) 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.interpolate(ys=[1, 3.], nodes='state_input')) p.set_val('traj.burn2.states:theta', value=burn2.interpolate(ys=[0, 4.0], nodes='state_input')) p.set_val('traj.burn2.states:vr', value=burn2.interpolate(ys=[0, 0], nodes='state_input')) p.set_val('traj.burn2.states:vt', value=burn2.interpolate(ys=[1, np.sqrt(1 / 3.)], nodes='state_input')) p.set_val('traj.burn2.states:deltav', value=burn2.interpolate(ys=[0.1, 0.2], nodes='state_input')) p.set_val('traj.burn2.states:accel', value=burn2.interpolate(ys=[0.1, 0], nodes='state_input')) p.set_val('traj.burn2.controls:u1', value=burn2.interpolate(ys=[0, 0], nodes='control_input')) 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 make_prob(self, transcription, num_segments, transcription_order, compressed): p = om.Problem(model=om.Group()) 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) phase.set_time_options(fix_initial=True, duration_bounds=(.5, 10)) phase.add_state('x', fix_initial=True, fix_final=False, solve_segments='forward') phase.add_state('y', fix_initial=True, fix_final=False, solve_segments='forward') # 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', fix_initial=True, fix_final=False, solve_segments='forward') phase.add_control('theta', continuity=True, rate_continuity=True, units='deg', lower=0.01, upper=179.9) phase.add_parameter('g', targets=['g'], units='m/s**2') 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(force_alloc_complex=True) 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 return p
def brachistochrone_min_time(transcription='gauss-lobatto', num_segments=8, transcription_order=3, compressed=True, optimizer='SLSQP'): p = om.Problem(model=om.Group()) p.driver = om.pyOptSparseDriver() p.driver.options['optimizer'] = optimizer p.driver.declare_coloring() 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) 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=False) phase.add_state('y', rate_source=BrachistochroneODE.states['y']['rate_source'], units=BrachistochroneODE.states['y']['units'], fix_initial=True, fix_final=False, solve_segments=False) phase.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) 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.model.linear_solver = om.DirectSolver() p.setup(check=['unconnected_inputs']) 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 p.final_setup() return p
def test_run_HS_problem_gl(self): p = om.Problem(model=om.Group()) p.driver = om.pyOptSparseDriver() p.driver.declare_coloring() _, optimizer = set_pyoptsparse_opt('SNOPT', fallback=True) p.driver.options['optimizer'] = optimizer traj = p.model.add_subsystem('traj', dm.Trajectory()) phase0 = traj.add_phase( 'phase0', dm.Phase(ode_class=HyperSensitiveODE, transcription=dm.GaussLobatto(num_segments=30, order=3))) phase0.set_time_options(fix_initial=True, fix_duration=True) phase0.add_state('x', fix_initial=True, fix_final=False, rate_source='x_dot', targets=['x']) phase0.add_state('xL', fix_initial=True, fix_final=False, rate_source='L', targets=['xL']) phase0.add_control('u', opt=True, targets=['u'], rate_continuity=False) phase0.add_boundary_constraint('x', loc='final', equals=1) phase0.add_objective('xL', loc='final') phase0.set_refine_options(refine=True) p.setup(check=True) tf = 100 p.set_val('traj.phase0.states:x', phase0.interpolate(ys=[1.5, 1], nodes='state_input')) p.set_val('traj.phase0.states:xL', phase0.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', phase0.interpolate(ys=[-0.6, 2.4], nodes='control_input')) dm.run_problem(p, refine=True) sqrt_two = np.sqrt(2) val = sqrt_two * tf c1 = (1.5 * np.exp(-val) - 1) / (np.exp(-val) - np.exp(val)) c2 = (1 - 1.5 * np.exp(val)) / (np.exp(-val) - np.exp(val)) ui = c1 * (1 + sqrt_two) + c2 * (1 - sqrt_two) uf = c1 * (1 + sqrt_two) * np.exp(val) + c2 * (1 - sqrt_two) * np.exp(-val) J = 0.5 * (c1**2 * (1 + sqrt_two) * np.exp(2 * val) + c2**2 * (1 - sqrt_two) * np.exp(-2 * val) - (1 + sqrt_two) * c1**2 - (1 - sqrt_two) * c2**2) assert_rel_error(self, p.get_val('traj.phase0.timeseries.controls:u')[0], ui, tolerance=1e-2) assert_rel_error(self, p.get_val('traj.phase0.timeseries.controls:u')[-1], uf, tolerance=1e-2) assert_rel_error(self, p.get_val('traj.phase0.timeseries.states:xL')[-1], J, tolerance=5e-4)
def _make_problem(self, transcription, num_seg, transcription_order=3, input_initial=False, input_duration=False): p = om.Problem(model=om.Group()) p.driver = om.ScipyOptimizeDriver() # Compute sparsity/coloring when run_driver is called p.driver.declare_coloring() t = { 'gauss-lobatto': dm.GaussLobatto(num_segments=num_seg, order=transcription_order), 'radau-ps': dm.Radau(num_segments=num_seg, order=transcription_order) } phase = dm.Phase(ode_class=_BrachistochroneTestODE, transcription=t[transcription]) p.model.add_subsystem('phase0', phase) phase.set_time_options(initial_bounds=(1, 1), duration_bounds=(.5, 10), units='s', time_phase_targets=['time_phase'], t_duration_targets=['t_duration'], t_initial_targets=['t_initial'], targets=['time'], input_initial=input_initial, input_duration=input_duration) phase.add_state('x', fix_initial=True, rate_source='xdot', units='m') phase.add_state('y', fix_initial=True, rate_source='ydot', units='m') phase.add_state('v', fix_initial=True, rate_source='vdot', targets=['v'], units='m/s') phase.add_control('theta', units='deg', rate_continuity=True, lower=0.01, upper=179.9, targets=['theta']) phase.add_parameter('g', units='m/s**2', opt=False, val=9.80665, targets=['g']) 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() if input_duration: p.model.add_design_var('phase0.t_duration', lower=0, upper=3, scaler=1.0) p.setup(check=True) p['phase0.t_initial'] = 0.5 p['phase0.t_duration'] = 5.0 p['phase0.states:x'] = phase.interp('x', [0, 10]) p['phase0.states:y'] = phase.interp('y', [10, 5]) p['phase0.states:v'] = phase.interp('v', [0, 9.9]) p['phase0.controls:theta'] = phase.interp('theta', [5, 100.5]) return p
def test_static_parameter_connections_gl(self): class TrajectoryODE(om.Group): def initialize(self): self.options.declare('num_nodes', types=int) def setup(self): nn = self.options['num_nodes'] self.add_subsystem( 'sum', om.ExecComp('m_tot = sum(m)', m={ 'value': np.zeros((2, 2)), 'units': 'kg' }, m_tot={ 'value': np.zeros(nn), 'units': 'kg' })) self.add_subsystem('eom', FlightPathEOM2D(num_nodes=nn)) self.connect('sum.m_tot', 'eom.m') optimizer = 'SLSQP' num_segments = 1 transcription_order = 5 p = om.Problem(model=om.Group()) p.driver = om.pyOptSparseDriver() p.driver.options['optimizer'] = optimizer p.driver.declare_coloring() seg_ends, _ = lgl(num_segments + 1) phase = dm.Phase(ode_class=TrajectoryODE, transcription=dm.GaussLobatto( num_segments=num_segments, order=transcription_order, segment_ends=seg_ends)) p.model.add_subsystem('phase0', phase) phase.set_time_options(initial_bounds=(0.0, 100.0), duration_bounds=(0., 100.), units='s') phase.add_state('h', fix_initial=True, fix_final=True, lower=0.0, units='m', rate_source='eom.h_dot') phase.add_state('v', fix_initial=True, fix_final=False, units='m/s', rate_source='eom.v_dot') phase.add_parameter('m', val=[[1, 2], [3, 4]], units='kg', targets='sum.m', dynamic=False) p.model.linear_solver = om.DirectSolver() p.setup(check=True, force_alloc_complex=True) p['phase0.t_initial'] = 0.0 p['phase0.t_duration'] = 100.0 p['phase0.states:h'] = phase.interpolate(ys=[20, 0], nodes='state_input') p['phase0.states:v'] = phase.interpolate(ys=[0, -5], nodes='state_input') p.run_model() expected = np.array([[1, 2], [3, 4]]) assert_near_equal(p.get_val('phase0.rhs_disc.sum.m'), expected) assert_near_equal(p.get_val('phase0.rhs_col.sum.m'), expected)
def make_traj(transcription='gauss-lobatto', transcription_order=3, compressed=False, connected=False, param_mode='param_sequence'): t = { 'gauss-lobatto': dm.GaussLobatto(num_segments=5, order=transcription_order, compressed=compressed), 'radau': dm.Radau(num_segments=20, order=transcription_order, compressed=compressed), 'runge-kutta': dm.RungeKutta(num_segments=5, compressed=compressed) } traj = dm.Trajectory() if param_mode == 'param_sequence': traj.add_parameter('c', opt=False, val=1.5, units='DU/TU', targets={ 'burn1': ['c'], 'coast': ['c'], 'burn2': ['c'] }) elif param_mode == 'param_sequence_missing_phase': traj.add_parameter('c', opt=False, val=1.5, units='DU/TU', targets={ 'burn1': ['c'], 'burn2': ['c'] }) elif param_mode == 'param_no_targets': traj.add_parameter('c', val=1.5, units='DU/TU') # First Phase (burn) burn1 = dm.Phase(ode_class=FiniteBurnODE, transcription=t[transcription]) 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', targets=['r'], units='DU') burn1.add_state('theta', fix_initial=True, fix_final=False, defect_scaler=100.0, rate_source='theta_dot', targets=['theta'], units='rad') burn1.add_state('vr', fix_initial=True, fix_final=False, defect_scaler=100.0, rate_source='vr_dot', targets=['vr'], units='DU/TU') burn1.add_state('vt', fix_initial=True, fix_final=False, defect_scaler=100.0, rate_source='vt_dot', targets=['vt'], units='DU/TU') burn1.add_state('accel', fix_initial=True, fix_final=False, rate_source='at_dot', targets=['accel'], units='DU/TU**2') burn1.add_state('deltav', fix_initial=True, fix_final=False, rate_source='deltav_dot', targets=None, 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, targets=['u1']) # Second Phase (Coast) coast = dm.Phase(ode_class=FiniteBurnODE, transcription=t[transcription]) 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', targets=None) coast.add_parameter('u1', opt=False, val=0.0, units='deg', targets=['u1']) # Third Phase (burn) burn2 = dm.Phase(ode_class=FiniteBurnODE, transcription=t[transcription]) if connected: traj.add_phase('burn2', burn2) traj.add_phase('coast', coast) burn2.set_time_options(initial_bounds=(1.0, 60), duration_bounds=(-10.0, -0.5), initial_ref=10, units='TU') burn2.add_state('r', fix_initial=True, fix_final=False, defect_scaler=100.0, rate_source='r_dot', targets=['r'], units='DU') burn2.add_state('theta', fix_initial=False, fix_final=False, defect_scaler=100.0, rate_source='theta_dot', targets=['theta'], units='rad') burn2.add_state('vr', fix_initial=True, fix_final=False, defect_scaler=1000.0, rate_source='vr_dot', targets=['vr'], units='DU/TU') burn2.add_state('vt', fix_initial=True, fix_final=False, defect_scaler=1000.0, rate_source='vt_dot', targets=['vt'], units='DU/TU') burn2.add_state('accel', fix_initial=False, fix_final=False, defect_scaler=1.0, rate_source='at_dot', targets=['accel'], 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', targets=None) burn2.add_objective('deltav', loc='initial', scaler=100.0) burn2.add_control('u1', rate_continuity=True, rate2_continuity=True, units='deg', scaler=0.01, lower=-180, upper=180, targets=['u1']) else: 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', targets=['r'], units='DU') burn2.add_state('theta', fix_initial=False, fix_final=False, defect_scaler=100.0, rate_source='theta_dot', targets=['theta'], units='rad') burn2.add_state('vr', fix_initial=False, fix_final=True, defect_scaler=1000.0, rate_source='vr_dot', targets=['vr'], units='DU/TU') burn2.add_state('vt', fix_initial=False, fix_final=True, defect_scaler=1000.0, rate_source='vt_dot', targets=['vt'], units='DU/TU') burn2.add_state('accel', fix_initial=False, fix_final=False, defect_scaler=1.0, rate_source='at_dot', targets=['accel'], 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', targets=None) 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=-180, upper=180, targets=['u1']) if 'sequence_missing_phase' in param_mode: coast.add_parameter('c', val=0.0, units='DU/TU', targets=['c']) elif 'no_targets' in param_mode: burn1.add_parameter('c', val=0.0, units='DU/TU', targets=['c']) coast.add_parameter('c', val=0.0, units='DU/TU', targets=['c']) burn2.add_parameter('c', val=0.0, units='DU/TU', targets=['c']) burn1.add_timeseries_output('pos_x', units='DU') coast.add_timeseries_output('pos_x', units='DU') burn2.add_timeseries_output('pos_x', units='DU') burn1.add_timeseries_output('pos_y', units='DU') coast.add_timeseries_output('pos_y', units='DU') burn2.add_timeseries_output('pos_y', units='DU') # Link Phases if connected: traj.link_phases(phases=['burn1', 'coast'], vars=['time', 'r', 'theta', 'vr', 'vt', 'deltav'], connected=True) # No direct connections to the end of a phase. traj.link_phases(phases=['burn2', 'coast'], vars=['r', 'theta', 'vr', 'vt', 'deltav'], locs=('++', '++')) traj.link_phases(phases=['burn2', 'coast'], vars=['time'], locs=('++', '++')) traj.link_phases(phases=['burn1', 'burn2'], vars=['accel'], locs=('++', '++')) else: traj.link_phases(phases=['burn1', 'coast', 'burn2'], vars=['time', 'r', 'theta', 'vr', 'vt', 'deltav']) traj.link_phases(phases=['burn1', 'burn2'], vars=['accel']) return traj
def test_racecar_for_docs(self): import numpy as np import openmdao.api as om 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, ref=0.04, rate_continuity=True) # steering angle phase.add_control(name='thrust', units=None, fix_initial=False, fix_final=False, rate_continuity=True) # 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'], static_target=True) # vehicle mass phase.add_parameter('beta', val=0.62, units=None, opt=False, targets=['tire.beta'], static_target=True) # brake bias phase.add_parameter('CoP', val=1.6, units='m', opt=False, targets=['normal.CoP'], static_target=True) # center of pressure location phase.add_parameter('h', val=0.3, units='m', opt=False, targets=['normal.h'], static_target=True) # center of gravity height phase.add_parameter('chi', val=0.5, units=None, opt=False, targets=['normal.chi'], static_target=True) # roll stiffness phase.add_parameter('ClA', val=4.0, units='m**2', opt=False, targets=['normal.ClA'], static_target=True) # downforce coefficient*area phase.add_parameter('CdA', val=2.0, units='m**2', opt=False, targets=['car.CdA'], static_target=True) # 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 # Nonzero velocity to avoid division by zero errors p.set_val('traj.phase0.states:V', phase.interp('V', [20, 20]), units='m/s') # All other states start at 0 p.set_val('traj.phase0.states:lambda', phase.interp('lambda', [0.0, 0.0]), units='rad') p.set_val('traj.phase0.states:omega', phase.interp('omega', [0.0, 0.0]), units='rad/s') p.set_val('traj.phase0.states:alpha', phase.interp('alpha', [0.0, 0.0]), units='rad') p.set_val('traj.phase0.states:ax', phase.interp('ax', [0.0, 0.0]), units='m/s**2') p.set_val('traj.phase0.states:ay', phase.interp('ay', [0.0, 0.0]), units='m/s**2') p.set_val('traj.phase0.states:n', phase.interp('n', [0.0, 0.0]), units='m') # initial guess for what the final time should be p.set_val('traj.phase0.states:t', phase.interp('t', [0.0, 100.0]), units='s') # Controls # a small amount of thrust can speed up convergence p.set_val('traj.phase0.controls:delta', phase.interp('delta', [0.0, 0.0]), units='rad') p.set_val('traj.phase0.controls:thrust', phase.interp('thrust', [0.1, 0.1]), units=None) dm.run_problem(p, run_driver=True) print('Optimization finished') # 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)
def brachistochrone_min_time(transcription='gauss-lobatto', num_segments=8, transcription_order=3, compressed=True, optimizer='SLSQP', dynamic_simul_derivs=True, force_alloc_complex=False, solve_segments=False, run_driver=True): p = om.Problem(model=om.Group()) if optimizer == 'SNOPT': p.driver = om.pyOptSparseDriver() p.driver.options['optimizer'] = optimizer p.driver.opt_settings['Major iterations limit'] = 100 p.driver.opt_settings['Major feasibility tolerance'] = 1.0E-6 p.driver.opt_settings['Major optimality tolerance'] = 1.0E-6 p.driver.opt_settings['iSumm'] = 6 else: p.driver = om.ScipyOptimizeDriver() if dynamic_simul_derivs: p.driver.declare_coloring() if transcription == 'gauss-lobatto': transcription = dm.GaussLobatto(num_segments=num_segments, order=transcription_order, compressed=compressed) fix_final = not solve_segments elif transcription == 'radau-ps': transcription = dm.Radau(num_segments=num_segments, order=transcription_order, compressed=compressed) fix_final = not solve_segments traj = dm.Trajectory() phase = dm.Phase(ode_class=BrachistochroneVectorStatesODE, transcription=transcription) traj.add_phase('phase0', phase) p.model.add_subsystem('traj0', traj) phase.set_time_options(fix_initial=True, duration_bounds=(.5, 10), units='s') # can't fix final position if you're solving the segments phase.add_state('pos', fix_initial=True, fix_final=fix_final, solve_segments=solve_segments, ref=[1, 1]) # phase.add_state('v', fix_initial=True, fix_final=False, solve_segments=solve_segments) # phase.add_control('theta', continuity=True, rate_continuity=True, units='deg', lower=0.01, upper=179.9) phase.add_parameter('g', opt=False, units='m/s**2', val=9.80665) if not fix_final: phase.add_boundary_constraint('pos', loc='final', equals=[10, 5]) # Minimize time at the end of the phase phase.add_objective('time', loc='final', scaler=10) p.model.linear_solver = om.DirectSolver() p.setup(check=True, force_alloc_complex=force_alloc_complex) p['traj0.phase0.t_initial'] = 0.0 p['traj0.phase0.t_duration'] = 1.8016 pos0 = [0, 10] posf = [10, 5] p['traj0.phase0.states:pos'] = phase.interp('pos', [pos0, posf]) p['traj0.phase0.states:v'] = phase.interp('v', [0, 9.9]) p['traj0.phase0.controls:theta'] = phase.interp('theta', [5, 100]) p['traj0.phase0.parameters:g'] = 9.80665 p.run_model() if run_driver: p.run_driver() return p
def test_brachistochrone_integrated_control_gauss_lobatto(self): import numpy as np import openmdao.api as om from openmdao.utils.assert_utils import assert_near_equal import dymos as dm p = om.Problem(model=om.Group()) p.driver = om.ScipyOptimizeDriver() phase = dm.Phase(ode_class=BrachistochroneODE, transcription=dm.GaussLobatto(num_segments=10)) p.model.add_subsystem('phase0', phase) phase.set_time_options(initial_bounds=(0, 0), duration_bounds=(.5, 10), units='s') phase.add_state('x', fix_initial=True, fix_final=True, rate_source='xdot', units='m') phase.add_state('y', fix_initial=True, fix_final=True, rate_source='ydot', units='m') phase.add_state('v', fix_initial=True, rate_source='vdot', units='m/s', targets=['v']) phase.add_state('theta', targets='theta', fix_initial=False, rate_source='theta_dot', units='rad') phase.add_control('theta_dot', units='deg/s', rate_continuity=True, lower=0, upper=60) phase.add_design_parameter('g', units='m/s**2', opt=False, val=9.80665, targets=['g']) # Minimize time at the end of the phase phase.add_objective('time', loc='final', scaler=10) p.model.linear_solver = om.DirectSolver() p.setup() p['phase0.t_initial'] = 0.0 p['phase0.t_duration'] = 2.0 p['phase0.states:x'] = phase.interpolate(ys=[0, 10], nodes='state_input') p['phase0.states:y'] = phase.interpolate(ys=[10, 5], nodes='state_input') p['phase0.states:v'] = phase.interpolate(ys=[0, 9.9], nodes='state_input') p['phase0.states:theta'] = np.radians( phase.interpolate(ys=[0.05, 100.0], nodes='state_input')) p['phase0.controls:theta_dot'] = phase.interpolate( ys=[50, 50], nodes='control_input') # Solve for the optimal trajectory p.run_driver() # Test the results assert_near_equal(p.get_val('phase0.timeseries.time')[-1], 1.8016, tolerance=1.0E-3) sim_out = phase.simulate(times_per_seg=20) x_sol = p.get_val('phase0.timeseries.states:x') y_sol = p.get_val('phase0.timeseries.states:y') v_sol = p.get_val('phase0.timeseries.states:v') theta_sol = p.get_val('phase0.timeseries.states:theta') theta_dot_sol = p.get_val('phase0.timeseries.controls:theta_dot') time_sol = p.get_val('phase0.timeseries.time') x_sim = sim_out.get_val('phase0.timeseries.states:x') y_sim = sim_out.get_val('phase0.timeseries.states:y') v_sim = sim_out.get_val('phase0.timeseries.states:v') theta_sim = sim_out.get_val('phase0.timeseries.states:theta') theta_dot_sim = sim_out.get_val('phase0.timeseries.controls:theta_dot') time_sim = sim_out.get_val('phase0.timeseries.time') x_interp = interp1d(time_sim[:, 0], x_sim[:, 0]) y_interp = interp1d(time_sim[:, 0], y_sim[:, 0]) v_interp = interp1d(time_sim[:, 0], v_sim[:, 0]) theta_interp = interp1d(time_sim[:, 0], theta_sim[:, 0]) theta_dot_interp = interp1d(time_sim[:, 0], theta_dot_sim[:, 0]) assert_near_equal(x_interp(time_sol), x_sol, tolerance=1.0E-5) assert_near_equal(y_interp(time_sol), y_sol, tolerance=1.0E-5) assert_near_equal(v_interp(time_sol), v_sol, tolerance=1.0E-5) assert_near_equal(theta_interp(time_sol), theta_sol, tolerance=1.0E-5) assert_near_equal(theta_dot_interp(time_sol), theta_dot_sol, tolerance=1.0E-5)
#p.driver.declare_coloring() # Define a Trajectory object #traj = dm.Trajectory() #p.model.add_subsystem('traj', subsys = traj) traj = p.model.add_subsystem('traj', subsys=dm.Trajectory()) # Define a Dymos Phase object with GaussLobatto Transcription # phase = dm.Phase(ode_class = ODESystem1, # transcription = dm.GaussLobatto(num_segments = 10, order = 3)) #traj.add_phase(name = 'phase0', phase = phase) phase = traj.add_phase( 'phase0', dm.Phase(ode_class=ODESystem1, transcription=dm.GaussLobatto(num_segments=n_segments))) # 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( initial_bounds=(0, 0), # 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.
def test_brachistochrone_base_phase_class_gl(self): import openmdao.api as om from openmdao.utils.assert_utils import assert_near_equal import dymos as dm class BrachistochronePhase(dm.Phase): def setup(self): self.options['ode_class'] = BrachistochroneODE self.set_time_options(fix_initial=True, duration_bounds=(.5, 10), units='s') self.add_state('x', fix_initial=True, rate_source='xdot') self.add_state('y', fix_initial=True, rate_source='ydot') self.add_state('v', fix_initial=True, rate_source='vdot', targets=['v']) self.add_control('theta', units='deg', rate_continuity=False, lower=0.01, upper=179.9, targets=['theta']) self.add_parameter('g', units='m/s**2', opt=False, val=9.80665, targets=['g']) super(BrachistochronePhase, self).setup() p = om.Problem(model=om.Group()) p.driver = om.ScipyOptimizeDriver() phase = BrachistochronePhase( transcription=dm.GaussLobatto(num_segments=20, order=3)) p.model.add_subsystem('phase0', phase) 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() p.setup() p['phase0.t_initial'] = 0.0 p['phase0.t_duration'] = 2.0 p['phase0.states:x'] = phase.interpolate(ys=[0, 10], nodes='state_input') p['phase0.states:y'] = phase.interpolate(ys=[10, 5], nodes='state_input') p['phase0.states:v'] = phase.interpolate(ys=[0, 9.9], nodes='state_input') p['phase0.controls:theta'] = phase.interpolate(ys=[5, 100.5], nodes='control_input') # Solve for the optimal trajectory p.run_driver() # Test the results assert_near_equal(p.get_val('phase0.timeseries.time')[-1], 1.8016, tolerance=1.0E-3) exp_out = phase.simulate() assert_near_equal(exp_out.get_val('phase0.timeseries.states:x')[-1], 10, tolerance=1.0E-3) assert_near_equal(exp_out.get_val('phase0.timeseries.states:y')[-1], 5, tolerance=1.0E-3)
def test_promotes_parameter(self): transcription = 'radau-ps' optimizer = 'SLSQP' num_segments = 10 transcription_order = 3 compressed = False p = om.Problem(model=om.Group()) p.driver = om.pyOptSparseDriver() OPT, OPTIMIZER = set_pyoptsparse_opt(optimizer, fallback=True) p.driver.options['optimizer'] = OPTIMIZER p.driver.declare_coloring() 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) traj = dm.Trajectory() phase = dm.Phase(ode_class=BrachistochroneODE, transcription=t) traj.add_phase('phase0', phase, promotes_inputs=['t_initial', 't_duration', 'parameters:g']) p.model.add_subsystem('traj', traj) phase.set_time_options(fix_initial=True, duration_bounds=(.5, 10), units='s') phase.add_state('x', fix_initial=True, fix_final=False, solve_segments=False, units='m', rate_source='xdot') phase.add_state('y', fix_initial=True, fix_final=False, solve_segments=False, units='m', rate_source='ydot') phase.add_state('v', fix_initial=True, fix_final=False, solve_segments=False, units='m/s', rate_source='vdot', targets=['v']) phase.add_control('theta', continuity=True, rate_continuity=True, units='deg', lower=0.01, upper=179.9, targets=['theta']) phase.add_parameter('g', units='m/s**2', val=9.80665, targets=['g']) 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.model.linear_solver = om.DirectSolver() p.setup(check=True) p.set_val('traj.t_initial', 0.0) p.set_val('traj.t_duration', 2.0) p.set_val('traj.phase0.states:x', phase.interp('x', ys=[0, 10])) p.set_val('traj.phase0.states:y', phase.interp('y', ys=[10, 5])) p.set_val('traj.phase0.states:v', phase.interp('v', ys=[0, 9.9])) p.set_val('traj.phase0.controls:theta', phase.interp('theta', ys=[5, 100])) p.set_val('traj.parameters:g', 9.80665) p.run_driver() assert_near_equal(p['traj.t_duration'], 1.8016, tolerance=1.0E-4)
def test_brachistochrone_quick_start(self): import numpy as np import openmdao.api as om import dymos as dm import matplotlib matplotlib.use('Agg') import matplotlib.pyplot as plt # # 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 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.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=[90, 90], nodes='control_input'), units='deg') # Run the driver to solve the problem p.run_driver() # 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_brachistochrone_upstream_control(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 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('control_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('theta', shape=(tx.grid_data.subset_num_nodes['control_input']), units='rad') # Add this external control as a design variable p.model.add_design_var('theta', units='rad', lower=1.0E-5, upper=np.pi) # Connect this to controls:theta in the appropriate phase. # connect calls are cached, so we can do this before we actually add the trajectory to the problem. p.model.connect('theta', 'traj.phase0.controls:theta') # # 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=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=True, 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', targets=['theta'], opt=False) # 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.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=[90, 90], nodes='control_input'), units='deg') # Run the driver to solve the problem p.run_driver() # 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 run_takeoff_no_transition(airplane, runway, flap_angle=0.0, wind_speed=0.0): p = om.Problem(model=om.Group()) p.driver = om.pyOptSparseDriver() p.driver.options['optimizer'] = 'SLSQP' p.driver.declare_coloring() p.model.linear_solver = om.DirectSolver() traj = p.model.add_subsystem('traj', dm.Trajectory()) # --------------------------------------------- Initial Run -------------------------------------------------------- initial_run = dm.Phase(ode_class=InitialRunODE, transcription=dm.GaussLobatto(num_segments=20, compressed=False), ode_init_kwargs={'airplane': airplane}) traj.add_phase('initial_run', initial_run) initial_run.set_time_options(fix_initial=True, units='s', duration_bounds=(10, 100)) # Initial run states initial_run.add_state(name='V', units='m/s', rate_source='initial_run_eom.v_dot', targets=['V'], fix_initial=True, fix_final=False, lower=0) initial_run.add_state(name='x', units='m', rate_source='initial_run_eom.x_dot', targets=['mlg_pos.x'], fix_initial=True, fix_final=False, lower=airplane.landing_gear.main.x) initial_run.add_state(name='mass', units='kg', rate_source='prop.m_dot', targets=['mass'], fix_initial=False, fix_final=False, lower=0.0, upper=airplane.limits.MTOW) # Initial run parameters initial_run.add_parameter(name='de', val=0.0, units='deg', desc='Elevator deflection', targets=['aero.de'], opt=False) initial_run.add_parameter( name='theta', val=0.0, units='deg', desc='Pitch Angle', targets=['aero.alpha', 'initial_run_eom.alpha', 'mlg_pos.theta'], opt=False) initial_run.add_parameter(name='h', val=0.0, units='m', desc='Vertical CG position', targets=['mlg_pos.h'], opt=False) # path constraint initial_run.add_path_constraint(name='initial_run_eom.f_mg', lower=0, units='N') initial_run.add_path_constraint(name='initial_run_eom.f_ng', lower=0, units='N') initial_run.add_boundary_constraint(name='initial_run_eom.f_ng', loc='final', units='N', lower=0.0, upper=0.5, shape=(1, )) initial_run.add_objective('mass', loc='initial', scaler=-1) initial_run.add_timeseries_output('initial_run_eom.f_mg', units='kN') initial_run.add_timeseries_output('initial_run_eom.f_ng', units='kN') initial_run.add_timeseries_output('aero.CL') initial_run.add_timeseries_output('aero.CD') initial_run.add_timeseries_output('aero.Cm') # --------------------------------------------- Rotation ----------------------------------------------------------- rotation = dm.Phase(ode_class=RotationODE, transcription=dm.GaussLobatto(num_segments=5, compressed=False), ode_init_kwargs={'airplane': airplane}) traj.add_phase(name='rotation', phase=rotation) rotation.set_time_options(fix_initial=False, units='s', duration_bounds=(1, 6)) # Rotation states rotation.add_state(name='V', units='m/s', rate_source='rotation_eom.v_dot', targets=['V'], fix_initial=False, fix_final=False, lower=0) rotation.add_state(name='x', units='m', rate_source='rotation_eom.x_dot', targets=['mlg_pos.x'], fix_initial=False, fix_final=False, lower=0) rotation.add_state(name='h', units='m', rate_source='rotation_eom.h_dot', targets=['mlg_pos.h'], fix_initial=True, fix_final=False) rotation.add_state(name='mass', units='kg', rate_source='prop.m_dot', targets=['mass'], fix_initial=False, fix_final=False, lower=0.0) rotation.add_state( name='theta', units='deg', rate_source='rotation_eom.theta_dot', targets=['aero.alpha', 'rotation_eom.alpha', 'mlg_pos.theta'], fix_initial=True, fix_final=False, lower=0.0) rotation.add_state(name='q', units='deg/s', rate_source='rotation_eom.q_dot', targets=['q'], fix_initial=True, fix_final=False, lower=0.0) # Rotation controls rotation.add_control(name='de', units='deg', lower=-30.0, upper=30.0, targets=['aero.de'], fix_initial=True, rate_continuity=True) # Rotation path constraints rotation.add_path_constraint(name='rotation_eom.f_mg', lower=0, units='N') # Rotation boundary constraint rotation.add_boundary_constraint(name='rotation_eom.f_mg', loc='final', units='N', lower=0.0, upper=0.5, shape=(1, )) rotation.add_boundary_constraint(name='x', loc='final', units='m', upper=runway.tora, shape=(1, )) rotation.add_timeseries_output('rotation_eom.f_mg', units='kN') rotation.add_timeseries_output('aero.CL') rotation.add_timeseries_output('aero.CD') rotation.add_timeseries_output('aero.Cm') # ---------------------------------------- Trajectory Parameters --------------------------------------------------- traj.add_parameter(name='dih', val=0.0, units='deg', lower=-5.0, upper=5.0, desc='Horizontal stabilizer angle', targets={ 'initial_run': ['aero.dih'], 'rotation': ['aero.dih'], }, opt=True, dynamic=False) traj.add_parameter( name='Vw', val=0.0, units='m/s', desc='Wind speed along the runway, defined as positive for a headwind', targets={ 'initial_run': ['Vw'], 'rotation': ['Vw'], }, opt=False, dynamic=False) traj.add_parameter(name='flap_angle', val=0.0, units='deg', desc='Flap defletion', targets={ 'initial_run': ['aero.flap_angle'], 'rotation': ['aero.flap_angle'], }, opt=False, dynamic=False) traj.add_parameter(name='elevation', val=0.0, units='m', desc='Runway elevation', targets={ 'initial_run': ['elevation'], 'rotation': ['elevation'], }, opt=False, dynamic=False) traj.add_parameter(name='rw_slope', val=0.0, units='rad', desc='Runway slope', targets={ 'initial_run': ['initial_run_eom.rw_slope'], 'rotation': ['rotation_eom.rw_slope'], }, opt=False, dynamic=False) # ------------------------------------------------ Link Phases ----------------------------------------------------- traj.link_phases(phases=['initial_run', 'rotation'], vars=['time', 'V', 'x', 'mass']) p.setup(check=True) p.set_val('traj.initial_run.t_initial', 0) p.set_val('traj.initial_run.t_duration', 60) p.set_val('traj.rotation.t_initial', 60) p.set_val('traj.rotation.t_duration', 3) p.set_val('traj.parameters:elevation', runway.elevation) p.set_val('traj.parameters:rw_slope', runway.slope) p.set_val('traj.parameters:flap_angle', flap_angle) p.set_val('traj.parameters:dih', 0.0) p.set_val('traj.parameters:Vw', wind_speed) p['traj.initial_run.states:x'] = initial_run.interpolate( ys=[airplane.landing_gear.main.x, 0.7 * runway.tora], nodes='state_input') p['traj.initial_run.states:V'] = initial_run.interpolate( ys=[0, 70], nodes='state_input') p['traj.initial_run.states:mass'] = initial_run.interpolate( ys=[airplane.limits.MTOW, airplane.limits.MTOW - 100], nodes='state_input') p['traj.initial_run.parameters:h'] = airplane.landing_gear.main.z p['traj.rotation.states:x'] = rotation.interpolate( ys=[0.7 * runway.tora, 0.8 * runway.tora], nodes='state_input') p['traj.rotation.states:V'] = rotation.interpolate(ys=[70, 80], nodes='state_input') p['traj.rotation.states:mass'] = rotation.interpolate( ys=[airplane.limits.MTOW - 100, airplane.limits.MTOW - 200], nodes='state_input') p['traj.rotation.states:h'] = airplane.landing_gear.main.z p['traj.rotation.states:q'] = rotation.interpolate(ys=[0.0, 10.0], nodes='state_input') p['traj.rotation.states:theta'] = rotation.interpolate(ys=[0.0, 15.0], nodes='state_input') p['traj.rotation.controls:de'] = rotation.interpolate( ys=[0.0, -20.0], nodes='control_input') dm.run_problem(p) sim_out = traj.simulate() print( f"RTOW: {p.get_val('traj.initial_run.timeseries.states:mass', units='kg')[0]} kg" ) print( f"Rotation speed (VR): {p.get_val('traj.initial_run.timeseries.states:V', units='kn')[-1]} kn" ) print( f"Vlof speed (Vlof): {p.get_val('traj.rotation.timeseries.states:V', units='kn')[-1]} kn" ) print(f"Horizontal stabilizer: {p.get_val('traj.parameters:dih')} deg") return p, sim_out
import openmdao.api as om import dymos as dm from donner_sub_ode import DonnerSubODE # Create the problem p = om.Problem(model=om.Group()) # Add the trajectory (optional for single phase problems) traj = dm.Trajectory() # Create the phases phase0 = dm.Phase(ode_class=DonnerSubODE, transcription=dm.GaussLobatto(num_segments=10, order=3, compressed=True, solve_segments=True)) phase1 = dm.Phase(ode_class=DonnerSubODE, transcription=dm.Radau(num_segments=10, order=3, compressed=True, solve_segments=True)) # Add the phase to the trajectory, and the trajectory to the model traj.add_phase('phase0', phase=phase0) traj.add_phase('phase1', phase=phase1) traj.link_phases(phases=['phase0', 'phase1'], vars=['time', 'x', 'y', 'phi']) p.model.add_subsystem('traj', traj) #
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) phase.add_boundary_constraint('aero.mach', loc='final', equals=1.0) phase.add_boundary_constraint('gam', loc='final', equals=0.0) 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) # 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 min_time_climb(num_seg=3, transcription_order=3, force_alloc_complex=False): p = om.Problem(model=om.Group()) tx = dm.GaussLobatto(num_segments=num_seg, order=transcription_order) traj = dm.Trajectory() phase = dm.Phase(ode_class=MinTimeClimbODE, transcription=tx) traj.add_phase('phase0', phase) p.model.add_subsystem('traj', traj) 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_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']) phase.add_boundary_constraint('h', loc='final', equals=20000, scaler=1.0E-3) phase.add_boundary_constraint('aero.mach', loc='final', equals=1.0) phase.add_boundary_constraint('gam', loc='final', equals=0.0) 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) # Unnecessary but included to test capability phase.add_path_constraint(name='alpha', lower=-8, upper=8) phase.add_path_constraint(name='time', lower=0, upper=400) phase.add_path_constraint(name='time_phase', lower=0, upper=400) # Minimize time at the end of the phase phase.add_objective('time', loc='final', ref=1.0) p.model.linear_solver = om.DirectSolver() p.setup(check=True, force_alloc_complex=force_alloc_complex) p['traj.phase0.t_initial'] = 0.0 p['traj.phase0.t_duration'] = 300.0 p['traj.phase0.states:r'] = phase.interp('r', [0.0, 111319.54]) p['traj.phase0.states:h'] = phase.interp('h', [100.0, 20000.0]) p['traj.phase0.states:v'] = phase.interp('v', [135.964, 283.159]) p['traj.phase0.states:gam'] = phase.interp('gam', [0.0, 0.0]) p['traj.phase0.states:m'] = phase.interp('m', [19030.468, 16841.431]) p['traj.phase0.controls:alpha'] = phase.interp('alpha', [0.0, 0.0]) return p
def test_simulate_array_param(self): # # 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(initial_bounds=(0, 0), duration_bounds=(.5, 10)) 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') 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) phase.add_parameter('array', units=None, shape=(10,), dynamic=False) # dummy array of data indeps = p.model.add_subsystem('indeps', om.IndepVarComp(), promotes=['*']) indeps.add_output('array', np.linspace(1, 10, 10), units=None) # add dummy array as a parameter and connect it p.model.connect('array', 'traj.phase0.parameters:array') # # 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, simulate=True) # Test the results sol_results = om.CaseReader('dymos_solution.db').get_case('final') sim_results = om.CaseReader('dymos_solution.db').get_case('final') sol = sol_results.get_val('traj.phase0.timeseries.parameters:array') sim = sim_results.get_val('traj.phase0.timeseries.parameters:array') assert_near_equal(sol - sim, np.zeros_like(sol)) # Test that the parameter is available in the solution and simulation files sol = sol_results.get_val('traj.phase0.parameters:array') sim = sim_results.get_val('traj.phase0.parameters:array') assert_near_equal(sol - sim, np.zeros_like(sol))
def setup_problem( trans=dm.GaussLobatto(num_segments=10), polynomial_control=False): from dymos.examples.brachistochrone.brachistochrone_ode import BrachistochroneODE from dymos.transcriptions.runge_kutta.runge_kutta import RungeKutta p = om.Problem(model=om.Group()) if isinstance(trans, RungeKutta): p.driver = om.pyOptSparseDriver() else: p.driver = om.ScipyOptimizeDriver() phase = dm.Phase(ode_class=BrachistochroneODE, transcription=trans) p.model.add_subsystem('phase0', phase) phase.set_time_options(initial_bounds=(0, 0), duration_bounds=(.5, 10)) phase.add_state('x', fix_initial=True, fix_final=not isinstance(trans, RungeKutta), rate_source=BrachistochroneODE.states['x']['rate_source'], units=BrachistochroneODE.states['x']['units']) phase.add_state('y', fix_initial=True, fix_final=not isinstance(trans, RungeKutta), rate_source=BrachistochroneODE.states['y']['rate_source'], units=BrachistochroneODE.states['y']['units']) phase.add_state('v', fix_initial=True, rate_source=BrachistochroneODE.states['v']['rate_source'], units=BrachistochroneODE.states['v']['units']) if not polynomial_control: phase.add_control('theta', units='deg', rate_continuity=False, lower=0.01, upper=179.9) else: phase.add_polynomial_control('theta', order=1, units='deg', lower=0.01, upper=179.9) phase.add_parameter('g', units='m/s**2', opt=False, val=9.80665) if isinstance(trans, RungeKutta): phase.add_timeseries_output('check', units='m/s', shape=(1, )) 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() p.setup() p['phase0.t_initial'] = 0.0 p['phase0.t_duration'] = 2.0 p['phase0.states:x'] = phase.interpolate(ys=[0, 10], nodes='state_input') p['phase0.states:y'] = phase.interpolate(ys=[10, 5], nodes='state_input') p['phase0.states:v'] = phase.interpolate(ys=[0, 9.9], nodes='state_input') if not polynomial_control: p['phase0.controls:theta'] = phase.interpolate(ys=[5, 100.5], nodes='control_input') else: p['phase0.polynomial_controls:theta'][:] = 5.0 return p