def test_brachistochrone_integrated_control_radau_ps(self):
        import numpy as np
        import openmdao.api as om
        from openmdao.utils.assert_utils import assert_near_equal
        import dymos as dm

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

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

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

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

        phase.add_state('x',
                        fix_initial=True,
                        fix_final=True,
                        rate_source='xdot',
                        units='m')
        phase.add_state('y',
                        fix_initial=True,
                        fix_final=True,
                        rate_source='ydot',
                        units='m')
        phase.add_state('v',
                        fix_initial=True,
                        rate_source='vdot',
                        units='m/s',
                        targets=['v'])
        phase.add_state('theta',
                        targets='theta',
                        fix_initial=False,
                        rate_source='theta_dot',
                        units='rad')

        phase.add_control('theta_dot',
                          units='deg/s',
                          rate_continuity=True,
                          lower=0,
                          upper=60)

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

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

        p.model.linear_solver = om.DirectSolver()
        p.model.options['assembled_jac_type'] = 'csc'

        p.setup()

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

        p['phase0.states:x'] = phase.interpolate(ys=[0, 10],
                                                 nodes='state_input')
        p['phase0.states:y'] = phase.interpolate(ys=[10, 5],
                                                 nodes='state_input')
        p['phase0.states:v'] = phase.interpolate(ys=[0, 9.9],
                                                 nodes='state_input')
        p['phase0.states:theta'] = np.radians(
            phase.interpolate(ys=[0.05, 100.0], nodes='state_input'))
        p['phase0.controls:theta_dot'] = phase.interpolate(
            ys=[50, 50], nodes='control_input')

        # Solve for the optimal trajectory
        p.run_driver()

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

        sim_out = phase.simulate(times_per_seg=20)

        x_sol = p.get_val('phase0.timeseries.states:x')
        y_sol = p.get_val('phase0.timeseries.states:y')
        v_sol = p.get_val('phase0.timeseries.states:v')
        theta_sol = p.get_val('phase0.timeseries.states:theta')
        theta_dot_sol = p.get_val('phase0.timeseries.controls:theta_dot')
        time_sol = p.get_val('phase0.timeseries.time')

        x_sim = sim_out.get_val('phase0.timeseries.states:x')
        y_sim = sim_out.get_val('phase0.timeseries.states:y')
        v_sim = sim_out.get_val('phase0.timeseries.states:v')
        theta_sim = sim_out.get_val('phase0.timeseries.states:theta')
        theta_dot_sim = sim_out.get_val('phase0.timeseries.controls:theta_dot')
        time_sim = sim_out.get_val('phase0.timeseries.time')

        x_interp = interp1d(time_sim[:, 0], x_sim[:, 0])
        y_interp = interp1d(time_sim[:, 0], y_sim[:, 0])
        v_interp = interp1d(time_sim[:, 0], v_sim[:, 0])
        theta_interp = interp1d(time_sim[:, 0], theta_sim[:, 0])
        theta_dot_interp = interp1d(time_sim[:, 0], theta_dot_sim[:, 0])

        assert_near_equal(x_interp(time_sol), x_sol, tolerance=1.0E-5)
        assert_near_equal(y_interp(time_sol), y_sol, tolerance=1.0E-5)
        assert_near_equal(v_interp(time_sol), v_sol, tolerance=1.0E-5)
        assert_near_equal(theta_interp(time_sol), theta_sol, tolerance=1.0E-5)
        assert_near_equal(theta_dot_interp(time_sol),
                          theta_dot_sol,
                          tolerance=1.0E-5)
Esempio n. 2
0
    def test_cp_rev_mode(self):
        '''
        -----------------------------------------------------
        The erroneous output contained these values:

            Raw Forward Derivative (Jfor)
        [[62.5 62.5 62.5]]

            Raw Reverse Derivative (Jrev)
        [[31.25 31.25 31.25]]

            Raw FD Derivative (Jfd)
        [[62.49998444 62.49998444 62.49998444]]
        ...
            Raw Forward Derivative (Jfor)
        [[62.5 62.5 62.5 62.5]]

            Raw Reverse Derivative (Jrev)
        [[31.25 31.25 31.25 31.25]]

            Raw FD Derivative (Jfd)
        [[62.49998444 62.49998444 62.49998444 62.49998444]]
        -----------------------------------------------------
        The corrected output contains these values:

            Raw Forward Derivative (Jfor)
        [[62.5 62.5 62.5]]

            Raw Reverse Derivative (Jrev)
        [[62.5 62.5 62.5]]

            Raw FD Derivative (Jfd)
        [[62.49998444 62.49998444 62.49998444]]
        ...
            Raw Forward Derivative (Jfor)
        [[62.5 62.5 62.5 62.5]]

            Raw Reverse Derivative (Jrev)
        [[62.5 62.5 62.5 62.5]]

            Raw FD Derivative (Jfd)
        [[62.49998444 62.49998444 62.49998444 62.49998444]]
        -----------------------------------------------------
        '''
        size = 7
        comm = MPI.COMM_WORLD
        rank = comm.rank
        sizes, offsets = evenly_distrib_idxs(comm.size, size)

        prob = om.Problem()
        model = prob.model

        # Create a distributed source for the distributed input.
        ivc = om.IndepVarComp()
        ivc.add_output('x_dist', np.zeros(sizes[rank]), distributed=True)
        ivc.add_output('x_nd', val=1)

        model.add_subsystem("indep", ivc)
        model.add_subsystem("D1", MixedDistrib2())
        model.add_subsystem('con_cmp1', om.ExecComp('con1 = y**2'), promotes=['con1', 'y'])

        model.connect('indep.x_dist', 'D1.in_dist')
        model.connect('indep.x_nd', ['D1.in_nd','y'])

        prob.driver = om.ScipyOptimizeDriver()
        prob.driver.options['optimizer'] = 'SLSQP'

        model.add_design_var('indep.x_nd', lower=5, upper=10)
        model.add_constraint('con1', upper=90)

        model.add_objective('D1.out_nd')

        prob.setup(force_alloc_complex=True)

        # Set initial values of distributed variable.
        x_dist_init = np.ones(sizes[rank])
        prob.set_val('indep.x_dist', x_dist_init)

        # Set initial values of non-distributed variable.
        prob.set_val('indep.x_nd', 10)

        prob.run_model()

        stream = StringIO()
        prob.check_partials(out_stream=stream)
        out_str = stream.getvalue()
        msg = "Problem.check_partials() output contains a reverse partial derivative divided by comm size."
        self.assertNotRegex(out_str, ".*31.25 31.25 31.25.*", msg)
def brachistochrone_min_time(transcription='gauss-lobatto',
                             num_segments=8,
                             transcription_order=3,
                             compressed=True,
                             optimizer='SLSQP',
                             dynamic_simul_derivs=True,
                             force_alloc_complex=False,
                             solve_segments=False,
                             run_driver=True):
    p = om.Problem(model=om.Group())

    if optimizer == 'SNOPT':
        p.driver = om.pyOptSparseDriver()
        p.driver.options['optimizer'] = optimizer
        p.driver.opt_settings['Major iterations limit'] = 100
        p.driver.opt_settings['Major feasibility tolerance'] = 1.0E-6
        p.driver.opt_settings['Major optimality tolerance'] = 1.0E-6
        p.driver.opt_settings['iSumm'] = 6
    else:
        p.driver = om.ScipyOptimizeDriver()

    if dynamic_simul_derivs:
        p.driver.declare_coloring()

    if transcription == 'gauss-lobatto':
        transcription = dm.GaussLobatto(num_segments=num_segments,
                                        order=transcription_order,
                                        compressed=compressed)
        fix_final = not solve_segments
    elif transcription == 'radau-ps':
        transcription = dm.Radau(num_segments=num_segments,
                                 order=transcription_order,
                                 compressed=compressed)
        fix_final = not solve_segments

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

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

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

    # can't fix final position if you're solving the segments

    phase.add_state('pos',
                    fix_initial=True,
                    fix_final=fix_final,
                    solve_segments=solve_segments,
                    ref=[1, 1])
    #
    phase.add_state('v',
                    fix_initial=True,
                    fix_final=False,
                    solve_segments=solve_segments)
    #
    phase.add_control('theta',
                      continuity=True,
                      rate_continuity=True,
                      units='deg',
                      lower=0.01,
                      upper=179.9)

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

    if not fix_final:
        phase.add_boundary_constraint('pos', loc='final', equals=[10, 5])

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

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

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

    pos0 = [0, 10]
    posf = [10, 5]

    p['traj0.phase0.states:pos'] = phase.interp('pos', [pos0, posf])
    p['traj0.phase0.states:v'] = phase.interp('v', [0, 9.9])
    p['traj0.phase0.controls:theta'] = phase.interp('theta', [5, 100])
    p['traj0.phase0.parameters:g'] = 9.80665

    p.run_model()
    if run_driver:
        p.run_driver()

    return p
Esempio n. 4
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()

        p.model.set_input_defaults('x', val=2 * np.ones(SIZE))
        p.model.set_input_defaults('y', val=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)
Esempio n. 5
0
    def test_brachistochrone_upstream_control(self):
        import numpy as np
        import openmdao.api as om
        from openmdao.utils.assert_utils import assert_near_equal
        import dymos as dm

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

        from dymos.examples.brachistochrone.brachistochrone_ode import BrachistochroneODE

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        # Run the driver to solve the problem
        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()
