Пример #1
0
    def test_hyper_sensitive_for_docs(self):
        import openmdao.api as om
        from openmdao.utils.assert_utils import assert_rel_error
        import dymos as dm
        from dymos.examples.plotting import plot_results
        from dymos.examples.hyper_sensitive.hyper_sensitive_ode import HyperSensitiveODE

        # Initialize the problem and assign the driver
        p = om.Problem(model=om.Group())
        p.driver = om.pyOptSparseDriver()
        p.driver.options['optimizer'] = 'SLSQP'
        p.driver.declare_coloring()

        # Setup the trajectory and its phase
        traj = p.model.add_subsystem('traj', dm.Trajectory())

        transcription = dm.Radau(num_segments=30, order=3, compressed=False)

        phase = traj.add_phase('phase0',
                               dm.Phase(ode_class=HyperSensitiveODE, transcription=transcription))

        phase.set_time_options(fix_initial=True, fix_duration=True)
        phase.add_state('x', fix_initial=True, fix_final=False, rate_source='x_dot', targets=['x'])
        phase.add_state('xL', fix_initial=True, fix_final=False, rate_source='L', targets=['xL'])
        phase.add_control('u', opt=True, targets=['u'])

        phase.add_boundary_constraint('x', loc='final', equals=1)

        phase.add_objective('xL', loc='final')

        p.setup(check=True)

        p.set_val('traj.phase0.states:x', phase.interpolate(ys=[1.5, 1], nodes='state_input'))
        p.set_val('traj.phase0.states:xL', phase.interpolate(ys=[0, 1], nodes='state_input'))
        p.set_val('traj.phase0.t_initial', 0)
        p.set_val('traj.phase0.t_duration', tf)
        p.set_val('traj.phase0.controls:u', phase.interpolate(ys=[-0.6, 2.4], nodes='control_input'))

        #
        # Solve the problem.
        #
        dm.run_problem(p)

        #
        # Verify that the results are correct.
        #
        ui, uf, J = solution()

        assert_rel_error(self,
                         p.get_val('traj.phase0.timeseries.controls:u')[0],
                         ui,
                         tolerance=1.5e-2)

        assert_rel_error(self,
                         p.get_val('traj.phase0.timeseries.controls:u')[-1],
                         uf,
                         tolerance=1.5e-2)

        assert_rel_error(self,
                         p.get_val('traj.phase0.timeseries.states:xL')[-1],
                         J,
                         tolerance=1e-2)

        #
        # Simulate the explicit solution and plot the results.
        #
        exp_out = traj.simulate()

        plot_results([('traj.phase0.timeseries.time', 'traj.phase0.timeseries.states:x',
                       'time (s)', 'x $(m)$'),
                      ('traj.phase0.timeseries.time', 'traj.phase0.timeseries.controls:u',
                       'time (s)', 'u $(m/s^2)$')],
                     title='Hyper Sensitive Problem Solution\nRadau Pseudospectral Method',
                     p_sol=p, p_sim=exp_out)

        plt.show()
    def test_connect_control_to_parameter(self):
        """ Test that the final value of a control in one phase can be connected as the value
        of a parameter in a subsequent phase. """
        import openmdao.api as om
        from openmdao.utils.assert_utils import assert_near_equal

        import dymos as dm
        from dymos.examples.cannonball.size_comp import CannonballSizeComp

        p = om.Problem(model=om.Group())

        p.driver = om.pyOptSparseDriver()
        p.driver.options['optimizer'] = 'SLSQP'
        p.driver.declare_coloring()

        external_params = p.model.add_subsystem('external_params',
                                                om.IndepVarComp())

        external_params.add_output('radius', val=0.10, units='m')
        external_params.add_output('dens', val=7.87, units='g/cm**3')

        external_params.add_design_var('radius',
                                       lower=0.01,
                                       upper=0.10,
                                       ref0=0.01,
                                       ref=0.10)

        p.model.add_subsystem('size_comp', CannonballSizeComp())

        traj = p.model.add_subsystem('traj', dm.Trajectory())

        transcription = dm.Radau(num_segments=5, order=3, compressed=True)
        ascent = dm.Phase(ode_class=CannonballODEVectorCD,
                          transcription=transcription)

        ascent = traj.add_phase('ascent', ascent)

        # All initial states except flight path angle are fixed
        # Final flight path angle is fixed (we will set it to zero so that the phase ends at apogee)

        ascent.set_time_options(fix_initial=True,
                                duration_bounds=(1, 100),
                                duration_ref=100,
                                units='s')
        ascent.add_state('r',
                         fix_initial=True,
                         fix_final=False,
                         rate_source='r_dot',
                         units='m')
        ascent.add_state('h',
                         fix_initial=True,
                         fix_final=False,
                         units='m',
                         rate_source='h_dot')
        ascent.add_state('gam',
                         fix_initial=False,
                         fix_final=True,
                         units='rad',
                         rate_source='gam_dot')
        ascent.add_state('v',
                         fix_initial=False,
                         fix_final=False,
                         units='m/s',
                         rate_source='v_dot')

        ascent.add_parameter('S',
                             targets=['S'],
                             units='m**2',
                             static_target=True)
        ascent.add_parameter('mass',
                             targets=['m'],
                             units='kg',
                             static_target=True)

        ascent.add_control('CD', targets=['CD'], opt=False, val=0.05)

        # Limit the muzzle energy
        ascent.add_boundary_constraint('ke',
                                       loc='initial',
                                       upper=400000,
                                       lower=0,
                                       ref=100000)

        # Second Phase (descent)
        transcription = dm.GaussLobatto(num_segments=5,
                                        order=3,
                                        compressed=True)
        descent = dm.Phase(ode_class=CannonballODEVectorCD,
                           transcription=transcription)

        traj.add_phase('descent', descent)

        # All initial states and time are free (they will be linked to the final states of ascent.
        # Final altitude is fixed (we will set it to zero so that the phase ends at ground impact)
        descent.set_time_options(initial_bounds=(.5, 100),
                                 duration_bounds=(.5, 100),
                                 duration_ref=100,
                                 units='s')
        descent.add_state('r', units='m', rate_source='r_dot')
        descent.add_state('h',
                          units='m',
                          rate_source='h_dot',
                          fix_initial=False,
                          fix_final=True)
        descent.add_state('gam',
                          units='rad',
                          rate_source='gam_dot',
                          fix_initial=False,
                          fix_final=False)
        descent.add_state('v',
                          units='m/s',
                          rate_source='v_dot',
                          fix_initial=False,
                          fix_final=False)

        descent.add_parameter('S',
                              targets=['S'],
                              units='m**2',
                              static_target=True)
        descent.add_parameter('mass',
                              targets=['m'],
                              units='kg',
                              static_target=True)
        descent.add_parameter('CD', targets=['CD'], val=0.01)

        descent.add_objective('r', loc='final', scaler=-1.0)

        # Add externally-provided design parameters to the trajectory.
        # In this case, we connect 'm' to pre-existing input parameters named 'mass' in each phase.
        traj.add_parameter('m',
                           units='kg',
                           val=1.0,
                           targets={
                               'ascent': 'mass',
                               'descent': 'mass'
                           },
                           static_target=True)

        # In this case, by omitting targets, we're connecting these parameters to parameters
        # with the same name in each phase.
        traj.add_parameter('S', units='m**2', val=0.005, static_target=True)

        # Link Phases (link time and all state variables)
        traj.link_phases(phases=['ascent', 'descent'], vars=['*'])

        # Issue Connections
        p.model.connect('external_params.radius', 'size_comp.radius')
        p.model.connect('external_params.dens', 'size_comp.dens')

        p.model.connect('size_comp.mass', 'traj.parameters:m')
        p.model.connect('size_comp.S', 'traj.parameters:S')

        traj.connect('ascent.timeseries.controls:CD',
                     'descent.parameters:CD',
                     src_indices=[-1],
                     flat_src_indices=True)

        # A linear solver at the top level can improve performance.
        p.model.linear_solver = om.DirectSolver()

        # Finish Problem Setup
        p.setup()

        # Set Initial Guesses
        p.set_val('external_params.radius', 0.05, units='m')
        p.set_val('external_params.dens', 7.87, units='g/cm**3')

        p.set_val('traj.ascent.controls:CD', 0.5)

        p.set_val('traj.ascent.t_initial', 0.0)
        p.set_val('traj.ascent.t_duration', 10.0)

        p.set_val('traj.ascent.states:r', ascent.interp('r', [0, 100]))
        p.set_val('traj.ascent.states:h', ascent.interp('h', [0, 100]))
        p.set_val('traj.ascent.states:v', ascent.interp('v', [200, 150]))
        p.set_val('traj.ascent.states:gam',
                  ascent.interp('gam', [25, 0]),
                  units='deg')

        p.set_val('traj.descent.t_initial', 10.0)
        p.set_val('traj.descent.t_duration', 10.0)

        p.set_val('traj.descent.states:r', descent.interp('r', [100, 200]))
        p.set_val('traj.descent.states:h', descent.interp('h', [100, 0]))
        p.set_val('traj.descent.states:v', descent.interp('v', [150, 200]))
        p.set_val('traj.descent.states:gam',
                  descent.interp('gam', [0, -45]),
                  units='deg')

        dm.run_problem(p, simulate=True, make_plots=True)

        assert_near_equal(p.get_val('traj.descent.states:r')[-1],
                          3183.25,
                          tolerance=1.0E-2)
        assert_near_equal(
            p.get_val('traj.ascent.timeseries.controls:CD')[-1],
            p.get_val('traj.descent.timeseries.parameters:CD')[0])
Пример #3
0
    def test_two_phase_cannonball_for_docs(self):
        import openmdao.api as om
        from openmdao.utils.assert_utils import assert_near_equal

        import dymos as dm
        from dymos.examples.cannonball.size_comp import CannonballSizeComp
        from dymos.examples.cannonball.cannonball_phase import CannonballPhase

        p = om.Problem(model=om.Group())

        p.driver = om.pyOptSparseDriver()
        p.driver.options['optimizer'] = 'SLSQP'
        p.driver.declare_coloring()

        external_params = p.model.add_subsystem('external_params', om.IndepVarComp())

        external_params.add_output('radius', val=0.10, units='m')
        external_params.add_output('dens', val=7.87, units='g/cm**3')

        external_params.add_design_var('radius', lower=0.01, upper=0.10, ref0=0.01, ref=0.10)

        p.model.add_subsystem('size_comp', CannonballSizeComp())

        traj = p.model.add_subsystem('traj', dm.Trajectory())

        transcription = dm.Radau(num_segments=5, order=3, compressed=True)
        ascent = CannonballPhase(transcription=transcription)

        ascent = traj.add_phase('ascent', ascent)

        # All initial states except flight path angle are fixed
        # Final flight path angle is fixed (we will set it to zero so that the phase ends at apogee)
        ascent.set_time_options(fix_initial=True, duration_bounds=(1, 100), duration_ref=100, units='s')
        ascent.set_state_options('r', fix_initial=True, fix_final=False)
        ascent.set_state_options('h', fix_initial=True, fix_final=False)
        ascent.set_state_options('gam', fix_initial=False, fix_final=True)
        ascent.set_state_options('v', fix_initial=False, fix_final=False)

        ascent.add_parameter('S', targets=['aero.S'], units='m**2')
        ascent.add_parameter('mass', targets=['eom.m', 'kinetic_energy.m'], units='kg')

        # Limit the muzzle energy
        ascent.add_boundary_constraint('kinetic_energy.ke', loc='initial', units='J',
                                       upper=400000, lower=0, ref=100000, shape=(1,))

        # Second Phase (descent)
        transcription = dm.GaussLobatto(num_segments=5, order=3, compressed=True)
        descent = CannonballPhase(transcription=transcription)

        traj.add_phase('descent', descent)

        # All initial states and time are free (they will be linked to the final states of ascent.
        # Final altitude is fixed (we will set it to zero so that the phase ends at ground impact)
        descent.set_time_options(initial_bounds=(.5, 100), duration_bounds=(.5, 100),
                                 duration_ref=100, units='s')
        descent.add_state('r', )
        descent.add_state('h', fix_initial=False, fix_final=True)
        descent.add_state('gam', fix_initial=False, fix_final=False)
        descent.add_state('v', fix_initial=False, fix_final=False)

        descent.add_parameter('S', targets=['aero.S'], units='m**2')
        descent.add_parameter('mass', targets=['eom.m', 'kinetic_energy.m'], units='kg')

        descent.add_objective('r', loc='final', scaler=-1.0)

        # Add internally-managed design parameters to the trajectory.
        traj.add_parameter('CD',
                           targets={'ascent': ['aero.CD'], 'descent': ['aero.CD']},
                           val=0.5, units=None, opt=False)
        traj.add_parameter('CL',
                           targets={'ascent': ['aero.CL'], 'descent': ['aero.CL']},
                           val=0.0, units=None, opt=False)
        traj.add_parameter('T',
                           targets={'ascent': ['eom.T'], 'descent': ['eom.T']},
                           val=0.0, units='N', opt=False)
        traj.add_parameter('alpha',
                           targets={'ascent': ['eom.alpha'], 'descent': ['eom.alpha']},
                           val=0.0, units='deg', opt=False)

        # Add externally-provided design parameters to the trajectory.
        # In this case, we connect 'm' to pre-existing input parameters named 'mass' in each phase.
        traj.add_parameter('m', units='kg', val=1.0,
                           targets={'ascent': 'mass', 'descent': 'mass'})

        # In this case, by omitting targets, we're connecting these parameters to parameters
        # with the same name in each phase.
        traj.add_parameter('S', units='m**2', val=0.005)

        # Link Phases (link time and all state variables)
        traj.link_phases(phases=['ascent', 'descent'], vars=['*'])

        # Issue Connections
        p.model.connect('external_params.radius', 'size_comp.radius')
        p.model.connect('external_params.dens', 'size_comp.dens')

        p.model.connect('size_comp.mass', 'traj.parameters:m')
        p.model.connect('size_comp.S', 'traj.parameters:S')

        # A linear solver at the top level can improve performance.
        p.model.linear_solver = om.DirectSolver()

        # Finish Problem Setup
        p.setup()

        # Set Initial Guesses
        p.set_val('external_params.radius', 0.05, units='m')
        p.set_val('external_params.dens', 7.87, units='g/cm**3')

        p.set_val('traj.parameters:CD', 0.5)
        p.set_val('traj.parameters:CL', 0.0)
        p.set_val('traj.parameters:T', 0.0)

        p.set_val('traj.ascent.t_initial', 0.0)
        p.set_val('traj.ascent.t_duration', 10.0)

        p.set_val('traj.ascent.states:r', ascent.interpolate(ys=[0, 100], nodes='state_input'))
        p.set_val('traj.ascent.states:h', ascent.interpolate(ys=[0, 100], nodes='state_input'))
        p.set_val('traj.ascent.states:v', ascent.interpolate(ys=[200, 150], nodes='state_input'))
        p.set_val('traj.ascent.states:gam', ascent.interpolate(ys=[25, 0], nodes='state_input'),
                  units='deg')

        p.set_val('traj.descent.t_initial', 10.0)
        p.set_val('traj.descent.t_duration', 10.0)

        p.set_val('traj.descent.states:r', descent.interpolate(ys=[100, 200], nodes='state_input'))
        p.set_val('traj.descent.states:h', descent.interpolate(ys=[100, 0], nodes='state_input'))
        p.set_val('traj.descent.states:v', descent.interpolate(ys=[150, 200], nodes='state_input'))
        p.set_val('traj.descent.states:gam', descent.interpolate(ys=[0, -45], nodes='state_input'),
                  units='deg')

        dm.run_problem(p)

        assert_near_equal(p.get_val('traj.descent.states:r')[-1],
                          3183.25, tolerance=1.0E-2)

        exp_out = traj.simulate()

        print('optimal radius: {0:6.4f} m '.format(p.get_val('external_params.radius',
                                                             units='m')[0]))
        print('cannonball mass: {0:6.4f} kg '.format(p.get_val('size_comp.mass',
                                                               units='kg')[0]))
        print('launch angle: {0:6.4f} '
              'deg '.format(p.get_val('traj.ascent.timeseries.states:gam',  units='deg')[0, 0]))
        print('maximum range: {0:6.4f} '
              'm '.format(p.get_val('traj.descent.timeseries.states:r')[-1, 0]))

        fig, axes = plt.subplots(nrows=1, ncols=1, figsize=(10, 6))

        time_imp = {'ascent': p.get_val('traj.ascent.timeseries.time'),
                    'descent': p.get_val('traj.descent.timeseries.time')}

        time_exp = {'ascent': exp_out.get_val('traj.ascent.timeseries.time'),
                    'descent': exp_out.get_val('traj.descent.timeseries.time')}

        r_imp = {'ascent': p.get_val('traj.ascent.timeseries.states:r'),
                 'descent': p.get_val('traj.descent.timeseries.states:r')}

        r_exp = {'ascent': exp_out.get_val('traj.ascent.timeseries.states:r'),
                 'descent': exp_out.get_val('traj.descent.timeseries.states:r')}

        h_imp = {'ascent': p.get_val('traj.ascent.timeseries.states:h'),
                 'descent': p.get_val('traj.descent.timeseries.states:h')}

        h_exp = {'ascent': exp_out.get_val('traj.ascent.timeseries.states:h'),
                 'descent': exp_out.get_val('traj.descent.timeseries.states:h')}

        axes.plot(r_imp['ascent'], h_imp['ascent'], 'bo')

        axes.plot(r_imp['descent'], h_imp['descent'], 'ro')

        axes.plot(r_exp['ascent'], h_exp['ascent'], 'b--')

        axes.plot(r_exp['descent'], h_exp['descent'], 'r--')

        axes.set_xlabel('range (m)')
        axes.set_ylabel('altitude (m)')

        fig, axes = plt.subplots(nrows=4, ncols=1, figsize=(10, 6))
        states = ['r', 'h', 'v', 'gam']
        for i, state in enumerate(states):
            x_imp = {'ascent': p.get_val('traj.ascent.timeseries.states:{0}'.format(state)),
                     'descent': p.get_val('traj.descent.timeseries.states:{0}'.format(state))}

            x_exp = {'ascent': exp_out.get_val('traj.ascent.timeseries.states:{0}'.format(state)),
                     'descent': exp_out.get_val('traj.descent.timeseries.states:{0}'.format(state))}

            axes[i].set_ylabel(state)

            axes[i].plot(time_imp['ascent'], x_imp['ascent'], 'bo')
            axes[i].plot(time_imp['descent'], x_imp['descent'], 'ro')
            axes[i].plot(time_exp['ascent'], x_exp['ascent'], 'b--')
            axes[i].plot(time_exp['descent'], x_exp['descent'], 'r--')

        params = ['CL', 'CD', 'T', 'alpha', 'mass', 'S']
        fig, axes = plt.subplots(nrows=6, ncols=1, figsize=(12, 6))
        for i, param in enumerate(params):
            p_imp = {
                'ascent': p.get_val('traj.ascent.timeseries.parameters:{0}'.format(param)),
                'descent': p.get_val('traj.descent.timeseries.parameters:{0}'.format(param))}

            p_exp = {'ascent': exp_out.get_val('traj.ascent.timeseries.'
                                               'parameters:{0}'.format(param)),
                     'descent': exp_out.get_val('traj.descent.timeseries.'
                                                'parameters:{0}'.format(param))}

            axes[i].set_ylabel(param)

            axes[i].plot(time_imp['ascent'], p_imp['ascent'], 'bo')
            axes[i].plot(time_imp['descent'], p_imp['descent'], 'ro')
            axes[i].plot(time_exp['ascent'], p_exp['ascent'], 'b--')
            axes[i].plot(time_exp['descent'], p_exp['descent'], 'r--')

        plt.show()
