Example #1
0
    def setup_aerostruct(self):
        """
        Specific method to add the necessary components to the problem for an
        aerostructural problem.

        Because this code has been extended to work for multiple aerostructural
        surfaces, a good portion of it is spent doing the bookkeeping for parameter
        passing and ensuring that each component modifies the correct data.
        """

        # Set the problem name if the user doesn't
        if 'prob_name' not in self.prob_dict.keys():
            self.prob_dict['prob_name'] = 'aerostruct'

        # Create the base root-level group
        root = Group()
        coupled = Group()

        # Create the problem and assign the root group
        self.prob = Problem()
        self.prob.root = root

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

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

            # Strip the surface names from the desvars list and save this
            # modified list as self.desvars
            desvar_names = []
            for desvar in self.desvars.keys():

                # Check to make sure that the surface's name is in the design
                # variable and only add the desvar to the list if it corresponds
                # to this surface.
                if name[:-1] in desvar:
                    desvar_names.append(''.join(desvar.split('.')[1:]))

            # Add independent variables that do not belong to a specific component
            indep_vars = []
            for var in surface['geo_vars']:
                if var in desvar_names or var in surface['initial_geo'] or 'thickness' in var:
                    indep_vars.append((var, surface[var]))

            # Add components to include in the surface's group
            tmp_group.add('indep_vars',
                     IndepVarComp(indep_vars),
                     promotes=['*'])
            tmp_group.add('tube',
                     MaterialsTube(surface),
                     promotes=['*'])
            tmp_group.add('mesh',
                     GeometryMesh(surface, self.desvars),
                     promotes=['*'])
            tmp_group.add('struct_setup',
                     SpatialBeamSetup(surface),
                     promotes=['*'])

            # Add bspline components for active bspline geometric variables.
            # We only add the component if the corresponding variable is a desvar,
            # a special parameter (radius), or if the user or geometry provided
            # an initial distribution.
            for var in surface['bsp_vars']:
                if var in desvar_names or var in surface['initial_geo'] or 'thickness' in var:
                    n_pts = surface['num_y']
                    if var in ['thickness_cp', 'radius_cp']:
                        n_pts -= 1
                    trunc_var = var.split('_')[0]
                    tmp_group.add(trunc_var + '_bsp',
                             Bspline(var, trunc_var, surface['num_'+var], n_pts),
                             promotes=['*'])

            # Add monotonic constraints for selected variables
            if surface['monotonic_con'] is not None:
                if type(surface['monotonic_con']) is not list:
                    surface['monotonic_con'] = [surface['monotonic_con']]
                for var in surface['monotonic_con']:
                    tmp_group.add('monotonic_' + var,
                        MonotonicConstraint(var, surface), promotes=['*'])

            # Add tmp_group to the problem with the name of the surface.
            name_orig = name
            name = name[:-1]
            root.add(name, tmp_group, promotes=[])

            # Add components to the 'coupled' group for each surface.
            # The 'coupled' group must contain all components and parameters
            # needed to converge the aerostructural system.
            tmp_group = Group()
            tmp_group.add('def_mesh',
                     TransferDisplacements(surface),
                     promotes=['*'])
            tmp_group.add('aero_geom',
                     VLMGeometry(surface),
                     promotes=['*'])
            tmp_group.add('struct_states',
                     SpatialBeamStates(surface),
                     promotes=['*'])
            tmp_group.struct_states.ln_solver = LinearGaussSeidel()
            tmp_group.struct_states.ln_solver.options['atol'] = 1e-20

            name = name_orig
            coupled.add(name[:-1], tmp_group, promotes=[])

            # Add a loads component to the coupled group
            coupled.add(name_orig + 'loads', TransferLoads(surface), promotes=[])

            # Add a performance group which evaluates the data after solving
            # the coupled system
            tmp_group = Group()

            tmp_group.add('struct_funcs',
                     SpatialBeamFunctionals(surface),
                     promotes=['*'])
            tmp_group.add('aero_funcs',
                     VLMFunctionals(surface, self.prob_dict),
                     promotes=['*'])

            root.add(name_orig + 'perf', tmp_group, promotes=["rho", "v", "alpha", "re", "M"])

            root.add_metadata(surface['name'] + 'yield_stress', surface['yield'])
            root.add_metadata(surface['name'] + 'fem_origin', surface['fem_origin'])

        # Add a single 'aero_states' component for the whole system within the
        # coupled group.
        coupled.add('aero_states',
                 VLMStates(self.surfaces),
                 promotes=['v', 'alpha', 'rho'])

        # Explicitly connect parameters from each surface's group and the common
        # 'aero_states' group.
        for surface in self.surfaces:
            name = surface['name']

            root.connect(name[:-1] + '.K', 'coupled.' + name[:-1] + '.K')

            # Perform the connections with the modified names within the
            # 'aero_states' group.
            root.connect('coupled.' + name[:-1] + '.def_mesh', 'coupled.aero_states.' + name + 'def_mesh')
            root.connect('coupled.' + name[:-1] + '.b_pts', 'coupled.aero_states.' + name + 'b_pts')
            root.connect('coupled.' + name[:-1] + '.c_pts', 'coupled.aero_states.' + name + 'c_pts')
            root.connect('coupled.' + name[:-1] + '.normals', 'coupled.aero_states.' + name + 'normals')

            # Connect the results from 'aero_states' to the performance groups
            root.connect('coupled.aero_states.' + name + 'sec_forces', name + 'perf' + '.sec_forces')

            # Connect the results from 'coupled' to the performance groups
            root.connect('coupled.' + name[:-1] + '.def_mesh', 'coupled.' + name + 'loads.def_mesh')
            root.connect('coupled.aero_states.' + name + 'sec_forces', 'coupled.' + name + 'loads.sec_forces')

            # Connect the output of the loads component with the FEM
            # displacement parameter. This links the coupling within the coupled
            # group that necessitates the subgroup solver.
            root.connect('coupled.' + name + 'loads.loads', 'coupled.' + name[:-1] + '.loads')

            # Connect aerodyamic mesh to coupled group mesh
            root.connect(name[:-1] + '.mesh', 'coupled.' + name[:-1] + '.mesh')

            # Connect performance calculation variables
            root.connect(name[:-1] + '.radius', name + 'perf.radius')
            root.connect(name[:-1] + '.A', name + 'perf.A')
            root.connect(name[:-1] + '.thickness', name + 'perf.thickness')

            # Connection performance functional variables
            root.connect(name + 'perf.structural_weight', 'total_perf.' + name + 'structural_weight')
            root.connect(name + 'perf.L', 'total_perf.' + name + 'L')
            root.connect(name + 'perf.CL', 'total_perf.' + name + 'CL')
            root.connect(name + 'perf.CD', 'total_perf.' + name + 'CD')
            root.connect('coupled.aero_states.' + name + 'sec_forces', 'total_perf.' + name + 'sec_forces')

            # Connect parameters from the 'coupled' group to the performance
            # groups for the individual surfaces.
            root.connect(name[:-1] + '.nodes', name + 'perf.nodes')
            root.connect('coupled.' + name[:-1] + '.disp', name + 'perf.disp')
            root.connect('coupled.' + name[:-1] + '.S_ref', name + 'perf.S_ref')
            root.connect('coupled.' + name[:-1] + '.widths', name + 'perf.widths')
            root.connect('coupled.' + name[:-1] + '.lengths', name + 'perf.lengths')
            root.connect('coupled.' + name[:-1] + '.cos_sweep', name + 'perf.cos_sweep')

            # Connect parameters from the 'coupled' group to the total performance group.
            root.connect('coupled.' + name[:-1] + '.S_ref', 'total_perf.' + name + 'S_ref')
            root.connect('coupled.' + name[:-1] + '.widths', 'total_perf.' + name + 'widths')
            root.connect('coupled.' + name[:-1] + '.chords', 'total_perf.' + name + 'chords')
            root.connect('coupled.' + name[:-1] + '.b_pts', 'total_perf.' + name + 'b_pts')
            root.connect(name + 'perf.cg_location', 'total_perf.' + name + 'cg_location')

        # Set solver properties for the coupled group
        coupled.ln_solver = ScipyGMRES()
        coupled.ln_solver.preconditioner = LinearGaussSeidel()
        coupled.aero_states.ln_solver = LinearGaussSeidel()
        coupled.nl_solver = NLGaussSeidel()

        # This is only available in the most recent version of OpenMDAO.
        # It may help converge tightly coupled systems when using NLGS.
        try:
            coupled.nl_solver.options['use_aitken'] = True
            coupled.nl_solver.options['aitken_alpha_min'] = 0.01
            # coupled.nl_solver.options['aitken_alpha_max'] = 0.5
        except:
            pass

        if self.prob_dict['print_level'] == 2:
            coupled.ln_solver.options['iprint'] = 1
        if self.prob_dict['print_level']:
            coupled.nl_solver.options['iprint'] = 1

        # Add the coupled group to the root problem
        root.add('coupled', coupled, promotes=['v', 'alpha', 'rho'])

        # Add problem information as an independent variables component
        prob_vars = [('v', self.prob_dict['v']),
            ('alpha', self.prob_dict['alpha']),
            ('M', self.prob_dict['M']),
            ('re', self.prob_dict['Re']/self.prob_dict['reynolds_length']),
            ('rho', self.prob_dict['rho'])]

        root.add('prob_vars',
                 IndepVarComp(prob_vars),
                 promotes=['*'])

        # Add functionals to evaluate performance of the system.
        # Note that only the interesting results are promoted here; not all
        # of the parameters.
        root.add('total_perf',
                 TotalPerformance(self.surfaces, self.prob_dict),
                 promotes=['L_equals_W', 'fuelburn', 'CM', 'CL', 'CD', 'v', 'rho', 'cg', 'weighted_obj', 'total_weight'])

        # Actually set up the system
        self.setup_prob()