Esempio n. 6
0
    def test(self):
        import numpy as np

        from openaerostruct.geometry.utils import generate_mesh
        from openaerostruct.integration.aerostruct_groups import AerostructGeometry, AerostructPoint
        from openaerostruct.utils.constants import grav_constant

        import openmdao.api as om

        # Create a dictionary to store options about the surface
        mesh_dict = {'num_y' : 5,
                     'num_x' : 2,
                     'wing_type' : 'CRM',
                     'symmetry' : True,
                     'num_twist_cp' : 5}

        mesh, twist_cp = generate_mesh(mesh_dict)

        surface = {
                    # Wing definition
                    'name' : 'wing',        # name of the surface
                    'symmetry' : True,     # if true, model one half of wing
                                            # reflected across the plane y = 0
                    'S_ref_type' : 'wetted', # how we compute the wing area,
                                             # can be 'wetted' or 'projected'
                    'fem_model_type' : 'tube',

                    'thickness_cp' : np.array([.1, .2, .3]),

                    'twist_cp' : twist_cp,
                    'mesh' : mesh,

                    # Aerodynamic performance of the lifting surface at
                    # an angle of attack of 0 (alpha=0).
                    # These CL0 and CD0 values are added to the CL and CD
                    # obtained from aerodynamic analysis of the surface to get
                    # the total CL and CD.
                    # These CL0 and CD0 values do not vary wrt alpha.
                    'CL0' : 0.0,            # CL of the surface at alpha=0
                    'CD0' : 0.015,            # CD of the surface at alpha=0

                    # Airfoil properties for viscous drag calculation
                    'k_lam' : 0.05,         # percentage of chord with laminar
                                            # flow, used for viscous drag
                    't_over_c_cp' : np.array([0.15]),      # thickness over chord ratio (NACA0015)
                    'c_max_t' : .303,       # chordwise location of maximum (NACA0015)
                                            # thickness
                    'with_viscous' : True,
                    'with_wave' : False,     # if true, compute wave drag

                    # Structural values are based on aluminum 7075
                    'E' : 70.e9,            # [Pa] Young's modulus of the spar
                    'G' : 30.e9,            # [Pa] shear modulus of the spar
                    'yield' : 500.e6 / 2.5, # [Pa] yield stress divided by 2.5 for limiting case
                    'mrho' : 3.e3,          # [kg/m^3] material density
                    'fem_origin' : 0.35,    # normalized chordwise location of the spar
                    'wing_weight_ratio' : 2.,
                    'struct_weight_relief' : False,    # True to add the weight of the structure to the loads on the structure
                    'distributed_fuel_weight' : False,
                    # Constraints
                    'exact_failure_constraint' : False, # if false, use KS function
                    }

        # Create the problem and assign the model group
        prob = om.Problem()

        # Add problem information as an independent variables component
        indep_var_comp = om.IndepVarComp()
        indep_var_comp.add_output('v', val=248.136, units='m/s')
        indep_var_comp.add_output('alpha', val=5., units='deg')
        indep_var_comp.add_output('Mach_number', val=0.84)
        indep_var_comp.add_output('re', val=1.e6, units='1/m')
        indep_var_comp.add_output('rho', val=0.38, units='kg/m**3')
        indep_var_comp.add_output('CT', val=grav_constant * 17.e-6, units='1/s')
        indep_var_comp.add_output('R', val=11.165e6, units='m')
        indep_var_comp.add_output('W0', val=0.4 * 3e5,  units='kg')
        indep_var_comp.add_output('speed_of_sound', val=295.4, units='m/s')
        indep_var_comp.add_output('load_factor', val=1.)
        indep_var_comp.add_output('empty_cg', val=np.zeros((3)), units='m')

        prob.model.add_subsystem('prob_vars',
             indep_var_comp,
             promotes=['*'])

        aerostruct_group = AerostructGeometry(surface=surface)

        name = 'wing'

        # Add tmp_group to the problem with the name of the surface.
        prob.model.add_subsystem(name, aerostruct_group)

        point_name = 'AS_point_0'

        # Create the aero point group and add it to the model
        AS_point = AerostructPoint(surfaces=[surface])

        prob.model.add_subsystem(point_name, AS_point,
            promotes_inputs=['v', 'alpha', 'Mach_number', 're', 'rho', 'CT', 'R',
                'W0', 'speed_of_sound', 'empty_cg', 'load_factor'])

        com_name = point_name + '.' + name + '_perf'
        prob.model.connect(name + '.local_stiff_transformed', point_name + '.coupled.' + name + '.local_stiff_transformed')
        prob.model.connect(name + '.nodes', point_name + '.coupled.' + name + '.nodes')

        # Connect aerodyamic mesh to coupled group mesh
        prob.model.connect(name + '.mesh', point_name + '.coupled.' + name + '.mesh')

        # Connect performance calculation variables
        prob.model.connect(name + '.radius', com_name + '.radius')
        prob.model.connect(name + '.thickness', com_name + '.thickness')
        prob.model.connect(name + '.nodes', com_name + '.nodes')
        prob.model.connect(name + '.cg_location', point_name + '.' + 'total_perf.' + name + '_cg_location')
        prob.model.connect(name + '.structural_mass', point_name + '.' + 'total_perf.' + name + '_structural_mass')
        prob.model.connect(name + '.t_over_c', com_name + '.t_over_c')

        prob.driver = om.ScipyOptimizeDriver()
        prob.driver.options['tol'] = 1e-9

        recorder = om.SqliteRecorder("aerostruct.db")
        prob.driver.add_recorder(recorder)
        prob.driver.recording_options['record_derivatives'] = True
        prob.driver.recording_options['includes'] = ['*']

        # Setup problem and add design variables, constraint, and objective
        prob.model.add_design_var('wing.twist_cp', lower=-10., upper=15.)
        prob.model.add_design_var('wing.thickness_cp', lower=0.01, upper=0.5, scaler=1e2)
        prob.model.add_constraint('AS_point_0.wing_perf.failure', upper=0.)
        prob.model.add_constraint('AS_point_0.wing_perf.thickness_intersects', upper=0.)

        # Add design variables, constraisnt, and objective on the problem
        prob.model.add_design_var('alpha', lower=-10., upper=10.)
        prob.model.add_constraint('AS_point_0.L_equals_W', equals=0.)
        prob.model.add_objective('AS_point_0.fuelburn', scaler=1e-5)

        # Set up the problem
        prob.setup(check=True)

        prob.run_driver()

        assert_rel_error(self, prob['AS_point_0.fuelburn'][0], 97696.33252514644, 1e-8)
    def test_brachistochrone_integrated_control_radau_ps(self):
        import numpy as np
        import openmdao.api as om
        from openmdao.utils.assert_utils import assert_near_equal
        import dymos as dm

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

        traj = dm.Trajectory()

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

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

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

        phase.add_state('x',
                        fix_initial=True,
                        fix_final=True,
                        rate_source='xdot',
                        units='m')
        phase.add_state('y',
                        fix_initial=True,
                        fix_final=True,
                        rate_source='ydot',
                        units='m')
        phase.add_state('v', fix_initial=True, rate_source='vdot', units='m/s')
        phase.add_state('theta',
                        targets='theta',
                        fix_initial=False,
                        rate_source='theta_dot')

        phase.add_control('theta_dot',
                          units='deg/s',
                          rate_continuity=True,
                          shape=(1, ),
                          lower=0,
                          upper=60)

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

        phase.set_simulate_options(rtol=1.0E-8, atol=1.0E-8)

        # 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['traj.phase0.t_initial'] = 0.0
        p['traj.phase0.t_duration'] = 2.0

        p.set_val('traj.phase0.states:x', phase.interp('x', ys=[0, 10]))
        p.set_val('traj.phase0.states:y', phase.interp('y', ys=[10, 5]))
        p.set_val('traj.phase0.states:v', phase.interp('v', ys=[0, 9.9]))
        p.set_val('traj.phase0.states:theta',
                  phase.interp('theta', ys=[5, 100.5]),
                  units='deg')
        p.set_val('traj.phase0.controls:theta_dot',
                  phase.interp('theta_dot', ys=[50, 50]))

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

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

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

        x_sol = sol_case.get_val('traj.phase0.timeseries.states:x')
        y_sol = sol_case.get_val('traj.phase0.timeseries.states:y')
        v_sol = sol_case.get_val('traj.phase0.timeseries.states:v')
        theta_sol = sol_case.get_val('traj.phase0.timeseries.states:theta')
        theta_dot_sol = sol_case.get_val(
            'traj.phase0.timeseries.controls:theta_dot')
        time_sol = sol_case.get_val('traj.phase0.timeseries.time')

        x_sim = sim_case.get_val('traj.phase0.timeseries.states:x')
        y_sim = sim_case.get_val('traj.phase0.timeseries.states:y')
        v_sim = sim_case.get_val('traj.phase0.timeseries.states:v')
        theta_sim = sim_case.get_val('traj.phase0.timeseries.states:theta')
        theta_dot_sim = sim_case.get_val(
            'traj.phase0.timeseries.controls:theta_dot')
        time_sim = sim_case.get_val('traj.phase0.timeseries.time')

        x_interp = interp1d(time_sim[:, 0], x_sim[:, 0])
        y_interp = interp1d(time_sim[:, 0], y_sim[:, 0])
        v_interp = interp1d(time_sim[:, 0], v_sim[:, 0])
        theta_interp = interp1d(time_sim[:, 0], theta_sim[:, 0])
        theta_dot_interp = interp1d(time_sim[:, 0], theta_dot_sim[:, 0])

        assert_near_equal(x_interp(time_sol), x_sol, tolerance=1.0E-4)
        assert_near_equal(y_interp(time_sol), y_sol, tolerance=1.0E-4)
        assert_near_equal(v_interp(time_sol), v_sol, tolerance=1.0E-4)
        assert_near_equal(theta_interp(time_sol), theta_sol, tolerance=1.0E-4)
        assert_near_equal(theta_dot_interp(time_sol),
                          theta_dot_sol,
                          tolerance=1.0E-4)
Esempio n. 8
0
    def test_parameter_multiple_boundary_constraints(self):

        expected = 'In phase phase0, parameter `g` is subject to multiple boundary ' \
                   'or path constraints.\nParameters are single values that do not change in ' \
                   'time, and may only be used in a single boundary or path constraint.'

        for tx in (dm.GaussLobatto, dm.Radau, dm.ExplicitShooting):
            with self.subTest():
                p = om.Problem(model=om.Group())

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

                phase = dm.Phase(ode_class=BrachistochroneODE,
                                 transcription=tx(num_segments=5, order=3))

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

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

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

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

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

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

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

                # We'll let g vary, but make sure it hits the desired value.
                # It's a static parameter, so it shouldn't matter whether we enforce it
                # at the start or the end of the phase, so here we'll do both.
                # Note if we make these equality constraints, some optimizers (SLSQP) will
                # see the problem as infeasible.
                phase.add_boundary_constraint('x',
                                              loc='final',
                                              units='m',
                                              equals=10)
                phase.add_boundary_constraint('y',
                                              loc='final',
                                              units='m',
                                              equals=5)
                phase.add_boundary_constraint('g',
                                              loc='initial',
                                              units='m/s**2',
                                              upper=9.80665)
                phase.add_boundary_constraint('g',
                                              loc='final',
                                              units='m/s**2',
                                              upper=9.80665)

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

                p.model.linear_solver = om.DirectSolver()

                with self.assertRaises(RuntimeError) as e:
                    p.setup()

                self.assertEqual(str(e.exception), expected)
Esempio n. 9
0
    def test_parameter_path_constraint(self):

        for tx in (dm.GaussLobatto, dm.Radau, dm.ExplicitShooting):
            with self.subTest():
                p = om.Problem(model=om.Group())

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

                phase = dm.Phase(ode_class=BrachistochroneODE,
                                 transcription=tx(num_segments=5, order=3))

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

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

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

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

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

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

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

                # We'll let g vary, but make sure it hits the desired value.
                # It's a static parameter, so it shouldn't matter whether we enforce it
                # at the start or the end of the phase.  In this case we'll use a path
                # constraint to enforce it, but it will still just be a single scalar constraint.
                phase.add_boundary_constraint('x',
                                              loc='final',
                                              units='m',
                                              equals=10)
                phase.add_boundary_constraint('y',
                                              loc='final',
                                              units='m',
                                              equals=5)
                phase.add_path_constraint('g', units='m/s**2', upper=9.80665)

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

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

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

                p['phase0.states:x'] = phase.interp('x', [0, 10])
                p['phase0.states:y'] = phase.interp('y', [10, 5])
                p['phase0.states:v'] = phase.interp('v', [0, 9.9])
                p['phase0.controls:theta'] = phase.interp('theta', [5, 100])
                p['phase0.parameters:g'] = 5

                p.run_driver()

                assert_near_equal(p.get_val('phase0.timeseries.time')[-1],
                                  1.8016,
                                  tolerance=1.0E-4)
                assert_near_equal(
                    p.get_val('phase0.timeseries.parameters:g')[0],
                    9.80665,
                    tolerance=1.0E-6)
                assert_near_equal(
                    p.get_val('phase0.timeseries.parameters:g')[-1],
                    9.80665,
                    tolerance=1.0E-6)
Esempio n. 10
0
    def test_brachistochrone_forward_shooting(self):
        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()
        p.driver.declare_coloring()

        with warnings.catch_warnings(record=True) as w:
            # Cause all warnings to always be triggered.
            warnings.simplefilter("always")
            # Trigger a warning.
            phase = dm.Phase(ode_class=BrachistochroneODE,
                             transcription=dm.RungeKutta(num_segments=20))
            assert issubclass(w[-1].category, DeprecationWarning)
            expected_msg = 'The RungeKutta transcription is deprecated and ' \
                           'will be removed in Dymos v1.0.0.\nFor equivalent behavior, users ' \
                           'should switch to GaussLobatto(order=3, solve_segments=\'forward\') or ' \
                           'GaussLobatto(order=3, solve_segments=\'backward\')'

            self.assertEqual(str(w[-1].message), expected_msg)

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

        phase.set_time_options(initial_bounds=(0, 0), duration_bounds=(0.5, 2.0))

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

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

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

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

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

        # Final state values can't be controlled with simple bounds in ExplicitPhase,
        # so use nonlinear boundary constraints instead.
        phase.add_boundary_constraint('x', loc='final', equals=10)
        phase.add_boundary_constraint('y', loc='final', equals=5)

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

        p.model.linear_solver = om.DirectSolver()

        p.setup(check=True)

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

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

        # Solve for the optimal trajectory
        p.run_driver()

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

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

        assert_near_equal(exp_out.get_val('phase0.timeseries.states:x')[-1, 0], 10,
                          tolerance=1.0E-3)
        assert_near_equal(exp_out.get_val('phase0.timeseries.states:y')[-1, 0], 5,
                          tolerance=1.0E-3)
Esempio n. 11
0
    def test_control_rate2_boundary_constraint_gl(self):
        p = om.Problem(model=om.Group())

        p.driver = om.ScipyOptimizeDriver()

        p.driver.declare_coloring()

        phase = dm.Phase(ode_class=BrachistochroneODE,
                         transcription=dm.GaussLobatto(num_segments=20,
                                                       order=3,
                                                       compressed=True))

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

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

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

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

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

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

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

        phase.add_boundary_constraint('theta_rate2',
                                      loc='final',
                                      equals=0.0,
                                      units='deg/s**2')

        # Minimize time at the end of the phase
        phase.add_objective('time')

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

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

        p['phase0.states:x'] = phase.interp('x', [0, 10])
        p['phase0.states:y'] = phase.interp('y', [10, 5])
        p['phase0.states:v'] = phase.interp('v', [0, 9.9])
        p['phase0.controls:theta'] = phase.interp('theta', [5, 100])
        p['phase0.parameters:g'] = 8

        p.run_driver()

        plt.plot(p.get_val('phase0.timeseries.states:x'),
                 p.get_val('phase0.timeseries.states:y'), 'ko')

        plt.figure()

        plt.plot(p.get_val('phase0.timeseries.time'),
                 p.get_val('phase0.timeseries.controls:theta'), 'ro')

        plt.plot(p.get_val('phase0.timeseries.time'),
                 p.get_val('phase0.timeseries.control_rates:theta_rate'), 'bo')

        plt.plot(p.get_val('phase0.timeseries.time'),
                 p.get_val('phase0.timeseries.control_rates:theta_rate2'),
                 'go')
        plt.show()

        assert_near_equal(
            p.get_val('phase0.timeseries.control_rates:theta_rate2')[-1],
            0,
            tolerance=1.0E-6)
Esempio n. 12
0
    def test_brachistochrone_for_docs_gauss_lobatto(self):
        import openmdao.api as om
        from openmdao.utils.assert_utils import assert_near_equal
        import dymos as dm
        from dymos.examples.plotting import plot_results
        from dymos.examples.brachistochrone import BrachistochroneODE
        import matplotlib.pyplot as plt

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

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

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

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

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

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

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

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

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

        p.model.linear_solver = om.DirectSolver()

        #
        # Setup the Problem
        #
        p.setup()

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

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

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

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

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

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

        plt.show()
