def setup(self): surfaces = self.options['surfaces'] for surface in surfaces: name = surface['name'] self.connect(name + '.normals', 'aero_states.' + name + '_normals') # self.connect(name + '.b_pts', 'aero_states.' + name + '_b_pts') # self.connect(name + '.c_pts', 'aero_states.' + name + '_c_pts') # self.connect(name + '.cos_sweep', 'aero_states.' + name + '_cos_sweep') # self.connect(name + '.widths', 'aero_states.' + name + '_widths') # Connect the results from 'aero_states' to the performance groups self.connect('aero_states.' + name + '_sec_forces', name + '_perf' + '.sec_forces') # Connect S_ref for performance calcs self.connect(name + '.S_ref', name + '_perf.S_ref') self.connect(name + '.widths', name + '_perf.widths') self.connect(name + '.chords', name + '_perf.chords') self.connect(name + '.lengths', name + '_perf.lengths') self.connect(name + '.cos_sweep', name + '_perf.cos_sweep') # Connect S_ref for performance calcs self.connect(name + '.S_ref', 'total_perf.' + name + '_S_ref') self.connect(name + '.widths', 'total_perf.' + name + '_widths') self.connect(name + '.chords', 'total_perf.' + name + '_chords') self.connect(name + '.b_pts', 'total_perf.' + name + '_b_pts') self.connect(name + '_perf' + '.CL', 'total_perf.' + name + '_CL') self.connect(name + '_perf' + '.CD', 'total_perf.' + name + '_CD') self.connect('aero_states.' + name + '_sec_forces', 'total_perf.' + name + '_sec_forces') self.add_subsystem(name, VLMGeometry(surface=surface)) # Add a single 'aero_states' component that solves for the circulations # and forces from all the surfaces. # While other components only depends on a single surface, # this component requires information from all surfaces because # each surface interacts with the others. aero_states = VLMStates(surfaces=surfaces) aero_states.linear_solver = LinearRunOnce() self.add_subsystem('aero_states', aero_states, promotes_inputs=['v', 'alpha', 'rho'], promotes_outputs=['circulations']) # Explicitly connect parameters from each surface's group and the common # 'aero_states' group. # This is necessary because the VLMStates component requires information # from each surface, but this information is stored within each # surface's group. for surface in surfaces: self.add_subsystem(surface['name'] +'_perf', VLMFunctionals(surface=surface), promotes_inputs=["v", "alpha", "M", "re", "rho"]) self.add_subsystem('total_perf', TotalAeroPerformance(surfaces=surfaces), promotes_inputs=['v', 'rho', 'cg', 'S_ref_total'], promotes_outputs=['CM', 'CL', 'CD'])
def setup(self): surfaces = self.options['surfaces'] coupled = Group() for surface in surfaces: name = surface['name'] # 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. coupled.connect(name + '_loads.loads', name + '.loads') # Perform the connections with the modified names within the # 'aero_states' group. coupled.connect(name + '.normals', 'aero_states.' + name + '_normals') coupled.connect(name + '.def_mesh', 'aero_states.' + name + '_def_mesh') # Connect the results from 'coupled' to the performance groups coupled.connect(name + '.def_mesh', name + '_loads.def_mesh') coupled.connect('aero_states.' + name + '_sec_forces', name + '_loads.sec_forces') # Connect the results from 'aero_states' to the performance groups self.connect('coupled.aero_states.' + name + '_sec_forces', name + '_perf' + '.sec_forces') # Connection performance functional variables self.connect(name + '_perf.CL', 'total_perf.' + name + '_CL') self.connect(name + '_perf.CD', 'total_perf.' + name + '_CD') self.connect('coupled.aero_states.' + name + '_sec_forces', 'total_perf.' + name + '_sec_forces') self.connect('coupled.' + name + '.chords', name + '_perf.aero_funcs.chords') # Connect parameters from the 'coupled' group to the performance # groups for the individual surfaces. self.connect('coupled.' + name + '.disp', name + '_perf.disp') self.connect('coupled.' + name + '.S_ref', name + '_perf.S_ref') self.connect('coupled.' + name + '.widths', name + '_perf.widths') # self.connect('coupled.' + name + '.chords', name + '_perf.chords') self.connect('coupled.' + name + '.lengths', name + '_perf.lengths') self.connect('coupled.' + name + '.cos_sweep', name + '_perf.cos_sweep') # Connect parameters from the 'coupled' group to the total performance group. self.connect('coupled.' + name + '.S_ref', 'total_perf.' + name + '_S_ref') self.connect('coupled.' + name + '.widths', 'total_perf.' + name + '_widths') self.connect('coupled.' + name + '.chords', 'total_perf.' + name + '_chords') self.connect('coupled.' + name + '.b_pts', 'total_perf.' + name + '_b_pts') # Add components to the 'coupled' group for each surface. # The 'coupled' group must contain all components and parameters # needed to converge the aerostructural system. coupled_AS_group = CoupledAS(surface=surface) if surface['distributed_fuel_weight']: promotes = ['load_factor'] else: promotes = [] coupled.add_subsystem(name, coupled_AS_group, promotes_inputs=promotes) # Add a single 'aero_states' component for the whole system within the # coupled group. coupled.add_subsystem('aero_states', VLMStates(surfaces=surfaces), promotes_inputs=['v', 'alpha', 'rho']) # Explicitly connect parameters from each surface's group and the common # 'aero_states' group. for surface in surfaces: name = surface['name'] # Add a loads component to the coupled group coupled.add_subsystem(name + '_loads', LoadTransfer(surface=surface)) """ ### Change the solver settings here ### """ # Set solver properties for the coupled group # coupled.linear_solver = ScipyIterativeSolver() # coupled.linear_solver.precon = LinearRunOnce() coupled.nonlinear_solver = NonlinearBlockGS(use_aitken=True) coupled.nonlinear_solver.options['maxiter'] = 100 coupled.nonlinear_solver.options['atol'] = 1e-7 coupled.nonlinear_solver.options['rtol'] = 1e-30 # coupled.linear_solver = DirectSolver() coupled.linear_solver = DirectSolver(assemble_jac=True) coupled.options['assembled_jac_type'] = 'csc' # coupled.nonlinear_solver = NewtonSolver(solve_subsystems=True) # coupled.nonlinear_solver.options['maxiter'] = 50 coupled.nonlinear_solver.options['iprint'] = 2 """ ### End change of solver settings ### """ # Add the coupled group to the model problem self.add_subsystem('coupled', coupled, promotes_inputs=['v', 'alpha', 'rho']) for surface in surfaces: name = surface['name'] # Add a performance group which evaluates the data after solving # the coupled system perf_group = CoupledPerformance(surface=surface) self.add_subsystem(name + '_perf', perf_group, promotes_inputs=['rho', 'v', 'alpha', 're', 'Mach_number']) # Add functionals to evaluate performance of the system. # Note that only the interesting results are promoted here; not all # of the parameters. self.add_subsystem('total_perf', TotalPerformance(surfaces=surfaces, user_specified_Sref=self.options['user_specified_Sref'], internally_connect_fuelburn=self.options['internally_connect_fuelburn']), promotes_inputs=['v', 'rho', 'empty_cg', 'total_weight', 'CT', 'speed_of_sound', 'R', 'Mach_number', 'W0', 'load_factor', 'S_ref_total'], promotes_outputs=['L_equals_W', 'fuelburn', 'CL', 'CD', 'CM', 'cg'])
def setup(self): surfaces = self.options['surfaces'] rotational = self.options['rotational'] # Loop through each surface and connect relevant parameters for surface in surfaces: name = surface['name'] self.connect(name + '.normals', 'aero_states.' + name + '_normals') # Connect the results from 'aero_states' to the performance groups self.connect('aero_states.' + name + '_sec_forces', name + '_perf' + '.sec_forces') # Connect S_ref for performance calcs self.connect(name + '.S_ref', name + '_perf.S_ref') self.connect(name + '.widths', name + '_perf.widths') self.connect(name + '.chords', name + '_perf.chords') self.connect(name + '.lengths', name + '_perf.lengths') self.connect(name + '.cos_sweep', name + '_perf.cos_sweep') # Connect S_ref for performance calcs self.connect(name + '.S_ref', 'total_perf.' + name + '_S_ref') self.connect(name + '.widths', 'total_perf.' + name + '_widths') self.connect(name + '.chords', 'total_perf.' + name + '_chords') self.connect(name + '.b_pts', 'total_perf.' + name + '_b_pts') self.connect(name + '_perf' + '.CL', 'total_perf.' + name + '_CL') self.connect(name + '_perf' + '.CD', 'total_perf.' + name + '_CD') self.connect('aero_states.' + name + '_sec_forces', 'total_perf.' + name + '_sec_forces') self.add_subsystem(name, VLMGeometry(surface=surface)) # Add a single 'aero_states' component that solves for the circulations # and forces from all the surfaces. # While other components only depends on a single surface, # this component requires information from all surfaces because # each surface interacts with the others. if self.options['compressible'] == True: aero_states = CompressibleVLMStates(surfaces=surfaces, rotational=rotational) prom_in = ['v', 'alpha', 'beta', 'rho', 'Mach_number'] else: aero_states = VLMStates(surfaces=surfaces, rotational=rotational) prom_in = ['v', 'alpha', 'beta', 'rho'] aero_states.linear_solver = om.LinearRunOnce() if rotational: prom_in.extend(['omega', 'cg']) self.add_subsystem('aero_states', aero_states, promotes_inputs=prom_in, promotes_outputs=['circulations']) # Explicitly connect parameters from each surface's group and the common # 'aero_states' group. # This is necessary because the VLMStates component requires information # from each surface, but this information is stored within each # surface's group. for surface in surfaces: self.add_subsystem(surface['name'] + '_perf', VLMFunctionals(surface=surface), promotes_inputs=[ 'v', 'alpha', 'beta', 'Mach_number', 're', 'rho' ]) # Add the total aero performance group to compute the CL, CD, and CM # of the total aircraft. This accounts for all lifting surfaces. self.add_subsystem( 'total_perf', TotalAeroPerformance( surfaces=surfaces, user_specified_Sref=self.options['user_specified_Sref']), promotes_inputs=['v', 'rho', 'cg', 'S_ref_total'], promotes_outputs=['CM', 'CL', 'CD'])
def setup(self): surfaces = self.options['surfaces'] # Loop through each surface and connect relevant parameters for surface in surfaces: name = surface['name'] self.connect(name + '.normals', 'aero_states.' + name + '_normals') # Connect the results from 'aero_states' to the performance groups self.connect('aero_states.' + name + '_sec_forces', name + '_perf' + '.sec_forces') # Connect S_ref for performance calcs self.connect(name + '.S_ref', name + '_perf.S_ref') self.connect(name + '.widths', name + '_perf.widths') self.connect(name + '.chords', name + '_perf.chords') self.connect(name + '.lengths', name + '_perf.lengths') self.connect(name + '.cos_sweep', name + '_perf.cos_sweep') # Connect S_ref for performance calcs self.connect(name + '.S_ref', 'total_perf.' + name + '_S_ref') self.connect(name + '.widths', 'total_perf.' + name + '_widths') self.connect(name + '.chords', 'total_perf.' + name + '_chords') self.connect(name + '.b_pts', 'total_perf.' + name + '_b_pts') self.connect(name + '_perf' + '.CL', 'total_perf.' + name + '_CL') self.connect(name + '_perf' + '.CD', 'total_perf.' + name + '_CD') self.connect('aero_states.' + name + '_sec_forces', 'total_perf.' + name + '_sec_forces') self.add_subsystem(name, VLMGeometry(surface=surface)) # Add a single 'aero_states' component that solves for the circulations # and forces from all the surfaces. # While other components only depends on a single surface, # this component requires information from all surfaces because # each surface interacts with the others. aero_states = VLMStates(surfaces=surfaces) aero_states.linear_solver = LinearRunOnce() self.add_subsystem('aero_states', aero_states, promotes_inputs=['v', 'alpha', 'rho'], promotes_outputs=['circulations']) # Explicitly connect parameters from each surface's group and the common # 'aero_states' group. # This is necessary because the VLMStates component requires information # from each surface, but this information is stored within each # surface's group. for surface in surfaces: self.add_subsystem(surface['name'] +'_perf', VLMFunctionals(surface=surface), promotes_inputs=["v", "alpha", "M", "re", "rho"]) # Add the total aero performance group to compute the CL, CD, and CM # of the total aircraft. This accounts for all lifting surfaces. self.add_subsystem('total_perf', TotalAeroPerformance(surfaces=surfaces, user_specified_Sref=self.options['user_specified_Sref']), promotes_inputs=['v', 'rho', 'cg', 'S_ref_total'], promotes_outputs=['CM', 'CL', 'CD'])
def setup(self): surfaces = self.options['surfaces'] rotational = self.options['rotational'] rollOnly = self.options['rollOnly'] coupled = om.Group() for surface in surfaces: name = surface['name'] # 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. coupled.connect(name + '_loads.loads', name + '.loads') # Perform the connections with the modified names within the # 'aero_states' group. coupled.connect(name + '.def_mesh', 'aero_states.' + name + '_def_mesh') # Connect the results from 'coupled' to the performance groups coupled.connect(name + '.def_mesh', name + '_loads.def_mesh') coupled.connect('aero_states.' + name + '_sec_forces', name + '_loads.sec_forces') # Connect the results from 'aero_states' to the performance groups self.connect('coupled.aero_states.' + name + '_sec_forces', name + '_perf' + '.sec_forces') # Connection performance functional variables self.connect(name + '_perf.CL', 'total_perf.' + name + '_CL') self.connect(name + '_perf.CD', 'total_perf.' + name + '_CD') self.connect('coupled.aero_states.' + name + '_sec_forces', 'total_perf.' + name + '_sec_forces') self.connect('coupled.' + name + '.chords', name + '_perf.aero_funcs.chords') # Connect parameters from the 'coupled' group to the performance # groups for the individual surfaces. self.connect('coupled.' + name + '.disp', name + '_perf.disp') self.connect('coupled.' + name + '.S_ref', name + '_perf.S_ref') self.connect('coupled.' + name + '.widths', name + '_perf.widths') # self.connect('coupled.' + name + '.chords', name + '_perf.chords') self.connect('coupled.' + name + '.lengths', name + '_perf.lengths') self.connect('coupled.' + name + '.cos_sweep', name + '_perf.cos_sweep') # Connect parameters from the 'coupled' group to the total performance group. self.connect('coupled.' + name + '.S_ref', 'total_perf.' + name + '_S_ref') self.connect('coupled.' + name + '.widths', 'total_perf.' + name + '_widths') self.connect('coupled.' + name + '.chords', 'total_perf.' + name + '_chords') self.connect('coupled.' + name + '.b_pts', 'total_perf.' + name + '_b_pts') # Add components to the 'coupled' group for each surface. # The 'coupled' group must contain all components and parameters # needed to converge the aerostructural system. coupled_AS_group = _CoupledAS(surface=surface) if surface[ 'distributed_fuel_weight'] or 'n_point_masses' in surface.keys( ) or surface['struct_weight_relief']: prom_in = ['load_factor'] else: prom_in = [] coupled.add_subsystem(name, coupled_AS_group, promotes_inputs=prom_in) if self.options['compressible'] == True: aero_states = CompressibleVLMStates(surfaces=surfaces, rotational=rotational) prom_in = ['v', 'alpha', 'beta', 'rho', 'Mach_number'] else: aero_states = VLMStates(surfaces=surfaces, rotational=rotational) prom_in = ['v', 'alpha', 'beta', 'rho'] # If rotational, add in roll rate and cg if rotational: prom_in.extend(['omega', 'cg']) # Add a single 'aero_states' component for the whole system within the # coupled group. coupled.add_subsystem('aero_states', aero_states, promotes_inputs=prom_in) # Explicitly connect parameters from each surface's group and the common # 'aero_states' group. for surface in surfaces: name = surface['name'] # Connect normals if 'control_surfaces' in surface: first = surface['control_surfaces'][0]['name'] last = surface['control_surfaces'][-1]['name'] coupled.connect( f'{name}.aero_geom.normals', f'{name}.control_surfaces.undeflected_normals') coupled.connect(f'{name}.control_surfaces.deflected_normals', f'aero_states.{name}_normals') else: coupled.connect(f'{name}.aero_geom.normals', f'aero_states.{name}_normals') # Add a loads component to the coupled group for surface in surfaces: name = surface['name'] coupled.add_subsystem(name + '_loads', LoadTransfer(surface=surface)) """ ### Change the solver settings here ### """ # Set solver properties for the coupled group # coupled.linear_solver = ScipyKrylov() # coupled.linear_solver.precon = om.LinearRunOnce() coupled.nonlinear_solver = om.NonlinearBlockGS(use_aitken=True) coupled.nonlinear_solver.options['maxiter'] = 100 coupled.nonlinear_solver.options['atol'] = 1e-7 coupled.nonlinear_solver.options['rtol'] = 1e-30 coupled.nonlinear_solver.options['iprint'] = 2 coupled.nonlinear_solver.options['err_on_non_converge'] = True # coupled.linear_solver = om.DirectSolver() coupled.linear_solver = om.DirectSolver(assemble_jac=True) coupled.options['assembled_jac_type'] = 'csc' # coupled.nonlinear_solver = om.NewtonSolver(solve_subsystems=True) # coupled.nonlinear_solver.options['maxiter'] = 50 """ ### End change of solver settings ### """ prom_in = ['v', 'alpha', 'rho'] if self.options['compressible'] == True: prom_in.append('Mach_number') # If rotational, add in roll rate and cg if rotational: prom_in.extend(['omega', 'cg']) # Add the coupled group to the model problem self.add_subsystem('coupled', coupled, promotes_inputs=prom_in) for surface in surfaces: name = surface['name'] # Add a performance group which evaluates the data after solving # the coupled system perf_group = CoupledPerformance(surface=surface) self.add_subsystem( name + '_perf', perf_group, promotes_inputs=['rho', 'v', 'alpha', 're', 'Mach_number']) # Add functionals to evaluate performance of the system. # Note that only the interesting results are promoted here; not all # of the parameters. if not rotational: self.add_subsystem( 'total_perf', TotalPerformance( surfaces=surfaces, user_specified_Sref=self.options['user_specified_Sref'], internally_connect_fuelburn=self. options['internally_connect_fuelburn']), promotes_inputs=[ 'v', 'rho', 'empty_cg', 'total_weight', 'CT', 'speed_of_sound', 'R', 'Mach_number', 'W0', 'load_factor', 'S_ref_total' ], promotes_outputs=[ 'L_equals_W', 'fuelburn', 'CL', 'CD', 'CM', 'cg' ]) elif rotational: # In rotational case cannot take in empty_cg and calculate cg. # Leads to a cycle between three groups (rotational aero needs # cg well before cg calculation) if rollOnly: self.add_subsystem( 'total_perf', TotalRollPerformance( surfaces=surfaces, user_specified_Sref=self. options['user_specified_Sref'], internally_connect_fuelburn=self. options['internally_connect_fuelburn'], rotational=self.options['rotational']), promotes_inputs=['v', 'rho', 'cg', 'S_ref_total'], promotes_outputs=['CL', 'CD', 'CM']) else: self.add_subsystem( 'total_perf', TotalPerformance(surfaces=surfaces, user_specified_Sref=self. options['user_specified_Sref'], internally_connect_fuelburn=self. options['internally_connect_fuelburn'], rotational=self.options['rotational']), promotes_inputs=[ 'v', 'rho', 'cg', 'CT', 'speed_of_sound', 'R', 'Mach_number', 'W0', 'load_factor', 'S_ref_total' ], promotes_outputs=[ 'L_equals_W', 'fuelburn', 'CL', 'CD', 'CM' ])