Пример #4
0
class Build_Pack():
    """
    Use Dymos to minimize the thickness of the insulator,
    while still maintaining a temperature below 100degC after a 45 second transient
    """

    p = om.Problem(model=om.Group())
    model = p.model
    nn = 1
    p.driver = om.ScipyOptimizeDriver()
    p.driver = om.pyOptSparseDriver(optimizer='SLSQP')
    # p.driver.opt_settings['iSumm'] = 6
    # record_file = 'geometry.sql'
    # p.add_recorder(om.SqliteRecorder(record_file))
    # p.recording_options['includes'] = ['*']
    # p.recording_options['record_objectives'] = True
    # p.recording_options['record_constraints'] = True
    # p.recording_options['record_desvars'] = True
    # p.recording_options['record_inputs'] = True

    p.driver.declare_coloring()

    traj = p.model.add_subsystem('traj', dm.Trajectory())

    phase = traj.add_phase(
        'phase0',
        dm.Phase(ode_class=tempODE,
                 transcription=dm.GaussLobatto(num_segments=20,
                                               order=3,
                                               compressed=False)))

    phase.set_time_options(fix_initial=True, fix_duration=True)

    phase.add_state('T',
                    rate_source='Tdot',
                    units='K',
                    ref=333.15,
                    defect_ref=333.15,
                    fix_initial=True,
                    fix_final=False,
                    solve_segments=False)

    phase.add_boundary_constraint('T',
                                  loc='final',
                                  units='K',
                                  upper=333.15,
                                  lower=293.15,
                                  shape=(1, ))
    phase.add_parameter('d',
                        opt=True,
                        lower=0.001,
                        upper=0.5,
                        val=0.001,
                        units='m',
                        ref0=0,
                        ref=1)
    phase.add_objective('d', loc='final', ref=1)

    model.add_subsystem('sizing', SizingGroup(num_nodes=nn), promotes=['*'])

    p.model.connect('traj.phase0.timeseries.parameters:d',
                    'cell_s_w',
                    src_indices=[-1])  # connect final value of d with cell_s_w
    # model.add_design_var('sizing.L', lower=-1, upper=1)
    # model.add_objective('OD1.Eff')
    p.model.linear_solver = om.DirectSolver()
    p.setup(force_alloc_complex=True)

    p['traj.phase0.t_initial'] = 0.0
    p['traj.phase0.t_duration'] = 45
    p['traj.phase0.states:T'] = phase.interpolate(ys=[293.15, 333.15],
                                                  nodes='state_input')
    p['traj.phase0.parameters:d'] = 0.001

    # p.set_val('DESIGN.rot_ir' , 60)

    p.run_model()
    # cpd = p.check_partials(method='cs', compact_print=True) #check partial derivatives
    # assert_check_partials(cpd)
    # quit()

    dm.run_problem(p)

    # p.record('final') #trigger problem record (or call run_driver if it's attached to the driver)

    # p.run_driver()
    # p.cleanup()

    model.list_inputs(prom_name=True)
    model.list_outputs(prom_name=True)
    # p.check_partials(compact_print=True, method='cs')

    print("num of cells: ", p['n_cells'])
    print("flux: ", p['flux'])
    print("PCM mass: ", p['PCM_tot_mass'])
    print("PCM thickness (mm): ", p['t_PCM'])
    print("OHP mass: ", p['mass_OHP'])
    print("packaging mass: ", p['p_mass'])
    print("total mass: ", p['tot_mass'])
    print("package mass fraction: ", p['mass_frac'])
    print("pack energy density: ", p['energy'] / (p['tot_mass']))
    print("cell energy density: ",
          (p['q_max'] * p['v_n_c']) / (p['cell_mass']))
    print("pack energy (kWh): ", p['energy'] / 1000.)
    print("pack cost ($K): ", p['n_cells'] * 0.4)
    def test_doc_ssto_polynomial_control(self):
        import numpy as np
        import matplotlib.pyplot as plt
        import openmdao.api as om
        from openmdao.utils.assert_utils import assert_near_equal
        import dymos as dm

        g = 1.61544  # lunar gravity, m/s**2

        class LaunchVehicle2DEOM(om.ExplicitComponent):
            """
            Simple 2D Cartesian Equations of Motion for a launch vehicle subject to thrust and drag.
            """
            def initialize(self):
                self.options.declare('num_nodes', types=int)

            def setup(self):
                nn = self.options['num_nodes']

                # Inputs
                self.add_input('vx',
                               val=np.zeros(nn),
                               desc='x velocity',
                               units='m/s')

                self.add_input('vy',
                               val=np.zeros(nn),
                               desc='y velocity',
                               units='m/s')

                self.add_input('m', val=np.zeros(nn), desc='mass', units='kg')

                self.add_input('theta',
                               val=np.zeros(nn),
                               desc='pitch angle',
                               units='rad')

                self.add_input('thrust',
                               val=2100000 * np.ones(nn),
                               desc='thrust',
                               units='N')

                self.add_input('Isp',
                               val=265.2 * np.ones(nn),
                               desc='specific impulse',
                               units='s')

                # Outputs
                self.add_output('xdot',
                                val=np.zeros(nn),
                                desc='velocity component in x',
                                units='m/s')

                self.add_output('ydot',
                                val=np.zeros(nn),
                                desc='velocity component in y',
                                units='m/s')

                self.add_output('vxdot',
                                val=np.zeros(nn),
                                desc='x acceleration magnitude',
                                units='m/s**2')

                self.add_output('vydot',
                                val=np.zeros(nn),
                                desc='y acceleration magnitude',
                                units='m/s**2')

                self.add_output('mdot',
                                val=np.zeros(nn),
                                desc='mass rate of change',
                                units='kg/s')

                # Setup partials
                ar = np.arange(self.options['num_nodes'])

                self.declare_partials(of='xdot',
                                      wrt='vx',
                                      rows=ar,
                                      cols=ar,
                                      val=1.0)
                self.declare_partials(of='ydot',
                                      wrt='vy',
                                      rows=ar,
                                      cols=ar,
                                      val=1.0)

                self.declare_partials(of='vxdot', wrt='vx', rows=ar, cols=ar)
                self.declare_partials(of='vxdot', wrt='m', rows=ar, cols=ar)
                self.declare_partials(of='vxdot',
                                      wrt='theta',
                                      rows=ar,
                                      cols=ar)
                self.declare_partials(of='vxdot',
                                      wrt='thrust',
                                      rows=ar,
                                      cols=ar)

                self.declare_partials(of='vydot', wrt='m', rows=ar, cols=ar)
                self.declare_partials(of='vydot',
                                      wrt='theta',
                                      rows=ar,
                                      cols=ar)
                self.declare_partials(of='vydot', wrt='vy', rows=ar, cols=ar)
                self.declare_partials(of='vydot',
                                      wrt='thrust',
                                      rows=ar,
                                      cols=ar)

                self.declare_partials(of='mdot',
                                      wrt='thrust',
                                      rows=ar,
                                      cols=ar)
                self.declare_partials(of='mdot', wrt='Isp', rows=ar, cols=ar)

            def compute(self, inputs, outputs):
                theta = inputs['theta']
                cos_theta = np.cos(theta)
                sin_theta = np.sin(theta)
                vx = inputs['vx']
                vy = inputs['vy']
                m = inputs['m']
                F_T = inputs['thrust']
                Isp = inputs['Isp']

                outputs['xdot'] = vx
                outputs['ydot'] = vy
                outputs['vxdot'] = F_T * cos_theta / m
                outputs['vydot'] = F_T * sin_theta / m - g
                outputs['mdot'] = -F_T / (g * Isp)

            def compute_partials(self, inputs, jacobian):
                theta = inputs['theta']
                cos_theta = np.cos(theta)
                sin_theta = np.sin(theta)
                m = inputs['m']
                F_T = inputs['thrust']
                Isp = inputs['Isp']

                # jacobian['vxdot', 'vx'] = -CDA * rho * vx / m
                jacobian['vxdot', 'm'] = -(F_T * cos_theta) / m**2
                jacobian['vxdot', 'theta'] = -(F_T / m) * sin_theta
                jacobian['vxdot', 'thrust'] = cos_theta / m

                # jacobian['vydot', 'vy'] = -CDA * rho * vy / m
                jacobian['vydot', 'm'] = -(F_T * sin_theta) / m**2
                jacobian['vydot', 'theta'] = (F_T / m) * cos_theta
                jacobian['vydot', 'thrust'] = sin_theta / m

                jacobian['mdot', 'thrust'] = -1.0 / (g * Isp)
                jacobian['mdot', 'Isp'] = F_T / (g * Isp**2)

        class LaunchVehicleLinearTangentODE(om.Group):
            """
            The LaunchVehicleLinearTangentODE for this case consists of a guidance component and
            the EOM.  Guidance is simply an OpenMDAO ExecComp which computes the arctangent of the
            tan_theta variable.
            """
            def initialize(self):
                self.options.declare(
                    'num_nodes',
                    types=int,
                    desc='Number of nodes to be evaluated in the RHS')

            def setup(self):
                nn = self.options['num_nodes']

                self.add_subsystem(
                    'guidance',
                    om.ExecComp('theta=arctan(tan_theta)',
                                theta={
                                    'value': np.ones(nn),
                                    'units': 'rad'
                                },
                                tan_theta={'value': np.ones(nn)}))

                self.add_subsystem('eom', LaunchVehicle2DEOM(num_nodes=nn))

                self.connect('guidance.theta', 'eom.theta')

        #
        # Setup and solve the optimal control problem
        #
        p = om.Problem(model=om.Group())

        traj = p.model.add_subsystem('traj', dm.Trajectory())

        phase = dm.Phase(ode_class=LaunchVehicleLinearTangentODE,
                         transcription=dm.Radau(num_segments=20,
                                                order=3,
                                                compressed=False))
        traj.add_phase('phase0', phase)

        phase.set_time_options(initial_bounds=(0, 0),
                               duration_bounds=(10, 1000),
                               units='s')

        #
        # Set the state options.  We include rate_source, units, and targets here since the ODE
        # is not decorated with their default values.
        #
        phase.add_state('x',
                        fix_initial=True,
                        lower=0,
                        rate_source='eom.xdot',
                        units='m')
        phase.add_state('y',
                        fix_initial=True,
                        lower=0,
                        rate_source='eom.ydot',
                        units='m')
        phase.add_state('vx',
                        fix_initial=True,
                        lower=0,
                        rate_source='eom.vxdot',
                        units='m/s',
                        targets=['eom.vx'])
        phase.add_state('vy',
                        fix_initial=True,
                        rate_source='eom.vydot',
                        units='m/s',
                        targets=['eom.vy'])
        phase.add_state('m',
                        fix_initial=True,
                        rate_source='eom.mdot',
                        units='kg',
                        targets=['eom.m'])

        #
        # The tangent of theta is modeled as a linear polynomial over the duration of the phase.
        #
        phase.add_polynomial_control('tan_theta',
                                     order=1,
                                     units=None,
                                     opt=True,
                                     targets=['guidance.tan_theta'])

        #
        # Parameters values for thrust and specific impulse are design parameters. They are
        # provided by an IndepVarComp in the phase, but with opt=False their values are not
        # design variables in the optimization problem.
        #
        phase.add_parameter('thrust',
                            units='N',
                            opt=False,
                            val=3.0 * 50000.0 * 1.61544,
                            targets=['eom.thrust'])
        phase.add_parameter('Isp',
                            units='s',
                            opt=False,
                            val=1.0E6,
                            targets=['eom.Isp'])

        #
        # Set the boundary constraints.  These are all states which could also be handled
        # by setting fix_final=True and including the correct final value in the initial guess.
        #
        phase.add_boundary_constraint('y',
                                      loc='final',
                                      equals=1.85E5,
                                      linear=True)
        phase.add_boundary_constraint('vx', loc='final', equals=1627.0)
        phase.add_boundary_constraint('vy', loc='final', equals=0)

        phase.add_objective('time', index=-1, scaler=0.01)

        #
        # Add theta as a timeseries output since it's not included by default.
        #
        phase.add_timeseries_output('guidance.theta', units='deg')

        #
        # Set the optimizer
        #
        p.driver = om.pyOptSparseDriver()
        p.driver.options['optimizer'] = 'SLSQP'
        p.driver.declare_coloring()

        #
        # We don't strictly need to define a linear solver here since our problem is entirely
        # feed-forward with no iterative loops.  It's good practice to add one, however, since
        # failing to do so can cause incorrect derivatives if iterative processes are ever
        # introduced to the system.
        #
        p.model.linear_solver = om.DirectSolver()

        p.setup(check=True)

        #
        # Assign initial guesses for the independent variables in the problem.
        #
        p['traj.phase0.t_initial'] = 0.0
        p['traj.phase0.t_duration'] = 500.0
        p['traj.phase0.states:x'] = phase.interpolate(ys=[0, 350000.0],
                                                      nodes='state_input')
        p['traj.phase0.states:y'] = phase.interpolate(ys=[0, 185000.0],
                                                      nodes='state_input')
        p['traj.phase0.states:vx'] = phase.interpolate(ys=[0, 1627.0],
                                                       nodes='state_input')
        p['traj.phase0.states:vy'] = phase.interpolate(ys=[1.0E-6, 0],
                                                       nodes='state_input')
        p['traj.phase0.states:m'] = phase.interpolate(ys=[50000, 50000],
                                                      nodes='state_input')
        p['traj.phase0.polynomial_controls:tan_theta'] = [[0.5 * np.pi], [0.0]]

        #
        # Solve the problem.
        #
        dm.run_problem(p)

        #
        # Check the results.
        #
        assert_near_equal(p.get_val('traj.phase0.timeseries.time')[-1],
                          481,
                          tolerance=0.01)

        #
        # Get the explitly simulated results
        #
        exp_out = traj.simulate()

        #
        # Plot the results
        #
        fig, axes = plt.subplots(nrows=2, ncols=1, figsize=(10, 8))

        axes[0].plot(p.get_val('traj.phase0.timeseries.states:x'),
                     p.get_val('traj.phase0.timeseries.states:y'),
                     marker='o',
                     ms=4,
                     linestyle='None',
                     label='solution')

        axes[0].plot(exp_out.get_val('traj.phase0.timeseries.states:x'),
                     exp_out.get_val('traj.phase0.timeseries.states:y'),
                     marker=None,
                     linestyle='-',
                     label='simulation')

        axes[0].set_xlabel('range (m)')
        axes[0].set_ylabel('altitude (m)')
        axes[0].set_aspect('equal')

        axes[1].plot(p.get_val('traj.phase0.timeseries.time'),
                     p.get_val('traj.phase0.timeseries.theta'),
                     marker='o',
                     ms=4,
                     linestyle='None')

        axes[1].plot(exp_out.get_val('traj.phase0.timeseries.time'),
                     exp_out.get_val('traj.phase0.timeseries.theta'),
                     linestyle='-',
                     marker=None)

        axes[1].set_xlabel('time (s)')
        axes[1].set_ylabel('theta (deg)')

        plt.suptitle(
            'Single Stage to Orbit Solution Using Polynomial Controls')
        fig.legend(loc='lower center', ncol=2)

        plt.show()
Пример #6
0
    def test_simulate_options(self):
        import itertools
        import numpy as np
        import openmdao.api as om
        import dymos as dm

        for method, atol, first_step in itertools.product(
            ('RK23', 'RK45'), (0.01, 0.001), (None, 0.01, .1)):
            with self.subTest():

                #
                # Define the OpenMDAO problem
                #
                p = om.Problem(model=om.Group())

                #
                # Define a Trajectory object
                #
                traj = dm.Trajectory()

                p.model.add_subsystem('traj', subsys=traj)

                #
                # Define a Dymos Phase object with GaussLobatto Transcription
                #
                phase = dm.Phase(ode_class=BrachistochroneODE,
                                 transcription=dm.GaussLobatto(num_segments=10,
                                                               order=3))

                traj.add_phase(name='phase0', phase=phase)

                #
                # Set the time options
                # Time has no targets in our ODE.
                # We fix the initial time so that the it is not a design variable in the optimization.
                # The duration of the phase is allowed to be optimized, but is bounded on [0.5, 10].
                #
                phase.set_time_options(fix_initial=True,
                                       duration_bounds=(0.5, 10.0),
                                       units='s')

                #
                # Set the time options
                # Initial values of positions and velocity are all fixed.
                # The final value of position are fixed, but the final velocity is a free variable.
                # The equations of motion are not functions of position, so 'x' and 'y' have no targets.
                # The rate source points to the output in the ODE which provides the time derivative of the
                # given state.
                phase.add_state('x',
                                fix_initial=True,
                                fix_final=True,
                                rate_source='xdot')
                phase.add_state('y',
                                fix_initial=True,
                                fix_final=True,
                                rate_source='ydot')
                phase.add_state('v',
                                fix_initial=True,
                                fix_final=False,
                                rate_source='vdot')

                # Define theta as a control.
                phase.add_control(name='theta',
                                  units='rad',
                                  lower=0,
                                  upper=np.pi)

                # Minimize final time.
                phase.add_objective('time', loc='final')

                # Set the simulate options
                phase.set_simulate_options(method=method,
                                           atol=atol,
                                           first_step=first_step)

                # Set the driver.
                p.driver = om.ScipyOptimizeDriver()

                # Allow OpenMDAO to automatically determine our sparsity pattern.
                # Doing so can significant speed up the execution of Dymos.
                p.driver.declare_coloring()

                # Setup the problem
                p.setup(check=True)

                # Now that the OpenMDAO problem is setup, we can set the values of the states.
                p.set_val('traj.phase0.states:x',
                          phase.interp('x', [0, 10]),
                          units='m')

                p.set_val('traj.phase0.states:y',
                          phase.interp('y', [10, 5]),
                          units='m')

                p.set_val('traj.phase0.states:v',
                          phase.interp('v', [0, 5]),
                          units='m/s')

                p.set_val('traj.phase0.controls:theta',
                          phase.interp('theta', [90, 90]),
                          units='deg')

                # Run the driver to solve the problem
                dm.run_problem(p, run_driver=True, simulate=True)
Пример #7
0
    def test_reentry_mixed_controls(self):

        p = om.Problem(model=om.Group())
        p.driver = om.pyOptSparseDriver()
        p.driver.declare_coloring(tol=1.0E-12)
        p.driver.options['optimizer'] = 'IPOPT'
        p.driver.opt_settings['alpha_for_y'] = 'safer-min-dual-infeas'
        p.driver.opt_settings['print_level'] = 5
        p.driver.opt_settings['nlp_scaling_method'] = 'gradient-based'
        p.driver.opt_settings['mu_strategy'] = 'monotone'

        traj = p.model.add_subsystem('traj', Trajectory())
        phase0 = traj.add_phase(
            'phase0',
            Phase(ode_class=ShuttleODE,
                  transcription=Radau(num_segments=30, order=3)))

        phase0.set_time_options(fix_initial=True, units='s', duration_ref=200)
        phase0.add_state('h',
                         fix_initial=True,
                         fix_final=True,
                         units='ft',
                         rate_source='hdot',
                         targets=['h'],
                         lower=0,
                         ref0=75000,
                         ref=300000,
                         defect_ref=1000)
        phase0.add_state('gamma',
                         fix_initial=True,
                         fix_final=True,
                         units='rad',
                         rate_source='gammadot',
                         targets=['gamma'],
                         lower=-89. * np.pi / 180,
                         upper=89. * np.pi / 180)
        phase0.add_state('phi',
                         fix_initial=True,
                         fix_final=False,
                         units='rad',
                         rate_source='phidot',
                         lower=0,
                         upper=89. * np.pi / 180)
        phase0.add_state('psi',
                         fix_initial=True,
                         fix_final=False,
                         units='rad',
                         rate_source='psidot',
                         targets=['psi'],
                         lower=0,
                         upper=90. * np.pi / 180)
        phase0.add_state('theta',
                         fix_initial=True,
                         fix_final=False,
                         units='rad',
                         rate_source='thetadot',
                         targets=['theta'],
                         lower=-89. * np.pi / 180,
                         upper=89. * np.pi / 180)
        phase0.add_state('v',
                         fix_initial=True,
                         fix_final=True,
                         units='ft/s',
                         rate_source='vdot',
                         targets=['v'],
                         lower=500,
                         ref0=2500,
                         ref=25000)
        phase0.add_control('alpha',
                           units='rad',
                           opt=True,
                           lower=-np.pi / 2,
                           upper=np.pi / 2)
        phase0.add_polynomial_control('beta',
                                      order=9,
                                      units='rad',
                                      opt=True,
                                      lower=-89 * np.pi / 180,
                                      upper=1 * np.pi / 180)

        phase0.add_objective('theta', loc='final', ref=-0.01)
        phase0.add_path_constraint('q', lower=0, upper=70, ref=70)

        p.setup(check=True, force_alloc_complex=True)

        p.set_val('traj.phase0.states:h',
                  phase0.interp('h', [260000, 80000]),
                  units='ft')
        p.set_val('traj.phase0.states:gamma',
                  phase0.interp('gamma', [-1 * np.pi / 180, -5 * np.pi / 180]),
                  units='rad')
        p.set_val('traj.phase0.states:phi',
                  phase0.interp('phi', [0, 75 * np.pi / 180]),
                  units='rad')
        p.set_val('traj.phase0.states:psi',
                  phase0.interp('psi', [90 * np.pi / 180, 10 * np.pi / 180]),
                  units='rad')
        p.set_val('traj.phase0.states:theta',
                  phase0.interp('theta', [0, 25 * np.pi / 180]),
                  units='rad')
        p.set_val('traj.phase0.states:v',
                  phase0.interp('v', [25600, 2500]),
                  units='ft/s')
        p.set_val('traj.phase0.t_initial', 0, units='s')
        p.set_val('traj.phase0.t_duration', 2000, units='s')
        p.set_val('traj.phase0.controls:alpha',
                  phase0.interp('alpha', [17.4, 17.4]),
                  units='deg')
        p.set_val('traj.phase0.polynomial_controls:beta',
                  phase0.interp('beta', [-20, 0]),
                  units='deg')

        run_problem(p, simulate=True)

        sol = om.CaseReader('dymos_solution.db').get_case('final')
        sim = om.CaseReader('dymos_simulation.db').get_case('final')

        from scipy.interpolate import interp1d

        t_sol = sol.get_val('traj.phase0.timeseries.time')
        beta_sol = sol.get_val(
            'traj.phase0.timeseries.polynomial_controls:beta', units='deg')

        t_sim = sim.get_val('traj.phase0.timeseries.time')
        beta_sim = sim.get_val(
            'traj.phase0.timeseries.polynomial_controls:beta', units='deg')

        sol_interp = interp1d(t_sol.ravel(), beta_sol.ravel())
        sim_interp = interp1d(t_sim.ravel(), beta_sim.ravel())

        t = np.linspace(0, t_sol.ravel()[-1], 1000)

        assert_near_equal(sim_interp(t), sol_interp(t), tolerance=0.01)

        assert_near_equal(p.get_val('traj.phase0.timeseries.time')[-1],
                          expected_results['constrained']['time'],
                          tolerance=1e-2)

        assert_near_equal(p.get_val('traj.phase0.timeseries.states:theta',
                                    units='deg')[-1],
                          expected_results['constrained']['theta'],
                          tolerance=1e-2)