Esempio n. 13
0
    def test_control_rate2_path_constraint_gl(self):
        import openmdao.api as om
        from openmdao.utils.assert_utils import assert_rel_error
        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,
                                                       order=5))

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

        phase.set_time_options(initial_bounds=(0, 0), duration_bounds=(.5, 10))

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

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

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

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

        phase.add_path_constraint('theta_rate2',
                                  lower=-200,
                                  upper=200,
                                  units='rad/s**2')

        p.model.linear_solver = om.DirectSolver()
        p.model.options['assembled_jac_type'] = 'csc'

        p.setup()

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

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

        # Solve for the optimal trajectory
        p.run_driver()

        # Test the results
        assert_rel_error(self,
                         p.get_val('phase0.timeseries.time')[-1],
                         1.8016,
                         tolerance=1.0E-3)
Esempio n. 14
0
    def test_timeseries_radau(self, test_smaller_timeseries=False):
        p = om.Problem(model=om.Group())

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

        phase = dm.Phase(ode_class=BrachistochroneODE,
                         transcription=dm.Radau(num_segments=8,
                                                order=3,
                                                compressed=True))

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

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

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

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

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

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

        if test_smaller_timeseries:
            phase.add_design_parameter(
                'g',
                targets=BrachistochroneODE.parameters['g']['targets'],
                opt=True,
                units='m/s**2',
                val=9.80665,
                include_timeseries=False)
        else:
            phase.add_design_parameter(
                'g',
                targets=BrachistochroneODE.parameters['g']['targets'],
                opt=True,
                units='m/s**2',
                val=9.80665)

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

        p.model.options['assembled_jac_type'] = 'csc'
        p.model.linear_solver = om.DirectSolver()
        p.setup(check=True)

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

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

        p.run_driver()

        gd = phase.options['transcription'].grid_data
        state_input_idxs = gd.subset_node_indices['state_input']
        control_input_idxs = gd.subset_node_indices['control_input']

        assert_near_equal(p.get_val('phase0.time'),
                          p.get_val('phase0.timeseries.time')[:, 0])

        assert_near_equal(p.get_val('phase0.time_phase'),
                          p.get_val('phase0.timeseries.time_phase')[:, 0])

        for state in ('x', 'y', 'v'):
            assert_near_equal(
                p.get_val('phase0.states:{0}'.format(state)),
                p.get_val('phase0.timeseries.states:'
                          '{0}'.format(state))[state_input_idxs])

        for control in ('theta', ):
            assert_near_equal(
                p.get_val('phase0.controls:{0}'.format(control)),
                p.get_val('phase0.timeseries.controls:'
                          '{0}'.format(control))[control_input_idxs])

        for dp in ('g', ):
            for i in range(gd.subset_num_nodes['all']):
                if test_smaller_timeseries:
                    with self.assertRaises(KeyError):
                        p.get_val(
                            'phase0.timeseries.design_parameters:{0}'.format(
                                dp))
                else:
                    assert_near_equal(
                        p.get_val(
                            'phase0.design_parameters:{0}'.format(dp))[0, :],
                        p.get_val('phase0.timeseries.design_parameters:'
                                  '{0}'.format(dp))[i])

        # call simulate to test SolveIVP transcription
        exp_out = phase.simulate()
        if test_smaller_timeseries:
            with self.assertRaises(KeyError):
                exp_out.get_val(
                    'phase0.timeseries.design_parameters:{0}'.format(dp))
        else:  # no error accessing timseries.design_parameter
            exp_out.get_val(
                'phase0.timeseries.design_parameters:{0}'.format(dp))

        # Test that the state rates are output in both the radau and solveivp timeseries outputs
        t_sol = p.get_val('phase0.timeseries.time')
        t_sim = exp_out.get_val('phase0.timeseries.time')

        for state_name in ('x', 'y', 'v'):
            rate_sol = p.get_val(f'phase0.timeseries.state_rates:{state_name}')
            rate_sim = exp_out.get_val(
                f'phase0.timeseries.state_rates:{state_name}')

            rate_t_sim = interp1d(t_sim.ravel(), rate_sim.ravel())

            assert_near_equal(rate_t_sim(t_sol), rate_sol, tolerance=1.0E-3)
    def test(self):
        # Create a dictionary to store options about the surface
        # OM: vary 'num_y' and 'num_x' to change the size of the mesh
        mesh_dict = {
            'num_y': 5,
            'num_x': 2,
            'wing_type': 'rect',
            'symmetry': True
        }

        mesh = generate_mesh(mesh_dict)

        surf_dict = {
            # Wing definition
            'name': 'wing',  # name of the surface
            'symmetry': True,  # if true, model one half of wing
            # reflected across the plane y = 0
            'S_ref_type': 'wetted',  # how we compute the wing area,
            # can be 'wetted' or 'projected'
            'fem_model_type': 'tube',
            'thickness_cp': np.ones((2)) * .1,
            'twist_cp': np.ones((2)),
            'mesh': mesh,

            # Aerodynamic performance of the lifting surface at
            # an angle of attack of 0 (alpha=0).
            # These CL0 and CD0 values are added to the CL and CD
            # obtained from aerodynamic analysis of the surface to get
            # the total CL and CD.
            # These CL0 and CD0 values do not vary wrt alpha.
            'CL0': 0.0,  # CL of the surface at alpha=0
            'CD0': 0.015,  # CD of the surface at alpha=0

            # Airfoil properties for viscous drag calculation
            'k_lam': 0.05,  # percentage of chord with laminar
            # flow, used for viscous drag
            't_over_c_cp':
            np.array([0.15]),  # thickness over chord ratio (NACA0015)
            'c_max_t': .303,  # chordwise location of maximum (NACA0015)
            # thickness
            'with_viscous': True,
            'with_wave': False,  # if true, compute wave drag

            # Structural values are based on aluminum 7075
            'E': 70.e9,  # [Pa] Young's modulus of the spar
            'G': 30.e9,  # [Pa] shear modulus of the spar
            'yield':
            500.e6 / 2.5,  # [Pa] yield stress divided by 2.5 for limiting case
            'mrho': 3.e3,  # [kg/m^3] material density
            'fem_origin': 0.35,  # normalized chordwise location of the spar
            'wing_weight_ratio': 2.,
            'struct_weight_relief':
            False,  # True to add the weight of the structure to the loads on the structure
            'distributed_fuel_weight': False,
            # Constraints
            'exact_failure_constraint': False,  # if false, use KS function
        }

        surfaces = [surf_dict]

        # Create the problem and assign the model group
        prob = om.Problem()

        # Add problem information as an independent variables component
        indep_var_comp = om.IndepVarComp()
        indep_var_comp.add_output('v', val=248.136, units='m/s')
        indep_var_comp.add_output('alpha', val=9., units='deg')
        indep_var_comp.add_output('Mach_number', val=0.84)
        indep_var_comp.add_output('re', val=1.e6, units='1/m')
        indep_var_comp.add_output('rho', val=0.38, units='kg/m**3')
        indep_var_comp.add_output('CT',
                                  val=grav_constant * 17.e-6,
                                  units='1/s')
        indep_var_comp.add_output('R', val=11.165e6, units='m')
        indep_var_comp.add_output('W0', val=0.4 * 3e5, units='kg')
        indep_var_comp.add_output('speed_of_sound', val=295.4, units='m/s')
        indep_var_comp.add_output('load_factor', val=1.)
        indep_var_comp.add_output('empty_cg', val=np.zeros((3)), units='m')

        prob.model.add_subsystem('prob_vars', indep_var_comp, promotes=['*'])

        # Loop over each surface in the surfaces list
        for surface in surfaces:

            # Get the surface name and create a group to contain components
            # only for this surface
            name = surface['name']

            aerostruct_group = AerostructGeometry(surface=surface)

            # Add tmp_group to the problem with the name of the surface.
            prob.model.add_subsystem(name, aerostruct_group)

        # Loop through and add a certain number of aero points
        for i in range(1):

            point_name = 'AS_point_{}'.format(i)
            # Connect the parameters within the model for each aero point

            # Create the aero point group and add it to the model
            AS_point = AerostructPoint(surfaces=surfaces)

            prob.model.add_subsystem(point_name, AS_point)

            # Connect flow properties to the analysis point
            prob.model.connect('v', point_name + '.v')
            prob.model.connect('alpha', point_name + '.alpha')
            prob.model.connect('Mach_number', point_name + '.Mach_number')
            prob.model.connect('re', point_name + '.re')
            prob.model.connect('rho', point_name + '.rho')
            prob.model.connect('CT', point_name + '.CT')
            prob.model.connect('R', point_name + '.R')
            prob.model.connect('W0', point_name + '.W0')
            prob.model.connect('speed_of_sound',
                               point_name + '.speed_of_sound')
            prob.model.connect('empty_cg', point_name + '.empty_cg')
            prob.model.connect('load_factor', point_name + '.load_factor')

            for surface in surfaces:

                com_name = point_name + '.' + name + '_perf'
                prob.model.connect(
                    name + '.local_stiff_transformed', point_name +
                    '.coupled.' + name + '.local_stiff_transformed')
                prob.model.connect(name + '.nodes',
                                   point_name + '.coupled.' + name + '.nodes')

                # Connect aerodyamic mesh to coupled group mesh
                prob.model.connect(name + '.mesh',
                                   point_name + '.coupled.' + name + '.mesh')

                # Connect performance calculation variables
                prob.model.connect(name + '.radius', com_name + '.radius')
                prob.model.connect(name + '.thickness',
                                   com_name + '.thickness')
                prob.model.connect(name + '.nodes', com_name + '.nodes')
                prob.model.connect(
                    name + '.cg_location',
                    point_name + '.' + 'total_perf.' + name + '_cg_location')
                prob.model.connect(
                    name + '.structural_mass', point_name + '.' +
                    'total_perf.' + name + '_structural_mass')
                prob.model.connect(name + '.t_over_c', com_name + '.t_over_c')

        prob.driver = om.ScipyOptimizeDriver()
        prob.driver.options['tol'] = 1e-9

        # Setup problem and add design variables, constraint, and objective
        prob.model.add_design_var('wing.twist_cp', lower=-10., upper=15.)
        prob.model.add_design_var('wing.thickness_cp',
                                  lower=0.01,
                                  upper=0.5,
                                  scaler=1e2)
        prob.model.add_constraint('AS_point_0.wing_perf.failure', upper=0.)
        prob.model.add_constraint('AS_point_0.wing_perf.thickness_intersects',
                                  upper=0.)

        # Add design variables, constraisnt, and objective on the problem
        prob.model.add_design_var('alpha', lower=-10., upper=10.)
        prob.model.add_constraint('AS_point_0.L_equals_W', equals=0.)
        prob.model.add_objective('AS_point_0.fuelburn', scaler=1e-5)

        # Set up the problem
        prob.setup()

        prob.run_driver()

        assert_rel_error(self, prob['AS_point_0.fuelburn'][0], 68345.6633812,
                         1e-5)
