#!/usr/bin/env python
# -*- coding: utf-8 -*-
# run within virtual environment (uses Python 3 syntax)
import argparse
import os
import re
from array import array
from collections import Counter
from subprocess import check_output
def meshFile(param):
global base
base, ext = os.path.splitext(param)
if ext.lower() != '.pyfrm':
raise argparse.ArgumentTypeError('Mesh file must have a .pyfrm extension')
if not os.path.exists(param):
raise argparse.ArgumentTypeError("{} not found".format(param))
return param
def writeHeader(xdmfFile):
"write XDMF header"
xdmfFile.write('\n')
xdmfFile.write('\n')
xdmfFile.write('\n')
xdmfFile.write(' \n')
return
def writeTopology(xdmfFile, tType, nCells, connFile):
"write Topology element"
xdmfFile.write(' \n'.format(tType, nCells))
xdmfFile.write(' \n'.format(connFile))
xdmfFile.write(' \n')
return
def writeGeometry(xdmfFile, nDims, nCells, nodeArray, pyfrm, dataset):
"write Geometry element"
nVerts = len(nodeArray)
if nDims == 2:
xdmfFile.write(' \n') # co-ordinates in separate arrays
else:
xdmfFile.write(' \n') # co-ordinates in separate arrays
for coord in range(nDims):
xdmfFile.write(' \n'.format(' ; '.join("$" + str(k) for k in range(nVerts))))
writeHyperSlab(xdmfFile, coord, nDims, nCells, nodeArray, pyfrm, dataset)
xdmfFile.write(' \n')
xdmfFile.write(' \n')
return
def writeHyperSlab(xdmfFile, coord, nDims, nCells, nodeArray, pyfrm, dataset):
"write HyperSlab element"
nVerts = len(nodeArray)
for vert in nodeArray:
xdmfFile.write(' \n')
xdmfFile.write(' \n')
xdmfFile.write(' {:<3} 0 {}\n'.format(vert, coord)) # select vertex and co-ordinate (format is vertex, cell, co-ordinate)
xdmfFile.write(' 1 1 1\n') # select every cell, for this vertex and co-ordinate
xdmfFile.write(' 1 {} 1\n'.format(nCells)) # loop over cells
xdmfFile.write(' \n')
xdmfFile.write(' \n')
xdmfFile.write(' {}:/{}\n'.format(pyfrm, dataset))
xdmfFile.write(' \n')
xdmfFile.write(' \n')
return
def writeAttribute(xdmfFile, tag):
"write Attribute element"
xdmfFile.write(' \n')
xdmfFile.write(' \n')
xdmfFile.write(' {}\n'.format(tag)) # tag with partition number
xdmfFile.write(' \n')
xdmfFile.write(' \n')
return
def writeConnectivities(connFile, nCells, nVerts):
"write connectivities to xml file"
cf = open(connFile, 'w')
cf.write('\n')
for i in range (0, nCells):
cf.write(' ')
for j in range(nVerts):
cf.write(' ' + repr(j*nCells+i).ljust(1))
cf.write('\n')
cf.write('\n')
cf.close()
print('connectivities written to ' + connFile)
return
def writeFooter(xdmfFile):
"write XDMF footer"
xdmfFile.write(' \n')
xdmfFile.write('\n')
return
def readH5lsOutput(os_output):
"read output from 'h5ls' command"
tet4, tet10, tet20, tet35, tet56, tet84 = ({} for i in range(6)) # tet cells
pri6, pri18, pri40, pri75, pri126, pri196 = ({} for i in range(6)) # pri cells
pyr5, pyr14, pyr30, pyr55, pyr91, pyr140 = ({} for i in range(6)) # pyr cells
hex8, hex27, hex64, hex125, hex216 = ({} for i in range(5)) # hex cells
tri3, tri6, tri10, tri15, tri21 = ({} for i in range(5)) # tri cells
quad4, quad9, quad16, quad25, quad36 = ({} for i in range(5)) # quad cells
for line in os_output.splitlines():
spt = re.search('spt', line.decode()) # restrict to 'spt' arrays
if spt:
chunk = line.decode().split()
partno = int(re.search('\d+', chunk[0]).group())
nnodes = int(re.search('(\d+),', line.decode()).group(1))
ncells = int(re.search(' (\d+),', line.decode()).group(1))
if re.search('quad', line.decode()): # check whether cells are quadrilaterals
if nnodes == 4:
quad4[partno] = ncells
elif nnodes == 9:
quad9[partno] = ncells
elif nnodes == 16:
quad16[partno] = ncells
elif nnodes == 25:
quad25[partno] = ncells
elif nnodes == 36:
quad36[partno] = ncells
else:
print("unknown cell order")
elif re.search('tri', line.decode()): # check whether cells are triangles
if nnodes == 3:
tri3[partno] = ncells
elif nnodes == 6:
tri6[partno] = ncells
elif nnodes == 10:
tri10[partno] = ncells
elif nnodes == 15:
tri15[partno] = ncells
elif nnodes == 21:
tri21[partno] = ncells
else:
print("unknown cell order")
elif re.search('hex', line.decode()): # check whether cells are hexahedrons
if nnodes == 8:
hex8[partno] = ncells
elif nnodes == 27:
hex27[partno] = ncells
elif nnodes == 64:
hex64[partno] = ncells
elif nnodes == 125:
hex125[partno] = ncells
elif nnodes == 216:
hex216[partno] = ncells
else:
print("unknown cell order")
elif re.search('pyr', line.decode()): # check whether cells are pyramids
if nnodes == 5:
pyr5[partno] = ncells
elif nnodes == 14:
pyr14[partno] = ncells
elif nnodes == 30:
pyr30[partno] = ncells
elif nnodes == 55:
pyr55[partno] = ncells
elif nnodes == 91:
pyr91[partno] = ncells
elif nnodes == 140:
pyr140[partno] = ncells
else:
print("unknown cell order")
elif re.search('pri', line.decode()): # check whether cells are prisms
if nnodes == 6:
pri6[partno] = ncells
elif nnodes == 18:
pri18[partno] = ncells
elif nnodes == 40:
pri40[partno] = ncells
elif nnodes == 75:
pri75[partno] = ncells
elif nnodes == 126:
pri126[partno] = ncells
elif nnodes == 196:
pri196[partno] = ncells
else:
print("unknown cell order")
elif re.search('tet', line.decode()): # check whether cells are tetrahedrons
if nnodes == 4:
tet4[partno] = ncells
elif nnodes == 10:
tet10[partno] = ncells
elif nnodes == 20:
tet20[partno] = ncells
elif nnodes == 35:
tet35[partno] = ncells
elif nnodes == 56:
tet56[partno] = ncells
elif nnodes == 84:
tet84[partno] = ncells
else:
print("unknown cell order")
else:
print("unknown cell type")
break
return (tet4, tet10, tet20, tet35, tet56, tet84,
pri6, pri18, pri40, pri75, pri126, pri196,
pyr5, pyr14, pyr30, pyr55, pyr91, pyr140,
hex8, hex27, hex64, hex125, hex216,
tri3, tri6, tri10, tri15, tri21,
quad4, quad9, quad16, quad25, quad36)
# read command line arguments
parser = argparse.ArgumentParser(description="extract connectivities from mesh file")
parser.add_argument("mesh", help="mesh file (.pyfrm)", type=meshFile)
args = parser.parse_args()
# use 'h5ls' command to provide array dimensions
h5lsOutput = check_output(["h5ls", args.mesh])
(ntet4, ntet10, ntet20, ntet35, ntet56, ntet84,
npri6, npri18, npri40, npri75, npri126, npri196,
npyr5, npyr14, npyr30, npyr55, npyr91, npyr140,
nhex8, nhex27, nhex64, nhex125, nhex216,
ntri3, ntri6, ntri10, ntri15, ntri21,
nquad4, nquad9, nquad16, nquad25, nquad36) = readH5lsOutput(h5lsOutput)
# cell types
firstOrderCellType = ['tet', 'tet', 'tet', 'tet', 'tet', 'tet',
'pri', 'pri', 'pri', 'pri', 'pri', 'pri',
'pyr', 'pyr', 'pyr', 'pyr', 'pyr', 'pyr',
'hex', 'hex', 'hex', 'hex', 'hex',
'tri', 'tri', 'tri', 'tri', 'tri',
'quad', 'quad', 'quad', 'quad', 'quad']
ndims = {'quad': 2, 'tri': 2, 'hex': 3, 'pyr': 3, 'pri': 3, 'tet':3}
xdmfTopologyType = {'quad': 'Quadrilateral', 'tri': 'Triangle', 'hex': 'Hexahedron',
'pyr' : 'Pyramid', 'pri': 'Wedge', 'tet': 'Tetrahedron'}
# node identification (see pyfr/readers/nodemaps.py): reduces high-order cells to first order
# example, for second-order pyramid:
# >>> from pyfr.readers.nodemaps import GmshNodeMaps
# >>> [GmshNodeMaps.to_pyfr['pyr', 14][i] for i in range(5)]
nodeIDs = {}
nodeIDs[29] = [0, 2, 8, 6] # quad9
nodeIDs[28] = [0, 1, 3, 2] # quad4
nodeIDs[24] = [0, 2, 5] # tri6
nodeIDs[23] = [0, 1, 2] # tri3
nodeIDs[19] = [0, 2, 8, 6, 18, 20, 26, 24] # hex27
nodeIDs[18] = [0, 1, 3, 2, 4, 5, 7, 6] # hex8
nodeIDs[13] = [0, 2, 8, 6, 13] # pyr14
nodeIDs[12] = [0, 1, 3, 2, 4] # pyr5
nodeIDs[7] = [0, 2, 5, 12, 14, 17] # pri18
nodeIDs[6] = [0, 1, 2, 3, 4, 5] # pri6
nodeIDs[1] = [0, 2, 5, 9] # tet10
nodeIDs[0] = [0, 1, 2, 3] # tet4
# keys are partition numbers
tet4Keys = list(ntet4.keys())
tet10Keys = list(ntet10.keys())
pri6Keys = list(npri6.keys())
pri18Keys = list(npri18.keys())
pyr5Keys = list(npyr5.keys())
pyr14Keys = list(npyr14.keys())
hex8Keys = list(nhex8.keys())
hex27Keys = list(nhex27.keys())
tri3Keys = list(ntri3.keys())
tri6Keys = list(ntri6.keys())
quad4Keys = list(nquad4.keys())
quad9Keys = list(nquad9.keys())
# concatenate keys
allKeys = (tet4Keys + tet10Keys +
pri6Keys + pri18Keys +
pyr5Keys + pyr14Keys +
hex8Keys + hex27Keys +
tri3Keys + tri6Keys +
quad4Keys + quad9Keys)
# number of types present in each partition
numTypes = Counter(allKeys)
# list of partition keys
partKeys = list(numTypes.keys())
# list of cell dictionaries
partitions = [ntet4, ntet10, ntet20, ntet35, ntet56, ntet84,
npri6, npri18, npri40, npri75, npri126, npri196,
npyr5, npyr14, npyr30, npyr55, npyr91, npyr140,
nhex8, nhex27, nhex64, nhex125, nhex216,
ntri3, ntri6, ntri10, ntri15, ntri21,
nquad4, nquad9, nquad16, nquad25, nquad36]
numCellTypes = len(partitions)
if partKeys:
# write files
g = open(os.path.join(base + '.xdmf'), 'w')
writeHeader(g)
for part in partKeys:
g.write(' \n'.format(part))
for cellType in range(numCellTypes):
if part in partitions[cellType]: # check whether these cells exist in this partition
xfname = os.path.join('con_' + firstOrderCellType[cellType] + '_p' + str(part) + '.xml')
dname = os.path.join('spt_' + firstOrderCellType[cellType] + '_p' + str(part))
g.write(' \n'.format(xdmfTopologyType[firstOrderCellType[cellType]], part))
writeTopology(g, xdmfTopologyType[firstOrderCellType[cellType]],
partitions[cellType][part], xfname)
writeGeometry(g, ndims[firstOrderCellType[cellType]],
partitions[cellType][part],
nodeIDs[cellType],
args.mesh, dname)
writeAttribute(g, part)
g.write(' \n')
# connectivities file
writeConnectivities(xfname, partitions[cellType][part],
len(nodeIDs[cellType]))
g.write(' \n')
writeFooter(g)
g.close()
else:
print("mesh does not contain any supported cell types")