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.Radau(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_design_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.design_parameters:g', 9.80665)

        #
        # 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()

        # 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()
Exemplo n.º 2
0
    def test_doc_ssto_linear_tangent_guidance(self):
        import numpy as np
        import matplotlib.pyplot as plt
        import openmdao.api as om
        from openmdao.utils.assert_utils import assert_near_equal
        import dymos as dm
        from dymos.examples.plotting import plot_results

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        class LinearTangentGuidanceComp(om.ExplicitComponent):
            """ Compute pitch angle from static controls governing linear expression for
                pitch angle tangent as function of time.
            """

            def initialize(self):
                self.options.declare('num_nodes', types=int)

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

                self.add_input('a_ctrl',
                               val=np.zeros(nn),
                               desc='linear tangent slope',
                               units='1/s')

                self.add_input('b_ctrl',
                               val=np.zeros(nn),
                               desc='tangent of theta at t=0',
                               units=None)

                self.add_input('time_phase',
                               val=np.zeros(nn),
                               desc='time',
                               units='s')

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

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

                self.declare_partials(of='theta', wrt='a_ctrl', rows=arange, cols=arange, val=1.0)
                self.declare_partials(of='theta', wrt='b_ctrl', rows=arange, cols=arange, val=1.0)
                self.declare_partials(of='theta', wrt='time_phase', rows=arange, cols=arange, val=1.0)

            def compute(self, inputs, outputs):
                a = inputs['a_ctrl']
                b = inputs['b_ctrl']
                t = inputs['time_phase']
                outputs['theta'] = np.arctan(a * t + b)

            def compute_partials(self, inputs, jacobian):
                a = inputs['a_ctrl']
                b = inputs['b_ctrl']
                t = inputs['time_phase']

                x = a * t + b
                denom = x ** 2 + 1.0

                jacobian['theta', 'a_ctrl'] = t / denom
                jacobian['theta', 'b_ctrl'] = 1.0 / denom
                jacobian['theta', 'time_phase'] = a / denom

        # @dm.declare_time(units='s', targets=['guidance.time_phase'])
        # @dm.declare_state('x', rate_source='eom.xdot', units='m')
        # @dm.declare_state('y', rate_source='eom.ydot', units='m')
        # @dm.declare_state('vx', rate_source='eom.vxdot', targets=['eom.vx'], units='m/s')
        # @dm.declare_state('vy', rate_source='eom.vydot', targets=['eom.vy'], units='m/s')
        # @dm.declare_state('m', rate_source='eom.mdot', targets=['eom.m'], units='kg')
        # @dm.declare_parameter('thrust', targets=['eom.thrust'], units='N')
        # @dm.declare_parameter('a_ctrl', targets=['guidance.a_ctrl'], units='1/s')
        # @dm.declare_parameter('b_ctrl', targets=['guidance.b_ctrl'], units=None)
        # @dm.declare_parameter('Isp', targets=['eom.Isp'], units='s')
        class LaunchVehicleLinearTangentODE(om.Group):
            """
            The LaunchVehicleLinearTangentODE for this case consists of a guidance component and
            the EOM.  Guidance is simply an OpenMDAO ExecComp which computes the arctangent of the
            tan_theta variable.
            """
            def initialize(self):
                self.options.declare('num_nodes', types=int,
                                     desc='Number of nodes to be evaluated in the RHS')

            def setup(self):
                nn = self.options['num_nodes']
                self.add_subsystem('guidance', LinearTangentGuidanceComp(num_nodes=nn))
                self.add_subsystem('eom', LaunchVehicle2DEOM(num_nodes=nn))
                self.connect('guidance.theta', 'eom.theta')

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

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

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

        phase = dm.Phase(ode_class=LaunchVehicleLinearTangentODE,
                         transcription=dm.GaussLobatto(num_segments=10, order=5, compressed=True))

        traj.add_phase('phase0', phase)

        phase.set_time_options(initial_bounds=(0, 0), duration_bounds=(10, 1000),
                               units='s', targets=['guidance.time_phase'])

        phase.add_state('x', fix_initial=True, lower=0, rate_source='eom.xdot', units='m')
        phase.add_state('y', fix_initial=True, lower=0, rate_source='eom.ydot', units='m')
        phase.add_state('vx', fix_initial=True, lower=0, rate_source='eom.vxdot', targets=['eom.vx'], units='m/s')
        phase.add_state('vy', fix_initial=True, rate_source='eom.vydot', targets=['eom.vy'], units='m/s')
        phase.add_state('m', fix_initial=True, rate_source='eom.mdot', targets=['eom.m'], units='kg')

        phase.add_boundary_constraint('y', loc='final', equals=1.85E5, linear=True)
        phase.add_boundary_constraint('vx', loc='final', equals=1627.0)
        phase.add_boundary_constraint('vy', loc='final', equals=0)

        phase.add_design_parameter('a_ctrl', units='1/s', opt=True, targets=['guidance.a_ctrl'])
        phase.add_design_parameter('b_ctrl', units=None, opt=True, targets=['guidance.b_ctrl'])
        phase.add_design_parameter('thrust', units='N', opt=False, val=3.0 * 50000.0 * 1.61544, targets=['eom.thrust'])
        phase.add_design_parameter('Isp', units='s', opt=False, val=1.0E6, targets=['eom.Isp'])

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

        p.model.linear_solver = om.DirectSolver()

        phase.add_timeseries_output('guidance.theta', units='deg')

        p.setup(check=True)

        p['traj.phase0.t_initial'] = 0.0
        p['traj.phase0.t_duration'] = 500.0
        p['traj.phase0.states:x'] = phase.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.design_parameters:a_ctrl'] = -0.01
        p['traj.phase0.design_parameters:b_ctrl'] = 3.0

        dm.run_problem(p)

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

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

        #
        # Plot the results
        #
        plot_results([('traj.phase0.timeseries.states:x', 'traj.phase0.timeseries.states:y',
                       'range (m)', 'altitude (m)'),
                      ('traj.phase0.timeseries.time', 'traj.phase0.timeseries.theta',
                       'range (m)', 'altitude (m)')],
                     title='Single Stage to Orbit Solution Using Linear Tangent Guidance',
                     p_sol=p, p_sim=exp_out)

        plt.show()
Exemplo n.º 3
0
 def test_for_docs(self):
     prob = self.trajectory_example()
     assert_near_equal(prob['phase2.vm.ode_integ.velocity_final'], 1.66689857, 1e-8)
Exemplo n.º 4
0
    def test_get_remote(self):

        N = 3

        class DistribComp(om.ExplicitComponent):

            def setup(self):
                rank = self.comm.rank
                sizes, offsets = evenly_distrib_idxs(self.comm.size, N)

                self.add_input('x', shape=1, src_indices=[0], distributed=True)
                self.add_output('y', shape=sizes[rank], distributed=True)

            def compute(self, inputs, outputs):
                rank = self.comm.rank
                sizes, offsets = evenly_distrib_idxs(self.comm.size, N)

                outputs['y'] = inputs['x']*np.ones((sizes[rank],))
                if rank == 0:
                    outputs['y'][0] = 2.

        class MyModel(om.Group):

            def setup(self):
                self.add_subsystem('ivc', om.IndepVarComp('x', 0.), promotes_outputs=['*'])
                self.add_subsystem('dst', DistribComp(), promotes_inputs=['*'])
                self.add_subsystem('sum', om.ExecComp('z = sum(y)', y=np.zeros((N,)), z=0.0))
                self.connect('dst.y', 'sum.y', src_indices=om.slicer[:])

                self.add_subsystem('par', om.ParallelGroup(), promotes_inputs=['*'])
                self.par.add_subsystem('c1', om.ExecComp(['y=2.0*x']), promotes_inputs=['*'])
                self.par.add_subsystem('c2', om.ExecComp(['y=5.0*x']), promotes_inputs=['*'])

        prob = om.Problem(model=MyModel())
        prob.setup(mode='fwd')

        prob['x'] = 7.0
        prob.run_model()

        # get_remote=True
        assert_near_equal(prob.get_val('x', get_remote=True), [7.])
        assert_near_equal(prob.get_val('dst.x', get_remote=True), [7., 7.])  # ???????
        assert_near_equal(prob.get_val('dst.y', get_remote=True), [2., 7., 7.])
        assert_near_equal(prob.get_val('par.c1.x', get_remote=True), [7.])
        assert_near_equal(prob.get_val('par.c1.y', get_remote=True), [14.])
        assert_near_equal(prob.get_val('par.c2.x', get_remote=True), [7.])
        assert_near_equal(prob.get_val('par.c2.y', get_remote=True), [35.])

        self.maxDiff = None

        remote_msg = ("<model> <class MyModel>: Variable '{name}' is not local to rank {rank}. "
                      "You can retrieve values from other processes using "
                      "`get_val(<name>, get_remote=True)`.")

        distrib_msg = ("<model> <class MyModel>: Variable '{name}' is a distributed variable. "
                       "You can retrieve values from all processes using "
                       "`get_val(<name>, get_remote=True)` or from the local "
                       "process using `get_val(<name>, get_remote=False)`.")

        if prob.comm.rank == 0:
            #
            # get_remote=False
            #

            # get the local part of the distributed inputs/outputs
            assert_near_equal(prob.get_val('dst.x', get_remote=False), [7.])  # from src ('ivc.x')
            assert_near_equal(prob.model.get_val('dst.x', get_remote=False, from_src=False), [7.])
            assert_near_equal(prob.get_val('dst.y', get_remote=False), [2., 7.])

            # par.c1 is local
            assert_near_equal(prob.get_val('par.c1.x', get_remote=False), [7.])  # from src ('ivc.x')
            assert_near_equal(prob.model.get_val('par.c1.x', get_remote=False, from_src=False), [7.])
            assert_near_equal(prob.get_val('par.c1.y', get_remote=False), [14.])

            # par.c2 is remote
            assert_near_equal(prob.get_val('par.c2.x', get_remote=False), [7.])  # from src ('ivc.x')

            with self.assertRaises(RuntimeError) as cm:
                prob.model.get_val('par.c2.x', get_remote=False, from_src=False)
            self.assertEqual(str(cm.exception), remote_msg.format(name='par.c2.x', rank=0))

            with self.assertRaises(RuntimeError) as cm:
                prob.get_val('par.c2.y', get_remote=False)
            self.assertEqual(str(cm.exception), remote_msg.format(name='par.c2.y', rank=0))

            #
            # get_remote=None
            #

            with self.assertRaises(RuntimeError) as cm:
                prob['dst.x']
            self.assertEqual(str(cm.exception), distrib_msg.format(name='dst.x'))

            with self.assertRaises(RuntimeError) as cm:
                prob.model.get_val('dst.x', get_remote=None, from_src=False)
            self.assertEqual(str(cm.exception), distrib_msg.format(name='dst.x'))

            with self.assertRaises(RuntimeError) as cm:
                prob['dst.y']
            self.assertEqual(str(cm.exception), distrib_msg.format(name='dst.y'))

            # par.c1 is local
            assert_near_equal(prob['par.c1.x'], [7.])  # from src ('ivc.x')
            assert_near_equal(prob.model.get_val('par.c1.x', get_remote=None, from_src=False), [7.])
            assert_near_equal(prob['par.c1.y'], [14.])

            # par.c2 is remote
            assert_near_equal(prob['par.c2.x'], [7.])  # from src ('ivc.x')

            with self.assertRaises(RuntimeError) as cm:
                prob.model.get_val('par.c2.x', get_remote=None, from_src=False)
            self.assertEqual(str(cm.exception), remote_msg.format(name='par.c2.x', rank=0))

            with self.assertRaises(RuntimeError) as cm:
                prob['par.c2.y']
            self.assertEqual(str(cm.exception), remote_msg.format(name='par.c2.y', rank=0))

        else:
            #
            # get_remote=False
            #

            # get the local part of the distributed inputs/outputs
            assert_near_equal(prob.get_val('dst.x', get_remote=False), [7.])  # from src ('ivc.x')
            assert_near_equal(prob.model.get_val('dst.x', get_remote=False, from_src=False), [7.])
            assert_near_equal(prob.get_val('dst.y', get_remote=False), [7.])

            # par.c1 is remote
            prob.get_val('par.c1.x', get_remote=False)  # from src ('ivc.x')

            with self.assertRaises(RuntimeError) as cm:
                prob.model.get_val('par.c1.x', get_remote=False, from_src=False)
            self.assertEqual(str(cm.exception), remote_msg.format(name='par.c1.x', rank=1))

            with self.assertRaises(RuntimeError) as cm:
                prob.get_val('par.c1.y', get_remote=False)
            self.assertEqual(str(cm.exception), remote_msg.format(name='par.c1.y', rank=1))

            # par.c2 is local
            assert_near_equal(prob.get_val('par.c2.x', get_remote=False), [7.])  # from src ('ivc.x')
            assert_near_equal(prob.model.get_val('par.c2.x', get_remote=False, from_src=False), [7.])
            assert_near_equal(prob.get_val('par.c2.y', get_remote=False), [35.])

            #
            # get_remote=None
            #

            with self.assertRaises(RuntimeError) as cm:
                prob['dst.x']
            self.assertEqual(str(cm.exception), distrib_msg.format(name='dst.x'))

            with self.assertRaises(RuntimeError) as cm:
                prob.model.get_val('dst.x', get_remote=None, from_src=False)
            self.assertEqual(str(cm.exception), distrib_msg.format(name='dst.x'))

            with self.assertRaises(RuntimeError) as cm:
                prob['dst.y']
            self.assertEqual(str(cm.exception), distrib_msg.format(name='dst.y'))

            # par.c1 is remote
            assert_near_equal(prob['par.c1.x'], [7.])  # from src ('ivc.x')

            with self.assertRaises(RuntimeError) as cm:
                prob.model.get_val('par.c1.x', get_remote=None, from_src=False)
            self.assertEqual(str(cm.exception), remote_msg.format(name='par.c1.x', rank=1))

            with self.assertRaises(RuntimeError) as cm:
                prob['par.c1.y']
            self.assertEqual(str(cm.exception), remote_msg.format(name='par.c1.y', rank=1))

            # par.c2 is local
            assert_near_equal(prob['par.c2.x'], [7.])  # from src ('ivc.x')
            assert_near_equal(prob.model.get_val('par.c2.x', get_remote=None, from_src=False), [7.])
            assert_near_equal(prob['par.c2.y'], [35.])
    def test_nonlinear_circuit_analysis(self):
        import numpy as np

        import openmdao.api as om

        class Resistor(om.ExplicitComponent):
            """Computes current across a resistor using Ohm's law."""
            def initialize(self):
                self.options.declare('R',
                                     default=1.,
                                     desc='Resistance in Ohms')

            def setup(self):
                self.add_input('V_in', units='V')
                self.add_input('V_out', units='V')
                self.add_output('I', units='A')

                # partial derivs are constant, so we can assign their values in setup
                R = self.options['R']
                self.declare_partials('I', 'V_in', val=1 / R)
                self.declare_partials('I', 'V_out', val=-1 / R)

            def compute(self, inputs, outputs):
                deltaV = inputs['V_in'] - inputs['V_out']
                outputs['I'] = deltaV / self.options['R']

        class Diode(om.ExplicitComponent):
            """Computes current across a diode using the Shockley diode equation."""
            def initialize(self):
                self.options.declare('Is',
                                     default=1e-15,
                                     desc='Saturation current in Amps')
                self.options.declare('Vt',
                                     default=.025875,
                                     desc='Thermal voltage in Volts')

            def setup(self):
                self.add_input('V_in', units='V')
                self.add_input('V_out', units='V')
                self.add_output('I', units='A')

                # non-linear component, so we'll declare the partials here but compute them in compute_partials
                self.declare_partials('I', 'V_in')
                self.declare_partials('I', 'V_out')

            def compute(self, inputs, outputs):
                deltaV = inputs['V_in'] - inputs['V_out']
                Is = self.options['Is']
                Vt = self.options['Vt']
                outputs['I'] = Is * (np.exp(deltaV / Vt) - 1)

            def compute_partials(self, inputs, J):
                deltaV = inputs['V_in'] - inputs['V_out']
                Is = self.options['Is']
                Vt = self.options['Vt']
                I = Is * np.exp(deltaV / Vt)

                J['I', 'V_in'] = I / Vt
                J['I', 'V_out'] = -I / Vt

        class Node(om.ImplicitComponent):
            """Computes voltage residual across a node based on incoming and outgoing current."""
            def initialize(self):
                self.options.declare(
                    'n_in',
                    default=1,
                    types=int,
                    desc='number of connections with + assumed in')
                self.options.declare(
                    'n_out',
                    default=1,
                    types=int,
                    desc='number of current connections + assumed out')

            def setup(self):
                self.add_output('V', val=5., units='V')

                for i in range(self.options['n_in']):
                    i_name = 'I_in:{}'.format(i)
                    self.add_input(i_name, units='A')
                    self.declare_partials('V', i_name, val=1)

                for i in range(self.options['n_out']):
                    i_name = 'I_out:{}'.format(i)
                    self.add_input(i_name, units='A')
                    self.declare_partials('V', i_name, val=-1)

                    # note: we don't declare any partials wrt `V` here,
                    #      because the residual doesn't directly depend on it

            def apply_nonlinear(self, inputs, outputs, residuals):
                residuals['V'] = 0.
                for i_conn in range(self.options['n_in']):
                    residuals['V'] += inputs['I_in:{}'.format(i_conn)]
                for i_conn in range(self.options['n_out']):
                    residuals['V'] -= inputs['I_out:{}'.format(i_conn)]

        class Circuit(om.Group):
            def setup(self):
                self.add_subsystem('n1',
                                   Node(n_in=1, n_out=2),
                                   promotes_inputs=[('I_in:0', 'I_in')])
                self.add_subsystem('n2', Node())  # leaving defaults

                self.add_subsystem('R1',
                                   Resistor(R=100.),
                                   promotes_inputs=[('V_out', 'Vg')])
                self.add_subsystem('R2', Resistor(R=10000.))
                self.add_subsystem('D1',
                                   Diode(),
                                   promotes_inputs=[('V_out', 'Vg')])

                self.connect('n1.V', ['R1.V_in', 'R2.V_in'])
                self.connect('R1.I', 'n1.I_out:0')
                self.connect('R2.I', 'n1.I_out:1')

                self.connect('n2.V', ['R2.V_out', 'D1.V_in'])
                self.connect('R2.I', 'n2.I_in:0')
                self.connect('D1.I', 'n2.I_out:0')

                self.nonlinear_solver = om.NewtonSolver()
                self.linear_solver = om.DirectSolver()

                self.nonlinear_solver.options['iprint'] = 2
                self.nonlinear_solver.options['maxiter'] = 10
                self.nonlinear_solver.options['solve_subsystems'] = True
                self.nonlinear_solver.linesearch = om.ArmijoGoldsteinLS()
                self.nonlinear_solver.linesearch.options['maxiter'] = 10
                self.nonlinear_solver.linesearch.options['iprint'] = 2

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

        model.add_subsystem('circuit', Circuit())

        p.setup()

        p.set_val('circuit.I_in', 0.1)
        p.set_val('circuit.Vg', 0.)

        # set some initial guesses
        p.set_val('circuit.n1.V', 10.)
        p.set_val('circuit.n2.V', 1e-3)

        p.run_model()

        assert_near_equal(p['circuit.n1.V'], 9.90804735, 1e-5)
        assert_near_equal(p['circuit.n2.V'], 0.71278185, 1e-5)
        assert_near_equal(p['circuit.R1.I'], 0.09908047, 1e-5)
        assert_near_equal(p['circuit.R2.I'], 0.00091953, 1e-5)
        assert_near_equal(p['circuit.D1.I'], 0.00091953, 1e-5)

        # sanity check: should sum to .1 Amps
        assert_near_equal(p['circuit.R1.I'] + p['circuit.D1.I'], .1, 1e-6)
Exemplo n.º 6
0
    def test_brachistochrone_explicit_shooting_path_constraint_renamed(self):

        for method in ['rk4', 'ralston']:
            with self.subTest(
                    f"test brachistochrone explicit shooting with method '{method}'"
            ):
                prob = om.Problem()

                prob.driver = om.pyOptSparseDriver(optimizer='SLSQP')

                tx = dm.transcriptions.ExplicitShooting(
                    num_segments=10,
                    grid='gauss-lobatto',
                    method=method,
                    order=3,
                    num_steps_per_segment=5,
                    compressed=True)

                phase = dm.Phase(ode_class=BrachistochroneODE,
                                 transcription=tx)

                phase.set_time_options(units='s',
                                       fix_initial=True,
                                       duration_bounds=(1.0, 10.0))

                # automatically discover states
                phase.set_state_options('x', fix_initial=True)
                phase.set_state_options('y', fix_initial=True)
                phase.set_state_options('v', fix_initial=True)

                phase.add_parameter('g',
                                    val=9.80665,
                                    units='m/s**2',
                                    opt=False)
                phase.add_control('theta',
                                  val=45.0,
                                  units='deg',
                                  opt=True,
                                  lower=1.0E-6,
                                  upper=179.9)

                phase.add_boundary_constraint('x', loc='final', equals=10.0)
                phase.add_boundary_constraint('y', loc='final', equals=5.0)
                phase.add_path_constraint('ydot',
                                          constraint_name='foo',
                                          lower=-100,
                                          upper=0)

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

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

                prob.setup(force_alloc_complex=True)

                prob.set_val('phase0.t_initial', 0.0)
                prob.set_val('phase0.t_duration', 2)
                prob.set_val('phase0.states:x', 0.0)
                prob.set_val('phase0.states:y', 10.0)
                prob.set_val('phase0.states:v', 1.0E-6)
                prob.set_val('phase0.controls:theta',
                             phase.interp('theta', ys=[0.01, 90]),
                             units='deg')

                prob.run_driver()

                x = prob.get_val('phase0.timeseries.states:x')
                y = prob.get_val('phase0.timeseries.states:y')
                ydot = prob.get_val('phase0.timeseries.foo')

                assert_near_equal(x[-1, ...], 10.0, tolerance=1.0E-3)
                self.assertTrue(
                    np.all(ydot <= 1.0E-6),
                    msg='Not all elements of path constraint satisfied')
                assert_near_equal(y[-1, ...], 5.0, tolerance=1.0E-3)

                with np.printoptions(linewidth=1024):
                    cpd = prob.check_partials(compact_print=True,
                                              method='cs',
                                              out_stream=None)
                    assert_check_partials(cpd, atol=1.0E-5, rtol=1.0E-5)
Exemplo n.º 7
0
    def test_explicit_shooting_timeseries_ode_output(self):

        for method in ['rk4', 'ralston']:
            with self.subTest(
                    f"test brachistochrone explicit shooting with method '{method}'"
            ):
                prob = om.Problem()

                prob.driver = om.pyOptSparseDriver(optimizer='SLSQP')

                tx = dm.transcriptions.ExplicitShooting(
                    num_segments=5,
                    grid='gauss-lobatto',
                    method=method,
                    order=3,
                    num_steps_per_segment=10,
                    compressed=True)

                phase = dm.Phase(ode_class=BrachistochroneODE,
                                 transcription=tx)

                phase.set_time_options(units='s',
                                       fix_initial=True,
                                       duration_bounds=(1.0, 10.0))

                # automatically discover states
                phase.set_state_options('x', fix_initial=True)
                phase.set_state_options('y', fix_initial=True)
                phase.set_state_options('v', fix_initial=True)

                phase.add_parameter('g',
                                    val=1.0,
                                    units='m/s**2',
                                    opt=True,
                                    lower=1,
                                    upper=9.80665)
                phase.add_control('theta',
                                  val=45.0,
                                  units='deg',
                                  opt=True,
                                  lower=1.0E-6,
                                  upper=179.9)

                phase.add_boundary_constraint('x', loc='final', equals=10.0)
                phase.add_boundary_constraint('y', loc='final', equals=5.0)

                phase.add_timeseries_output('*')

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

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

                prob.setup(force_alloc_complex=True)

                prob.set_val('phase0.t_initial', 0.0)
                prob.set_val('phase0.t_duration', 2)
                prob.set_val('phase0.states:x', 0.0)
                prob.set_val('phase0.states:y', 10.0)
                prob.set_val('phase0.states:v', 1.0E-6)
                prob.set_val('phase0.parameters:g', 1.0, units='m/s**2')
                prob.set_val('phase0.controls:theta',
                             phase.interp('theta', ys=[0.01, 90]),
                             units='deg')

                dm.run_problem(prob, run_driver=True)

                x = prob.get_val('phase0.timeseries.states:x')
                y = prob.get_val('phase0.timeseries.states:y')
                v = prob.get_val('phase0.timeseries.states:v')
                theta = prob.get_val('phase0.timeseries.controls:theta',
                                     units='rad')
                check = prob.get_val('phase0.timeseries.check')
                t = prob.get_val('phase0.timeseries.time')

                assert_near_equal(x[-1, ...], 10.0, tolerance=1.0E-3)
                assert_near_equal(y[-1, ...], 5.0, tolerance=1.0E-3)
                assert_near_equal(t[-1, ...], 1.8016, tolerance=1.0E-2)
                assert_near_equal(check, v / np.sin(theta))

                prob.model.list_outputs()

                with np.printoptions(linewidth=1024):
                    cpd = prob.check_partials(method='cs', out_stream=None)
                    assert_check_partials(cpd, atol=1.0E-5, rtol=1.0E-5)
Exemplo n.º 8
0
    def test_brachistochrone_recording(self):
        import matplotlib
        matplotlib.use('Agg')
        import openmdao.api as om
        from openmdao.utils.assert_utils import assert_near_equal
        import dymos as dm
        from dymos.examples.brachistochrone.brachistochrone_ode import BrachistochroneODE

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

        phase = dm.Phase(ode_class=BrachistochroneODE,
                         transcription=dm.GaussLobatto(num_segments=10))

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

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

        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_control('theta',
                          units='deg',
                          rate_continuity=False,
                          lower=0.01,
                          upper=179.9)

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

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

        p.model.linear_solver = om.DirectSolver()

        p.setup()

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

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

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

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

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

        outputs = dict([
            (o[0], o[1])
            for o in case.list_outputs(units=True, shape=True, out_stream=None)
        ])

        assert_near_equal(
            p['phase0.controls:theta'],
            outputs['phase0.control_group.indep_controls.controls:theta']
            ['value'])
Exemplo n.º 9
0
    def test_case1(self):

        self.prob = Problem()
        self.prob.model.set_input_defaults('fl_start.P', 17., units='psi')
        self.prob.model.set_input_defaults('fl_start.T', 500., units='degR')
        self.prob.model.set_input_defaults('fl_start.MN', 0.5)
        self.prob.model.set_input_defaults('fl_start.W', 100., units='lbm/s')

        self.prob.model.add_subsystem(
            'fl_start',
            FlowStart(thermo_data=species_data.janaf, elements=AIR_ELEMENTS))

        self.prob.set_solver_print(level=-1)
        self.prob.setup(check=False)

        np.seterr(divide='raise')
        # 6 cases to check against
        for i, data in enumerate(ref_data):
            self.prob['fl_start.P'] = data[h_map['Pt']]
            self.prob['fl_start.T'] = data[h_map['Tt']]
            self.prob['fl_start.W'] = data[h_map['W']]
            self.prob['fl_start.MN'] = data[h_map['MN']]

            self.prob.run_model()

            # check outputs
            tol = 1.0e-3

            if data[h_map[
                    'MN']] >= 2.:  # The Mach 2.0 case is at a ridiculously low temperature, so accuracy is questionable
                tol = 5e-2

            print('Case: ', data[h_map['Pt']], data[h_map['Tt']],
                  data[h_map['W']], data[h_map['MN']])
            npss = data[h_map['Pt']]
            pyc = self.prob['fl_start.Fl_O:tot:P']
            assert_near_equal(pyc, npss, tol)
            npss = data[h_map['Tt']]
            pyc = self.prob['fl_start.Fl_O:tot:T']
            rel_err = abs(npss - pyc) / npss
            print('Tt:', npss, pyc, rel_err)
            assert_near_equal(pyc, npss, tol)
            npss = data[h_map['W']]
            pyc = self.prob['fl_start.Fl_O:stat:W']
            rel_err = abs(npss - pyc) / npss
            print('W:', npss, pyc, rel_err)
            assert_near_equal(pyc, npss, tol)
            npss = data[h_map['ht']]
            pyc = self.prob['fl_start.Fl_O:tot:h']
            rel_err = abs(npss - pyc) / npss
            print('ht:', npss, pyc, rel_err)
            assert_near_equal(pyc, npss, tol)
            npss = data[h_map['s']]
            pyc = self.prob['fl_start.Fl_O:tot:S']
            rel_err = abs(npss - pyc) / npss
            print('S:', npss, pyc, rel_err)
            assert_near_equal(pyc, npss, tol)
            npss = data[h_map['rhot']]
            pyc = self.prob['fl_start.Fl_O:tot:rho']
            rel_err = abs(npss - pyc) / npss
            print('rhot:', npss, pyc, rel_err)
            assert_near_equal(pyc, npss, tol)
            npss = data[h_map['gamt']]
            pyc = self.prob['fl_start.Fl_O:tot:gamma']
            rel_err = abs(npss - pyc) / npss
            print('gamt:', npss, pyc, rel_err)
            assert_near_equal(pyc, npss, tol)
            npss = data[h_map['MN']]
            pyc = self.prob['fl_start.Fl_O:stat:MN']
            rel_err = abs(npss - pyc) / npss
            print('MN:', npss, pyc, rel_err)
            assert_near_equal(pyc, npss, tol)
            npss = data[h_map['Ps']]
            pyc = self.prob['fl_start.Fl_O:stat:P']
            rel_err = abs(npss - pyc) / npss
            print('Ps:', npss, pyc, rel_err)
            assert_near_equal(pyc, npss, tol)
            npss = data[h_map['Ts']]
            pyc = self.prob['fl_start.Fl_O:stat:T']
            rel_err = abs(npss - pyc) / npss
            print('Ts:', npss, pyc, rel_err)
            assert_near_equal(pyc, npss, tol)
            npss = data[h_map['hs']]
            pyc = self.prob['fl_start.Fl_O:stat:h']
            rel_err = abs(npss - pyc) / npss
            print('hs:', npss, pyc, rel_err)
            assert_near_equal(pyc, npss, tol)
            npss = data[h_map['rhos']]
            pyc = self.prob['fl_start.Fl_O:stat:rho']
            rel_err = abs(npss - pyc) / npss
            print('rhos:', npss, pyc, rel_err)
            assert_near_equal(pyc, npss, tol)
            npss = data[h_map['gams']]
            pyc = self.prob['fl_start.Fl_O:stat:gamma']
            rel_err = abs(npss - pyc) / npss
            print('gams:', npss, pyc, rel_err)
            assert_near_equal(pyc, npss, tol)
            npss = data[h_map['V']]
            pyc = self.prob['fl_start.Fl_O:stat:V']
            rel_err = abs(npss - pyc) / npss
            print('V:', npss, pyc, rel_err)
            assert_near_equal(pyc, npss, tol)
            npss = data[h_map['A']]
            pyc = self.prob['fl_start.Fl_O:stat:area']
            rel_err = abs(npss - pyc) / npss
            print('A:', npss, pyc, rel_err)
            assert_near_equal(pyc, npss, tol)
            print()
Exemplo n.º 10
0
    def test_eval_f_derivs_scalar(self):
        gd = dm.transcriptions.grid_data.GridData(
            num_segments=10,
            transcription='gauss-lobatto',
            transcription_order=3)

        time_options = dm.phase.options.TimeOptionsDictionary()

        time_options['targets'] = 't'
        time_options['units'] = 's'

        state_options = {'x': dm.phase.options.StateOptionsDictionary()}

        state_options['x']['shape'] = (1, )
        state_options['x']['units'] = 's**2'
        state_options['x']['rate_source'] = 'x_dot'
        state_options['x']['targets'] = ['x']

        param_options = {'p': dm.phase.options.ParameterOptionsDictionary()}

        param_options['p']['shape'] = (1, )
        param_options['p']['units'] = 's**2'
        param_options['p']['targets'] = ['p']

        control_options = {}
        polynomial_control_options = {}

        prob = om.Problem()

        prob.model.add_subsystem(
            'fixed_step_integrator',
            RKIntegrationComp(SimpleODE,
                              time_options,
                              state_options,
                              param_options,
                              control_options,
                              polynomial_control_options,
                              grid_data=gd,
                              num_steps_per_segment=100))
        prob.setup(mode='fwd', force_alloc_complex=True)

        prob.set_val('fixed_step_integrator.states:x', 0.5)
        prob.set_val('fixed_step_integrator.t_initial', 0.0)
        prob.set_val('fixed_step_integrator.t_duration', 2.0)
        prob.set_val('fixed_step_integrator.parameters:p', 1.0)

        prob.final_setup()

        prob.run_model()

        x = np.array([[0.5]])
        t = np.array([[0.0]])
        theta = np.array([[0, 2.0, 1.0]]).T

        f_nom = np.zeros_like(x)

        f_x_fd = np.zeros_like(x)
        f_t_fd = np.zeros_like(x)
        f_theta_fd = np.zeros_like(x)

        intg = prob.model.fixed_step_integrator
        f_x = intg._f_x
        f_t = intg._f_t
        f_theta = intg._f_θ

        intg._eval_subprob.model.ode_eval.set_segment_index(9)

        intg.eval_f(x, t, theta, f_nom)
        intg.eval_f_derivs(x, t, theta, f_x, f_t, f_theta)

        step = 1.0E-8

        intg.eval_f(x + step, t, theta, f_x_fd)
        f_x_fd = (f_x_fd - f_nom) / step
        assert_near_equal(f_x, f_x_fd, tolerance=1.0E-6)

        intg.eval_f(x, t + step, theta, f_t_fd)
        f_t_fd = (f_t_fd - f_nom) / step
        assert_near_equal(f_t, f_t_fd, tolerance=1.0E-6)

        theta[2] = theta[2] + step
        prob.model.fixed_step_integrator.eval_f(x, t, theta, f_theta_fd)
        f_theta_fd = (f_theta_fd - f_nom) / step
        assert_near_equal(f_theta.real[0, 2],
                          f_theta_fd[0, 0],
                          tolerance=1.0E-6)
Exemplo n.º 11
0
    def test_fwd_parameters_controls(self):
        gd = dm.transcriptions.grid_data.GridData(
            num_segments=5,
            transcription='gauss-lobatto',
            transcription_order=5,
            compressed=True)

        time_options = dm.phase.options.TimeOptionsDictionary()

        time_options['units'] = 's'

        state_options = {
            'x': dm.phase.options.StateOptionsDictionary(),
            'y': dm.phase.options.StateOptionsDictionary(),
            'v': dm.phase.options.StateOptionsDictionary()
        }

        state_options['x']['shape'] = (1, )
        state_options['x']['units'] = 'm'
        state_options['x']['rate_source'] = 'xdot'
        state_options['x']['targets'] = []

        state_options['y']['shape'] = (1, )
        state_options['y']['units'] = 'm'
        state_options['y']['rate_source'] = 'ydot'
        state_options['y']['targets'] = []

        state_options['v']['shape'] = (1, )
        state_options['v']['units'] = 'm/s'
        state_options['v']['rate_source'] = 'vdot'
        state_options['v']['targets'] = ['v']

        param_options = {'g': dm.phase.options.ParameterOptionsDictionary()}

        param_options['g']['shape'] = (1, )
        param_options['g']['units'] = 'm/s**2'
        param_options['g']['targets'] = ['g']

        control_options = {
            'theta': dm.phase.options.ControlOptionsDictionary()
        }

        control_options['theta']['shape'] = (1, )
        control_options['theta']['units'] = 'rad'
        control_options['theta']['targets'] = ['theta']

        polynomial_control_options = {}

        p = om.Problem()

        p.model.add_subsystem(
            'fixed_step_integrator',
            RKIntegrationComp(
                ode_class=BrachistochroneODE,
                time_options=time_options,
                state_options=state_options,
                parameter_options=param_options,
                control_options=control_options,
                polynomial_control_options=polynomial_control_options,
                num_steps_per_segment=10,
                grid_data=gd,
                ode_init_kwargs=None))

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

        p.set_val('fixed_step_integrator.states:x', 0.0)
        p.set_val('fixed_step_integrator.states:y', 10.0)
        p.set_val('fixed_step_integrator.states:v', 0.0)
        p.set_val('fixed_step_integrator.t_initial', 0.0)
        p.set_val('fixed_step_integrator.t_duration', 1.8016)
        p.set_val('fixed_step_integrator.parameters:g', 9.80665)

        p.set_val('fixed_step_integrator.controls:theta',
                  np.linspace(0.01, 100.0, 21),
                  units='deg')

        p.run_model()

        x = p.get_val('fixed_step_integrator.states_out:x')
        y = p.get_val('fixed_step_integrator.states_out:y')
        v = p.get_val('fixed_step_integrator.states_out:v')
        theta = p.get_val('fixed_step_integrator.control_values:theta')

        # These tolerances are loose since theta is not properly spaced along the lgl nodes.
        assert_near_equal(x[-1, ...], 10.0, tolerance=0.1)
        assert_near_equal(y[-1, ...], 5.0, tolerance=0.1)
        assert_near_equal(v[-1, ...], 9.9, tolerance=0.1)

        with np.printoptions(linewidth=1024):
            cpd = p.check_partials(compact_print=True,
                                   method='cs',
                                   show_only_incorrect=True)
            assert_check_partials(cpd)
Exemplo n.º 12
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_upstream_control(self):
        import numpy as np
        import openmdao.api as om
        from openmdao.utils.assert_utils import assert_near_equal
        import dymos as dm

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

        from dymos.examples.brachistochrone.brachistochrone_ode import BrachistochroneODE

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

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

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

        # Add the output to provide the values of theta at the control input nodes of the transcription.
        ivc.add_output('theta',
                       shape=(tx.grid_data.subset_num_nodes['control_input']),
                       units='rad')

        # Add this external control as a design variable
        p.model.add_design_var('theta', units='rad', lower=1.0E-5, upper=np.pi)
        # Connect this to controls:theta in the appropriate phase.
        # connect calls are cached, so we can do this before we actually add the trajectory to the problem.
        p.model.connect('theta', 'traj.phase0.controls:theta')

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        # Run the driver to solve the problem
        p.run_driver()

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

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

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

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

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

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

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

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

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

        plt.show()
Exemplo n.º 14
0
    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'] = 'SLSQP'
        p.driver.declare_coloring()

        traj = dm.Trajectory()

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

        # First Phase (burn)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        # Finish Problem Setup

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

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

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

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

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

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

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

        p.set_val('traj.coast.states:r',
                  value=coast.interpolate(ys=[1.3, 1.5], nodes='state_input'))
        p.set_val('traj.coast.states:theta',
                  value=coast.interpolate(ys=[2.1767, 1.7],
                                          nodes='state_input'))

        p.set_val('traj.coast.states:vr',
                  value=coast.interpolate(ys=[0.3285, 0], nodes='state_input'))
        p.set_val('traj.coast.states:vt',
                  value=coast.interpolate(ys=[0.97, 1], nodes='state_input'))
        p.set_val('traj.coast.states:accel',
                  value=coast.interpolate(ys=[0, 0], nodes='state_input'))

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

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

        p.set_val('traj.burn2.controls:u1',
                  value=burn2.interpolate(ys=[0, 0], nodes='control_input'))

        dm.run_problem(p)

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

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

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

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

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

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

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

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

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

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

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

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

        plt.show()
Exemplo n.º 15
0
    def test_2_input_2_fidelity(self):
        import numpy as np
        import openmdao.api as om

        mm = om.MultiFiMetaModelUnStructuredComp(nfi=2)
        mm.add_input('x', np.zeros((1, 2)))
        mm.add_output('y', np.zeros((1, )))

        # Surrrogate model that implements the multifidelity cokriging method.
        mm.options['default_surrogate'] = om.MultiFiCoKrigingSurrogate(
            normalize=False)

        prob = om.Problem()
        prob.model.add_subsystem('mm', mm)

        prob.setup()

        x_hifi = np.array([
            [0.13073587, 0.24909577],  # expensive (hifi) doe
            [0.91915571, 0.4735261],
            [0.75830543, 0.13321705],
            [0.51760477, 0.34594101],
            [0.03531219, 0.77765831],
            [0.27249206, 0.5306115],
            [0.62762489, 0.65778471],
            [0.3914706, 0.09852519],
            [0.86565585, 0.85350002],
            [0.40806563, 0.91465314]
        ])

        y_hifi = np.array([
            69.22687251161716, 28.427292491743817, 20.36175030334259,
            7.840766670948326, 23.950783505007422, 16.0326610719367,
            77.32264403894713, 26.625242780670835, 135.85615334210993,
            101.43980212355875
        ])

        x_lofi = np.array([
            [0.91430235, 0.17029894],  # cheap (lowfi) doe
            [0.99329651, 0.76431519],
            [0.2012252, 0.35006032],
            [0.61707854, 0.90210676],
            [0.15113004, 0.0133355],
            [0.07108082, 0.55344447],
            [0.4483159, 0.52182902],
            [0.5926638, 0.06595122],
            [0.66305449, 0.48579608],
            [0.47965045, 0.7407793],
            [0.13073587, 0.24909577],  # notice hifi doe inclusion
            [0.91915571, 0.4735261],
            [0.75830543, 0.13321705],
            [0.51760477, 0.34594101],
            [0.03531219, 0.77765831],
            [0.27249206, 0.5306115],
            [0.62762489, 0.65778471],
            [0.3914706, 0.09852519],
            [0.86565585, 0.85350002],
            [0.40806563, 0.91465314]
        ])

        y_lofi = list([
            18.204898470295255, 107.66640600958577, 46.11717344625053,
            186.002239934648, 135.12480249921992, 65.3605467926758,
            51.72316385370553, 15.541873662737451, 72.77648156410065,
            100.33324800434931, 86.69974561161716, 52.63307549174382,
            34.358261803342586, 28.218996970948325, 57.280532805007425,
            41.9510060719367, 107.05618533894713, 39.580998480670836,
            171.46115394210995, 138.87939632355875
        ])

        mm.options['train:x'] = x_hifi
        mm.options['train:y'] = y_hifi
        mm.options['train:x_fi2'] = x_lofi
        mm.options['train:y_fi2'] = y_lofi

        prob.set_val('mm.x', np.array([[2. / 3., 1. / 3.]]))
        prob.run_model()

        assert_near_equal(prob.get_val('mm.y'), 26.26, tolerance=0.02)
Exemplo n.º 16
0
    def benchmark_case1(self):

        prob = om.Problem()

        prob.model = mp_single_spool = MPSingleSpool()

        prob.setup(check=False)

        #Define the initial design point
        prob.set_val('DESIGN.fc.alt', 0.0, units='ft')
        prob.set_val('DESIGN.fc.MN', 0.000001)
        prob.set_val('DESIGN.balance.T4_target', 2370.0, units='degR')
        prob.set_val('DESIGN.balance.pwr_target', 4000.0, units='hp')
        prob.set_val('DESIGN.balance.nozz_PR_target', 1.2)
        prob.set_val('DESIGN.comp.PR', 13.5)
        prob.set_val('DESIGN.comp.eff', 0.83)
        prob.set_val('DESIGN.turb.eff', 0.86)
        prob.set_val('DESIGN.pt.eff', 0.9)

        # Set initial guesses for balances
        prob['DESIGN.balance.FAR'] = 0.0175506829934
        prob['DESIGN.balance.W'] = 27.265
        prob['DESIGN.balance.turb_PR'] = 3.8768
        prob['DESIGN.balance.pt_PR'] = 2.8148
        prob['DESIGN.fc.balance.Pt'] = 14.6955113159
        prob['DESIGN.fc.balance.Tt'] = 518.665288153

        for i, pt in enumerate(mp_single_spool.od_pts):

            # initial guesses
            prob[pt + '.balance.W'] = 27.265
            prob[pt + '.balance.FAR'] = 0.0175506829934
            prob[pt + '.balance.HP_Nmech'] = 8070.0
            prob[pt + '.fc.balance.Pt'] = 15.703
            prob[pt + '.fc.balance.Tt'] = 558.31
            prob[pt + '.turb.PR'] = 3.8768
            prob[pt + '.pt.PR'] = 2.8148

        prob.set_solver_print(level=-1)
        prob.set_solver_print(level=2, depth=1)

        np.seterr(divide='raise')

        prob.run_model()
        tol = 1e-5
        print()

        reg_data = 27.265342457866705
        ans = prob['DESIGN.inlet.Fl_O:stat:W'][0]
        print('W:', reg_data, ans)
        assert_near_equal(ans, reg_data, tol)

        reg_data = 13.5
        ans = prob['DESIGN.perf.OPR'][0]
        print('OPR:', reg_data, ans)
        assert_near_equal(ans, reg_data, tol)

        reg_data = 0.01755077946196377
        ans = prob['DESIGN.balance.FAR'][0]
        print('Main FAR:', reg_data, ans)
        assert_near_equal(ans, reg_data, tol)

        reg_data = 3.876811443569159
        ans = prob['DESIGN.balance.turb_PR'][0]
        print('HPT PR:', reg_data, ans)
        assert_near_equal(ans, reg_data, tol)

        reg_data = 800.8503668285215
        ans = prob['DESIGN.perf.Fg'][0]
        print('Fg:', reg_data, ans)
        assert_near_equal(ans, reg_data, tol)

        reg_data = 2.151092078410839
        ans = prob['DESIGN.perf.TSFC'][0]
        print('TSFC:', reg_data, ans)
        assert_near_equal(ans, reg_data, tol)

        reg_data = 1190.1777648503974
        ans = prob['DESIGN.comp.Fl_O:tot:T'][0]
        print('Tt3:', reg_data, ans)
        assert_near_equal(ans, reg_data, tol)

        reg_data = 25.897231212494944
        ans = prob['OD.inlet.Fl_O:stat:W'][0]
        print('W:', reg_data, ans)
        assert_near_equal(ans, reg_data, tol)

        reg_data = 12.42972778706185
        ans = prob['OD.perf.OPR'][0]
        print('OPR:', reg_data, ans)
        assert_near_equal(ans, reg_data, tol)

        reg_data = 0.016304387482120156
        ans = prob['OD.balance.FAR'][0]
        print('Main FAR:', reg_data, ans)
        assert_near_equal(ans, reg_data, tol)

        reg_data = 7853.753342243985
        ans = prob['OD.balance.HP_Nmech'][0]
        print('HP Nmech:', reg_data, ans)
        assert_near_equal(ans, reg_data, tol)

        reg_data = 696.618372248896
        ans = prob['OD.perf.Fg'][0]
        print('Fg:', reg_data, ans)
        assert_near_equal(ans, reg_data, tol)

        reg_data = 2.5052545862974696
        ans = prob['OD.perf.TSFC'][0]
        print('TSFC:', reg_data, ans)
        assert_near_equal(ans, reg_data, tol)

        reg_data = 1158.5197002795887
        ans = prob['OD.comp.Fl_O:tot:T'][0]
        print('Tt3:', reg_data, ans)
        assert_near_equal(ans, reg_data, tol)

        print()
Exemplo n.º 17
0
    def test_2_states_run_model(self):

        for method in [
                'rk4', 'euler', '3/8', 'ralston', 'rkf', 'rkck', 'dopri'
        ]:
            with self.subTest(
                    f"test brachistochrone explicit shooting with method '{method}'"
            ):

                prob = om.Problem()

                tx = dm.transcriptions.ExplicitShooting(
                    num_segments=2,
                    grid='gauss-lobatto',
                    method=method,
                    order=3,
                    num_steps_per_segment=50,
                    compressed=True)

                phase = dm.Phase(ode_class=Simple2StateODE, transcription=tx)

                phase.set_time_options(targets=['t'], units='s')

                # automatically discover states
                phase.set_state_options('x',
                                        targets=['x'],
                                        rate_source='x_dot')
                phase.set_state_options('y',
                                        targets=['y'],
                                        rate_source='y_dot')

                phase.add_parameter('p', targets=['p'])

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

                prob.setup(force_alloc_complex=True)

                prob.set_val('phase0.t_initial', 0.0)
                prob.set_val('phase0.t_duration', 1.0)
                prob.set_val('phase0.states:x', 0.5)
                prob.set_val('phase0.states:y', 1.0)
                prob.set_val('phase0.parameters:p', 1)

                prob.run_model()

                t_f = prob.get_val('phase0.integrator.t_final')
                x_f = prob.get_val('phase0.integrator.states_out:x')
                y_f = prob.get_val('phase0.integrator.states_out:y')

                if method == 'euler':
                    tol = 5.0E-2
                else:
                    tol = 1.0E-3

                assert_near_equal(t_f, 1.0)
                assert_near_equal(x_f[-1, ...], 2.64085909, tolerance=tol)
                assert_near_equal(y_f[-1, ...], 0.1691691, tolerance=tol)

                with np.printoptions(linewidth=1024):
                    cpd = prob.check_partials(compact_print=True, method='cs')
                    assert_check_partials(cpd, atol=1.0E-5, rtol=1.0E-5)
Exemplo n.º 18
0
    def test_feature_example(self):
        def eval_cannonball_range(gam_init, prob, complex_step=False):
            """
            Compute distance given initial speed and angle of cannonball.

            Parameters
            ----------
            gam_init : float
                Initial cannonball firing angle in degrees.
            prob : <Problem>
                OpenMDAO problem that contains the equations of motion.
            complex_step : bool
                Set to True to perform complex step.

            Returns
            -------
            float
                Negative of range in m.
            """
            dt = 0.1  # Time step
            h_init = 1.0  # Height of cannon.
            v_init = 100.0  # Initial cannonball velocity.
            h_target = 0.0  #

            v = v_init
            gam = gam_init
            h = h_init
            r = 0.0
            t = 0.0

            if complex_step:
                prob.set_complex_step_mode(True)

            while h > h_target:

                # Set values
                prob.set_val('v', v)
                prob.set_val('gam', gam, units='deg')

                # Run the model
                prob.run_model()

                # Extract rates
                v_dot = prob.get_val('v_dot')
                gam_dot = prob.get_val('gam_dot', units='deg/s')
                h_dot = prob.get_val('h_dot')
                r_dot = prob.get_val('r_dot')

                h_last = h
                r_last = r

                # Euler Integration
                v = v + dt * v_dot
                gam = gam + dt * gam_dot
                h = h + dt * h_dot
                r = r + dt * r_dot
                t += dt
                # print(v, gam, h, r)

            # Linear interpolation between last two points to get the landing point accurate.
            r_final = r_last + (r - r_last) * h_last / (h_last - h)

            if complex_step:
                prob.set_complex_step_mode(False)

            #print(f"Distance: {r_final}, Time: {t}, Angle: {gam_init}")
            return -r_final

        def gradient_cannonball_range(gam_init, prob):
            """
            Uses complex step to compute gradient of range wrt initial angle.

            Parameters
            ----------
            gam_init : float
                Initial cannonball firing angle in degrees.
            prob : <Problem>
                OpenMDAO problem that contains the equations of motion.

            Returns
            -------
            float
                Derivative of range wrt initial angle in m/deg.
            """
            step = 1.0e-14
            dr_dgam = eval_cannonball_range(gam_init + step * 1j,
                                            prob,
                                            complex_step=True)
            return dr_dgam.imag / step

        prob = om.Problem(model=CannonballODE())
        prob.setup(force_alloc_complex=True)

        # Set constants
        prob.set_val('CL', 0.0)  # Lift Coefficient
        prob.set_val('CD', 0.05)  # Drag Coefficient
        prob.set_val('S', 0.25 * np.pi,
                     units='ft**2')  # Wetted Area (1 ft diameter ball)
        prob.set_val('rho', 1.225)  # Atmospheric Density
        prob.set_val('m', 5.5)  # Cannonball Mass

        prob.set_val('alpha', 0.0)  # Angle of Attack (Not Applicable)
        prob.set_val('T', 0.0)  # Thrust (Not Applicable)

        result = minimize(eval_cannonball_range,
                          27.0,
                          method='SLSQP',
                          jac=gradient_cannonball_range,
                          args=(prob))

        assert_near_equal(result['x'], 42.3810579, 1e-3)
Exemplo n.º 19
0
    def test_brachistochrone_explicit_shooting_polynomial_control(self):
        prob = om.Problem()

        prob.driver = om.pyOptSparseDriver(optimizer='SLSQP')

        tx = dm.transcriptions.ExplicitShooting(num_segments=3,
                                                grid='radau-ps',
                                                method='rk4',
                                                order=3,
                                                num_steps_per_segment=10,
                                                compressed=True)

        phase = dm.Phase(ode_class=BrachistochroneODE, transcription=tx)

        phase.set_time_options(units='s',
                               fix_initial=True,
                               duration_bounds=(1.0, 10.0))

        # automatically discover states
        phase.set_state_options('x', fix_initial=True)
        phase.set_state_options('y', fix_initial=True)
        phase.set_state_options('v', fix_initial=True)

        phase.add_parameter('g',
                            val=1.0,
                            units='m/s**2',
                            opt=True,
                            lower=1,
                            upper=9.80665)
        phase.add_polynomial_control('theta',
                                     order=9,
                                     val=45.0,
                                     units='deg',
                                     opt=True,
                                     lower=1.0E-6,
                                     upper=179.9)

        phase.add_boundary_constraint('x', loc='final', equals=10.0)
        phase.add_boundary_constraint('y', loc='final', equals=5.0)

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

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

        prob.setup(force_alloc_complex=True)

        prob.set_val('phase0.t_initial', 0.0)
        prob.set_val('phase0.t_duration', 2)
        prob.set_val('phase0.states:x', 0.0)
        prob.set_val('phase0.states:y', 10.0)
        prob.set_val('phase0.states:v', 1.0E-6)
        prob.set_val('phase0.parameters:g', 1.0, units='m/s**2')
        prob.set_val('phase0.polynomial_controls:theta',
                     phase.interp('theta', ys=[0.01, 90]),
                     units='deg')

        prob.run_driver()

        x = prob.get_val('phase0.timeseries.states:x')
        y = prob.get_val('phase0.timeseries.states:y')
        t = prob.get_val('phase0.timeseries.time')
        tp = prob.get_val('phase0.timeseries.time_phase')

        assert_near_equal(x[-1, ...], 10.0, tolerance=1.0E-5)
        assert_near_equal(y[-1, ...], 5.0, tolerance=1.0E-5)
        assert_near_equal(t[-1, ...], 1.8016, tolerance=5.0E-3)
        assert_near_equal(tp[-1, ...], 1.8016, tolerance=5.0E-3)

        with np.printoptions(linewidth=1024):
            cpd = prob.check_partials(compact_print=False,
                                      method='cs',
                                      out_stream=None)
            assert_check_partials(cpd, atol=1.0E-5, rtol=1.0E-5)
Exemplo n.º 20
0
    def test_two_phase_cannonball_for_docs(self):
        import numpy as np
        from scipy.interpolate import interp1d

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

        import dymos as dm
        from dymos.models.atmosphere.atmos_1976 import USatm1976Data

        #############################################
        # Component for the design part of the model
        #############################################
        class CannonballSizeComp(om.ExplicitComponent):
            """
            Compute the area and mass of a cannonball with a given radius and density.

            Notes
            -----
            This component is not vectorized with 'num_nodes' as is the usual way
            with Dymos, but is instead intended to compute a scalar mass and reference
            area from scalar radius and density inputs. This component does not reside
            in the ODE but instead its outputs are connected to the trajectory via
            input design parameters.
            """
            def setup(self):
                self.add_input(name='radius',
                               val=1.0,
                               desc='cannonball radius',
                               units='m')
                self.add_input(name='dens',
                               val=7870.,
                               desc='cannonball density',
                               units='kg/m**3')

                self.add_output(name='mass',
                                shape=(1, ),
                                desc='cannonball mass',
                                units='kg')
                self.add_output(name='S',
                                shape=(1, ),
                                desc='aerodynamic reference area',
                                units='m**2')

                self.declare_partials(of='mass', wrt='dens')
                self.declare_partials(of='mass', wrt='radius')

                self.declare_partials(of='S', wrt='radius')

            def compute(self, inputs, outputs):
                radius = inputs['radius']
                dens = inputs['dens']

                outputs['mass'] = (4 / 3.) * dens * np.pi * radius**3
                outputs['S'] = np.pi * radius**2

            def compute_partials(self, inputs, partials):
                radius = inputs['radius']
                dens = inputs['dens']

                partials['mass', 'dens'] = (4 / 3.) * np.pi * radius**3
                partials['mass', 'radius'] = 4. * dens * np.pi * radius**2

                partials['S', 'radius'] = 2 * np.pi * radius

        #############################################
        # Build the ODE class
        #############################################
        class CannonballODE(om.ExplicitComponent):
            """
            Cannonball ODE assuming flat earth and accounting for air resistance
            """
            def initialize(self):
                self.options.declare('num_nodes', types=int)

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

                # static parameters
                self.add_input('m', units='kg')
                self.add_input('S', units='m**2')
                # 0.5 good assumption for a sphere
                self.add_input('CD', 0.5)

                # time varying inputs
                self.add_input('h', units='m', shape=nn)
                self.add_input('v', units='m/s', shape=nn)
                self.add_input('gam', units='rad', shape=nn)

                # state rates
                self.add_output('v_dot',
                                shape=nn,
                                units='m/s**2',
                                tags=['dymos.state_rate_source:v'])
                self.add_output('gam_dot',
                                shape=nn,
                                units='rad/s',
                                tags=['dymos.state_rate_source:gam'])
                self.add_output('h_dot',
                                shape=nn,
                                units='m/s',
                                tags=['dymos.state_rate_source:h'])
                self.add_output('r_dot',
                                shape=nn,
                                units='m/s',
                                tags=['dymos.state_rate_source:r'])
                self.add_output('ke', shape=nn, units='J')

                # Ask OpenMDAO to compute the partial derivatives using complex-step
                # with a partial coloring algorithm for improved performance, and use
                # a graph coloring algorithm to automatically detect the sparsity pattern.
                self.declare_coloring(wrt='*', method='cs')

                alt_data = USatm1976Data.alt * om.unit_conversion('ft', 'm')[0]
                rho_data = USatm1976Data.rho * om.unit_conversion(
                    'slug/ft**3', 'kg/m**3')[0]
                self.rho_interp = interp1d(np.array(alt_data, dtype=complex),
                                           np.array(rho_data, dtype=complex),
                                           kind='linear')

            def compute(self, inputs, outputs):

                gam = inputs['gam']
                v = inputs['v']
                h = inputs['h']
                m = inputs['m']
                S = inputs['S']
                CD = inputs['CD']

                GRAVITY = 9.80665  # m/s**2

                # handle complex-step gracefully from the interpolant
                if np.iscomplexobj(h):
                    rho = self.rho_interp(inputs['h'])
                else:
                    rho = self.rho_interp(inputs['h']).real

                q = 0.5 * rho * inputs['v']**2
                qS = q * S
                D = qS * CD
                cgam = np.cos(gam)
                sgam = np.sin(gam)
                outputs['v_dot'] = -D / m - GRAVITY * sgam
                outputs['gam_dot'] = -(GRAVITY / v) * cgam
                outputs['h_dot'] = v * sgam
                outputs['r_dot'] = v * cgam
                outputs['ke'] = 0.5 * m * v**2

        #############################################
        # Setup the Dymos problem
        #############################################

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

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

        p.model.add_subsystem('size_comp',
                              CannonballSizeComp(),
                              promotes_inputs=['radius', 'dens'])
        p.model.set_input_defaults('dens', val=7.87, units='g/cm**3')
        p.model.add_design_var('radius',
                               lower=0.01,
                               upper=0.10,
                               ref0=0.01,
                               ref=0.10,
                               units='m')

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

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

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

        # All initial states except flight path angle are fixed
        # Final flight path angle is fixed (we will set it to zero
        # so that the phase ends at apogee).
        # The output of the ODE which provides the rate source for each state
        # is obtained from the tags used on those outputs in the ODE.
        # The units of the states are automatically inferred by multiplying the units
        # of those rates by the time units.
        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', units='m**2', static_target=True)
        ascent.add_parameter('m', units='kg', static_target=True)

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

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

        traj.add_phase('descent', descent)

        # All initial states and time are free, since
        #    they will be linked to the final states of ascent.
        # Final altitude is fixed, because 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', units='m**2', static_target=True)
        descent.add_parameter('m', units='kg', static_target=True)

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

        # Add internally-managed design parameters to the trajectory.
        traj.add_parameter('CD',
                           targets={
                               'ascent': ['CD'],
                               'descent': ['CD']
                           },
                           val=0.5,
                           units=None,
                           opt=False,
                           static_target=True)

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

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

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

        # Issue Connections
        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 constants and initial guesses
        #############################################
        p.set_val('radius', 0.05, units='m')
        p.set_val('dens', 7.87, units='g/cm**3')

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

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

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

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

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

        #####################################################
        # Run the optimization and final explicit simulation
        #####################################################
        dm.run_problem(p)

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

        # use the explicit simulation to check the final collocation solution accuracy
        exp_out = traj.simulate()

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

        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(f'traj.ascent.timeseries.states:{state}'),
                'descent': p.get_val(f'traj.descent.timeseries.states:{state}')
            }

            x_exp = {
                'ascent':
                exp_out.get_val(f'traj.ascent.timeseries.states:{state}'),
                'descent':
                exp_out.get_val(f'traj.descent.timeseries.states:{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 = ['m', 'S']
        fig, axes = plt.subplots(nrows=6, ncols=1, figsize=(12, 6))
        for i, param in enumerate(params):
            p_imp = {
                'ascent':
                p.get_val(f'traj.ascent.timeseries.parameters:{param}'),
                'descent':
                p.get_val(f'traj.descent.timeseries.parameters:{param}')
            }

            p_exp = {
                'ascent':
                exp_out.get_val(f'traj.ascent.timeseries.parameters:{param}'),
                'descent':
                exp_out.get_val(f'traj.descent.timeseries.parameters:{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()
Exemplo n.º 21
0
    def test_default_vals_stick(self):
        """
        Make the balanced field problem without any set_val calls after setup.
        """
        p = om.Problem()

        # 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,
                                  initial_val=0.0, duration_val=35.0)
        br_to_v1.add_state('r', fix_initial=True, lower=0, ref=1000.0, defect_ref=1000.0,
                           val=br_to_v1.interpolate(ys=[0, 2500.0], nodes='state_input'))
        br_to_v1.add_state('v', fix_initial=True, lower=0.0001, ref=100.0, defect_ref=100.0,
                           val=br_to_v1.interpolate(ys=[0.0001, 100.0], nodes='state_input'))
        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,
                             initial_val=35.0, duration_val=35.0)
        rto.add_state('r', fix_initial=False, lower=0, ref=1000.0, defect_ref=1000.0,
                      val=rto.interpolate(ys=[2500, 5000.0], nodes='state_input'))
        rto.add_state('v', fix_initial=False, lower=0.0001, ref=100.0, defect_ref=100.0,
                      val=rto.interpolate(ys=[110, 0.0001], nodes='state_input'))
        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,
                                  initial_val=35.0, duration_val=35.0)
        v1_to_vr.add_state('r', fix_initial=False, lower=0, ref=1000.0, defect_ref=1000.0,
                           val=v1_to_vr.interpolate(ys=[2500, 300.0], nodes='state_input'))
        v1_to_vr.add_state('v', fix_initial=False, lower=0.0001, ref=100.0, defect_ref=100.0,
                           val=v1_to_vr.interpolate(ys=[100, 110.0], nodes='state_input'))
        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,
                                initial_val=70.0, duration_val=5.0)
        rotate.add_state('r', fix_initial=False, lower=0, ref=1000.0, defect_ref=1000.0,
                         val=rotate.interpolate(ys=[1750, 1800.0], nodes='state_input'))
        rotate.add_state('v', fix_initial=False, lower=0.0001, ref=100.0, defect_ref=100.0,
                         val=rotate.interpolate(ys=[80, 85.0], nodes='state_input'))
        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,
                               initial_val=75.0, duration_val=10.0)
        climb.add_state('r', fix_initial=False, lower=0, ref=1000.0, defect_ref=1000.0,
                        val=climb.interpolate(ys=[1800, 2500.0], nodes='state_input'))
        climb.add_state('h', fix_initial=True, lower=0.0, ref=1.0, defect_ref=1.0, val=0.0)
        climb.add_state('v', fix_initial=False, lower=0.0001, ref=100.0, defect_ref=100.0,
                        val=climb.interpolate(ys=[85, 90], nodes='state_input'))
        climb.add_state('gam', fix_initial=True, lower=0.0, ref=0.05, defect_ref=0.05,
                        val=climb.interpolate(ys=[0, 0.05], nodes='state_input'))
        climb.add_control('alpha', opt=True, units='deg', lower=-10, upper=15, ref=10,
                          val=climb.interpolate(ys=[0.01, 0.01], nodes='control_input'))
        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', dynamic=False,
                           desc='nominal aircraft thrust',
                           targets={'br_to_v1': ['T']})

        traj.add_parameter('T_engine_out', val=27000, opt=False, units='lbf', dynamic=False,
                           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', dynamic=False,
                           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, dynamic=False,
                           desc='nominal runway friction coeffcient',
                           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, dynamic=False,
                           desc='runway friction coefficient under braking',
                           targets={'rto': ['mu_r']})

        traj.add_parameter('h_runway', val=0., opt=False, units='ft', dynamic=True,
                           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', dynamic=False,
                           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', dynamic=False,
                           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, dynamic=False,
                           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, dynamic=False,
                           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, dynamic=False,
                           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', dynamic=False,
                           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', dynamic=False,
                           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, dynamic=False,
                           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, dynamic=False,
                           desc='maximum lift coefficient for linear fit',
                           targets={f'{phase}': ['CL_max'] for phase in ['br_to_v1', 'v1_to_vr',
                                                                         'rto', 'rotate' 'climb']})

        # Standard "end of first phase to beginning of second phase" linkages
        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
        rto.add_boundary_constraint('v', loc='final', upper=0.001, 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.2, ref=1.2)

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

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

        p.run_model()

        assert_near_equal(p.get_val('traj.rotate.t_initial'), 70)
        assert_near_equal(p.get_val('traj.rotate.t_duration'), 5)
        assert_near_equal(p.get_val('traj.rotate.polynomial_controls:alpha'), np.array([[0, 10]]).T)
        assert_near_equal(p.get_val('traj.climb.controls:alpha'),
                          p.model.traj.phases.climb.interpolate(ys=[0.01, 0.01], nodes='control_input'))
        assert_near_equal(p.get_val('traj.climb.states:gam'),
                          p.model.traj.phases.climb.interpolate(ys=[0.0, 0.05], nodes='state_input'))
        assert_near_equal(p.get_val('traj.climb.states:h'),
                          p.model.traj.phases.climb.interpolate(ys=[0.0, 0.0], nodes='state_input'))
        assert_near_equal(p.get_val('traj.v1_to_vr.parameters:alpha'), 0.0)