Esempio n. 16
0
def run_wisdem(fname_wt_input, fname_modeling_options, fname_opt_options):
    # Load all yaml inputs and validate (also fills in defaults)
    wt_initial = WindTurbineOntologyPython(fname_wt_input,
                                           fname_modeling_options,
                                           fname_opt_options)
    wt_init, modeling_options, opt_options = wt_initial.get_input_data()

    # Initialize openmdao problem. If running with multiple processors in MPI, use parallel finite differencing equal to the number of cores used.
    # Otherwise, initialize the WindPark system normally. Get the rank number for parallelization. We only print output files using the root processor.
    blade_opt_options = opt_options['optimization_variables']['blade']
    tower_opt_options = opt_options['optimization_variables']['tower']
    control_opt_options = opt_options['optimization_variables']['control']
    if MPI:
        # Determine the number of design variables
        n_DV = 0
        if blade_opt_options['aero_shape']['twist']['flag']:
            n_DV += blade_opt_options['aero_shape']['twist']['n_opt'] - 2
        if blade_opt_options['aero_shape']['chord']['flag']:
            n_DV += blade_opt_options['aero_shape']['chord']['n_opt'] - 3
        if blade_opt_options['aero_shape']['af_positions']['flag']:
            n_DV += modeling_options['blade']['n_af_span'] - blade_opt_options[
                'aero_shape']['af_positions']['af_start'] - 1
        if blade_opt_options['structure']['spar_cap_ss']['flag']:
            n_DV += blade_opt_options['structure']['spar_cap_ss']['n_opt'] - 2
        if blade_opt_options['structure']['spar_cap_ps'][
                'flag'] and not blade_opt_options['structure']['spar_cap_ps'][
                    'equal_to_suction']:
            n_DV += blade_opt_options['structure']['spar_cap_ps']['n_opt'] - 2
        if opt_options['optimization_variables']['control']['tsr']['flag']:
            n_DV += 1
        if opt_options['optimization_variables']['control']['servo'][
                'pitch_control']['flag']:
            n_DV += 2
        if opt_options['optimization_variables']['control']['servo'][
                'torque_control']['flag']:
            n_DV += 2
        if opt_options['optimization_variables']['control']['servo'][
                'flap_control']['flag']:
            n_DV += 2
        if 'dac' in blade_opt_options:
            if blade_opt_options['dac']['te_flap_end']['flag']:
                n_DV += modeling_options['blade']['n_te_flaps']
            if blade_opt_options['dac']['te_flap_ext']['flag']:
                n_DV += modeling_options['blade']['n_te_flaps']
        if tower_opt_options['outer_diameter']['flag']:
            n_DV += modeling_options['tower']['n_height']
        if tower_opt_options['layer_thickness']['flag']:
            n_DV += (modeling_options['tower']['n_height'] -
                     1) * modeling_options['tower']['n_layers']

        if opt_options['driver']['form'] == 'central':
            n_DV *= 2

        # Extract the number of cores available
        max_cores = MPI.COMM_WORLD.Get_size()

        if max_cores / 2. != np.round(max_cores / 2.):
            exit(
                'ERROR: the parallelization logic only works for an even number of cores available'
            )

        # Define the color map for the parallelization, determining the maximum number of parallel finite difference (FD) evaluations based on the number of design variables (DV). OpenFAST on/off changes things.
        if modeling_options['Analysis_Flags']['OpenFAST']:
            # If openfast is called, the maximum number of FD is the number of DV, if we have the number of cores available that doubles the number of DVs, otherwise it is half of the number of DV (rounded to the lower integer). We need this because a top layer of cores calls a bottom set of cores where OpenFAST runs.
            if max_cores > 2. * n_DV:
                n_FD = n_DV
            else:
                n_FD = int(np.floor(max_cores / 2))
            # The number of OpenFAST runs is the minimum between the actual number of requested OpenFAST simulations, and the number of cores available (minus the number of DV, which sit and wait for OF to complete)

            # need to calculate the number of OpenFAST runs from the user input
            n_OF_runs = 0
            if modeling_options['openfast']['dlc_settings']['run_power_curve']:
                if modeling_options['openfast']['dlc_settings']['Power_Curve'][
                        'turbulent_power_curve']:
                    n_OF_runs += len(
                        modeling_options['openfast']['dlc_settings']
                        ['Power_Curve']['U']) * len(
                            modeling_options['openfast']['dlc_settings']
                            ['Power_Curve']['Seeds'])
                else:
                    n_OF_runs += len(modeling_options['openfast']
                                     ['dlc_settings']['Power_Curve']['U'])
            if modeling_options['openfast']['dlc_settings'][
                    'run_IEC'] or modeling_options['openfast']['dlc_settings'][
                        'run_blade_fatigue']:
                for dlc in modeling_options['openfast']['dlc_settings']['IEC']:
                    dlc_vars = list(dlc.keys())
                    # Number of wind speeds
                    if 'U' not in dlc_vars:
                        if dlc['DLC'] == 1.4:  # assuming 1.4 is run at [V_rated-2, V_rated, V_rated] and +/- direction change
                            n_U = 6
                        elif dlc[
                                'DLC'] == 5.1:  # assuming 1.4 is run at [V_rated-2, V_rated, V_rated]
                            n_U = 3
                        elif dlc['DLC'] in [
                                6.1, 6.3
                        ]:  # assuming V_50 for [-8, 8] deg yaw error
                            n_U = 2
                        else:
                            print(
                                'Warning: for OpenFAST DLC %1.1f specified in the Analysis Options, wind speeds "U" must be provided'
                                % dlc['DLC'])
                    else:
                        n_U = len(dlc['U'])
                    # Number of seeds
                    if 'Seeds' not in dlc_vars:
                        if dlc['DLC'] == 1.4:  # not turbulent
                            n_Seeds = 1
                        else:
                            print(
                                'Warning: for OpenFAST DLC %1.1f specified in the Analysis Options, turbulent seeds "Seeds" must be provided'
                                % dlc['DLC'])
                    else:
                        n_Seeds = len(dlc['Seeds'])

                    n_OF_runs += n_U * n_Seeds

            n_DV = max([n_DV, 1])
            max_parallel_OF_runs = max(
                [int(np.floor((max_cores - n_DV) / n_DV)), 1])
            n_OF_runs_parallel = min([int(n_OF_runs), max_parallel_OF_runs])

            modeling_options['openfast']['dlc_settings'][
                'n_OF_runs'] = n_OF_runs
        else:
            # If OpenFAST is not called, the number of parallel calls to compute the FDs is just equal to the minimum of cores available and DV
            n_FD = min([max_cores, n_DV])
            n_OF_runs_parallel = 1

        # Define the color map for the cores (how these are distributed between finite differencing and openfast runs)
        n_FD = max([n_FD, 1])
        comm_map_down, comm_map_up, color_map = map_comm_heirarchical(
            n_FD, n_OF_runs_parallel)
        rank = MPI.COMM_WORLD.Get_rank()
        color_i = color_map[rank]
        comm_i = MPI.COMM_WORLD.Split(color_i, 1)
    else:
        color_i = 0
        rank = 0

    folder_output = opt_options['general']['folder_output']
    if rank == 0 and not os.path.isdir(folder_output):
        os.mkdir(folder_output)

    if color_i == 0:  # the top layer of cores enters, the others sit and wait to run openfast simulations
        if MPI:
            if modeling_options['Analysis_Flags']['OpenFAST']:
                # Parallel settings for OpenFAST
                modeling_options['openfast']['analysis_settings'][
                    'mpi_run'] = True
                modeling_options['openfast']['analysis_settings'][
                    'mpi_comm_map_down'] = comm_map_down
                modeling_options['openfast']['analysis_settings'][
                    'cores'] = n_OF_runs_parallel
            # Parallel settings for OpenMDAO
            wt_opt = om.Problem(model=om.Group(num_par_fd=n_FD), comm=comm_i)
            wt_opt.model.add_subsystem('comp',
                                       WindPark(
                                           modeling_options=modeling_options,
                                           opt_options=opt_options),
                                       promotes=['*'])
        else:
            # Sequential finite differencing and openfast simulations
            modeling_options['openfast']['analysis_settings']['cores'] = 1
            wt_opt = om.Problem(model=WindPark(
                modeling_options=modeling_options, opt_options=opt_options))

        # If at least one of the design variables is active, setup an optimization
        if opt_options['opt_flag']:
            # If a step size for the driver-level finite differencing is provided, use that step size. Otherwise use a default value.
            if 'step_size' in opt_options['driver']:
                step_size = opt_options['driver']['step_size']
            else:
                step_size = 1.e-6

            # Solver has specific meaning in OpenMDAO
            wt_opt.model.approx_totals(method='fd',
                                       step=step_size,
                                       form=opt_options['driver']['form'])

            # Set optimization solver and options. First, Scipy's SLSQP
            if opt_options['driver']['solver'] == 'SLSQP':
                wt_opt.driver = om.ScipyOptimizeDriver()
                wt_opt.driver.options['optimizer'] = opt_options['driver'][
                    'solver']
                wt_opt.driver.options['tol'] = opt_options['driver']['tol']
                wt_opt.driver.options['maxiter'] = opt_options['driver'][
                    'max_iter']

            # The next two optimization methods require pyOptSparse.
            elif opt_options['driver']['solver'] == 'CONMIN':
                try:
                    from openmdao.api import pyOptSparseDriver
                except:
                    exit(
                        'You requested the optimization solver CONMIN, but you have not installed the pyOptSparseDriver. Please do so and rerun.'
                    )
                wt_opt.driver = pyOptSparseDriver()
                wt_opt.driver.options['optimizer'] = opt_options['driver'][
                    'solver']
                wt_opt.driver.opt_settings['ITMAX'] = opt_options['driver'][
                    'max_iter']

            elif opt_options['driver']['solver'] == 'SNOPT':
                try:
                    from openmdao.api import pyOptSparseDriver
                except:
                    exit(
                        'You requested the optimization solver SNOPT, but you have not installed the pyOptSparseDriver. Please do so and rerun.'
                    )
                wt_opt.driver = pyOptSparseDriver()
                try:
                    wt_opt.driver.options['optimizer'] = opt_options['driver'][
                        'solver']
                except:
                    exit(
                        'You requested the optimization solver SNOPT, but you have not installed it within the pyOptSparseDriver. Please do so and rerun.'
                    )
                wt_opt.driver.opt_settings[
                    'Major optimality tolerance'] = float(
                        opt_options['driver']['tol'])
                wt_opt.driver.opt_settings['Major iterations limit'] = int(
                    opt_options['driver']['max_major_iter'])
                wt_opt.driver.opt_settings['Iterations limit'] = int(
                    opt_options['driver']['max_minor_iter'])
                wt_opt.driver.opt_settings[
                    'Major feasibility tolerance'] = float(
                        opt_options['driver']['tol'])
                wt_opt.driver.opt_settings['Summary file'] = os.path.join(
                    folder_output, 'SNOPT_Summary_file.txt')
                wt_opt.driver.opt_settings['Print file'] = os.path.join(
                    folder_output, 'SNOPT_Print_file.txt')
                if 'hist_file_name' in opt_options['driver']:
                    wt_opt.driver.hist_file = opt_options['driver'][
                        'hist_file_name']
                if 'verify_level' in opt_options['driver']:
                    wt_opt.driver.opt_settings['Verify level'] = opt_options[
                        'driver']['verify_level']
                # wt_opt.driver.declare_coloring()
                if 'hotstart_file' in opt_options['driver']:
                    wt_opt.driver.hotstart_file = opt_options['driver'][
                        'hotstart_file']

            else:
                exit('The optimizer ' + opt_options['driver']['solver'] +
                     'is not yet supported!')

            # Set merit figure. Each objective has its own scaling.
            if opt_options['merit_figure'] == 'AEP':
                wt_opt.model.add_objective('sse.AEP', ref=-1.e6)
            elif opt_options['merit_figure'] == 'blade_mass':
                wt_opt.model.add_objective('elastic.precomp.blade_mass',
                                           ref=1.e4)
            elif opt_options['merit_figure'] == 'LCOE':
                wt_opt.model.add_objective('financese.lcoe', ref=0.1)
            elif opt_options['merit_figure'] == 'blade_tip_deflection':
                wt_opt.model.add_objective('tcons.tip_deflection_ratio')
            elif opt_options['merit_figure'] == 'tower_mass':
                wt_opt.model.add_objective('towerse.tower_mass')
            elif opt_options['merit_figure'] == 'tower_cost':
                wt_opt.model.add_objective('tcc.tower_cost')
            elif opt_options['merit_figure'] == 'Cp':
                if modeling_options['Analysis_Flags']['ServoSE']:
                    wt_opt.model.add_objective('sse.powercurve.Cp_regII',
                                               ref=-1.)
                else:
                    wt_opt.model.add_objective('ccblade.CP', ref=-1.)
            elif opt_options[
                    'merit_figure'] == 'My_std':  # for DAC optimization on root-flap-bending moments
                wt_opt.model.add_objective('aeroelastic.My_std', ref=1.e6)
            elif opt_options[
                    'merit_figure'] == 'DEL_RootMyb':  # for DAC optimization on root-flap-bending moments
                wt_opt.model.add_objective('aeroelastic.DEL_RootMyb', ref=1.e5)
            elif opt_options[
                    'merit_figure'] == 'flp1_std':  # for DAC optimization on flap angles - TORQUE 2020 paper (need to define time constant in ROSCO)
                wt_opt.model.add_objective('aeroelastic.flp1_std')  #1.e-8)
            else:
                exit('The merit figure ' + opt_options['merit_figure'] +
                     ' is not supported.')

            # Set optimization design variables.

            if blade_opt_options['aero_shape']['twist']['flag']:
                indices = range(
                    2, blade_opt_options['aero_shape']['twist']['n_opt'])
                wt_opt.model.add_design_var('blade.opt_var.twist_opt_gain',
                                            indices=indices,
                                            lower=0.,
                                            upper=1.)

            chord_options = blade_opt_options['aero_shape']['chord']
            if chord_options['flag']:
                indices = range(3, chord_options['n_opt'] - 1)
                wt_opt.model.add_design_var('blade.opt_var.chord_opt_gain',
                                            indices=indices,
                                            lower=chord_options['min_gain'],
                                            upper=chord_options['max_gain'])

            if blade_opt_options['aero_shape']['af_positions']['flag']:
                n_af = modeling_options['blade']['n_af_span']
                indices = range(
                    blade_opt_options['aero_shape']['af_positions']
                    ['af_start'], n_af - 1)
                af_pos_init = wt_init['components']['blade'][
                    'outer_shape_bem']['airfoil_position']['grid']
                lb_af = np.zeros(n_af)
                ub_af = np.zeros(n_af)
                for i in range(1, indices[0]):
                    lb_af[i] = ub_af[i] = af_pos_init[i]
                for i in indices:
                    lb_af[i] = 0.5 * (af_pos_init[i - 1] +
                                      af_pos_init[i]) + step_size
                    ub_af[i] = 0.5 * (af_pos_init[i + 1] +
                                      af_pos_init[i]) - step_size
                lb_af[-1] = ub_af[-1] = 1.
                wt_opt.model.add_design_var('blade.opt_var.af_position',
                                            indices=indices,
                                            lower=lb_af[indices],
                                            upper=ub_af[indices])

            spar_cap_ss_options = blade_opt_options['structure']['spar_cap_ss']
            if spar_cap_ss_options['flag']:
                indices = range(1, spar_cap_ss_options['n_opt'] - 1)
                wt_opt.model.add_design_var(
                    'blade.opt_var.spar_cap_ss_opt_gain',
                    indices=indices,
                    lower=spar_cap_ss_options['min_gain'],
                    upper=spar_cap_ss_options['max_gain'])

            # Only add the pressure side design variables if we do set
            # `equal_to_suction` as False in the optimization yaml.
            spar_cap_ps_options = blade_opt_options['structure']['spar_cap_ps']
            if spar_cap_ps_options[
                    'flag'] and not spar_cap_ps_options['equal_to_suction']:
                indices = range(1, spar_cap_ps_options['n_opt'] - 1)
                wt_opt.model.add_design_var(
                    'blade.opt_var.spar_cap_ps_opt_gain',
                    indices=indices,
                    lower=spar_cap_ps_options['min_gain'],
                    upper=spar_cap_ps_options['max_gain'])

            if 'dac' in blade_opt_options:
                if blade_opt_options['dac']['te_flap_end']['flag']:
                    wt_opt.model.add_design_var('blade.opt_var.te_flap_end',
                                                lower=blade_opt_options['dac']
                                                ['te_flap_end']['min_end'],
                                                upper=blade_opt_options['dac']
                                                ['te_flap_end']['max_end'])
                if blade_opt_options['dac']['te_flap_ext']['flag']:
                    wt_opt.model.add_design_var('blade.opt_var.te_flap_ext',
                                                lower=blade_opt_options['dac']
                                                ['te_flap_ext']['min_ext'],
                                                upper=blade_opt_options['dac']
                                                ['te_flap_ext']['max_ext'])

            if tower_opt_options['outer_diameter']['flag']:
                wt_opt.model.add_design_var(
                    'tower.diameter',
                    lower=tower_opt_options['outer_diameter']['lower_bound'],
                    upper=tower_opt_options['outer_diameter']['upper_bound'],
                    ref=5.)

            if tower_opt_options['layer_thickness']['flag']:
                wt_opt.model.add_design_var(
                    'tower.layer_thickness',
                    lower=tower_opt_options['layer_thickness']['lower_bound'],
                    upper=tower_opt_options['layer_thickness']['upper_bound'],
                    ref=1e-2)

            # -- Control --
            if control_opt_options['tsr']['flag']:
                wt_opt.model.add_design_var(
                    'opt_var.tsr_opt_gain',
                    lower=control_opt_options['tsr']['min_gain'],
                    upper=control_opt_options['tsr']['max_gain'])
            if control_opt_options['servo']['pitch_control']['flag']:
                wt_opt.model.add_design_var('control.PC_omega',
                                            lower=control_opt_options['servo']
                                            ['pitch_control']['omega_min'],
                                            upper=control_opt_options['servo']
                                            ['pitch_control']['omega_max'])
                wt_opt.model.add_design_var('control.PC_zeta',
                                            lower=control_opt_options['servo']
                                            ['pitch_control']['zeta_min'],
                                            upper=control_opt_options['servo']
                                            ['pitch_control']['zeta_max'])
            if control_opt_options['servo']['torque_control']['flag']:
                wt_opt.model.add_design_var('control.VS_omega',
                                            lower=control_opt_options['servo']
                                            ['torque_control']['omega_min'],
                                            upper=control_opt_options['servo']
                                            ['torque_control']['omega_max'])
                wt_opt.model.add_design_var('control.VS_zeta',
                                            lower=control_opt_options['servo']
                                            ['torque_control']['zeta_min'],
                                            upper=control_opt_options['servo']
                                            ['torque_control']['zeta_max'])
            if 'flap_control' in control_opt_options['servo']:
                if control_opt_options['servo']['flap_control']['flag']:
                    wt_opt.model.add_design_var(
                        'control.Flp_omega',
                        lower=control_opt_options['servo']['flap_control']
                        ['omega_min'],
                        upper=control_opt_options['servo']['flap_control']
                        ['omega_max'])
                    wt_opt.model.add_design_var(
                        'control.Flp_zeta',
                        lower=control_opt_options['servo']['flap_control']
                        ['zeta_min'],
                        upper=control_opt_options['servo']['flap_control']
                        ['zeta_max'])

            # Set non-linear constraints
            blade_constraints = opt_options['constraints']['blade']
            if blade_constraints['strains_spar_cap_ss']['flag']:
                if blade_opt_options['structure']['spar_cap_ss']['flag']:
                    wt_opt.model.add_constraint(
                        'rlds.constr.constr_max_strainU_spar', upper=1.0)
                else:
                    print(
                        'WARNING: the strains of the suction-side spar cap are set to be constrained, but spar cap thickness is not an active design variable. The constraint is not enforced.'
                    )

            if blade_constraints['strains_spar_cap_ps']['flag']:
                if blade_opt_options['structure']['spar_cap_ps'][
                        'flag'] or blade_opt_options['structure'][
                            'spar_cap_ps']['equal_to_suction']:
                    wt_opt.model.add_constraint(
                        'rlds.constr.constr_max_strainL_spar', upper=1.0)
                else:
                    print(
                        'WARNING: the strains of the pressure-side spar cap are set to be constrained, but spar cap thickness is not an active design variable. The constraint is not enforced.'
                    )

            if blade_constraints['stall']['flag']:
                if blade_opt_options['aero_shape']['twist']['flag']:
                    wt_opt.model.add_constraint(
                        'stall_check.no_stall_constraint', upper=1.0)
                else:
                    print(
                        'WARNING: the margin to stall is set to be constrained, but twist is not an active design variable. The constraint is not enforced.'
                    )

            if blade_constraints['tip_deflection']['flag']:
                if blade_opt_options['structure']['spar_cap_ss'][
                        'flag'] or blade_opt_options['structure'][
                            'spar_cap_ps']['flag']:
                    wt_opt.model.add_constraint('tcons.tip_deflection_ratio',
                                                upper=1.0)
                else:
                    print(
                        'WARNING: the tip deflection is set to be constrained, but spar caps thickness is not an active design variable. The constraint is not enforced.'
                    )

            if blade_constraints['chord']['flag']:
                if blade_opt_options['aero_shape']['chord']['flag']:
                    wt_opt.model.add_constraint('blade.pa.max_chord_constr',
                                                upper=1.0)
                else:
                    print(
                        'WARNING: the max chord is set to be constrained, but chord is not an active design variable. The constraint is not enforced.'
                    )

            if blade_constraints['frequency']['flap_above_3P']:
                if blade_opt_options['structure']['spar_cap_ss'][
                        'flag'] or blade_opt_options['structure'][
                            'spar_cap_ps']['flag']:
                    wt_opt.model.add_constraint(
                        'rlds.constr.constr_flap_f_margin', upper=0.0)
                else:
                    print(
                        'WARNING: the blade flap frequencies are set to be constrained, but spar caps thickness is not an active design variable. The constraint is not enforced.'
                    )

            if blade_constraints['frequency']['edge_above_3P']:
                wt_opt.model.add_constraint('rlds.constr.constr_edge_f_margin',
                                            upper=0.0)

            # if blade_constraints['frequency']['flap_below_3P']:
            #     wt_opt.model.add_constraint('rlds.constr.constr_flap_f_below_3P', upper= 1.0)

            # if blade_constraints['frequency']['edge_below_3P']:
            #     wt_opt.model.add_constraint('rlds.constr.constr_edge_f_below_3P', upper= 1.0)

            # if blade_constraints['frequency']['flap_above_3P'] and blade_constraints['frequency']['flap_below_3P']:
            #     exit('The blade flap frequency is constrained to be both above and below 3P. Please check the constraint flags.')

            # if blade_constraints['frequency']['edge_above_3P'] and blade_constraints['frequency']['edge_below_3P']:
            #     exit('The blade edge frequency is constrained to be both above and below 3P. Please check the constraint flags.')

            if blade_constraints['rail_transport']['flag']:
                if blade_constraints['rail_transport']['8_axle']:
                    wt_opt.model.add_constraint(
                        'elastic.rail.constr_LV_8axle_horiz',
                        lower=0.8,
                        upper=1.0)
                    wt_opt.model.add_constraint('elastic.rail.constr_strainPS',
                                                upper=1.0)
                    wt_opt.model.add_constraint('elastic.rail.constr_strainSS',
                                                upper=1.0)
                elif blade_constraints['rail_transport']['4_axle']:
                    wt_opt.model.add_constraint(
                        'elastic.rail.constr_LV_4axle_horiz', upper=1.0)
                else:
                    exit(
                        'You have activated the rail transport constraint module. Please define whether you want to model 4- or 8-axle flatcars.'
                    )

            if opt_options['constraints']['blade']['moment_coefficient'][
                    'flag']:
                wt_opt.model.add_constraint(
                    'ccblade.CM',
                    lower=opt_options['constraints']['blade']
                    ['moment_coefficient']['min'],
                    upper=opt_options['constraints']['blade']
                    ['moment_coefficient']['max'])
            if opt_options['constraints']['blade']['match_cl_cd'][
                    'flag_cl'] or opt_options['constraints']['blade'][
                        'match_cl_cd']['flag_cd']:
                data_target = np.loadtxt(opt_options['constraints']['blade']
                                         ['match_cl_cd']['filename'])
                eta_opt = np.linspace(
                    0., 1., opt_options['optimization_variables']['blade']
                    ['aero_shape']['twist']['n_opt'])
                target_cl = np.interp(eta_opt, data_target[:, 0],
                                      data_target[:, 3])
                target_cd = np.interp(eta_opt, data_target[:, 0],
                                      data_target[:, 4])
                eps_cl = 1.e-2
                if opt_options['constraints']['blade']['match_cl_cd'][
                        'flag_cl']:
                    wt_opt.model.add_constraint('ccblade.cl_n_opt',
                                                lower=target_cl - eps_cl,
                                                upper=target_cl + eps_cl)
                if opt_options['constraints']['blade']['match_cl_cd'][
                        'flag_cd']:
                    wt_opt.model.add_constraint('ccblade.cd_n_opt',
                                                lower=target_cd - eps_cl,
                                                upper=target_cd + eps_cl)
            if opt_options['constraints']['blade']['match_L_D'][
                    'flag_L'] or opt_options['constraints']['blade'][
                        'match_L_D']['flag_D']:
                data_target = np.loadtxt(opt_options['constraints']['blade']
                                         ['match_L_D']['filename'])
                eta_opt = np.linspace(
                    0., 1., opt_options['optimization_variables']['blade']
                    ['aero_shape']['twist']['n_opt'])
                target_L = np.interp(eta_opt, data_target[:, 0],
                                     data_target[:, 7])
                target_D = np.interp(eta_opt, data_target[:, 0],
                                     data_target[:, 8])
            eps_L = 1.e+2
            if opt_options['constraints']['blade']['match_L_D']['flag_L']:
                wt_opt.model.add_constraint('ccblade.L_n_opt',
                                            lower=target_L - eps_L,
                                            upper=target_L + eps_L)
            if opt_options['constraints']['blade']['match_L_D']['flag_D']:
                wt_opt.model.add_constraint('ccblade.D_n_opt',
                                            lower=target_D - eps_L,
                                            upper=target_D + eps_L)

            tower_constraints = opt_options['constraints']['tower']
            if tower_constraints['height_constraint']['flag']:
                wt_opt.model.add_constraint(
                    'towerse.height_constraint',
                    lower=tower_constraints['height_constraint']
                    ['lower_bound'],
                    upper=tower_constraints['height_constraint']
                    ['upper_bound'])

            if tower_constraints['stress']['flag']:
                wt_opt.model.add_constraint('towerse.post.stress', upper=1.0)

            if tower_constraints['global_buckling']['flag']:
                wt_opt.model.add_constraint('towerse.post.global_buckling',
                                            upper=1.0)

            if tower_constraints['shell_buckling']['flag']:
                wt_opt.model.add_constraint('towerse.post.shell_buckling',
                                            upper=1.0)

            if tower_constraints['constr_d_to_t']['flag']:
                wt_opt.model.add_constraint('towerse.constr_d_to_t', upper=0.0)

            if tower_constraints['constr_taper']['flag']:
                wt_opt.model.add_constraint('towerse.constr_taper', lower=0.0)

            if tower_constraints['slope']['flag']:
                wt_opt.model.add_constraint('towerse.slope', upper=1.0)

            if tower_constraints['frequency_1']['flag']:
                wt_opt.model.add_constraint(
                    'towerse.tower.f1',
                    lower=tower_constraints['frequency_1']['lower_bound'],
                    upper=tower_constraints['frequency_1']['upper_bound'])

            control_constraints = opt_options['constraints']['control']
            if control_constraints['flap_control']['flag']:
                if modeling_options['Analysis_Flags']['OpenFAST'] != True:
                    exit(
                        'Please turn on the call to OpenFAST if you are trying to optimize trailing edge flaps.'
                    )
                wt_opt.model.add_constraint(
                    'sse_tune.tune_rosco.Flp_Kp',
                    lower=control_constraints['flap_control']['min'],
                    upper=control_constraints['flap_control']['max'])
                wt_opt.model.add_constraint(
                    'sse_tune.tune_rosco.Flp_Ki',
                    lower=control_constraints['flap_control']['min'],
                    upper=control_constraints['flap_control']['max'])

            # Set recorder on the OpenMDAO driver level using the `optimization_log`
            # filename supplied in the optimization yaml
            if opt_options['recorder']['flag']:
                recorder = om.SqliteRecorder(
                    os.path.join(folder_output,
                                 opt_options['recorder']['file_name']))
                wt_opt.driver.add_recorder(recorder)
                wt_opt.add_recorder(recorder)

                wt_opt.driver.recording_options['excludes'] = ['*_df']
                wt_opt.driver.recording_options['record_constraints'] = True
                wt_opt.driver.recording_options['record_desvars'] = True
                wt_opt.driver.recording_options['record_objectives'] = True

        # Setup openmdao problem
        wt_opt.setup()

        # Load initial wind turbine data from wt_initial to the openmdao problem
        wt_opt = yaml2openmdao(wt_opt, modeling_options, wt_init)
        wt_opt['blade.opt_var.s_opt_twist'] = np.linspace(
            0., 1., blade_opt_options['aero_shape']['twist']['n_opt'])
        if blade_opt_options['aero_shape']['twist']['flag']:
            init_twist_opt = np.interp(
                wt_opt['blade.opt_var.s_opt_twist'], wt_init['components']
                ['blade']['outer_shape_bem']['twist']['grid'],
                wt_init['components']['blade']['outer_shape_bem']['twist']
                ['values'])
            lb_twist = np.array(
                blade_opt_options['aero_shape']['twist']['lower_bound'])
            ub_twist = np.array(
                blade_opt_options['aero_shape']['twist']['upper_bound'])
            wt_opt['blade.opt_var.twist_opt_gain'] = (
                init_twist_opt - lb_twist) / (ub_twist - lb_twist)
            if max(wt_opt['blade.opt_var.twist_opt_gain']) > 1. or min(
                    wt_opt['blade.opt_var.twist_opt_gain']) < 0.:
                print(
                    'Warning: the initial twist violates the upper or lower bounds of the twist design variables.'
                )

        blade_constraints = opt_options['constraints']['blade']
        wt_opt['blade.opt_var.s_opt_chord'] = np.linspace(
            0., 1., blade_opt_options['aero_shape']['chord']['n_opt'])
        wt_opt['blade.ps.s_opt_spar_cap_ss'] = np.linspace(
            0., 1., blade_opt_options['structure']['spar_cap_ss']['n_opt'])
        wt_opt['blade.ps.s_opt_spar_cap_ps'] = np.linspace(
            0., 1., blade_opt_options['structure']['spar_cap_ps']['n_opt'])
        wt_opt['rlds.constr.max_strainU_spar'] = blade_constraints[
            'strains_spar_cap_ss']['max']
        wt_opt['rlds.constr.max_strainL_spar'] = blade_constraints[
            'strains_spar_cap_ps']['max']
        wt_opt['stall_check.stall_margin'] = blade_constraints['stall'][
            'margin'] * 180. / np.pi

        # Place the last design variables from a previous run into the problem.
        # This needs to occur after the above setup() and yaml2openmdao() calls
        # so these values are correctly placed in the problem.
        if 'warmstart_file' in opt_options['driver']:

            # Directly read the pyoptsparse sqlite db file
            from pyoptsparse import SqliteDict
            db = SqliteDict(opt_options['driver']['warmstart_file'])

            # Grab the last iteration's design variables
            last_key = db['last']
            desvars = db[last_key]['xuser']

            # Obtain the already-setup OM problem's design variables
            if wt_opt.model._static_mode:
                design_vars = wt_opt.model._static_design_vars
            else:
                design_vars = wt_opt.model._design_vars

            # Get the absolute names from the promoted names within the OM model.
            # We need this because the pyoptsparse db has the absolute names for
            # variables but the OM model uses the promoted names.
            prom2abs = wt_opt.model._var_allprocs_prom2abs_list['output']
            abs2prom = {}
            for key in design_vars:
                abs2prom[prom2abs[key][0]] = key

            # Loop through each design variable
            for key in desvars:
                prom_key = abs2prom[key]

                # Scale each DV based on the OM scaling from the problem.
                # This assumes we're running the same problem with the same scaling
                scaler = design_vars[prom_key]['scaler']
                adder = design_vars[prom_key]['adder']

                if scaler is None:
                    scaler = 1.0
                if adder is None:
                    adder = 0.0

                scaled_dv = desvars[key] / scaler - adder

                # Special handling for blade twist as we only have the
                # last few control points as design variables
                if 'twist_opt_gain' in key:
                    wt_opt[key][2:] = scaled_dv
                else:
                    wt_opt[key][:] = scaled_dv

        if 'check_totals' in opt_options['driver']:
            if opt_options['driver']['check_totals']:
                wt_opt.run_model()
                totals = wt_opt.compute_totals()

        if 'check_partials' in opt_options['driver']:
            if opt_options['driver']['check_partials']:
                wt_opt.run_model()
                checks = wt_opt.check_partials(compact_print=True)

        sys.stdout.flush()
        # Run openmdao problem
        if opt_options['opt_flag']:
            wt_opt.run_driver()
        else:
            wt_opt.run_model()

        if (not MPI) or (MPI and rank == 0):
            # Save data coming from openmdao to an output yaml file
            froot_out = os.path.join(folder_output,
                                     opt_options['general']['fname_output'])
            wt_initial.write_ontology(wt_opt, froot_out)

            # Save data to numpy and matlab arrays
            fileIO.save_data(froot_out, wt_opt)

    if MPI and modeling_options['Analysis_Flags']['OpenFAST']:
        # subprocessor ranks spin, waiting for FAST simulations to run
        sys.stdout.flush()
        if rank in comm_map_up.keys():
            subprocessor_loop(comm_map_up)
        sys.stdout.flush()

        # close signal to subprocessors
        if rank == 0:
            subprocessor_stop(comm_map_down)
        sys.stdout.flush()

    if rank == 0:
        return wt_opt, modeling_options, opt_options
    else:
        return [], [], []
