def test_22(self, train=False, refDeriv=False): """ Test 22 """ refFile = os.path.join(self.base_path, "ref/test_DVGeometry_22.ref") with BaseRegTest(refFile, train=train) as handler: handler.root_print("Test FFD writing function") # Write duplicate of outerbox FFD axes = ["i", "k", "j"] slices = np.array( [ # Slice 1 [[[-1, -1, -1], [-1, 1, -1]], [[-1, -1, 1], [-1, 1, 1]]], # Slice 2 [[[1, -1, -1], [1, 1, -1]], [[1, -1, 1], [1, 1, 1]]], # Slice 3 [[[2, -1, -1], [2, 1, -1]], [[2, -1, 1], [2, 1, 1]]], ] ) N0 = [2, 2] N1 = [2, 2] N2 = [2, 2] copyName = os.path.join(self.base_path, "../../input_files/test1.xyz") geo_utils.write_wing_FFD_file(copyName, slices, N0, N1, N2, axes=axes) # Load original and duplicate origFFD = DVGeometry(os.path.join(self.base_path, "../../input_files/outerBoxFFD.xyz")) copyFFD = DVGeometry(copyName) norm_diff = np.linalg.norm(origFFD.FFD.coef - copyFFD.FFD.coef) handler.par_add_norm("norm", norm_diff, rtol=1e-7, atol=1e-7) os.remove(copyName)
def setDVGeo(ffdFile, cmplx=False): # Setup geometry/mesh DVGeo = DVGeometry(ffdFile, complex=cmplx) nTwist = 6 DVGeo.addRefAxis( "wing", Curve( x=numpy.linspace(5.0 / 4.0, 1.5 / 4.0 + 7.5, nTwist), y=numpy.zeros(nTwist), z=numpy.linspace(0, 14, nTwist), k=2, ), ) def twist(val, geo): for i in range(nTwist): geo.rot_z["wing"].coef[i] = val[i] def span(val, geo): C = geo.extractCoef("wing") s = geo.extractS("wing") for i in range(len(C)): C[i, 2] += s[i] * val[0] geo.restoreCoef(C, "wing") DVGeo.addGlobalDV("twist", [0] * nTwist, twist, lower=-10, upper=10, scale=1.0) DVGeo.addGlobalDV("span", [0], span, lower=-10, upper=10, scale=1.0) DVGeo.addLocalDV("shape", lower=-0.5, upper=0.5, axis="y", scale=10.0) return DVGeo
def setup_cb(comm): # Create the solver CFDSolver = ADFLOW(options=options, comm=comm, debug=False) # Setup geometry/mesh DVGeo = DVGeometry(ffdFile) nTwist = 6 DVGeo.addRefAxis( 'wing', Curve(x=numpy.linspace(5.0 / 4.0, 1.5 / 4.0 + 7.5, nTwist), y=numpy.zeros(nTwist), z=numpy.linspace(0, 14, nTwist), k=2)) def twist(val, geo): for i in range(nTwist): geo.rot_z['wing'].coef[i] = val[i] DVGeo.addGeoDVGlobal('twist', [0] * nTwist, twist, lower=-10, upper=10, scale=1.0) DVGeo.addGeoDVLocal('shape', lower=-0.5, upper=0.5, axis='y', scale=10.0) mesh = USMesh(options=meshOptions, comm=comm) CFDSolver.setMesh(mesh) CFDSolver.setDVGeo(DVGeo) return CFDSolver, mesh, DVGeo, None
def generate_dvgeo_dvcon_c172(self): meshfile = os.path.join(self.base_path, '../inputFiles/c172.stl') ffdfile = os.path.join(self.base_path, '../inputFiles/c172.xyz') testmesh = mesh.Mesh.from_file(meshfile) # test mesh dim 0 is triangle index # dim 1 is each vertex of the triangle # dim 2 is x, y, z dimension # create a DVGeo object with a few local thickness variables DVGeo = DVGeometry(ffdfile) nRefAxPts = DVGeo.addRefAxis("wing", xFraction=0.25, alignIndex="k") self.nTwist = nRefAxPts - 1 def twist(val, geo): for i in range(1, nRefAxPts): geo.rot_z["wing"].coef[i] = val[i - 1] DVGeo.addGeoDVGlobal(dvName="twist", value=[0] * self.nTwist, func=twist, lower=-10, upper=10, scale=1) DVGeo.addGeoDVLocal("local", lower=-0.5, upper=0.5, axis="y", scale=1) # create a DVConstraints object for the wing DVCon =DVConstraints() DVCon.setDVGeo(DVGeo) p0 = testmesh.vectors[:,0,:] / 1000 v1 = testmesh.vectors[:,1,:] / 1000 - p0 v2 = testmesh.vectors[:,2,:] / 1000 - p0 DVCon.setSurface([p0, v1, v2]) return DVGeo, DVCon
def test_spanwise_dvs(self, train=False, refDeriv=False): refFile = os.path.join(self.base_path, "ref/test_Cylinder_spanwise_dvs.ref") with BaseRegTest(refFile, train=train) as handler: handler.root_print("Test 1: Basic FFD, global DVs") radius = 1.0 height = 10.0 DVCon = DVConstraints() surf = self.make_cylinder_mesh(radius, height) DVCon.setSurface(surf) # DVCon.writeSurfaceTecplot('cylinder_surface.dat') ffd_name = os.path.join(self.base_path, "../inputFiles/cylinder_ffd.xyz") self.make_ffd(ffd_name, radius, height) DVGeo = DVGeometry(ffd_name) DVGeo.addSpanwiseLocalDV("shape", "i", lower=-0.5, upper=0.5, axis="y", scale=1.0) size = DVGeo._getNDVSpanwiseLocal() DVCon.setDVGeo(DVGeo) leList = [[0, 0, 0], [-radius / 2, 0, height]] xAxis = [-1, 0, 0] yAxis = [0, 1, 0] DVCon.addLERadiusConstraints(leList, nSpan=5, axis=yAxis, chordDir=xAxis, scaled=False) # DVCon.writeTecplot('cylinder_constraints.dat') funcs = {} DVCon.evalFunctions(funcs) print(funcs) handler.root_add_dict("funcs1", funcs, rtol=1e-6, atol=1e-6) np.random.seed(0) DVGeo.setDesignVars({"shape": (np.random.rand(size) - 0.5)}) funcs = {} DVCon.evalFunctions(funcs) handler.root_add_dict("funcs2", funcs, rtol=1e-6, atol=1e-6) print(funcs) funcsSens = {} DVCon.evalFunctionsSens(funcsSens) print(funcsSens) handler.root_add_dict("funcsSens", funcsSens, rtol=1e-6, atol=1e-6) print(funcsSens)
def test_23_xyzFraction(self, train=False): """ Test 23 This test verifies the correct implementation of the generalized `xFraction`, `yFraction` (and indirectly `zFraction`) Given an arbitrary input for the in-plane location of the reference axis nodes, the test sets up the axis object and compares the nodes location with a reference file. As the geometry of the FFD box is simple, the values can be also hand calculated: xFraction = 0.3, FFD x interval [-1,1] ---> 0.6 displacement from x min (% displ calculated from LE=xmin) --> x = -0.4 yFraction = 0.6, FFD y interval [-0.5,0.5] ---> 0.6 displacement from y max (% displ calculated from top of the box=ymax) --> x = -0.1 """ refFile = os.path.join(self.base_path, "ref/test_DVGeometry_23.ref") with BaseRegTest(refFile, train=train) as handler: handler.root_print("Test generalized axis node location section in plane") DVGeo = DVGeometry(os.path.join(self.base_path, "../../input_files/2x1x8_rectangle.xyz")) xfraction = 0.3 yfraction = 0.6 rotType = 0 DVGeo.addRefAxis("RefAx", xFraction=xfraction, yFraction=yfraction, alignIndex="k", rotType=rotType) nodes_loc = DVGeo.axis["RefAx"]["curve"].X handler.root_add_val("RefAxis_nodes_coord", nodes_loc, rtol=1e-12, atol=1e-12)
def test_1(self, train=False, refDeriv=False): refFile = os.path.join(self.base_path, 'ref/test_Cylinder_01.ref') with BaseRegTest(refFile, train=train) as handler: handler.root_print("Test 1: Basic FFD, global DVs") radius = 1.0 height = 10.0 DVCon = DVConstraints() surf = self.make_cylinder_mesh(radius, height) DVCon.setSurface(surf) # DVCon.writeSurfaceTecplot('cylinder_surface.dat') ffd_name = os.path.join(self.base_path, '../inputFiles/cylinder_ffd.xyz') self.make_ffd(ffd_name, radius, height) DVGeo = DVGeometry(ffd_name) nAxPts = DVGeo.addRefAxis('thru', xFraction=0.5, alignIndex='i', raySize=1.0) def scale_circle(val, geo): for i in range(nAxPts): geo.scale['thru'].coef[i] = val[0] DVGeo.addGeoDVGlobal('scale_circle', func=scale_circle, value=[1]) DVCon.setDVGeo(DVGeo) leList = [[0, 0, 0], [-radius / 2, 0, height]] xAxis = [-1, 0, 0] yAxis = [0, 1, 0] DVCon.addLERadiusConstraints(leList, nSpan=5, axis=yAxis, chordDir=xAxis, scaled=False) # DVCon.writeTecplot('cylinder_constraints.dat') funcs = {} DVCon.evalFunctions(funcs) print(funcs) handler.root_add_dict('funcs1', funcs, rtol=1e-6, atol=1e-6) DVGeo.setDesignVars({'scale_circle': 0.5}) funcs = {} DVCon.evalFunctions(funcs) handler.root_add_dict('funcs2', funcs, rtol=1e-6, atol=1e-6) print(funcs) funcsSens = {} DVCon.evalFunctionsSens(funcsSens) print(funcsSens) handler.root_add_dict('funcsSens', funcsSens, rtol=1e-6, atol=1e-6) print(funcsSens)
def deform_DVGeo(geo): # ========================================================================= # Setup DVGeometry object # ========================================================================= # rst DVGeometry DVGeo = DVGeometry(input_files + "deform_geometry_ffd.xyz") # Create reference axis nRefAxPts = DVGeo.addRefAxis("wing", xFraction=0.25, alignIndex="k") # Set the Twist Variable def twist(val, geo): for i in range(nRefAxPts): geo.rot_z["wing"].coef[i] = val[i] # Add the Twist Design Variable to DVGeo DVGeo.addGlobalDV(dvName="twist", value=[0] * nRefAxPts, func=twist, lower=-10, upper=10, scale=1.0) # Get Design Variables dvDict = DVGeo.getValues() # Set First Twist Section to 5deg dvDict["twist"][0] = 5 # Set Design Variables DVGeo.setDesignVars(dvDict) # rst DVGeometry (end) # ========================================================================= # Update pyGeo Object and output result # ========================================================================= # rst UpdatePyGeo DVGeo.updatePyGeo(geo, "tecplot", "wingNew", nRefU=10, nRefV=10)
def test_13b(self, train=False, refDeriv=False): refFile = os.path.join(self.base_path,'ref/test_DVConstraints_13b.ref') with BaseRegTest(refFile, train=train) as handler: handler.root_print("Test 13: PlanarityConstraint, rectangular box") ffdfile = os.path.join(self.base_path, '../inputFiles/2x1x8_rectangle.xyz') DVGeo = DVGeometry(ffdfile) DVGeo.addGeoDVLocal("local", lower=-0.5, upper=0.5, axis="y", scale=1) # create a DVConstraints object with a simple plane consisting of 2 triangles DVCon =DVConstraints() DVCon.setDVGeo(DVGeo) p0 = np.zeros(shape=(2,3)) p1 = np.zeros(shape=(2,3)) p2 = np.zeros(shape=(2,3)) vertex1 = np.array([0.5, -0.25, 0.0]) vertex2 = np.array([0.5, -0.25, 4.0]) vertex3 = np.array([-0.5, -0.25, 0.0]) vertex4 = np.array([-0.5, -0.25, 4.0]) p0[:,:] = vertex1 p2[:,:] = vertex4 p1[0,:] = vertex2 p1[1,:] = vertex3 v1 = p1 - p0 v2 = p2 - p0 DVCon.setSurface([p0, v1, v2]) DVCon.addPlanarityConstraint(origin=[0.,-0.25,2.0], planeAxis=[0.,1.,0.]) funcs, funcsSens = self.generic_test_base(DVGeo, DVCon, handler, checkDerivs=False) # this should be coplanar and the planarity constraint shoudl be zero handler.assert_allclose(funcs['DVCon1_planarity_constraints_0'], np.zeros(1), name='planarity', rtol=1e-7, atol=1e-7)
def setupDVGeo(base_path, rotType=None): # create the Parent FFD FFDFile = os.path.join(base_path, "../inputFiles/outerBoxFFD.xyz") DVGeo = DVGeometry(FFDFile) # create a reference axis for the parent axisPoints = [[-1.0, 0.0, 0.0], [1.5, 0.0, 0.0]] c1 = Curve(X=axisPoints, k=2) if rotType is not None: DVGeo.addRefAxis("mainAxis", curve=c1, axis="y", rotType=rotType) else: DVGeo.addRefAxis("mainAxis", curve=c1, axis="y") # create the child FFD FFDFile = os.path.join(base_path, "../inputFiles/simpleInnerFFD.xyz") DVGeoChild = DVGeometry(FFDFile, child=True) # create a reference axis for the child axisPoints = [[-0.5, 0.0, 0.0], [0.5, 0.0, 0.0]] c1 = Curve(X=axisPoints, k=2) DVGeoChild.addRefAxis("nestedAxis", curve=c1, axis="y") return DVGeo, DVGeoChild
def setup(self): # create the DVGeo object that does the computations if self.options["ffd_file"] is not None: # we are doing an FFD-based DVGeo ffd_file = self.options["ffd_file"] self.DVGeo = DVGeometry(ffd_file) if self.options["vsp_file"] is not None: # we are doing a VSP based DVGeo vsp_file = self.options["vsp_file"] vsp_options = self.options["vsp_options"] self.DVGeo = DVGeometryVSP(vsp_file, comm=self.comm, **vsp_options) self.DVCon = DVConstraints() self.DVCon.setDVGeo(self.DVGeo) self.omPtSetList = []
def test_spanwise_dvs(self, train=False, refDeriv=False): """ Test spanwise_dvs """ # refFile = os.path.join(self.base_path,'ref/test_DVGeometry_spanwise_dvs.ref') # with BaseRegTest(refFile, train=train) as handler: # handler.root_print("Test spanwise local variables writing function") meshfile = os.path.join(self.base_path, "../../input_files/c172.stl") ffdfile = os.path.join(self.base_path, "../../input_files/c172.xyz") testmesh = mesh.Mesh.from_file(meshfile) # test mesh dim 0 is triangle index # dim 1 is each vertex of the triangle # dim 2 is x, y, z dimension # create a DVGeo object with a few local thickness variables DVGeo = DVGeometry(ffdfile) DVGeo.addSpanwiseLocalDV("shape", "i", lower=-0.5, upper=0.5, axis="y", scale=1.0) # create a DVConstraints object for the wing DVCon = DVConstraints() DVCon.setDVGeo(DVGeo) p0 = testmesh.vectors[:, 0, :] / 1000 v1 = testmesh.vectors[:, 1, :] / 1000 - p0 v2 = testmesh.vectors[:, 2, :] / 1000 - p0 DVCon.setSurface([p0, v1, v2]) leList = [[0.7, 0.0, 0.1], [0.7, 0.0, 2.4]] teList = [[0.9, 0.0, 0.1], [0.9, 0.0, 2.4]] nSpan = 10 nChord = 10 name = "thickness_con" DVCon.addThicknessConstraints2D(leList, teList, nSpan, nChord, name=name) size = DVGeo._getNDVSpanwiseLocal() np.random.seed(0) DVGeo.setDesignVars({"shape": (np.random.rand(size) - 0.5)}) funcs = {} DVCon.evalFunctions(funcs) # print(funcs) for i in range(nChord): for j in range(nSpan - 1): np.testing.assert_allclose(funcs[name][i * nChord + j + 1], funcs[name][i * nChord + j], rtol=2e-15)
def setupDVGeo(base_path): #create the Parent FFD FFDFile = os.path.join(base_path,'../inputFiles/outerBoxFFD.xyz') DVGeo = DVGeometry(FFDFile) # create a reference axis for the parent axisPoints = [[ -1.0, 0. , 0.],[ 1.5, 0., 0.]] c1 = Curve(X=axisPoints,k=2) DVGeo.addRefAxis('mainAxis',curve=c1, axis='y') # create the child FFD FFDFile = os.path.join(base_path,'../inputFiles/simpleInnerFFD.xyz') DVGeoChild = DVGeometry(FFDFile,child=True) # create a reference axis for the child axisPoints = [[ -0.5, 0. , 0.],[ 0.5, 0., 0.]] c1 = Curve(X=axisPoints,k=2) DVGeoChild.addRefAxis('nestedAxis',curve=c1, axis='y') return DVGeo,DVGeoChild
def setupDVGeoD8(base_path, isComplex): # create the Parent FFD FFDFile = os.path.join(base_path, "../../input_files/bodyFFD.xyz") DVGeo = DVGeometry(FFDFile, isComplex=isComplex) # create a reference axis for the parent axisPoints = [[0.0, 0.0, 0.0], [26.0, 0.0, 0.0], [30.5, 0.0, 0.9], [32.5, 0.0, 1.01], [34.0, 0.0, 0.95]] c1 = Curve(X=axisPoints, k=2) DVGeo.addRefAxis("mainAxis", curve=c1, axis="y") # create the child FFD FFDFile = os.path.join(base_path, "../../input_files/nozzleFFD.xyz") DVGeoChild = DVGeometry(FFDFile, child=True, isComplex=isComplex) # create a reference axis for the child axisPoints = [[32.4, 1.0, 1.0], [34, 1.0, 0.9]] c1 = Curve(X=axisPoints, k=2) DVGeoChild.addRefAxis("nestedAxis", curve=c1, axis="y") return DVGeo, DVGeoChild
def test_ffdSplineOrder(self, train=False, refDeriv=False): """ Test custom FFD spline order """ refFile = os.path.join(self.base_path, "ref/test_ffd_spline_order.ref") with BaseRegTest(refFile, train=train) as handler: handler.root_print("Test custom FFD spline order") ffdfile = os.path.join(self.base_path, "../../input_files/deform_geometry_ffd.xyz") DVGeo = DVGeometry(ffdfile, kmax=6) # create local DVs DVGeo.addLocalDV("xdir", lower=-1.0, upper=1.0, axis="x", scale=1.0) DVGeo.addLocalDV("ydir", lower=-1.0, upper=1.0, axis="y", scale=1.0) DVGeo.addLocalDV("zdir", lower=-1.0, upper=1.0, axis="z", scale=1.0) commonUtils.testSensitivities(DVGeo, refDeriv, handler, pointset=3)
def create_fresh_dvgeo(): # The Plot3D file ffdbox.xyz contains the coordinates of the free-form deformation (FFD) volume # The "i" direction of the cube consists of 10 points along the x (streamwise) axis # The "j" direction of the cube is 2 points up and down (y axis direction) # The "k" direction of the cube is 8 along the span (z axis direction) FFDfile = "ffdbox.xyz" # initialize the DVGeometry object with the FFD file DVGeo = DVGeometry(FFDfile) stlmesh = mesh.Mesh.from_file("baseline_wing.stl") # create a pointset. pointsets are of shape npts by 3 (the second dim is xyz coordinate) # already have the wing mesh as a triangulated surface (stl file) # each vertex set is its own pointset, so we actually add three pointsets DVGeo.addPointSet(stlmesh.v0, "mesh_v0") DVGeo.addPointSet(stlmesh.v1, "mesh_v1") DVGeo.addPointSet(stlmesh.v2, "mesh_v2") return DVGeo, stlmesh
def test(self): from openaerostruct.geometry.utils import generate_mesh, write_FFD_file from openaerostruct.geometry.geometry_group import Geometry from openaerostruct.transfer.displacement_transfer import DisplacementTransfer from openaerostruct.aerodynamics.aero_groups import AeroPoint from openmdao.api import IndepVarComp, Problem, Group, NewtonSolver, ScipyIterativeSolver, LinearBlockGS, NonlinearBlockGS, DirectSolver, LinearBlockGS, PetscKSP, ScipyOptimizeDriver# TODO, SqliteRecorder, CaseReader, profile from openmdao.devtools import iprofile from openmdao.api import view_model from six import iteritems from pygeo import DVGeometry # Create a dictionary to store options about the surface mesh_dict = {'num_y' : 7, 'num_x' : 3, 'wing_type' : 'CRM', 'symmetry' : True, 'num_twist_cp' : 5, 'span_cos_spacing' : 0.} mesh, _ = generate_mesh(mesh_dict) surf_dict = { # Wing definition 'name' : 'wing', # name of the surface 'symmetry' : True, # if true, model one half of wing # reflected across the plane y = 0 'S_ref_type' : 'wetted', # how we compute the wing area, # can be 'wetted' or 'projected' 'fem_model_type' : 'tube', 'DVGeo' : True, 'mesh' : mesh, 'mx' : 2, 'my' : 3, # Aerodynamic performance of the lifting surface at # an angle of attack of 0 (alpha=0). # These CL0 and CD0 values are added to the CL and CD # obtained from aerodynamic analysis of the surface to get # the total CL and CD. # These CL0 and CD0 values do not vary wrt alpha. 'CL0' : 0.0, # CL of the surface at alpha=0 'CD0' : 0.015, # CD of the surface at alpha=0 # Airfoil properties for viscous drag calculation 'k_lam' : 0.05, # percentage of chord with laminar # flow, used for viscous drag 't_over_c_cp' : np.array([0.15]), # thickness over chord ratio (NACA0015) 'c_max_t' : .303, # chordwise location of maximum (NACA0015) # thickness 'with_viscous' : True, # if true, compute viscous drag 'with_wave' : False, # if true, compute wave drag } surfaces = [surf_dict] # Create the problem and the model group prob = Problem() indep_var_comp = IndepVarComp() indep_var_comp.add_output('v', val=248.136, units='m/s') indep_var_comp.add_output('alpha', val=6.64, units='deg') indep_var_comp.add_output('Mach_number', val=0.84) indep_var_comp.add_output('re', val=1.e6, units='1/m') indep_var_comp.add_output('rho', val=0.38, units='kg/m**3') indep_var_comp.add_output('cg', val=np.zeros((3)), units='m') prob.model.add_subsystem('prob_vars', indep_var_comp, promotes=['*']) # Loop over each surface in the surfaces list for surface in surfaces: filename = write_FFD_file(surface, surface['mx'], surface['my']) DVGeo = DVGeometry(filename) geom_group = Geometry(surface=surface, DVGeo=DVGeo) # Add tmp_group to the problem as the name of the surface. # Note that is a group and performance group for each # individual surface. prob.model.add_subsystem(surface['name'], geom_group) # Loop through and add a certain number of aero points for i in range(1): # Create the aero point group and add it to the model aero_group = AeroPoint(surfaces=surfaces) point_name = 'aero_point_{}'.format(i) prob.model.add_subsystem(point_name, aero_group) # Connect flow properties to the analysis point prob.model.connect('v', point_name + '.v') prob.model.connect('alpha', point_name + '.alpha') prob.model.connect('Mach_number', point_name + '.Mach_number') prob.model.connect('re', point_name + '.re') prob.model.connect('rho', point_name + '.rho') prob.model.connect('cg', point_name + '.cg') # Connect the parameters within the model for each aero point for surface in surfaces: name = surface['name'] # Connect the mesh from the geometry component to the analysis point prob.model.connect(name + '.mesh', point_name + '.' + name + '.def_mesh') # Perform the connections with the modified names within the # 'aero_states' group. prob.model.connect(name + '.mesh', point_name + '.aero_states.' + name + '_def_mesh') prob.model.connect(name + '.t_over_c', point_name + '.' + name + '_perf.' + 't_over_c') from openmdao.api import pyOptSparseDriver prob.driver = pyOptSparseDriver() prob.driver.options['optimizer'] = "SNOPT" prob.driver.opt_settings = {'Major optimality tolerance': 1.0e-6, 'Major feasibility tolerance': 1.0e-6} # Setup problem and add design variables, constraint, and objective prob.model.add_design_var('alpha', lower=-15, upper=15) prob.model.add_design_var('wing.shape', lower=-3, upper=2) # prob.model.add_constraint('wing.shape', equals=0., indices=range(surf_dict['my'] * 2), linear=True) prob.model.add_constraint(point_name + '.wing_perf.CL', equals=0.5) prob.model.add_objective(point_name + '.wing_perf.CD', scaler=1e4) # Set up the problem prob.setup() # view_model(prob, outfile='aero.html', show_browser=False) # prob.run_model() prob.run_driver() assert_rel_error(self, prob['aero_point_0.wing_perf.CD'][0], 0.03398038, 1e-6) assert_rel_error(self, prob['aero_point_0.wing_perf.CL'][0], 0.5, 1e-6) assert_rel_error(self, prob['aero_point_0.CM'][1], -1.7736315914915437, 1e-5)
'nkswitchtol':1e-2, 'adjointl2convergence': 1e-15, 'nkls':'non monotone', 'blocksplitting': True } ) h = 1e-40 # Setup aeroproblem, cfdsolver ap = AeroProblem(name='mdo_tutorial', alpha=1.8, mach=0.80, R=287.87, altitude=10000.0, areaRef=45.5, chordRef=3.25, evalFuncs=['cl','cmz','drag']) CFDSolver = ADFLOW(options=aeroOptions) if 'complex' in sys.argv: DVGeo = DVGeometry('../inputFiles/mdo_tutorial_ffd.fmt', complex=True) else: DVGeo = DVGeometry('../inputFiles/mdo_tutorial_ffd.fmt', complex=False) nTwist = 2 DVGeo.addRefAxis('wing', pyspline.Curve(x=numpy.linspace(5.0/4.0, 1.5/4.0+7.5, nTwist), y=numpy.zeros(nTwist), z=numpy.linspace(0,14, nTwist), k=2)) def twist(val, geo): for i in range(nTwist): geo.rot_z['wing'].coef[i] = val[i] def span(val, geo): # Span C = geo.extractCoef('wing') s = geo.extractS('wing')
areaRef=1.0, chordRef=1.0, evalFuncs=["cl", "cd"], ) # Add angle of attack variable ap.addDV("alpha", value=alpha[i], lower=0, upper=10.0, scale=1.0) aeroProblems.append(ap) # rst aeroproblem (end) # ====================================================================== # Geometric Design Variable Set-up # ====================================================================== # rst dvgeo (beg) # Create DVGeometry object FFDFile = "ffd.xyz" DVGeo = DVGeometry(FFDFile) # DVGeo = DVGeometry_FFD(FFDFile) DVGeo.addGeoDVLocal("shape", lower=-0.05, upper=0.05, axis="y", scale=1.0) span = 1.0 pos = np.array([0.5]) * span CFDSolver.addSlices("z", pos, sliceType="absolute") # Add DVGeo object to CFD solver CFDSolver.setDVGeo(DVGeo) # rst dvgeo (end) # ====================================================================== # DVConstraint Setup # ====================================================================== # rst dvcon (beg) DVCon = DVConstraints() # DVCon = DVConstraints_FFD_data() DVCon.setDVGeo(DVGeo)
C = geo.extractCoef("wing") C[-1, 0] += val[0] C[-1, 1] += val[1] C[-1, 2] += val[2] # Also need to get "dihedral" angle for this section theta = numpy.arctan(val[1] / (C[-1, 2] - C[-2, 2])) geo.rot_x["wing"].coef[-1] = -theta * 180 / numpy.pi geo.restoreCoef(C, "wing") # Set the chord as well geo.scale["wing"].coef[-1] = val[3] coords = hyp.getSurfaceCoordinates() DVGeo = DVGeometry(ffdFile) coef = DVGeo.FFD.vols[0].coef.copy() # First determine the reference chord lengths: nSpan = coef.shape[2] ref = numpy.zeros((nSpan, 3)) for k in range(nSpan): max_x = numpy.max(coef[:, :, k, 0]) min_x = numpy.min(coef[:, :, k, 0]) ref[k, 0] = min_x + 0.25 * (max_x - min_x) ref[k, 1] = numpy.average(coef[:, :, k, 1]) ref[k, 2] = numpy.average(coef[:, :, k, 2]) c0 = Curve(X=ref, k=2)
def test_boxes(self, train=False): # box1 and box2 intersect # box3 does not intersect anything comps = ["box1", "box2", "box3"] ffdFiles = [os.path.join(inputDir, f"{comp}.xyz") for comp in comps] triMeshFiles = [os.path.join(inputDir, f"{comp}.cgns") for comp in comps] # Set up component DVGeo objects DVGeoBox1 = DVGeometry(ffdFiles[0]) DVGeoBox2 = DVGeometry(ffdFiles[1]) DVGeoBox3 = DVGeometry(ffdFiles[2]) # Set up DVGeometryMulti object DVGeo = DVGeometryMulti() DVGeo.addComponent("box1", DVGeoBox1, triMeshFiles[0]) DVGeo.addComponent("box2", DVGeoBox2, triMeshFiles[1]) DVGeo.addComponent("box3", DVGeoBox3, None) # Define some feature curves featureCurves = [ # Curves on box1 "part_15_1d", # Curves on box2 "part_35_1d", "part_37_1d", "part_39_1d", ] curveEpsDict = { # Curves on box1 "part_15_1d": 1e-3, # Curves on box2 "part_35_1d": 1e-3, "part_37_1d": 1e-3, "part_39_1d": 1e-3, # Intersection curve "intersection": 1e-3, } # Track some intersecting surfaces trackSurfaces = { # box1 "part_14": 1e-3, # box2 "part_39": 1e-3, } # Exclude some intersecting surfaces excludeSurfaces = { # box1 "part_15": 1e-3, # box2 "part_40": 1e-3, } # Add the intersection between box1 and box2 DVGeo.addIntersection( "box1", "box2", dStarA=0.15, dStarB=0.15, featureCurves=featureCurves, project=True, includeCurves=True, curveEpsDict=curveEpsDict, trackSurfaces=trackSurfaces, excludeSurfaces=excludeSurfaces, ) # Add a few design variables DVGeoDict = DVGeo.getDVGeoDict() for comp in comps: # Create reference axis nRefAxPts = DVGeoDict[comp].addRefAxis("box", xFraction=0.5, alignIndex="j", rotType=4) nTwist = nRefAxPts - 1 # Set up a twist variable def twist(val, geo, nRefAxPts=nRefAxPts): for i in range(1, nRefAxPts): geo.rot_z["box"].coef[i] = val[i - 1] DVGeoDict[comp].addGlobalDV(dvName=f"{comp}_twist", value=[0] * nTwist, func=twist) # Define a test point set pts = np.array( [ # Points on box1 away from intersection [0.0, 0.0, 0.0], [0.5, 0.1, -0.5], # Points on box2 away from intersection [0.5, 0.0, 2.0], [0.375, 0.125, 2.0], # Point on box3 [2.5, 0.5, 0.0], # Point on curve part_15_1d [0.3, 0.0, 0.5], # Point on curve part_35_1d [0.75, -0.25, 0.6], # Point on curve part_37_1d [0.25, -0.25, 0.6], # Point on curve part_39_1d [0.25, 0.25, 0.6], # Point on intersection curve [0.25, 0.1, 0.5], # Point on tracked surface part_14, box1 [0.5, -0.3, 0.5], # Point on tracked surface part_39, box2 [0.25, 0.12, 0.51], # Point on excluded surface part_15, box1 [0.5, 0.3, 0.5], # Point on excluded surface part_40, box2 [0.375, 0.25, 0.6], # Other points near the intersection [0.25, 0.251, 0.5], [0.5, 0.251, 0.5], [0.51, 0.25, 0.4], [0.75, 0.25, 0.6], [0.5, 0.25, 0.6], [0.25, 0.5, 0.6], [0.5, -0.25, 0.6], [0.25, -0.5, 0.6], ] ) # Add the point set ptSetName = "test_set" comm = MPI.COMM_WORLD DVGeo.addPointSet(pts, ptSetName, comm=comm, applyIC=True) # Apply twist to the two intersecting boxes dvDict = DVGeo.getValues() dvDict["box1_twist"] = 2 dvDict["box2_twist"] = 2 DVGeo.setDesignVars(dvDict) # Update the point set ptsUpdated = DVGeo.update(ptSetName) # Regression test the updated points refFile = os.path.join(baseDir, "ref/test_DVGeometryMulti.ref") with BaseRegTest(refFile, train=train) as handler: handler.par_add_val("ptsUpdated", ptsUpdated, tol=1e-14) # Now we will test the derivatives # Build a dIdpt array # We will have nNodes*3 many functions of interest nNodes = pts.shape[0] dIdpt = np.zeros((nNodes * 3, nNodes, 3)) # Set the seeds to one such that we get individual derivatives for each coordinate of each point # The first function of interest gets the first coordinate of the first point # The second function gets the second coordinate of first point, and so on for i in range(nNodes): for j in range(3): dIdpt[i * 3 + j, i, j] = 1 # Get the derivatives from DVGeo funcSens = DVGeo.totalSensitivity(dIdpt, ptSetName) # Perturb the DVs with finite differences and compute FD gradients dvDict = DVGeo.getValues() funcSensFD = {} dh = 1e-5 for x in dvDict: nx = len(dvDict[x]) funcSensFD[x] = np.zeros((nx, nNodes * 3)) for i in range(nx): xRef = dvDict[x][i].copy() # Compute the central difference dvDict[x][i] = xRef + dh DVGeo.setDesignVars(dvDict) ptsNewPlus = DVGeo.update(ptSetName) dvDict[x][i] = xRef - dh DVGeo.setDesignVars(dvDict) ptsNewMinus = DVGeo.update(ptSetName) funcSensFD[x][i, :] = (ptsNewPlus.flatten() - ptsNewMinus.flatten()) / (2 * dh) # Set the DV back to the original value dvDict[x][i] = xRef.copy() # Check that the analytic derivatives are consistent with FD for x in dvDict: np.testing.assert_allclose(funcSens[x].T, funcSensFD[x], rtol=1e-4, atol=1e-10) # Test that adding a point outside any FFD raises an Error with self.assertRaises(Error): DVGeo.addPointSet(np.array([[-1.0, 0.0, 0.0]]), "test_error")
def test(self): from openaerostruct.geometry.utils import generate_mesh, write_FFD_file from openaerostruct.integration.aerostruct_groups import AerostructGeometry, AerostructPoint import openmdao.api as om from pygeo import DVGeometry # Create a dictionary to store options about the surface mesh_dict = {'num_y' : 5, 'num_x' : 2, 'wing_type' : 'CRM', 'symmetry' : True, 'num_twist_cp' : 5} mesh, twist_cp = generate_mesh(mesh_dict) surf_dict = { # Wing definition 'name' : 'wing', # name of the surface 'symmetry' : True, # if true, model one half of wing # reflected across the plane y = 0 'S_ref_type' : 'wetted', # how we compute the wing area, # can be 'wetted' or 'projected' 'fem_model_type' : 'tube', 'thickness_cp' : np.array([.1, .2, .3]), 'mesh' : mesh, 'geom_manipulator' : 'FFD', 'mx' : 2, 'my' : 3, # Aerodynamic performance of the lifting surface at # an angle of attack of 0 (alpha=0). # These CL0 and CD0 values are added to the CL and CD # obtained from aerodynamic analysis of the surface to get # the total CL and CD. # These CL0 and CD0 values do not vary wrt alpha. 'CL0' : 0.0, # CL of the surface at alpha=0 'CD0' : 0.015, # CD of the surface at alpha=0 # Airfoil properties for viscous drag calculation 'k_lam' : 0.05, # percentage of chord with laminar # flow, used for viscous drag 't_over_c_cp' : np.array([0.15]), # thickness over chord ratio (NACA0015) 'c_max_t' : .303, # chordwise location of maximum (NACA0015) # thickness 'with_viscous' : True, 'with_wave' : False, # if true, compute wave drag # Structural values are based on aluminum 7075 'E' : 70.e9, # [Pa] Young's modulus of the spar 'G' : 30.e9, # [Pa] shear modulus of the spar 'yield' : 500.e6 / 2.5, # [Pa] yield stress divided by 2.5 for limiting case 'mrho' : 3.e3, # [kg/m^3] material density 'fem_origin' : 0.35, # normalized chordwise location of the spar 'wing_weight_ratio' : 2., 'struct_weight_relief' : False, # True to add the weight of the structure to the loads on the structure 'distributed_fuel_weight' : False, # Constraints 'exact_failure_constraint' : False, # if false, use KS function } surfaces = [surf_dict] # Create the problem and assign the model group prob = om.Problem() # Add problem information as an independent variables component indep_var_comp = om.IndepVarComp() indep_var_comp.add_output('v', val=248.136, units='m/s') indep_var_comp.add_output('alpha', val=5., units='deg') indep_var_comp.add_output('Mach_number', val=0.84) indep_var_comp.add_output('re', val=1.e6, units='1/m') indep_var_comp.add_output('rho', val=0.38, units='kg/m**3') indep_var_comp.add_output('CT', val=grav_constant * 17.e-6, units='1/s') indep_var_comp.add_output('R', val=11.165e6, units='m') indep_var_comp.add_output('W0', val=0.4 * 3e5, units='kg') indep_var_comp.add_output('speed_of_sound', val=295.4, units='m/s') indep_var_comp.add_output('load_factor', val=1.) indep_var_comp.add_output('empty_cg', val=np.zeros((3)), units='m') prob.model.add_subsystem('prob_vars', indep_var_comp, promotes=['*']) # Loop over each surface in the surfaces list for surface in surfaces: # Get the surface name and create a group to contain components # only for this surface name = surface['name'] filename = write_FFD_file(surface, surface['mx'], surface['my']) DVGeo = DVGeometry(filename) aerostruct_group = AerostructGeometry(surface=surface, DVGeo=DVGeo) # Add tmp_group to the problem with the name of the surface. prob.model.add_subsystem(name, aerostruct_group) # Loop through and add a certain number of aero points for i in range(1): point_name = 'AS_point_{}'.format(i) # Connect the parameters within the model for each aero point # Create the aero point group and add it to the model AS_point = AerostructPoint(surfaces=surfaces) prob.model.add_subsystem(point_name, AS_point) # Connect flow properties to the analysis point prob.model.connect('v', point_name + '.v') prob.model.connect('alpha', point_name + '.alpha') prob.model.connect('Mach_number', point_name + '.Mach_number') prob.model.connect('re', point_name + '.re') prob.model.connect('rho', point_name + '.rho') prob.model.connect('CT', point_name + '.CT') prob.model.connect('R', point_name + '.R') prob.model.connect('W0', point_name + '.W0') prob.model.connect('speed_of_sound', point_name + '.speed_of_sound') prob.model.connect('empty_cg', point_name + '.empty_cg') prob.model.connect('load_factor', point_name + '.load_factor') for surface in surfaces: com_name = point_name + '.' + name + '_perf' prob.model.connect(name + '.local_stiff_transformed', point_name + '.coupled.' + name + '.local_stiff_transformed') prob.model.connect(name + '.nodes', point_name + '.coupled.' + name + '.nodes') # Connect aerodyamic mesh to coupled group mesh prob.model.connect(name + '.mesh', point_name + '.coupled.' + name + '.mesh') # Connect performance calculation variables prob.model.connect(name + '.radius', com_name + '.radius') prob.model.connect(name + '.thickness', com_name + '.thickness') prob.model.connect(name + '.nodes', com_name + '.nodes') prob.model.connect(name + '.cg_location', point_name + '.' + 'total_perf.' + name + '_cg_location') prob.model.connect(name + '.structural_mass', point_name + '.' + 'total_perf.' + name + '_structural_mass') prob.model.connect(name + '.t_over_c', com_name + '.t_over_c') # Import the Scipy Optimizer and set the driver of the problem to use # it, which defaults to an SLSQP optimization method prob.driver = om.ScipyOptimizeDriver() recorder = om.SqliteRecorder("aerostruct_ffd.db") prob.driver.add_recorder(recorder) prob.driver.recording_options['record_derivatives'] = True prob.driver.recording_options['includes'] = ['*'] # Setup problem and add design variables, constraint, and objective prob.model.add_design_var('wing.shape', lower=-3, upper=2) prob.model.add_design_var('wing.thickness_cp', lower=0.01, upper=0.5, scaler=1e2) prob.model.add_constraint('AS_point_0.wing_perf.failure', upper=0.) prob.model.add_constraint('AS_point_0.wing_perf.thickness_intersects', upper=0.) # Add design variables, constraisnt, and objective on the problem prob.model.add_design_var('alpha', lower=-10., upper=10.) prob.model.add_constraint('AS_point_0.L_equals_W', equals=0.) prob.model.add_objective('AS_point_0.fuelburn', scaler=1e-5) # iprofile.setup() # iprofile.start() # Set up the problem prob.setup() # om.view_model(prob, outfile='aerostruct_ffd', show_browser=False) # prob.run_model() prob.run_driver() # prob.check_partials(compact_print=True) # print("\nWing CL:", prob['aero_point_0.wing_perf.CL']) # print("Wing CD:", prob['aero_point_0.wing_perf.CD']) # from helper import plot_3d_points # # mesh = prob['aero_point_0.wing.def_mesh'] # plot_3d_points(mesh) # # filename = mesh_dict['wing_type'] + '_' + str(mesh_dict['num_x']) + '_' + str(mesh_dict['num_y']) # filename += '_' + str(surf_dict['mx']) + '_' + str(surf_dict['my']) + '.mesh' # np.save(filename, mesh) assert_rel_error(self, prob['AS_point_0.fuelburn'][0], 97680.8964568375, 1e-3)
Xpt[:100, 1] = 0.5 * np.sin(t) Xpt[:100, 2] = 0.0 Xpt[100:, 0] = 0.5 * np.cos(t) + 0.5 Xpt[100:, 1] = 0.5 * np.sin(t) Xpt[100:, 2] = 1.0 # rst create DVGeo # The Plot3D file ffdbox.xyz contains the coordinates of the free-form deformation (FFD)volume # we will be using for this problem. It's a cube with sides of length 1 centered on (0, 0,0.5). # The "i" direction of the cube consists of 10 points along the x axis # The "j" direction of the cube is 2 points up and down (y axis direction) # The "k" direction of the cube is 2 points into the page (z axis direction) FFDfile = "ffdbox.xyz" # initialize the DVGeometry object with the FFD file DVGeo = DVGeometry(FFDfile) # rst add pointset # add the cylinder pointset to the FFD under the name 'cylinder' DVGeo.addPointSet(Xpt.copy(), "cylinder") DVGeo.writePointSet("cylinder", "pointset") # rst add shape DV # Now that we have pointsets added, we should parameterize the geometry. # Adding local geometric design to make local modifications to FFD box # This option will perturb all the control points but only the y (up-down) direction DVGeo.addLocalDV("shape", lower=-0.5, upper=0.5, axis="y", scale=1.0) # rst getLocalIndex # The control points of the FFD are the same as the coordinates of the points in the input file
mach=0.8, altitude=10000, areaRef=45.5, chordRef=3.25, evalFuncs=["cl", "cd"]) # Add angle of attack variable ap.addDV("alpha", value=1.5, lower=0, upper=10.0, scale=0.1) # rst aeroproblem (end) # ====================================================================== # Geometric Design Variable Set-up # ====================================================================== # rst dvgeo (beg) # Create DVGeometry object FFDFile = "ffd.xyz" DVGeo = DVGeometry(FFDFile) # Create reference axis nRefAxPts = DVGeo.addRefAxis("wing", xFraction=0.25, alignIndex="k") nTwist = nRefAxPts - 1 # Set up global design variables def twist(val, geo): for i in range(1, nRefAxPts): geo.rot_z["wing"].coef[i] = val[i - 1] DVGeo.addGlobalDV(dvName="twist", value=[0] * nTwist, func=twist,
def test_24_rot0_nonaligned(self, train=False, refDeriv=False): """ Test 24 This test ensures that the scaling attributes (scale_x, scale_y, and scale_z) are effective when rotType=0 is selected. Moreover, this test ensures that rotType=0 reference axis can handle (given appropriate input parameters) FFD blocks that are not aligned with the main system of reference, e.g. the blades of a 3-bladed wind turbine rotor. The newly added input parameters rot0ang and rot0axis are used to provide the user control on this. The operations that pyGeo performs for this test are the following: We start from an initial "vertical" FFD box which, using the combination of rotType=0, rot0ang=-90, and rot0axis=[1,0,0] for addRefAxis(), is first rotated to have its "spanwise" axis along the y axis. Then, the script scales the 2nd section along the z axis for a "thickness" increase and the 4th section along the x axis for "chord" increase, it adds a +/- 30 deg twist respectively, and finally rotates the deformed FFD back in the initial position. The twist is added to ensure that the operation order is maintained, and the scaling preserves the orthogonality of the FFD in the section plane. This is a particular case as the FFD box is already aligned with the main axis and we "flip" the y and z axes, but the same criteria can be applied to a general rotation. """ refFile = os.path.join(self.base_path, "ref/test_DVGeometry_24.ref") with BaseRegTest(refFile, train=train) as handler: handler.root_print("Test twist and scaling for FFDs non-aligned to main system of reference") DVGeo = DVGeometry(os.path.join(self.base_path, "../../input_files/2x1x8_rectangle.xyz")) rotType = 0 xfraction = 0.5 nRefAxPts = DVGeo.addRefAxis("RefAx", xFraction=xfraction, alignIndex="k", rotType=rotType, rot0ang=-90) fix_root_sect = 1 nTwist = nRefAxPts - fix_root_sect DVGeo.addGlobalDV(dvName="twist", value=[0] * nTwist, func=commonUtils.twist, lower=-90, upper=90, scale=1) DVGeo.addGlobalDV( dvName="thickness", value=[1.0] * nTwist, func=commonUtils.thickness, lower=0.7, upper=5.0, scale=1 ) DVGeo.addGlobalDV( dvName="chord", value=[1.0] * nTwist, func=commonUtils.chord, lower=0.7, upper=5.0, scale=1 ) commonUtils.testSensitivities(DVGeo, refDeriv, handler, pointset=2) x = DVGeo.getValues() # Modifying the twist keyName = "twist" twistTest = [30, 0, -30] x[keyName] = twistTest # Modifying the chord keyName = "thickness" thickTest = [3.0, 1.0, 1.0] x[keyName] = thickTest # Modifying the chord keyName = "chord" chordTest = [1.0, 1.0, 2.0] x[keyName] = chordTest DVGeo.setDesignVars(x) DVGeo.update("testPoints") FFD_coords = DVGeo.FFD.coef.copy() handler.root_add_val("Updated FFD coordinates", FFD_coords, rtol=1e-12, atol=1e-12)
def setup_blocks(self, testID, isComplex=False): # Make tiny FFD ffd_name = '../inputFiles/tiny_cube_{:02d}.xyz'.format(testID) file_name = os.path.join(self.base_path, ffd_name) self.make_cube_ffd(file_name, 1, 1, 1, 1, 1, 1) tiny = DVGeometry(file_name, child=True, complex=isComplex) os.remove(file_name) tiny.addRefAxis('ref', xFraction=0.5, alignIndex='j', rotType=7) # Make tiny FFD ffd_name = '../inputFiles/small_cube_{:02d}.xyz'.format(testID) file_name = os.path.join(self.base_path, ffd_name) self.make_cube_ffd(file_name, 0, 0, 0, 2, 2, 2) small = DVGeometry(file_name, child=True, complex=isComplex) os.remove(file_name) small.addRefAxis('ref', xFraction=0.5, alignIndex='j') # Make big FFD ffd_name = '../inputFiles/big_cube_{:02d}.xyz'.format(testID) file_name = os.path.join(self.base_path, ffd_name) self.make_cube_ffd(file_name, 0, 0, 0, 3, 3, 3) big = DVGeometry(file_name, complex=isComplex) os.remove(file_name) big.addRefAxis('ref', xFraction=0.5, alignIndex='i') big.addChild(small) small.addChild(tiny) # Add point set points = numpy.array([ [0.5, 0.5, 0.5], [1.25, 1.25, 1.25], [1.5, 1.5, 1.5], [2.0, 2.5, 0.5], ]) big.addPointSet(points, 'X') return big, small, tiny
"gridFile": os.getcwd(), "fileType": "OpenFOAM", "symmetryPlanes": [[[0, 0, 0], [0, 1, 0]]], "aExp": 3.0, "bExp": 5.0, "alpha": 1.0, "LdefFact": 0.20, } mesh = USMesh(options=meshOptions, comm=gcomm) coords0 = mesh.getSurfaceCoordinates() # setup FFD FFDFile = "./FFD/globalFFD.fmt" DVGeo = DVGeometry(FFDFile) # Setup curves for ref_axis x = [-2.0, 0.0, 0.1, 1.044, 5.0] y = [0.1, 0.1, 0.1, 0.1, 0.1] z = [0.1, 0.1, 0.1, 0.1, 0.1] nLength = len(x) c1 = Curve(x=x, y=y, z=z, k=2) DVGeo.addRefAxis("bodyAxis", curve=c1, axis="z") DVGeoChild = DVGeometry("./FFD/bodyFittedFFD.fmt", child=True) # Setup curves for ref_axis x1 = [0.0, 0.1, 0.862, 1.044] y1 = [0.1, 0.1, 0.1, 0.1]
def test_parent_shape_child_rot(self, train=False, refDeriv=False): ffd_name = '../../tests/inputFiles/small_cube.xyz' self.make_cube_ffd(ffd_name, 0.1, 0.1, 0.1, 0.8, 0.8, 0.8) small = DVGeometry(ffd_name, child=True) small.addRefAxis('ref', xFraction=0.5, alignIndex='j') x0 = 0.0 y0 = 0.0 z0 = 0.0 dx = 1.0 dy = 1.0 dz = 1.0 axes = ['k', 'j', 'i'] slices = numpy.array( # Slice 1 [ [[[x0, y0, z0], [x0 + dx, y0, z0], [x0 + 2 * dx, y0, z0]], [[x0, y0 + dy, z0], [x0 + dx, y0 + dy, z0], [x0 + 2 * dx, y0 + dy, z0]]], # Slice 2 [[[x0, y0, z0 + dz], [x0 + dx, y0, z0 + dz], [x0 + 2 * dx, y0, z0 + dz]], [[x0, y0 + dy, z0 + dz], [x0 + dx, y0 + dy, z0 + dz], [x0 + 2 * dx, y0 + dy, z0 + dz]]] ], dtype='d') N0 = [2] N1 = [2] N2 = [3] ffd_name = '../../tests/inputFiles/big_cube.xyz' geo_utils.write_wing_FFD_file(ffd_name, slices, N0, N1, N2, axes=axes) big = DVGeometry(ffd_name) big.addRefAxis('ref', xFraction=0.5, alignIndex='j') big.addChild(small) # Add point set points = numpy.array([[0.5, 0.5, 0.5]]) big.addPointSet(points, 'X') # Add only translation variables add_vars(big, 'big', local='z', rotate='y') add_vars(small, 'small', rotate='y') ang = 45 ang_r = numpy.deg2rad(ang) # Modify design variables x = big.getValues() # add a local shape change x['local_z_big'] = numpy.array( [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.5, 0.5, 0.5, 0.5]) big.setDesignVars(x) Xs = big.update('X') # add a rotation of the child FFD x['rotate_y_small'] = ang big.setDesignVars(x) Xrot_ffd = big.update('X') # the modification caused by the child FFD should be the same as rotating the deformed point of the parent # (you would think) rot_mat = numpy.array([[numpy.cos(ang_r), 0, numpy.sin(ang_r)], [0, 1, 0], [-numpy.sin(ang_r), 0, numpy.cos(ang_r)]]) Xrot = numpy.dot(rot_mat, (Xs - points).T) + points.T numpy.testing.assert_array_almost_equal(Xrot_ffd.T, Xrot)
def test(self): from openaerostruct.geometry.utils import generate_mesh, write_FFD_file from openaerostruct.geometry.geometry_group import Geometry from openaerostruct.aerodynamics.aero_groups import AeroPoint from openaerostruct.integration.multipoint_comps import MultiCD import openmdao.api as om from openmdao.utils.assert_utils import assert_check_partials from pygeo import DVGeometry # Create a dictionary to store options about the surface mesh_dict = { 'num_y': 5, 'num_x': 3, 'wing_type': 'CRM', 'symmetry': True, 'num_twist_cp': 5, 'span_cos_spacing': 0.0, } mesh, _ = generate_mesh(mesh_dict) surf_dict = { # Wing definition 'name': 'wing', # name of the surface 'symmetry': True, # if true, model one half of wing # reflected across the plane y = 0 'S_ref_type': 'wetted', # how we compute the wing area, # can be 'wetted' or 'projected' 'fem_model_type': 'tube', 'mesh': mesh, 'mx': 2, 'my': 3, 'geom_manipulator': 'FFD', # Aerodynamic performance of the lifting surface at # an angle of attack of 0 (alpha=0). # These CL0 and CD0 values are added to the CL and CD # obtained from aerodynamic analysis of the surface to get # the total CL and CD. # These CL0 and CD0 values do not vary wrt alpha. 'CL0': 0.0, # CL of the surface at alpha=0 'CD0': 0.015, # CD of the surface at alpha=0 # Airfoil properties for viscous drag calculation 'k_lam': 0.05, # percentage of chord with laminar # flow, used for viscous drag 't_over_c_cp': np.array([0.15]), # thickness over chord ratio (NACA0015) 'c_max_t': 0.303, # chordwise location of maximum (NACA0015) # thickness 'with_viscous': True, # if true, compute viscous drag 'with_wave': False, # if true, compute wave drag } surfaces = [surf_dict] n_points = 2 # Create the problem and the model group prob = om.Problem() indep_var_comp = om.IndepVarComp() indep_var_comp.add_output('v', val=248.136, units='m/s') indep_var_comp.add_output('alpha', val=np.ones(n_points) * 6.64, units='deg') indep_var_comp.add_output('Mach_number', val=0.84) indep_var_comp.add_output('re', val=1.0e6, units='1/m') indep_var_comp.add_output('rho', val=0.38, units='kg/m**3') indep_var_comp.add_output('cg', val=np.zeros((3)), units='m') prob.model.add_subsystem('prob_vars', indep_var_comp, promotes=['*']) # Loop over each surface in the surfaces list for surface in surfaces: # Get the surface name and create a group to contain components # only for this surface name = surface['name'] # FFD setup filename = write_FFD_file(surface, surface['mx'], surface['my']) DVGeo = DVGeometry(filename) geom_group = Geometry(surface=surface, DVGeo=DVGeo) # Add tmp_group to the problem with the name of the surface. prob.model.add_subsystem(name + '_geom', geom_group) # Loop through and add a certain number of aero points for i in range(n_points): # Create the aero point group and add it to the model aero_group = AeroPoint(surfaces=surfaces) point_name = 'aero_point_{}'.format(i) prob.model.add_subsystem(point_name, aero_group) # Connect flow properties to the analysis point prob.model.connect('v', point_name + '.v') prob.model.connect('alpha', point_name + '.alpha', src_indices=[i]) prob.model.connect('Mach_number', point_name + '.Mach_number') prob.model.connect('re', point_name + '.re') prob.model.connect('rho', point_name + '.rho') prob.model.connect('cg', point_name + '.cg') # Connect the parameters within the model for each aero point for surface in surfaces: name = surface['name'] # Connect the drag coeff at this point to the multi_CD component, which does the summation. prob.model.connect(point_name + '.CD', 'multi_CD.' + str(i) + '_CD') # Connect the mesh from the geometry component to the analysis point prob.model.connect(name + '_geom.mesh', point_name + '.' + name + '.def_mesh') # Perform the connections with the modified names within the # 'aero_states' group. prob.model.connect( name + '_geom.mesh', point_name + '.aero_states.' + name + '_def_mesh') prob.model.connect( name + '_geom.t_over_c', point_name + '.' + name + '_perf.' + 't_over_c') prob.model.add_subsystem('multi_CD', MultiCD(n_points=n_points), promotes_outputs=['CD']) prob.driver = om.ScipyOptimizeDriver() # Setup problem and add design variables, constraint, and objective # design variable is the wing shape, and angle-of-attack at each point. prob.model.add_design_var('alpha', lower=-15, upper=15) prob.model.add_design_var('wing_geom.shape', lower=-3, upper=2) # set different target CL value at each point. prob.model.add_constraint('aero_point_0.wing_perf.CL', equals=0.45) prob.model.add_constraint('aero_point_1.wing_perf.CL', equals=0.5) # objective is the sum of CDs at each point. prob.model.add_objective('CD', scaler=1e4) # Set up the problem prob.setup() prob.run_model() # Check the partials at this point in the design space data = prob.check_partials(compact_print=True, out_stream=None, method='fd', step=1e-5) assert_check_partials(data, atol=1e20, rtol=1e-3)
#rst Import libraries import numpy from pygeo import DVGeometry from idwarp import USMesh #rst Create DVGeometry object FFDFile = 'ffd.xyz' DVGeo = DVGeometry(FFDFile) #rst Create reference axis nRefAxPts = DVGeo.addRefAxis('wing', xFraction=0.25, alignIndex='k') #rst Dihedral def dihedral(val, geo): C = geo.extractCoef('wing') for i in range(1, nRefAxPts): C[i, 1] += val[i - 1] geo.restoreCoef(C, 'wing') #rst Twist def twist(val, geo): for i in range(1, nRefAxPts): geo.rot_z['wing'].coef[i] = val[i - 1] #rst Taper def taper(val, geo): s = geo.extractS('wing') slope = (val[1] - val[0]) / (s[-1] - s[0])