Exemplo n.º 22
0
    def test_dup_par_par_derivs(self):
        # duplicated output, parallel input
        prob = om.Problem()
        model = prob.model

        model.add_subsystem('indep', om.IndepVarComp('x', 1.0), promotes=['x'])

        par = model.add_subsystem('par', om.ParallelGroup(), promotes=['x'])
        par.add_subsystem('C1', om.ExecComp('y = 2.5 * x'), promotes=['x'])
        par.add_subsystem('C2', om.ExecComp('y = 7 * x'), promotes=['x'])

        model.add_design_var('x')

        model.add_constraint('par.C1.y',
                             upper=0.0,
                             parallel_deriv_color='parc')
        model.add_constraint('par.C2.y',
                             upper=0.0,
                             parallel_deriv_color='parc')

        prob.model.linear_solver = om.LinearBlockGS()

        prob.setup(check=False, mode='rev')
        prob.set_solver_print(level=0)
        prob.run_model()

        assert_near_equal(prob.get_val('par.C1.y', get_remote=True), 2.5, 1e-6)
        assert_near_equal(prob.get_val('par.C2.y', get_remote=True), 7., 1e-6)

        J = prob.driver._compute_totals()

        assert_near_equal(J['par.C1.y', 'indep.x'][0][0], 2.5, 1e-6)
        assert_near_equal(prob.get_val('par.C1.y', get_remote=True), 2.5, 1e-6)
        assert_near_equal(J['par.C2.y', 'indep.x'][0][0], 7., 1e-6)
        assert_near_equal(prob.get_val('par.C2.y', get_remote=True), 7., 1e-6)