Example #2
0
    def setup_struct(self):
        """
        Specific method to add the necessary components to the problem for a
        structural problem.
        """

        # Set the problem name if the user doesn't
        if 'prob_name' not in self.prob_dict.keys():
            self.prob_dict['prob_name'] = 'struct'

        # Create the base root-level group
        root = Group()

        # Create the problem and assign the root group
        self.prob = Problem()
        self.prob.root = root

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

            # Get the surface name and create a group to contain components
            # only for this surface.
            # This group's name is whatever the surface's name is.
            # The default is 'wing'.
            name = surface['name']
            tmp_group = Group()

            # Strip the surface names from the desvars list and save this
            # modified list as self.desvars
            desvar_names = []
            for desvar in self.desvars.keys():

                # Check to make sure that the surface's name is in the design
                # variable and only add the desvar to the list if it corresponds
                # to this surface.
                if name[:-1] in desvar:
                    desvar_names.append(''.join(desvar.split('.')[1:]))

            # Add independent variables that do not belong to a specific component.
            # Note that these are the only ones necessary for structual-only
            # analysis and optimization.
            # Here we check and only add the variables that are desvars or a
            # special var, radius, which is necessary to compute weight.
            indep_vars = [('loads', surface['loads'])]
            for var in surface['geo_vars']:
                if var in desvar_names or 'thickness' in var or var in surface['initial_geo']:
                    indep_vars.append((var, surface[var]))

            # Add structural components to the surface-specific group
            tmp_group.add('indep_vars',
                     IndepVarComp(indep_vars),
                     promotes=['*'])
            tmp_group.add('mesh',
                     GeometryMesh(surface, self.desvars),
                     promotes=['*'])
            tmp_group.add('tube',
                     MaterialsTube(surface),
                     promotes=['*'])
            tmp_group.add('struct_setup',
                     SpatialBeamSetup(surface),
                     promotes=['*'])
            tmp_group.add('struct_states',
                     SpatialBeamStates(surface),
                     promotes=['*'])
            tmp_group.add('struct_funcs',
                     SpatialBeamFunctionals(surface),
                     promotes=['*'])

            # Add bspline components for active bspline geometric variables.
            # We only add the component if the corresponding variable is a desvar
            # or special (radius).
            for var in surface['bsp_vars']:
                if var in desvar_names or var in surface['initial_geo'] or 'thickness' in var:
                    n_pts = surface['num_y']
                    if var in ['thickness_cp', 'radius_cp']:
                        n_pts -= 1
                    trunc_var = var.split('_')[0]
                    tmp_group.add(trunc_var + '_bsp',
                             Bspline(var, trunc_var, surface['num_'+var], n_pts),
                             promotes=['*'])

            # Add tmp_group to the problem with the name of the surface.
            # The default is 'wing'.
            root.add(name[:-1], tmp_group, promotes=[])

            root.add_metadata(surface['name'] + 'yield_stress', surface['yield'])
            root.add_metadata(surface['name'] + 'fem_origin', surface['fem_origin'])

        # Actually set up the problem
        self.setup_prob()