def add_geometry_to_problem(prob, surfaces):

    # 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 group to the problem with the name of the surface.
        prob.model.add_subsystem(name, aerostruct_group)

    return prob
    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': 11,
            '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, 0.5 * 340.], units='m/s')
        indep_var_comp.add_output('alpha', val=[5., 10.], units='deg')
        indep_var_comp.add_output('Mach_number', val=[0.84, 0.5])
        indep_var_comp.add_output('re', val=[1.e6, 0.5e6], units='1/m')
        indep_var_comp.add_output('rho', val=[0.38, .764], 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., 2.5])
        indep_var_comp.add_output('empty_cg', val=np.zeros((3)), units='m')

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

        # Add morphing variables as an independent variables component
        morphing_vars = om.IndepVarComp()
        morphing_vars.add_output('t_over_c_cp', val=np.array([0.15]))
        morphing_vars.add_output('thickness_cp',
                                 val=np.array([0.01, 0.01, 0.01]),
                                 units='m')
        morphing_vars.add_output('twist_cp_0',
                                 val=np.array([2., 3., 4., 4., 4.]),
                                 units='deg')
        morphing_vars.add_output('twist_cp_1',
                                 val=np.array([4., 4., 4., 5., 6.]),
                                 units='deg')

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

        # Connect geometric design variables to each point
        prob.model.connect('t_over_c_cp',
                           'AS_point_0.wing.geometry.t_over_c_cp')
        prob.model.connect('t_over_c_cp',
                           'AS_point_1.wing.geometry.t_over_c_cp')

        prob.model.connect('thickness_cp',
                           'AS_point_0.wing.tube_group.thickness_cp')
        prob.model.connect('thickness_cp',
                           'AS_point_1.wing.tube_group.thickness_cp')

        prob.model.connect('twist_cp_0', 'AS_point_0.wing.geometry.twist_cp')
        prob.model.connect('twist_cp_1', 'AS_point_1.wing.geometry.twist_cp')

        for point in range(2):

            name = 'wing'

            point_name = 'AS_point_{}'.format(point)

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

            prob.model.add_subsystem(point_name, AS_point)

            aerostruct_group = AerostructGeometry(surface=surface,
                                                  connect_geom_DVs=False)
            AS_point.add_subsystem(name, aerostruct_group)

            # Connect flow properties to the analysis point
            prob.model.connect('alpha',
                               point_name + '.alpha',
                               src_indices=[point])
            prob.model.connect('v', point_name + '.v', src_indices=[point])
            prob.model.connect('Mach_number',
                               point_name + '.Mach_number',
                               src_indices=[point])
            prob.model.connect('re', point_name + '.re', src_indices=[point])
            prob.model.connect('rho', point_name + '.rho', src_indices=[point])
            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',
                               src_indices=[point])

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

            # Connect aerodyamic mesh to coupled group mesh
            AS_point.connect(name + '.mesh', 'coupled.' + name + '.mesh')

            # Connect performance calculation variables
            AS_point.connect(name + '.radius', name + '_perf' + '.radius')
            AS_point.connect(name + '.thickness',
                             name + '_perf' + '.thickness')
            AS_point.connect(name + '.nodes', name + '_perf' + '.nodes')
            AS_point.connect(name + '.cg_location',
                             'total_perf.' + name + '_cg_location')
            AS_point.connect(name + '.structural_mass',
                             'total_perf.' + name + '_structural_mass')
            AS_point.connect(name + '.geometry.t_over_c',
                             name + '_perf' + '.t_over_c')
            AS_point.connect(name + '.geometry.t_over_c', name + '.t_over_c')

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

        recorder = om.SqliteRecorder("morphing_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('twist_cp_0', lower=-10., upper=15.)
        prob.model.add_design_var('twist_cp_1', lower=-10., upper=15.)
        prob.model.add_design_var('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.)
        prob.model.add_constraint('AS_point_1.wing_perf.failure', upper=0.)
        prob.model.add_constraint('AS_point_1.wing_perf.thickness_intersects',
                                  upper=0.)

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

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

        # 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)
    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' : 11,
                     '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 = Problem()

        # Add problem information as an independent variables component
        indep_var_comp = 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('alpha_maneuver', 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=np.array([0.348, 0.9237]), 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=np.array([1., 2.5]))
        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(2):

            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, internally_connect_fuelburn=False)

            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('Mach_number', point_name + '.Mach_number')
            prob.model.connect('re', point_name + '.re')
            prob.model.connect('rho', point_name + '.rho', src_indices=[i])
            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', src_indices=[i])
            prob.model.connect('fuel_mass', point_name + '.total_perf.L_equals_W.fuelburn')
            prob.model.connect('fuel_mass', point_name + '.total_perf.CG.fuelburn')

            for surface in surfaces:

                if i==0:
                    prob.model.connect('load_factor', name + '.load_factor', src_indices=[i])
                prob.model.connect('load_factor', point_name + '.coupled.load_factor', src_indices=[i])

                com_name = point_name + '.' + name + '_perf.'
                prob.model.connect(name + '.K', point_name + '.coupled.' + name + '.K')
                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_weights', point_name + '.coupled.' + name + '.element_weights')

                # 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_weight', point_name + '.' + 'total_perf.' + name + '_structural_weight')

                # Connect wingbox properties to von Mises stress calcs
                prob.model.connect(name + '.Qz', com_name + 'Qz')
                prob.model.connect(name + '.Iz', com_name + 'Iz')
                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 + '.skin_thickness', com_name + 'skin_thickness')
                prob.model.connect(name + '.t_over_c', com_name + 't_over_c')

        prob.model.connect('alpha', 'AS_point_0' + '.alpha')
        prob.model.connect('alpha_maneuver', 'AS_point_1' + '.alpha')

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

        prob.model.connect('AS_point_0.fuelburn', 'fuel_vol_delta.fuelburn')
        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')

        prob.model.connect('wing.struct_setup.fuel_vols', 'AS_point_1.coupled.wing.struct_states.fuel_vols')
        prob.model.connect('fuel_mass', 'AS_point_1.coupled.wing.struct_states.fuel_mass')

        comp = ExecComp('fuel_diff = (fuel_mass - fuelburn) / fuelburn')
        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')

        comp = ExecComp('fuel_diff_25 = (fuel_mass - fuelburn) / fuelburn')
        prob.model.add_subsystem('fuel_diff_25', comp,
            promotes_inputs=['fuel_mass'],
            promotes_outputs=['fuel_diff_25'])
        prob.model.connect('AS_point_1.fuelburn', 'fuel_diff_25.fuelburn')
        #=======================================================================================
        #=======================================================================================

        from openmdao.api import ScipyOptimizeDriver
        prob.driver = ScipyOptimizeDriver()
        prob.driver.options['tol'] = 1e-9

        # from openmdao.api import pyOptSparseDriver
        # prob.driver = pyOptSparseDriver()
        # prob.driver.add_recorder(SqliteRecorder("cases.sql"))
        # prob.driver.options['optimizer'] = "SNOPT"
        # prob.driver.opt_settings['Major optimality tolerance'] = 5e-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_design_var('alpha_maneuver', lower=-15., upper=15)

        prob.model.add_constraint('AS_point_0.CL', equals=0.5)
        # prob.model.add_constraint('AS_point_0.wing_perf.failure', upper=0.)
        prob.model.add_constraint('AS_point_1.L_equals_W', equals=0.)
        prob.model.add_constraint('AS_point_1.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.)
        # prob.model.add_constraint('fuel_diff_25', equals=0.)
        #=======================================================================================
        #=======================================================================================

        # Set up the problem
        prob.setup()

        # from openmdao.api import view_model
        # view_model(prob)

        prob.run_driver()

        # prob.check_partials(form='central', compact_print=True)

        # print(prob['AS_point_0.fuelburn'][0])
        # print(prob['wing.structural_weight'][0]/1.25)

        assert_rel_error(self, prob['AS_point_0.fuelburn'][0], 94556.9136969, 1e-5)
        assert_rel_error(self, prob['wing.structural_weight'][0]/1.25, 277535.59118440875, 1e-5)
Exemple #4
0
    def test(self):
        # Create a dictionary to store options about the surface
        mesh_dict = {
            'num_y': 13,
            'num_x': 2,
            'wing_type': 'CRM',
            'symmetry': True,
            'num_twist_cp': 5,
            'span_cos_spacing': 1.
        }

        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': 'tube',
            'thickness_cp': np.ones(2) * 0.06836728,
            '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.12]),  # thickness over chord ratio (NACA0015)
            'c_max_t': .303,  # chordwise location of maximum (NACA0015)
            # thickness
            'with_viscous': False,
            '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': 1.,
            '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 = Problem()

        # Add problem information as an independent variables component
        indep_var_comp = 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=9.80665 * 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:

                prob.model.connect('load_factor', name + '.load_factor')

                com_name = point_name + '.' + name + '_perf'
                prob.model.connect(name + '.K',
                                   point_name + '.coupled.' + name + '.K')
                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_weight', point_name + '.' +
                    'total_perf.' + name + '_structural_weight')
                prob.model.connect(name + '.t_over_c', com_name + '.t_over_c')

        # Set up the problem
        prob.setup()

        # from openmdao.api import view_model
        # view_model(prob)

        prob.run_model()

        assert_rel_error(self, prob['AS_point_0.wing_perf.CL'][0],
                         0.501212803372, 1e-6)
        assert_rel_error(self, prob['AS_point_0.wing_perf.failure'][0],
                         -0.434049851068, 1e-6)
        assert_rel_error(self, prob['AS_point_0.fuelburn'][0], 70365.875285,
                         1e-4)
        assert_rel_error(self, prob['AS_point_0.CM'][1], -1.2725562400264683,
                         1e-5)