Exemplo n.º 23
0
    def test_feature_vectorized_derivs(self):
        import numpy as np
        import openmdao.api as om

        SIZE = 5

        class ExpensiveAnalysis(om.ExplicitComponent):

            def setup(self):

                self.add_input('x', val=np.ones(SIZE))
                self.add_input('y', val=np.ones(SIZE))

                self.add_output('f', shape=1)

                self.declare_partials('f', 'x')
                self.declare_partials('f', 'y')

            def compute(self, inputs, outputs):

                outputs['f'] = np.sum(inputs['x']**inputs['y'])

            def compute_partials(self, inputs, J):

                J['f', 'x'] = inputs['y']*inputs['x']**(inputs['y']-1)
                J['f', 'y'] = (inputs['x']**inputs['y'])*np.log(inputs['x'])

        class CheapConstraint(om.ExplicitComponent):

            def setup(self):

                self.add_input('y', val=np.ones(SIZE))
                self.add_output('g', shape=SIZE)

                row_col = np.arange(SIZE, dtype=int)
                self.declare_partials('g', 'y', rows=row_col, cols=row_col)

                self.limit = 2*np.arange(SIZE)

            def compute(self, inputs, outputs):

                outputs['g'] = inputs['y']**2 - self.limit

            def compute_partials(self, inputs, J):

                J['g', 'y'] = 2*inputs['y']

        p = om.Problem()

        dvs = p.model.add_subsystem('des_vars', om.IndepVarComp(), promotes=['*'])
        dvs.add_output('x', 2*np.ones(SIZE))
        dvs.add_output('y', 2*np.ones(SIZE))

        p.model.add_subsystem('obj', ExpensiveAnalysis(), promotes=['x', 'y', 'f'])
        p.model.add_subsystem('constraint', CheapConstraint(), promotes=['y', 'g'])

        p.model.add_design_var('x', lower=.1, upper=10000)
        p.model.add_design_var('y', lower=-1000, upper=10000)
        p.model.add_constraint('g', upper=0, vectorize_derivs=True)
        p.model.add_objective('f')

        p.setup(mode='rev')

        p.run_model()

        p.driver = om.ScipyOptimizeDriver()
        p.run_driver()

        assert_near_equal(p['x'], [0.10000691, 0.1, 0.1, 0.1, 0.1], 1e-5)
        assert_near_equal(p['y'], [0, 1.41421, 2.0, 2.44948, 2.82842], 1e-5)
