def test_deprecated_solver_names(self): class DummySolver(): pass model = Group() # check nl_solver setter & getter msg = "The 'nl_solver' attribute provides backwards compatibility " \ "with OpenMDAO 1.x ; use 'nonlinear_solver' instead." with assert_warning(DeprecationWarning, msg): model.nl_solver = DummySolver() with assert_warning(DeprecationWarning, msg): solver = model.nl_solver self.assertTrue(isinstance(solver, DummySolver)) # check ln_solver setter & getter msg = "The 'ln_solver' attribute provides backwards compatibility " \ "with OpenMDAO 1.x ; use 'linear_solver' instead." with assert_warning(DeprecationWarning, msg): model.ln_solver = DummySolver() with assert_warning(DeprecationWarning, msg): solver = model.ln_solver self.assertTrue(isinstance(solver, DummySolver))
def test_deprecated_solver_names(self): class DummySolver(): pass model = Group() # check nl_solver setter with warnings.catch_warnings(record=True) as w: model.nl_solver = DummySolver() self.assertEqual(len(w), 1) self.assertTrue(issubclass(w[0].category, DeprecationWarning)) self.assertEqual(str(w[0].message), "The 'nl_solver' attribute provides backwards compatibility " "with OpenMDAO 1.x ; use 'nonlinear_solver' instead.") # check nl_solver getter with warnings.catch_warnings(record=True) as w: solver = model.nl_solver self.assertEqual(len(w), 1) self.assertTrue(issubclass(w[0].category, DeprecationWarning)) self.assertEqual(str(w[0].message), "The 'nl_solver' attribute provides backwards compatibility " "with OpenMDAO 1.x ; use 'nonlinear_solver' instead.") self.assertTrue(isinstance(solver, DummySolver)) # check ln_solver setter with warnings.catch_warnings(record=True) as w: model.ln_solver = DummySolver() self.assertEqual(len(w), 1) self.assertTrue(issubclass(w[0].category, DeprecationWarning)) self.assertEqual(str(w[0].message), "The 'ln_solver' attribute provides backwards compatibility " "with OpenMDAO 1.x ; use 'linear_solver' instead.") # check ln_solver getter with warnings.catch_warnings(record=True) as w: solver = model.ln_solver self.assertEqual(len(w), 1) self.assertTrue(issubclass(w[0].category, DeprecationWarning)) self.assertEqual(str(w[0].message), "The 'ln_solver' attribute provides backwards compatibility " "with OpenMDAO 1.x ; use 'linear_solver' instead.") self.assertTrue(isinstance(solver, DummySolver))
root.add('vlmfuncs', vlmfuncs_comp, promotes=['*']) # Add components to the coupled MDA here coupled = Group() coupled.add('mesh', mesh_comp, promotes=["*"]) ############################################################ # Problem 2b: # Try different nonlinear solvers on the coupled and root groups. # Nonlinear Gauss Seidel is included as an example. # Examine http://openmdao.readthedocs.io/en/latest/srcdocs/packages/openmdao.solvers.html # to see syntax for other options. ############################################################ ## Nonlinear Gauss Seidel on the coupled group coupled.nl_solver = NLGaussSeidel() coupled.nl_solver.options['iprint'] = 1 coupled.nl_solver.options['atol'] = 1e-5 coupled.nl_solver.options['rtol'] = 1e-12 ############################################################ # Problem 2c: # Try different linear solvers for the coupled group. # Again examine http://openmdao.readthedocs.io/en/latest/srcdocs/packages/openmdao.solvers.html # for linear solver options. ############################################################ ## Krylov Solver - LNGS preconditioning coupled.ln_solver = ScipyGMRES() coupled.ln_solver.options['iprint'] = 1 coupled.ln_solver.preconditioner = LinearGaussSeidel()
#Hi-Fi Group mda_h = Group() #Add disciplines to the high-fidelity group mda_h.add('mult_filter_h', Filter(ns, fidelity)) mda_h.add('displacement_transfer_h', DisplacementTransfer(na_h, ns)) mda_h.add('aerodynamics_h', Panair(na_h, network_info_h, case_name_h, aero_template_h, sym_plane_index=sym_plane_index), promotes=['V','Sw','alpha','rho_a','CL','CDi','apoints_coord']) mda_h.add('load_transfer_h', LoadTransfer(na_h, ns)) mda_h.add('structures_h', NastranStatic(node_id, node_id_all, n_stress, tn, mn, sn, case_name_h, an=an), promotes=['mass','VMStress','n','m','t','s','Ix','Iy','node_coord_all','a']) #Inner interpolation method mda_h.add('inter_h', Interpolation(na_h, ns, function = function_type, bias = bias_inter), promotes=['apoints_coord','node_coord']) #Define solver type and tolerance for MDA Lo-Fi mda_l.nl_solver = NLGaussSeidel() #The solver execution limit is used to control fidelity levels if fidelity == 'high': mda_l.nl_solver.options['maxiter'] = 0 #No Lo-Fi iterations mda_l.nl_solver.options['rutol'] = 1.e-1 mda_l.nl_solver.options['use_aitken'] = True mda_l.nl_solver.options['aitken_alpha_min'] = 0.1 mda_l.nl_solver.options['aitken_alpha_max'] = 1.5 mda_l.ln_solver = ScipyGMRES() #Define solver type and tolerance for MDA Hi-Fi mda_h.nl_solver = NLGaussSeidel() #The solver execution limit is used to control fidelity levels if fidelity == 'low':
# root.add('agrr', Aggregation(n_stress,p,function), promotes=['*']) mda = Group() #Add disciplines to the group mda.add('displacement_transfer', DisplacementTransfer(na, ns), promotes=['*']) mda.add('aerodynamics', Panair(na, network_info), promotes=['*']) mda.add('load_transfer', LoadTransfer(na, ns), promotes=['*']) mda.add('structures', NastranStatic(node_id, node_id_all, n_stress, tn, mn), promotes=['*']) #Define solver type and tolerance for MDA mda.nl_solver = NLGaussSeidel() # mda.nl_solver.options['rtol'] = 1.e-1 mda.nl_solver.options['maxiter'] = 15 mda.nl_solver.options['rutol'] = 1.e-2 mda.nl_solver.options['use_aitken'] = True mda.nl_solver.options['aitken_alpha_min'] = 0.1 mda.nl_solver.options['aitken_alpha_max'] = 1.5 mda.ln_solver = ScipyGMRES() root.add('mda_group', mda, promotes=['*']) # top.root.mda_group.deriv_options['type'] = 'fd' # top.root.mda_group.deriv_options['step_size'] = 1.0e-1 #Recorder recorder = SqliteRecorder('opti_g_30')
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()
## Newton Solver on the coupled group # coupled.nl_solver = Newton() # coupled.nl_solver.options['iprint'] = 1 # coupled.nl_solver.line_search.options['iprint'] = 1 ## Hybrid NLGS-Newton on the coupled group # coupled.nl_solver = HybridGSNewton() # coupled.nl_solver.nlgs.options['iprint'] = 1 # coupled.nl_solver.nlgs.options['maxiter'] = 5 # coupled.nl_solver.newton.options['atol'] = 1e-7 # coupled.nl_solver.newton.options['rtol'] = 1e-7 # coupled.nl_solver.newton.options['iprint'] = 1 # Newton solver on the root group root.nl_solver = Newton() root.nl_solver.options['iprint'] = 1 root.nl_solver.line_search.options['iprint'] = 1 root.ln_solver = ScipyGMRES() root.ln_solver.options['iprint'] = 1 root.ln_solver.preconditioner = LinearGaussSeidel() coupled.vlmstates.ln_solver = LinearGaussSeidel() coupled.spatialbeamstates.ln_solver = LinearGaussSeidel() ############################################### # Don't change the linear solver confuguration ############################################### # linear solver configuration # coupled.ln_solver = ScipyGMRES() # coupled.ln_solver.options['iprint'] = 1 # coupled.ln_solver.preconditioner = LinearGaussSeidel()
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=["*"]) ## Hybrid NLGS-Newton on the coupled group coupled.nl_solver = HybridGSNewton() coupled.nl_solver.nlgs.options['iprint'] = 1 coupled.nl_solver.nlgs.options['maxiter'] = 5 coupled.nl_solver.newton.options['atol'] = 1e-6 coupled.nl_solver.newton.options['rtol'] = 1e-6 coupled.nl_solver.newton.options['iprint'] = 1 # Krylov Solver - LNGS preconditioning coupled.ln_solver = ScipyGMRES() coupled.ln_solver.options['iprint'] = 1 coupled.ln_solver.preconditioner = LinearGaussSeidel() coupled.vlmstates.ln_solver = LinearGaussSeidel() coupled.spatialbeamstates.ln_solver = LinearGaussSeidel()
GeometryMesh(mesh, aero_ind, num_twist), promotes=['*']) coupled.add('def_mesh', TransferDisplacements(num_y), promotes=['*']) coupled.add('vlmstates', VLMStates(num_y), promotes=['*']) coupled.add('loads', TransferLoads(num_y), promotes=['*']) coupled.add('spatialbeamstates', SpatialBeamStates(num_y, cons, E, G), promotes=['*']) coupled.nl_solver = Newton() coupled.nl_solver.options['iprint'] = 1 coupled.nl_solver.line_search.options['iprint'] = 1 # LNGS Solver # coupled.ln_solver = LinearGaussSeidel() # coupled.ln_solver.options['maxiter'] = 100 # Krylov Solver - No preconditioning # coupled.ln_solver = ScipyGMRES() # coupled.ln_solver.options['iprint'] = 1 # Krylov Solver - LNGS preconditioning coupled.ln_solver = ScipyGMRES() coupled.ln_solver.options['iprint'] = 1 coupled.ln_solver.preconditioner = LinearGaussSeidel()
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()
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()