Пример #8
0
    def test_balanced_field_length_for_docs(self):
        import matplotlib.pyplot as plt
        import openmdao.api as om
        from openmdao.utils.general_utils import set_pyoptsparse_opt
        from openmdao.utils.assert_utils import assert_near_equal
        import dymos as dm
        from dymos.examples.balanced_field.balanced_field_ode import BalancedFieldODEComp

        p = om.Problem()

        _, optimizer = set_pyoptsparse_opt('IPOPT', fallback=True)

        p.driver = om.pyOptSparseDriver()
        p.driver.declare_coloring()

        # Use IPOPT if available, with fallback to SLSQP
        p.driver.options['optimizer'] = optimizer
        p.driver.options['print_results'] = False
        if optimizer == 'IPOPT':
            p.driver.opt_settings['print_level'] = 5
            p.driver.opt_settings['derivative_test'] = 'first-order'

        # First Phase: Brake release to V1 - both engines operable
        br_to_v1 = dm.Phase(ode_class=BalancedFieldODEComp,
                            transcription=dm.Radau(num_segments=3),
                            ode_init_kwargs={'mode': 'runway'})
        br_to_v1.set_time_options(fix_initial=True,
                                  duration_bounds=(1, 1000),
                                  duration_ref=10.0)
        br_to_v1.add_state('r',
                           fix_initial=True,
                           lower=0,
                           ref=1000.0,
                           defect_ref=1000.0)
        br_to_v1.add_state('v',
                           fix_initial=True,
                           lower=0,
                           ref=100.0,
                           defect_ref=100.0)
        br_to_v1.add_parameter('alpha', val=0.0, opt=False, units='deg')
        br_to_v1.add_timeseries_output('*')

        # Second Phase: Rejected takeoff at V1 - no engines operable
        rto = dm.Phase(ode_class=BalancedFieldODEComp,
                       transcription=dm.Radau(num_segments=3),
                       ode_init_kwargs={'mode': 'runway'})
        rto.set_time_options(fix_initial=False,
                             duration_bounds=(1, 1000),
                             duration_ref=1.0)
        rto.add_state('r',
                      fix_initial=False,
                      lower=0,
                      ref=1000.0,
                      defect_ref=1000.0)
        rto.add_state('v',
                      fix_initial=False,
                      lower=0,
                      ref=100.0,
                      defect_ref=100.0)
        rto.add_parameter('alpha', val=0.0, opt=False, units='deg')
        rto.add_timeseries_output('*')

        # Third Phase: V1 to Vr - single engine operable
        v1_to_vr = dm.Phase(ode_class=BalancedFieldODEComp,
                            transcription=dm.Radau(num_segments=3),
                            ode_init_kwargs={'mode': 'runway'})
        v1_to_vr.set_time_options(fix_initial=False,
                                  duration_bounds=(1, 1000),
                                  duration_ref=1.0)
        v1_to_vr.add_state('r',
                           fix_initial=False,
                           lower=0,
                           ref=1000.0,
                           defect_ref=1000.0)
        v1_to_vr.add_state('v',
                           fix_initial=False,
                           lower=0,
                           ref=100.0,
                           defect_ref=100.0)
        v1_to_vr.add_parameter('alpha', val=0.0, opt=False, units='deg')
        v1_to_vr.add_timeseries_output('*')

        # Fourth Phase: Rotate - single engine operable
        rotate = dm.Phase(ode_class=BalancedFieldODEComp,
                          transcription=dm.Radau(num_segments=3),
                          ode_init_kwargs={'mode': 'runway'})
        rotate.set_time_options(fix_initial=False,
                                duration_bounds=(1.0, 5),
                                duration_ref=1.0)
        rotate.add_state('r',
                         fix_initial=False,
                         lower=0,
                         ref=1000.0,
                         defect_ref=1000.0)
        rotate.add_state('v',
                         fix_initial=False,
                         lower=0,
                         ref=100.0,
                         defect_ref=100.0)
        rotate.add_polynomial_control('alpha',
                                      order=1,
                                      opt=True,
                                      units='deg',
                                      lower=0,
                                      upper=10,
                                      ref=10,
                                      val=[0, 10])
        rotate.add_timeseries_output('*')

        # Fifth Phase: Climb to target speed and altitude at end of runway.
        climb = dm.Phase(ode_class=BalancedFieldODEComp,
                         transcription=dm.Radau(num_segments=5),
                         ode_init_kwargs={'mode': 'climb'})
        climb.set_time_options(fix_initial=False,
                               duration_bounds=(1, 100),
                               duration_ref=1.0)
        climb.add_state('r',
                        fix_initial=False,
                        lower=0,
                        ref=1000.0,
                        defect_ref=1000.0)
        climb.add_state('h',
                        fix_initial=True,
                        lower=0,
                        ref=1.0,
                        defect_ref=1.0)
        climb.add_state('v',
                        fix_initial=False,
                        lower=0,
                        ref=100.0,
                        defect_ref=100.0)
        climb.add_state('gam',
                        fix_initial=True,
                        lower=0,
                        ref=0.05,
                        defect_ref=0.05)
        climb.add_control('alpha',
                          opt=True,
                          units='deg',
                          lower=-10,
                          upper=15,
                          ref=10)
        climb.add_timeseries_output('*')

        # Instantiate the trajectory and add phases
        traj = dm.Trajectory()
        p.model.add_subsystem('traj', traj)
        traj.add_phase('br_to_v1', br_to_v1)
        traj.add_phase('rto', rto)
        traj.add_phase('v1_to_vr', v1_to_vr)
        traj.add_phase('rotate', rotate)
        traj.add_phase('climb', climb)

        # Add parameters common to multiple phases to the trajectory
        traj.add_parameter('m',
                           val=174200.,
                           opt=False,
                           units='lbm',
                           desc='aircraft mass',
                           targets={
                               'br_to_v1': ['m'],
                               'v1_to_vr': ['m'],
                               'rto': ['m'],
                               'rotate': ['m'],
                               'climb': ['m']
                           })

        traj.add_parameter('T_nominal',
                           val=27000 * 2,
                           opt=False,
                           units='lbf',
                           static_target=True,
                           desc='nominal aircraft thrust',
                           targets={'br_to_v1': ['T']})

        traj.add_parameter('T_engine_out',
                           val=27000,
                           opt=False,
                           units='lbf',
                           static_target=True,
                           desc='thrust under a single engine',
                           targets={
                               'v1_to_vr': ['T'],
                               'rotate': ['T'],
                               'climb': ['T']
                           })

        traj.add_parameter(
            'T_shutdown',
            val=0.0,
            opt=False,
            units='lbf',
            static_target=True,
            desc='thrust when engines are shut down for rejected takeoff',
            targets={'rto': ['T']})

        traj.add_parameter('mu_r_nominal',
                           val=0.03,
                           opt=False,
                           units=None,
                           static_target=True,
                           desc='nominal runway friction coefficient',
                           targets={
                               'br_to_v1': ['mu_r'],
                               'v1_to_vr': ['mu_r'],
                               'rotate': ['mu_r']
                           })

        traj.add_parameter('mu_r_braking',
                           val=0.3,
                           opt=False,
                           units=None,
                           static_target=True,
                           desc='runway friction coefficient under braking',
                           targets={'rto': ['mu_r']})

        traj.add_parameter('h_runway',
                           val=0.,
                           opt=False,
                           units='ft',
                           desc='runway altitude',
                           targets={
                               'br_to_v1': ['h'],
                               'v1_to_vr': ['h'],
                               'rto': ['h'],
                               'rotate': ['h']
                           })

        traj.add_parameter('rho',
                           val=1.225,
                           opt=False,
                           units='kg/m**3',
                           static_target=True,
                           desc='atmospheric density',
                           targets={
                               'br_to_v1': ['rho'],
                               'v1_to_vr': ['rho'],
                               'rto': ['rho'],
                               'rotate': ['rho']
                           })

        traj.add_parameter('S',
                           val=124.7,
                           opt=False,
                           units='m**2',
                           static_target=True,
                           desc='aerodynamic reference area',
                           targets={
                               'br_to_v1': ['S'],
                               'v1_to_vr': ['S'],
                               'rto': ['S'],
                               'rotate': ['S'],
                               'climb': ['S']
                           })

        traj.add_parameter(
            'CD0',
            val=0.03,
            opt=False,
            units=None,
            static_target=True,
            desc='zero-lift drag coefficient',
            targets={
                f'{phase}': ['CD0']
                for phase in ['br_to_v1', 'v1_to_vr', 'rto', 'rotate'
                              'climb']
            })

        traj.add_parameter(
            'AR',
            val=9.45,
            opt=False,
            units=None,
            static_target=True,
            desc='wing aspect ratio',
            targets={
                f'{phase}': ['AR']
                for phase in ['br_to_v1', 'v1_to_vr', 'rto', 'rotate'
                              'climb']
            })

        traj.add_parameter(
            'e',
            val=801,
            opt=False,
            units=None,
            static_target=True,
            desc='Oswald span efficiency factor',
            targets={
                f'{phase}': ['e']
                for phase in ['br_to_v1', 'v1_to_vr', 'rto', 'rotate'
                              'climb']
            })

        traj.add_parameter(
            'span',
            val=35.7,
            opt=False,
            units='m',
            static_target=True,
            desc='wingspan',
            targets={
                f'{phase}': ['span']
                for phase in ['br_to_v1', 'v1_to_vr', 'rto', 'rotate'
                              'climb']
            })

        traj.add_parameter(
            'h_w',
            val=1.0,
            opt=False,
            units='m',
            static_target=True,
            desc='height of wing above CG',
            targets={
                f'{phase}': ['h_w']
                for phase in ['br_to_v1', 'v1_to_vr', 'rto', 'rotate'
                              'climb']
            })

        traj.add_parameter(
            'CL0',
            val=0.5,
            opt=False,
            units=None,
            static_target=True,
            desc='zero-alpha lift coefficient',
            targets={
                f'{phase}': ['CL0']
                for phase in ['br_to_v1', 'v1_to_vr', 'rto', 'rotate'
                              'climb']
            })

        traj.add_parameter(
            'CL_max',
            val=2.0,
            opt=False,
            units=None,
            static_target=True,
            desc='maximum lift coefficient for linear fit',
            targets={
                f'{phase}': ['CL_max']
                for phase in ['br_to_v1', 'v1_to_vr', 'rto', 'rotate'
                              'climb']
            })

        traj.add_parameter(
            'alpha_max',
            val=10.0,
            opt=False,
            units='deg',
            static_target=True,
            desc='angle of attack at maximum lift',
            targets={
                f'{phase}': ['alpha_max']
                for phase in ['br_to_v1', 'v1_to_vr', 'rto', 'rotate'
                              'climb']
            })

        # Standard "end of first phase to beginning of second phase" linkages
        # Alpha changes from being a parameter in v1_to_vr to a polynomial control
        # in rotate, to a dynamic control in `climb`.
        traj.link_phases(['br_to_v1', 'v1_to_vr'], vars=['time', 'r', 'v'])
        traj.link_phases(['v1_to_vr', 'rotate'],
                         vars=['time', 'r', 'v', 'alpha'])
        traj.link_phases(['rotate', 'climb'], vars=['time', 'r', 'v', 'alpha'])
        traj.link_phases(['br_to_v1', 'rto'], vars=['time', 'r', 'v'])

        # Less common "final value of r must be the match at ends of two phases".
        traj.add_linkage_constraint(phase_a='rto',
                                    var_a='r',
                                    loc_a='final',
                                    phase_b='climb',
                                    var_b='r',
                                    loc_b='final',
                                    ref=1000)

        # Define the constraints and objective for the optimal control problem
        v1_to_vr.add_boundary_constraint('v_over_v_stall',
                                         loc='final',
                                         lower=1.2,
                                         ref=100)

        rto.add_boundary_constraint('v',
                                    loc='final',
                                    equals=0.,
                                    ref=100,
                                    linear=True)

        rotate.add_boundary_constraint('F_r',
                                       loc='final',
                                       equals=0,
                                       ref=100000)

        climb.add_boundary_constraint('h',
                                      loc='final',
                                      equals=35,
                                      ref=35,
                                      units='ft',
                                      linear=True)
        climb.add_boundary_constraint('gam',
                                      loc='final',
                                      equals=5,
                                      ref=5,
                                      units='deg',
                                      linear=True)
        climb.add_path_constraint('gam', lower=0, upper=5, ref=5, units='deg')
        climb.add_boundary_constraint('v_over_v_stall',
                                      loc='final',
                                      lower=1.25,
                                      ref=1.25)

        rto.add_objective('r', loc='final', ref=1.0)

        #
        # Setup the problem and set the initial guess
        #
        p.setup(check=True)

        p.set_val('traj.br_to_v1.t_initial', 0)
        p.set_val('traj.br_to_v1.t_duration', 35)
        p.set_val('traj.br_to_v1.states:r', br_to_v1.interp('r', [0, 2500.0]))
        p.set_val('traj.br_to_v1.states:v', br_to_v1.interp('v', [0, 100.0]))
        p.set_val('traj.br_to_v1.parameters:alpha', 0, units='deg')

        p.set_val('traj.v1_to_vr.t_initial', 35)
        p.set_val('traj.v1_to_vr.t_duration', 35)
        p.set_val('traj.v1_to_vr.states:r',
                  v1_to_vr.interp('r', [2500, 300.0]))
        p.set_val('traj.v1_to_vr.states:v', v1_to_vr.interp('v', [100, 110.0]))
        p.set_val('traj.v1_to_vr.parameters:alpha', 0.0, units='deg')

        p.set_val('traj.rto.t_initial', 35)
        p.set_val('traj.rto.t_duration', 35)
        p.set_val('traj.rto.states:r', rto.interp('r', [2500, 5000.0]))
        p.set_val('traj.rto.states:v', rto.interp('v', [110, 0]))
        p.set_val('traj.rto.parameters:alpha', 0.0, units='deg')

        p.set_val('traj.rotate.t_initial', 70)
        p.set_val('traj.rotate.t_duration', 5)
        p.set_val('traj.rotate.states:r', rotate.interp('r', [1750, 1800.0]))
        p.set_val('traj.rotate.states:v', rotate.interp('v', [80, 85.0]))
        p.set_val('traj.rotate.polynomial_controls:alpha', 0.0, units='deg')

        p.set_val('traj.climb.t_initial', 75)
        p.set_val('traj.climb.t_duration', 15)
        p.set_val('traj.climb.states:r',
                  climb.interp('r', [5000, 5500.0]),
                  units='ft')
        p.set_val('traj.climb.states:v',
                  climb.interp('v', [160, 170.0]),
                  units='kn')
        p.set_val('traj.climb.states:h',
                  climb.interp('h', [0, 35.0]),
                  units='ft')
        p.set_val('traj.climb.states:gam',
                  climb.interp('gam', [0, 5.0]),
                  units='deg')
        p.set_val('traj.climb.controls:alpha', 5.0, units='deg')

        dm.run_problem(p, run_driver=True, simulate=True)

        # Test this example in Dymos' continuous integration
        assert_near_equal(p.get_val('traj.rto.states:r')[-1],
                          2188.2,
                          tolerance=0.01)

        sim_case = om.CaseReader('dymos_solution.db').get_case('final')

        fig, axes = plt.subplots(2, 1, sharex=True, gridspec_kw={'top': 0.92})
        for phase in ['br_to_v1', 'rto', 'v1_to_vr', 'rotate', 'climb']:
            r = sim_case.get_val(f'traj.{phase}.timeseries.states:r',
                                 units='ft')
            v = sim_case.get_val(f'traj.{phase}.timeseries.states:v',
                                 units='kn')
            t = sim_case.get_val(f'traj.{phase}.timeseries.time', units='s')
            axes[0].plot(t, r, '-', label=phase)
            axes[1].plot(t, v, '-', label=phase)
        fig.suptitle('Balanced Field Length')
        axes[1].set_xlabel('time (s)')
        axes[0].set_ylabel('range (ft)')
        axes[1].set_ylabel('airspeed (kts)')
        axes[0].grid(True)
        axes[1].grid(True)

        tv1 = sim_case.get_val('traj.br_to_v1.timeseries.time', units='s')[-1,
                                                                           0]
        v1 = sim_case.get_val('traj.br_to_v1.timeseries.states:v',
                              units='kn')[-1, 0]

        tf_rto = sim_case.get_val('traj.rto.timeseries.time', units='s')[-1, 0]
        rf_rto = sim_case.get_val('traj.rto.timeseries.states:r',
                                  units='ft')[-1, 0]

        axes[0].annotate(f'field length = {r[-1, 0]:5.1f} ft',
                         xy=(t[-1, 0], r[-1, 0]),
                         xycoords='data',
                         xytext=(0.7, 0.5),
                         textcoords='axes fraction',
                         arrowprops=dict(arrowstyle='->'),
                         horizontalalignment='center',
                         verticalalignment='top')

        axes[0].annotate(f'',
                         xy=(tf_rto, rf_rto),
                         xycoords='data',
                         xytext=(0.7, 0.5),
                         textcoords='axes fraction',
                         arrowprops=dict(arrowstyle='->'),
                         horizontalalignment='center',
                         verticalalignment='top')

        axes[1].annotate(f'$v1$ = {v1:5.1f} kts',
                         xy=(tv1, v1),
                         xycoords='data',
                         xytext=(0.5, 0.5),
                         textcoords='axes fraction',
                         arrowprops=dict(arrowstyle='->'),
                         horizontalalignment='center',
                         verticalalignment='top')

        plt.legend()

        if SHOW_PLOTS:
            plt.show()
Пример #9
0
    def _test_static_ode_output(self, transcription):

        #
        # Define the OpenMDAO problem
        #
        p = om.Problem(model=om.Group())

        #
        # Define a Trajectory object
        #
        traj = dm.Trajectory()

        p.model.add_subsystem('traj', subsys=traj)

        #
        # Define a Dymos Phase object with GaussLobatto Transcription
        #
        phase = dm.Phase(ode_class=BrachODEStaticOutput,
                         transcription=transcription(num_segments=10, order=3))

        traj.add_phase(name='phase0', phase=phase)

        #
        # Set the time options
        # Time has no targets in our ODE.
        # We fix the initial time so that the it is not a design variable in the optimization.
        # The duration of the phase is allowed to be optimized, but is bounded on [0.5, 10].
        #
        phase.set_time_options(fix_initial=True,
                               duration_bounds=(1.0, 10.0),
                               units='s')

        #
        # Set the time options
        # Initial values of positions and velocity are all fixed.
        # The final value of position are fixed, but the final velocity is a free variable.
        # The equations of motion are not functions of position, so 'x' and 'y' have no targets.
        # The rate source points to the output in the ODE which provides the time derivative of the
        # given state.
        phase.add_state('x', fix_initial=True, fix_final=True)
        phase.add_state('y', fix_initial=True, fix_final=True)
        phase.add_state('v', fix_initial=True)

        # Define theta as a control.
        phase.add_control(name='theta',
                          units='rad',
                          lower=0.01,
                          upper=np.pi - 0.01,
                          shape=(1, ))

        phase.add_timeseries_output('*')

        # Minimize final time.
        phase.add_objective('time', loc='final')

        # Set the driver.
        p.driver = om.ScipyOptimizeDriver()

        # Allow OpenMDAO to automatically determine our sparsity pattern.
        # Doing so can significant speed up the execution of Dymos.
        p.driver.declare_coloring()

        # Setup the problem
        with warnings.catch_warnings(record=True) as ctx:
            warnings.simplefilter('always')
            p.setup(check=True)
            expected_warning = "Cannot add ODE output foo to the timeseries output. It is sized " \
                               "such that its first dimension != num_nodes."
            self.assertIn(expected_warning, [str(w.message) for w in ctx])

        # Now that the OpenMDAO problem is setup, we can set the values of the states.

        p.set_val('traj.phase0.t_initial', 0.0, units='s')
        p.set_val('traj.phase0.t_duration', 5.0, units='s')

        p.set_val('traj.phase0.states:x',
                  phase.interpolate(ys=[0, 10], nodes='state_input'),
                  units='m')

        p.set_val('traj.phase0.states:y',
                  phase.interpolate(ys=[10, 5], nodes='state_input'),
                  units='m')

        p.set_val('traj.phase0.states:v',
                  phase.interpolate(ys=[0, 5], nodes='state_input'),
                  units='m/s')

        p.set_val('traj.phase0.controls:theta',
                  phase.interpolate(ys=[0.01, 90], nodes='control_input'),
                  units='deg')

        # Run the driver to solve the problem
        with warnings.catch_warnings(record=True) as ctx:
            warnings.simplefilter('always')
            dm.run_problem(p, simulate=True, make_plots=False)
            expected_warning = "Cannot add ODE output foo to the timeseries output. It is sized " \
                               "such that its first dimension != num_nodes."
            self.assertIn(expected_warning, [str(w.message) for w in ctx])

        sol = om.CaseReader('dymos_solution.db').get_case('final')
        sim = om.CaseReader('dymos_simulation.db').get_case('final')

        with self.assertRaises(expected_exception=KeyError) as e:
            sol.get_val('traj.phase0.timeseries.foo')
        self.assertEqual(
            str(e.exception),
            "'Variable name \"traj.phase0.timeseries.foo\" not found.'")

        with self.assertRaises(expected_exception=KeyError) as e:
            sim.get_val('traj.phase0.timeseries.foo')
        self.assertEqual(
            str(e.exception),
            "'Variable name \"traj.phase0.timeseries.foo\" not found.'")
Пример #10
0
                fix_final=True,
                solve_segments=False)

phase.add_boundary_constraint('T',
                              loc='final',
                              units='K',
                              upper=60,
                              lower=20,
                              shape=(1, ))
phase.add_parameter('d', opt=True, lower=0.00001, upper=0.1)
phase.add_objective('d', loc='final', scaler=1)
p.model.linear_solver = om.DirectSolver()
p.setup()
p['traj.phase0.t_initial'] = 0.0
p['traj.phase0.states:T'] = phase.interpolate(ys=[20, 60], nodes='state_input')
dm.run_problem(p)
exp_out = traj.simulate()
plot_results([('traj.phase0.timeseries.time',
               'traj.phase0.timeseries.states:T', 'time (s)', 'temp (K)')],
             title='Temps',
             p_sol=p,
             p_sim=exp_out)
plt.show()

# t = np.linspace(0, 45, 101)

# def dT_calc(Ts,t):