Exemplo n.º 24
0
    def test_dup_par(self, mode, auto):
        # duplicated output, parallel input
        prob = om.Problem()
        model = prob.model

        if not auto:
            model.add_subsystem('indep',
                                om.IndepVarComp('x', 1.0),
                                promotes=['x'])

        par = model.add_subsystem('par', om.ParallelGroup(), promotes=['x'])
        par.add_subsystem('C1', om.ExecComp('y = 2.5 * x'), promotes=['x'])
        par.add_subsystem('C2', om.ExecComp('y = 7 * x'), promotes=['x'])

        wrt = ['x']

        of = ['par.C1.y', 'par.C2.y']

        prob.model.linear_solver = om.LinearRunOnce()

        prob.setup(check=False, mode=mode)
        prob.set_solver_print(level=0)
        prob.run_model()

        assert_near_equal(prob.get_val('par.C1.y', get_remote=True), 2.5, 1e-6)
        assert_near_equal(prob.get_val('par.C2.y', get_remote=True), 7., 1e-6)

        J = prob.compute_totals(of=of, wrt=wrt)

        assert_near_equal(J['par.C1.y', 'x'][0][0], 2.5, 1e-6)
        assert_near_equal(J['par.C2.y', 'x'][0][0], 7., 1e-6)

        assert_near_equal(prob.get_val('par.C1.y', get_remote=True), 2.5, 1e-6)
        assert_near_equal(prob.get_val('par.C2.y', get_remote=True), 7., 1e-6)