Esempio n. 17
0
def setup_problem(
        trans=dm.GaussLobatto(num_segments=10), polynomial_control=False):
    from dymos.examples.brachistochrone.brachistochrone_ode import BrachistochroneODE
    from dymos.transcriptions.runge_kutta.runge_kutta import RungeKutta

    p = om.Problem(model=om.Group())
    if isinstance(trans, RungeKutta):
        p.driver = om.pyOptSparseDriver()
    else:
        p.driver = om.ScipyOptimizeDriver()

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

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

    phase.set_time_options(initial_bounds=(0, 0), duration_bounds=(.5, 10))

    phase.add_state('x',
                    fix_initial=True,
                    fix_final=not isinstance(trans, RungeKutta),
                    rate_source=BrachistochroneODE.states['x']['rate_source'],
                    units=BrachistochroneODE.states['x']['units'])
    phase.add_state('y',
                    fix_initial=True,
                    fix_final=not isinstance(trans, RungeKutta),
                    rate_source=BrachistochroneODE.states['y']['rate_source'],
                    units=BrachistochroneODE.states['y']['units'])
    phase.add_state('v',
                    fix_initial=True,
                    rate_source=BrachistochroneODE.states['v']['rate_source'],
                    units=BrachistochroneODE.states['v']['units'])

    if not polynomial_control:
        phase.add_control('theta',
                          units='deg',
                          rate_continuity=False,
                          lower=0.01,
                          upper=179.9)
    else:
        phase.add_polynomial_control('theta',
                                     order=1,
                                     units='deg',
                                     lower=0.01,
                                     upper=179.9)

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

    if isinstance(trans, RungeKutta):
        phase.add_timeseries_output('check', units='m/s', shape=(1, ))
        phase.add_boundary_constraint('x', loc='final', equals=10)
        phase.add_boundary_constraint('y', loc='final', equals=5)

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

    p.model.linear_solver = om.DirectSolver()

    p.setup()

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

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

    return p