#     Tf = Ts[0]
#     Tc = Ts[1]
#     K = 0.03 # W/mk
Пример #11
0
def ex_aircraft_steady_flight(optimizer='SLSQP',
                              solve_segments=False,
                              use_boundary_constraints=False,
                              compressed=False):
    p = om.Problem(model=om.Group())
    p.driver = om.pyOptSparseDriver()
    _, optimizer = set_pyoptsparse_opt(optimizer, fallback=False)
    p.driver.options['optimizer'] = optimizer
    p.driver.declare_coloring()
    if optimizer == 'SNOPT':
        p.driver.opt_settings['Major iterations limit'] = 20
        p.driver.opt_settings['Major feasibility tolerance'] = 1.0E-6
        p.driver.opt_settings['Major optimality tolerance'] = 1.0E-6
        p.driver.opt_settings["Linesearch tolerance"] = 0.10
        p.driver.opt_settings['iSumm'] = 6
    if optimizer == 'SLSQP':
        p.driver.opt_settings['MAXIT'] = 50

    num_seg = 15
    seg_ends, _ = lgl(num_seg + 1)

    phase = dm.Phase(ode_class=AircraftODE,
                     transcription=dm.Radau(num_segments=num_seg,
                                            segment_ends=seg_ends,
                                            order=3,
                                            compressed=compressed,
                                            solve_segments=solve_segments))

    # Pass Reference Area from an external source
    assumptions = p.model.add_subsystem('assumptions', om.IndepVarComp())
    assumptions.add_output('S', val=427.8, units='m**2')
    assumptions.add_output('mass_empty', val=1.0, units='kg')
    assumptions.add_output('mass_payload', val=1.0, units='kg')

    p.model.add_subsystem('phase0', phase)

    phase.set_time_options(initial_bounds=(0, 0),
                           duration_bounds=(300, 10000),
                           duration_ref=5600)

    fix_final = True
    if use_boundary_constraints:
        fix_final = False
        phase.add_boundary_constraint('mass_fuel',
                                      loc='final',
                                      units='lbm',
                                      equals=1e-3,
                                      linear=False)
        phase.add_boundary_constraint('alt',
                                      loc='final',
                                      units='kft',
                                      equals=10.0,
                                      linear=False)

    phase.add_state('range',
                    units='NM',
                    rate_source='range_rate_comp.dXdt:range',
                    fix_initial=True,
                    fix_final=False,
                    ref=1e-3,
                    defect_ref=1e-3,
                    lower=0,
                    upper=2000)

    phase.add_state('mass_fuel',
                    units='lbm',
                    rate_source='propulsion.dXdt:mass_fuel',
                    fix_initial=True,
                    fix_final=fix_final,
                    upper=1.5E5,
                    lower=0.0,
                    ref=1e2,
                    defect_ref=1e2)

    phase.add_state('alt',
                    units='kft',
                    rate_source='climb_rate',
                    fix_initial=True,
                    fix_final=fix_final,
                    lower=0.0,
                    upper=60,
                    ref=1e-3,
                    defect_ref=1e-3)

    phase.add_control('climb_rate',
                      units='ft/min',
                      opt=True,
                      lower=-3000,
                      upper=3000,
                      targets=['gam_comp.climb_rate'],
                      rate_continuity=True,
                      rate2_continuity=False)

    phase.add_control('mach',
                      targets=['tas_comp.mach', 'aero.mach'],
                      opt=False)

    phase.add_parameter(
        'S',
        targets=['aero.S', 'flight_equilibrium.S', 'propulsion.S'],
        units='m**2')

    phase.add_parameter('mass_empty',
                        targets=['mass_comp.mass_empty'],
                        units='kg')
    phase.add_parameter('mass_payload',
                        targets=['mass_comp.mass_payload'],
                        units='kg')

    phase.add_path_constraint('propulsion.tau',
                              lower=0.01,
                              upper=2.0,
                              shape=(1, ))

    p.model.connect('assumptions.S', 'phase0.parameters:S')
    p.model.connect('assumptions.mass_empty', 'phase0.parameters:mass_empty')
    p.model.connect('assumptions.mass_payload',
                    'phase0.parameters:mass_payload')

    phase.add_objective('range', loc='final', ref=-1.0e-4)

    p.setup()

    p['phase0.t_initial'] = 0.0
    p['phase0.t_duration'] = 3600.0
    p['phase0.states:range'][:] = phase.interpolate(ys=(0, 724.0),
                                                    nodes='state_input')
    p['phase0.states:mass_fuel'][:] = phase.interpolate(ys=(30000, 1e-3),
                                                        nodes='state_input')
    p['phase0.states:alt'][:] = 10.0

    p['phase0.controls:mach'][:] = 0.8

    p['assumptions.S'] = 427.8
    p['assumptions.mass_empty'] = 0.15E6
    p['assumptions.mass_payload'] = 84.02869 * 400

    dm.run_problem(p)

    return p
    def _test_transcription(self, transcription=dm.GaussLobatto):
        #
        # Define the OpenMDAO problem
        #
        p = om.Problem(model=om.Group())

        #
        # Define a Trajectory object
        #
        traj = dm.Trajectory()

        p.model.add_subsystem('traj', subsys=traj)

        #
        # Define a Dymos Phase object with GaussLobatto Transcription
        #
        phase = dm.Phase(ode_class=BrachistochroneODE,
                         transcription=transcription(num_segments=10,
                                                     order=3,
                                                     solve_segments='forward'))

        traj.add_phase(name='phase0', phase=phase)

        #
        # Set the time options
        # Time has no targets in our ODE.
        # We fix the initial time so that the it is not a design variable in the optimization.
        # The duration of the phase is allowed to be optimized, but is bounded on [0.5, 10].
        #
        phase.set_time_options(fix_initial=True, fix_duration=True, units='s')

        #
        # Set the time options
        # Initial values of positions and velocity are all fixed.
        # The final value of position are fixed, but the final velocity is a free variable.
        # The equations of motion are not functions of position, so 'x' and 'y' have no targets.
        # The rate source points to the output in the ODE which provides the time derivative of the
        # given state.
        phase.add_state('x', fix_initial=True)
        phase.add_state('y', fix_initial=True)
        phase.add_state('v', fix_initial=True)

        phase.add_state('int_one', fix_initial=True, rate_source='one')
        phase.add_state('int_time', fix_initial=True, rate_source='time')
        phase.add_state('int_time_phase',
                        fix_initial=True,
                        rate_source='time_phase')
        phase.add_state('int_int_one', fix_initial=True, rate_source='int_one')

        # Define theta as a control.
        phase.add_control(name='theta', units='rad', lower=0, upper=np.pi)

        # With no targets we must explicitly assign units and shape to this parameter.
        # Its only purpose is to be integrated as the rate source for a state.
        phase.add_parameter(name='one', opt=False, units=None, shape=(1, ))

        # Minimize final time.
        phase.add_objective('time', loc='final')

        # Set the driver.
        p.driver = om.ScipyOptimizeDriver()

        # Allow OpenMDAO to automatically determine our sparsity pattern.
        # Doing so can significant speed up the execution of Dymos.
        p.driver.declare_coloring()

        # Setup the problem
        p.setup(check=True)

        # Now that the OpenMDAO problem is setup, we can set the values of the states.

        p.set_val('traj.phase0.t_initial', 0.0, units='s')
        p.set_val('traj.phase0.t_duration', 5.0, units='s')

        p.set_val('traj.phase0.parameters:one', 1.0)

        p.set_val('traj.phase0.states:x',
                  phase.interp('x', [0, 10]),
                  units='m')

        p.set_val('traj.phase0.states:y',
                  phase.interp('y', [10, 5]),
                  units='m')

        p.set_val('traj.phase0.states:v',
                  phase.interp('v', [0, 5]),
                  units='m/s')

        p.set_val('traj.phase0.controls:theta',
                  phase.interp('theta', [0.1, 45], nodes='control_input'),
                  units='deg')

        # Additional states to test rate sources
        p.set_val('traj.phase0.states:int_one',
                  phase.interp('int_one', [0, 10]),
                  units='s')

        p.set_val('traj.phase0.states:int_time',
                  phase.interp('int_time', [0, 10]),
                  units='s**2')

        p.set_val('traj.phase0.states:int_time_phase',
                  phase.interp('int_time_phase', [0, 10]),
                  units='s**2')

        p.set_val('traj.phase0.states:int_int_one',
                  phase.interp('int_int_one', [0, 10]),
                  units='s**2')

        # Run the driver to solve the problem
        dm.run_problem(p, simulate=True)

        time_sol = p.get_val('traj.phase0.timeseries.time')
        time_phase_sol = p.get_val('traj.phase0.timeseries.time_phase')
        int_one_sol = p.get_val('traj.phase0.timeseries.states:int_one')
        int_time_sol = p.get_val('traj.phase0.timeseries.states:int_time')
        int_time_phase_sol = p.get_val(
            'traj.phase0.timeseries.states:int_time_phase')
        int_int_one_sol = p.get_val(
            'traj.phase0.timeseries.states:int_int_one')

        time_sim = p.get_val('traj.phase0.timeseries.time')
        time_phase_sim = p.get_val('traj.phase0.timeseries.time_phase')
        int_one_sim = p.get_val('traj.phase0.timeseries.states:int_one')
        int_time_sim = p.get_val('traj.phase0.timeseries.states:int_time')
        int_time_phase_sim = p.get_val(
            'traj.phase0.timeseries.states:int_time_phase')
        int_int_one_sim = p.get_val(
            'traj.phase0.timeseries.states:int_int_one')

        # Integral of one should match time and time_phase in this case.
        assert_near_equal(int_one_sol, time_sol, tolerance=1.0E-12)
        assert_near_equal(int_one_sol, time_phase_sol, tolerance=1.0E-12)

        assert_near_equal(int_one_sim, time_sim, tolerance=1.0E-12)
        assert_near_equal(int_one_sim, time_phase_sim, tolerance=1.0E-12)

        # Integral of time and time_phase should be t**2/2
        assert_near_equal(time_sol, time_phase_sol, tolerance=1.0E-12)
        assert_near_equal(int_time_sol, time_sol**2 / 2, tolerance=1.0E-12)
        assert_near_equal(int_time_phase_sol,
                          time_phase_sol**2 / 2,
                          tolerance=1.0E-12)

        assert_near_equal(time_sim, time_phase_sim, tolerance=1.0E-12)
        assert_near_equal(int_time_sim, time_sim**2 / 2, tolerance=1.0E-12)
        assert_near_equal(int_time_phase_sim,
                          time_phase_sim**2 / 2,
                          tolerance=1.0E-12)

        # Double integral of one should be the same as the integral of time
        assert_near_equal(int_int_one_sol, int_time_sol, tolerance=1.0E-12)
        assert_near_equal(int_int_one_sim, int_time_sim, tolerance=1.0E-12)

        assert_timeseries_near_equal(time_sol, int_int_one_sol, time_sim,
                                     int_int_one_sim)
    def _test_integrate_polynomial_control_rate2(self, transcription):
        #
        # Define the OpenMDAO problem
        #
        p = om.Problem(model=om.Group())

        #
        # Define a Trajectory object
        #
        traj = dm.Trajectory()

        p.model.add_subsystem('traj', subsys=traj)

        #
        # Define a Dymos Phase object with GaussLobatto Transcription
        #
        phase = dm.Phase(ode_class=BrachistochroneODE,
                         transcription=transcription(num_segments=20, order=3))

        traj.add_phase(name='phase0', phase=phase)

        #
        # Set the time options
        # Time has no targets in our ODE.
        # We fix the initial time so that the it is not a design variable in the optimization.
        # The duration of the phase is allowed to be optimized, but is bounded on [0.5, 10].
        #
        phase.set_time_options(fix_initial=True,
                               duration_bounds=(1.0, 10.0),
                               units='s')

        #
        # Set the time options
        # Initial values of positions and velocity are all fixed.
        # The final value of position are fixed, but the final velocity is a free variable.
        # The equations of motion are not functions of position, so 'x' and 'y' have no targets.
        # The rate source points to the output in the ODE which provides the time derivative of the
        # given state.
        phase.add_state('x', fix_initial=True, fix_final=True)
        phase.add_state('y', fix_initial=True, fix_final=True)
        phase.add_state('v', fix_initial=True)

        phase.add_state('int_theta_dot',
                        fix_initial=False,
                        rate_source='theta_rate2')
        phase.add_state('int_theta',
                        fix_initial=False,
                        rate_source='int_theta_dot',
                        targets=['theta'])

        # Define theta as a control.
        phase.add_polynomial_control(name='theta',
                                     order=11,
                                     units='rad',
                                     shape=(1, ),
                                     targets=None)

        # Force the initial value of the theta polynomial control to equal the initial value of the theta state.
        traj.add_linkage_constraint(phase_a='phase0',
                                    phase_b='phase0',
                                    var_a='theta',
                                    var_b='int_theta',
                                    loc_a='initial',
                                    loc_b='initial')

        traj.add_linkage_constraint(phase_a='phase0',
                                    phase_b='phase0',
                                    var_a='int_theta_dot',
                                    var_b='theta_rate',
                                    loc_a='initial',
                                    loc_b='initial',
                                    units='rad/s')

        # Minimize final time.
        phase.add_objective('time', loc='final')

        # Set the driver.
        p.driver = om.pyOptSparseDriver(optimizer='SLSQP')

        # Allow OpenMDAO to automatically determine our sparsity pattern.
        # Doing so can significant speed up the execution of Dymos.
        p.driver.declare_coloring()

        # Setup the problem
        p.setup(check=True)

        # Now that the OpenMDAO problem is setup, we can set the values of the states.

        p.set_val('traj.phase0.t_initial', 0.0, units='s')
        p.set_val('traj.phase0.t_duration', 5.0, units='s')

        p.set_val('traj.phase0.states:x',
                  phase.interp('x', [0, 10]),
                  units='m')
        p.set_val('traj.phase0.states:y',
                  phase.interp('y', [10, 5]),
                  units='m')
        p.set_val('traj.phase0.states:v',
                  phase.interp('v', [0, 5]),
                  units='m/s')
        p.set_val('traj.phase0.states:int_theta',
                  phase.interp('int_theta', [0.1, 45]),
                  units='deg')
        p.set_val('traj.phase0.states:int_theta_dot',
                  phase.interp('int_theta_dot', [0, 0]),
                  units='deg/s')
        p.set_val('traj.phase0.polynomial_controls:theta', 45.0, units='deg')

        # Run the driver to solve the problem
        dm.run_problem(p, simulate=True, make_plots=True)

        sol = om.CaseReader('dymos_solution.db').get_case('final')
        sim = om.CaseReader('dymos_simulation.db').get_case('final')

        t_sol = sol.get_val('traj.phase0.timeseries.time')
        t_sim = sim.get_val('traj.phase0.timeseries.time')

        x_sol = sol.get_val('traj.phase0.timeseries.states:x')
        x_sim = sim.get_val('traj.phase0.timeseries.states:x')

        y_sol = sol.get_val('traj.phase0.timeseries.states:y')
        y_sim = sim.get_val('traj.phase0.timeseries.states:y')

        v_sol = sol.get_val('traj.phase0.timeseries.states:v')
        v_sim = sim.get_val('traj.phase0.timeseries.states:v')

        int_theta_sol = sol.get_val('traj.phase0.timeseries.states:int_theta')
        int_theta_sim = sim.get_val('traj.phase0.timeseries.states:int_theta')

        theta_sol = sol.get_val(
            'traj.phase0.timeseries.polynomial_controls:theta')
        theta_sim = sim.get_val(
            'traj.phase0.timeseries.polynomial_controls:theta')

        assert_timeseries_near_equal(t_sol,
                                     x_sol,
                                     t_sim,
                                     x_sim,
                                     tolerance=1.0E-2)
        assert_timeseries_near_equal(t_sol,
                                     y_sol,
                                     t_sim,
                                     y_sim,
                                     tolerance=1.0E-2)
        assert_timeseries_near_equal(t_sol,
                                     v_sol,
                                     t_sim,
                                     v_sim,
                                     tolerance=1.0E-2)
        assert_timeseries_near_equal(t_sol,
                                     int_theta_sol,
                                     t_sim,
                                     int_theta_sim,
                                     tolerance=1.0E-2)
Пример #14
0
    def test_ivp_driver_run_problem(self):
        import openmdao.api as om
        import dymos as dm
        import matplotlib.pyplot as plt
        plt.switch_backend('Agg')  # disable plotting to the screen

        from dymos.examples.oscillator.doc.oscillator_ode import OscillatorODE

        # Instantiate an OpenMDAO Problem instance.
        prob = om.Problem()

        # We need an optimization driver.  To solve this simple problem ScipyOptimizerDriver will work.
        prob.driver = om.ScipyOptimizeDriver()

        # Instantiate a Dymos Trajectory and add it to the Problem model.
        traj = dm.Trajectory()
        prob.model.add_subsystem('traj', traj)

        # Instantiate a Phase and add it to the Trajectory.
        phase = dm.Phase(ode_class=OscillatorODE,
                         transcription=dm.Radau(num_segments=4))
        traj.add_phase('phase0', phase)

        # Tell Dymos that the duration of the phase is bounded.
        phase.set_time_options(fix_initial=True, fix_duration=True)

        # Tell Dymos the states to be propagated using the given ODE.
        phase.add_state('x',
                        fix_initial=True,
                        rate_source='v',
                        targets=['x'],
                        units='m')
        phase.add_state('v',
                        fix_initial=True,
                        rate_source='v_dot',
                        targets=['v'],
                        units='m/s')

        # The spring constant, damping coefficient, and mass are inputs to the system that are
        # constant throughout the phase.
        phase.add_parameter('k', units='N/m', targets=['k'])
        phase.add_parameter('c', units='N*s/m', targets=['c'])
        phase.add_parameter('m', units='kg', targets=['m'])

        # Since we're using an optimization driver, an objective is required.  We'll minimize
        # the final time in this case.
        phase.add_objective('time', loc='final')

        # Setup the OpenMDAO problem
        prob.setup()

        # Assign values to the times and states
        prob.set_val('traj.phase0.t_initial', 0.0)
        prob.set_val('traj.phase0.t_duration', 15.0)

        prob.set_val('traj.phase0.states:x', 10.0)
        prob.set_val('traj.phase0.states:v', 0.0)

        prob.set_val('traj.phase0.parameters:k', 1.0)
        prob.set_val('traj.phase0.parameters:c', 0.5)
        prob.set_val('traj.phase0.parameters:m', 1.0)

        # Now we're using the optimization driver to iteratively run the model and vary the
        # phase duration until the final y value is 0.
        dm.run_problem(prob)

        # Perform an explicit simulation of our ODE from the initial conditions.
        sim_out = traj.simulate(times_per_seg=50)

        # Plot the state values obtained from the phase timeseries objects in the simulation output.
        t_sol = prob.get_val('traj.phase0.timeseries.time')
        t_sim = sim_out.get_val('traj.phase0.timeseries.time')

        states = ['x', 'v']
        fig, axes = plt.subplots(len(states), 1)
        for i, state in enumerate(states):
            sol = axes[i].plot(
                t_sol, prob.get_val(f'traj.phase0.timeseries.states:{state}'),
                'o')
            sim = axes[i].plot(
                t_sim,
                sim_out.get_val(f'traj.phase0.timeseries.states:{state}'), '-')
            axes[i].set_ylabel(state)
        axes[-1].set_xlabel('time (s)')
        fig.legend((sol[0], sim[0]), ('solution', 'simulation'),
                   'lower right',
                   ncol=2)
        plt.tight_layout()
        plt.show()