Exemple #5
0
                          val=np.array([310.95, 340.294]),
                          units='m/s')
indep_var_comp.add_output('load_factor', val=np.array([1., 2.5]))
indep_var_comp.add_output('empty_cg', val=np.zeros((3)), units='m')
indep_var_comp.add_output('fuel_mass', val=3000., 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 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 aerostruct points
for i in range(2):

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

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

    prob.model.add_subsystem(point_name, AS_point)
    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],
                         70754.19144483653, 1e-5)
    def test(self):
        # Create a dictionary to store options about the surface
        mesh_dict = {
            'num_y': 5,
            'num_x': 3,
            '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,  # 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
            'Wf_reserve':
            15000.,  # [kg] reserve fuel mass
        }

        surfaces = [surf_dict]

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

        # Add problem information as an independent variables component
        indep_var_comp = 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')

        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', name + '.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_weights',
                    point_name + '.coupled.' + name + '.element_weights')
                prob.model.connect(
                    'load_factor',
                    point_name + '.coupled.' + name + '.load_factor')

                # 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_weight', point_name + '.' +
                    'total_perf.' + name + '_structural_weight')

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

        from openmdao.api import ScipyOptimizeDriver
        prob.driver = ScipyOptimizeDriver()
        prob.driver.options['tol'] = 1e-9

        # Set up the problem
        prob.setup()
        #
        # from openmdao.api import view_model
        # view_model(prob)

        prob.run_model()

        # prob.model.list_outputs(values=True,
        #                         implicit=False,
        #                         units=True,
        #                         shape=True,
        #                         bounds=True,
        #                         residuals=True,
        #                         scaling=True,
        #                         hierarchical=False,
        #                         print_arrays=True)

        print(prob['AS_point_0.fuelburn'][0])
        print(prob['wing.structural_weight'][0] / 1.25)
        print(prob['AS_point_0.wing_perf.failure'][0])

        assert_rel_error(self, prob['AS_point_0.fuelburn'][0], 112532.399999,
                         1e-5)
        assert_rel_error(self, prob['wing.structural_weight'][0] / 1.25,
                         235533.421185, 1e-5)
        assert_rel_error(self, prob['AS_point_0.wing_perf.failure'][0],
                         1.70644139941, 1e-5)
    def test(self):
        from openaerostruct.geometry.utils import generate_mesh, write_FFD_file

        from openaerostruct.integration.aerostruct_groups import AerostructGeometry, AerostructPoint

        import openmdao.api as om
        from pygeo import DVGeometry


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

        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.array([.1, .2, .3]),

                    'mesh' : mesh,

                    'geom_manipulator' : 'FFD',
                    'mx' : 2,
                    'my' : 3,

                    # 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=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=['*'])

        # 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']

            filename = write_FFD_file(surface, surface['mx'], surface['my'])
            DVGeo = DVGeometry(filename)
            aerostruct_group = AerostructGeometry(surface=surface, DVGeo=DVGeo)

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

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

        recorder = om.SqliteRecorder("aerostruct_ffd.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.shape', lower=-3, upper=2)
        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)

        # iprofile.setup()
        # iprofile.start()

        # Set up the problem
        prob.setup()

        # om.view_model(prob, outfile='aerostruct_ffd', show_browser=False)

        # prob.run_model()
        prob.run_driver()

        # prob.check_partials(compact_print=True)

        # print("\nWing CL:", prob['aero_point_0.wing_perf.CL'])
        # print("Wing CD:", prob['aero_point_0.wing_perf.CD'])


        # from helper import plot_3d_points
        #
        # mesh = prob['aero_point_0.wing.def_mesh']
        # plot_3d_points(mesh)
        #
        # filename = mesh_dict['wing_type'] + '_' + str(mesh_dict['num_x']) + '_' + str(mesh_dict['num_y'])
        # filename += '_' + str(surf_dict['mx']) + '_' + str(surf_dict['my']) + '.mesh'
        # np.save(filename, mesh)

        assert_rel_error(self, prob['AS_point_0.fuelburn'][0], 97680.8964568375, 1e-3)
    def test(self):

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

        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
            'S_ref_type':
            'wetted',  # how we compute the wing area,
            # can be 'wetted' or 'projected'
            'mesh':
            mesh,
            'twist_cp':
            np.array([4., 5., 8., 8., 8., 9.]),
            'fem_model_type':
            'wingbox',
            'data_x_upper':
            upper_x,
            'data_x_lower':
            lower_x,
            'data_y_upper':
            upper_y,
            'data_y_lower':
            lower_y,
            '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]),
            'original_wingbox_airfoil_t_over_c':
            0.12,

            # Aerodynamic deltas.
            # 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.
            # They can be used to account for things that are not included, such as contributions from the fuselage, nacelles, tail surfaces, etc.
            'CL0':
            0.0,
            'CD0':
            0.0078,
            'with_viscous':
            True,  # if true, compute viscous drag
            'with_wave':
            True,  # if true, compute wave drag

            # Airfoil properties for viscous drag calculation
            'k_lam':
            0.05,  # percentage of chord with laminar
            # flow, used for viscous drag
            'c_max_t':
            .38,  # chordwise location of maximum thickness
            't_over_c_cp':
            np.array([0.08, 0.08, 0.08, 0.10, 0.10, 0.08]),

            # 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
            'wing_weight_ratio':
            1.25,
            'exact_failure_constraint':
            False,  # if false, use KS function
            'struct_weight_relief':
            True,
            'distributed_fuel_weight':
            True,
            '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 = Problem()

        # Add problem information as an independent variables component
        indep_var_comp = IndepVarComp()
        indep_var_comp.add_output('v',
                                  val=np.array([.85 * 295.07, .64 * 340.294]),
                                  units='m/s')
        indep_var_comp.add_output('alpha', val=0., units='deg')
        indep_var_comp.add_output('alpha_maneuver', val=0., units='deg')
        indep_var_comp.add_output('Mach_number', val=np.array([0.85, 0.64]))
        indep_var_comp.add_output('re',val=np.array([0.348*295.07*.85*1./(1.43*1e-5), \
                                  1.225*340.294*.64*1./(1.81206*1e-5)]),  units='1/m')
        indep_var_comp.add_output('rho',
                                  val=np.array([0.348, 1.225]),
                                  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=np.array([295.07, 340.294]),
                                  units='m/s')
        indep_var_comp.add_output('load_factor', val=np.array([1., 2.5]))
        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 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 aerostruct points
        for i in range(2):

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

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

            prob.model.add_subsystem(point_name, AS_point)

            # Connect flow properties to the analysis point
            prob.model.connect('v', point_name + '.v', src_indices=[i])
            prob.model.connect('Mach_number',
                               point_name + '.Mach_number',
                               src_indices=[i])
            prob.model.connect('re', point_name + '.re', src_indices=[i])
            prob.model.connect('rho', point_name + '.rho', src_indices=[i])
            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',
                               src_indices=[i])
            prob.model.connect('empty_cg', point_name + '.empty_cg')
            prob.model.connect('load_factor',
                               point_name + '.load_factor',
                               src_indices=[i])
            prob.model.connect('fuel_mass',
                               point_name + '.total_perf.L_equals_W.fuelburn')
            prob.model.connect('fuel_mass',
                               point_name + '.total_perf.CG.fuelburn')

            for surface in surfaces:

                name = surface['name']

                if i == 0:
                    # This load factor connects to a component used to compute weights in a mass sense. It's load factor should always just be 1.
                    prob.model.connect('load_factor',
                                       name + '.load_factor',
                                       src_indices=[i])
                if surf_dict['distributed_fuel_weight']:
                    prob.model.connect('load_factor',
                                       point_name + '.coupled.load_factor',
                                       src_indices=[i])

                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')
                if surf_dict['struct_weight_relief']:
                    prob.model.connect(
                        name + '.element_weights',
                        point_name + '.coupled.' + name + '.element_weights')

                # 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_weight', point_name + '.' +
                    'total_perf.' + name + '_structural_weight')

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

        prob.model.connect('alpha', 'AS_point_0' + '.alpha')
        prob.model.connect('alpha_maneuver', 'AS_point_1' + '.alpha')

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

        if surf_dict['distributed_fuel_weight']:
            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')

            prob.model.connect(
                'wing.struct_setup.fuel_vols',
                'AS_point_1.coupled.wing.struct_states.fuel_vols')
            prob.model.connect(
                'fuel_mass', 'AS_point_1.coupled.wing.struct_states.fuel_mass')

        comp = ExecComp('fuel_diff = (fuel_mass - fuelburn) / fuelburn')
        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')

        ## Use these settings if you do not have pyOptSparse or SNOPT
        prob.driver = ScipyOptimizeDriver()
        prob.driver.options['optimizer'] = 'SLSQP'
        prob.driver.options['tol'] = 1e-8

        recorder = SqliteRecorder("unit_test.db")
        prob.driver.add_recorder(recorder)

        # We could also just use prob.driver.recording_options['includes']=['*'] here, but for large meshes the database file becomes extremely large. So we just select the variables we need.
        prob.driver.recording_options['includes'] = \
        ['prob_vars.alpha', 'prob_vars.rho', 'prob_vars.v', 'prob_vars.cg', \
        'AS_point_1.total_perf.CG.cg', 'AS_point_0.total_perf.CG.cg', \
        'AS_point_0.coupled.wing_loads.loads', 'AS_point_1.coupled.wing_loads.loads', \
        'wing.wingbox_group.skin_thickness_bsp.skin_thickness', \
        'wing.geometry.mesh.rotate.mesh',\
        'wing.wingbox_group.spar_thickness_bsp.spar_thickness', \
        'wing.wingbox_group.spar_thickness_bsp.spar_thickness', \
        'wing.geometry.t_over_c_bsp.t_over_c', \
        'AS_point_0.wing_perf.struct_funcs.vonmises.vonmises', \
        'AS_point_1.wing_perf.struct_funcs.vonmises.vonmises', \
        'wing.struct_setup.structural_weight.structural_weight', \
        'AS_point_0.coupled.wing.def_mesh.displacement_transfer.def_mesh', \
        'AS_point_1.coupled.wing.def_mesh.displacement_transfer.def_mesh', \
        'AS_point_0.coupled.wing.aero_geom.normals', \
        'AS_point_1.coupled.wing.aero_geom.normals', \
        'AS_point_0.coupled.wing.aero_geom.widths',  'AS_point_1.coupled.wing.aero_geom.widths', \
        'AS_point_0.coupled.aero_states.panel_forces_surf.wing_sec_forces', \
        'AS_point_1.coupled.aero_states.panel_forces_surf.wing_sec_forces', \
        'AS_point_0.wing_perf.aero_funcs.coeffs.CL1', 'AS_point_1.wing_perf.aero_funcs.coeffs.CL1', \
        'AS_point_0.coupled.wing.aero_geom.S_ref',
        'AS_point_1.coupled.wing.aero_geom.S_ref',
        'wing.geometry.twist_bsp.twist']

        prob.driver.recording_options['record_objectives'] = True
        prob.driver.recording_options['record_constraints'] = True
        prob.driver.recording_options['record_desvars'] = True
        prob.driver.recording_options['record_inputs'] = True

        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_design_var('alpha_maneuver', lower=-15., upper=15)

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

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

        prob.run_driver()

        # print(prob['AS_point_0.fuelburn'][0])
        # print(prob['wing.structural_weight'][0]/1.25)
        # print(prob['wing.geometry.t_over_c_cp'])

        assert_rel_error(self, prob['AS_point_0.fuelburn'][0], 101946.723936,
                         1e-5)
        assert_rel_error(self, prob['wing.structural_weight'][0] / 1.25,
                         358406.34121, 1e-5)
        assert_rel_error(
            self, prob['wing.geometry.t_over_c_cp'],
            np.array([
                0.10305255, 0.08205957, 0.11089362, 0.13089362, 0.10205957,
                0.09361004
            ]), 1e-5)
    def test(self):
        import numpy as np

        from openaerostruct.geometry.utils import generate_mesh

        from openaerostruct.integration.aerostruct_groups import AerostructGeometry, AerostructPoint

        from openmdao.api import IndepVarComp, Problem, Group, SqliteRecorder

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

        # Add problem information as an independent variables component
        indep_var_comp = 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')

        from openmdao.api import ScipyOptimizeDriver
        prob.driver = ScipyOptimizeDriver()
        prob.driver.options['tol'] = 1e-9

        recorder = 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], 104393.448214,
                         1e-8)
    def setup(self):
        # Total number of nodes to use in the spanwise (num_y) and
        # chordwise (num_x) directions. Vary these to change the level of fidelity.

        mesh_dict = self.options['mesh_dict']
        rv_dict = self.options['rv_dict']
        num_y = mesh_dict['num_y']
        num_x = mesh_dict['num_x']

        mesh = generate_mesh(mesh_dict)

        # Apply camber to the mesh
        camber = 1 - np.linspace(-1, 1, num_x) ** 2
        camber *= 0.3 * 0.05

        for ind_x in range(num_x):
            mesh[ind_x, :, 2] = camber[ind_x]

        # Introduce geometry manipulation variables to define the ScanEagle shape
        zshear_cp = np.zeros(10)
        zshear_cp[0] = .3

        xshear_cp = np.zeros(10)
        xshear_cp[0] = .15

        chord_cp = np.ones(10)
        chord_cp[0] = .5
        chord_cp[-1] = 1.5
        chord_cp[-2] = 1.3

        radius_cp = 0.01  * np.ones(10)

        # Define wing parameters
        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',

                    'taper' : 0.8,
                    'zshear_cp' : zshear_cp,
                    'xshear_cp' : xshear_cp,
                    'chord_cp' : chord_cp,
                    'sweep' : 20.,
                    'twist_cp' : np.array([2.5, 2.5, 5.]), #np.zeros((3)),
                    'thickness_cp' : np.ones((3))*.008,

                    # Give OAS the radius and mesh from before
                    'radius_cp' : radius_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.12]),      # thickness over chord ratio
                    'c_max_t' : .303,       # chordwise location of maximum (NACA0015)
                                            # thickness
                    'with_viscous' : True,
                    'with_wave' : False,     # if true, compute wave drag

                    # Material properties taken from http://www.performance-composites.com/carbonfibre/mechanicalproperties_2.asp
                    'yield' : 350.e6,

                    'fem_origin' : 0.35,    # normalized chordwise location of the spar
                    'wing_weight_ratio' : 1., # multiplicative factor on the computed structural weight
                    'struct_weight_relief' : True,    # 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
                    }

        # Add problem information as an independent variables component
        indep_var_comp = IndepVarComp()
        # indep_var_comp.add_output('v', val=22.876, units='m/s')
        indep_var_comp.add_output('alpha', val=5., units='deg')
        # indep_var_comp.add_output('re', val=1.e6, units='1/m')
        # indep_var_comp.add_output('rho', val=0.770816, units='kg/m**3')
        # indep_var_comp.add_output('speed_of_sound', val=322.2, units='m/s')
        indep_var_comp.add_output('empty_cg', val=np.array([0.2, 0., 0.]), units='m')

        # Create independent input variables depending on which variables are
        # being considered as random variables
        if 'Mach_number' not in rv_dict:
            indep_var_comp.add_output('Mach_number', val=0.071)
        if 'CT' not in rv_dict:
            indep_var_comp.add_output('CT', val=9.80665 * 8.6e-6, units='1/s')
        if 'W0' not in rv_dict:
            indep_var_comp.add_output('W0', val=10.0,  units='kg')
        if 'R' not in rv_dict:
            indep_var_comp.add_output('R', val=1800, units='km')
        if 'load_factor' not in rv_dict:
            indep_var_comp.add_output('load_factor', val=1.)
        if 'E' not in rv_dict:
            indep_var_comp.add_output('E', val=85.e9, units='N/m**2')
        if 'G' not in rv_dict:
            # indep_var_comp.add_output('G', val=25.e9, units='N/m**2')
            indep_var_comp.add_output('G', val=5.e9, units='N/m**2')
        if 'mrho' not in rv_dict:
            indep_var_comp.add_output('mrho', val=1600, units='kg/m**3')
        if 'altitude' not in rv_dict:
            indep_var_comp.add_output('altitude', val=4.57, units='km')

        self.add_subsystem('prob_vars', indep_var_comp, promotes=['*'])
        # Add atmosphere related properties
        self.add_subsystem('atmos', AtmosGroup(), promotes=['*'])

        # Add the AerostructGeometry group, which computes all the intermediary
        # parameters for the aero and structural analyses, like the structural
        # stiffness matrix and some aerodynamic geometry arrays
        aerostruct_group = AerostructGeometry(surface=surface)

        name = 'wing'

        # Add the group to the problem
        self.add_subsystem(name, aerostruct_group,
                           promotes_inputs=['load_factor'])

        point_name = 'AS_point_0'

        # Create the aerostruct point group and add it to the model.
        # This contains all the actual aerostructural analyses.
        AS_point = AerostructPoint(surfaces=[surface])

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

        # Issue quite a few connections within the model to make sure all of the
        # parameters are connected correctly.
        com_name = point_name + '.' + name + '_perf'
        self.connect(name + '.local_stiff_transformed', point_name + '.coupled.' + name + '.local_stiff_transformed')
        self.connect(name + '.nodes', point_name + '.coupled.' + name + '.nodes')

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

        # Connect performance calculation variables
        self.connect(name + '.radius', com_name + '.radius')
        self.connect(name + '.thickness', com_name + '.thickness')
        self.connect(name + '.nodes', com_name + '.nodes')
        self.connect(name + '.cg_location', point_name + '.' + 'total_perf.' + name + '_cg_location')
        self.connect(name + '.structural_weight', point_name + '.' + 'total_perf.' + name + '_structural_weight')
        self.connect(name + '.t_over_c', com_name + '.t_over_c')

        # Make connections based on whether a variable is a random variable or not
        if 'E' not in rv_dict:
            self.connect('E', com_name + '.struct_funcs.vonmises.E')
            self.connect('E', name + '.struct_setup.assembly.E')
        if 'G' not in rv_dict:
            self.connect('G', com_name + '.struct_funcs.vonmises.G')
            self.connect('G', name + '.struct_setup.assembly.G')
        if 'mrho' not in rv_dict:
            self.connect('mrho', name + '.struct_setup.structural_weight.mrho')
        if 'load_factor' not in rv_dict:
            self.connect('load_factor', point_name + '.coupled.' + name + '.load_factor')
