def setup_aerostruct(self): """ Specific method to add the necessary components to the problem for an aerostructural problem. """ # 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() # Add independent variables that do not belong to a specific component indep_vars = [ ('twist_cp', numpy.zeros(surface['num_twist'])), ('thickness_cp', numpy.ones(surface['num_thickness']) * numpy.max(surface['t'])), ('r', surface['r']), ('dihedral', surface['dihedral']), ('sweep', surface['sweep']), ('span', surface['span']), ('taper', surface['taper']) ] # Obtain the Jacobians to interpolate the data from the b-spline # control points jac_twist = get_bspline_mtx(surface['num_twist'], surface['num_y']) jac_thickness = get_bspline_mtx(surface['num_thickness'], surface['num_y'] - 1) # Add components to include in the surface's group tmp_group.add('indep_vars', IndepVarComp(indep_vars), promotes=['*']) tmp_group.add('twist_bsp', Bspline('twist_cp', 'twist', jac_twist), promotes=['*']) tmp_group.add('thickness_bsp', Bspline('thickness_cp', 'thickness', jac_thickness), promotes=['*']) tmp_group.add('tube', MaterialsTube(surface), promotes=['*']) # Add tmp_group to the problem with the name of the surface. name_orig = name name = name[:-1] exec(name + ' = tmp_group') exec('root.add("' + name + '", ' + name + ', 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('mesh', GeometryMesh(surface), promotes=['*']) 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() name = name_orig exec(name + ' = tmp_group') exec('coupled.add("' + name[:-1] + '", ' + name + ', promotes=[])') # Add a loads component to the coupled group exec('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), promotes=['*']) name = name_orig + 'perf' exec(name + ' = tmp_group') exec('root.add("' + name + '", ' + name + ', promotes=["rho", "v", "alpha", "Re", "M"])') # Add a single 'aero_states' component for the whole system within the # coupled group. coupled.add('aero_states', VLMStates(self.surfaces, self.prob_dict), 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'] # 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') root.connect('coupled.' + name + 'loads.loads', name + 'perf.loads') # 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 design variables root.connect(name[:-1] + '.dihedral', 'coupled.' + name[:-1] + '.dihedral') root.connect(name[:-1] + '.span', 'coupled.' + name[:-1] + '.span') root.connect(name[:-1] + '.sweep', 'coupled.' + name[:-1] + '.sweep') root.connect(name[:-1] + '.taper', 'coupled.' + name[:-1] + '.taper') root.connect(name[:-1] + '.twist', 'coupled.' + name[:-1] + '.twist') # Connect structural design variables root.connect(name[:-1] + '.A', 'coupled.' + name[:-1] + '.A') root.connect(name[:-1] + '.Iy', 'coupled.' + name[:-1] + '.Iy') root.connect(name[:-1] + '.Iz', 'coupled.' + name[:-1] + '.Iz') root.connect(name[:-1] + '.J', 'coupled.' + name[:-1] + '.J') # Connect performance calculation variables root.connect(name[:-1] + '.r', name + 'perf.r') root.connect(name[:-1] + '.A', name + 'perf.A') # Connection performance functional variables root.connect(name + 'perf.weight', 'fuelburn.' + name + 'weight') root.connect(name + 'perf.weight', 'eq_con.' + name + 'weight') root.connect(name + 'perf.L', 'eq_con.' + name + 'L') root.connect(name + 'perf.CL', 'fuelburn.' + name + 'CL') root.connect(name + 'perf.CD', 'fuelburn.' + name + 'CD') # Connect paramters from the 'coupled' group to the performance # group. root.connect('coupled.' + 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') # Set solver properties for the coupled group coupled.ln_solver = ScipyGMRES() coupled.ln_solver.options['iprint'] = 1 coupled.ln_solver.preconditioner = LinearGaussSeidel() coupled.aero_states.ln_solver = LinearGaussSeidel() coupled.nl_solver = NLGaussSeidel() coupled.nl_solver.options['iprint'] = 1 # Ensure that the groups are ordered correctly within the coupled group # so that a system with multiple surfaces is solved corretly. order_list = [] for surface in self.surfaces: order_list.append(surface['name'][:-1]) order_list.append('aero_states') for surface in self.surfaces: order_list.append(surface['name'] + 'loads') coupled.set_order(order_list) # 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']), ('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('fuelburn', FunctionalBreguetRange(self.surfaces, self.prob_dict), promotes=['fuelburn']) root.add('eq_con', FunctionalEquilibrium(self.surfaces, self.prob_dict), promotes=['eq_con', 'fuelburn']) # Actually set up the system self.setup_prob()
des_vars = [('twist', numpy.zeros(surface['num_y'])), ('span', surface['span']), ('r', r), ('thickness', thickness), ('loads', loads)] # Add components to the root problem. Note that each of the components # is defined with `promotes=['*']`, which means that all parameters # within that component are promoted to the root level so that all # other components can access them. For example, GeometryMesh creates # the mesh, and SpatialBeamStates uses this mesh information. # The data passing happens behind-the-scenes in OpenMDAO. root.add('des_vars', IndepVarComp(des_vars), promotes=['*']) root.add('mesh', GeometryMesh(surface), promotes=['*']) root.add('tube', MaterialsTube(surface), promotes=['*']) root.add('spatialbeamstates', SpatialBeamStates(surface), promotes=['*']) root.add('spatialbeamfuncs', SpatialBeamFunctionals(surface), promotes=['*']) # Instantiate an OpenMDAO problem and all the root group that we just # created to the problem. prob = Problem() prob.root = root # Set a driver for the optimization problem. Without a driver, OpenMDAO # doesn't know how to change the parameters to achieve an optimal solution. # There are a few options, but ScipyOptimizer and SLSQP are generally # the best options to use without installing additional packages. prob.driver = ScipyOptimizer() prob.driver.options['optimizer'] = 'SLSQP' prob.driver.options['disp'] = True
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() surface['r'] = surface['r'] / 5 surface['t'] = surface['r'] / 20 # 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. indep_vars = [ ('thickness_cp', numpy.ones(surface['num_thickness']) * numpy.max(surface['t'])), ('r', surface['r']), ('loads', surface['loads']) ] # Obtain the Jacobians to interpolate the data from the b-spline # control points jac_twist = get_bspline_mtx(surface['num_twist'], surface['num_y']) jac_thickness = get_bspline_mtx(surface['num_thickness'], surface['num_y'] - 1) # Add structural components to the surface-specific group tmp_group.add('indep_vars', IndepVarComp(indep_vars), promotes=['*']) tmp_group.add('twist_bsp', Bspline('twist_cp', 'twist', jac_twist), promotes=['*']) tmp_group.add('thickness_bsp', Bspline('thickness_cp', 'thickness', jac_thickness), promotes=['*']) tmp_group.add('mesh', GeometryMesh(surface), promotes=['*']) tmp_group.add('tube', MaterialsTube(surface), promotes=['*']) tmp_group.add('struct_states', SpatialBeamStates(surface), promotes=['*']) tmp_group.add('struct_funcs', SpatialBeamFunctionals(surface), promotes=['*']) # Add tmp_group to the problem with the name of the surface. # The default is 'wing'. nm = name name = name[:-1] exec(name + ' = tmp_group') exec('root.add("' + name + '", ' + name + ', promotes=[])') # Actually set up the problem self.setup_prob()
('rho', rho), ('r', r), ('t', t), ] indep_vars_comp = IndepVarComp(indep_vars) tube_comp = MaterialsTube(num_y) mesh_comp = GeometryMesh(mesh, aero_ind, num_twist) spatialbeamstates_comp = SpatialBeamStates(num_y, E, G) def_mesh_comp = TransferDisplacements(num_y) vlmstates_comp = VLMStates(num_y) loads_comp = TransferLoads(num_y) vlmfuncs_comp = VLMFunctionals(num_y, CL0, CD0) spatialbeamfuncs_comp = SpatialBeamFunctionals(num_y, E, G, stress, mrho) fuelburn_comp = FunctionalBreguetRange(W0, CT, a, R, M) eq_con_comp = FunctionalEquilibrium(W0) root.add('indep_vars', indep_vars_comp, promotes=['*']) root.add('tube', tube_comp, promotes=['*']) # Add components to the MDA here coupled = Group() coupled.add('mesh', mesh_comp, promotes=["*"]) coupled.add('spatialbeamstates', spatialbeamstates_comp, promotes=["*"]) coupled.add('def_mesh', def_mesh_comp, promotes=["*"]) coupled.add('vlmstates', vlmstates_comp, promotes=["*"]) coupled.add('loads', loads_comp, promotes=["*"]) ############################################################
coupled.ln_solver.preconditioner = LinearGaussSeidel() coupled.vlmstates.ln_solver = LinearGaussSeidel() coupled.spatialbeamstates.ln_solver = LinearGaussSeidel() # Direct Inversion Solver # coupled.ln_solver = DirectSolver() root.add('coupled', coupled, promotes=['*']) root.add('vlmfuncs', VLMFunctionals(num_y), promotes=['*']) root.add('spatialbeamfuncs', SpatialBeamFunctionals(num_y, E, G, stress, mrho), promotes=['*']) root.add('fuelburn', FunctionalBreguetRange(W0, CT, a, R, M), promotes=['*']) root.add('eq_con', FunctionalEquilibrium(W0), promotes=['*']) prob = Problem() prob.root = root coupled.nl_solver.options['iprint'] = 1 # makes OpenMDAO print out solver convergence data coupled.ln_solver.options['iprint'] = 1 # makes OpenMDAO print out solver convergence data prob.driver.add_recorder(SqliteRecorder('prob1d.db'))
# done for you as examples ############################################################ indep_vars_comp = IndepVarComp(indep_vars) twist_comp = Bspline('twist_cp', 'twist', jac_twist) thickness_comp = Bspline('thickness_cp', 'thickness', jac_thickness) tube_comp = MaterialsTube(fem_ind) mesh_comp = GeometryMesh(mesh, aero_ind) spatialbeamstates_comp = SpatialBeamStates(aero_ind, fem_ind, E, G) def_mesh_comp = TransferDisplacements(aero_ind, fem_ind) vlmstates_comp = VLMStates(aero_ind) loads_comp = TransferLoads(aero_ind, fem_ind) vlmfuncs_comp = VLMFunctionals(aero_ind, CL0, CD0) spatialbeamfuncs_comp = SpatialBeamFunctionals(aero_ind, fem_ind, E, G, stress, mrho) fuelburn_comp = FunctionalBreguetRange(W0, CT, a, R, M, aero_ind) eq_con_comp = FunctionalEquilibrium(W0, aero_ind) ############################################################ ############################################################ root.add('indep_vars', indep_vars_comp, promotes=['*']) root.add('twist_bsp', twist_comp, promotes=['*']) root.add('thickness_bsp', thickness_comp, promotes=['*'])
def setup_aerostruct(self): """ Specific method to add the necessary components to the problem for an aerostructural problem. """ # 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() # Add independent variables that do not belong to a specific component indep_vars = [ (name + 'twist_cp', numpy.zeros(surface['num_twist'])), (name + 'thickness_cp', numpy.ones(surface['num_thickness']) * numpy.max(surface['t'])), (name + 'r', surface['r']), (name + 'dihedral', surface['dihedral']), (name + 'sweep', surface['sweep']), (name + 'span', surface['span']), (name + 'taper', surface['taper']) ] # Obtain the Jacobians to interpolate the data from the b-spline # control points jac_twist = get_bspline_mtx(surface['num_twist'], surface['num_y']) jac_thickness = get_bspline_mtx(surface['num_thickness'], surface['num_y'] - 1) # Add components to include in the '_pre_solve' group tmp_group.add('indep_vars', IndepVarComp(indep_vars), promotes=['*']) tmp_group.add('twist_bsp', Bspline(name + 'twist_cp', name + 'twist', jac_twist), promotes=['*']) tmp_group.add('thickness_bsp', Bspline(name + 'thickness_cp', name + 'thickness', jac_thickness), promotes=['*']) tmp_group.add('tube', MaterialsTube(surface), promotes=['*']) # Add tmp_group to the problem with the name of the surface and # '_pre_solve' appended. name_orig = name #.strip('_') name = name + 'pre_solve' exec(name + ' = tmp_group') exec('root.add("' + name + '", ' + name + ', promotes=["*"])') # Add components to the 'coupled' group for each surface tmp_group = Group() tmp_group.add('mesh', GeometryMesh(surface), promotes=['*']) tmp_group.add('def_mesh', TransferDisplacements(surface), promotes=['*']) tmp_group.add('vlmgeom', VLMGeometry(surface), promotes=['*']) tmp_group.add('spatialbeamstates', SpatialBeamStates(surface), promotes=['*']) tmp_group.spatialbeamstates.ln_solver = LinearGaussSeidel() name = name_orig + 'group' exec(name + ' = tmp_group') exec('coupled.add("' + name + '", ' + name + ', promotes=["*"])') # Add a loads component to the coupled group exec('coupled.add("' + name_orig + 'loads' + '", ' + 'TransferLoads(surface)' + ', promotes=["*"])') # Add a '_post_solve' group which evaluates the data after solving # the coupled system tmp_group = Group() tmp_group.add('spatialbeamfuncs', SpatialBeamFunctionals(surface), promotes=['*']) tmp_group.add('vlmfuncs', VLMFunctionals(surface), promotes=['*']) name = name_orig + 'post_solve' exec(name + ' = tmp_group') exec('root.add("' + name + '", ' + name + ', promotes=["*"])') # Add a single 'VLMStates' component for the whole system coupled.add('vlmstates', VLMStates(self.surfaces, self.prob_dict), promotes=['*']) # Set solver properties for the coupled group coupled.ln_solver = ScipyGMRES() coupled.ln_solver.options['iprint'] = 1 coupled.ln_solver.preconditioner = LinearGaussSeidel() coupled.vlmstates.ln_solver = LinearGaussSeidel() coupled.nl_solver = NLGaussSeidel() coupled.nl_solver.options['iprint'] = 1 # Ensure that the groups are ordered correctly within the coupled group order_list = [] for surface in self.surfaces: order_list.append(surface['name'] + 'group') order_list.append('vlmstates') for surface in self.surfaces: order_list.append(surface['name'] + 'loads') coupled.set_order(order_list) # Add the coupled group to the root problem root.add('coupled', coupled, promotes=['*']) # 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']), ('rho', self.prob_dict['rho'])] root.add('prob_vars', IndepVarComp(prob_vars), promotes=['*']) # Add functionals to evaluate performance of the system root.add('fuelburn', FunctionalBreguetRange(self.surfaces, self.prob_dict), promotes=['*']) root.add('eq_con', FunctionalEquilibrium(self.surfaces, self.prob_dict), promotes=['*']) self.setup_prob()