def solve_nonlinear(self, params, unknowns, resids): mesh = self.mesh.copy() self.geo_params.update(params) if fortran_flag: mesh = OAS_API.oas_api.manipulate_mesh(mesh, self.geo_params['taper'], self.geo_params['chord'], self.geo_params['sweep'], self.geo_params['xshear'], self.geo_params['span'], self.geo_params['yshear'], self.geo_params['dihedral'], self.geo_params['zshear'], self.geo_params['twist'], self.symmetry, self.rotate_x) else: taper(mesh, self.geo_params['taper'], self.symmetry) scale_x(mesh, self.geo_params['chord']) sweep(mesh, self.geo_params['sweep'], self.symmetry) shear_x(mesh, self.geo_params['xshear']) stretch(mesh, self.geo_params['span'], self.symmetry) shear_y(mesh, self.geo_params['yshear']) dihedral(mesh, self.geo_params['dihedral'], self.symmetry) shear_z(mesh, self.geo_params['zshear']) rotate(mesh, self.geo_params['twist'], self.symmetry, self.rotate_x) # Only compute the radius on the first iteration. if self.compute_radius and 'radius_cp' not in self.desvar_names: # Get spar radii and interpolate to radius control points. # Need to refactor this at some point. unknowns['radius'] = radii(mesh, self.surface['t_over_c']) self.compute_radius = False unknowns['mesh'] = mesh
def solve_nonlinear(self, params, unknowns, resids): mesh = self.mesh.copy() self.geo_params.update(params) if fortran_flag: mesh = OAS_API.oas_api.manipulate_mesh(mesh, self.geo_params['taper'], self.geo_params['chord'], self.geo_params['sweep'], self.geo_params['xshear'], self.geo_params['dihedral'], self.geo_params['zshear'], self.geo_params['twist'], self.geo_params['span'], self.symmetry, self.rotate_x) else: taper(mesh, self.geo_params['taper'], self.symmetry) scale_x(mesh, self.geo_params['chord']) stretch(mesh, self.geo_params['span'], self.symmetry) sweep(mesh, self.geo_params['sweep'], self.symmetry) shear_x(mesh, self.geo_params['xshear']) dihedral(mesh, self.geo_params['dihedral'], self.symmetry) shear_z(mesh, self.geo_params['zshear']) rotate(mesh, self.geo_params['twist'], self.symmetry, self.rotate_x) # Only compute the radius on the first iteration. if self.compute_radius and 'radius_cp' not in self.desvar_names: # Get spar radii and interpolate to radius control points. # Need to refactor this at some point. unknowns['radius'] = radii(mesh, self.surface['t_over_c']) self.compute_radius = False unknowns['mesh'] = mesh
OAS_prob.add_surface(surface) # Get the finalized surface, which includes the created mesh object. # Here, `surface` is a dictionary that contains information relevant to # one surface within the analysis or optimization. surface = OAS_prob.surfaces[0] # If you want to view the information contained within `surface`, # uncomment the following line of code. # pp(surface) # Obtain the number of spanwise node points from the defined surface. num_y = surface['num_y'] # Create an array of radii for the spar elements. r = radii(surface['mesh']) # Obtain the starting thickness for each of the spar elements based # on the radii. thickness = r / 5 # Define the loads here. Choose either a tip load or distributed load # by commenting the lines as necessary. loads = numpy.zeros((num_y, 6)) P = 1e4 # load of 10 kN # loads[0, 2] = P # tip load loads[1:, 2] = P / (num_y - 1) # load distributed across all nodes # Instantiate the OpenMDAO group for the root problem. root = Group()
def setup(num_inboard=2, num_outboard=3, check=False, out_stream=sys.stdout): ''' Setup the aerostruct mesh using OpenMDAO''' # Define the aircraft properties from CRM import span, v, alpha, rho # Define spatialbeam properties from aluminum import E, G, stress, mrho # Create the mesh with 2 inboard points and 3 outboard points. # This will be mirrored to produce a mesh with 7 spanwise points, # or 6 spanwise panels # print(type(num_inboard)) mesh = gen_crm_mesh(int(num_inboard), int(num_outboard), num_x=2) num_x, num_y = mesh.shape[:2] num_twist = np.max([int((num_y - 1) / 5), 5]) r = radii(mesh) # Set the number of thickness control points and the initial thicknesses num_thickness = num_twist t = r / 10 mesh = mesh.reshape(-1, mesh.shape[-1]) aero_ind = np.atleast_2d(np.array([num_x, num_y])) fem_ind = [num_y] aero_ind, fem_ind = get_inds(aero_ind, fem_ind) # Set additional mesh parameters dihedral = 0. # dihedral angle in degrees sweep = 0. # shearing sweep angle in degrees taper = 1. # taper ratio # Initial displacements of zero tot_n_fem = np.sum(fem_ind[:, 0]) disp = np.zeros((tot_n_fem, 6)) # Define Jacobians for b-spline controls tot_n_fem = np.sum(fem_ind[:, 0]) num_surf = fem_ind.shape[0] jac_twist = get_bspline_mtx(num_twist, num_y) jac_thickness = get_bspline_mtx(num_thickness, tot_n_fem-num_surf) # Define ... twist_cp = np.zeros(num_twist) thickness_cp = np.ones(num_thickness)*np.max(t) # Define the design variables des_vars = [ ('twist_cp', twist_cp), ('dihedral', dihedral), ('sweep', sweep), ('span', span), ('taper', taper), ('v', v), ('alpha', alpha), ('rho', rho), ('disp', disp), ('aero_ind', aero_ind), ('fem_ind', fem_ind) ] root = Group() root.add('des_vars', IndepVarComp(des_vars), promotes=['twist_cp','span','v','alpha','rho','disp','dihedral']) root.add('mesh', # This component is needed, otherwise resulting loads matrix is NaN GeometryMesh(mesh, aero_ind), # changes mesh given span, sweep, twist, and des_vars promotes=['span','sweep','dihedral','twist','taper','mesh']) root.add('def_mesh', TransferDisplacements(aero_ind, fem_ind), promotes=['mesh','disp','def_mesh']) prob = Problem() prob.root = root prob.setup(check=check, out_stream=out_stream) prob.run() # Output the def_mesh for the aero modules def_mesh = prob['def_mesh'] # Other variables needed for aero and struct modules params = { 'mesh': mesh, 'num_x': num_x, 'num_y': num_y, 'span': span, 'twist_cp': twist_cp, 'thickness_cp': thickness_cp, 'v': v, 'alpha': alpha, 'rho': rho, 'r': r, 't': t, 'aero_ind': aero_ind, 'fem_ind': fem_ind, 'num_thickness': num_thickness, 'num_twist': num_twist, 'sweep': sweep, 'taper': taper, 'dihedral': dihedral, 'E': E, 'G': G, 'stress': stress, 'mrho': mrho, 'tot_n_fem': tot_n_fem, 'num_surf': num_surf, 'jac_twist': jac_twist, 'jac_thickness': jac_thickness, 'out_stream': out_stream, 'check': check } return (def_mesh, params)
def add_surface(self, input_dict={}): """ Add a surface to the problem. One surface definition is needed for each planar lifting surface. Parameters ---------- input_dict : dictionary Surface definition. Note that there are default values defined by `get_default_surf_dict` that are overwritten based on the user-provided input_dict. """ # Get defaults and update surf_dict with the user-provided input surf_dict = self.get_default_surf_dict() surf_dict.update(input_dict) # Check to see if the user provides the mesh points. If they do, # get the chordwise and spanwise number of points if 'mesh' in surf_dict.keys(): mesh = surf_dict['mesh'] num_x, num_y = mesh.shape # If the user doesn't provide a mesh, obtain the values from surf_dict # to create the mesh elif 'num_x' in surf_dict.keys(): num_x = surf_dict['num_x'] num_y = surf_dict['num_y'] span = surf_dict['span'] chord = surf_dict['chord'] span_cos_spacing = surf_dict['span_cos_spacing'] chord_cos_spacing = surf_dict['chord_cos_spacing'] # Check to make sure that an odd number of spanwise points (num_y) was provided if not num_y % 2: Error('num_y must be an odd number.') # Generate rectangular mesh if surf_dict['wing_type'] == 'rect': mesh = gen_rect_mesh(num_x, num_y, span, chord, span_cos_spacing, chord_cos_spacing) # Generate CRM mesh elif surf_dict['wing_type'] == 'CRM': npi = int(numpy.ceil(((num_y - 1) / 2) * .4)) npo = (num_y - 1) // 2 + 2 - npi mesh = gen_crm_mesh(n_points_inboard=npi, n_points_outboard=npo, num_x=num_x) num_x, num_y = mesh.shape[:2] else: Error( 'wing_type option not understood. Must be either "CRM" or "rect".' ) # Chop the mesh in half if using symmetry during analysis. # Note that this means that the provided mesh should be the full mesh if surf_dict['symmetry']: num_y = int((num_y + 1) / 2) mesh = mesh[:, :num_y, :] else: Error("Please either provide a mesh or a valid set of parameters.") # Apply the user-provided coordinate offset to position the mesh mesh = mesh + surf_dict['offset'] # Get the spar radius r = radii(mesh) # Set the number of twist and thickness control points. # These b-spline control points are what the optimizer sees # and controls if 'num_twist' not in input_dict.keys(): surf_dict['num_twist'] = numpy.max([int((num_y - 1) / 5), 5]) if 'num_thickness' not in input_dict.keys(): surf_dict['num_thickness'] = numpy.max([int((num_y - 1) / 5), 5]) # Store updated values surf_dict['num_x'] = num_x surf_dict['num_y'] = num_y surf_dict['mesh'] = mesh surf_dict['r'] = r surf_dict['t'] = r / 10 # Set default loads at the tips loads = numpy.zeros((r.shape[0] + 1, 6), dtype='complex') loads[0, 2] = 1e3 if not surf_dict['symmetry']: loads[-1, 2] = 1e3 surf_dict['loads'] = loads # Throw a warning if the user provides two surfaces with the same name name = surf_dict['name'] for surface in self.surfaces: if name == surface['name']: print("Warning: Two surfaces have the same name.") # Append '_' to each repeated surface name if not name: surf_dict['name'] = name else: surf_dict['name'] = name + '_' # Add the individual surface description to the surface list self.surfaces.append(surf_dict)
from transfer import TransferDisplacements, TransferLoads from vlm import VLMStates, VLMFunctionals from spatialbeam import SpatialBeamStates, SpatialBeamFunctionals, radii from materials import MaterialsTube from functionals import FunctionalBreguetRange, FunctionalEquilibrium from openmdao.devtools.partition_tree_n2 import view_tree from gs_newton import HybridGSNewton ############################################################ # Change mesh size here ############################################################ # Create the mesh with 2 inboard points and 3 outboard points mesh = gen_crm_mesh(n_points_inboard=2, n_points_outboard=3) num_y = mesh.shape[1] r = radii(mesh) t = r / 10 # Define the aircraft properties execfile('CRM.py') # Define the material properties execfile('aluminum.py') # Create the top-level system root = Group() # Define the independent variables indep_vars = [ ('span', span), ('twist', numpy.zeros(num_y)),
def setup(num_inboard=2, num_outboard=3, check=False, out_stream=sys.stdout): ''' Setup the aerostruct mesh using OpenMDAO''' # Define the aircraft properties from CRM import span, v, alpha, rho # Define spatialbeam properties from aluminum import E, G, stress, mrho # Create the mesh with 2 inboard points and 3 outboard points. # This will be mirrored to produce a mesh with 7 spanwise points, # or 6 spanwise panels # print(type(num_inboard)) mesh = gen_crm_mesh(int(num_inboard), int(num_outboard), num_x=2) num_x, num_y = mesh.shape[:2] num_twist = np.max([int((num_y - 1) / 5), 5]) r = radii(mesh) # Set the number of thickness control points and the initial thicknesses num_thickness = num_twist t = r / 10 mesh = mesh.reshape(-1, mesh.shape[-1]) aero_ind = np.atleast_2d(np.array([num_x, num_y])) fem_ind = [num_y] aero_ind, fem_ind = get_inds(aero_ind, fem_ind) # Set additional mesh parameters dihedral = 0. # dihedral angle in degrees sweep = 0. # shearing sweep angle in degrees taper = 1. # taper ratio # Initial displacements of zero tot_n_fem = np.sum(fem_ind[:, 0]) disp = np.zeros((tot_n_fem, 6)) # Define Jacobians for b-spline controls tot_n_fem = np.sum(fem_ind[:, 0]) num_surf = fem_ind.shape[0] jac_twist = get_bspline_mtx(num_twist, num_y) jac_thickness = get_bspline_mtx(num_thickness, tot_n_fem - num_surf) # Define ... twist_cp = np.zeros(num_twist) thickness_cp = np.ones(num_thickness) * np.max(t) # Define the design variables des_vars = [('twist_cp', twist_cp), ('dihedral', dihedral), ('sweep', sweep), ('span', span), ('taper', taper), ('v', v), ('alpha', alpha), ('rho', rho), ('disp', disp), ('aero_ind', aero_ind), ('fem_ind', fem_ind)] root = Group() root.add( 'des_vars', IndepVarComp(des_vars), promotes=['twist_cp', 'span', 'v', 'alpha', 'rho', 'disp', 'dihedral']) root.add( 'mesh', # This component is needed, otherwise resulting loads matrix is NaN GeometryMesh( mesh, aero_ind), # changes mesh given span, sweep, twist, and des_vars promotes=['span', 'sweep', 'dihedral', 'twist', 'taper', 'mesh']) root.add('def_mesh', TransferDisplacements(aero_ind, fem_ind), promotes=['mesh', 'disp', 'def_mesh']) prob = Problem() prob.root = root prob.setup(check=check, out_stream=out_stream) prob.run() # Output the def_mesh for the aero modules def_mesh = prob['def_mesh'] # Other variables needed for aero and struct modules params = { 'mesh': mesh, 'num_x': num_x, 'num_y': num_y, 'span': span, 'twist_cp': twist_cp, 'thickness_cp': thickness_cp, 'v': v, 'alpha': alpha, 'rho': rho, 'r': r, 't': t, 'aero_ind': aero_ind, 'fem_ind': fem_ind, 'num_thickness': num_thickness, 'num_twist': num_twist, 'sweep': sweep, 'taper': taper, 'dihedral': dihedral, 'E': E, 'G': G, 'stress': stress, 'mrho': mrho, 'tot_n_fem': tot_n_fem, 'num_surf': num_surf, 'jac_twist': jac_twist, 'jac_thickness': jac_thickness, 'out_stream': out_stream, 'check': check } return (def_mesh, params)
def add_surface(self, input_dict={}): """ Add a surface to the problem. One surface definition is needed for each planar lifting surface. Parameters ---------- input_dict : dictionary Surface definition. Note that there are default values defined by `get_default_surface` that are overwritten based on the user-provided input_dict. """ # Get defaults and update surface with the user-provided input surf_dict = self.get_default_surf_dict() surf_dict.update(input_dict) # Check to see if the user provides the mesh points. If they do, # get the chordwise and spanwise number of points if 'mesh' in surf_dict.keys(): mesh = surf_dict['mesh'] num_x, num_y = mesh.shape[:2] # If the user doesn't provide a mesh, obtain the values from surface # to create the mesh elif 'num_x' in surf_dict.keys(): num_x = surf_dict['num_x'] num_y = surf_dict['num_y'] span = surf_dict['span'] chord = surf_dict['root_chord'] span_cos_spacing = surf_dict['span_cos_spacing'] chord_cos_spacing = surf_dict['chord_cos_spacing'] # Check to make sure that an odd number of spanwise points (num_y) was provided if not num_y % 2: Error('num_y must be an odd number.') # Generate rectangular mesh if surf_dict['wing_type'] == 'rect': mesh = gen_rect_mesh(num_x, num_y, span, chord, span_cos_spacing, chord_cos_spacing) # Generate CRM mesh. Note that this outputs twist information # based on the data from the CRM definition paper, so we save # this twist information to the surf_dict. elif 'CRM' in surf_dict['wing_type']: mesh, eta, twist = gen_crm_mesh(num_x, num_y, span, chord, span_cos_spacing, chord_cos_spacing, surf_dict['wing_type']) num_x, num_y = mesh.shape[:2] surf_dict['crm_twist'] = twist else: Error('wing_type option not understood. Must be either a type of ' + '"CRM" or "rect".') # Chop the mesh in half if using symmetry during analysis. # Note that this means that the provided mesh should be the full mesh if surf_dict['symmetry']: num_y = int((num_y+1)/2) mesh = mesh[:, :num_y, :] else: Error("Please either provide a mesh or a valid set of parameters.") # Compute span. We need .real to make span to avoid OpenMDAO warnings. quarter_chord = 0.25 * mesh[-1] + 0.75 * mesh[0] surf_dict['span'] = max(quarter_chord[:, 1]).real - min(quarter_chord[:, 1]).real if surf_dict['symmetry']: surf_dict['span'] *= 2. # Apply the user-provided coordinate offset to position the mesh mesh = mesh + surf_dict['offset'] # We need to initialize some variables to ones and some others to zeros. # Here we define the lists for each case. ones_list = ['chord_cp', 'thickness_cp', 'radius_cp'] zeros_list = ['twist_cp', 'xshear_cp', 'zshear_cp'] surf_dict['bsp_vars'] = ones_list + zeros_list # Loop through bspline variables and set the number of control points if # the user hasn't initalized the array. for var in surf_dict['bsp_vars']: numkey = 'num_' + var if surf_dict[var] is None: if numkey not in input_dict: surf_dict[numkey] = np.max([int((num_y - 1) / 5), min(5, num_y-1)]) else: surf_dict[numkey] = len(surf_dict[var]) # Interpolate the twist values from the CRM wing definition to the twist # control points if 'CRM' in surf_dict['wing_type']: num_twist = surf_dict['num_twist_cp'] # If the surface is symmetric, simply interpolate the initial # twist_cp values based on the mesh data if surf_dict['symmetry']: twist = np.interp(np.linspace(0, 1, num_twist), eta, surf_dict['crm_twist']) else: # If num_twist is odd, create the twist vector and mirror it # then stack the two together, but remove the duplicated twist # value. if num_twist % 2: twist = np.interp(np.linspace(0, 1, (num_twist+1)/2), eta, surf_dict['crm_twist']) twist = np.hstack((twist[:-1], twist[::-1])) # If num_twist is even, mirror the twist vector and stack # them together else: twist = np.interp(np.linspace(0, 1, num_twist/2), eta, surf_dict['crm_twist']) twist = np.hstack((twist, twist[::-1])) # Continue to use the user-defined twist_cp if inputted to the # surface dictionary. Otherwise, use the prescribed CRM twist. if surf_dict['twist_cp'] is None: surf_dict['twist_cp'] = twist # Store updated values surf_dict['num_x'] = num_x surf_dict['num_y'] = num_y surf_dict['mesh'] = mesh radius = radii(mesh, surf_dict['t_over_c']) surf_dict['radius'] = radius # Set initial thicknesses surf_dict['thickness'] = radius / 10 # We now loop through the possible bspline variables and populate # the 'initial_geo' list with the variables that the geometry # or user provided. For example, the CRM wing defines an initial twist. # We must treat this separately so we add a twist bspline component # even if it is not a desvar. surf_dict['initial_geo'] = [] for var in surf_dict['geo_vars']: # Add the bspline variables when they're needed if var in surf_dict['bsp_vars']: numkey = 'num_' + var if surf_dict[var] is None: # Add the intialized geometry variables to either ones or zeros. # These initial values do not perturb the mesh. if var in ones_list: surf_dict[var] = np.ones(surf_dict[numkey], dtype=data_type) elif var in zeros_list: surf_dict[var] = np.zeros(surf_dict[numkey], dtype=data_type) else: surf_dict['initial_geo'].append(var) # If the user provided a scalar variable (span, sweep, taper, etc), # then include that in the initial_geo list elif var in input_dict.keys(): surf_dict['initial_geo'].append(var) if 'thickness_cp' not in surf_dict['initial_geo']: surf_dict['thickness_cp'] *= np.max(surf_dict['thickness']) if surf_dict['loads'] is None: # Set default loads at the tips loads = np.zeros((surf_dict['thickness'].shape[0] + 1, 6), dtype=data_type) loads[0, 2] = 1e4 if not surf_dict['symmetry']: loads[-1, 2] = 1e4 surf_dict['loads'] = loads if surf_dict['disp'] is None: # Set default disp if not provided surf_dict['disp'] = np.zeros((surf_dict['num_y'], 6), dtype=data_type) # Throw a warning if the user provides two surfaces with the same name name = surf_dict['name'] for surface in self.surfaces: if name == surface['name']: OASWarning("Two surfaces have the same name.") # Append '_' to each repeated surface name if not name: surf_dict['name'] = name else: surf_dict['name'] = name + '_' # Add the individual surface description to the surface list self.surfaces.append(surf_dict)
num_x = 3 # number of spanwise nodes num_y = 11 # number of chordwise nodes num_y_sym = numpy.int((num_y + 1) / 2) span = 32. # [m] chord = 1. # [m] cosine_spacing = 0. full_wing_mesh = gen_mesh(num_x, num_y, span, chord, cosine_spacing) num_twist = numpy.max([int((num_y - 1) / 5), 5]) half_wing_mesh = full_wing_mesh[:, (num_y_sym - 1):, :] ########################## # Define beam properties # ########################## r = radii(half_wing_mesh) / 5 # beam radius thick = r / 5 # beam thickness fem_origin = 0.5 # elastic axis position along the chord #zeta = zeta_vect[i] zeta = 0.0 # damping percentual coeff. zeta = float(zeta) E = 70.e9 # [Pa] poisson = 0.3 G = E / (2 * (1 + poisson)) mrho = 2800. # [kg/m^3] #################################### # Define the independent variables # #################################### indep_vars = [('span', span), ('twist', numpy.zeros(num_twist)),