Exemple #12
0
def get_problem(surface, ailerons):
    surface['control_surfaces'] = ailerons
    # Create the problem and assign the model group
    prob = Problem()

    # Add problem information as an independent variables component
    indep_var_comp = IndepVarComp()
    indep_var_comp.add_output('v', val=25., units='m/s')
    indep_var_comp.add_output('alpha', val=alpha, units='deg')
    indep_var_comp.add_output('re', val=5e5, units='1/m')
    indep_var_comp.add_output('rho', val=rho, units='kg/m**3')
    indep_var_comp.add_output('cg', val=cg_loc, units='m')
    indep_var_comp.add_output('delta_aileron', val=12.5, units='deg')
    indep_var_comp.add_output('omega',
                              val=np.array([0., 0., 0.]),
                              units='rad/s')

    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],
                               rotational=True,
                               rollOnly=True)

    prob.model.add_subsystem(point_name,
                             AS_point,
                             promotes_inputs=[
                                 'v', 'alpha', 'Mach_number', 're', 'rho',
                                 'cg', 'omega'
                             ])
    for aileron in ailerons:
        prob.model.connect(
            'delta_aileron', point_name + '.coupled.' + name +
            '.control_surfaces.' + aileron['name'] + '.delta_aileron')

    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 + '.t_over_c', com_name + '.t_over_c')

    ##from openmdao.api import ScipyOptimizeDriver
    #prob.driver = ScipyOptimizeDriver()
    #prob.driver.options['tol'] = 1e-9

    # Set up the problem
    prob.setup(check=True)
    prob.model.AS_point_0.coupled.nonlinear_solver.options['maxiter'] = 10000
    prob.model.AS_point_0.coupled.nonlinear_solver.options['atol'] = 1e-6

    return prob