Пример #15
0
    def test_racecar_for_docs(self):
        import numpy as np
        import openmdao.api as om
        from openmdao.utils.assert_utils import assert_near_equal
        import dymos as dm
        import matplotlib.pyplot as plt
        import matplotlib as mpl

        from dymos.examples.racecar.combinedODE import CombinedODE
        from dymos.examples.racecar.spline import (
            get_spline,
            get_track_points,
            get_gate_normals,
            reverse_transform_gates,
            set_gate_displacements,
            transform_gates,
        )
        from dymos.examples.racecar.linewidthhelper import linewidth_from_data_units
        from dymos.examples.racecar.tracks import ovaltrack  # track curvature imports

        # change track here and in curvature.py. Tracks are defined in tracks.py
        track = ovaltrack

        # generate nodes along the centerline for curvature calculation (different
        # than collocation nodes)
        points = get_track_points(track)

        # fit the centerline spline.
        finespline, gates, gatesd, curv, slope = get_spline(points, s=0.0)
        # by default 10000 points
        s_final = track.get_total_length()

        # Define the OpenMDAO problem
        p = om.Problem(model=om.Group())

        # Define a Trajectory object
        traj = dm.Trajectory()
        p.model.add_subsystem('traj', subsys=traj)

        # Define a Dymos Phase object with GaussLobatto Transcription
        phase = dm.Phase(ode_class=CombinedODE,
                         transcription=dm.GaussLobatto(num_segments=50,
                                                       order=3,
                                                       compressed=True))

        traj.add_phase(name='phase0', phase=phase)

        # Set the time options, in this problem we perform a change of variables. So 'time' is
        # actually 's' (distance along the track centerline)
        # This is done to fix the collocation nodes in space, which saves us the calculation of
        # the rate of change of curvature.
        # The state equations are written with respect to time, the variable change occurs in
        # timeODE.py
        phase.set_time_options(fix_initial=True,
                               fix_duration=True,
                               duration_val=s_final,
                               targets=['curv.s'],
                               units='m',
                               duration_ref=s_final,
                               duration_ref0=10)

        # Define states
        phase.add_state('t',
                        fix_initial=True,
                        fix_final=False,
                        units='s',
                        lower=0,
                        rate_source='dt_ds',
                        ref=100)  # time
        phase.add_state(
            'n',
            fix_initial=False,
            fix_final=False,
            units='m',
            upper=4.0,
            lower=-4.0,
            rate_source='dn_ds',
            targets=['n'],
            ref=4.0
        )  # normal distance to centerline. The bounds on n define the
        # width of the track
        phase.add_state('V',
                        fix_initial=False,
                        fix_final=False,
                        units='m/s',
                        ref=40,
                        ref0=5,
                        rate_source='dV_ds',
                        targets=['V'])  # velocity
        phase.add_state(
            'alpha',
            fix_initial=False,
            fix_final=False,
            units='rad',
            rate_source='dalpha_ds',
            targets=['alpha'],
            ref=0.15)  # vehicle heading angle with respect to centerline
        phase.add_state(
            'lambda',
            fix_initial=False,
            fix_final=False,
            units='rad',
            rate_source='dlambda_ds',
            targets=['lambda'],
            ref=0.01
        )  # vehicle slip angle, or angle between the axis of the vehicle
        # and velocity vector (all cars drift a little)
        phase.add_state('omega',
                        fix_initial=False,
                        fix_final=False,
                        units='rad/s',
                        rate_source='domega_ds',
                        targets=['omega'],
                        ref=0.3)  # yaw rate
        phase.add_state('ax',
                        fix_initial=False,
                        fix_final=False,
                        units='m/s**2',
                        rate_source='dax_ds',
                        targets=['ax'],
                        ref=8)  # longitudinal acceleration
        phase.add_state('ay',
                        fix_initial=False,
                        fix_final=False,
                        units='m/s**2',
                        rate_source='day_ds',
                        targets=['ay'],
                        ref=8)  # lateral acceleration

        # Define Controls
        phase.add_control(name='delta',
                          units='rad',
                          lower=None,
                          upper=None,
                          fix_initial=False,
                          fix_final=False,
                          targets=['delta'],
                          ref=0.04)  # steering angle
        phase.add_control(
            name='thrust',
            units=None,
            fix_initial=False,
            fix_final=False,
            targets=['thrust']
        )  # the thrust controls the longitudinal force of the rear tires and is
        # positive while accelerating, negative while braking

        # Performance Constraints
        pmax = 960000  # W
        phase.add_path_constraint('power', upper=pmax,
                                  ref=100000)  # engine power limit

        # The following four constraints are the tire friction limits, with 'rr' designating the
        # rear right wheel etc. This limit is computed in tireConstraintODE.py
        phase.add_path_constraint('c_rr', upper=1)
        phase.add_path_constraint('c_rl', upper=1)
        phase.add_path_constraint('c_fr', upper=1)
        phase.add_path_constraint('c_fl', upper=1)

        # Some of the vehicle design parameters are available to set here. Other parameters can
        # be found in their respective ODE files.
        phase.add_parameter(
            'M',
            val=800.0,
            units='kg',
            opt=False,
            targets=['car.M', 'tire.M', 'tireconstraint.M', 'normal.M'],
            dynamic=False)  # vehicle mass
        phase.add_parameter('beta',
                            val=0.62,
                            units=None,
                            opt=False,
                            targets=['tire.beta'],
                            dynamic=False)  # brake bias
        phase.add_parameter('CoP',
                            val=1.6,
                            units='m',
                            opt=False,
                            targets=['normal.CoP'],
                            dynamic=False)  # center of pressure location
        phase.add_parameter('h',
                            val=0.3,
                            units='m',
                            opt=False,
                            targets=['normal.h'],
                            dynamic=False)  # center of gravity height
        phase.add_parameter('chi',
                            val=0.5,
                            units=None,
                            opt=False,
                            targets=['normal.chi'],
                            dynamic=False)  # roll stiffness
        phase.add_parameter('ClA',
                            val=4.0,
                            units='m**2',
                            opt=False,
                            targets=['normal.ClA'],
                            dynamic=False)  # downforce coefficient*area
        phase.add_parameter('CdA',
                            val=2.0,
                            units='m**2',
                            opt=False,
                            targets=['car.CdA'],
                            dynamic=False)  # drag coefficient*area

        # Minimize final time.
        # note that we use the 'state' time instead of Dymos 'time'
        phase.add_objective('t', loc='final')

        # Add output timeseries
        phase.add_timeseries_output('*')

        # Link the states at the start and end of the phase in order to ensure a continous lap
        traj.link_phases(
            phases=['phase0', 'phase0'],
            vars=['V', 'n', 'alpha', 'omega', 'lambda', 'ax', 'ay'],
            locs=('final', 'initial'))

        # Set the driver. IPOPT or SNOPT are recommended but SLSQP might work.
        p.driver = om.pyOptSparseDriver(optimizer='IPOPT')

        p.driver.opt_settings['mu_init'] = 1e-3
        p.driver.opt_settings['max_iter'] = 500
        p.driver.opt_settings['acceptable_tol'] = 1e-3
        p.driver.opt_settings['constr_viol_tol'] = 1e-3
        p.driver.opt_settings['compl_inf_tol'] = 1e-3
        p.driver.opt_settings['acceptable_iter'] = 0
        p.driver.opt_settings['tol'] = 1e-3
        p.driver.opt_settings['nlp_scaling_method'] = 'none'
        p.driver.opt_settings['print_level'] = 5
        p.driver.opt_settings[
            'nlp_scaling_method'] = 'gradient-based'  # for faster convergence
        p.driver.options['print_results'] = False

        # Allow OpenMDAO to automatically determine our sparsity pattern.
        # Doing so can significant speed up the execution of Dymos.
        p.driver.declare_coloring()

        # Setup the problem
        p.setup(check=True)  # force_alloc_complex=True
        # Now that the OpenMDAO problem is setup, we can set the values of the states.

        # States
        p.set_val(
            'traj.phase0.states:V',
            phase.interpolate(ys=[20, 20], nodes='state_input'),
            units='m/s'
        )  # non-zero velocity in order to protect against 1/0 errors.
        p.set_val('traj.phase0.states:lambda',
                  phase.interpolate(ys=[0.0, 0.0], nodes='state_input'),
                  units='rad')  # all other states start at 0
        p.set_val('traj.phase0.states:omega',
                  phase.interpolate(ys=[0.0, 0.0], nodes='state_input'),
                  units='rad/s')
        p.set_val('traj.phase0.states:alpha',
                  phase.interpolate(ys=[0.0, 0.0], nodes='state_input'),
                  units='rad')
        p.set_val('traj.phase0.states:ax',
                  phase.interpolate(ys=[0.0, 0.0], nodes='state_input'),
                  units='m/s**2')
        p.set_val('traj.phase0.states:ay',
                  phase.interpolate(ys=[0.0, 0.0], nodes='state_input'),
                  units='m/s**2')
        p.set_val('traj.phase0.states:n',
                  phase.interpolate(ys=[0.0, 0.0], nodes='state_input'),
                  units='m')
        p.set_val('traj.phase0.states:t',
                  phase.interpolate(ys=[0.0, 100.0], nodes='state_input'),
                  units='s')  # initial guess for what the final time should be

        # Controls
        p.set_val('traj.phase0.controls:delta',
                  phase.interpolate(ys=[0.0, 0.0], nodes='control_input'),
                  units='rad')
        p.set_val(
            'traj.phase0.controls:thrust',
            phase.interpolate(ys=[0.1, 0.1], nodes='control_input'),
            units=None)  # a small amount of thrust can speed up convergence

        dm.run_problem(p, run_driver=True)
        print('Optimization finished')

        # Test this example in Dymos' continuous integration process
        assert_near_equal(p.get_val('traj.phase0.timeseries.states:t')[-1],
                          22.2657,
                          tolerance=0.01)

        # Get optimized time series
        n = p.get_val('traj.phase0.timeseries.states:n')
        s = p.get_val('traj.phase0.timeseries.time')
        V = p.get_val('traj.phase0.timeseries.states:V')
        thrust = p.get_val('traj.phase0.timeseries.controls:thrust')
        delta = p.get_val('traj.phase0.timeseries.controls:delta')
        power = p.get_val('traj.phase0.timeseries.power', units='W')

        print("Plotting")

        # We know the optimal distance from the centerline (n). To transform this into the racing
        # line we fit a spline to the displaced points. This will let us plot the racing line in
        # x/y coordinates
        normals = get_gate_normals(finespline, slope)
        newgates = []
        newnormals = []
        newn = []
        for i in range(len(n)):
            index = ((s[i] / s_final) * np.array(finespline).shape[1]).astype(
                int)  # interpolation to find the appropriate index
            if index[0] == np.array(finespline).shape[1]:
                index[0] = np.array(finespline).shape[1] - 1
            if i > 0 and s[i] == s[i - 1]:
                continue
            else:
                newgates.append(
                    [finespline[0][index[0]], finespline[1][index[0]]])
                newnormals.append(normals[index[0]])
                newn.append(n[i][0])

        newgates = reverse_transform_gates(newgates)
        displaced_gates = set_gate_displacements(newn, newgates, newnormals)
        displaced_gates = np.array((transform_gates(displaced_gates)))

        npoints = 1000
        # fit the racing line spline to npoints
        displaced_spline, gates, gatesd, curv, slope = get_spline(
            displaced_gates, 1 / npoints, 0)

        plt.rcParams.update({'font.size': 12})

        def plot_track_with_data(state, s):
            # this function plots the track
            state = np.array(state)[:, 0]
            s = np.array(s)[:, 0]
            s_new = np.linspace(0, s_final, npoints)

            # Colormap and norm of the track plot
            cmap = mpl.cm.get_cmap('viridis')
            norm = mpl.colors.Normalize(vmin=np.amin(state),
                                        vmax=np.amax(state))

            fig, ax = plt.subplots(figsize=(15, 6))
            # establishes the figure axis limits needed for plotting the track below
            plt.plot(displaced_spline[0],
                     displaced_spline[1],
                     linewidth=0.1,
                     solid_capstyle="butt")

            plt.axis('equal')
            # the linewidth is set in order to match the width of the track
            plt.plot(finespline[0],
                     finespline[1],
                     'k',
                     linewidth=linewidth_from_data_units(8.5, ax),
                     solid_capstyle="butt")
            plt.plot(finespline[0],
                     finespline[1],
                     'w',
                     linewidth=linewidth_from_data_units(8, ax),
                     solid_capstyle="butt"
                     )  # 8 is the width, and the 8.5 wide line draws 'kerbs'
            plt.xlabel('x (m)')
            plt.ylabel('y (m)')

            # plot spline with color
            for i in range(1, len(displaced_spline[0])):
                s_spline = s_new[i]
                index_greater = np.argwhere(s >= s_spline)[0][0]
                index_less = np.argwhere(s < s_spline)[-1][0]

                x = s_spline
                xp = np.array([s[index_less], s[index_greater]])
                fp = np.array([state[index_less], state[index_greater]])
                interp_state = np.interp(
                    x, xp,
                    fp)  # interpolate the given state to calculate the color

                # calculate the appropriate color
                state_color = norm(interp_state)
                color = cmap(state_color)
                color = mpl.colors.to_hex(color)

                # the track plot consists of thousands of tiny lines:
                point = [displaced_spline[0][i], displaced_spline[1][i]]
                prevpoint = [
                    displaced_spline[0][i - 1], displaced_spline[1][i - 1]
                ]
                if i <= 5 or i == len(displaced_spline[0]) - 1:
                    plt.plot([point[0], prevpoint[0]],
                             [point[1], prevpoint[1]],
                             color,
                             linewidth=linewidth_from_data_units(1.5, ax),
                             solid_capstyle="butt",
                             antialiased=True)
                else:
                    plt.plot([point[0], prevpoint[0]],
                             [point[1], prevpoint[1]],
                             color,
                             linewidth=linewidth_from_data_units(1.5, ax),
                             solid_capstyle="projecting",
                             antialiased=True)

            clb = plt.colorbar(mpl.cm.ScalarMappable(norm=norm, cmap=cmap),
                               fraction=0.02,
                               pad=0.04)  # add colorbar

            if np.array_equal(state, V[:, 0]):
                clb.set_label('Velocity (m/s)')
            elif np.array_equal(state, thrust[:, 0]):
                clb.set_label('Thrust')
            elif np.array_equal(state, delta[:, 0]):
                clb.set_label('Delta')

            plt.tight_layout()
            plt.grid()

        # Create the plots
        plot_track_with_data(V, s)
        plot_track_with_data(thrust, s)
        plot_track_with_data(delta, s)

        # Plot the main vehicle telemetry
        fig, axes = plt.subplots(nrows=4, ncols=1, figsize=(15, 8))

        # Velocity vs s
        axes[0].plot(s,
                     p.get_val('traj.phase0.timeseries.states:V'),
                     label='solution')

        axes[0].set_xlabel('s (m)')
        axes[0].set_ylabel('V (m/s)')
        axes[0].grid()
        axes[0].set_xlim(0, s_final)

        # n vs s
        axes[1].plot(s,
                     p.get_val('traj.phase0.timeseries.states:n', units='m'),
                     label='solution')

        axes[1].set_xlabel('s (m)')
        axes[1].set_ylabel('n (m)')
        axes[1].grid()
        axes[1].set_xlim(0, s_final)

        # throttle vs s
        axes[2].plot(s, thrust)

        axes[2].set_xlabel('s (m)')
        axes[2].set_ylabel('thrust')
        axes[2].grid()
        axes[2].set_xlim(0, s_final)

        # delta vs s
        axes[3].plot(s,
                     p.get_val('traj.phase0.timeseries.controls:delta',
                               units=None),
                     label='solution')

        axes[3].set_xlabel('s (m)')
        axes[3].set_ylabel('delta')
        axes[3].grid()
        axes[3].set_xlim(0, s_final)

        plt.tight_layout()

        # Performance constraint plot. Tire friction and power constraints
        fig, axes = plt.subplots(nrows=1, ncols=1, figsize=(15, 4))
        plt.subplots_adjust(right=0.82, bottom=0.14, top=0.97, left=0.07)

        axes.plot(s,
                  p.get_val('traj.phase0.timeseries.c_fl', units=None),
                  label='c_fl')
        axes.plot(s,
                  p.get_val('traj.phase0.timeseries.c_fr', units=None),
                  label='c_fr')
        axes.plot(s,
                  p.get_val('traj.phase0.timeseries.c_rl', units=None),
                  label='c_rl')
        axes.plot(s,
                  p.get_val('traj.phase0.timeseries.c_rr', units=None),
                  label='c_rr')

        axes.plot(s, power / pmax, label='Power')

        axes.legend(bbox_to_anchor=(1.04, 0.5), loc="center left")
        axes.set_xlabel('s (m)')
        axes.set_ylabel('Performance constraints')
        axes.grid()
        axes.set_xlim(0, s_final)

        plt.show()
Пример #16
0
def brachistochrone_min_time(transcription='gauss-lobatto',
                             num_segments=8,
                             transcription_order=3,
                             compressed=True,
                             optimizer='SLSQP',
                             run_driver=True,
                             force_alloc_complex=False,
                             solve_segments=False):
    p = om.Problem(model=om.Group())

    p.driver = om.pyOptSparseDriver()
    p.driver.options['optimizer'] = optimizer
    p.driver.declare_coloring(tol=1.0E-12)

    if transcription == 'gauss-lobatto':
        t = dm.GaussLobatto(num_segments=num_segments,
                            order=transcription_order,
                            compressed=compressed)
    elif transcription == 'radau-ps':
        t = dm.Radau(num_segments=num_segments,
                     order=transcription_order,
                     compressed=compressed)
    elif transcription == 'runge-kutta':
        t = dm.RungeKutta(num_segments=num_segments,
                          order=transcription_order,
                          compressed=compressed)
    traj = dm.Trajectory()
    phase = dm.Phase(ode_class=BrachistochroneODE, transcription=t)
    p.model.add_subsystem('traj0', traj)
    traj.add_phase('phase0', phase)

    # p.model.add_subsystem('traj0', traj)

    phase.set_time_options(fix_initial=True, duration_bounds=(.5, 10))

    phase.add_state('x',
                    rate_source=BrachistochroneODE.states['x']['rate_source'],
                    units=BrachistochroneODE.states['x']['units'],
                    fix_initial=True,
                    fix_final=False,
                    solve_segments=solve_segments)
    phase.add_state('y',
                    rate_source=BrachistochroneODE.states['y']['rate_source'],
                    units=BrachistochroneODE.states['y']['units'],
                    fix_initial=True,
                    fix_final=False,
                    solve_segments=solve_segments)

    # Note that by omitting the targets here Dymos will automatically attempt to connect
    # to a top-level input named 'v' in the ODE, and connect to nothing if it's not found.
    phase.add_state('v',
                    rate_source=BrachistochroneODE.states['v']['rate_source'],
                    units=BrachistochroneODE.states['v']['units'],
                    fix_initial=True,
                    fix_final=False,
                    solve_segments=solve_segments)

    phase.add_control(
        'theta',
        targets=BrachistochroneODE.parameters['theta']['targets'],
        continuity=True,
        rate_continuity=True,
        units='deg',
        lower=0.01,
        upper=179.9)

    phase.add_parameter('g',
                        targets=BrachistochroneODE.parameters['g']['targets'],
                        units='m/s**2',
                        val=9.80665)

    phase.add_timeseries('timeseries2',
                         transcription=dm.Radau(num_segments=num_segments * 5,
                                                order=transcription_order,
                                                compressed=compressed),
                         subset='control_input')

    phase.add_boundary_constraint('x', loc='final', equals=10)
    phase.add_boundary_constraint('y', loc='final', equals=5)
    # Minimize time at the end of the phase
    phase.add_objective('time_phase', loc='final', scaler=10)

    p.setup(check=['unconnected_inputs'],
            force_alloc_complex=force_alloc_complex)

    p['traj0.phase0.t_initial'] = 0.0
    p['traj0.phase0.t_duration'] = 2.0

    p['traj0.phase0.states:x'] = phase.interpolate(ys=[0, 10],
                                                   nodes='state_input')
    p['traj0.phase0.states:y'] = phase.interpolate(ys=[10, 5],
                                                   nodes='state_input')
    p['traj0.phase0.states:v'] = phase.interpolate(ys=[0, 9.9],
                                                   nodes='state_input')
    p['traj0.phase0.controls:theta'] = phase.interpolate(ys=[5, 100],
                                                         nodes='control_input')
    p['traj0.phase0.parameters:g'] = 9.80665

    dm.run_problem(p, run_driver=run_driver)

    # Plot results
    if SHOW_PLOTS:
        exp_out = traj.simulate()

        fig, ax = plt.subplots()
        fig.suptitle('Brachistochrone Solution')

        x_imp = p.get_val('traj0.phase0.timeseries.states:x')
        y_imp = p.get_val('traj0.phase0.timeseries.states:y')

        x_exp = exp_out.get_val('traj0.phase0.timeseries.states:x')
        y_exp = exp_out.get_val('traj0.phase0.timeseries.states:y')

        ax.plot(x_imp, y_imp, 'ro', label='implicit')
        ax.plot(x_exp, y_exp, 'b-', label='explicit')

        ax.set_xlabel('x (m)')
        ax.set_ylabel('y (m)')
        ax.grid(True)
        ax.legend(loc='upper right')

        fig, ax = plt.subplots()
        fig.suptitle('Brachistochrone Solution')

        x_imp = p.get_val('traj0.phase0.timeseries.time_phase')
        y_imp = p.get_val('traj0.phase0.timeseries.controls:theta')

        x_exp = exp_out.get_val('traj0.phase0.timeseries.time_phase')
        y_exp = exp_out.get_val('traj0.phase0.timeseries.controls:theta')

        ax.plot(x_imp, y_imp, 'ro', label='implicit')
        ax.plot(x_exp, y_exp, 'b-', label='explicit')

        ax.set_xlabel('time (s)')
        ax.set_ylabel('theta (rad)')
        ax.grid(True)
        ax.legend(loc='lower right')

        plt.show()

    return p