Exemplo n.º 25
0
    def test_case1(self):

        self.prob = Problem()
        cycle = self.prob.model = Cycle()
        cycle.options['thermo_method'] = 'CEA'
        cycle.options['thermo_data'] = janaf

        cycle.add_subsystem('flow_start', FlowStart())
        cycle.add_subsystem('nozzle', Nozzle(lossCoef='Cfg', internal_solver=True))

        cycle.set_input_defaults('nozzle.Ps_exhaust', 10.0, units='lbf/inch**2')
        cycle.set_input_defaults('flow_start.MN', 0.0)
        cycle.set_input_defaults('flow_start.T', 500.0, units='degR')
        cycle.set_input_defaults('flow_start.P', 17.0, units='psi')
        cycle.set_input_defaults('flow_start.W', 100.0, units='lbm/s')

        cycle.pyc_connect_flow("flow_start.Fl_O", "nozzle.Fl_I")

        self.prob.setup(check=False, force_alloc_complex=True)

        # 4 cases to check against
        for i, data in enumerate(ref_data):

            self.prob['nozzle.Cfg'] = data[h_map['Cfg']]
            self.prob['nozzle.Ps_exhaust'] = data[h_map['PsExh']]
            # input flowstation

            self.prob['flow_start.P'] = data[h_map['Fl_I.Pt']]
            self.prob['flow_start.T'] = data[h_map['Fl_I.Tt']]
            self.prob['flow_start.W'] = data[h_map['Fl_I.W']]
            self.prob['flow_start.MN'] = data[h_map['Fl_I.MN']]

            self.prob.run_model()

            # check outputs
            Fg, V, PR = data[h_map['Fg']], data[
                h_map['Vactual']], data[h_map['PR']]
            MN = data[h_map['Fl_O.MN']]
            Ath = data[h_map['Ath']]
            Pt = data[h_map['Fl_O.Pt']]
            MN_computed = self.prob['nozzle.Fl_O:stat:MN']
            Fg_computed = self.prob['nozzle.Fg']
            V_computed = self.prob['nozzle.Fl_O:stat:V']
            PR_computed = self.prob['nozzle.PR']
            Ath_computed = self.prob['nozzle.Fl_O:stat:area']
            Pt_computed = self.prob['nozzle.Fl_O:tot:P']

            # Used for all
            tol = 5.e-3

            assert_near_equal(MN_computed, MN, tol)

            assert_near_equal(Fg_computed, Fg, tol)
            assert_near_equal(V_computed, V, tol)
            assert_near_equal(Pt_computed, Pt, tol)

            assert_near_equal(PR_computed, PR, tol)
            assert_near_equal(Ath_computed, Ath, tol)

            partial_data = self.prob.check_partials(out_stream=None, method='cs', 
                                                    includes=['nozzle.*'], excludes=['*.base_thermo.*',])
            assert_check_partials(partial_data, atol=1e-8, rtol=1e-8)