Esempio n. 18
0
    def test_brachistochrone_base_phase_class_gl(self):
        import openmdao.api as om
        from openmdao.utils.assert_utils import assert_rel_error
        import dymos as dm

        class BrachistochronePhase(dm.Phase):
            def setup(self):

                self.options['ode_class'] = BrachistochroneODE
                self.set_time_options(initial_bounds=(0, 0),
                                      duration_bounds=(.5, 10),
                                      units='s')
                self.add_state('x',
                               fix_initial=True,
                               rate_source='xdot',
                               units='m')
                self.add_state('y',
                               fix_initial=True,
                               rate_source='ydot',
                               units='m')
                self.add_state('v',
                               fix_initial=True,
                               rate_source='vdot',
                               targets=['v'],
                               units='m/s')
                self.add_control('theta',
                                 units='deg',
                                 rate_continuity=False,
                                 lower=0.01,
                                 upper=179.9,
                                 targets=['theta'])
                self.add_design_parameter('g',
                                          units='m/s**2',
                                          opt=False,
                                          val=9.80665,
                                          targets=['g'])

                super(BrachistochronePhase, self).setup()

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

        phase = BrachistochronePhase(
            transcription=dm.GaussLobatto(num_segments=20, order=3))
        p.model.add_subsystem('phase0', phase)

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

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

        p.model.linear_solver = om.DirectSolver()

        p.setup()

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

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

        # Solve for the optimal trajectory
        p.run_driver()

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

        exp_out = phase.simulate()

        assert_rel_error(self,
                         exp_out.get_val('phase0.timeseries.states:x')[-1],
                         10,
                         tolerance=1.0E-3)
        assert_rel_error(self,
                         exp_out.get_val('phase0.timeseries.states:y')[-1],
                         5,
                         tolerance=1.0E-3)
Esempio n. 19
0
    def test_brachistochrone_forward_shooting_path_constrained_time(self):
        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.RungeKutta(num_segments=20))

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

        phase.set_time_options(initial_bounds=(0, 0),
                               duration_bounds=(0.5, 2.0))

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

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

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

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

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

        # Final state values can't be controlled with simple bounds in ExplicitPhase,
        # so use nonlinear boundary constraints instead.
        phase.add_boundary_constraint('x', loc='final', equals=10)
        phase.add_boundary_constraint('y', loc='final', equals=5)

        phase.add_path_constraint('time', lower=0.0, upper=2.0)
        phase.add_path_constraint('time_phase', lower=0.0, upper=2.0)

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

        p.model.linear_solver = om.DirectSolver()

        p.setup(check=True)

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

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

        # Solve for the optimal trajectory
        p.run_driver()

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

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

        assert_near_equal(exp_out.get_val('phase0.timeseries.states:x')[-1, 0],
                          10,
                          tolerance=1.0E-3)
        assert_near_equal(exp_out.get_val('phase0.timeseries.states:y')[-1, 0],
                          5,
                          tolerance=1.0E-3)
Esempio n. 20
0
    def test_brachistochrone_undecorated_ode_gl(self):
        import numpy as np
        import matplotlib
        matplotlib.use('Agg')
        import matplotlib.pyplot as plt
        import openmdao.api as om
        from openmdao.utils.assert_utils import assert_rel_error
        import dymos as dm

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

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

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

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

        phase.add_state('x',
                        fix_initial=True,
                        fix_final=True,
                        rate_source='xdot',
                        units='m')
        phase.add_state('y',
                        fix_initial=True,
                        fix_final=True,
                        rate_source='ydot',
                        units='m')
        phase.add_state('v',
                        fix_initial=True,
                        rate_source='vdot',
                        targets=['v'],
                        units='m/s')

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

        phase.add_design_parameter('g',
                                   units='m/s**2',
                                   opt=False,
                                   val=9.80665,
                                   targets=['g'])

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

        p.model.linear_solver = om.DirectSolver()

        p.setup()

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

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

        # Solve for the optimal trajectory
        p.run_driver()

        # Test the results
        assert_rel_error(self,
                         p.get_val('phase0.timeseries.time')[-1],
                         1.8016,
                         tolerance=1.0E-3)
Esempio n. 21
0
    def test_simulate_array_param(self):
        #
        # Initialize the Problem and the optimization driver
        #
        p = om.Problem(model=om.Group())
        p.driver = om.ScipyOptimizeDriver()
        p.driver.declare_coloring()

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

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

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

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

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

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

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

        phase.add_parameter('g', units='m/s**2', val=9.80665)
        phase.add_parameter('array',
                            units=None,
                            shape=(10, ),
                            static_target=True)

        # dummy array of data
        indeps = p.model.add_subsystem('indeps',
                                       om.IndepVarComp(),
                                       promotes=['*'])
        indeps.add_output('array', np.linspace(1, 10, 10), units=None)
        # add dummy array as a parameter and connect it
        p.model.connect('array', 'traj.phase0.parameters:array')

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

        p.model.linear_solver = om.DirectSolver()

        #
        # Setup the Problem
        #
        p.setup()

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

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

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

        # Test the results
        sol_results = om.CaseReader('dymos_solution.db').get_case('final')
        sim_results = om.CaseReader('dymos_solution.db').get_case('final')

        sol = sol_results.get_val('traj.phase0.timeseries.parameters:array')
        sim = sim_results.get_val('traj.phase0.timeseries.parameters:array')

        assert_near_equal(sol - sim, np.zeros_like(sol))

        # Test that the parameter is available in the solution and simulation files
        sol = sol_results.get_val('traj.phase0.parameters:array')
        sim = sim_results.get_val('traj.phase0.parameters:array')

        assert_near_equal(sol - sim, np.zeros_like(sol))
Esempio n. 22
0
    def test_parameter_no_timeseries(self):
        import openmdao.api as om
        import dymos as dm
        from dymos.examples.brachistochrone.brachistochrone_ode import BrachistochroneODE

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

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

        phase = dm.Phase(ode_class=BrachistochroneODE,
                         transcription=dm.GaussLobatto(num_segments=8,
                                                       order=3,
                                                       compressed=True))

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

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

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

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

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

        phase.add_control('theta',
                          continuity=True,
                          rate_continuity=True,
                          opt=True,
                          units='deg',
                          lower=0.01,
                          upper=179.9,
                          ref=1,
                          ref0=0)

        # upgrade_doc: begin parameter_no_timeseries
        phase.add_parameter('g',
                            opt=False,
                            units='m/s**2',
                            include_timeseries=False)
        # upgrade_doc: end parameter_no_timeseries

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

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

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

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

        p.run_driver()

        with self.assertRaises(KeyError):
            p.get_val('phase0.timeseries.parameters:g}')