Exemple #13
0
def fctOptim(mrhoi, skin, spar, span, toverc):

    # Starting time
    starttime = time.time()

    # Materials
    #sandw1=material(66.35,4.25e9,1.63e9,58.7e6/1.5,34.7,"sandw1")
    #sandw2=material(174.5,14.15e9,5.44e9,195.6e6/1.5,43.4,"sandw2")
    #sandw3=material(483,42.5e9,16.3e9,586e6/1.5,46.8,"sandw3")
    sandw4 = material(504.5, 42.5e9, 16.3e9, 586e6 / 1.5, 44.9, "sandw4")
    #sandw5=material(574.5,42.5e9,16.3e9,586e6/1.5,39.3,"sandw5")
    sandw5 = material(560.5, 42.5e9, 16.3e9, 586e6 / 1.5, 40.3, "sandw5")
    sandw6 = material(529, 42.5e9, 16.3e9, 237e6 / 1.5, 42.75, "sandw6")
    al7075 = material(2.80e3, 72.5e9, 27e9, 444.5e6 / 1.5,
                      13.15 * (1 - 0.426) + 2.61 * 0.426,
                      "al7075")  #from EDUPACK
    #al7075oas=material(2.78e3,73.1e9,73.1e9/2/1.33,444.5e6/1.5,13.15*(1-0.426)+2.61*0.426,"al7075") #from OAS example
    qiCFRP = material(1565, 54.9e9, 21e9, 670e6 / 1.5, 48.1, "qiCFRP")
    steel = material(7750, 200e9, 78.5e9, 562e6 / 1.5,
                     4.55 * (1 - 0.374) + 1.15 * 0.374, "steel")
    gfrp = material(1860, 21.4e9, 8.14e9, 255e6, 6.18,
                    "gfrp")  #epoxy-Eglass,woven,QI
    #nomat=material(1370,0.01,0.01,0.01,60,"noMaterial")
    nomat = material(50, 1e8, 1e4, 1e5, 6000, "noMaterial")
    #nomat=material(50,1e8,1e4,1e5,60,"noMaterial")
    fakemat = material((2.80e3 + 7750) / 2, (72.5e9 + 200e9) / 2,
                       (27e9 + 78.5e9) / 2, (444.5e6 / 1.5 + 562e6 / 1.5) / 2,
                       (13.15 * (1 - 0.426) + 2.61 * 0.426 + 4.55 *
                        (1 - 0.374) + 1.15 * 0.374) / 2, "fakemat")
    nomatEnd = material(10000, 5e9, 2e9, 20e6 / 1.5, 60, "nomatEnd")

    materials = [
        al7075, qiCFRP, steel, gfrp, nomat, fakemat, nomatEnd, sandw4, sandw5,
        sandw6
    ]
    #    materials=[al7075, qiCFRP, steel, gfrp, nomat, fakemat, nomatEnd, sandw3, sandw4, sandw5, sandw6]
    #    materials=[al7075, qiCFRP, steel, gfrp, nomat, fakemat, nomatEnd,sandw1,sandw2,sandw3]
    #    materials=[sandw4, sandw5, nomat, nomatEnd]

    # Provide coordinates for a portion of an airfoil for the wingbox cross-section as an nparray with dtype=complex (to work with the complex-step approximation for derivatives).
    # These should be for an airfoil with the chord scaled to 1.
    # We use the 10% to 60% portion of the NACA 63412 airfoil for this case
    # We use the coordinates available from airfoiltools.com. Using such a large number of coordinates is not necessary.
    # The first and last x-coordinates of the upper and lower surfaces must be the same
    upper_x = np.array([
        0.1, 0.11, 0.12, 0.13, 0.14, 0.15, 0.16, 0.17, 0.18, 0.19, 0.2, 0.21,
        0.22, 0.23, 0.24, 0.25, 0.26, 0.27, 0.28, 0.29, 0.3, 0.31, 0.32, 0.33,
        0.34, 0.35, 0.36, 0.37, 0.38, 0.39, 0.4, 0.41, 0.42, 0.43, 0.44, 0.45,
        0.46, 0.47, 0.48, 0.49, 0.5, 0.51, 0.52, 0.53, 0.54, 0.55, 0.56, 0.57,
        0.58, 0.59, 0.6
    ],
                       dtype='complex128')
    lower_x = np.array([
        0.1, 0.11, 0.12, 0.13, 0.14, 0.15, 0.16, 0.17, 0.18, 0.19, 0.2, 0.21,
        0.22, 0.23, 0.24, 0.25, 0.26, 0.27, 0.28, 0.29, 0.3, 0.31, 0.32, 0.33,
        0.34, 0.35, 0.36, 0.37, 0.38, 0.39, 0.4, 0.41, 0.42, 0.43, 0.44, 0.45,
        0.46, 0.47, 0.48, 0.49, 0.5, 0.51, 0.52, 0.53, 0.54, 0.55, 0.56, 0.57,
        0.58, 0.59, 0.6
    ],
                       dtype='complex128')
    upper_y = np.array([
        0.0513, 0.0537, 0.0559, 0.0580, 0.0600, 0.0619, 0.0636, 0.0652, 0.0668,
        0.0682, 0.0696, 0.0709, 0.0721, 0.0732, 0.0742, 0.0752, 0.0761, 0.0769,
        0.0776, 0.0782, 0.0788, 0.0793, 0.0797, 0.0801, 0.0804, 0.0806, 0.0808,
        0.0808, 0.0808, 0.0807, 0.0806, 0.0804, 0.0801, 0.0798, 0.0794, 0.0789,
        0.0784, 0.0778, 0.0771, 0.0764, 0.0757, 0.0749, 0.0740, 0.0732, 0.0723,
        0.0713, 0.0703, 0.0692, 0.0681, 0.0669, 0.0657
    ],
                       dtype='complex128')
    lower_y = np.array([
        -0.0296, -0.0307, -0.0317, -0.0326, -0.0335, -0.0343, -0.0350, -0.0357,
        -0.0363, -0.0368, -0.0373, -0.0378, -0.0382, -0.0386, -0.0389, -0.0391,
        -0.0394, -0.0395, -0.0397, -0.0398, -0.0398, -0.0398, -0.0398, -0.0397,
        -0.0396, -0.0394, -0.0392, -0.0389, -0.0386, -0.0382, -0.0378, -0.0374,
        -0.0369, -0.0363, -0.0358, -0.0352, -0.0345, -0.0338, -0.0331, -0.0324,
        -0.0316, -0.0308, -0.0300, -0.0292, -0.0283, -0.0274, -0.0265, -0.0256,
        -0.0246, -0.0237, -0.0227
    ],
                       dtype='complex128')

    Rcurv = RadiusCurvature(upper_x, lower_x, upper_y, lower_y)

    # Create a dictionary to store options about the surface
    mesh_dict = {
        'num_y': 15,
        'num_x': 3,
        'wing_type': 'rect',
        'symmetry': True,
        'chord_cos_spacing': 0,
        'span_cos_spacing': 0,
        'num_twist_cp': 4
    }

    mesh = generate_mesh(mesh_dict)

    # Batteries and solar panels
    densityPV = 0.23  #kg/m^2
    energeticDensityBattery = 435 * 0.995 * 0.95 * 0.875 * 0.97  #Wh/kg 0.995=battery controller efficiency, 0.95=end of life capacity loss of 5%, 0.97=min battery SOC of 3%, 0.875=packaging efficiency
    emissionBat = 0.104 / 0.995 / 0.95 / 0.875 / 0.97  #[kgCO2/Wh]
    night_hours = 13  #h
    productivityPV = 350.0 * 0.97 * 0.95  #[W/m^2] 350 from Zephyr power figure, 0.97=MPPT efficiency, 0.95=battery round trip efficiency
    emissionPV = 0.05 / 0.97 / 0.95  #[kgCO2/W] emissions of the needed PV surface to produce 1W
    emissionsPerW = emissionPV + emissionBat * night_hours  #[kgCO2/W]

    # Dictionary for the lifting surface
    surf_dict = {
        # Wing definition
        'name':
        'wing',  # give the surface some name
        'symmetry':
        True,  # if True, model only one half of the lifting surface
        'S_ref_type':
        'projected',  # how we compute the wing area,
        # can be 'wetted' or 'projected'
        'mesh':
        mesh,
        'fem_model_type':
        'wingbox',  # 'wingbox' or 'tube'
        'data_x_upper':
        upper_x,
        'data_x_lower':
        lower_x,
        'data_y_upper':
        upper_y,
        'data_y_lower':
        lower_y,
        'airfoil_radius_curvature':
        Rcurv,
        'twist_cp':
        np.array([10., 20., 20., 20.]),  # [deg]
        'chord_cp': [1.5],  # [m]
        'span':
        span,  #[m]
        'taper':
        0.3,
        'spar_thickness_cp':
        np.array([spar, spar, spar, spar]),  # [m]
        'skin_thickness_cp':
        np.array([skin / 2, skin, skin * 1.5, 2 * skin]),  # [m]
        't_over_c_cp':
        np.array([0.75 * toverc, toverc, toverc, 1.25 * toverc]),  #TODELETE
        'original_wingbox_airfoil_t_over_c':
        0.12,

        # Aerodynamic deltas.
        # 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.
        # They can be used to account for things that are not included, such as contributions from the fuselage, camber, etc.
        'CL0':
        0.0,  # CL delta
        'CD0':
        0.0078,  # CD delta
        'with_viscous':
        True,  # if true, compute viscous drag
        'with_wave':
        True,  # if true, compute wave drag

        # Airfoil properties for viscous drag calculation
        'k_lam':
        0.80,
        #'k_lam' : 0.05,         # fraction of chord with laminar
        # flow, used for viscous drag
        'c_max_t':
        .349,  # chordwise location of maximum thickness

        # Materials
        'materlist':
        materials,
        'puissanceMM':
        1,  #power used in muli-material function

        # Structural values
        'strength_factor_for_upper_skin':
        1.0,  # the yield stress is multiplied by this factor for the upper skin
        'wing_weight_ratio':
        1.,
        'exact_failure_constraint':
        False,  # if false, use KS function
        'struct_weight_relief':
        True,

        # Engines
        'n_point_masses':
        1,  # number of point masses in the system; in this case, the engine (omit option if no point masses)

        # Power
        'productivityPV':
        productivityPV,  #[W/m^2]
        'densityPV':
        densityPV + productivityPV / energeticDensityBattery *
        night_hours,  #[kg/m^2] the weight of the batteries is counted here
        'payload_power':
        125.5,  #[W] payload=150 + avionics=211
        'motor_propeller_efficiency':
        0.84,  #thrusting power/electrical power used by propulsion
        'co2PV':
        emissionsPerW * productivityPV /
        (densityPV + productivityPV / energeticDensityBattery *
         night_hours),  #[kgCO2/kg] #co2 burden of PV cells and battery
        'prop_density':
        0.0058,  #[kg/W]
        'mppt_density':
        0.00045,  #[kg/W]
    }

    surfaces = [surf_dict]

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

    # Add problem information as an independent variables component data for altitude=23240 m and 0 m
    speed = 15.56  #m/s
    speed_dive = 1.4 * speed  #m/s
    gust_speed = 3.4  #m/s
    rho_air = 0.089  #kg/m**3
    speed_sound = 295.1  #m/s
    #n_gust = 1 + 0.5*rho_air*speed_dive*gust_speed*2*pi/3000

    indep_var_comp = IndepVarComp()
    indep_var_comp.add_output('Mach_number',
                              val=np.array([
                                  speed / speed_sound,
                                  (speed_dive**2 + gust_speed**2)**0.5 /
                                  speed_sound, 0
                              ]))
    indep_var_comp.add_output(
        'v',
        val=np.array([speed, (speed_dive**2 + gust_speed**2)**0.5, 0]),
        units='m/s')
    indep_var_comp.add_output('re',val=np.array([rho_air*speed*1./(1.4*1e-5), \
                              rho_air*speed_dive*1./(1.4*1e-5), 0]),  units='1/m') #L=10m,
    indep_var_comp.add_output('rho',
                              val=np.array([rho_air, rho_air, 1.225]),
                              units='kg/m**3')
    indep_var_comp.add_output('speed_of_sound',
                              val=np.array([speed_sound, speed_sound, 340]),
                              units='m/s')

    indep_var_comp.add_output('W0_without_point_masses', val=8, units='kg')

    indep_var_comp.add_output('load_factor', val=np.array([1., 1.1, 0.]))
    indep_var_comp.add_output('alpha', val=0., units='deg')
    indep_var_comp.add_output('alpha_gust',
                              val=atan(gust_speed / speed_dive) * 180 / pi,
                              units='deg')

    indep_var_comp.add_output('empty_cg', val=np.zeros((3)), units='m')

    indep_var_comp.add_output('mrho',
                              val=np.array([mrhoi, mrhoi]),
                              units='kg/m**3')

    indep_var_comp.add_output('engine_location', val=-0.3)  #VMGM

    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 groups to the problem with the name of the surface.
        prob.model.add_subsystem(name, aerostruct_group)

    prob.model.add_subsystem('YoungMM',
                             YoungMM(surface=surface),
                             promotes_inputs=['mrho'],
                             promotes_outputs=['young'])  #VMGM
    prob.model.add_subsystem('ShearMM',
                             ShearMM(surface=surface),
                             promotes_inputs=['mrho'],
                             promotes_outputs=['shear'])  #VMGM
    prob.model.add_subsystem('YieldMM',
                             YieldMM(surface=surface),
                             promotes_inputs=['mrho'],
                             promotes_outputs=['yield'])  #VMGM
    prob.model.add_subsystem('CO2MM',
                             CO2MM(surface=surface),
                             promotes_inputs=['mrho'],
                             promotes_outputs=['co2'])  #VMGM
    prob.model.add_subsystem(
        'PointMassLocations',
        PointMassLocations(surface=surface),
        promotes_inputs=['engine_location', 'span', 'nodes'],
        promotes_outputs=['point_mass_locations'])  #VMGM
    prob.model.add_subsystem('PointMasses',
                             PointMasses(surface=surface),
                             promotes_inputs=['PV_surface'],
                             promotes_outputs=['point_masses'])  #VMGM

    prob.model.add_subsystem(
        'W0_comp',
        ExecComp('W0 = W0_without_point_masses + 2*sum(point_masses)',
                 units='kg'),
        promotes=['*'])

    prob.model.connect('mrho',
                       name + '.struct_setup.structural_mass.mrho')  #ED
    prob.model.connect('young', name +
                       '.struct_setup.assembly.local_stiff.young')  #VMGM
    prob.model.connect('shear', name +
                       '.struct_setup.assembly.local_stiff.shear')  #VMGM
    prob.model.connect('wing.span', 'span')  #VMGM
    prob.model.connect('AS_point_0.total_perf.PV_surface', 'PV_surface')  #VMGM
    prob.model.connect(name + '.nodes', 'nodes')  #VMGM

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

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

        # Create the aerostruct 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', src_indices=[i])
        prob.model.connect('Mach_number',
                           point_name + '.Mach_number',
                           src_indices=[i])
        prob.model.connect('re', point_name + '.re', src_indices=[i])
        prob.model.connect('rho', point_name + '.rho', src_indices=[i])
        prob.model.connect('W0', point_name + '.W0')
        prob.model.connect('speed_of_sound',
                           point_name + '.speed_of_sound',
                           src_indices=[i])
        prob.model.connect('empty_cg', point_name + '.empty_cg')
        prob.model.connect('load_factor',
                           point_name + '.load_factor',
                           src_indices=[i])

        for surface in surfaces:

            name = surface['name']

            prob.model.connect('load_factor',
                               point_name + '.coupled.load_factor',
                               src_indices=[i])  #for PV distributed weight

            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')
            prob.model.connect('young',
                               com_name + 'struct_funcs.vonmises.young')
            prob.model.connect('shear',
                               com_name + 'struct_funcs.vonmises.shear')
            prob.model.connect('yield',
                               com_name + 'struct_funcs.failure.yield')  #VMGM
            prob.model.connect('young',
                               com_name + 'struct_funcs.buckling.young')  #VMGM
            prob.model.connect('shear',
                               com_name + 'struct_funcs.buckling.shear')  #VMGM
            prob.model.connect(name + '.t_over_c', com_name +
                               'struct_funcs.buckling.t_over_c')  #VMGM

            # Connect aerodyamic mesh to coupled group mesh
            prob.model.connect(name + '.mesh',
                               point_name + '.coupled.' + name + '.mesh')
            if surf_dict['struct_weight_relief']:
                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 + '.Qx', com_name + 'Qx')

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

            coupled_name = point_name + '.coupled.' + name
            prob.model.connect('point_masses', coupled_name + '.point_masses')
            prob.model.connect('point_mass_locations',
                               coupled_name + '.point_mass_locations')

    prob.model.connect('alpha', 'AS_point_0' + '.alpha')
    prob.model.connect('alpha_gust', 'AS_point_1' + '.alpha')
    prob.model.connect('alpha', 'AS_point_2' + '.alpha')  #VMGM

    # Here we add the co2 objective componenet to the model
    prob.model.add_subsystem('emittedco2',
                             structureCO2(surfaces=surfaces),
                             promotes_inputs=['co2'],
                             promotes_outputs=['emitted_co2'])  #VMGM
    prob.model.connect('wing.structural_mass', 'emittedco2.mass')
    prob.model.connect('AS_point_0.total_perf.PV_mass', 'emittedco2.PV_mass')
    prob.model.connect('wing.spars_mass', 'emittedco2.spars_mass')  #VMGM

    #Here we add the thickness constraint to the model
    prob.model.add_subsystem('acceptableThickness',
                             checkThickness(surface=surface),
                             promotes_outputs=['acceptableThickness'])
    prob.model.connect('wing.geometry.t_over_c_cp',
                       'acceptableThickness.t_over_c')
    prob.model.connect('wing.chord_cp', 'acceptableThickness.chordroot')
    prob.model.connect('wing.skin_thickness_cp',
                       'acceptableThickness.skinThickness')
    prob.model.connect('wing.taper', 'acceptableThickness.taper')

    prob.model.connect('wing.struct_setup.PV_areas',
                       'AS_point_0.coupled.wing.struct_states.PV_areas')
    prob.model.connect('AS_point_0.total_perf.PV_mass',
                       'AS_point_0.coupled.wing.struct_states.PV_mass')
    prob.model.connect('wing.struct_setup.PV_areas',
                       'AS_point_1.coupled.wing.struct_states.PV_areas')
    prob.model.connect('AS_point_0.total_perf.PV_mass',
                       'AS_point_1.coupled.wing.struct_states.PV_mass')
    prob.model.connect('wing.chord_cp',
                       'AS_point_1.wing_perf.struct_funcs.chord')
    prob.model.connect('wing.taper', 'AS_point_1.wing_perf.struct_funcs.taper')
    prob.model.connect('wing.struct_setup.PV_areas',
                       'AS_point_2.coupled.wing.struct_states.PV_areas')  #VMGM
    prob.model.connect('AS_point_0.total_perf.PV_mass',
                       'AS_point_2.coupled.wing.struct_states.PV_mass')  #VMGM
    prob.model.connect('wing.chord_cp',
                       'AS_point_2.wing_perf.struct_funcs.chord')  #VMGM
    prob.model.connect('wing.taper',
                       'AS_point_2.wing_perf.struct_funcs.taper')  #VMGM

    # Objective function
    prob.model.add_objective('emitted_co2', scaler=1e-4)

    # Design variables
    prob.model.add_design_var('wing.twist_cp',
                              lower=-20.,
                              upper=20.,
                              scaler=0.1)  #VMGM
    prob.model.add_design_var('wing.spar_thickness_cp',
                              lower=0.0001,
                              upper=0.1,
                              scaler=1e4)
    prob.model.add_design_var('wing.skin_thickness_cp',
                              lower=0.0001,
                              upper=0.1,
                              scaler=1e3)
    prob.model.add_design_var('wing.span', lower=1., upper=1000., scaler=0.1)
    prob.model.add_design_var('wing.chord_cp', lower=1.4, upper=500., scaler=1)
    ##prob.model.add_design_var('wing.span', lower=1., upper=50., scaler=0.1)
    ##prob.model.add_design_var('wing.chord_cp', lower=1., upper=500., scaler=1)
    prob.model.add_design_var('wing.taper', lower=0.3, upper=0.99, scaler=10)
    prob.model.add_design_var('wing.geometry.t_over_c_cp',
                              lower=0.01,
                              upper=0.4,
                              scaler=10.)
    prob.model.add_design_var('mrho', lower=504.5, upper=504.5,
                              scaler=0.001)  #ED
    #prob.model.add_design_var('mrho', lower=500, upper=8000, scaler=0.001) #ED
    prob.model.add_design_var('engine_location', lower=-1, upper=0,
                              scaler=10.)  #VMGM

    # Constraints
    prob.model.add_constraint('AS_point_0.L_equals_W', equals=0.)
    prob.model.add_constraint(
        'AS_point_0.enough_power', upper=0.
    )  #Make sure needed power stays below the solar power producible by the wing
    prob.model.add_constraint(
        'acceptableThickness', upper=0.
    )  #Make sure skin thickness fits in the wing (to avoid negative spar mass)
    #prob.model.add_constraint('AS_point_0.wing_perf.failure', upper=0.)  #VMGM
    #prob.model.add_constraint('AS_point_0.wing_perf.buckling', upper=0.)  #VMGM
    prob.model.add_constraint('AS_point_1.wing_perf.failure', upper=0.)
    prob.model.add_constraint('AS_point_1.wing_perf.buckling', upper=0.)
    prob.model.add_constraint('AS_point_2.wing_perf.failure', upper=0.)  #VMGM
    prob.model.add_constraint('AS_point_2.wing_perf.buckling', upper=0.)  #VMGM
    prob.model.add_constraint(
        'AS_point_0.coupled.wing.S_ref',
        upper=200.)  # Surface constarint to avoid snowball effect

    #prob.model.approx_totals(method='fd', step=5e-7, form='forward', step_calc='rel')
    #prob.model.nonlinear_solver = newton = NewtonSolver()

    prob.driver = ScipyOptimizeDriver()
    prob.driver.options['optimizer'] = 'SLSQP'
    prob.driver.options['tol'] = 1e-6
    prob.driver.options['maxiter'] = 250
    #prob.driver.options['debug_print'] = ['desvars','ln_cons','nl_cons','totals']

    recorder = SqliteRecorder("aerostructMrhoi" + str(mrhoi) + "sk" +
                              str(skin) + "sr" + str(spar) + "sn" + str(span) +
                              "tc" + str(toverc) + ".db")
    prob.driver.add_recorder(recorder)

    # We could also just use prob.driver.recording_options['includes']=['*'] here, but for large meshes the database file becomes extremely large. So we just select the variables we need.
    prob.driver.recording_options['includes'] = [
        'alpha',
        'rho',
        'v',
        'cg',
        'alpha_gust',  #
        'AS_point_1.cg',
        'AS_point_0.cg',  #
        'AS_point_0.cg',  #ED
        'AS_point_0.coupled.wing_loads.loads',
        'AS_point_1.coupled.wing_loads.loads',  #
        'AS_point_2.coupled.wing_loads.loads',  #
        'AS_point_0.coupled.wing.normals',
        'AS_point_1.coupled.wing.normals',  #
        'AS_point_0.coupled.wing.widths',
        'AS_point_1.coupled.wing.widths',  #
        'AS_point_0.coupled.aero_states.wing_sec_forces',
        'AS_point_1.coupled.aero_states.wing_sec_forces',  #
        'AS_point_2.coupled.aero_states.wing_sec_forces',  #
        'AS_point_0.wing_perf.CL1',
        'AS_point_1.wing_perf.CL1',  #
        'AS_point_0.coupled.wing.S_ref',
        'AS_point_1.coupled.wing.S_ref',  #
        'wing.geometry.twist',
        'wing.geometry.mesh.taper.taper',
        'wing.geometry.mesh.stretch.span',
        'wing.geometry.mesh.scale_x.chord',
        'wing.mesh',
        'wing.skin_thickness',
        'wing.spar_thickness',
        'wing.t_over_c',
        'wing.structural_mass',
        'AS_point_0.wing_perf.vonmises',
        'AS_point_1.wing_perf.vonmises',  #
        'AS_point_0.coupled.wing.def_mesh',
        'AS_point_1.coupled.wing.def_mesh',  #
        'AS_point_0.total_perf.PV_mass',
        'AS_point_0.total_perf.total_weight',
        'AS_point_0.CL',
        'AS_point_0.CD',
        'yield',
        'point_masses',  #VMGM
        'point_mass_locations',  #VMGM
        'engine_location',  #VMGM
    ]

    prob.driver.recording_options['record_objectives'] = True
    prob.driver.recording_options['record_constraints'] = True
    prob.driver.recording_options['record_desvars'] = True
    prob.driver.recording_options['record_inputs'] = True

    # Set up the problem
    prob.setup()

    ##prob.run_model() #ED2
    ##data = prob.check_partials(out_stream=None, compact_print=True, method='cs') #ED2
    ##print(data)  #ED2

    prob.run_driver()
    ##print (prob.model.list_outputs(values=False, implicit=False))  #VMGM
    print('The wingbox mass (including the wing_weight_ratio) is',
          prob['wing.structural_mass'][0], '[kg]')
    endtime = time.time()
    totaltime = endtime - starttime
    print('computing time is', totaltime)
    print('co2 emissions are', prob['emitted_co2'][0])
    print('The wing surface is', prob['AS_point_0.coupled.wing.S_ref'][0],
          '[m2]')  #VMGM

    return prob['wing.structural_mass'][0], totaltime, prob['mrho'][0], prob[
        'emitted_co2'][0]
def get_problem(surface, match_number=0.84, height=7000):
    # Create the problem and assign the model group
    prob = Problem()

    grav_constant = data_heights[height]['g']
    v = match_number * data_heights[height]['speed']
    re = data_heights[height]['ro'] * data_heights[height][
        'speed'] * match_number / data_heights[height]['visc']
    rho = data_heights[height]['ro']
    speed_of_sound = data_heights[height]['speed']

    indep_var_comp = IndepVarComp()
    indep_var_comp.add_output('v', val=v, units='m/s')  # change this too
    indep_var_comp.add_output('alpha', val=5., units='deg')
    indep_var_comp.add_output('Mach_number', val=match_number)
    indep_var_comp.add_output('re', val=re, units='1/m')  # change this too
    indep_var_comp.add_output('rho', val=rho, 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=speed_of_sound,
                              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 = ScipyOptimizeDriver()
    prob.driver.options['tol'] = 1e-9

    recorder = 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)
    prob.setup(check=True)
    return prob