Пример #17
0
    def test_min_time_climb_for_docs_partial_coloring(self):
        import openmdao.api as om
        from openmdao.utils.assert_utils import assert_near_equal

        import dymos as dm
        from dymos.examples.min_time_climb.doc.min_time_climb_ode_partial_coloring import MinTimeClimbODE

        for fd in (False, True):
            if fd:
                pc_options = (False, True)
            else:
                pc_options = (False,)
            for pc in pc_options:
                with self.subTest(f'Finite Differencing: {fd}  Partial Coloring: {pc}'):
                    print(f'Finite Differencing: {fd}  Partial Coloring: {pc}')

                    #
                    # Instantiate the problem and configure the optimization driver
                    #
                    p = om.Problem(model=om.Group())

                    p.driver = om.pyOptSparseDriver()
                    p.driver.options['optimizer'] = 'IPOPT'
                    p.driver.declare_coloring(tol=1.0E-12)
                    p.driver.opt_settings['max_iter'] = 500
                    p.driver.opt_settings['alpha_for_y'] = 'safer-min-dual-infeas'

                    #
                    # Instantiate the trajectory and phase
                    #
                    traj = dm.Trajectory()

                    phase = dm.Phase(ode_class=MinTimeClimbODE,
                                     ode_init_kwargs={'fd': fd, 'partial_coloring': pc},
                                     transcription=dm.GaussLobatto(num_segments=30))

                    traj.add_phase('phase0', phase)

                    p.model.add_subsystem('traj', traj)

                    #
                    # Set the options on the optimization variables
                    #
                    phase.set_time_options(fix_initial=True, duration_bounds=(50, 400),
                                           duration_ref=100.0)

                    phase.add_state('r', fix_initial=True, lower=0, upper=1.0E6,
                                    ref=1.0E3, defect_ref=1.0E3, units='m',
                                    rate_source='flight_dynamics.r_dot')

                    phase.add_state('h', fix_initial=True, lower=0, upper=20000.0,
                                    ref=1.0E2, defect_ref=1.0E2, units='m',
                                    rate_source='flight_dynamics.h_dot')

                    phase.add_state('v', fix_initial=True, lower=10.0,
                                    ref=1.0E2, defect_ref=1.0E2, units='m/s',
                                    rate_source='flight_dynamics.v_dot')

                    phase.add_state('gam', fix_initial=True, lower=-1.5, upper=1.5,
                                    ref=1.0, defect_ref=1.0, units='rad',
                                    rate_source='flight_dynamics.gam_dot')

                    phase.add_state('m', fix_initial=True, lower=10.0, upper=1.0E5,
                                    ref=1.0E3, defect_ref=1.0E3, units='kg',
                                    rate_source='prop.m_dot')

                    phase.add_control('alpha', units='deg', lower=-8.0, upper=8.0, scaler=1.0,
                                      rate_continuity=True, rate_continuity_scaler=100.0,
                                      rate2_continuity=False, targets=['alpha'])

                    phase.add_parameter('S', val=49.2386, units='m**2', opt=False, targets=['S'])
                    phase.add_parameter('Isp', val=1600.0, units='s', opt=False, targets=['Isp'])
                    phase.add_parameter('throttle', val=1.0, opt=False, targets=['throttle'])

                    #
                    # Setup the boundary and path constraints
                    #
                    phase.add_boundary_constraint('h', loc='final', equals=20000, scaler=1.0E-3, units='m')
                    phase.add_boundary_constraint('aero.mach', loc='final', equals=1.0, shape=(1,))
                    phase.add_boundary_constraint('gam', loc='final', equals=0.0, units='rad')

                    phase.add_path_constraint(name='h', lower=100.0, upper=20000, ref=20000)
                    phase.add_path_constraint(name='aero.mach', lower=0.1, upper=1.8, shape=(1,))

                    # Minimize time at the end of the phase
                    phase.add_objective('time', loc='final', ref=100.0)

                    p.model.linear_solver = om.DirectSolver()

                    #
                    # Setup the problem and set the initial guess
                    #
                    p.setup()

                    p['traj.phase0.t_initial'] = 0.0
                    p['traj.phase0.t_duration'] = 500

                    p['traj.phase0.states:r'] = phase.interpolate(ys=[0.0, 50000.0], nodes='state_input')
                    p['traj.phase0.states:h'] = phase.interpolate(ys=[100.0, 20000.0], nodes='state_input')
                    p['traj.phase0.states:v'] = phase.interpolate(ys=[135.964, 283.159], nodes='state_input')
                    p['traj.phase0.states:gam'] = phase.interpolate(ys=[0.0, 0.0], nodes='state_input')
                    p['traj.phase0.states:m'] = phase.interpolate(ys=[19030.468, 10000.], nodes='state_input')
                    p['traj.phase0.controls:alpha'] = phase.interpolate(ys=[0.0, 0.0], nodes='control_input')

                    #
                    # Solve for the optimal trajectory
                    #
                    dm.run_problem(p)

                    #
                    # This code is intended to save the coloring plots for the documentation.
                    # In practice, use the command line interface to view these files instead:
                    # `openmdao view_coloring coloring_files/total_coloring.pkl --view`
                    #
                    stfd = '_fd' if fd else ''
                    stpc = '_pc' if pc else ''
                    coloring_dir = f'coloring_files{stfd}{stpc}'
                    if fd or pc:
                        if os.path.exists(coloring_dir):
                            shutil.rmtree(coloring_dir)
                        shutil.move('coloring_files', coloring_dir)

                    _view_coloring(os.path.join(coloring_dir, 'total_coloring.pkl'))

                    #
                    # Test the results
                    #
                    assert_near_equal(p.get_val('traj.phase0.t_duration'), 321.0, tolerance=1.0E-1)
Пример #18
0
    def test_brachistochrone_for_docs_coloring_demo_solve_segments(self):
        import openmdao.api as om
        from openmdao.utils.assert_utils import assert_near_equal
        import dymos as dm
        from dymos.examples.plotting import plot_results
        from dymos.examples.brachistochrone import BrachistochroneODE
        from openmdao.utils.general_utils import set_pyoptsparse_opt

        #
        # Initialize the Problem and the optimization driver
        #
        p = om.Problem(model=om.Group())
        _, optimizer = set_pyoptsparse_opt('IPOPT', fallback=True)
        p.driver = om.pyOptSparseDriver(optimizer=optimizer)
        p.driver.opt_settings['print_level'] = 4
        # p.driver.declare_coloring()

        #
        # Create a trajectory and add a phase to it
        #
        traj = p.model.add_subsystem('traj', dm.Trajectory())

        #
        # In this case the phase has many segments to demonstrate the impact of coloring.
        #
        phase = traj.add_phase(
            'phase0',
            dm.Phase(ode_class=BrachistochroneODE,
                     transcription=dm.Radau(num_segments=100,
                                            solve_segments='forward')))

        #
        # Set the variables
        #
        phase.set_time_options(fix_initial=True, duration_bounds=(.5, 10))

        phase.add_state('x', fix_initial=True)

        phase.add_state('y', fix_initial=True)

        phase.add_state('v', fix_initial=True)

        phase.add_control('theta',
                          continuity=True,
                          rate_continuity=True,
                          units='deg',
                          lower=0.01,
                          upper=179.9)

        phase.add_parameter('g', units='m/s**2', val=9.80665)

        #
        # Replace state terminal bounds with nonlinear constraints
        #
        phase.add_boundary_constraint('x', loc='final', equals=10)
        phase.add_boundary_constraint('y', loc='final', equals=5)

        #
        # Minimize time at the end of the phase
        #
        phase.add_objective('time', loc='final', scaler=10)

        p.model.linear_solver = om.DirectSolver()

        #
        # Setup the Problem
        #
        p.setup()

        #
        # Set the initial values
        #
        p['traj.phase0.t_initial'] = 0.0
        p['traj.phase0.t_duration'] = 2.0

        p['traj.phase0.states:x'] = phase.interpolate(ys=[0, 10],
                                                      nodes='state_input')
        p['traj.phase0.states:y'] = phase.interpolate(ys=[10, 5],
                                                      nodes='state_input')
        p['traj.phase0.states:v'] = phase.interpolate(ys=[0, 9.9],
                                                      nodes='state_input')
        p['traj.phase0.controls:theta'] = phase.interpolate(
            ys=[5, 100.5], nodes='control_input')

        #
        # Solve for the optimal trajectory
        #
        dm.run_problem(p)

        # Test the results
        assert_near_equal(p.get_val('traj.phase0.timeseries.time')[-1],
                          1.8016,
                          tolerance=1.0E-3)

        # Generate the explicitly simulated trajectory
        exp_out = traj.simulate()

        plot_results(
            [('traj.phase0.timeseries.states:x',
              'traj.phase0.timeseries.states:y', 'x (m)', 'y (m)'),
             ('traj.phase0.timeseries.time',
              'traj.phase0.timeseries.controls:theta', 'time (s)',
              'theta (deg)')],
            title='Brachistochrone Solution\nRadau Pseudospectral Method',
            p_sol=p,
            p_sim=exp_out)

        plt.show()
Пример #19
0
    def test_min_time_climb_for_docs_gauss_lobatto(self):
        import matplotlib.pyplot as plt

        import openmdao.api as om
        from openmdao.utils.assert_utils import assert_near_equal

        import dymos as dm
        from dymos.examples.min_time_climb.min_time_climb_ode import MinTimeClimbODE
        from dymos.examples.plotting import plot_results

        #
        # Instantiate the problem and configure the optimization driver
        #
        p = om.Problem(model=om.Group())

        p.driver = om.pyOptSparseDriver()
        p.driver.options['optimizer'] = 'SLSQP'
        p.driver.declare_coloring()

        #
        # Instantiate the trajectory and phase
        #
        traj = dm.Trajectory()

        phase = dm.Phase(ode_class=MinTimeClimbODE,
                         transcription=dm.GaussLobatto(num_segments=15, compressed=True))

        traj.add_phase('phase0', phase)

        p.model.add_subsystem('traj', traj)

        #
        # Set the options on the optimization variables
        #
        phase.set_time_options(fix_initial=True, duration_bounds=(50, 400),
                               duration_ref=100.0)

        phase.add_state('r', fix_initial=True, lower=0, upper=1.0E6,
                        ref=1.0E3, defect_ref=1.0E3, units='m',
                        rate_source='flight_dynamics.r_dot')

        phase.add_state('h', fix_initial=True, lower=0, upper=20000.0,
                        ref=1.0E2, defect_ref=1.0E2, units='m',
                        rate_source='flight_dynamics.h_dot', targets=['h'])

        phase.add_state('v', fix_initial=True, lower=10.0,
                        ref=1.0E2, defect_ref=1.0E2, units='m/s',
                        rate_source='flight_dynamics.v_dot', targets=['v'])

        phase.add_state('gam', fix_initial=True, lower=-1.5, upper=1.5,
                        ref=1.0, defect_ref=1.0, units='rad',
                        rate_source='flight_dynamics.gam_dot', targets=['gam'])

        phase.add_state('m', fix_initial=True, lower=10.0, upper=1.0E5,
                        ref=1.0E3, defect_ref=1.0E3, units='kg',
                        rate_source='prop.m_dot', targets=['m'])

        phase.add_control('alpha', units='deg', lower=-8.0, upper=8.0, scaler=1.0,
                          rate_continuity=True, rate_continuity_scaler=100.0,
                          rate2_continuity=False, targets=['alpha'])

        phase.add_design_parameter('S', val=49.2386, units='m**2', opt=False, targets=['S'])
        phase.add_design_parameter('Isp', val=1600.0, units='s', opt=False, targets=['Isp'])
        phase.add_design_parameter('throttle', val=1.0, opt=False, targets=['throttle'])

        #
        # Setup the boundary and path constraints
        #
        phase.add_boundary_constraint('h', loc='final', equals=20000, scaler=1.0E-3, units='m')
        phase.add_boundary_constraint('aero.mach', loc='final', equals=1.0, shape=(1,))
        phase.add_boundary_constraint('gam', loc='final', equals=0.0, units='rad')

        phase.add_path_constraint(name='h', lower=100.0, upper=20000, ref=20000)
        phase.add_path_constraint(name='aero.mach', lower=0.1, upper=1.8, shape=(1,))

        # Minimize time at the end of the phase
        phase.add_objective('time', loc='final', ref=1.0)

        p.model.linear_solver = om.DirectSolver()

        #
        # Setup the problem and set the initial guess
        #
        p.setup(check=True)

        p['traj.phase0.t_initial'] = 0.0
        p['traj.phase0.t_duration'] = 500

        p['traj.phase0.states:r'] = phase.interpolate(ys=[0.0, 50000.0], nodes='state_input')
        p['traj.phase0.states:h'] = phase.interpolate(ys=[100.0, 20000.0], nodes='state_input')
        p['traj.phase0.states:v'] = phase.interpolate(ys=[135.964, 283.159], nodes='state_input')
        p['traj.phase0.states:gam'] = phase.interpolate(ys=[0.0, 0.0], nodes='state_input')
        p['traj.phase0.states:m'] = phase.interpolate(ys=[19030.468, 10000.], nodes='state_input')
        p['traj.phase0.controls:alpha'] = phase.interpolate(ys=[0.0, 0.0], nodes='control_input')

        #
        # Solve for the optimal trajectory
        #
        dm.run_problem(p)

        #
        # Test the results
        #
        assert_near_equal(p.get_val('traj.phase0.t_duration'), 321.0, tolerance=1.0E-1)

        #
        # Get the explicitly simulated solution and plot the results
        #
        exp_out = traj.simulate()

        plot_results([('traj.phase0.timeseries.time', 'traj.phase0.timeseries.states:h',
                       'time (s)', 'altitude (m)'),
                      ('traj.phase0.timeseries.time', 'traj.phase0.timeseries.controls:alpha',
                       'time (s)', 'alpha (deg)')],
                     title='Supersonic Minimum Time-to-Climb Solution',
                     p_sol=p, p_sim=exp_out)

        plt.show()
Пример #20
0
    def test_brachistochrone_for_docs_gauss_lobatto(self):
        import openmdao.api as om
        from openmdao.utils.assert_utils import assert_near_equal
        import dymos as dm
        from dymos.examples.plotting import plot_results
        from dymos.examples.brachistochrone import BrachistochroneODE
        import matplotlib.pyplot as plt

        #
        # Initialize the Problem and the optimization driver
        #
        p = om.Problem(model=om.Group())
        p.driver = om.ScipyOptimizeDriver()
        p.driver.declare_coloring()

        #
        # Create a trajectory and add a phase to it
        #
        traj = p.model.add_subsystem('traj', dm.Trajectory())

        phase = traj.add_phase(
            'phase0',
            dm.Phase(ode_class=BrachistochroneODE,
                     transcription=dm.GaussLobatto(num_segments=10)))

        #
        # Set the variables
        #
        phase.set_time_options(fix_initial=True, duration_bounds=(.5, 10))

        phase.add_state('x', fix_initial=True, fix_final=True)

        phase.add_state('y', fix_initial=True, fix_final=True)

        phase.add_state('v', fix_initial=True, fix_final=False)

        phase.add_control('theta',
                          continuity=True,
                          rate_continuity=True,
                          units='deg',
                          lower=0.01,
                          upper=179.9)

        phase.add_parameter('g', units='m/s**2', val=9.80665)
        #
        # Minimize time at the end of the phase
        #
        phase.add_objective('time', loc='final', scaler=10)

        p.model.linear_solver = om.DirectSolver()

        #
        # Setup the Problem
        #
        p.setup()

        #
        # Set the initial values
        #
        p['traj.phase0.t_initial'] = 0.0
        p['traj.phase0.t_duration'] = 2.0

        p['traj.phase0.states:x'] = phase.interpolate(ys=[0, 10],
                                                      nodes='state_input')
        p['traj.phase0.states:y'] = phase.interpolate(ys=[10, 5],
                                                      nodes='state_input')
        p['traj.phase0.states:v'] = phase.interpolate(ys=[0, 9.9],
                                                      nodes='state_input')
        p['traj.phase0.controls:theta'] = phase.interpolate(
            ys=[5, 100.5], nodes='control_input')

        #
        # Solve for the optimal trajectory
        #
        dm.run_problem(p)

        # Test the results
        assert_near_equal(p.get_val('traj.phase0.timeseries.time')[-1],
                          1.8016,
                          tolerance=1.0E-3)

        # Generate the explicitly simulated trajectory
        exp_out = traj.simulate()

        plot_results(
            [('traj.phase0.timeseries.states:x',
              'traj.phase0.timeseries.states:y', 'x (m)', 'y (m)'),
             ('traj.phase0.timeseries.time',
              'traj.phase0.timeseries.controls:theta', 'time (s)',
              'theta (deg)')],
            title='Brachistochrone Solution\nHigh-Order Gauss-Lobatto Method',
            p_sol=p,
            p_sim=exp_out)

        plt.show()
Пример #21
0
    def test_double_integrator_for_docs(self):
        import matplotlib.pyplot as plt
        import openmdao.api as om
        from openmdao.utils.assert_utils import assert_near_equal
        import dymos as dm
        from dymos.examples.plotting import plot_results
        from dymos.examples.double_integrator.double_integrator_ode import DoubleIntegratorODE

        # Initialize the problem and assign the driver
        p = om.Problem(model=om.Group())
        p.driver = om.pyOptSparseDriver()
        p.driver.options['optimizer'] = 'SLSQP'
        p.driver.declare_coloring()

        # Setup the trajectory and its phase
        traj = p.model.add_subsystem('traj', dm.Trajectory())

        transcription = dm.Radau(num_segments=30, order=3, compressed=False)

        phase = traj.add_phase(
            'phase0',
            dm.Phase(ode_class=DoubleIntegratorODE,
                     transcription=transcription))

        #
        # Set the options for our variables.
        #
        phase.set_time_options(fix_initial=True, fix_duration=True, units='s')
        phase.add_state('v',
                        fix_initial=True,
                        fix_final=True,
                        rate_source='u',
                        units='m/s')
        phase.add_state('x', fix_initial=True, rate_source='v', units='m')

        phase.add_control('u',
                          units='m/s**2',
                          scaler=0.01,
                          continuity=False,
                          rate_continuity=False,
                          rate2_continuity=False,
                          shape=(1, ),
                          lower=-1.0,
                          upper=1.0)

        #
        # Maximize distance travelled.
        #
        phase.add_objective('x', loc='final', scaler=-1)

        p.model.linear_solver = om.DirectSolver()

        #
        # Setup the problem and set our initial values.
        #
        p.setup(check=True)

        p['traj.phase0.t_initial'] = 0.0
        p['traj.phase0.t_duration'] = 1.0

        p.set_val('traj.phase0.states:x', phase.interp('x', ys=[0, 0.25]))
        p.set_val('traj.phase0.states:v', phase.interp('v', ys=[0, 0]))
        p.set_val('traj.phase0.controls:u', phase.interp('u', ys=[1, -1]))

        #
        # Solve the problem.
        #
        dm.run_problem(p)

        #
        # Verify that the results are correct.
        #
        x = p.get_val('traj.phase0.timeseries.states:x')
        v = p.get_val('traj.phase0.timeseries.states:v')

        assert_near_equal(x[0], 0.0, tolerance=1.0E-4)
        assert_near_equal(x[-1], 0.25, tolerance=1.0E-4)

        assert_near_equal(v[0], 0.0, tolerance=1.0E-4)
        assert_near_equal(v[-1], 0.0, tolerance=1.0E-4)

        #
        # Simulate the explicit solution and plot the results.
        #
        exp_out = traj.simulate()

        plot_results(
            [('traj.phase0.timeseries.time', 'traj.phase0.timeseries.states:x',
              'time (s)', 'x $(m)$'),
             ('traj.phase0.timeseries.time', 'traj.phase0.timeseries.states:v',
              'time (s)', 'v $(m/s)$'),
             ('traj.phase0.timeseries.time',
              'traj.phase0.timeseries.controls:u', 'time (s)', 'u $(m/s^2)$')],
            title='Double Integrator Solution\nRadau Pseudospectral Method',
            p_sol=p,
            p_sim=exp_out)

        plt.show()
    def test_brachistochrone_upstream_state(self):
        import openmdao.api as om
        from openmdao.utils.assert_utils import assert_near_equal
        import dymos as dm

        import matplotlib.pyplot as plt
        plt.switch_backend('Agg')

        from dymos.examples.brachistochrone.brachistochrone_ode import BrachistochroneODE

        #
        # Define the OpenMDAO problem
        #
        p = om.Problem(model=om.Group())

        # Instantiate the transcription so we can get the number of nodes from it while
        # building the problem.
        tx = dm.GaussLobatto(num_segments=10, order=3)

        # Add an indep var comp to provide the external control values
        ivc = p.model.add_subsystem('states_ivc',
                                    om.IndepVarComp(),
                                    promotes_outputs=['*'])

        # Add the output to provide the values of theta at the control input nodes of the transcription.
        ivc.add_output('x0', shape=(1, ), units='m')

        # Connect x0 to the state error component so we can constrain the given value of x0
        # to be equal to the value chosen in the phase.
        p.model.connect('x0', 'state_error_comp.x0_target')
        p.model.connect('traj.phase0.timeseries.states:x',
                        'state_error_comp.x0_actual',
                        src_indices=[0],
                        flat_src_indices=True)

        #
        # Define a Trajectory object
        #
        traj = dm.Trajectory()

        p.model.add_subsystem('traj', subsys=traj)

        p.model.add_subsystem(
            'state_error_comp',
            om.ExecComp('x0_error = x0_target - x0_actual',
                        x0_error={'units': 'm'},
                        x0_target={'units': 'm'},
                        x0_actual={'units': 'm'}))

        p.model.add_constraint('state_error_comp.x0_error', equals=0.0)

        #
        # Define a Dymos Phase object with GaussLobatto Transcription
        #
        phase = dm.Phase(ode_class=BrachistochroneODE, transcription=tx)

        traj.add_phase(name='phase0', phase=phase)

        #
        # Set the time options
        # Time has no targets in our ODE.
        # We fix the initial time so that the it is not a design variable in the optimization.
        # The duration of the phase is allowed to be optimized, but is bounded on [0.5, 10].
        #
        phase.set_time_options(fix_initial=True,
                               duration_bounds=(0.5, 10.0),
                               units='s')

        #
        # Set the time options
        # Initial values of positions and velocity are all fixed.
        # The final value of position are fixed, but the final velocity is a free variable.
        # The equations of motion are not functions of position, so 'x' and 'y' have no targets.
        # The rate source points to the output in the ODE which provides the time derivative of the
        # given state.
        phase.add_state('x',
                        fix_initial=False,
                        fix_final=True,
                        units='m',
                        rate_source='xdot')
        phase.add_state('y',
                        fix_initial=True,
                        fix_final=True,
                        units='m',
                        rate_source='ydot')
        phase.add_state('v',
                        fix_initial=True,
                        fix_final=False,
                        units='m/s',
                        rate_source='vdot',
                        targets=['v'])

        # Define theta as a control.
        # Use opt=False to allow it to be connected to an external source.
        # Arguments lower and upper are no longer valid for an input control.
        phase.add_control(name='theta', units='rad', targets=['theta'])

        # Minimize final time.
        phase.add_objective('time', loc='final')

        # Set the driver.
        p.driver = om.ScipyOptimizeDriver()

        # Allow OpenMDAO to automatically determine our sparsity pattern.
        # Doing so can significant speed up the execution of Dymos.
        p.driver.declare_coloring()

        # Setup the problem
        p.setup(check=True)

        # Now that the OpenMDAO problem is setup, we can set the values of the states.
        p.set_val('x0', 0.0, units='m')

        # Here we're intentially setting the intiial x value to something other than zero, just
        # to demonstrate that the optimizer brings it back in line with the value of x0 set above.
        p.set_val('traj.phase0.states:x',
                  phase.interp('x', [1, 10]),
                  units='m')

        p.set_val('traj.phase0.states:y',
                  phase.interp('y', [10, 5]),
                  units='m')

        p.set_val('traj.phase0.states:v',
                  phase.interp('v', [0, 5]),
                  units='m/s')

        p.set_val('traj.phase0.controls:theta',
                  phase.interp('theta', [90, 90]),
                  units='deg')

        # Run the driver to solve the problem
        dm.run_problem(p, make_plots=True)

        # Test the results
        assert_near_equal(p.get_val('traj.phase0.timeseries.time')[-1],
                          1.8016,
                          tolerance=1.0E-3)

        # Check the validity of our results by using scipy.integrate.solve_ivp to
        # integrate the solution.
        sim_out = traj.simulate()

        # Plot the results
        fig, axes = plt.subplots(nrows=1, ncols=2, figsize=(12, 4.5))

        axes[0].plot(p.get_val('traj.phase0.timeseries.states:x'),
                     p.get_val('traj.phase0.timeseries.states:y'),
                     'ro',
                     label='solution')

        axes[0].plot(sim_out.get_val('traj.phase0.timeseries.states:x'),
                     sim_out.get_val('traj.phase0.timeseries.states:y'),
                     'b-',
                     label='simulation')

        axes[0].set_xlabel('x (m)')
        axes[0].set_ylabel('y (m/s)')
        axes[0].legend()
        axes[0].grid()

        axes[1].plot(p.get_val('traj.phase0.timeseries.time'),
                     p.get_val('traj.phase0.timeseries.controls:theta',
                               units='deg'),
                     'ro',
                     label='solution')

        axes[1].plot(sim_out.get_val('traj.phase0.timeseries.time'),
                     sim_out.get_val('traj.phase0.timeseries.controls:theta',
                                     units='deg'),
                     'b-',
                     label='simulation')

        axes[1].set_xlabel('time (s)')
        axes[1].set_ylabel(r'$\theta$ (deg)')
        axes[1].legend()
        axes[1].grid()

        plt.show()
