def test_first_example(self): import math import numpy from numpy import array from spyfe.materials.mat_heatdiff import MatHeatDiff from spyfe.fesets.surfacelike import FESetT3 from spyfe.femms.femm_heatdiff import FEMMHeatDiff from spyfe.fields.nodal_field import NodalField from spyfe.integ_rules import TriRule from spyfe.fenode_set import FENodeSet # These are the constants in the problem, k is kappa a = 2.5 # radius on the columnthe dy = a / 2 * math.sin(15. / 180 * math.pi) dx = a / 2 * math.cos(15. / 180 * math.pi) Q = 4.5 # internal heat generation rate k = 1.8 # thermal conductivity m = MatHeatDiff(thermal_conductivity=array([[k, 0.0], [0.0, k]])) Dz = 1.0 # thickness of the slice xall = array([[0, 0], [dx, -dy], [dx, dy], [2 * dx, -2 * dy], [2 * dx, 2 * dy]]) fes = FESetT3(array([[1, 2, 3], [2, 4, 5], [2, 5, 3]]) - 1) femm = FEMMHeatDiff(material=m, fes=fes, integration_rule=TriRule(npts=1)) fens = FENodeSet(xyz=xall) geom = NodalField(fens=fens) temp = NodalField(nfens=xall.shape[0], dim=1) temp.set_ebc([3, 4]) temp.apply_ebc() temp.numberdofs(node_perm=[1, 2, 0, 4, 3]) print(temp.dofnums) K = femm.conductivity(geom, temp) print(K)
def test_fens_1(self): import math import numpy from spyfe.fenode_set import FENodeSet a = 2.5 # radius on the columnthe dy = a / 2 * math.sin(15. / 180 * math.pi) dx = a / 2 * math.cos(15. / 180 * math.pi) fens = FENodeSet(xyz=numpy.array([[0, 0], [dx, -dy], [dx, dy], [2 * dx, -2 * dy], [2 * dx, 2 * dy]])) print(fens.xyz)
def t3_ablock(Length, Width, nL, nW): """ Mesh of T3 elements of a rectangle. Alternate orientation. :param Length: :param Width: :param nL: :param nW: :param options: :return: See also: T3_blocku, T3_cblock, T3_crossblock, T3_ablock, T3_ablockc, T3_ablockn, T3_block, T3_blockc, T3_blockn """ nnodes = (nL + 1) * (nW + 1) ncells = 2 * (nL) * (nW) xs = OneBased2DArray((nnodes, 2)) conns = OneBased2DArray((ncells, 3), dtype=int) f = 1 for j in range_1based(1, (nW + 1)): for i in range_1based(1, (nL + 1)): xs[f, 0], xs[f, 1] = (i - 1) * Length / nL, (j - 1) * Width / nW f += 1 fens = FENodeSet(xs.raw_array()) def node_numbers1(i, j, nL, nW): f = (j - 1) * (nL + 1) + i return array([f, (f + 1), f + (nL + 1) + 1]) def node_numbers2(i, j, nL, nW): f = (j - 1) * (nL + 1) + i return array([f, f + (nL + 1) + 1, f + (nL + 1)]) gc = 1 for i in range_1based(1, nL): for j in range_1based(1, nW): nn = node_numbers1(i, j, nL, nW) conns[gc, :] = nn[:] gc += 1 nn = node_numbers2(i, j, nL, nW) conns[gc, :] = nn[:] gc += 1 fes = FESetT3(conn=conns.raw_array() - 1) return fens, fes
def h8_blockx(xs, ys, zs): """ Mesh of H8 elements of a rectangular 3D block. Mesh of a 3-D block, H8 finite elements. The nodes are located at the Cartesian product of the three intervals on the input. This allows for construction of graded meshes. :param xs: array of the X coordinates of the nodes :param ys: array of the Y coordinates of the nodes :param zs: array of the Z coordinates of the nodes :return: fens, fes See also: """ nL = len(xs) - 1 nW = len(ys) - 1 nH = len(zs) - 1 nnodes = (nL + 1) * (nW + 1) * (nH + 1) ncells = (nL) * (nW) * (nH) X = OneBased2DArray((nnodes, 3)) f = 1 for k in range_1based(1, (nH + 1)): for j in range_1based(1, (nW + 1)): for i in range_1based(1, (nL + 1)): X[f, 0], X[f, 1], X[f, 2] = xs[i - 1], ys[j - 1], zs[k - 1] f += 1 fens = FENodeSet(X.raw_array()) def node_numbers(i, j, k, nL, nW, nH): lf = (k - 1) * ((nL + 1) * (nW + 1)) + (j - 1) * (nL + 1) + i an = array([lf, (lf + 1), lf + (nL + 1) + 1, lf + (nL + 1)]) return hstack((an, an + ((nL + 1) * (nW + 1)))) conns = OneBased2DArray((ncells, 8), dtype=int) gc = 1 for i in range_1based(1, nL): for j in range_1based(1, nW): for k in range_1based(1, nH): nn = node_numbers(i, j, k, nL, nW, nH) conns[gc, :] = nn[:] gc += 1 fes = FESetH8(conn=conns.raw_array() - 1) return fens, fes
def q4_blockx(xs, ys): """ Mesh of Q4 elements of a rectangle. Mesh of a 2-D block, Q4 finite elements. The nodes are located at the Cartesian product of the two intervals on the input. This allows for construction of graded meshes. :param xs: array of the X coordinates of the nodes :param ys: array of the Y coordinates of the nodes :return: fens, fes See also: """ nL = len(xs) - 1 nW = len(ys) - 1 nnodes = (nL + 1) * (nW + 1) ncells = (nL) * (nW) X = OneBased2DArray((nnodes, 2)) f = 1 for j in range_1based(1, (nW + 1)): for i in range_1based(1, (nL + 1)): X[f, 0], X[f, 1] = xs[i - 1], ys[j - 1] f += 1 fens = FENodeSet(X.raw_array()) def node_numbers(i, j, nL, nW): f = (j - 1) * (nL + 1) + i return array([f, (f + 1), f + (nL + 1) + 1, f + (nL + 1)]).ravel() conns = OneBased2DArray((ncells, 4), dtype=int) gc = 1 for i in range_1based(1, nL): for j in range_1based(1, nW): nn = node_numbers(i, j, nL, nW) conns[gc, :] = nn[:] gc += 1 fes = FESetQ4(conn=conns.raw_array() - 1) return fens, fes
def import_mesh(filename): """Import Abaqus mesh. Import tetrahedral (4- and 10-node) or hexahedral (8- and 20-node) ABAQUS Mesh (.INP). Limitations: only the *NODE and *ELEMENT sections are read. Only 3D elements are handled. :param filename: :return: fens, feslist """ f = open(filename, 'r') chunk = 1000 node = numpy.zeros((chunk, 4)) # Find the node section while True: temp = f.readline() if temp == '': f.close() raise Exception('No nodes in mesh file?') temp = temp.strip() if temp[0:5].upper() == '*NODE': break # now we process the *NODE section # We have the node section, now we need to read the data nnode = 0 while True: temp = f.readline() temp = temp.strip() if temp[0] == '*': break # done with reading nodes A = temp.replace(',', ' ').split() node[nnode, :] = float(A[0]), float(A[1]), float(A[2]), float(A[3]) nnode = nnode + 1 if nnode >= node.shape[0]: node = numpy.vstack( (node, numpy.zeros((node.shape[1] + chunk, node.shape[1])))) # Now find and process all *ELEMENT blocks More_data = True elsets = [] while More_data: # Find the next block while temp[0] != '*': if len(temp) >= 8 and temp.upper()[0:8] == '*ELEMENT': break # got it temp = f.readline() if temp == '': f.close() More_data = False break temp = temp.strip() Valid, Type, Elset = _Parse_element_line(temp) if (Valid): # Valid element type nelem = 0 ennod = Type elem = numpy.zeros((chunk, ennod), dtype=int) while True: temp = f.readline() if temp == '': f.close() More_data = False break if temp[0] == '*': elem = elem[0:nelem, :] elsets.append((nelem, copy.deepcopy(elem), Elset)) break # done with reading this element block A = temp.replace(',', ' ').split() A = A[1:] # get rid of the element serial number for k in range(len(A)): elem[nelem, k] = int(A[k]) if len(A) < ennod: # we need to read a continuation line prevn = len(A) temp = f.readline() if temp == '': f.close() raise Exception('Premature end of element line') A = temp.replace(',', ' ').split() for k in range(len(A)): elem[nelem, prevn + k] = int(A[k]) if prevn + len(A) != ennod: raise Exception('Wrong number of element nodes') nelem += 1 if nelem >= elem.shape[0]: elem = numpy.vstack( (elem, numpy.zeros((chunk, elem.shape[1])))) else: temp = f.readline() if temp == '': More_data = False break # We are done with the file. f.close() # Some error checks node = node[0:nnode, :] if numpy.linalg.norm(numpy.array(numpy.arange(1, nnode + 1)) - node[:, 0]) != 0: raise Exception('Nodes are not in serial order') # Process output arguments # Extract coordinates fens = FENodeSet(xyz=node[:, 1:4]) # Now create all element sets feslist = [] for j, e in enumerate(elsets): nelem, elem, ElSet = e conn = elem - 1 if elem.shape[1] == 4: fes = FESetT4(conn=conn, label=j) elif elem.shape[1] == 10: fes = FESetT10(conn=conn, label=j) elif elem.shape[1] == 8: fes = FESetH8(conn=conn, label=j) elif elem.shape[1] == 20: fes = FESetH20(conn=conn, label=j) feslist.append(fes) return fens, feslist
def import_mesh(filename): """Import tetrahedral (4- and 10-node) NASTRAN mesh. Limitations: 1. only the GRID and CTETRA sections are read. 2. Only 4-node and 10-node tetrahedra are handled. 3. The file needs to be free-form (data separated by commas). :param filename: :return: """ f = open(filename, 'r') chunk = 5000 nnode = 0 node = numpy.zeros((chunk, 4)) nelem = 0 elem = numpy.zeros((chunk, 13), dtype=int) ennod = [] while True: temp = f.readline() if temp == '': f.close() break temp = temp.strip() if temp.upper()[0:4] == 'GRID': # Template: # GRID,1,,-1.32846E-017,3.25378E-033,0.216954 A = temp.replace(',', ' ').split() node[nnode, :] = float(A[1]), float(A[2]), float(A[3]), float(A[4]) nnode = nnode + 1 if nnode >= node.shape[0]: node = numpy.vstack((node, numpy.zeros( (chunk, node.shape[1])))) elif temp.upper()[0:6] == 'CTETRA': # Template: # CTETRA,1,3,15,14,12,16,8971,4853,8972,4850,8973,4848 A = temp.replace(',', ' ').split() elem[nelem, 0] = int(A[1]) elem[nelem, 1] = int(A[2]) if len(A) == 7: # nodes per element equals 4 nperel = 4 else: # nodes per element equals 10 nperel = 10 if len(A) < 13: # the line is continued: read the next line addtemp = f.readline() addA = addtemp.replace(',', ' ').split() A = A[3:] + addA for k in range(nperel): elem[nelem, k + 3] = int(A[k + 3]) elem[nelem, 2] = nperel nelem = nelem + 1 if nelem >= elem.shape[0]: elem = numpy.vstack((elem, numpy.zeros( (chunk, elem.shape[1])))) node = node[0:nnode, :] elem = elem[0:nelem, :] if numpy.linalg.norm(numpy.array(numpy.arange(1, nnode + 1)) - node[:, 0]) != 0: raise Exception('Nodes are not in serial order') # Process output arguments # Extract coordinates xyz = node[:, 1:4] # Cleanup element connectivities ennod = numpy.unique(elem[:, 2]) if len(ennod) != 1: raise Exception( 'This function cannot treat a mixture of element types at this point' ) if (ennod[0] != 4) and (ennod[0] != 10): raise Exception('Unknown element type') # Compensate for the Python indexing: make the connectivities zero-based conn = elem[:, 3:3 + ennod[0]] - 1 label = elem[:, 1] # Create output arguments. First the nodes fens = FENodeSet(xyz=xyz) # Now the geometric cells for each element set if ennod[0] == 4: fes = FESetT4(conn=conn, label=label) else: fes = FESetT10(conn=conn, label=label) return fens, [fes]
def t4_blockx(xs, ys, zs, orientation='a'): """Graded tetrahedral (T4) mesh of a rectangular block. :param xs: :param ys: :param zs: :param orientation: 'a', 'b', 'ca', 'cb' :return: """ nL = len(xs) - 1 nW = len(ys) - 1 nH = len(zs) - 1 nnodes = (nL + 1) * (nW + 1) * (nH + 1) xyzs = OneBased2DArray((nnodes, 3)) if (orientation == 'a'): t4ia = numpy.array([[1, 8, 5, 6], [3, 4, 2, 7], [7, 2, 6, 8], [4, 7, 8, 2], [2, 1, 6, 8], [4, 8, 1, 2]]) - 1 t4ib = numpy.array([[1, 8, 5, 6], [3, 4, 2, 7], [7, 2, 6, 8], [4, 7, 8, 2], [2, 1, 6, 8], [4, 8, 1, 2]]) - 1 elif (orientation == 'b'): t4ia = numpy.array([[2, 7, 5, 6], [1, 8, 5, 7], [1, 3, 4, 8], [2, 1, 5, 7], [1, 2, 3, 7], [3, 7, 8, 1]]) - 1 t4ib = numpy.array([[2, 7, 5, 6], [1, 8, 5, 7], [1, 3, 4, 8], [2, 1, 5, 7], [1, 2, 3, 7], [3, 7, 8, 1]]) - 1 elif (orientation == 'ca'): t4ia = numpy.array([[8, 4, 7, 5], [6, 7, 2, 5], [3, 4, 2, 7], [1, 2, 4, 5], [7, 4, 2, 5]]) - 1 t4ib = numpy.array([[7, 3, 6, 8], [5, 8, 6, 1], [2, 3, 1, 6], [4, 1, 3, 8], [6, 3, 1, 8]]) - 1 elif (orientation == 'cb'): t4ib = numpy.array([[8, 4, 7, 5], [6, 7, 2, 5], [3, 4, 2, 7], [1, 2, 4, 5], [7, 4, 2, 5]]) - 1 t4ia = numpy.array([[7, 3, 6, 8], [5, 8, 6, 1], [2, 3, 1, 6], [4, 1, 3, 8], [6, 3, 1, 8]]) - 1 else: raise Exception('Unknown orientation') ncells = t4ia.shape[0] * (nL) * (nW) * (nH) conns = OneBased2DArray((ncells, 4)) f = 1 for k in range_1based(1, nH + 1): for j in range_1based(1, nW + 1): for i in range_1based(1, nL + 1): xyzs[f, :] = xs[i - 1], ys[j - 1], zs[k - 1] f += 1 fens = FENodeSet(xyzs.raw_array()) def node_numbers(i, j, k, nL, nW, nH): f = (k - 1) * ((nL + 1) * (nW + 1)) + (j - 1) * (nL + 1) + i return array([ f, (f + 1), f + (nL + 1) + 1, f + (nL + 1), f + ((nL + 1) * (nW + 1)), (f + 1) + ((nL + 1) * (nW + 1)), f + (nL + 1) + 1 + ((nL + 1) * (nW + 1)), f + (nL + 1) + ((nL + 1) * (nW + 1)) ]) gc = 1 for i in range_1based(1, nL): for j in range_1based(1, nW): for k in range_1based(1, nH): nn = node_numbers(i, j, k, nL, nW, nH) if ((i + j + k) % 2 == 0): t4i = t4ib else: t4i = t4ia for r in range(t4i.shape[0]): conns[gc, :] = nn[t4i[r, :]] gc += 1 c = numpy.array(conns.raw_array() - 1, dtype=int) fes = FESetT4(conn=c) return fens, fes
def fuse_nodes(fens1, fens2, tolerance=0.0): # Fuse together nodes from to node sets. # # function [fens,new_indexes_of_fens1_nodes] = fuse_nodes(fens1, fens2, tolerance) # # Fuse two node sets. If necessary, by gluing together nodes located within tolerance of each other. # The two node sets, fens1 and fens2, are fused together by # merging the nodes that fall within a box of size "tolerance". # The merged node set, fens, and the new indexes of the nodes # in the set fens1 are returned. # # The set fens2 will be included unchanged, in the same order, # in the node set fens. # # The indexes of the node set fens1 will have changed. # # Example: # After the call to this function we have # k=new_indexes_of_fens1_nodes(j) is the node in the node set fens which # used to be node j in node set fens1. # The finite element set connectivity that used to refer to fens1 # needs to be updated to refer to the same nodes in the set fens as # fes = update_conn(fes ,new_indexes_of_fens1_nodes) # # See also: merge_nodes, update_conn # # I need to have the node number as non-zero to mark the replacement # when fused xyz1 = fens1.xyz id1 = numpy.array(range(1, fens1.count() + 1)) xyz2 = fens2.xyz id2 = numpy.array(range(1, fens2.count() + 1)) c1 = numpy.ones((xyz2.shape[0], 1)) # column matrix # Mark nodes from the first array that are duplicated in the second if (tolerance > 0): # should we attempt to merge nodes? Box2 = inflate_box(bounding_box(xyz2), tolerance) for i in range(fens1.count()): XYZ = xyz1[i, :].reshape(1, xyz1.shape[1]) # row matrix # Note that we are looking for distances of this node to nodes in the OTHER node set if in_box(Box2, XYZ): # This check makes this run much faster xyzd = abs( xyz2 - c1 * XYZ) # find the distances along coordinate directions dist = numpy.sum(xyzd, axis=1) jx = dist < tolerance if numpy.any(jx): minn = numpy.argmin(dist) id1[i] = -id2[minn] # Generate fused arrays of the nodes xyzm = numpy.zeros((fens1.count() + fens2.count(), xyz1.shape[1])) xyzm[0:fens2.count(), :] = xyz2 # fens2 are there without change mid = fens2.count() + 1 for i in range( fens1.count()): # and then we pick only non-duplicated fens1 if id1[i] > 0: id1[i] = mid xyzm[mid - 1, :] = xyz1[i, :] mid = mid + 1 else: id1[i] = id2[-id1[i] - 1] nfens = mid - 1 xyzm = xyzm[0:nfens, :] # Create the fused Node set fens = FENodeSet(xyz=xyzm) # The Node set 1 numbering will change new_indexes_of_fens1_nodes = id1 - 1 # go back to zero-based indexes # The node set 2 numbering stays the same, node set 1 will need to be # renumbered return fens, new_indexes_of_fens1_nodes
def q4_to_q8(fens, fes): """ :param fens: :param fes: :return: """ # Convert a mesh of quadrilateral Q4 to quadrilateral Q8. # # function [fens,fes] = Q4_to_Q8(fens,fes,options) # # options =attributes recognized by the constructor fe_set_Q8 # # Examples: # R=8.5 # [fens,fes]=Q4_sphere(R,1,1.0) # [fens,fes] = Q4_to_Q8(fens,fes,[]) # fens= onto_sphere(fens,R,[]) # drawmesh({fens,fes},'nodes','fes','facecolor','y', 'linewidth',2) hold on # # See also: fe_set_Q8 nedges = 4 ec = array([[0, 1], [1, 2], [2, 3], [3, 0]]) # make a search structure for edges edges = {} for i in range(fes.conn.shape[0]): conn = fes.conn[i, :] for J in range(nedges): ev = conn[ec[J, :]] anchor = numpy.amin(ev) otherv = numpy.amax(ev) if anchor not in edges: edges[anchor] = set([otherv]) else: edges[anchor].add(otherv) # now generate new node number for each edge nodes = copy.deepcopy(edges) n = fens.count() for anchor, othervs in edges.items(): nnew = [] for index in othervs: nnew.append(n) n += 1 nodes[anchor] = nnew xyz1 = fens.xyz xyz = numpy.zeros((n, xyz1.shape[1])) xyz[0:xyz1.shape[0], :] = xyz1[:, :] # calculate the locations of the new nodes # and construct the new nodes for anchor, othervs in edges.items(): nnew = nodes[anchor] othervs = list(othervs) for J in range(len(othervs)): e = othervs[J] xyz[nnew[J], :] = (xyz[anchor, :] + xyz[e, :]) / 2 # construct new finite elements nconns = numpy.zeros((fes.count(), 8), dtype=int) for i in range(fes.conn.shape[0]): conn = fes.conn[i, :] econn = numpy.zeros((nedges, )) for J in range(nedges): ev = conn[ec[J, :]] anchor = numpy.amin(ev) otherv = numpy.amax(ev) nnew = nodes[anchor] othervs = list(edges[anchor]) for k in range(len(othervs)): if othervs[k] == otherv: econn[J] = nnew[k] break nconns[i, 0:4] = conn nconns[i, 4:8] = econn fens = FENodeSet(xyz) fes = FESetQ8(conn=nconns, label=fes.label) return fens, fes