Exemplo n.º 26
0
    def test_continuity_comp(self):
        for transcription, compressed in params_list:
            with self.subTest():

                dm.options['include_check_partials'] = True
                num_seg = 3

                gd = GridData(num_segments=num_seg,
                              transcription_order=[5, 3, 3],
                              segment_ends=[0.0, 3.0, 10.0, 20],
                              transcription=transcription,
                              compressed=compressed == 'compressed')

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

                ivp = self.p.model.add_subsystem('ivc',
                                                 subsys=om.IndepVarComp(),
                                                 promotes_outputs=['*'])

                nn = gd.subset_num_nodes['all']

                ivp.add_output('x', val=np.arange(nn), units='m')
                ivp.add_output('y', val=np.arange(nn), units='m/s')
                ivp.add_output('u', val=np.zeros((nn, 3)), units='deg')
                ivp.add_output('v', val=np.arange(nn), units='N')
                ivp.add_output('u_rate', val=np.zeros((nn, 3)), units='deg/s')
                ivp.add_output('v_rate', val=np.arange(nn), units='N/s')
                ivp.add_output('u_rate2',
                               val=np.zeros((nn, 3)),
                               units='deg/s**2')
                ivp.add_output('v_rate2', val=np.arange(nn), units='N/s**2')
                ivp.add_output('t_duration', val=121.0, units='s')

                self.p.model.add_design_var('x', lower=0, upper=100)

                state_options = {
                    'x': StateOptionsDictionary(),
                    'y': StateOptionsDictionary()
                }
                control_options = {
                    'u': ControlOptionsDictionary(),
                    'v': ControlOptionsDictionary()
                }

                state_options['x']['units'] = 'm'
                state_options['y']['units'] = 'm/s'

                # Need these for later in the test.
                state_options['x']['shape'] = (1, )
                state_options['y']['shape'] = (1, )

                control_options['u']['units'] = 'deg'
                control_options['u']['shape'] = (3, )
                control_options['u']['continuity'] = True

                control_options['v']['units'] = 'N'

                # Need these for later in the test.
                state_options['x']['shape'] = (1, )
                state_options['y']['shape'] = (1, )
                control_options['v']['shape'] = (1, )

                control_options['u']['rate_continuity'] = True
                control_options['v']['rate_continuity'] = True

                control_options['u']['rate2_continuity'] = True
                control_options['v']['rate2_continuity'] = True

                if transcription == 'gauss-lobatto':
                    cnty_comp = GaussLobattoContinuityComp(
                        grid_data=gd,
                        time_units='s',
                        state_options=state_options,
                        control_options=control_options)
                elif transcription == 'radau-ps':
                    cnty_comp = RadauPSContinuityComp(
                        grid_data=gd,
                        time_units='s',
                        state_options=state_options,
                        control_options=control_options)
                else:
                    raise ValueError('unrecognized transcription')

                self.p.model.add_subsystem('cnty_comp', subsys=cnty_comp)
                # The sub-indices of state_disc indices that are segment ends
                num_seg_ends = gd.subset_num_nodes['segment_ends']
                segment_end_idxs = np.reshape(
                    gd.subset_node_indices['segment_ends'],
                    newshape=(num_seg_ends, 1))

                if compressed != 'compressed':
                    self.p.model.connect('x',
                                         'cnty_comp.states:x',
                                         src_indices=(segment_end_idxs, ))
                    self.p.model.connect('y',
                                         'cnty_comp.states:y',
                                         src_indices=(segment_end_idxs, ))

                self.p.model.connect('t_duration', 'cnty_comp.t_duration')

                size_u = nn * np.prod(control_options['u']['shape'])
                src_idxs_u = np.arange(size_u).reshape(
                    (nn, ) + control_options['u']['shape'])
                src_idxs_u = src_idxs_u[gd.subset_node_indices['segment_ends'],
                                        ...]

                size_v = nn * np.prod(control_options['v']['shape'])
                src_idxs_v = np.arange(size_v).reshape(
                    (nn, ) + control_options['v']['shape'])
                src_idxs_v = src_idxs_v[gd.subset_node_indices['segment_ends'],
                                        ...]

                src_idxs_u = (src_idxs_u, )
                src_idxs_v = (src_idxs_v, )

                # if transcription =='radau-ps' or compressed != 'compressed':
                self.p.model.connect('u',
                                     'cnty_comp.controls:u',
                                     src_indices=src_idxs_u,
                                     flat_src_indices=True)

                self.p.model.connect('u_rate',
                                     'cnty_comp.control_rates:u_rate',
                                     src_indices=src_idxs_u,
                                     flat_src_indices=True)

                self.p.model.connect('u_rate2',
                                     'cnty_comp.control_rates:u_rate2',
                                     src_indices=src_idxs_u,
                                     flat_src_indices=True)

                # if transcription =='radau-ps' or compressed != 'compressed':
                self.p.model.connect('v',
                                     'cnty_comp.controls:v',
                                     src_indices=src_idxs_v,
                                     flat_src_indices=True)

                self.p.model.connect('v_rate',
                                     'cnty_comp.control_rates:v_rate',
                                     src_indices=src_idxs_v,
                                     flat_src_indices=True)

                self.p.model.connect('v_rate2',
                                     'cnty_comp.control_rates:v_rate2',
                                     src_indices=src_idxs_v,
                                     flat_src_indices=True)

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

                self.p['x'] = np.random.rand(*self.p['x'].shape)
                self.p['y'] = np.random.rand(*self.p['y'].shape)
                self.p['u'] = np.random.rand(*self.p['u'].shape)
                self.p['v'] = np.random.rand(*self.p['v'].shape)
                self.p['u_rate'] = np.random.rand(*self.p['u'].shape)
                self.p['v_rate'] = np.random.rand(*self.p['v'].shape)
                self.p['u_rate2'] = np.random.rand(*self.p['u'].shape)
                self.p['v_rate2'] = np.random.rand(*self.p['v'].shape)

                self.p.run_model()

                if compressed != 'compressed':
                    for state in ('x', 'y'):
                        xpectd = self.p[state][segment_end_idxs, ...][2::2, ...] - \
                            self.p[state][segment_end_idxs, ...][1:-1:2, ...]

                        assert_near_equal(
                            self.p['cnty_comp.defect_states:{0}'.format(
                                state)],
                            xpectd.reshape((num_seg - 1, ) +
                                           state_options[state]['shape']))

                for ctrl in ('u', 'v'):

                    xpectd = self.p[ctrl][segment_end_idxs, ...][2::2, ...] - \
                        self.p[ctrl][segment_end_idxs, ...][1:-1:2, ...]

                    if compressed != 'compressed':
                        assert_near_equal(
                            self.p['cnty_comp.defect_controls:{0}'.format(
                                ctrl)],
                            xpectd.reshape((num_seg - 1, ) +
                                           control_options[ctrl]['shape']))

                np.set_printoptions(linewidth=1024)
                cpd = self.p.check_partials(method='cs', out_stream=None)
                assert_check_partials(cpd)