Пример #23
0
    def test_doc_ssto_earth(self):
        import matplotlib.pyplot as plt
        import openmdao.api as om
        import dymos as dm

        #
        # Setup and solve the optimal control problem
        #
        p = om.Problem(model=om.Group())
        p.driver = om.pyOptSparseDriver()
        p.driver.declare_coloring(tol=1.0E-12)

        from dymos.examples.ssto.launch_vehicle_ode import LaunchVehicleODE

        #
        # Initialize our Trajectory and Phase
        #
        traj = dm.Trajectory()

        phase = dm.Phase(ode_class=LaunchVehicleODE,
                         transcription=dm.GaussLobatto(num_segments=12,
                                                       order=3,
                                                       compressed=False))

        traj.add_phase('phase0', phase)
        p.model.add_subsystem('traj', traj)

        #
        # Set the options for the variables
        #
        phase.set_time_options(fix_initial=True, duration_bounds=(10, 500))

        phase.add_state('x',
                        fix_initial=True,
                        ref=1.0E5,
                        defect_ref=10000.0,
                        rate_source='xdot')
        phase.add_state('y',
                        fix_initial=True,
                        ref=1.0E5,
                        defect_ref=10000.0,
                        rate_source='ydot')
        phase.add_state('vx',
                        fix_initial=True,
                        ref=1.0E3,
                        defect_ref=1000.0,
                        rate_source='vxdot')
        phase.add_state('vy',
                        fix_initial=True,
                        ref=1.0E3,
                        defect_ref=1000.0,
                        rate_source='vydot')
        phase.add_state('m',
                        fix_initial=True,
                        ref=1.0E3,
                        defect_ref=100.0,
                        rate_source='mdot')

        phase.add_control('theta',
                          units='rad',
                          lower=-1.57,
                          upper=1.57,
                          targets=['theta'])
        phase.add_parameter('thrust',
                            units='N',
                            opt=False,
                            val=2100000.0,
                            targets=['thrust'])

        #
        # Set the options for our constraints and objective
        #
        phase.add_boundary_constraint('y',
                                      loc='final',
                                      equals=1.85E5,
                                      linear=True)
        phase.add_boundary_constraint('vx', loc='final', equals=7796.6961)
        phase.add_boundary_constraint('vy', loc='final', equals=0)

        phase.add_objective('time', loc='final', scaler=0.01)

        p.model.linear_solver = om.DirectSolver()

        #
        # Setup and set initial values
        #
        p.setup(check=True)

        p.set_val('traj.phase0.t_initial', 0.0)
        p.set_val('traj.phase0.t_duration', 150.0)
        p.set_val('traj.phase0.states:x', phase.interp('x', [0, 1.15E5]))
        p.set_val('traj.phase0.states:y', phase.interp('y', [0, 1.85E5]))
        p.set_val('traj.phase0.states:vy', phase.interp('vx', [1.0E-6, 0]))
        p.set_val('traj.phase0.states:m', phase.interp('vy', [117000, 1163]))
        p.set_val('traj.phase0.controls:theta',
                  phase.interp('theta', [1.5, -0.76]))
        p.set_val('traj.phase0.parameters:thrust', 2.1, units='MN')

        #
        # Solve the Problem
        #
        dm.run_problem(p)

        assert_near_equal(p.get_val('traj.phase0.timeseries.time')[-1],
                          143,
                          tolerance=0.05)
        assert_near_equal(
            p.get_val('traj.phase0.timeseries.states:y')[-1], 1.85E5, 1e-4)
        assert_near_equal(
            p.get_val('traj.phase0.timeseries.states:vx')[-1], 7796.6961, 1e-4)
        assert_near_equal(
            p.get_val('traj.phase0.timeseries.states:vy')[-1], 0, 1e-4)
        #
        # Get the explicitly simulated results
        #
        exp_out = traj.simulate()

        #
        # Plot the results
        #
        fig, axes = plt.subplots(nrows=2, ncols=1, figsize=(10, 8))

        axes[0].plot(p.get_val('traj.phase0.timeseries.states:x'),
                     p.get_val('traj.phase0.timeseries.states:y'),
                     marker='o',
                     ms=4,
                     linestyle='None',
                     label='solution')

        axes[0].plot(exp_out.get_val('traj.phase0.timeseries.states:x'),
                     exp_out.get_val('traj.phase0.timeseries.states:y'),
                     marker=None,
                     linestyle='-',
                     label='simulation')

        axes[0].set_xlabel('range (m)')
        axes[0].set_ylabel('altitude (m)')
        axes[0].set_aspect('equal')

        axes[1].plot(p.get_val('traj.phase0.timeseries.time'),
                     p.get_val('traj.phase0.timeseries.controls:theta'),
                     marker='o',
                     ms=4,
                     linestyle='None')

        axes[1].plot(exp_out.get_val('traj.phase0.timeseries.time'),
                     exp_out.get_val('traj.phase0.timeseries.controls:theta'),
                     linestyle='-',
                     marker=None)

        axes[1].set_xlabel('time (s)')
        axes[1].set_ylabel('theta (deg)')

        plt.suptitle(
            'Single Stage to Orbit Solution Using Linear Tangent Guidance')
        fig.legend(loc='lower center', ncol=2)

        plt.show()
    def test_brachistochrone_static_gravity(self):
        import openmdao.api as om
        from openmdao.utils.assert_utils import assert_near_equal
        import dymos as dm
        import matplotlib.pyplot as plt
        from dymos.examples.brachistochrone.brachistochrone_ode import BrachistochroneODE

        #
        # Initialize the Problem and the optimization driver
        #
        p = om.Problem(model=om.Group())
        p.driver = om.ScipyOptimizeDriver()
        p.driver.declare_coloring()

        #
        # Create a trajectory and add a phase to it
        #
        traj = p.model.add_subsystem('traj', dm.Trajectory())

        phase = traj.add_phase(
            'phase0',
            dm.Phase(ode_class=BrachistochroneODE,
                     ode_init_kwargs={'static_gravity': True},
                     transcription=dm.GaussLobatto(num_segments=10)))

        #
        # Set the variables
        #
        phase.set_time_options(initial_bounds=(0, 0), duration_bounds=(.5, 10))

        phase.add_state('x',
                        rate_source='xdot',
                        targets=None,
                        units='m',
                        fix_initial=True,
                        fix_final=True,
                        solve_segments=False)

        phase.add_state('y',
                        rate_source='ydot',
                        targets=None,
                        units='m',
                        fix_initial=True,
                        fix_final=True,
                        solve_segments=False)

        phase.add_state('v',
                        rate_source='vdot',
                        targets=['v'],
                        units='m/s',
                        fix_initial=True,
                        fix_final=False,
                        solve_segments=False)

        phase.add_control('theta',
                          targets=['theta'],
                          continuity=True,
                          rate_continuity=True,
                          units='deg',
                          lower=0.01,
                          upper=179.9)

        phase.add_parameter('g', targets=['g'], dynamic=False, opt=False)

        #
        # Minimize time at the end of the phase
        #
        phase.add_objective('time', loc='final', scaler=10)

        #
        # Setup the Problem
        #
        p.setup()

        #
        # Set the initial values
        # The initial time is fixed, and we set that fixed value here.
        # The optimizer is allowed to modify t_duration, but an initial guess is provided here.
        #
        p.set_val('traj.phase0.t_initial', 0.0)
        p.set_val('traj.phase0.t_duration', 2.0)

        # Guesses for states are provided at all state_input nodes.
        # We use the phase.interpolate method to linearly interpolate values onto the state input nodes.
        # Since fix_initial=True for all states and fix_final=True for x and y, the initial or final
        # values of the interpolation provided here will not be changed by the optimizer.
        p.set_val('traj.phase0.states:x',
                  phase.interpolate(ys=[0, 10], nodes='state_input'))
        p.set_val('traj.phase0.states:y',
                  phase.interpolate(ys=[10, 5], nodes='state_input'))
        p.set_val('traj.phase0.states:v',
                  phase.interpolate(ys=[0, 9.9], nodes='state_input'))

        # Guesses for controls are provided at all control_input node.
        # Here phase.interpolate is used to linearly interpolate values onto the control input nodes.
        p.set_val('traj.phase0.controls:theta',
                  phase.interpolate(ys=[5, 100.5], nodes='control_input'))

        # Set the value for gravitational acceleration.
        p.set_val('traj.phase0.parameters:g', 9.80665)

        #
        # Solve for the optimal trajectory
        #
        dm.run_problem(p, simulate=True)

        # Test the results
        assert_near_equal(p.get_val('traj.phase0.timeseries.time')[-1],
                          1.8016,
                          tolerance=1.0E-3)

        # Generate the explicitly simulated trajectory
        exp_out = om.CaseReader('dymos_simulation.db').get_case('final')

        # Extract the timeseries from the implicit solution and the explicit simulation
        x = p.get_val('traj.phase0.timeseries.states:x')
        y = p.get_val('traj.phase0.timeseries.states:y')
        t = p.get_val('traj.phase0.timeseries.time')
        theta = p.get_val('traj.phase0.timeseries.controls:theta')

        x_exp = exp_out.get_val('traj.phase0.timeseries.states:x')
        y_exp = exp_out.get_val('traj.phase0.timeseries.states:y')
        t_exp = exp_out.get_val('traj.phase0.timeseries.time')
        theta_exp = exp_out.get_val('traj.phase0.timeseries.controls:theta')

        fig, axes = plt.subplots(nrows=2, ncols=1)

        axes[0].plot(x, y, 'o')
        axes[0].plot(x_exp, y_exp, '-')
        axes[0].set_xlabel('x (m)')
        axes[0].set_ylabel('y (m)')

        axes[1].plot(t, theta, 'o')
        axes[1].plot(t_exp, theta_exp, '-')
        axes[1].set_xlabel('time (s)')
        axes[1].set_ylabel(r'$\theta$ (deg)')

        plt.show()
    def test_finite_burn_orbit_raise(self):
        import numpy as np
        import matplotlib.pyplot as plt
        import openmdao.api as om
        from openmdao.utils.assert_utils import assert_near_equal

        import dymos as dm
        from dymos.examples.finite_burn_orbit_raise.finite_burn_eom import FiniteBurnODE

        p = om.Problem(model=om.Group())

        p.driver = om.pyOptSparseDriver()
        p.driver.options['optimizer'] = 'IPOPT'
        p.driver.declare_coloring()

        traj = dm.Trajectory()

        traj.add_parameter('c', opt=False, val=1.5, units='DU/TU',
                           targets={'burn1': ['c'], 'coast': ['c'], 'burn2': ['c']})

        # First Phase (burn)

        burn1 = dm.Phase(ode_class=FiniteBurnODE,
                         transcription=dm.GaussLobatto(num_segments=5, order=3, compressed=False))

        burn1 = traj.add_phase('burn1', burn1)

        burn1.set_time_options(fix_initial=True, duration_bounds=(.5, 10), units='TU')
        burn1.add_state('r', fix_initial=True, fix_final=False, defect_scaler=100.0,
                        rate_source='r_dot', units='DU')
        burn1.add_state('theta', fix_initial=True, fix_final=False, defect_scaler=100.0,
                        rate_source='theta_dot', units='rad')
        burn1.add_state('vr', fix_initial=True, fix_final=False, defect_scaler=100.0,
                        rate_source='vr_dot', units='DU/TU')
        burn1.add_state('vt', fix_initial=True, fix_final=False, defect_scaler=100.0,
                        rate_source='vt_dot', units='DU/TU')
        burn1.add_state('accel', fix_initial=True, fix_final=False,
                        rate_source='at_dot', units='DU/TU**2')
        burn1.add_state('deltav', fix_initial=True, fix_final=False,
                        rate_source='deltav_dot', units='DU/TU')
        burn1.add_control('u1', rate_continuity=True, rate2_continuity=True, units='deg',
                          scaler=0.01, rate_continuity_scaler=0.001, rate2_continuity_scaler=0.001,
                          lower=-30, upper=30)
        # Second Phase (Coast)
        coast = dm.Phase(ode_class=FiniteBurnODE,
                         transcription=dm.GaussLobatto(num_segments=5, order=3, compressed=False))

        coast.set_time_options(initial_bounds=(0.5, 20), duration_bounds=(.5, 50), duration_ref=50,
                               units='TU')
        coast.add_state('r', fix_initial=False, fix_final=False, defect_scaler=100.0,
                        rate_source='r_dot', targets=['r'], units='DU')
        coast.add_state('theta', fix_initial=False, fix_final=False, defect_scaler=100.0,
                        rate_source='theta_dot', targets=['theta'], units='rad')
        coast.add_state('vr', fix_initial=False, fix_final=False, defect_scaler=100.0,
                        rate_source='vr_dot', targets=['vr'], units='DU/TU')
        coast.add_state('vt', fix_initial=False, fix_final=False, defect_scaler=100.0,
                        rate_source='vt_dot', targets=['vt'], units='DU/TU')
        coast.add_state('accel', fix_initial=True, fix_final=True,
                        rate_source='at_dot', targets=['accel'], units='DU/TU**2')
        coast.add_state('deltav', fix_initial=False, fix_final=False,
                        rate_source='deltav_dot', units='DU/TU')

        coast.add_parameter('u1', opt=False, val=0.0, units='deg', targets=['u1'])

        # Third Phase (burn)
        burn2 = dm.Phase(ode_class=FiniteBurnODE,
                         transcription=dm.GaussLobatto(num_segments=5, order=3, compressed=False))

        traj.add_phase('coast', coast)
        traj.add_phase('burn2', burn2)

        burn2.set_time_options(initial_bounds=(0.5, 50), duration_bounds=(.5, 10), initial_ref=10,
                               units='TU')
        burn2.add_state('r', fix_initial=False, fix_final=True, defect_scaler=100.0,
                        rate_source='r_dot', units='DU')
        burn2.add_state('theta', fix_initial=False, fix_final=False, defect_scaler=100.0,
                        rate_source='theta_dot', units='rad')
        burn2.add_state('vr', fix_initial=False, fix_final=True, defect_scaler=1000.0,
                        rate_source='vr_dot', units='DU/TU')
        burn2.add_state('vt', fix_initial=False, fix_final=True, defect_scaler=1000.0,
                        rate_source='vt_dot', units='DU/TU')
        burn2.add_state('accel', fix_initial=False, fix_final=False, defect_scaler=1.0,
                        rate_source='at_dot', units='DU/TU**2')
        burn2.add_state('deltav', fix_initial=False, fix_final=False, defect_scaler=1.0,
                        rate_source='deltav_dot', units='DU/TU')

        burn2.add_objective('deltav', loc='final', scaler=100.0)

        burn2.add_control('u1', rate_continuity=True, rate2_continuity=True, units='deg',
                          scaler=0.01, lower=-90, upper=90)

        burn1.add_timeseries_output('pos_x')
        coast.add_timeseries_output('pos_x')
        burn2.add_timeseries_output('pos_x')

        burn1.add_timeseries_output('pos_y')
        coast.add_timeseries_output('pos_y')
        burn2.add_timeseries_output('pos_y')

        # Link Phases
        traj.link_phases(phases=['burn1', 'coast', 'burn2'],
                         vars=['time', 'r', 'theta', 'vr', 'vt', 'deltav'])

        traj.link_phases(phases=['burn1', 'burn2'], vars=['accel'])

        p.model.add_subsystem('traj', subsys=traj)

        # Finish Problem Setup

        # Needed to move the direct solver down into the phases for use with MPI.
        #  - After moving down, used fewer iterations (about 30 less)

        p.driver.add_recorder(om.SqliteRecorder('two_burn_orbit_raise_example.db'))

        p.setup(check=True, mode='fwd')

        # Set Initial Guesses
        p.set_val('traj.parameters:c', value=1.5, units='DU/TU')

        burn1 = p.model.traj.phases.burn1
        burn2 = p.model.traj.phases.burn2
        coast = p.model.traj.phases.coast

        p.set_val('traj.burn1.t_initial', value=0.0)
        p.set_val('traj.burn1.t_duration', value=2.25)
        p.set_val('traj.burn1.states:r', value=burn1.interp('r', [1, 1.5]))
        p.set_val('traj.burn1.states:theta', value=burn1.interp('theta', [0, 1.7]))
        p.set_val('traj.burn1.states:vr', value=burn1.interp('vr', [0, 0]))
        p.set_val('traj.burn1.states:vt', value=burn1.interp('vt', [1, 1]))
        p.set_val('traj.burn1.states:accel', value=burn1.interp('accel', [0.1, 0]))
        p.set_val('traj.burn1.states:deltav', value=burn1.interp('deltav', [0, 0.1]))
        p.set_val('traj.burn1.controls:u1', value=burn1.interp('u1', [-3.5, 13.0]))

        p.set_val('traj.coast.t_initial', value=2.25)
        p.set_val('traj.coast.t_duration', value=3.0)

        p.set_val('traj.coast.states:r', value=coast.interp('r', [1.3, 1.5]))
        p.set_val('traj.coast.states:theta', value=coast.interp('theta', [2.1767, 1.7]))
        p.set_val('traj.coast.states:vr', value=coast.interp('vr', [0.3285, 0]))
        p.set_val('traj.coast.states:vt', value=coast.interp('vt', [0.97, 1]))
        p.set_val('traj.coast.states:accel', value=coast.interp('accel', [0, 0]))

        p.set_val('traj.burn2.t_initial', value=5.25)
        p.set_val('traj.burn2.t_duration', value=1.75)

        p.set_val('traj.burn2.states:r', value=burn2.interp('r', [1, 3.]))
        p.set_val('traj.burn2.states:theta', value=burn2.interp('theta', [0, 4.0]))
        p.set_val('traj.burn2.states:vr', value=burn2.interp('vr', [0, 0]))
        p.set_val('traj.burn2.states:vt', value=burn2.interp('vt', [1, np.sqrt(1 / 3.)]))
        p.set_val('traj.burn2.states:deltav', value=burn2.interp('deltav', [0.1, 0.2]))
        p.set_val('traj.burn2.states:accel', value=burn2.interp('accel', [0.1, 0]))

        p.set_val('traj.burn2.controls:u1', value=burn2.interp('u1', [0, 0]))

        dm.run_problem(p)

        assert_near_equal(p.get_val('traj.burn2.states:deltav')[-1], 0.3995,
                          tolerance=2.0E-3)

        #
        # Plot results
        #
        traj = p.model.traj
        exp_out = traj.simulate()

        fig = plt.figure(figsize=(8, 4))
        fig.suptitle('Two Burn Orbit Raise Solution')
        ax_u1 = plt.subplot2grid((2, 2), (0, 0))
        ax_deltav = plt.subplot2grid((2, 2), (1, 0))
        ax_xy = plt.subplot2grid((2, 2), (0, 1), rowspan=2)

        span = np.linspace(0, 2 * np.pi, 100)
        ax_xy.plot(np.cos(span), np.sin(span), 'k--', lw=1)
        ax_xy.plot(3 * np.cos(span), 3 * np.sin(span), 'k--', lw=1)
        ax_xy.set_xlim(-4.5, 4.5)
        ax_xy.set_ylim(-4.5, 4.5)

        ax_xy.set_xlabel('x ($R_e$)')
        ax_xy.set_ylabel('y ($R_e$)')

        ax_u1.set_xlabel('time ($TU$)')
        ax_u1.set_ylabel('$u_1$ ($deg$)')
        ax_u1.grid(True)

        ax_deltav.set_xlabel('time ($TU$)')
        ax_deltav.set_ylabel('${\Delta}v$ ($DU/TU$)')
        ax_deltav.grid(True)

        t_sol = dict((phs, p.get_val('traj.{0}.timeseries.time'.format(phs)))
                     for phs in ['burn1', 'coast', 'burn2'])
        x_sol = dict((phs, p.get_val('traj.{0}.timeseries.pos_x'.format(phs)))
                     for phs in ['burn1', 'coast', 'burn2'])
        y_sol = dict((phs, p.get_val('traj.{0}.timeseries.pos_y'.format(phs)))
                     for phs in ['burn1', 'coast', 'burn2'])
        dv_sol = dict((phs, p.get_val('traj.{0}.timeseries.states:deltav'.format(phs)))
                      for phs in ['burn1', 'coast', 'burn2'])
        u1_sol = dict((phs, p.get_val('traj.{0}.timeseries.controls:u1'.format(phs), units='deg'))
                      for phs in ['burn1', 'burn2'])

        t_exp = dict((phs, exp_out.get_val('traj.{0}.timeseries.time'.format(phs)))
                     for phs in ['burn1', 'coast', 'burn2'])
        x_exp = dict((phs, exp_out.get_val('traj.{0}.timeseries.pos_x'.format(phs)))
                     for phs in ['burn1', 'coast', 'burn2'])
        y_exp = dict((phs, exp_out.get_val('traj.{0}.timeseries.pos_y'.format(phs)))
                     for phs in ['burn1', 'coast', 'burn2'])
        dv_exp = dict((phs, exp_out.get_val('traj.{0}.timeseries.states:deltav'.format(phs)))
                      for phs in ['burn1', 'coast', 'burn2'])
        u1_exp = dict((phs, exp_out.get_val('traj.{0}.timeseries.controls:u1'.format(phs),
                                            units='deg'))
                      for phs in ['burn1', 'burn2'])

        for phs in ['burn1', 'coast', 'burn2']:
            try:
                ax_u1.plot(t_exp[phs], u1_exp[phs], '-', marker=None, color='C0')
                ax_u1.plot(t_sol[phs], u1_sol[phs], 'o', mfc='C1', mec='C1', ms=3)
            except KeyError:
                pass

            ax_deltav.plot(t_exp[phs], dv_exp[phs], '-', marker=None, color='C0')
            ax_deltav.plot(t_sol[phs], dv_sol[phs], 'o', mfc='C1', mec='C1', ms=3)

            ax_xy.plot(x_exp[phs], y_exp[phs], '-', marker=None, color='C0', label='explicit')
            ax_xy.plot(x_sol[phs], y_sol[phs], 'o', mfc='C1', mec='C1', ms=3, label='implicit')

        plt.show()
    def test_tandem_phases_for_docs(self):
        p = om.Problem(model=om.Group())

        p.driver = om.pyOptSparseDriver()
        p.driver.options['optimizer'] = 'SLSQP'
        p.driver.declare_coloring()

        #
        # First Phase: Standard Brachistochrone
        #
        num_segments = 10
        transcription_order = 3
        compressed = False

        tx0 = dm.GaussLobatto(num_segments=num_segments,
                              order=transcription_order,
                              compressed=compressed)

        tx1 = dm.Radau(num_segments=num_segments*2,
                       order=transcription_order*3,
                       compressed=compressed)

        phase0 = dm.Phase(ode_class=BrachistochroneODE, transcription=tx0)

        p.model.add_subsystem('phase0', phase0)

        phase0.set_time_options(fix_initial=True, duration_bounds=(.5, 10))

        phase0.add_state('x', rate_source=BrachistochroneODE.states['x']['rate_source'],
                         units=BrachistochroneODE.states['x']['units'],
                         fix_initial=True, fix_final=False, solve_segments=False)

        phase0.add_state('y', rate_source=BrachistochroneODE.states['y']['rate_source'],
                         units=BrachistochroneODE.states['y']['units'],
                         fix_initial=True, fix_final=False, solve_segments=False)

        phase0.add_state('v', rate_source=BrachistochroneODE.states['v']['rate_source'],
                         targets=BrachistochroneODE.states['v']['targets'],
                         units=BrachistochroneODE.states['v']['units'],
                         fix_initial=True, fix_final=False, solve_segments=False)

        phase0.add_control('theta', continuity=True, rate_continuity=True,
                           targets=BrachistochroneODE.parameters['theta']['targets'],
                           units='deg', lower=0.01, upper=179.9)

        phase0.add_input_parameter('g', targets=BrachistochroneODE.parameters['g']['targets'],
                                   units='m/s**2', val=9.80665)

        phase0.add_boundary_constraint('x', loc='final', equals=10)
        phase0.add_boundary_constraint('y', loc='final', equals=5)

        # Add alternative timeseries output to provide control inputs for the next phase
        phase0.add_timeseries('timeseries2', transcription=tx1, subset='control_input')

        #
        # Second Phase: Integration of ArcLength
        #

        phase1 = dm.Phase(ode_class=BrachistochroneArclengthODE, transcription=tx1)

        p.model.add_subsystem('phase1', phase1)

        phase1.set_time_options(fix_initial=True, input_duration=True)

        phase1.add_state('S', fix_initial=True, fix_final=False,
                         rate_source='Sdot', units='m')

        phase1.add_control('theta', opt=False, units='deg', targets='theta')
        phase1.add_control('v', opt=False, units='m/s', targets='v')

        #
        # Connect the two phases
        #
        p.model.connect('phase0.t_duration', 'phase1.t_duration')

        p.model.connect('phase0.timeseries2.controls:theta', 'phase1.controls:theta')
        p.model.connect('phase0.timeseries2.states:v', 'phase1.controls:v')

        # Minimize time at the end of the phase
        # phase1.add_objective('time', loc='final', scaler=1)
        # phase1.add_boundary_constraint('S', loc='final', upper=12)

        phase1.add_objective('S', loc='final', ref=1)

        p.model.linear_solver = om.DirectSolver()
        p.setup(check=True)

        p['phase0.t_initial'] = 0.0
        p['phase0.t_duration'] = 2.0

        p['phase0.states:x'] = phase0.interpolate(ys=[0, 10], nodes='state_input')
        p['phase0.states:y'] = phase0.interpolate(ys=[10, 5], nodes='state_input')
        p['phase0.states:v'] = phase0.interpolate(ys=[0, 9.9], nodes='state_input')
        p['phase0.controls:theta'] = phase0.interpolate(ys=[5, 100], nodes='control_input')
        p['phase0.input_parameters:g'] = 9.80665

        p['phase1.states:S'] = 0.0

        dm.run_problem(p)

        expected = np.sqrt((10-0)**2 + (10 - 5)**2)
        assert_near_equal(p['phase1.timeseries.states:S'][-1], expected, tolerance=1.0E-3)

        fig, (ax0, ax1) = plt.subplots(2, 1)
        fig.tight_layout()
        ax0.plot(p.get_val('phase0.timeseries.states:x'), p.get_val('phase0.timeseries.states:y'), '.')
        ax0.set_xlabel('x (m)')
        ax0.set_ylabel('y (m)')
        ax1.plot(p.get_val('phase1.timeseries.time'), p.get_val('phase1.timeseries.states:S'), '+')
        ax1.set_xlabel('t (s)')
        ax1.set_ylabel('S (m)')
        plt.show()