Esempio n. 23
0
    def _make_problem(self,
                      transcription,
                      num_seg,
                      transcription_order=3,
                      input_initial=False,
                      input_duration=False):
        p = om.Problem(model=om.Group())

        p.driver = om.ScipyOptimizeDriver()

        # Compute sparsity/coloring when run_driver is called
        p.driver.declare_coloring()

        t = {
            'gauss-lobatto':
            dm.GaussLobatto(num_segments=num_seg, order=transcription_order),
            'radau-ps':
            dm.Radau(num_segments=num_seg, order=transcription_order)
        }

        phase = dm.Phase(ode_class=_BrachistochroneTestODE,
                         transcription=t[transcription])

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

        phase.set_time_options(initial_bounds=(1, 1),
                               duration_bounds=(.5, 10),
                               units='s',
                               time_phase_targets=['time_phase'],
                               t_duration_targets=['t_duration'],
                               t_initial_targets=['t_initial'],
                               targets=['time'],
                               input_initial=input_initial,
                               input_duration=input_duration)

        phase.add_state('x', fix_initial=True, rate_source='xdot', units='m')
        phase.add_state('y', fix_initial=True, rate_source='ydot', units='m')
        phase.add_state('v',
                        fix_initial=True,
                        rate_source='vdot',
                        targets=['v'],
                        units='m/s')

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

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

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

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

        p.model.linear_solver = om.DirectSolver()

        if input_duration:
            p.model.add_design_var('phase0.t_duration',
                                   lower=0,
                                   upper=3,
                                   scaler=1.0)

        p.setup(check=True)

        p['phase0.t_initial'] = 0.5
        p['phase0.t_duration'] = 5.0

        p['phase0.states:x'] = phase.interp('x', [0, 10])
        p['phase0.states:y'] = phase.interp('y', [10, 5])
        p['phase0.states:v'] = phase.interp('v', [0, 9.9])
        p['phase0.controls:theta'] = phase.interp('theta', [5, 100.5])

        return p
Esempio n. 24
0
    def test_simplified_ode_timeseries_output(self):
        """
        # upgrade_doc: begin simplified_ode_output_timeseries
        phase.add_timeseries_output('tas_comp.TAS', shape=(1,), units='m/s')
        # upgrade_doc: end simplified_ode_output_timeseries
        """
        import openmdao.api as om
        import dymos as dm
        from dymos.examples.aircraft_steady_flight.aircraft_ode import AircraftODE

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

        transcription = dm.GaussLobatto(num_segments=1,
                                        order=13,
                                        compressed=False)
        phase = dm.Phase(ode_class=AircraftODE, transcription=transcription)
        p.model.add_subsystem('phase0', phase)

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

        phase.set_time_options(initial_bounds=(0, 0),
                               duration_bounds=(3600, 3600),
                               duration_ref=3600)

        phase.set_time_options(initial_bounds=(0, 0),
                               duration_bounds=(3600, 3600),
                               duration_ref=3600)

        phase.add_state('range',
                        units='km',
                        fix_initial=True,
                        fix_final=False,
                        scaler=0.01,
                        rate_source='range_rate_comp.dXdt:range',
                        defect_scaler=0.01)
        phase.add_state('mass_fuel',
                        units='kg',
                        fix_final=True,
                        upper=20000.0,
                        lower=0.0,
                        rate_source='propulsion.dXdt:mass_fuel',
                        scaler=1.0E-4,
                        defect_scaler=1.0E-2)
        phase.add_state('alt',
                        rate_source='climb_rate',
                        units='km',
                        fix_initial=True)

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

        phase.add_control('climb_rate',
                          targets=['gam_comp.climb_rate'],
                          units='m/s',
                          opt=False)

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

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

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

        # upgrade_doc: begin simplified_ode_output_timeseries
        phase.add_timeseries_output('tas_comp.TAS')
        # upgrade_doc: end simplified_ode_output_timeseries

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

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

        p.model.linear_solver = om.DirectSolver()

        p.setup()

        p['phase0.t_initial'] = 0.0
        p['phase0.t_duration'] = 1.515132 * 3600.0
        p['phase0.states:range'] = phase.interpolate(ys=(0, 1296.4),
                                                     nodes='state_input')
        p['phase0.states:mass_fuel'] = phase.interpolate(ys=(12236.594555, 0),
                                                         nodes='state_input')
        p['phase0.states:alt'] = 5.0
        p['phase0.controls:mach'] = 0.8
        p['phase0.controls:climb_rate'] = 0.0

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

        dm.run_problem(p)

        time = p.get_val('phase0.timeseries.time')
        tas = p.get_val('phase0.timeseries.TAS', units='km/s')
        range = p.get_val('phase0.timeseries.states:range')

        assert_near_equal(range, tas * time, tolerance=1.0E-4)

        exp_out = phase.simulate()

        time = exp_out.get_val('phase0.timeseries.time')
        tas = exp_out.get_val('phase0.timeseries.TAS', units='km/s')
        range = exp_out.get_val('phase0.timeseries.states:range')

        assert_near_equal(range, tas * time, tolerance=1.0E-4)
Esempio n. 25
0
# build the model
prob = om.Problem()
prob.model.add_subsystem('parab', Paraboloid(), promotes_inputs=['x', 'y'])

# define the component whose output will be constrained
prob.model.add_subsystem('const',
                         om.ExecComp('g = x + y'),
                         promotes_inputs=['x', 'y'])

# Design variables 'x' and 'y' span components, so we need to provide a common initial value for them
prob.model.set_input_defaults('x', 3.0)
prob.model.set_input_defaults('y', -4.0)

# setup the optimization
prob.driver = om.ScipyOptimizeDriver()
prob.driver.options['optimizer'] = 'COBYLA'

prob.model.add_design_var('x', lower=-50, upper=50)
prob.model.add_design_var('y', lower=-50, upper=50)
prob.model.add_objective('parab.f_xy')

# to add the constraint to the model
prob.model.add_constraint('const.g', lower=0, upper=10.)

prob.setup()
prob.run_driver()

# minimum value
print(prob.get_val('parab.f_xy'))
    def test_brachistochrone_static_gravity(self):
        import openmdao.api as om
        from openmdao.utils.assert_utils import assert_near_equal
        import dymos as dm
        import matplotlib.pyplot as plt
        from dymos.examples.brachistochrone.brachistochrone_ode import BrachistochroneODE

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

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

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

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

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

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

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

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

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

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

        #
        # Setup the Problem
        #
        p.setup()

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

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

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

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

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

        # 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()
Esempio n. 27
0
    def test(self):
        """
        This is an opt problem that tests the wingbox model with wave drag and the fuel vol constraint
        """

        # Create a dictionary to store options about the surface
        mesh_dict = {
            'num_y': 7,
            'num_x': 2,
            'wing_type': 'CRM',
            'symmetry': True,
            'num_twist_cp': 6,
            'chord_cos_spacing': 0,
            'span_cos_spacing': 0,
        }

        mesh, twist_cp = generate_mesh(mesh_dict)

        surf_dict = {
            # Wing definition
            'name':
            'wing',  # name of the surface
            'symmetry':
            True,  # if true, model one half of wing
            # reflected across the plane y = 0
            'S_ref_type':
            'wetted',  # how we compute the wing area,
            # can be 'wetted' or 'projected'
            'fem_model_type':
            'wingbox',
            'spar_thickness_cp':
            np.array([0.004, 0.005, 0.005, 0.008, 0.008, 0.01]),  # [m]
            'skin_thickness_cp':
            np.array([0.005, 0.01, 0.015, 0.020, 0.025, 0.026]),
            'twist_cp':
            np.array([4., 5., 8., 8., 8., 9.]),
            'mesh':
            mesh,
            'data_x_upper':
            upper_x,
            'data_x_lower':
            lower_x,
            'data_y_upper':
            upper_y,
            'data_y_lower':
            lower_y,
            'strength_factor_for_upper_skin':
            1.,

            # Aerodynamic performance of the lifting surface at
            # an angle of attack of 0 (alpha=0).
            # These CL0 and CD0 values are added to the CL and CD
            # obtained from aerodynamic analysis of the surface to get
            # the total CL and CD.
            # These CL0 and CD0 values do not vary wrt alpha.
            'CL0':
            0.0,  # CL of the surface at alpha=0
            'CD0':
            0.0078,  # CD of the surface at alpha=0

            # Airfoil properties for viscous drag calculation
            'k_lam':
            0.05,  # percentage of chord with laminar
            # flow, used for viscous drag
            't_over_c_cp':
            np.array([0.08, 0.08, 0.08, 0.10, 0.10, 0.08]),
            'original_wingbox_airfoil_t_over_c':
            0.12,
            'c_max_t':
            .38,  # chordwise location of maximum thickness
            'with_viscous':
            True,
            'with_wave':
            True,  # if true, compute wave drag

            # Structural values are based on aluminum 7075
            'E':
            73.1e9,  # [Pa] Young's modulus
            'G': (
                73.1e9 / 2 / 1.33
            ),  # [Pa] shear modulus (calculated using E and the Poisson's ratio here)
            'yield': (420.e6 / 1.5),  # [Pa] allowable yield stress
            'mrho':
            2.78e3,  # [kg/m^3] material density
            'strength_factor_for_upper_skin':
            1.0,  # the yield stress is multiplied by this factor for the upper skin
            # 'fem_origin' : 0.35,    # normalized chordwise location of the spar
            'wing_weight_ratio':
            1.25,
            'struct_weight_relief':
            True,
            'distributed_fuel_weight':
            True,
            # Constraints
            'exact_failure_constraint':
            False,  # if false, use KS function
            'fuel_density':
            803.,  # [kg/m^3] fuel density (only needed if the fuel-in-wing volume constraint is used)
            'Wf_reserve':
            15000.,  # [kg] reserve fuel mass
        }

        surfaces = [surf_dict]

        # Create the problem and assign the model group
        prob = om.Problem()

        # Add problem information as an independent variables component
        indep_var_comp = om.IndepVarComp()
        indep_var_comp.add_output('v', val=.85 * 295.07, units='m/s')
        indep_var_comp.add_output('alpha', val=0., units='deg')
        indep_var_comp.add_output('Mach_number', val=0.85)
        indep_var_comp.add_output('re',
                                  val=0.348 * 295.07 * .85 * 1. /
                                  (1.43 * 1e-5),
                                  units='1/m')
        indep_var_comp.add_output('rho', val=0.348, units='kg/m**3')
        indep_var_comp.add_output('CT', val=0.53 / 3600, units='1/s')
        indep_var_comp.add_output('R', val=14.307e6, units='m')
        indep_var_comp.add_output('W0',
                                  val=148000 + surf_dict['Wf_reserve'],
                                  units='kg')
        indep_var_comp.add_output('speed_of_sound', val=295.07, units='m/s')
        indep_var_comp.add_output('load_factor', val=1.)
        indep_var_comp.add_output('empty_cg', val=np.zeros((3)), units='m')
        indep_var_comp.add_output('fuel_mass', val=10000., units='kg')

        prob.model.add_subsystem('prob_vars', indep_var_comp, promotes=['*'])

        # Loop over each surface in the surfaces list
        for surface in surfaces:

            # Get the surface name and create a group to contain components
            # only for this surface
            name = surface['name']

            aerostruct_group = AerostructGeometry(surface=surface)

            # Add tmp_group to the problem with the name of the surface.
            prob.model.add_subsystem(name, aerostruct_group)

        # Loop through and add a certain number of aero points
        for i in range(1):

            point_name = 'AS_point_{}'.format(i)
            # Connect the parameters within the model for each aero point

            # Create the aero point group and add it to the model
            AS_point = AerostructPoint(surfaces=surfaces)

            prob.model.add_subsystem(point_name, AS_point)

            # Connect flow properties to the analysis point
            prob.model.connect('v', point_name + '.v')
            prob.model.connect('alpha', point_name + '.alpha')
            prob.model.connect('Mach_number', point_name + '.Mach_number')
            prob.model.connect('re', point_name + '.re')
            prob.model.connect('rho', point_name + '.rho')
            prob.model.connect('CT', point_name + '.CT')
            prob.model.connect('R', point_name + '.R')
            prob.model.connect('W0', point_name + '.W0')
            prob.model.connect('speed_of_sound',
                               point_name + '.speed_of_sound')
            prob.model.connect('empty_cg', point_name + '.empty_cg')
            prob.model.connect('load_factor', point_name + '.load_factor')

            for surface in surfaces:

                prob.model.connect('load_factor',
                                   point_name + '.coupled.load_factor')

                com_name = point_name + '.' + name + '_perf.'
                prob.model.connect(
                    name + '.local_stiff_transformed', point_name +
                    '.coupled.' + name + '.local_stiff_transformed')
                prob.model.connect(name + '.nodes',
                                   point_name + '.coupled.' + name + '.nodes')

                # Connect aerodyamic mesh to coupled group mesh
                prob.model.connect(name + '.mesh',
                                   point_name + '.coupled.' + name + '.mesh')
                prob.model.connect(
                    name + '.element_mass',
                    point_name + '.coupled.' + name + '.element_mass')

                # Connect performance calculation variables
                prob.model.connect(name + '.nodes', com_name + 'nodes')
                prob.model.connect(
                    name + '.cg_location',
                    point_name + '.' + 'total_perf.' + name + '_cg_location')
                prob.model.connect(
                    name + '.structural_mass', point_name + '.' +
                    'total_perf.' + name + '_structural_mass')

                # Connect wingbox properties to von Mises stress calcs
                prob.model.connect(name + '.Qz', com_name + 'Qz')
                prob.model.connect(name + '.J', com_name + 'J')
                prob.model.connect(name + '.A_enc', com_name + 'A_enc')
                prob.model.connect(name + '.htop', com_name + 'htop')
                prob.model.connect(name + '.hbottom', com_name + 'hbottom')
                prob.model.connect(name + '.hfront', com_name + 'hfront')
                prob.model.connect(name + '.hrear', com_name + 'hrear')

                prob.model.connect(name + '.spar_thickness',
                                   com_name + 'spar_thickness')
                prob.model.connect(name + '.t_over_c', com_name + 't_over_c')

            #=======================================================================================
            # Here we add the fuel volume constraint componenet to the model
            #=======================================================================================
            prob.model.add_subsystem('fuel_vol_delta',
                                     WingboxFuelVolDelta(surface=surface))
            prob.model.connect('AS_point_0.fuelburn',
                               'fuel_vol_delta.fuelburn')
            prob.model.connect('wing.struct_setup.fuel_vols',
                               'fuel_vol_delta.fuel_vols')
            prob.model.connect(
                'wing.struct_setup.fuel_vols',
                'AS_point_0.coupled.wing.struct_states.fuel_vols')
            prob.model.connect(
                'fuel_mass', 'AS_point_0.coupled.wing.struct_states.fuel_mass')

            comp = om.ExecComp('fuel_diff = (fuel_mass - fuelburn) / fuelburn',
                               fuel_mass={
                                   'value': 1.0,
                                   'units': 'kg'
                               },
                               fuelburn={
                                   'value': 1.0,
                                   'units': 'kg'
                               })
            prob.model.add_subsystem('fuel_diff',
                                     comp,
                                     promotes_inputs=['fuel_mass'],
                                     promotes_outputs=['fuel_diff'])
            prob.model.connect('AS_point_0.fuelburn', 'fuel_diff.fuelburn')
            #=======================================================================================
            #=======================================================================================

        prob.driver = om.ScipyOptimizeDriver()
        prob.driver.options['tol'] = 1e-9
        prob.driver.options['maxiter'] = 2

        # prob.driver = om.pyOptSparseDriver()
        # prob.driver.add_recorder(om.SqliteRecorder("cases.sql"))
        # prob.driver.options['optimizer'] = "SNOPT"
        # prob.driver.opt_settings['Major optimality tolerance'] = 1e-6
        # prob.driver.opt_settings['Major feasibility tolerance'] = 1e-8
        # prob.driver.opt_settings['Major iterations limit'] = 200

        prob.model.add_objective('AS_point_0.fuelburn', scaler=1e-5)

        prob.model.add_design_var('wing.twist_cp',
                                  lower=-15.,
                                  upper=15.,
                                  scaler=0.1)
        prob.model.add_design_var('wing.spar_thickness_cp',
                                  lower=0.003,
                                  upper=0.1,
                                  scaler=1e2)
        prob.model.add_design_var('wing.skin_thickness_cp',
                                  lower=0.003,
                                  upper=0.1,
                                  scaler=1e2)
        prob.model.add_design_var('wing.geometry.t_over_c_cp',
                                  lower=0.07,
                                  upper=0.2,
                                  scaler=10.)
        prob.model.add_design_var('fuel_mass',
                                  lower=0.,
                                  upper=2e5,
                                  scaler=1e-5)

        prob.model.add_constraint('AS_point_0.CL', equals=0.5)
        prob.model.add_constraint('AS_point_0.wing_perf.failure', upper=0.)

        #=======================================================================================
        # Here we add the fuel volume constraint
        #=======================================================================================
        prob.model.add_constraint('fuel_vol_delta.fuel_vol_delta', lower=0.)
        prob.model.add_constraint('fuel_diff', equals=0.)
        #=======================================================================================
        #=======================================================================================

        # Set up the problem
        prob.setup()

        # om.view_model(prob)

        prob.run_model()

        # Check the partials at the initial point in the design space,
        # only care about relative error
        data = prob.check_partials(compact_print=True,
                                   out_stream=None,
                                   method='cs',
                                   step=1e-40)

        assert_check_partials(data, atol=1e20, rtol=1e-6)

        # Run the optimizer for 2 iterations
        prob.run_driver()

        # Check the partials at this point in the design space
        data = prob.check_partials(compact_print=True,
                                   out_stream=None,
                                   method='cs',
                                   step=1e-40)
        assert_check_partials(data, atol=1e20, rtol=1e-6)