Exemplo n.º 27
0
 def test_results(self):
     assert_near_equal(self.p['ic.ode_integ.f'], 0.0*np.ones((self.nn,)))
     assert_near_equal(self.p['ic.ode_integ.f2'], np.linspace(0, 5, self.nn))
Exemplo n.º 28
0
                [0.86565585, 0.85350002],
                [0.40806563, 0.91465314]
            ]
        ]
        y = np.array([[branin(case) for case in x[0]],
                      [branin_low_fidelity(case) for case in x[1]]])

        mm.options['train:x'] = x[0]
        mm.options['train:y'] = y[0]
        mm.options['train:x_fi2'] = x[1]
        mm.options['train:y_fi2'] = y[1]

        prob['mm.x'] = np.array([[2. / 3., 1. / 3.]])
        prob.run_model()

        assert_near_equal(prob['mm.y'], 26.26, tolerance=0.02)

        prob['mm.x'] = np.array([[1. / 3., 2. / 3.]])
        prob.run_model()

        assert_near_equal(prob['mm.y'], 36.1031735, tolerance=0.02)

        # Now, vectorized model with both points predicted together.

        mm = om.MultiFiMetaModelUnStructuredComp(nfi=2, vec_size=2)
        mm.add_input('x', np.zeros((2, 1, 2)))
        mm.add_output('y', np.zeros((
            2,
            1,
        )))
Exemplo n.º 29
0
    def test_results(self):
        self.p.run_model()
        x = np.linspace(0, 5, self.nn)
        f_exact = -10.2*x**3/3 + 4.2*x**2/2 -10.5*x
        f2_exact = 5.1*x**3/3 +0.5*x**2/2-7.2*x

        # check first phase result
        assert_near_equal(self.p['phase1.a.ode_integ.f'], f_exact)
        assert_near_equal(self.p['phase1.b.ode_integ.f'], f_exact)
        assert_near_equal(self.p['phase1.ode_integ.f2'], f2_exact)

        # check second phase result
        assert_near_equal(self.p['phase2.a.ode_integ.f'], f_exact+f_exact[-1])
        assert_near_equal(self.p['phase2.b.ode_integ.f'], f_exact+f_exact[-1])
        assert_near_equal(self.p['phase2.ode_integ.f2'], f2_exact)

        # check third phase result
        assert_near_equal(self.p['phase3.a.ode_integ.f'], f_exact+2.0*f_exact[-1])
        assert_near_equal(self.p['phase3.b.ode_integ.f'], f_exact+2.0*f_exact[-1])
        assert_near_equal(self.p['phase3.ode_integ.f2'], f2_exact+f2_exact[-1])
    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()