Пример #27
0
    def test_reentry(self):
        import openmdao.api as om
        from openmdao.utils.assert_utils import assert_near_equal
        import dymos as dm
        from dymos.examples.shuttle_reentry.shuttle_ode import ShuttleODE
        from dymos.examples.plotting import plot_results

        # Instantiate the problem, add the driver, and allow it to use coloring
        p = om.Problem(model=om.Group())
        p.driver = om.pyOptSparseDriver()
        p.driver.declare_coloring()
        p.driver.options['optimizer'] = 'SLSQP'

        # Instantiate the trajectory and add a phase to it
        traj = p.model.add_subsystem('traj', dm.Trajectory())
        phase0 = traj.add_phase(
            'phase0',
            dm.Phase(ode_class=ShuttleODE,
                     transcription=dm.Radau(num_segments=15, order=3)))

        phase0.set_time_options(fix_initial=True, units='s', duration_ref=200)
        phase0.add_state('h',
                         fix_initial=True,
                         fix_final=True,
                         units='ft',
                         rate_source='hdot',
                         lower=0,
                         ref0=75000,
                         ref=300000,
                         defect_ref=1000)
        phase0.add_state('gamma',
                         fix_initial=True,
                         fix_final=True,
                         units='rad',
                         rate_source='gammadot',
                         lower=-89. * np.pi / 180,
                         upper=89. * np.pi / 180)
        phase0.add_state('phi',
                         fix_initial=True,
                         fix_final=False,
                         units='rad',
                         rate_source='phidot',
                         lower=0,
                         upper=89. * np.pi / 180)
        phase0.add_state('psi',
                         fix_initial=True,
                         fix_final=False,
                         units='rad',
                         rate_source='psidot',
                         lower=0,
                         upper=90. * np.pi / 180)
        phase0.add_state('theta',
                         fix_initial=True,
                         fix_final=False,
                         units='rad',
                         rate_source='thetadot',
                         lower=-89. * np.pi / 180,
                         upper=89. * np.pi / 180)
        phase0.add_state('v',
                         fix_initial=True,
                         fix_final=True,
                         units='ft/s',
                         rate_source='vdot',
                         lower=0,
                         ref0=2500,
                         ref=25000)
        phase0.add_control(
            'alpha',
            units='rad',
            opt=True,
            lower=-np.pi / 2,
            upper=np.pi / 2,
        )
        phase0.add_control(
            'beta',
            units='rad',
            opt=True,
            lower=-89 * np.pi / 180,
            upper=1 * np.pi / 180,
        )

        # The original implementation by Betts includes a heating rate path constraint.
        # This will work with the SNOPT optimizer but SLSQP has difficulty converging the solution.
        # phase0.add_path_constraint('q', lower=0, upper=70, units='Btu/ft**2/s', ref=70)
        phase0.add_timeseries_output('q', shape=(1, ), units='Btu/ft**2/s')

        phase0.add_objective('theta', loc='final', ref=-0.01)

        p.setup(check=True)

        p.set_val('traj.phase0.states:h',
                  phase0.interpolate(ys=[260000, 80000], nodes='state_input'),
                  units='ft')
        p.set_val('traj.phase0.states:gamma',
                  phase0.interpolate(ys=[-1, -5], nodes='state_input'),
                  units='deg')
        p.set_val('traj.phase0.states:phi',
                  phase0.interpolate(ys=[0, 75], nodes='state_input'),
                  units='deg')
        p.set_val('traj.phase0.states:psi',
                  phase0.interpolate(ys=[90, 10], nodes='state_input'),
                  units='deg')
        p.set_val('traj.phase0.states:theta',
                  phase0.interpolate(ys=[0, 25], nodes='state_input'),
                  units='deg')
        p.set_val('traj.phase0.states:v',
                  phase0.interpolate(ys=[25600, 2500], nodes='state_input'),
                  units='ft/s')
        p.set_val('traj.phase0.t_initial', 0, units='s')
        p.set_val('traj.phase0.t_duration', 2000, units='s')
        p.set_val('traj.phase0.controls:alpha',
                  phase0.interpolate(ys=[17.4, 17.4], nodes='control_input'),
                  units='deg')
        p.set_val('traj.phase0.controls:beta',
                  phase0.interpolate(ys=[-75, 0], nodes='control_input'),
                  units='deg')

        # Run the driver
        dm.run_problem(p)

        # Check the validity of the solution
        assert_near_equal(p.get_val('traj.phase0.timeseries.time')[-1],
                          2008.59,
                          tolerance=1e-3)
        assert_near_equal(p.get_val('traj.phase0.timeseries.states:theta',
                                    units='deg')[-1],
                          34.1412,
                          tolerance=1e-3)
        # assert_near_equal(p.get_val('traj.phase0.timeseries.time')[-1], 2181.90371131, tolerance=1e-3)
        # assert_near_equal(p.get_val('traj.phase0.timeseries.states:theta')[-1], .53440626, tolerance=1e-3)

        # Run the simulation to check if the model is physically valid
        sim_out = traj.simulate()

        # Plot the results

        plot_results([
            ('traj.phase0.timeseries.time',
             'traj.phase0.timeseries.controls:alpha', 'time (s)',
             'alpha (rad)'),
            ('traj.phase0.timeseries.time',
             'traj.phase0.timeseries.controls:beta', 'time (s)', 'beta (rad)'),
            ('traj.phase0.timeseries.time',
             'traj.phase0.timeseries.states:theta', 'time (s)', 'theta (rad)'),
            ('traj.phase0.timeseries.time', 'traj.phase0.timeseries.q',
             'time (s)', 'q (btu/ft/ft/s')
        ],
                     title='Reentry Solution',
                     p_sol=p,
                     p_sim=sim_out)

        plt.show()
    def test_brachistochrone_tandem_phases(self):
        from dymos.examples.brachistochrone.brachistochrone_ode import BrachistochroneODE

        import numpy as np
        import matplotlib.pyplot as plt
        plt.switch_backend('Agg')
        import openmdao.api as om
        import dymos as dm

        from openmdao.utils.assert_utils import assert_near_equal

        p = om.Problem(model=om.Group())

        p.driver = om.pyOptSparseDriver()
        p.driver.options['optimizer'] = 'SLSQP'
        p.driver.declare_coloring()

        # The transcription of the first phase
        tx0 = dm.GaussLobatto(num_segments=10, order=3, compressed=False)

        # The transcription for the second phase (and the secondary timeseries outputs from the first phase)
        tx1 = dm.Radau(num_segments=20, order=9, compressed=False)

        #
        # First Phase: Integrate the standard brachistochrone ODE
        #
        phase0 = dm.Phase(ode_class=BrachistochroneODE, transcription=tx0)

        p.model.add_subsystem('phase0', phase0)

        phase0.set_time_options(fix_initial=True, duration_bounds=(.5, 10))

        phase0.add_state('x', fix_initial=True, fix_final=False)

        phase0.add_state('y', fix_initial=True, fix_final=False)

        phase0.add_state('v', fix_initial=True, fix_final=False)

        phase0.add_control('theta',
                           continuity=True,
                           rate_continuity=True,
                           units='deg',
                           lower=0.01,
                           upper=179.9)

        phase0.add_parameter('g', units='m/s**2', val=9.80665)

        phase0.add_boundary_constraint('x', loc='final', equals=10)
        phase0.add_boundary_constraint('y', loc='final', equals=5)

        # Add alternative timeseries output to provide control inputs for the next phase
        phase0.add_timeseries('timeseries2',
                              transcription=tx1,
                              subset='control_input')

        #
        # Second Phase: Integration of ArcLength
        #
        phase1 = dm.Phase(ode_class=BrachistochroneArclengthODE,
                          transcription=tx1)

        p.model.add_subsystem('phase1', phase1)

        phase1.set_time_options(fix_initial=True, input_duration=True)

        phase1.add_state('S',
                         fix_initial=True,
                         fix_final=False,
                         rate_source='Sdot',
                         units='m')

        phase1.add_control('theta', opt=False, units='deg', targets='theta')
        phase1.add_control('v', opt=False, units='m/s', targets='v')

        #
        # Connect the two phases
        #
        p.model.connect('phase0.t_duration', 'phase1.t_duration')

        p.model.connect('phase0.timeseries2.controls:theta',
                        'phase1.controls:theta')
        p.model.connect('phase0.timeseries2.states:v', 'phase1.controls:v')

        # Minimize arclength at the end of the second phase
        phase1.add_objective('S', loc='final', ref=1)

        p.model.linear_solver = om.DirectSolver()
        p.setup(check=True)

        p['phase0.t_initial'] = 0.0
        p['phase0.t_duration'] = 2.0

        p['phase0.states:x'] = phase0.interpolate(ys=[0, 10],
                                                  nodes='state_input')
        p['phase0.states:y'] = phase0.interpolate(ys=[10, 5],
                                                  nodes='state_input')
        p['phase0.states:v'] = phase0.interpolate(ys=[0, 9.9],
                                                  nodes='state_input')
        p['phase0.controls:theta'] = phase0.interpolate(ys=[5, 100],
                                                        nodes='control_input')
        p['phase0.parameters:g'] = 9.80665

        p['phase1.states:S'] = 0.0

        dm.run_problem(p)

        expected = np.sqrt((10 - 0)**2 + (10 - 5)**2)
        assert_near_equal(p.get_val('phase1.timeseries.states:S')[-1],
                          expected,
                          tolerance=1.0E-3)

        fig, (ax0, ax1) = plt.subplots(2, 1)
        fig.tight_layout()
        ax0.plot(p.get_val('phase0.timeseries.states:x'),
                 p.get_val('phase0.timeseries.states:y'), '.')
        ax0.set_xlabel('x (m)')
        ax0.set_ylabel('y (m)')
        ax1.plot(p.get_val('phase1.timeseries.time'),
                 p.get_val('phase1.timeseries.states:S'), '+')
        ax1.set_xlabel('t (s)')
        ax1.set_ylabel('S (m)')
        plt.show()
    def test_steady_aircraft_for_docs(self):
        import matplotlib.pyplot as plt

        import openmdao.api as om
        from openmdao.utils.assert_utils import assert_near_equal

        import dymos as dm

        from dymos.examples.aircraft_steady_flight.aircraft_ode import AircraftODE
        from dymos.examples.plotting import plot_results
        from dymos.utils.lgl import lgl

        p = om.Problem(model=om.Group())
        p.driver = om.pyOptSparseDriver()
        p.driver.options['optimizer'] = 'SLSQP'
        p.driver.declare_coloring()

        num_seg = 15
        seg_ends, _ = lgl(num_seg + 1)

        traj = p.model.add_subsystem('traj', dm.Trajectory())

        phase = traj.add_phase(
            'phase0',
            dm.Phase(ode_class=AircraftODE,
                     transcription=dm.Radau(num_segments=num_seg,
                                            segment_ends=seg_ends,
                                            order=3,
                                            compressed=False)))

        # Pass Reference Area from an external source
        assumptions = p.model.add_subsystem('assumptions', om.IndepVarComp())
        assumptions.add_output('S', val=427.8, units='m**2')
        assumptions.add_output('mass_empty', val=1.0, units='kg')
        assumptions.add_output('mass_payload', val=1.0, units='kg')

        phase.set_time_options(fix_initial=True,
                               duration_bounds=(300, 10000),
                               duration_ref=5600)

        phase.add_state('range',
                        units='NM',
                        rate_source='range_rate_comp.dXdt:range',
                        fix_initial=True,
                        fix_final=False,
                        ref=1e-3,
                        defect_ref=1e-3,
                        lower=0,
                        upper=2000)

        phase.add_state('mass_fuel',
                        units='lbm',
                        rate_source='propulsion.dXdt:mass_fuel',
                        fix_initial=True,
                        fix_final=True,
                        upper=1.5E5,
                        lower=0.0,
                        ref=1e2,
                        defect_ref=1e2)

        phase.add_state('alt',
                        units='kft',
                        rate_source='climb_rate',
                        fix_initial=True,
                        fix_final=True,
                        lower=0.0,
                        upper=60,
                        ref=1e-3,
                        defect_ref=1e-3)

        phase.add_control('climb_rate',
                          units='ft/min',
                          opt=True,
                          lower=-3000,
                          upper=3000,
                          targets=['gam_comp.climb_rate'],
                          rate_continuity=True,
                          rate2_continuity=False)

        phase.add_control('mach',
                          targets=['tas_comp.mach', 'aero.mach'],
                          units=None,
                          opt=False)

        phase.add_parameter(
            'S',
            targets=['aero.S', 'flight_equilibrium.S', 'propulsion.S'],
            units='m**2')

        phase.add_parameter('mass_empty',
                            targets=['mass_comp.mass_empty'],
                            units='kg')
        phase.add_parameter('mass_payload',
                            targets=['mass_comp.mass_payload'],
                            units='kg')

        phase.add_path_constraint('propulsion.tau', lower=0.01, upper=2.0)

        p.model.connect('assumptions.S', 'traj.phase0.parameters:S')
        p.model.connect('assumptions.mass_empty',
                        'traj.phase0.parameters:mass_empty')
        p.model.connect('assumptions.mass_payload',
                        'traj.phase0.parameters:mass_payload')

        phase.add_objective('range', loc='final', ref=-1.0e-4)

        phase.add_timeseries_output('aero.CL')
        phase.add_timeseries_output('aero.CD')

        p.setup()

        p['traj.phase0.t_initial'] = 0.0
        p['traj.phase0.t_duration'] = 3600.0
        p['traj.phase0.states:range'][:] = phase.interpolate(
            ys=(0, 724.0), nodes='state_input')
        p['traj.phase0.states:mass_fuel'][:] = phase.interpolate(
            ys=(30000, 1e-3), nodes='state_input')
        p['traj.phase0.states:alt'][:] = 10.0

        p['traj.phase0.controls:mach'][:] = 0.8

        p['assumptions.S'] = 427.8
        p['assumptions.mass_empty'] = 0.15E6
        p['assumptions.mass_payload'] = 84.02869 * 400

        dm.run_problem(p)

        assert_near_equal(p.get_val('traj.phase0.timeseries.states:range',
                                    units='NM')[-1],
                          726.85,
                          tolerance=1.0E-2)

        exp_out = traj.simulate()

        assert_near_equal(p.get_val('traj.phase0.timeseries.CL')[0],
                          exp_out.get_val('traj.phase0.timeseries.CL')[0],
                          tolerance=1.0E-9)

        assert_near_equal(p.get_val('traj.phase0.timeseries.CD')[0],
                          exp_out.get_val('traj.phase0.timeseries.CD')[0],
                          tolerance=1.0E-9)

        plot_results(
            [('traj.phase0.timeseries.states:range',
              'traj.phase0.timeseries.states:alt', 'range (NM)',
              'altitude (kft)'),
             ('traj.phase0.timeseries.time',
              'traj.phase0.timeseries.states:mass_fuel', 'time (s)',
              'fuel mass (lbm)'),
             ('traj.phase0.timeseries.time', 'traj.phase0.timeseries.CL',
              'time (s)', 'lift coefficient'),
             ('traj.phase0.timeseries.time', 'traj.phase0.timeseries.CD',
              'time (s)', 'drag coefficient')],
            title='Commercial Aircraft Optimization',
            p_sol=p,
            p_sim=exp_out)

        plt.show()
Пример #30
0
    def test_vanderpol_simulate_true(self):
        # simulate true
        p = vanderpol(transcription='radau-ps', num_segments=30, transcription_order=3,
                      compressed=True, optimizer='SLSQP', delay=0.005, distrib=True, use_pyoptsparse=True)

        dm.run_problem(p, run_driver=True, simulate=True)