Esempio n. 28
0
    def test(self):
        import numpy as np

        import openmdao.api as om

        from openaerostruct.geometry.utils import generate_mesh
        from openaerostruct.geometry.geometry_group import Geometry
        from openaerostruct.aerodynamics.aero_groups import AeroPoint
        from openaerostruct.common.atmos_group import AtmosGroup

        # Create a dictionary to store options about the mesh
        mesh_dict = {
            'num_y': 7,
            'num_x': 2,
            'wing_type': 'CRM',
            'symmetry': True,
            'num_twist_cp': 5
        }

        # Generate the aerodynamic mesh based on the previous dictionary
        mesh, twist_cp = generate_mesh(mesh_dict)

        # Create a dictionary with info and options about the aerodynamic
        # lifting surface
        surface = {
            # Wing definition
            'name': 'wing',  # name of the surface
            'symmetry': True,  # if true, model one half of wing
            # reflected across the plane y = 0
            'S_ref_type': 'wetted',  # how we compute the wing area,
            # can be 'wetted' or 'projected'
            'fem_model_type': 'tube',
            'twist_cp': twist_cp,
            'mesh': mesh,
            'num_x': mesh.shape[0],
            'num_y': mesh.shape[1],

            # Aerodynamic performance of the lifting surface at
            # an angle of attack of 0 (alpha=0).
            # These CL0 and CD0 values are added to the CL and CD
            # obtained from aerodynamic analysis of the surface to get
            # the total CL and CD.
            # These CL0 and CD0 values do not vary wrt alpha.
            'CL0': 0.0,  # CL of the surface at alpha=0
            'CD0': 0.015,  # CD of the surface at alpha=0

            # Airfoil properties for viscous drag calculation
            'k_lam': 0.05,  # percentage of chord with laminar
            # flow, used for viscous drag
            't_over_c_cp':
            np.array([0.15]),  # thickness over chord ratio (NACA0015)
            'c_max_t': .303,  # chordwise location of maximum (NACA0015)
            # thickness
            'with_viscous': True,  # if true, compute viscous drag
            'with_wave': False,  # if true, compute wave drag
        }

        # Create the OpenMDAO problem
        prob = om.Problem()

        # Create an independent variable component that will supply the flow
        # conditions to the problem.
        indep_var_comp = om.IndepVarComp()
        indep_var_comp.add_output('alpha', val=5., units='deg')
        indep_var_comp.add_output('Mach_number', val=0.84)
        indep_var_comp.add_output('altitude', val=35000., units='ft')

        # Add this IndepVarComp to the problem model
        prob.model.add_subsystem('prob_vars', indep_var_comp, promotes=['*'])

        # Add this IndepVarComp to the problem model
        prob.model.add_subsystem('atmos', AtmosGroup(), promotes=['*'])

        # Create and add a group that handles the geometry for the
        # aerodynamic lifting surface
        geom_group = Geometry(surface=surface)
        prob.model.add_subsystem(surface['name'], geom_group)

        # Create the aero point group, which contains the actual aerodynamic
        # analyses
        aero_group = AeroPoint(surfaces=[surface])
        point_name = 'aero_point_0'
        prob.model.add_subsystem(
            point_name,
            aero_group,
            promotes_inputs=['v', 'alpha', 'Mach_number', 're', 'rho', 'cg'])

        name = surface['name']

        # Connect the mesh from the geometry component to the analysis point
        prob.model.connect(name + '.mesh',
                           point_name + '.' + name + '.def_mesh')

        # Perform the connections with the modified names within the
        # 'aero_states' group.
        prob.model.connect(name + '.mesh',
                           point_name + '.aero_states.' + name + '_def_mesh')

        prob.model.connect(name + '.t_over_c',
                           point_name + '.' + name + '_perf.' + 't_over_c')

        # Import the Scipy Optimizer and set the driver of the problem to use
        # it, which defaults to an SLSQP optimization method
        prob.driver = om.ScipyOptimizeDriver()
        prob.driver.options['tol'] = 1e-9

        # Setup problem and add design variables, constraint, and objective
        prob.model.add_design_var('wing.twist_cp', lower=-10., upper=15.)
        prob.model.add_constraint(point_name + '.wing_perf.CL', equals=0.5)
        prob.model.add_objective(point_name + '.wing_perf.CD', scaler=1e4)

        # Set up and run the optimization problem
        prob.setup()
        # prob.check_partials(compact_print=True)
        # exit()
        prob.run_driver()

        assert_rel_error(self, prob['aero_point_0.wing_perf.CD'][0],
                         0.030471796067577953, 1e-6)
        assert_rel_error(self, prob['aero_point_0.wing_perf.CL'][0], 0.5, 1e-6)
        assert_rel_error(self, prob['aero_point_0.CM'][1], -1.7331840488188963,
                         1e-6)
Esempio n. 29
0
    def test_ivp_driver_10_segs(self):
        import openmdao.api as om
        import dymos as dm
        import matplotlib.pyplot as plt
        plt.switch_backend('Agg')  # disable plotting to the screen

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

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

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

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

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

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

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

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

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

        # Setup the OpenMDAO problem
        prob.setup()

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

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

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

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

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

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

        states = ['x', 'v']
        fig, axes = plt.subplots(len(states), 1)
        for i, state in enumerate(states):
            sol = axes[i].plot(
                t_sol, prob.get_val(f'traj.phase0.timeseries.states:{state}'),
                'o')
            sim = axes[i].plot(
                t_sim,
                sim_out.get_val(f'traj.phase0.timeseries.states:{state}'), '-')
            axes[i].set_ylabel(state)
        axes[-1].set_xlabel('time (s)')
        fig.legend((sol[0], sim[0]), ('solution', 'simulation'),
                   'lower right',
                   ncol=2)
        plt.tight_layout()
        plt.show()
Esempio n. 30
0
    def test_recording_remote_voi(self):
        # Create a parallel model
        model = om.Group()

        model.add_subsystem('par', om.ParallelGroup())
        model.par.add_subsystem('G1', Mygroup())
        model.par.add_subsystem('G2', Mygroup())
        model.connect('par.G1.y', 'Obj.y1')
        model.connect('par.G2.y', 'Obj.y2')

        model.add_subsystem('Obj', om.ExecComp('obj=y1+y2'))
        model.add_objective('Obj.obj')

        # Configure driver to record VOIs on both procs
        driver = om.ScipyOptimizeDriver(disp=False)

        driver.recording_options['record_desvars'] = True
        driver.recording_options['record_objectives'] = True
        driver.recording_options['record_constraints'] = True
        driver.recording_options['includes'] = ['par.G1.y', 'par.G2.y']

        driver.add_recorder(self.recorder)

        # Create problem and run driver
        prob = om.Problem(model, driver)
        prob.add_recorder(self.recorder)
        prob.setup(mode='fwd')

        t0, t1 = run_driver(prob)
        prob.record('final')
        t2 = time()

        prob.cleanup()

        # Since the test will compare the last case recorded, just check the
        # current values in the problem. This next section is about getting those values

        # These involve collective gathers so all ranks need to run this
        expected_outputs = driver.get_design_var_values(get_remote=True)
        expected_outputs.update(driver.get_objective_values())
        expected_outputs.update(driver.get_constraint_values())

        # includes for outputs are specified as promoted names but we need absolute names
        prom2abs = model._var_allprocs_prom2abs_list['output']
        abs_includes = [
            prom2abs[n][0] for n in prob.driver.recording_options['includes']
        ]

        # Absolute path names of includes on this rank
        rrank = model.comm.rank
        rowned = model._owning_rank
        local_includes = [n for n in abs_includes if rrank == rowned[n]]

        # Get values for all vars on this rank
        inputs, outputs, residuals = model.get_nonlinear_vectors()

        # Get values for includes on this rank
        local_vars = {n: outputs[n] for n in local_includes}

        # Gather values for includes on all ranks
        all_vars = model.comm.gather(local_vars, root=0)

        if prob.comm.rank == 0:
            # Only on rank 0 do we have all the values. The all_vars variable is a list of
            # dicts from all ranks 0,1,... In this case, just ranks 0 and 1
            dct = {}
            for d in all_vars:
                dct.update(d)

            expected_includes = {
                'par.G1.Cy.y': dct['par.G1.Cy.y'],
                'par.G2.Cy.y': dct['par.G2.Cy.y'],
            }

            expected_outputs.update(expected_includes)

            coordinate = [0, 'ScipyOptimize_SLSQP', (driver.iter_count - 1, )]

            expected_data = ((coordinate, (t0, t1), expected_outputs, None,
                              None), )
            assertDriverIterDataRecorded(self, expected_data, self.eps)

            expected_data = (('final', (t1, t2), expected_outputs), )
            assertProblemDataRecorded(self, expected_data, self.eps)