def setUp(self): self.p = Problem(model=Group()) self.p.driver = pyOptSparseDriver() self.p.driver.options['optimizer'] = OPTIMIZER if OPTIMIZER == 'SNOPT': self.p.driver.opt_settings['Major iterations limit'] = 50 self.p.driver.opt_settings['iSumm'] = 6 self.p.driver.opt_settings['Verify level'] = 3 phase = Phase(ode_class=_CannonballODE, transcription=GaussLobatto(num_segments=15, order=3, compressed=False)) self.p.model.add_subsystem('phase0', phase) phase.set_time_options(initial_bounds=(0, 0), duration_bounds=(10, 20)) phase.set_state_options('r', fix_initial=True, fix_final=False, scaler=0.001, defect_scaler=0.001) phase.set_state_options('h', fix_initial=True, fix_final=True, # Require final altitude scaler=0.001, defect_scaler=0.001) phase.set_state_options('v', fix_initial=True, fix_final=False, scaler=0.01, defect_scaler=0.01) phase.set_state_options('gam', fix_final=False, scaler=1.0, defect_scaler=1.0) # Maximize final range by varying initial flight path angle phase.add_objective('r', loc='final', scaler=-0.01)
def test_initial_val_and_final_val_stick(self): p = Problem(model=Group()) p.driver = ScipyOptimizeDriver() p.driver.options['dynamic_simul_derivs'] = True phase = Phase(ode_class=BrachistochroneODE, transcription=GaussLobatto(num_segments=8, order=3)) p.model.add_subsystem('phase0', phase) phase.set_time_options(fix_initial=False, fix_duration=False, initial_val=0.01, duration_val=1.9) phase.set_state_options('x', fix_initial=True, fix_final=True) phase.set_state_options('y', fix_initial=True, fix_final=True) phase.set_state_options('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_design_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) phase.add_boundary_constraint('time', loc='initial', equals=0) p.model.linear_solver = DirectSolver() p.setup(check=True) assert_rel_error(self, p['phase0.t_initial'], 0.01) assert_rel_error(self, p['phase0.t_duration'], 1.9)
def __init__(self, body, sc, method, nb_seg, order, solver, ode_class, ode_kwargs, ph_name, snopt_opts=None, rec_file=None): NLP.__init__(self, body, sc, method, nb_seg, order, solver, snopt_opts=snopt_opts, rec_file=rec_file) # Transcription object if self.method == 'gauss-lobatto': self.transcription = GaussLobatto(num_segments=self.nb_seg, order=self.order, compressed=True) elif self.method == 'radau-ps': self.transcription = Radau(num_segments=self.nb_seg, order=self.order, compressed=True) elif self.method == 'runge-kutta': self.transcription = RungeKutta(num_segments=self.nb_seg, order=self.order, compressed=True) else: raise ValueError('method must be either gauss-lobatto or radau-ps') # Phase object self.phase = self.trajectory.add_phase(ph_name, Phase(ode_class=ode_class, ode_init_kwargs=ode_kwargs, transcription=self.transcription)) self.phase_name = ''.join(['traj.', ph_name]) # discretization nodes self.state_nodes = None self.control_nodes = None self.t_all = None self.t_state = None self.t_control = None self.idx_state_control = None # time of flight self.tof = None
def __init__(self, body, sc, method, nb_seg, order, solver, ode_class, ode_kwargs, ph_name, snopt_opts=None, rec_file=None): if isinstance(order, int): order = tuple(order for _ in range(len(nb_seg))) if isinstance(method, str): method = tuple(method for _ in range(len(nb_seg))) NLP.__init__(self, body, sc, method, nb_seg, order, solver, snopt_opts=snopt_opts, rec_file=rec_file) # Transcription objects list self.transcription = [] for i in range(len(self.nb_seg)): if self.method[i] == 'gauss-lobatto': t = GaussLobatto(num_segments=self.nb_seg[i], order=self.order[i], compressed=True) elif self.method[i] == 'radau-ps': t = Radau(num_segments=self.nb_seg[i], order=self.order[i], compressed=True) elif self.method[i] == 'runge-kutta': t = RungeKutta(num_segments=self.nb_seg[i], order=self.order[i], compressed=True) else: raise ValueError('method must be either gauss-lobatto, radau-ps or runge-kutta') self.transcription.append(t) # Phase objects list self.phase = [] self.phase_name = [] for i in range(len(self.nb_seg)): ph = self.trajectory.add_phase(ph_name[i], Phase(ode_class=ode_class[i], ode_init_kwargs=ode_kwargs[i], transcription=self.transcription[i])) self.phase.append(ph) self.phase_name.append(''.join(['traj.', ph_name[i]]))
def test_control_rate_path_constraint_gl(self): from openmdao.api import Problem, Group, ScipyOptimizeDriver, DirectSolver from openmdao.utils.assert_utils import assert_rel_error from dymos import Phase, GaussLobatto from dymos.examples.brachistochrone.brachistochrone_ode import BrachistochroneODE p = Problem(model=Group()) p.driver = ScipyOptimizeDriver() phase = Phase(ode_class=BrachistochroneODE, transcription=GaussLobatto(num_segments=10)) p.model.add_subsystem('phase0', phase) phase.set_time_options(initial_bounds=(0, 0), duration_bounds=(.5, 10)) phase.set_state_options('x', fix_initial=True, fix_final=True) phase.set_state_options('y', fix_initial=True, fix_final=True) phase.set_state_options('v', fix_initial=True) phase.add_control('theta', units='deg', rate_continuity=False, lower=0.01, upper=179.9) phase.add_design_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) phase.add_path_constraint('theta_rate', lower=0, upper=100, units='deg/s') p.model.linear_solver = 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_rel_error(self, p.get_val('phase0.timeseries.time')[-1], 1.8016, tolerance=1.0E-3)
def test_invalid_boundary_loc(self): p = Phase(ode_class=BrachistochroneODE, transcription=GaussLobatto(num_segments=8, order=3, compressed=True)) with self.assertRaises(ValueError) as e: p.add_boundary_constraint('x', loc='foo') expected = 'Invalid boundary constraint location "foo". Must be "initial" or "final".' self.assertEqual(str(e.exception), expected)
def test_add_existing_control_as_design_parameter(self): p = Phase(ode_class=BrachistochroneODE, transcription=GaussLobatto(num_segments=8, order=3)) p.add_control('theta') with self.assertRaises(ValueError) as e: p.add_design_parameter('theta') expected = 'theta has already been added as a control.' self.assertEqual(str(e.exception), expected)
def test_add_existing_design_parameter_as_input_parameter(self): p = Phase(ode_class=_A, transcription=GaussLobatto(num_segments=14, order=3, compressed=True)) p.add_design_parameter('theta') with self.assertRaises(ValueError) as e: p.add_input_parameter('theta') expected = 'theta has already been added as a design parameter.' self.assertEqual(str(e.exception), expected)
def test_objective_design_parameter_gl(self): p = Problem(model=Group()) p.driver = ScipyOptimizeDriver() p.driver.options['dynamic_simul_derivs'] = True phase = Phase(ode_class=BrachistochroneODE, transcription=GaussLobatto(num_segments=8, order=3, compressed=True)) p.model.add_subsystem('phase0', phase) phase.set_time_options(fix_initial=True, duration_bounds=(4, 10)) phase.set_state_options('x', fix_initial=True, fix_final=True) phase.set_state_options('y', fix_initial=True, fix_final=True) phase.set_state_options('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_design_parameter('g', units='m/s**2', opt=True, val=9.80665) # Minimize time at the end of the phase phase.add_objective('g') p.model.linear_solver = DirectSolver() p.setup(check=True) 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], nodes='control_input') p['phase0.design_parameters:g'] = 9.80665 p.run_driver() assert_rel_error(self, p['phase0.t_duration'], 10, tolerance=1.0E-3)
def test_invalid_options_nonoptimal_design_param(self): p = Problem(model=Group()) p.driver = ScipyOptimizeDriver() p.driver.options['dynamic_simul_derivs'] = True phase = Phase(ode_class=BrachistochroneODE, transcription=GaussLobatto(num_segments=16, order=3, compressed=True)) p.model.add_subsystem('phase0', phase) phase.set_time_options(fix_initial=True, duration_bounds=(4, 10)) phase.set_state_options('x', fix_initial=True, fix_final=True) phase.set_state_options('y', fix_initial=True, fix_final=True) phase.set_state_options('v', fix_initial=True, fix_final=False) phase.add_control('theta', continuity=True, rate_continuity=True, opt=False) phase.add_design_parameter('g', units='m/s**2', opt=False, lower=5, upper=10, ref0=5, ref=10, scaler=1, adder=0) # Minimize time at the end of the phase phase.add_objective('g') p.model.linear_solver = DirectSolver() with warnings.catch_warnings(record=True) as w: warnings.simplefilter('always') p.setup(check=False) print('\n'.join([str(ww.message) for ww in w])) expected = 'Invalid options for non-optimal design_parameter \'g\' in phase \'phase0\': ' \ 'lower, upper, scaler, adder, ref, ref0' self.assertIn(expected, [str(ww.message) for ww in w])
def test_fixed_time_invalid_options(self): p = Problem(model=Group()) p.driver = ScipyOptimizeDriver() p.driver.options['dynamic_simul_derivs'] = True phase = Phase(ode_class=BrachistochroneODE, transcription=GaussLobatto(num_segments=8, order=3)) p.model.add_subsystem('phase0', phase) phase.set_time_options(fix_initial=True, fix_duration=True, initial_bounds=(1.0, 5.0), initial_adder=0.0, initial_scaler=1.0, initial_ref0=0.0, initial_ref=1.0, duration_bounds=(1.0, 5.0), duration_adder=0.0, duration_scaler=1.0, duration_ref0=0.0, duration_ref=1.0) phase.set_state_options('x', fix_initial=True, fix_final=True) phase.set_state_options('y', fix_initial=True, fix_final=True) phase.set_state_options('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_design_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) phase.add_boundary_constraint('time', loc='initial', equals=0) p.model.linear_solver = DirectSolver() expected_msg0 = 'Phase time options have no effect because fix_initial=True for ' \ 'phase \'phase0\': initial_bounds, initial_scaler, initial_adder, ' \ 'initial_ref, initial_ref0' expected_msg1 = 'Phase time options have no effect because fix_duration=True for' \ ' phase \'phase0\': duration_bounds, duration_scaler, ' \ 'duration_adder, duration_ref, duration_ref0' with warnings.catch_warnings(record=True) as ctx: warnings.simplefilter('always') p.setup(check=True) self.assertIn(expected_msg0, [str(w.message) for w in ctx]) self.assertIn(expected_msg1, [str(w.message) for w in ctx])
def test_ex_double_integrator_input_and_fixed_times_warns(self): """ Tests that time optimization options cause a ValueError to be raised when t_initial and t_duration are connected to external sources. """ p = Problem(model=Group()) p.driver = ScipyOptimizeDriver() p.driver.options['dynamic_simul_derivs'] = True phase = Phase(ode_class=BrachistochroneODE, transcription=GaussLobatto(num_segments=8, order=3)) p.model.add_subsystem('phase0', phase) phase.set_time_options(input_initial=True, fix_initial=True, input_duration=True, fix_duration=True) phase.set_state_options('x', fix_initial=True, fix_final=True) phase.set_state_options('y', fix_initial=True, fix_final=True) phase.set_state_options('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_design_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) phase.add_boundary_constraint('time', loc='initial', equals=0) p.model.linear_solver = DirectSolver() with warnings.catch_warnings(record=True) as ctx: warnings.simplefilter('always') p.setup(check=True) expected_msg0 = 'Phase \'phase0\' initial time is an externally-connected input, therefore ' \ 'fix_initial has no effect.' expected_msg1 = 'Phase \'phase0\' time duration is an externally-connected input, ' \ 'therefore fix_duration has no effect.' self.assertIn(expected_msg0, [str(w.message) for w in ctx]) self.assertIn(expected_msg1, [str(w.message) for w in ctx])
def _make_problem(self, transcription, num_seg, transcription_order=3): p = Problem(model=Group()) p.driver = ScipyOptimizeDriver() # Compute sparsity/coloring when run_driver is called p.driver.options['dynamic_simul_derivs'] = True t = {'gauss-lobatto': GaussLobatto(num_segments=num_seg, order=transcription_order), 'radau-ps': Radau(num_segments=num_seg, order=transcription_order), 'runge-kutta': RungeKutta(num_segments=num_seg)} phase = 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)) phase.set_state_options('x', fix_initial=True) phase.set_state_options('y', fix_initial=True) phase.set_state_options('v', fix_initial=True) phase.add_control('theta', units='deg', rate_continuity=True, lower=0.01, upper=179.9) phase.add_design_parameter('g', units='m/s**2', opt=False, 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', loc='final', scaler=10) p.model.linear_solver = DirectSolver() p.setup() p['phase0.t_initial'] = 1.0 p['phase0.t_duration'] = 3.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') return p
def test_gauss_lobatto(self): p = Problem(model=Group()) phase = Phase(ode_class=MyODE, ode_init_kwargs={'n_traj': n_traj}, transcription=GaussLobatto(num_segments=25, order=3, compressed=True)) p.model.add_subsystem('phase0', phase) phase.add_input_parameter('alpha', val=np.ones((n_traj, 2)), units='m') try: p.setup() except Exception as e: self.fail('Exception encountered in setup:\n' + str(e))
def test_unbounded_time(self): p = Problem(model=Group()) p.driver = ScipyOptimizeDriver() p.driver.options['dynamic_simul_derivs'] = True phase = Phase(ode_class=BrachistochroneODE, transcription=GaussLobatto(num_segments=8, order=3)) p.model.add_subsystem('phase0', phase) phase.set_time_options(fix_initial=False, fix_duration=False) phase.set_state_options('x', fix_initial=True, fix_final=True) phase.set_state_options('y', fix_initial=True, fix_final=True) phase.set_state_options('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_design_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) phase.add_boundary_constraint('time', loc='initial', equals=0) p.model.linear_solver = DirectSolver() p.setup(check=True) 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], nodes='control_input') p['phase0.design_parameters:g'] = 9.80665 p.run_driver() self.assertTrue(p.driver.result.success, msg='Brachistochrone with outbounded times has failed')
def test_invalid_ode_wrong_class(self): p = Problem(model=Group()) p.driver = ScipyOptimizeDriver() p.driver.options['dynamic_simul_derivs'] = True phase = Phase(ode_class=_A, transcription=GaussLobatto(num_segments=20, order=3, compressed=True)) p.model.add_subsystem('phase0', phase) phase.set_time_options(fix_initial=True, duration_bounds=(4, 10)) phase.set_state_options('x', fix_initial=True, fix_final=True) phase.set_state_options('y', fix_initial=True, fix_final=True) phase.set_state_options('v', fix_initial=True, fix_final=False) phase.add_control('theta', continuity=True, rate_continuity=True, opt=False) phase.add_design_parameter('g', units='m/s**2', opt=True, val=9.80665) # Minimize time at the end of the phase phase.add_objective('g') p.model.linear_solver = DirectSolver() with self.assertRaises(ValueError) as e: p.setup(check=True) self.assertEqual( str(e.exception), 'ode_class must be derived from openmdao.core.System.')
def test_timeseries_gl(self): p = Problem(model=Group()) p.driver = ScipyOptimizeDriver() p.driver.options['dynamic_simul_derivs'] = True phase = Phase(ode_class=BrachistochroneODE, transcription=GaussLobatto(num_segments=8, order=3, compressed=True)) p.model.add_subsystem('phase0', phase) phase.set_time_options(fix_initial=True, duration_bounds=(.5, 10)) phase.set_state_options('x', fix_initial=True, fix_final=True) phase.set_state_options('y', fix_initial=True, fix_final=True) phase.set_state_options('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_design_parameter('g', units='m/s**2', opt=False, val=9.80665) # Minimize time at the end of the phase phase.add_objective('time_phase', loc='final', scaler=10) p.model.linear_solver = DirectSolver() p.setup(check=True) 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], nodes='control_input') p['phase0.design_parameters:g'] = 9.80665 p.run_driver() gd = phase.options['transcription'].grid_data state_input_idxs = gd.subset_node_indices['state_input'] control_input_idxs = gd.subset_node_indices['control_input'] col_idxs = gd.subset_node_indices['col'] assert_rel_error(self, p.get_val('phase0.time'), p.get_val('phase0.timeseries.time')[:, 0]) assert_rel_error(self, p.get_val('phase0.time_phase'), p.get_val('phase0.timeseries.time_phase')[:, 0]) for state in ('x', 'y', 'v'): assert_rel_error( self, p.get_val('phase0.states:{0}'.format(state)), p.get_val('phase0.timeseries.states:' '{0}'.format(state))[state_input_idxs]) assert_rel_error( self, p.get_val('phase0.state_interp.state_col:{0}'.format(state)), p.get_val('phase0.timeseries.states:' '{0}'.format(state))[col_idxs]) for control in ('theta', ): assert_rel_error( self, p.get_val('phase0.controls:{0}'.format(control)), p.get_val('phase0.timeseries.controls:' '{0}'.format(control))[control_input_idxs]) for dp in ('g', ): for i in range(gd.subset_num_nodes['all']): assert_rel_error( self, p.get_val('phase0.design_parameters:{0}'.format(dp))[0, :], p.get_val('phase0.timeseries.design_parameters:{0}'.format( dp))[i])
def test_reentry_gauss_lobatto_dymos(self): p = Problem(model=Group()) p.driver = pyOptSparseDriver() p.driver.declare_coloring() traj = p.model.add_subsystem("traj", Trajectory()) phase0 = traj.add_phase( "phase0", Phase(ode_class=ShuttleODE, transcription=GaussLobatto(num_segments=50, order=3))) phase0.set_time_options(fix_initial=True, units="s", duration_ref=200) phase0.set_state_options("h", fix_initial=True, fix_final=True, units="ft", rate_source="hdot", targets=["h"], lower=0, ref0=75000, ref=300000, defect_ref=1000) phase0.set_state_options("gamma", fix_initial=True, fix_final=True, units="rad", rate_source="gammadot", targets=["gamma"], lower=-89. * np.pi / 180, upper=89. * np.pi / 180) phase0.set_state_options("phi", fix_initial=True, fix_final=False, units="rad", rate_source="phidot", lower=0, upper=89. * np.pi / 180) phase0.set_state_options("psi", fix_initial=True, fix_final=False, units="rad", rate_source="psidot", targets=["psi"], lower=0, upper=90. * np.pi / 180) phase0.set_state_options("theta", fix_initial=True, fix_final=False, units="rad", rate_source="thetadot", targets=["theta"], lower=-89. * np.pi / 180, upper=89. * np.pi / 180) phase0.set_state_options("v", fix_initial=True, fix_final=True, units="ft/s", rate_source="vdot", targets=["v"], lower=0, ref0=2500, ref=25000) phase0.add_control("alpha", units="rad", opt=True, lower=-np.pi / 2, upper=np.pi / 2, targets=["alpha"]) phase0.add_control("beta", units="rad", opt=True, lower=-89 * np.pi / 180, upper=1 * np.pi / 180, targets=["beta"]) phase0.add_path_constraint("q", lower=0, upper=70, units="Btu/ft**2/s", ref=70) phase0.add_objective("theta", loc="final", ref=-0.01) p.driver.options["optimizer"] = 'SNOPT' p.driver.opt_settings["iSumm"] = 6 p.setup(check=True) p.set_val("traj.phase0.states:h", phase0.interpolate(ys=[260000, 80000], nodes="state_input"), units="ft") p.set_val("traj.phase0.states:gamma", phase0.interpolate(ys=[-1 * np.pi / 180, -5 * np.pi / 180], nodes="state_input"), units="rad") p.set_val("traj.phase0.states:phi", phase0.interpolate(ys=[0, 75 * np.pi / 180], nodes="state_input"), units="rad") p.set_val("traj.phase0.states:psi", phase0.interpolate(ys=[90 * np.pi / 180, 10 * np.pi / 180], nodes="state_input"), units="rad") p.set_val("traj.phase0.states:theta", phase0.interpolate(ys=[0, 25 * np.pi / 180], nodes="state_input"), units="rad") p.set_val("traj.phase0.states:v", phase0.interpolate(ys=[25600, 2500], nodes="state_input"), units="ft/s") p.set_val("traj.phase0.t_initial", 0, units="s") p.set_val("traj.phase0.t_duration", 2000, units="s") p.set_val("traj.phase0.controls:alpha", phase0.interpolate( ys=[17.4 * np.pi / 180, 17.4 * np.pi / 180], nodes="control_input"), units="rad") p.set_val("traj.phase0.controls:beta", phase0.interpolate(ys=[-75 * np.pi / 180, 0 * np.pi / 180], nodes="control_input"), units="rad") p.run_driver() print(p.get_val("traj.phase0.timeseries.time")[-1]) print(p.get_val("traj.phase0.timeseries.states:theta")[-1]) assert_rel_error(self, p.get_val("traj.phase0.timeseries.time")[-1], 2181.88191719, tolerance=1e-3) assert_rel_error(self, p.get_val("traj.phase0.timeseries.states:theta")[-1], .53440955, tolerance=1e-3)
def test_brachistochrone_recording(self): import matplotlib matplotlib.use('Agg') from openmdao.api import Problem, Group, ScipyOptimizeDriver, DirectSolver, \ SqliteRecorder, CaseReader from openmdao.utils.assert_utils import assert_rel_error from dymos import Phase, GaussLobatto from dymos.examples.brachistochrone.brachistochrone_ode import BrachistochroneODE p = Problem(model=Group()) p.driver = ScipyOptimizeDriver() phase = Phase(ode_class=BrachistochroneODE, transcription=GaussLobatto(num_segments=10)) p.model.add_subsystem('phase0', phase) phase.set_time_options(initial_bounds=(0, 0), duration_bounds=(.5, 10)) phase.set_state_options('x', fix_initial=True, fix_final=True) phase.set_state_options('y', fix_initial=True, fix_final=True) phase.set_state_options('v', fix_initial=True) phase.add_control('theta', units='deg', rate_continuity=False, lower=0.01, upper=179.9) phase.add_design_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 = DirectSolver() # Recording rec = SqliteRecorder('brachistochrone_solution.db') p.driver.recording_options['record_desvars'] = True p.driver.recording_options['record_responses'] = True p.driver.recording_options['record_objectives'] = True p.driver.recording_options['record_constraints'] = True p.model.recording_options['record_metadata'] = True p.driver.add_recorder(rec) p.model.add_recorder(rec) phase.add_recorder(rec) 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_rel_error(self, p.get_val('phase0.timeseries.time')[-1], 1.8016, tolerance=1.0E-3) cr = CaseReader('brachistochrone_solution.db') system_cases = cr.list_cases('root') case = cr.get_case(system_cases[-1]) outputs = dict([ (o[0], o[1]) for o in case.list_outputs(units=True, shape=True, out_stream=None) ]) assert_rel_error( self, p['phase0.controls:theta'], outputs['phase0.control_group.indep_controls.controls:theta'] ['value'])
ignored_pairs = [] for i, j in combinations(range(n_traj), 2): theta, heading, start_x, start_y, end_x, end_y = locs[i] line1 = [start_x, start_y, end_x, end_y] theta, heading, start_x, start_y, end_x, end_y = locs[j] line2 = [start_x, start_y, end_x, end_y] crosses, pt = intersect(line1, line2, r_space) if not crosses: ignored_pairs.append((i, j)) print("ignoring pair:", (i, j)) if not coloring: ignored_pairs = [] phase = Phase(transcription=GaussLobatto(num_segments=20, order=3), ode_class=PlaneODE2D, ode_init_kwargs={'ignored_pairs': ignored_pairs}) p.model.add_subsystem('phase0', phase) max_time = 500.0 start_mass = 25.0 phase.set_time_options(initial_bounds=(0, 0), duration_bounds=(10, max_time)) for i in range(n_traj): theta, heading, start_x, start_y, end_x, end_y = locs[i] phase.set_state_options('p%dx' % i,
def test_brachistochrone_integrated_control_gauss_lobatto(self): import numpy as np from openmdao.api import Problem, Group, ScipyOptimizeDriver, DirectSolver from openmdao.utils.assert_utils import assert_rel_error from dymos import Phase, GaussLobatto p = Problem(model=Group()) p.driver = ScipyOptimizeDriver() phase = Phase(ode_class=BrachistochroneODE, transcription=GaussLobatto(num_segments=10)) p.model.add_subsystem('phase0', phase) phase.set_time_options(initial_bounds=(0, 0), duration_bounds=(.5, 10)) phase.set_state_options('x', fix_initial=True, fix_final=True) phase.set_state_options('y', fix_initial=True, fix_final=True) phase.set_state_options('v', fix_initial=True) phase.set_state_options('theta', targets='theta', fix_initial=False) 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) # Minimize time at the end of the phase phase.add_objective('time', loc='final', scaler=10) p.model.linear_solver = 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_rel_error(self, 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_rel_error(self, x_interp(time_sol), x_sol, tolerance=1.0E-5) assert_rel_error(self, y_interp(time_sol), y_sol, tolerance=1.0E-5) assert_rel_error(self, v_interp(time_sol), v_sol, tolerance=1.0E-5) assert_rel_error(self, theta_interp(time_sol), theta_sol, tolerance=1.0E-5) assert_rel_error(self, theta_dot_interp(time_sol), theta_dot_sol, tolerance=1.0E-5)
def test_two_phase_cannonball_for_docs(self): from openmdao.api import Problem, Group, IndepVarComp, DirectSolver, SqliteRecorder, \ pyOptSparseDriver from openmdao.utils.assert_utils import assert_rel_error from dymos import Phase, Trajectory, Radau, GaussLobatto from dymos.examples.cannonball.cannonball_ode import CannonballODE from dymos.examples.cannonball.size_comp import CannonballSizeComp p = Problem(model=Group()) p.driver = pyOptSparseDriver() p.driver.options['optimizer'] = 'SLSQP' p.driver.options['dynamic_simul_derivs'] = True external_params = p.model.add_subsystem('external_params', IndepVarComp()) external_params.add_output('radius', val=0.10, units='m') external_params.add_output('dens', val=7.87, units='g/cm**3') external_params.add_design_var('radius', lower=0.01, upper=0.10, ref0=0.01, ref=0.10) p.model.add_subsystem('size_comp', CannonballSizeComp()) traj = p.model.add_subsystem('traj', Trajectory()) transcription = Radau(num_segments=5, order=3, compressed=True) ascent = Phase(ode_class=CannonballODE, transcription=transcription) ascent = traj.add_phase('ascent', ascent) # All initial states except flight path angle are fixed # Final flight path angle is fixed (we will set it to zero so that the phase ends at apogee) ascent.set_time_options(fix_initial=True, duration_bounds=(1, 100), duration_ref=100, units='s') ascent.set_state_options('r', fix_initial=True, fix_final=False) ascent.set_state_options('h', fix_initial=True, fix_final=False) ascent.set_state_options('gam', fix_initial=False, fix_final=True) ascent.set_state_options('v', fix_initial=False, fix_final=False) # Limit the muzzle energy ascent.add_boundary_constraint('kinetic_energy.ke', loc='initial', units='J', upper=400000, lower=0, ref=100000, shape=(1,)) # Second Phase (descent) transcription = GaussLobatto(num_segments=5, order=3, compressed=True) descent = Phase(ode_class=CannonballODE, transcription=transcription) traj.add_phase('descent', descent) # All initial states and time are free (they will be linked to the final states of ascent. # Final altitude is fixed (we will set it to zero so that the phase ends at ground impact) descent.set_time_options(initial_bounds=(.5, 100), duration_bounds=(.5, 100), duration_ref=100) descent.set_state_options('r', fix_initial=False, fix_final=False) descent.set_state_options('h', fix_initial=False, fix_final=True) descent.set_state_options('gam', fix_initial=False, fix_final=False) descent.set_state_options('v', fix_initial=False, fix_final=False) descent.add_objective('r', loc='final', scaler=-1.0) # Add internally-managed design parameters to the trajectory. traj.add_design_parameter('CD', val=0.5, units=None, opt=False) traj.add_design_parameter('CL', val=0.0, units=None, opt=False) traj.add_design_parameter('T', val=0.0, units='N', opt=False) traj.add_design_parameter('alpha', val=0.0, units='deg', opt=False) # Add externally-provided design parameters to the trajectory. traj.add_input_parameter('mass', target_params={'ascent': 'm', 'descent': 'm'}, val=1.0) traj.add_input_parameter('S', val=0.005) # Link Phases (link time and all state variables) traj.link_phases(phases=['ascent', 'descent'], vars=['*']) # Issue Connections p.model.connect('external_params.radius', 'size_comp.radius') p.model.connect('external_params.dens', 'size_comp.dens') p.model.connect('size_comp.mass', 'traj.input_parameters:mass') p.model.connect('size_comp.S', 'traj.input_parameters:S') # Finish Problem Setup p.model.linear_solver = DirectSolver() p.driver.add_recorder(SqliteRecorder('ex_two_phase_cannonball.db')) p.setup(check=True) # Set Initial Guesses p.set_val('external_params.radius', 0.05, units='m') p.set_val('external_params.dens', 7.87, units='g/cm**3') p.set_val('traj.design_parameters:CD', 0.5) p.set_val('traj.design_parameters:CL', 0.0) p.set_val('traj.design_parameters:T', 0.0) p.set_val('traj.ascent.t_initial', 0.0) p.set_val('traj.ascent.t_duration', 10.0) p.set_val('traj.ascent.states:r', ascent.interpolate(ys=[0, 100], nodes='state_input')) p.set_val('traj.ascent.states:h', ascent.interpolate(ys=[0, 100], nodes='state_input')) p.set_val('traj.ascent.states:v', ascent.interpolate(ys=[200, 150], nodes='state_input')) p.set_val('traj.ascent.states:gam', ascent.interpolate(ys=[25, 0], nodes='state_input'), units='deg') p.set_val('traj.descent.t_initial', 10.0) p.set_val('traj.descent.t_duration', 10.0) p.set_val('traj.descent.states:r', descent.interpolate(ys=[100, 200], nodes='state_input')) p.set_val('traj.descent.states:h', descent.interpolate(ys=[100, 0], nodes='state_input')) p.set_val('traj.descent.states:v', descent.interpolate(ys=[150, 200], nodes='state_input')) p.set_val('traj.descent.states:gam', descent.interpolate(ys=[0, -45], nodes='state_input'), units='deg') p.run_driver() assert_rel_error(self, 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', 'm', 'S'] fig, axes = plt.subplots(nrows=6, ncols=1, figsize=(12, 6)) for i, param in enumerate(params): p_imp = { 'ascent': p.get_val('traj.ascent.timeseries.traj_parameters:{0}'.format(param)), 'descent': p.get_val('traj.descent.timeseries.traj_parameters:{0}'.format(param))} p_exp = {'ascent': exp_out.get_val('traj.ascent.timeseries.' 'traj_parameters:{0}'.format(param)), 'descent': exp_out.get_val('traj.descent.timeseries.' 'traj_parameters:{0}'.format(param))} axes[i].set_ylabel(param) axes[i].plot(time_imp['ascent'], p_imp['ascent'], 'bo') axes[i].plot(time_imp['descent'], p_imp['descent'], 'ro') axes[i].plot(time_exp['ascent'], p_exp['ascent'], 'b--') axes[i].plot(time_exp['descent'], p_exp['descent'], 'r--') plt.show()
def test_cruise_results_gl(self): p = Problem(model=Group()) if optimizer == 'SNOPT': p.driver = pyOptSparseDriver() p.driver.options['optimizer'] = optimizer p.driver.options['dynamic_simul_derivs'] = True p.driver.opt_settings['Major iterations limit'] = 100 p.driver.opt_settings['Major step limit'] = 0.05 p.driver.opt_settings['Major feasibility tolerance'] = 1.0E-6 p.driver.opt_settings['Major optimality tolerance'] = 1.0E-6 p.driver.opt_settings["Linesearch tolerance"] = 0.10 p.driver.opt_settings['iSumm'] = 6 p.driver.opt_settings['Verify level'] = 3 else: p.driver = ScipyOptimizeDriver() p.driver.options['dynamic_simul_derivs'] = True transcription = GaussLobatto(num_segments=1, order=13, compressed=False) phase = Phase(ode_class=AircraftODE, transcription=transcription) p.model.add_subsystem('phase0', phase) # Pass Reference Area from an external source assumptions = p.model.add_subsystem('assumptions', IndepVarComp()) assumptions.add_output('S', val=427.8, units='m**2') assumptions.add_output('mass_empty', val=1.0, units='kg') assumptions.add_output('mass_payload', val=1.0, units='kg') phase.set_time_options(initial_bounds=(0, 0), duration_bounds=(3600, 3600), duration_ref=3600) phase.set_state_options('range', units='km', fix_initial=True, fix_final=False, scaler=0.01, defect_scaler=0.01) phase.set_state_options('mass_fuel', fix_final=True, upper=20000.0, lower=0.0, scaler=1.0E-4, defect_scaler=1.0E-2) phase.set_state_options('alt', units='km', fix_initial=True) phase.add_control('mach', units=None, opt=False) phase.add_control('climb_rate', units='m/s', opt=False) phase.add_input_parameter('S', units='m**2') phase.add_input_parameter('mass_empty', units='kg') phase.add_input_parameter('mass_payload', units='kg') phase.add_path_constraint('propulsion.tau', lower=0.01, upper=1.0, shape=(1, )) phase.add_timeseries_output('tas_comp.TAS', units='m/s') p.model.connect('assumptions.S', 'phase0.input_parameters:S') p.model.connect('assumptions.mass_empty', 'phase0.input_parameters:mass_empty') p.model.connect('assumptions.mass_payload', 'phase0.input_parameters:mass_payload') phase.add_objective('time', loc='final', ref=3600) p.model.linear_solver = DirectSolver() p.setup() p['phase0.t_initial'] = 0.0 p['phase0.t_duration'] = 1.515132 * 3600.0 p['phase0.states:range'] = phase.interpolate(ys=(0, 1296.4), nodes='state_input') p['phase0.states:mass_fuel'] = phase.interpolate(ys=(12236.594555, 0), nodes='state_input') p['phase0.states:alt'] = 5.0 p['phase0.controls:mach'] = 0.8 p['phase0.controls:climb_rate'] = 0.0 p['assumptions.S'] = 427.8 p['assumptions.mass_empty'] = 0.15E6 p['assumptions.mass_payload'] = 84.02869 * 400 p.run_driver() time = p.get_val('phase0.timeseries.time') tas = p.get_val('phase0.timeseries.TAS', units='km/s') range = p.get_val('phase0.timeseries.states:range') assert_rel_error(self, range, tas * time, tolerance=1.0E-4)
def test_control_rate2_boundary_constraint_gl(self): p = Problem(model=Group()) p.driver = ScipyOptimizeDriver() p.driver.options['dynamic_simul_derivs'] = True phase = Phase(ode_class=BrachistochroneODE, transcription=GaussLobatto(num_segments=20, order=3, compressed=True)) p.model.add_subsystem('phase0', phase) phase.set_time_options(fix_initial=True, duration_bounds=(0.1, 10)) phase.set_state_options('x', fix_initial=True, fix_final=True) phase.set_state_options('y', fix_initial=True, fix_final=True) phase.set_state_options('v', fix_initial=True, fix_final=False) phase.add_control('theta', continuity=True, rate_continuity=True, rate2_continuity=True, units='deg', lower=0.01, upper=179.9) phase.add_design_parameter('g', units='m/s**2', opt=False, val=9.80665) phase.add_boundary_constraint('theta_rate2', loc='final', equals=0.0, units='deg/s**2') # Minimize time at the end of the phase phase.add_objective('time') p.model.linear_solver = DirectSolver() p.setup(check=True) 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], nodes='control_input') p['phase0.design_parameters:g'] = 8 p.run_driver() plt.plot(p.get_val('phase0.timeseries.states:x'), p.get_val('phase0.timeseries.states:y'), 'ko') plt.figure() plt.plot(p.get_val('phase0.timeseries.time'), p.get_val('phase0.timeseries.controls:theta'), 'ro') plt.plot(p.get_val('phase0.timeseries.time'), p.get_val('phase0.timeseries.control_rates:theta_rate'), 'bo') plt.plot(p.get_val('phase0.timeseries.time'), p.get_val('phase0.timeseries.control_rates:theta_rate2'), 'go') plt.show() assert_rel_error( self, p.get_val('phase0.timeseries.control_rates:theta_rate2')[-1], 0, tolerance=1.0E-6)
def test_design_parameter_boundary_constraint(self): p = Problem(model=Group()) p.driver = ScipyOptimizeDriver() p.driver.options['dynamic_simul_derivs'] = True phase = Phase(ode_class=BrachistochroneODE, transcription=GaussLobatto(num_segments=20, order=3, compressed=True)) p.model.add_subsystem('phase0', phase) phase.set_time_options(fix_initial=True, duration_bounds=(.5, 10)) phase.set_state_options('x', fix_initial=True, fix_final=True) phase.set_state_options('y', fix_initial=True, fix_final=True) phase.set_state_options('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_design_parameter('g', units='m/s**2', opt=True, val=9.80665) # We'll let g vary, but make sure it hits the desired value. # It's a static design parameter, so it shouldn't matter whether we enforce it # at the start or the end of the phase, so here we'll do both. # Note if we make these equality constraints, some optimizers (SLSQP) will # see the problem as infeasible. phase.add_boundary_constraint('g', loc='initial', units='m/s**2', upper=9.80665) phase.add_boundary_constraint('g', loc='final', units='m/s**2', upper=9.80665) # Minimize time at the end of the phase phase.add_objective('time_phase', loc='final', scaler=10) p.model.linear_solver = DirectSolver() p.setup(check=True) 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], nodes='control_input') p['phase0.design_parameters:g'] = 5 p.run_driver() assert_rel_error(self, p.get_val('phase0.timeseries.time')[-1], 1.8016, tolerance=1.0E-4) assert_rel_error(self, p.get_val('phase0.timeseries.design_parameters:g')[0], 9.80665, tolerance=1.0E-6) assert_rel_error( self, p.get_val('phase0.timeseries.design_parameters:g')[-1], 9.80665, tolerance=1.0E-6)
def brachistochrone_min_time(transcription='gauss-lobatto', num_segments=8, transcription_order=3, run_driver=True, compressed=True, optimizer='SLSQP'): p = Problem(model=Group()) # if optimizer == 'SNOPT': p.driver = pyOptSparseDriver() p.driver.options['optimizer'] = optimizer p.driver.options['dynamic_simul_derivs'] = True if transcription == 'gauss-lobatto': t = GaussLobatto(num_segments=num_segments, order=transcription_order, compressed=compressed) elif transcription == 'radau-ps': t = Radau(num_segments=num_segments, order=transcription_order, compressed=compressed) elif transcription == 'runge-kutta': t = RungeKutta(num_segments=num_segments, order=transcription_order, compressed=compressed) phase = Phase(ode_class=BrachistochroneODE, transcription=t) p.model.add_subsystem('phase0', phase) phase.set_time_options(fix_initial=True, duration_bounds=(.5, 10)) phase.set_state_options('x', fix_initial=True, fix_final=False, solve_segments=False) phase.set_state_options('y', fix_initial=True, fix_final=False, solve_segments=False) phase.set_state_options('v', fix_initial=True, fix_final=False, solve_segments=False) phase.add_control('theta', continuity=True, rate_continuity=True, units='deg', lower=0.01, upper=179.9) phase.add_input_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 = DirectSolver() p.setup(check=True) 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], nodes='control_input') p['phase0.input_parameters:g'] = 9.80665 p.run_model() if run_driver: p.run_driver() # Plot results if SHOW_PLOTS: exp_out = phase.simulate() fig, ax = plt.subplots() fig.suptitle('Brachistochrone Solution') x_imp = p.get_val('phase0.timeseries.states:x') y_imp = p.get_val('phase0.timeseries.states:y') x_exp = exp_out.get_val('phase0.timeseries.states:x') y_exp = exp_out.get_val('phase0.timeseries.states:y') ax.plot(x_imp, y_imp, 'ro', label='implicit') ax.plot(x_exp, y_exp, 'b-', label='explicit') ax.set_xlabel('x (m)') ax.set_ylabel('y (m)') ax.grid(True) ax.legend(loc='upper right') fig, ax = plt.subplots() fig.suptitle('Brachistochrone Solution') x_imp = p.get_val('phase0.timeseries.time_phase') y_imp = p.get_val('phase0.timeseries.controls:theta') x_exp = exp_out.get_val('phase0.timeseries.time_phase') y_exp = exp_out.get_val('phase0.timeseries.controls:theta') ax.plot(x_imp, y_imp, 'ro', label='implicit') ax.plot(x_exp, y_exp, 'b-', label='explicit') ax.set_xlabel('time (s)') ax.set_ylabel('theta (rad)') ax.grid(True) ax.legend(loc='lower right') plt.show() return p
def test_two_burn_orbit_raise_gl_rk_gl_constrained(self): import numpy as np import matplotlib.pyplot as plt from openmdao.api import Problem, Group, pyOptSparseDriver, DirectSolver from openmdao.utils.assert_utils import assert_rel_error from openmdao.utils.general_utils import set_pyoptsparse_opt from dymos import Phase, GaussLobatto, RungeKutta, Trajectory from dymos.examples.finite_burn_orbit_raise.finite_burn_eom import FiniteBurnODE traj = Trajectory() p = Problem(model=Group()) p.model.add_subsystem('traj', traj) p.driver = pyOptSparseDriver() _, optimizer = set_pyoptsparse_opt('SNOPT', fallback=True) p.driver.options['optimizer'] = optimizer p.driver.options['dynamic_simul_derivs'] = True traj.add_design_parameter('c', opt=False, val=1.5, units='DU/TU') # First Phase (burn) burn1 = Phase(ode_class=FiniteBurnODE, transcription=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)) burn1.set_state_options('r', fix_initial=True, fix_final=False) burn1.set_state_options('theta', fix_initial=True, fix_final=False) burn1.set_state_options('vr', fix_initial=True, fix_final=False) burn1.set_state_options('vt', fix_initial=True, fix_final=False) burn1.set_state_options('accel', fix_initial=True, fix_final=False) burn1.set_state_options('deltav', fix_initial=True, fix_final=False) burn1.add_control('u1', rate_continuity=True, rate2_continuity=True, units='deg', scaler=0.01, lower=-30, upper=30) # Second Phase (Coast) coast = Phase(ode_class=FiniteBurnODE, transcription=RungeKutta(num_segments=20, compressed=True)) traj.add_phase('coast', coast) coast.set_time_options(initial_bounds=(0.5, 20), duration_bounds=(.5, 10), duration_ref=10) coast.set_state_options('r', fix_initial=False, fix_final=False) coast.set_state_options('theta', fix_initial=False, fix_final=False) coast.set_state_options('vr', fix_initial=False, fix_final=False) coast.set_state_options('vt', fix_initial=False, fix_final=False) coast.set_state_options('accel', fix_initial=True, fix_final=False) coast.set_state_options('deltav', fix_initial=False, fix_final=False) coast.add_design_parameter('u1', opt=False, val=0.0) # Third Phase (burn) burn2 = Phase(ode_class=FiniteBurnODE, transcription=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) burn2.set_state_options('r', fix_initial=False, fix_final=True) burn2.set_state_options('theta', fix_initial=False, fix_final=False) burn2.set_state_options('vr', fix_initial=False, fix_final=True) burn2.set_state_options('vt', fix_initial=False, fix_final=True) burn2.set_state_options('accel', fix_initial=False, fix_final=False, defect_scaler=1.0) burn2.set_state_options('deltav', fix_initial=False, fix_final=False, defect_scaler=1.0) burn2.add_control('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 = 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_rel_error(self, 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 brachistochrone_min_time(transcription='gauss-lobatto', num_segments=8, transcription_order=3, compressed=True, sim_record='brach_min_time_sim.db', optimizer='SLSQP', dynamic_simul_derivs=True, force_alloc_complex=False, solve_segments=False, run_driver=True): p = Problem(model=Group()) if optimizer == 'SNOPT': p.driver = 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 = ScipyOptimizeDriver() p.driver.options['dynamic_simul_derivs'] = dynamic_simul_derivs if transcription == 'runge-kutta': transcription = RungeKutta(num_segments=num_segments, compressed=compressed) elif transcription == 'gauss-lobatto': transcription = GaussLobatto(num_segments=num_segments, order=transcription_order, compressed=compressed) elif transcription == 'radau-ps': transcription = Radau(num_segments=num_segments, order=transcription_order, compressed=compressed) phase = Phase(ode_class=BrachistochroneVectorStatesODE, transcription=transcription) p.model.add_subsystem('phase0', phase) phase.set_time_options(fix_initial=True, duration_bounds=(.5, 10)) fix_final = not solve_segments # can't fix final position if you're solving the segments phase.set_state_options('pos', fix_initial=True, fix_final=fix_final, solve_segments=solve_segments) phase.set_state_options('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_design_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 = DirectSolver() p.setup(check=True, force_alloc_complex=force_alloc_complex) p['phase0.t_initial'] = 0.0 p['phase0.t_duration'] = 2.0 pos0 = [0, 10] posf = [10, 5] p['phase0.states:pos'] = phase.interpolate(ys=[pos0, posf], 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], nodes='control_input') p['phase0.design_parameters:g'] = 9.80665 p.run_model() if run_driver: p.run_driver() # Plot results if SHOW_PLOTS: p.run_driver() exp_out = phase.simulate(record_file=sim_record) fig, ax = plt.subplots() fig.suptitle('Brachistochrone Solution') x_imp = p.get_val('phase0.timeseries.states:pos')[:, 0] y_imp = p.get_val('phase0.timeseries.states:pos')[:, 1] x_exp = exp_out.get_val('phase0.timeseries.states:pos')[:, 0] y_exp = exp_out.get_val('phase0.timeseries.states:pos')[:, 1] ax.plot(x_imp, y_imp, 'ro', label='implicit') ax.plot(x_exp, y_exp, 'b-', label='explicit') ax.set_xlabel('x (m)') ax.set_ylabel('y (m)') ax.grid(True) ax.legend(loc='upper right') fig, ax = plt.subplots() fig.suptitle('Brachistochrone Solution') x_imp = p.get_val('phase0.timeseries.time') y_imp = p.get_val('phase0.timeseries.control_rates:theta_rate2') x_exp = exp_out.get_val('phase0.timeseries.time') y_exp = exp_out.get_val('phase0.timeseries.control_rates:theta_rate2') ax.plot(x_imp, y_imp, 'ro', label='implicit') ax.plot(x_exp, y_exp, 'b-', label='explicit') ax.set_xlabel('time (s)') ax.set_ylabel('theta rate2 (rad/s**2)') ax.grid(True) ax.legend(loc='lower right') plt.show() return p
def test_reentry_mixed_controls(self): p = om.Problem(model=om.Group()) p.driver = om.pyOptSparseDriver() p.driver.declare_coloring() OPT, OPTIMIZER = set_pyoptsparse_opt('IPOPT', fallback=False) p.driver.options['optimizer'] = OPTIMIZER traj = p.model.add_subsystem('traj', Trajectory()) phase0 = traj.add_phase( 'phase0', Phase(ode_class=ShuttleODE, transcription=GaussLobatto(num_segments=20, order=3))) phase0.set_time_options(fix_initial=True, units='s', duration_ref=200) phase0.add_state('h', fix_initial=True, fix_final=True, units='ft', rate_source='hdot', targets=['h'], lower=0, ref0=75000, ref=300000, defect_ref=1000) phase0.add_state('gamma', fix_initial=True, fix_final=True, units='rad', rate_source='gammadot', targets=['gamma'], lower=-89. * np.pi / 180, upper=89. * np.pi / 180) phase0.add_state('phi', fix_initial=True, fix_final=False, units='rad', rate_source='phidot', lower=0, upper=89. * np.pi / 180) phase0.add_state('psi', fix_initial=True, fix_final=False, units='rad', rate_source='psidot', targets=['psi'], lower=0, upper=90. * np.pi / 180) phase0.add_state('theta', fix_initial=True, fix_final=False, units='rad', rate_source='thetadot', targets=['theta'], lower=-89. * np.pi / 180, upper=89. * np.pi / 180) phase0.add_state('v', fix_initial=True, fix_final=True, units='ft/s', rate_source='vdot', targets=['v'], lower=500, ref0=2500, ref=25000) phase0.add_control('alpha', units='rad', opt=True, lower=-np.pi / 2, upper=np.pi / 2) phase0.add_polynomial_control('beta', order=5, units='rad', opt=True, lower=-89 * np.pi / 180, upper=1 * np.pi / 180) phase0.add_objective('theta', loc='final', ref=-0.01) p.setup(check=True, force_alloc_complex=True) p.set_val('traj.phase0.states:h', phase0.interpolate(ys=[260000, 80000], nodes='state_input'), units='ft') p.set_val('traj.phase0.states:gamma', phase0.interpolate(ys=[-1 * np.pi / 180, -5 * np.pi / 180], nodes='state_input'), units='rad') p.set_val('traj.phase0.states:phi', phase0.interpolate(ys=[0, 75 * np.pi / 180], nodes='state_input'), units='rad') p.set_val('traj.phase0.states:psi', phase0.interpolate(ys=[90 * np.pi / 180, 10 * np.pi / 180], nodes='state_input'), units='rad') p.set_val('traj.phase0.states:theta', phase0.interpolate(ys=[0, 25 * np.pi / 180], nodes='state_input'), units='rad') p.set_val('traj.phase0.states:v', phase0.interpolate(ys=[25600, 2500], nodes='state_input'), units='ft/s') p.set_val('traj.phase0.t_initial', 0, units='s') p.set_val('traj.phase0.t_duration', 2000, units='s') p.set_val('traj.phase0.controls:alpha', phase0.interpolate( ys=[17.4 * np.pi / 180, 17.4 * np.pi / 180], nodes='control_input'), units='rad') p.set_val('traj.phase0.polynomial_controls:beta', np.radians(-75)) p.run_driver() exp_out = traj.simulate() from scipy.interpolate import interp1d t_sol = p.get_val('traj.phase0.timeseries.time') beta_sol = p.get_val('traj.phase0.timeseries.polynomial_controls:beta') t_sim = exp_out.get_val('traj.phase0.timeseries.time') beta_sim = exp_out.get_val( 'traj.phase0.timeseries.polynomial_controls:beta') sol_interp = interp1d(t_sol.ravel(), beta_sol.ravel()) sim_interp = interp1d(t_sim.ravel(), beta_sim.ravel()) t = np.linspace(0, t_sol.ravel()[-1], 100) assert_near_equal(sim_interp(t), sol_interp(t), tolerance=1.0E-3)
def test_brachistochrone_vector_ode_path_constraints_gl_no_indices(self): p = Problem(model=Group()) p.driver = ScipyOptimizeDriver() p.driver.options['dynamic_simul_derivs'] = True phase = Phase(ode_class=BrachistochroneVectorStatesODE, transcription=GaussLobatto(num_segments=20, order=3)) p.model.add_subsystem('phase0', phase) phase.set_time_options(fix_initial=True, duration_bounds=(.5, 10)) phase.set_state_options('pos', fix_initial=True, fix_final=True) phase.set_state_options('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_design_parameter('g', units='m/s**2', opt=False, val=9.80665) phase.add_path_constraint('pos_dot', shape=(2, ), units='m/s', lower=-4, upper=12) phase.add_timeseries_output('pos_dot', shape=(2, ), units='m/s') # Minimize time at the end of the phase phase.add_objective('time', loc='final', scaler=10) p.model.linear_solver = DirectSolver() p.setup(check=True, force_alloc_complex=True) p['phase0.t_initial'] = 0.0 p['phase0.t_duration'] = 2.0 pos0 = [0, 10] posf = [10, 5] p['phase0.states:pos'] = phase.interpolate(ys=[pos0, posf], 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], nodes='control_input') p['phase0.design_parameters:g'] = 9.80665 p.run_driver() assert_rel_error(self, np.min(p.get_val('phase0.timeseries.pos_dot')[:, -1]), -4, tolerance=1.0E-2) # Plot results if SHOW_PLOTS: exp_out = phase.simulate(times_per_seg=20) fig, ax = plt.subplots() fig.suptitle('Brachistochrone Solution') x_imp = p.get_val('phase0.timeseries.states:pos')[:, 0] y_imp = p.get_val('phase0.timeseries.states:pos')[:, 1] x_exp = exp_out.get_val('phase0.timeseries.states:pos')[:, 0] y_exp = exp_out.get_val('phase0.timeseries.states:pos')[:, 1] ax.plot(x_imp, y_imp, 'ro', label='implicit') ax.plot(x_exp, y_exp, 'b-', label='explicit') ax.set_xlabel('x (m)') ax.set_ylabel('y (m)') ax.grid(True) ax.legend(loc='upper right') fig, ax = plt.subplots() fig.suptitle('Brachistochrone Solution\nVelocity') t_imp = p.get_val('phase0.timeseries.time') t_exp = exp_out.get_val('phase0.timeseries.time') xdot_imp = p.get_val('phase0.timeseries.pos_dot')[:, 0] ydot_imp = p.get_val('phase0.timeseries.pos_dot')[:, 1] xdot_exp = exp_out.get_val('phase0.timeseries.pos_dot')[:, 0] ydot_exp = exp_out.get_val('phase0.timeseries.pos_dot')[:, 1] ax.plot(t_imp, xdot_imp, 'bo', label='implicit') ax.plot(t_exp, xdot_exp, 'b-', label='explicit') ax.plot(t_imp, ydot_imp, 'ro', label='implicit') ax.plot(t_exp, ydot_exp, 'r-', label='explicit') ax.set_xlabel('t (s)') ax.set_ylabel('v (m/s)') ax.grid(True) ax.legend(loc='upper right') fig, ax = plt.subplots() fig.suptitle('Brachistochrone Solution') x_imp = p.get_val('phase0.timeseries.time') y_imp = p.get_val('phase0.timeseries.control_rates:theta_rate2') x_exp = exp_out.get_val('phase0.timeseries.time') y_exp = exp_out.get_val( 'phase0.timeseries.control_rates:theta_rate2') ax.plot(x_imp, y_imp, 'ro', label='implicit') ax.plot(x_exp, y_exp, 'b-', label='explicit') ax.set_xlabel('time (s)') ax.set_ylabel('theta rate2 (rad/s**2)') ax.grid(True) ax.legend(loc='lower right') plt.show() return p