def addFaceTraction(order, assembler, load): # Create the surface traction aux = TACS.AuxElements() # Get the element node locations nelems = assembler.getNumElements() for i in range(nelems): # Get the information about the given element elem, Xpt, vars0, dvars, ddvars = assembler.getElementData(i) # Loop over the nodes and create the traction forces in the # x/y/z directions nnodes = order*order tx = np.zeros(nnodes, dtype=TACS.dtype) ty = np.zeros(nnodes, dtype=TACS.dtype) tz = np.zeros(nnodes, dtype=TACS.dtype) # Set the components of the surface traction for j in range(nnodes): x = Xpt[3*j] y = Xpt[3*j+1] z = Xpt[3*j+2] theta = -R*np.arctan2(y, x) p = -load*np.sin(beta*z)*np.sin(alpha*theta) tx[j] = p*Xpt[3*j]/R ty[j] = p*Xpt[3*j+1]/R tz[j] = 0.0 trac = elements.ShellTraction(order, tx, ty, tz) aux.addElement(i, trac) return aux
def createAuxElements(self, assembler, order=3): aux = TACS.AuxElements() tx = np.zeros(order * order) ty = np.zeros(order * order) tz = -np.ones(order * order) trac = elements.ShellTraction(order, tx, ty, tz) for i in range(assembler.getNumElements()): aux.addElement(i, trac) return aux
def computeTractionLoad(name, forest, assembler, trac): """ Add a surface traction to all quadrants or octants that touch a face or edge with the given name. The assembler must be created from the provided forest. The list trac must have a traction for each face (6) for octants or each edge (4) for quadrants. Note: This code uses the fact that the getOctsWithName or getQuadsWithName returns the local face or edge index touching the surface or edge in the info member. Args: name (str): Name of the surface where the traction will be added forest (QuadForest or OctForest): Forest for the finite-element mesh assembler (Assembler): TACSAssembler object for the finite-element problem trac (list): List of tractions, one for each possible face/edge orientation Returns: Vec: A force vector containing the traction """ if isinstance(forest, TMR.OctForest): octants = forest.getOctants() face_octs = forest.getOctsWithName(name) elif isinstance(forest, TMR.QuadForest): octants = forest.getQuadrants() face_octs = forest.getQuadsWithName(name) # Create the force vector and zero the variables in the assembler force = assembler.createVec() assembler.zeroVariables() # Create the auxiliary element class aux = TACS.AuxElements() for i in range(len(face_octs)): index = face_octs[i].tag if index is not None: aux.addElement(index, trac[face_octs[i].info]) # Keep auxiliary elements already set in the assembler # aux_tmp = assembler.getAuxElements() assembler.setAuxElements(aux) # Compute the residual where force = -residual assembler.assembleRes(force) force.scale(-1.0) # Reset the auxiliary elements assembler.setAuxElements(None) # (aux_tmp) return force
def addFaceTraction(order, forest, attr, assembler, tr): trac = [] for findex in range(6): trac.append(elements.Traction3D(order, findex, tr[0], tr[1], tr[2])) # Retrieve octants from the forest octants = forest.getOctants() face_octs = forest.getOctsWithAttribute(attr) aux = TACS.AuxElements() for i in range(len(face_octs)): aux.addElement(face_octs[i].tag, trac[face_octs[i].info]) return aux
def rectangular_domain(nx, ny, Ly=100.0): # Set the y-dimension based on a unit aspect ratio Lx = (nx*Ly)/ny # Compute the total area area = Lx*Ly # Set the number of elements/nodes in the problem nnodes = (nx+1)*(ny+1) nelems = nx*ny nodes = np.arange(nnodes).reshape((nx+1, ny+1)) # Set the node locations xpts = np.zeros((nnodes, 3)) x = np.linspace(0, Lx, nx+1) y = np.linspace(0, Ly, ny+1) for j in range(ny+1): for i in range(nx+1): xpts[nodes[i,j],0] = x[i] xpts[nodes[i,j],1] = y[j] # Set the connectivity and create the corresponding elements conn = np.zeros((nelems, 4), dtype=np.intc) for j in range(ny): for i in range(nx): # Append the first set of nodes n = i + nx*j conn[n,0] = nodes[i, j] conn[n,1] = nodes[i+1, j] conn[n,2] = nodes[i, j+1] conn[n,3] = nodes[i+1, j+1] bcs = np.array(nodes[0,:], dtype=np.intc) # Create the tractions and add them to the surface surf = 1 # The u=1 positive surface tx = np.zeros(2) ty = -100*np.ones(2) trac = elements.PSQuadTraction(surf, tx, ty) # Create the auxiliary element class aux = TACS.AuxElements() for j in range(int(ny/8)): num = nx-1 + nx*j aux.addElement(num, trac) return xpts, conn, bcs, aux, area
def addEdgeTraction(order, forest, attr, assembler, tr): trac = [] for findex in range(4): trac.append(elements.PSQuadTraction(findex, tr[0]*np.ones(2), \ tr[1]*np.ones(2))) # Retrieve octants from the forest quadrants = forest.getQuadrants() edge_quads = forest.getQuadsWithAttribute(attr) aux = TACS.AuxElements() for i in range(len(edge_quads)): index = quadrants.findIndex(edge_quads[i]) if index is not None: aux.addElement(index, trac[edge_quads[i].tag]) return aux
def addFaceTraction(order, forest, assembler): # Get the quadrilaterals of interest quads = forest.getQuadsWithName('traction') # Create the surface traction aux = TACS.AuxElements() # Loop over the nodes and create the traction forces in the x/y/z # directions nnodes = order tx = np.zeros(nnodes, dtype=TACS.dtype) ty = np.zeros(nnodes, dtype=TACS.dtype) ty[:] = 10.0 # Create the shell traction surf = 1 trac = elements.PSQuadTraction(surf, tx, ty) for q in quads: aux.addElement(q.tag, trac) return aux
def addFaceTraction(order, assembler): # Create the surface traction aux = TACS.AuxElements() # Get the element node locations nelems = assembler.getNumElements() # Loop over the nodes and create the traction forces in the x/y/z # directions nnodes = order * order tx = np.zeros(nnodes, dtype=TACS.dtype) ty = np.zeros(nnodes, dtype=TACS.dtype) tz = np.zeros(nnodes, dtype=TACS.dtype) tz[:] = 5.0 # Create the shell traction trac = elements.ShellTraction(order, tx, ty, tz) for i in range(nelems): aux.addElement(i, trac) return aux
def createAssembler(m=5.0, c=0.5, k=5.0, u0=-0.5, udot0=1.0, pc=None): num_disps = 1 num_nodes = 1 # Spring element #spr = SpringMassDamper(num_disps, num_nodes, m, c, k) dspr = PSPACE.PySMD(m, c, k, u0, udot0) sprcb = SMDUpdate(dspr) sspr = PSPACE.PyStochasticElement(dspr, pc, sprcb) dforce = ForcingElement(num_disps, num_nodes, amplitude=1.0, omega=10.0) forcecb = ForceUpdate(dforce) sforce = PSPACE.PyStochasticElement(dforce, pc, forcecb) ndof_per_node = 1 * pc.getNumBasisTerms() num_owned_nodes = 1 num_elems = 1 # Add user-defined element to TACS comm = MPI.COMM_WORLD assembler = TACS.Assembler.create(comm, ndof_per_node, num_owned_nodes, num_elems) ptr = np.array([0, 1], dtype=np.intc) conn = np.array([0], dtype=np.intc) assembler.setElementConnectivity(ptr, conn) # Set elements assembler.setElements([sspr]) # set Auxiliary elements aux = TACS.AuxElements() aux.addElement(0, sforce) assembler.setAuxElements(aux) assembler.initialize() return assembler
elem = elements.MITCShell(order, stiff) return elem def cylinderEvalTraction(Xp): x = Xp[0] y = Xp[1] z = Xp[2] theta = -R * np.arctan2(y, x) p = -load * np.sin(beta * z) * np.sin(alpha * theta) return [p * x / R, p * y / R, 0.0] def addFaceTraction(case, order, assembler, load): # Create the surface traction aux = TACS.AuxElements() # Get the element node locations nelems = assembler.getNumElements() if case == 'cylinder': trac = elements.ShellTraction(order, evalf=cylinderEvalTraction) for i in range(nelems): aux.addElement(i, trac) elif case == 'disk': nnodes = order * order tx = np.zeros(nnodes, dtype=TACS.dtype) ty = np.zeros(nnodes, dtype=TACS.dtype) tz = np.zeros(nnodes, dtype=TACS.dtype) tz[:] = load
def bracket_domain(nb, nc, Lx=100.0): # Compute the total area area = 0.64 * Lx**2 # The total number of elements along one direction nx = nb + nc # Allocate all of the nodes (including blanked nodes) nodes = np.ones((nx + 1, nx + 1), dtype=np.int) nodes[nb + 1:, nb + 1:] = -1 # Allocate all of the elements (including blanked elements) elems = np.ones((nx, nx), dtype=np.int) elems[nb:, nb:] = -1 # Number the nodes index = 0 for j in range(nx + 1): for i in range(nx + 1): if nodes[i, j] >= 0: nodes[i, j] = index index += 1 # Set the element numbering index = 0 for j in range(nx): for i in range(nx): if elems[i, j] >= 0: elems[i, j] = index index += 1 # Set the boundary conditions bcs = np.array(nodes[:(nb + 1), -1], dtype=np.intc) # Compute the number of nodes and elements n = (nb + 1)**2 + 2 * (nb + 1) * nc ne = nb**2 + 2 * nb * nc # Allocate the arrays conn = np.zeros((ne, 4), dtype=np.intc) xpts = np.zeros((n, 3)) # Set the node locations for j in range(nx + 1): for i in range(nx + 1): if nodes[i, j] >= 0: xpts[nodes[i, j], 0] = (Lx * i) / nx xpts[nodes[i, j], 1] = (Lx * j) / nx # Set the nodal connectivity for j in range(nx): for i in range(nx): if elems[i, j] >= 0: conn[elems[i, j], 0] = nodes[i, j] conn[elems[i, j], 1] = nodes[i + 1, j] conn[elems[i, j], 2] = nodes[i, j + 1] conn[elems[i, j], 3] = nodes[i + 1, j + 1] # Create the tractions and add them to the surface surf = 1 # The u=1 positive surface tx = np.zeros(2) ty = 100 * np.ones(2) trac = elements.PSQuadTraction(surf, tx, ty) # Create the auxiliary element class aux = TACS.AuxElements() for j in range(int(5 * nb / 6), nb): aux.addElement(elems[-1, j], trac) return xpts, conn, bcs, aux, area
def createAssembler(tacs_comm): # Set constitutive properties rho = 4540.0 # density, kg/m^3 E = 118e9 # elastic modulus, Pa 118e9 nu = 0.325 # poisson's ratio ys = 1050e6 # yield stress, Pa kappa = 6.89 specific_heat = 463.0 # Set the back-pressure for the traction load pb = 654.9 #10.0 # A value for the surface pressure # Load in the mesh mesh = TACS.MeshLoader(tacs_comm) mesh.scanBDFFile('tacs_aero.bdf') # Get the number of components set in the mesh. Each component is num_components = mesh.getNumComponents() # Each domain consists of the union of two components. The component # corresponding to the surface traction and the component corresponding # to remaining plate elements. There is one design variable associated # with each domain that shares a common (x,z) coordinate num_domains = num_components // 2 # Create the constitutvie propertes and model props_plate = constitutive.MaterialProperties(rho=rho, specific_heat=specific_heat, kappp=kappa, E=E, nu=nu, ys=ys) # Create the basis class basis = elements.LinearHexaBasis() element_list = [] for k in range(num_domains): # Create the elements in an element list con = constitutive.SolidConstitutive(props_plate, t=1.0, tNum=k) phys_model = elements.LinearThermoelasticity3D(con) # Create the element element_list.append(elements.Element3D(phys_model, basis)) varsPerNode = phys_model.getVarsPerNode() # Set the face index for the side of the element where the traction # will be applied. The face indices are as follows for the hexahedral # basis class: # -x: 0, +x: 1, -y: 2, +y: 3, -z: 4, +z: 5 faceIndex = 4 # Set the wedge angle - 5 degrees theta = np.radians(5.0) # Compute the outward normal components for the face nx = np.sin(theta) nz = -np.cos(theta) # Set the traction components tr = [-pb * nx, 0.0, -pb * nz, 0.0] # Create the traction class traction = elements.Traction3D(varsPerNode, faceIndex, basis, tr) # Set the elements corresponding to each component number num_components = mesh.getNumComponents() for k in range(num_components): mesh.setElement(k, element_list[k % num_domains]) # Create the assembler object assembler = mesh.createTACS(varsPerNode) # Set the traction load into the assembler object aux = TACS.AuxElements(assembler) for k in range(num_domains, num_components): mesh.addAuxElement(aux, k, traction) assembler.setAuxElements(aux) return assembler, num_domains