def trimesh2d(vertices, segments=None, holes=None, maxArea=None, quality=True, dofs_per_node=1, logFilename="tri.log", triangleExecutablePath=None): """ Triangulates an area described by a number vertices (vertices) and a set of segments that describes a closed polygon. Parameters: vertices array [nVertices x 2] with vertices describing the geometry. [[v0_x, v0_y], [ ... ], [vn_x, vn_y]] segments array [nSegments x 3] with segments describing the geometry. [[s0_v0, s0_v1,marker], [ ... ], [sn_v0, sn_v1,marker]] holes [Not currently used] maxArea Maximum area for triangle. (None) quality If true, triangles are prevented having angles < 30 degrees. (True) dofs_per_node Number of degrees of freedom per node. logFilename Filename for triangle output ("tri.log") Returns: coords Node coordinates [[n0_x, n0_y], [ ... ], [nn_x, nn_y]] edof Element topology [[el0_dof1, ..., el0_dofn], [ ... ], [eln_dof1, ..., eln_dofn]] dofs Node dofs [[n0_dof1, ..., n0_dofn], [ ... ], [nn_dof1, ..., nn_dofn]] bdofs Boundary dofs. Dictionary containing lists of dofs for each boundary marker. Dictionary key = marker id. """ # Check for triangle executable triangleExecutable = triangleExecutablePath if triangleExecutable == None: triangleExecutable = "" if sys.platform == "win32": triangleExecutable = which("triangle.exe") else: triangleExecutable = which("triangle") else: if not os.path.exists(triangleExecutable): triangleExecutable = None if triangleExecutable == None: print( "Error: Could not find triangle. Please make sure that the \ntriangle executable is available on the search path (PATH)." ) return None, None, None, None # Create triangle options options = "" if maxArea != None: options += "-a%f " % maxArea + " " if quality: options += "-q" # Set initial variables nSegments = 0 nHoles = 0 nAttribs = 0 nBoundaryMarkers = 1 nVertices = len(vertices) # All files are created as temporary files if not os.path.exists("./trimesh.temp"): os.mkdir("./trimesh.temp") filename = "./trimesh.temp/polyfile.poly" if not segments is None: nSegments = len(segments) if not holes is None: nHoles = len(holes) # Create a .poly file polyFile = open(filename, "w") polyFile.write("%d 2 %d \n" % (nVertices, nAttribs)) i = 0 for vertex in vertices: polyFile.write("%d %g %g\n" % (i, vertex[0], vertex[1])) i = i + 1 polyFile.write("%d %d \n" % (nSegments, nBoundaryMarkers)) i = 0 for segment in segments: polyFile.write("%d %d %d %d\n" % (i, segment[0], segment[1], segment[2])) i = i + 1 polyFile.write("0\n") polyFile.close() # Execute triangle os.system("%s %s %s > tri.log" % (triangleExecutable, options, filename)) # Read results from triangle strippedName = os.path.splitext(filename)[0] nodeFilename = "%s.1.node" % strippedName elementFilename = "%s.1.ele" % strippedName polyFilename = "%s.1.poly" % strippedName # Read vertices allVertices = None boundaryVertices = {} if os.path.exists(nodeFilename): nodeFile = open(nodeFilename, "r") nodeInfo = list(map(int, nodeFile.readline().split())) nNodes = nodeInfo[0] allVertices = np.zeros([nNodes, 2], 'd') for i in range(nNodes): vertexRow = list(map(float, nodeFile.readline().split())) boundaryMarker = int(vertexRow[3]) if not (boundaryMarker in boundaryVertices): boundaryVertices[boundaryMarker] = [] allVertices[i, :] = [vertexRow[1], vertexRow[2]] boundaryVertices[boundaryMarker].append(i + 1) nodeFile.close() # Read elements elements = [] if os.path.exists(elementFilename): elementFile = open(elementFilename, "r") elementInfo = list(map(int, elementFile.readline().split())) nElements = elementInfo[0] elements = np.zeros([nElements, 3], 'i') for i in range(nElements): elementRow = list(map(int, elementFile.readline().split())) elements[i, :] = [ elementRow[1] + 1, elementRow[2] + 1, elementRow[3] + 1 ] elementFile.close() # Clean up try: pass #os.remove(filename) #os.remove(nodeFilename) #os.remove(elementFilename) #os.remove(polyFilename) except: pass # Add dofs in edof and bcVerts dofs = cfc.createdofs(np.size(allVertices, 0), dofs_per_node) if dofs_per_node > 1: expandedElements = np.zeros((np.size(elements, 0), 3 * dofs_per_node), 'i') dofs = cfc.createdofs(np.size(allVertices, 0), dofs_per_node) elIdx = 0 for elementTopo in elements: for i in range(3): expandedElements[elIdx, i * dofs_per_node:( i * dofs_per_node + dofs_per_node)] = dofs[elementTopo[i] - 1, :] elIdx += 1 for bVertIdx in boundaryVertices.keys(): bVert = boundaryVertices[bVertIdx] bVertNew = [] for i in range(len(bVert)): for j in range(dofs_per_node): bVertNew.append(dofs[bVert[i] - 1][j]) boundaryVertices[bVertIdx] = bVertNew return allVertices, np.asarray( expandedElements), dofs, boundaryVertices return allVertices, elements, dofs, boundaryVertices
def trimesh2d( vertices, segments=None, holes=None, maxArea=None, quality=True, dofsPerNode=1, logFilename="tri.log", triangleExecutablePath=None, ): """ Triangulates an area described by a number vertices (vertices) and a set of segments that describes a closed polygon. Parameters: vertices array [nVertices x 2] with vertices describing the geometry. [[v0_x, v0_y], [ ... ], [vn_x, vn_y]] segments array [nSegments x 3] with segments describing the geometry. [[s0_v0, s0_v1,marker], [ ... ], [sn_v0, sn_v1,marker]] holes [Not currently used] maxArea Maximum area for triangle. (None) quality If true, triangles are prevented having angles < 30 degrees. (True) dofsPerNode Number of degrees of freedom per node. logFilename Filename for triangle output ("tri.log") Returns: coords Node coordinates [[n0_x, n0_y], [ ... ], [nn_x, nn_y]] edof Element topology [[el0_dof1, ..., el0_dofn], [ ... ], [eln_dof1, ..., eln_dofn]] dofs Node dofs [[n0_dof1, ..., n0_dofn], [ ... ], [nn_dof1, ..., nn_dofn]] bdofs Boundary dofs. Dictionary containing lists of dofs for each boundary marker. Dictionary key = marker id. """ # Check for triangle executable triangleExecutable = triangleExecutablePath if triangleExecutable == None: triangleExecutable = "" if sys.platform == "win32": triangleExecutable = which("triangle.exe") else: triangleExecutable = which("triangle") else: if not os.path.exists(triangleExecutable): triangleExecutable = None if triangleExecutable == None: print( "Error: Could not find triangle. Please make sure that the \ntriangle executable is available on the search path (PATH)." ) return None, None, None, None # Create triangle options options = "" if maxArea != None: options += "-a%f " % maxArea + " " if quality: options += "-q" # Set initial variables nSegments = 0 nHoles = 0 nAttribs = 0 nBoundaryMarkers = 1 nVertices = len(vertices) # All files are created as temporary files if not os.path.exists("./trimesh.temp"): os.mkdir("./trimesh.temp") filename = "./trimesh.temp/polyfile.poly" if not segments is None: nSegments = len(segments) if not holes is None: nHoles = len(holes) # Create a .poly file polyFile = open(filename, "w") polyFile.write("%d 2 %d \n" % (nVertices, nAttribs)) i = 0 for vertex in vertices: polyFile.write("%d %g %g\n" % (i, vertex[0], vertex[1])) i = i + 1 polyFile.write("%d %d \n" % (nSegments, nBoundaryMarkers)) i = 0 for segment in segments: polyFile.write("%d %d %d %d\n" % (i, segment[0], segment[1], segment[2])) i = i + 1 polyFile.write("0\n") polyFile.close() # Execute triangle os.system("%s %s %s > tri.log" % (triangleExecutable, options, filename)) # Read results from triangle strippedName = os.path.splitext(filename)[0] nodeFilename = "%s.1.node" % strippedName elementFilename = "%s.1.ele" % strippedName polyFilename = "%s.1.poly" % strippedName # Read vertices allVertices = None boundaryVertices = {} if os.path.exists(nodeFilename): nodeFile = open(nodeFilename, "r") nodeInfo = list(map(int, nodeFile.readline().split())) nNodes = nodeInfo[0] allVertices = np.zeros([nNodes, 2], "d") for i in range(nNodes): vertexRow = list(map(float, nodeFile.readline().split())) boundaryMarker = int(vertexRow[3]) if not (boundaryMarker in boundaryVertices): boundaryVertices[boundaryMarker] = [] allVertices[i, :] = [vertexRow[1], vertexRow[2]] boundaryVertices[boundaryMarker].append(i + 1) nodeFile.close() # Read elements elements = [] if os.path.exists(elementFilename): elementFile = open(elementFilename, "r") elementInfo = list(map(int, elementFile.readline().split())) nElements = elementInfo[0] elements = np.zeros([nElements, 3], "i") for i in range(nElements): elementRow = list(map(int, elementFile.readline().split())) elements[i, :] = [elementRow[1] + 1, elementRow[2] + 1, elementRow[3] + 1] elementFile.close() # Clean up try: pass # os.remove(filename) # os.remove(nodeFilename) # os.remove(elementFilename) # os.remove(polyFilename) except: pass # Add dofs in edof and bcVerts dofs = cfc.createdofs(np.size(allVertices, 0), dofsPerNode) if dofsPerNode > 1: expandedElements = np.zeros((np.size(elements, 0), 3 * dofsPerNode), "i") dofs = cfc.createdofs(np.size(allVertices, 0), dofsPerNode) elIdx = 0 for elementTopo in elements: for i in range(3): expandedElements[elIdx, i * dofsPerNode : (i * dofsPerNode + dofsPerNode)] = dofs[elementTopo[i] - 1, :] elIdx += 1 for bVertIdx in boundaryVertices.keys(): bVert = boundaryVertices[bVertIdx] bVertNew = [] for i in range(len(bVert)): for j in range(dofsPerNode): bVertNew.append(dofs[bVert[i] - 1][j]) boundaryVertices[bVertIdx] = bVertNew return allVertices, np.asarray(expandedElements), dofs, boundaryVertices return allVertices, elements, dofs, boundaryVertices
def create(self, is3D=False): ''' Meshes a surface or volume defined by the geometry in geoData. Parameters: is3D - Optional parameter that only needs to be set if geometry is loaded from a geo-file, i.e. if geoData is a path string. Default False. Returns: coords Node coordinates [[n0_x, n0_y, n0_z], [ ... ], [nn_x, nn_y, nn_z]] edof Element topology [[el0_dof1, ..., el0_dofn], [ ... ], [eln_dof1, ..., eln_dofn]] dofs Node dofs [[n0_dof1, ..., n0_dofn], [ ... ], [nn_dof1, ..., nn_dofn]] bdofs Boundary dofs. Dictionary containing lists of dofs for each boundary marker. Dictionary key = marker id. elementmarkers List of integer markers. Row i contains the marker of element i. Markers are similar to boundary markers and can be used to identify in which region an element lies. boundaryElements (optional) returned if self.return_boundary_elements is true. Contains dictionary with boundary elements. The keys are markers and the values are lists of elements for that marker. Running this function also creates object variables: nodesOnCurve Dictionary containing lists of node-indices. Key is a curve-ID and the value is a list of indices of all nodes on that curve, including its end points. nodesOnSurface Dictionary containing lists of node-indices. Key is a surface-ID and the value is a list of indices of the nodes on that surface, including its boundary. nodesOnVolume Dictionary containing lists of node-indices. Key is a volume-ID and the value is a list of indices of the nodes in that volume, including its surface. ''' #Nodes per element for different element types: #(taken from Chapter 9, page 89 of the gmsh manual) nodesPerElmDict = { 1: 2, 2: 3, 3: 4, 4: 4, 5: 8, 6: 6, 7: 5, 8: 3, 9: 6, 10: 9, 11: 10, 12: 27, 13: 18, 14: 14, 15: 1, 16: 8, 17: 20, 18: 15, 19: 13, 20: 9, 21: 10, 22: 12, 23: 15, 24: 15, 25: 21, 26: 4, 27: 5, 28: 6, 29: 20, 30: 35, 31: 56, 92: 64, 93: 125 } nodesPerElement = nodesPerElmDict[self.el_type] # Check for GMSH executable [NOTE]Mostly copied from trimesh2d(). TODO: Test on different systems gmshExe = self.gmsh_exec_path if gmshExe == None: gmshExe = "" if sys.platform == "win32": gmshExe = which("gmsh.exe") else: gmshExe = which("gmsh") else: if not os.path.exists(gmshExe): gmshExe = os.path.join(os.getcwd(), self.gmsh_exec_path) #Try relative path if not os.path.exists(gmshExe): gmshExe = None #Relative path didnt work either if gmshExe == None: raise IOError( "Error: Could not find GMSH. Please make sure that the \GMSH executable is available on the search path (PATH)." ) # Create a temporary directory for GMSH oldStyleTempDir = False if self.mesh_dir != "": tempMeshDir = self.mesh_dir os.mkdir(tempMeshDir) else: tempMeshDir = tempfile.mkdtemp() if type( self.geometry ) is str: #If geometry data is given as a .geo file we will just pass it on to gmsh later. geoFilePath = self.geometry dim = 3 if is3D else 2 #In this case geoData is a path string, so the dimension must be supplied by the user. if not os.path.exists(geoFilePath): geoFilePath = os.path.join(os.getcwd(), geoFilePath) #Try relative path if not os.path.exists(geoFilePath): raise IOError("Error: Could not find geo-file " + geoFilePath) else: dim = 3 if self.geometry.is3D else 2 #Get the dimension of the model from geoData. if oldStyleTempDir: if not os.path.exists("./gmshMeshTemp"): os.mkdir("./gmshMeshTemp") geoFilePath = os.path.normpath( os.path.join(os.getcwd(), "gmshMeshTemp/tempGeometry.geo") ) #"gmshMeshTemp/tempGeometry.geo" else: geoFilePath = os.path.normpath( os.path.join(tempMeshDir, 'tempGeometry.geo')) self.geofile = open(geoFilePath, "w") #Create temp geometry file self._writeGeoFile() #Write geoData to file self.geofile.close() if oldStyleTempDir: mshFileName = os.path.normpath( os.path.join(os.getcwd(), 'gmshMeshTemp/meshFile.msh') ) #Filepath to the msh-file that will be generated. else: mshFileName = os.path.normpath( os.path.join(tempMeshDir, 'meshFile.msh')) #construct options string: options = "" options += ' -' + str(dim) options += ' -clscale ' + str(self.el_size_factor) #scale factor options += ' -o \"%s\"' % mshFileName options += ' -clcurv' if self.clcurv else '' options += ' -clmin ' + str( self.min_size) if self.min_size is not None else '' options += ' -clmax ' + str( self.max_size) if self.max_size is not None else '' options += ' -algo ' + self.meshing_algorithm if self.meshing_algorithm is not None else '' options += ' -order 2' if self.el_type in self._2ndOrderElms else '' options += ' -format msh22' options += ' ' + self.additional_options #Execute gmsh gmshExe = os.path.normpath(gmshExe) print("GMSH binary: " + gmshExe) #print('""%s" "%s" %s"' % (gmshExe, geoFilePath, options)) #os.system('""%s" "%s" %s"' % (gmshExe, geoFilePath, options)) #retval = os.system(r'"%s" "%s" %s' % (gmshExe, geoFilePath, options)) output = subprocess.Popen(r'"%s" "%s" %s' % (gmshExe, geoFilePath, options), shell=True, stdout=subprocess.PIPE).stdout.read() #Read generated msh file: #print("Opening msh file " + mshFileName)#TEMP mshFile = open(mshFileName, 'r') print("Mesh file : " + mshFileName) #print("Reading msh file...")#TEMP ln = mshFile.readline() while (ln != '$Nodes\n'): #Read until we find the nodes ln = mshFile.readline() nbrNodes = int(mshFile.readline()) allNodes = np.zeros([nbrNodes, dim], 'd') for i in range(nbrNodes): line = list(map(float, mshFile.readline().split())) allNodes[i, :] = line[ 1:dim + 1] #Grab the coordinates (1:3 if 2D, 1:4 if 3D) while (mshFile.readline() != '$Elements\n'): #Read until we find the elements pass nbrElements = int(mshFile.readline() ) #The nbr of elements (including marker elements). elements = [] elementmarkers = [] bdofs = { } #temp dictionary of sets. Key:MarkerID. Value:Set. The sets will be converted to lists. boundaryElements = {} #nodeOnPoint = {} #dictionary pointID : nodeNumber self.nodesOnCurve = {} #dictionary lineID : set of [nodeNumber] self.nodesOnSurface = {} #dictionary surfID : set of [nodeNumber] self.nodesOnVolume = {} #dictionary volID : set of [nodeNumber] for i in range( nbrElements): #Read all elements (points, surfaces, etc): line = list(map(int, mshFile.readline().split())) eType = line[1] #second int is the element type. nbrTags = line[2] #Third int is the nbr of tags on this element. marker = line[3] #Fourth int (first tag) is the marker. entityID = line[ 4] #Fifth int is the ID of the geometric entity (points, curves, etc) that the element belongs to nodes = line[3 + nbrTags:len( line)] #The rest after tags are node indices. if ( eType == self.el_type ): #If the element type is the kind of element we are looking for: elements.append( nodes) #Add the nodes of the elements to the list. elementmarkers.append( marker ) #Add element marker. It is used for keeping track of elements (thickness, heat-production and such) else: #If the element is not a "real" element we store its node at marker in bdof instead: _insertInSetDict(bdofs, marker, nodes) # We also store the full information as 'boundary elements' _insertBoundaryElement(boundaryElements, eType, marker, nodes) #if eType == 15: #If point. Commmented away because points only make elements if they have non-zero markers, so nodeOnPoint is not very useful. # nodeOnPoint[entityID-1] = nodes[0] #insert node into nodeOnPoint. (ID-1 because we want 0-based indices) if eType in [1, 8, 26, 27, 28]: #If line _insertInSetDict(self.nodesOnCurve, entityID - 1, _offsetIndices( nodes, -1)) #insert nodes into nodesOnCurve elif eType in [2, 3, 9, 10, 16, 20, 21, 22, 23, 24, 25]: #If surfaceelement _insertInSetDict(self.nodesOnSurface, entityID - 1, _offsetIndices( nodes, -1)) #insert nodes into nodesOnSurface else: #if volume element. _insertInSetDict(self.nodesOnVolume, entityID - 1, _offsetIndices(nodes, -1)) elements = np.array(elements) for key in bdofs.keys(): #Convert the sets of boundary nodes to lists. bdofs[key] = list(bdofs[key]) for key in self.nodesOnCurve.keys(): #Convert set to list self.nodesOnCurve[key] = list(self.nodesOnCurve[key]) for key in self.nodesOnSurface.keys(): #Convert set to list self.nodesOnSurface[key] = list(self.nodesOnSurface[key]) for key in self.nodesOnVolume.keys(): #Convert set to list self.nodesOnVolume[key] = list(self.nodesOnVolume[key]) mshFile.close() # Remove temporary mesh directory if not explicetly specified. if self.mesh_dir == "": shutil.rmtree(tempMeshDir) dofs = createdofs(np.size(allNodes, 0), self.dofs_per_node) if self.dofs_per_node > 1: #This if-chunk copied from pycalfem_utils.py self.topo = elements expandedElements = np.zeros( (np.size(elements, 0), nodesPerElement * self.dofs_per_node), 'i') elIdx = 0 for elementTopo in elements: for i in range(nodesPerElement): expandedElements[elIdx, i * self.dofs_per_node:( i * self.dofs_per_node + self.dofs_per_node)] = dofs[elementTopo[i] - 1, :] elIdx += 1 for keyID in bdofs.keys(): bVerts = bdofs[keyID] bVertsNew = [] for i in range(len(bVerts)): for j in range(self.dofs_per_node): bVertsNew.append(dofs[bVerts[i] - 1][j]) bdofs[keyID] = bVertsNew if self.return_boundary_elements: return allNodes, np.asarray( expandedElements ), dofs, bdofs, elementmarkers, boundaryElements return allNodes, np.asarray( expandedElements), dofs, bdofs, elementmarkers if self.return_boundary_elements: return allNodes, elements, dofs, bdofs, elementmarkers, boundaryElements return allNodes, elements, dofs, bdofs, elementmarkers
def create(self, is3D=False): """ Meshes a surface or volume defined by the geometry in geoData. Parameters: is3D - Optional parameter that only needs to be set if geometry is loaded from a geo-file, i.e. if geoData is a path string. Default False. Returns: coords Node coordinates [[n0_x, n0_y, n0_z], [ ... ], [nn_x, nn_y, nn_z]] edof Element topology [[el0_dof1, ..., el0_dofn], [ ... ], [eln_dof1, ..., eln_dofn]] dofs Node dofs [[n0_dof1, ..., n0_dofn], [ ... ], [nn_dof1, ..., nn_dofn]] bdofs Boundary dofs. Dictionary containing lists of dofs for each boundary marker. Dictionary key = marker id. elementmarkers List of integer markers. Row i contains the marker of element i. Markers are similar to boundary markers and can be used to identify in which region an element lies. Running this function also creates object variables: nodesOnCurve Dictionary containing lists of node-indices. Key is a curve-ID and the value is a list of indices of all nodes on that curve, including its end points. nodesOnSurface Dictionary containing lists of node-indices. Key is a surface-ID and the value is a list of indices of the nodes on that surface, including its boundary. nodesOnVolume Dictionary containing lists of node-indices. Key is a volume-ID and the value is a list of indices of the nodes in that volume, including its surface. """ # Nodes per element for different element types: # (taken from Chapter 9, page 89 of the gmsh manual) nodesPerElmDict = { 1: 2, 2: 3, 3: 4, 4: 4, 5: 8, 6: 6, 7: 5, 8: 3, 9: 6, 10: 9, 11: 10, 12: 27, 13: 18, 14: 14, 15: 1, 16: 8, 17: 20, 18: 15, 19: 13, 20: 9, 21: 10, 22: 12, 23: 15, 24: 15, 25: 21, 26: 4, 27: 5, 28: 6, 29: 20, 30: 35, 31: 56, 92: 64, 93: 125, } nodesPerElement = nodesPerElmDict[self.elType] # Check for GMSH executable [NOTE]Mostly copied from trimesh2d(). TODO: Test on different systems gmshExe = self.gmshExecPath if gmshExe == None: gmshExe = "" if sys.platform == "win32": gmshExe = which("gmsh.exe") else: gmshExe = which("gmsh") else: if not os.path.exists(gmshExe): gmshExe = os.path.join(os.getcwd(), self.gmshExecPath) # Try relative path if not os.path.exists(gmshExe): gmshExe = None # Relative path didnt work either if gmshExe == None: raise IOError( "Error: Could not find GMSH. Please make sure that the \GMSH executable is available on the search path (PATH)." ) if ( type(self.geometry) is str ): # If geometry data is given as a .geo file we will just pass it on to gmsh later. geoFilePath = self.geometry dim = ( 3 if is3D else 2 ) # In this case geoData is a path string, so the dimension must be supplied by the user. if not os.path.exists(geoFilePath): geoFilePath = os.path.join(os.getcwd(), geoFilePath) # Try relative path if not os.path.exists(geoFilePath): raise IOError("Error: Could not find geo-file " + geoFilePath) else: dim = 3 if self.geometry.is3D else 2 # Get the dimension of the model from geoData. if not os.path.exists("./gmshMeshTemp"): os.mkdir("./gmshMeshTemp") geoFilePath = os.path.normpath( os.path.join(os.getcwd(), "gmshMeshTemp/tempGeometry.geo") ) # "gmshMeshTemp/tempGeometry.geo" self.geofile = open(geoFilePath, "w") # Create temp geometry file self._writeGeoFile() # Write geoData to file self.geofile.close() mshFileName = os.path.normpath( os.path.join(os.getcwd(), "gmshMeshTemp/meshFile.msh") ) # Filepath to the msh-file that will be generated. # construct options string: options = "" options += " -" + str(dim) options += " -clscale " + str(self.elSizeFactor) # scale factor options += ' -o "%s"' % mshFileName options += " -clcurv" if self.clcurv else "" options += " -clmin " + str(self.minSize) if self.minSize is not None else "" options += " -clmax " + str(self.maxSize) if self.maxSize is not None else "" options += " -algo " + self.meshingAlgorithm if self.meshingAlgorithm is not None else "" options += " -order 2" if self.elType in self._2ndOrderElms else "" options += " " + self.additionalOptions # Execute gmsh gmshExe = os.path.normpath(gmshExe) os.system('%s "%s" %s' % (gmshExe, geoFilePath, options)) # Read generated msh file: # print("Opening msh file " + mshFileName)#TEMP mshFile = open(mshFileName, "r") # print("Reading msh file...")#TEMP ln = mshFile.readline() while ln != "$Nodes\n": # Read until we find the nodes ln = mshFile.readline() nbrNodes = int(mshFile.readline()) allNodes = np.zeros([nbrNodes, dim], "d") for i in range(nbrNodes): line = list(map(float, mshFile.readline().split())) allNodes[i, :] = line[1 : dim + 1] # Grab the coordinates (1:3 if 2D, 1:4 if 3D) while mshFile.readline() != "$Elements\n": # Read until we find the elements pass nbrElements = int(mshFile.readline()) # The nbr of elements (including marker elements). elements = [] elementmarkers = [] bdofs = {} # temp dictionary of sets. Key:MarkerID. Value:Set. The sets will be converted to lists. # nodeOnPoint = {} #dictionary pointID : nodeNumber self.nodesOnCurve = {} # dictionary lineID : set of [nodeNumber] self.nodesOnSurface = {} # dictionary surfID : set of [nodeNumber] self.nodesOnVolume = {} # dictionary volID : set of [nodeNumber] for i in range(nbrElements): # Read all elements (points, surfaces, etc): line = list(map(int, mshFile.readline().split())) eType = line[1] # second int is the element type. nbrTags = line[2] # Third int is the nbr of tags on this element. marker = line[3] # Fourth int (first tag) is the marker. entityID = line[ 4 ] # Fifth int is the ID of the geometric entity (points, curves, etc) that the element belongs to nodes = line[3 + nbrTags : len(line)] # The rest after tags are node indices. if eType == self.elType: # If the element type is the kind of element we are looking for: elements.append(nodes) # Add the nodes of the elements to the list. elementmarkers.append( marker ) # Add element marker. It is used for keeping track of elements (thickness, heat-production and such) else: # If the element is not a "real" element we store its node at marker in bdof instead: _insertInSetDict(bdofs, marker, nodes) # if eType == 15: #If point. Commmented away because points only make elements if they have non-zero markers, so nodeOnPoint is not very useful. # nodeOnPoint[entityID-1] = nodes[0] #insert node into nodeOnPoint. (ID-1 because we want 0-based indices) if eType in [1, 8, 26, 27, 28]: # If line _insertInSetDict( self.nodesOnCurve, entityID - 1, _offsetIndices(nodes, -1) ) # insert nodes into nodesOnCurve elif eType in [2, 3, 9, 10, 16, 20, 21, 22, 23, 24, 25]: # If surfaceelement _insertInSetDict( self.nodesOnSurface, entityID - 1, _offsetIndices(nodes, -1) ) # insert nodes into nodesOnSurface else: # if volume element. _insertInSetDict(self.nodesOnVolume, entityID - 1, _offsetIndices(nodes, -1)) elements = np.array(elements) for key in bdofs.keys(): # Convert the sets of boundary nodes to lists. bdofs[key] = list(bdofs[key]) for key in self.nodesOnCurve.keys(): # Convert set to list self.nodesOnCurve[key] = list(self.nodesOnCurve[key]) for key in self.nodesOnSurface.keys(): # Convert set to list self.nodesOnSurface[key] = list(self.nodesOnSurface[key]) for key in self.nodesOnVolume.keys(): # Convert set to list self.nodesOnVolume[key] = list(self.nodesOnVolume[key]) # print("Closing msh file...")#TEMP mshFile.close() dofs = createdofs(np.size(allNodes, 0), self.dofsPerNode) if self.dofsPerNode > 1: # This if-chunk copied from pycalfem_utils.py self.topo = elements expandedElements = np.zeros((np.size(elements, 0), nodesPerElement * self.dofsPerNode), "i") elIdx = 0 for elementTopo in elements: for i in range(nodesPerElement): expandedElements[elIdx, i * self.dofsPerNode : (i * self.dofsPerNode + self.dofsPerNode)] = dofs[ elementTopo[i] - 1, : ] elIdx += 1 for keyID in bdofs.keys(): bVerts = bdofs[keyID] bVertsNew = [] for i in range(len(bVerts)): for j in range(self.dofsPerNode): bVertsNew.append(dofs[bVerts[i] - 1][j]) bdofs[keyID] = bVertsNew return allNodes, np.asarray(expandedElements), dofs, bdofs, elementmarkers return allNodes, elements, dofs, bdofs, elementmarkers