#!/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')
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 cell is quadrangular
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 cell is triangular
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 cell is hexahedral
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")
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): reduce high-order cells to first order
nodeIDs = {}
nodeIDs[29] = [0, 2, 8, 6] # quad9
nodeIDs[28] = [0, 1, 3, 2] # quad4
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
# sort datasets
hex8Keys = list(nhex8.keys()) # keys are partition numbers
hex27Keys = list(nhex27.keys())
quad4Keys = list(nquad4.keys())
tri3Keys = list(ntri3.keys())
allKeys = quad4Keys + tri3Keys + hex8Keys + hex27Keys # concatenate keys
numTypes = Counter(allKeys) # number of types present in each partition
partKeys = list(numTypes.keys()) # partition keys
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] # list of cell dictionaries
numCellTypes = len(partitions)
if partKeys:
# write files
g = open(os.path.join(base + '.xdmf'), 'w')
writeHeader(g)
for part in partKeys:
if numTypes[part] > 1: # check whether partition contain multiple cell types
g.write(' \n'.format(part))
else:
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))
if numTypes[part] > 1:
g.write(' \n'.format(cellType))
writeTopology(g, xdmfTopologyType[firstOrderCellType[cellType]],
partitions[cellType][part], xfname)
writeGeometry(g, ndims[firstOrderCellType[cellType]],
partitions[cellType][part],
nodeIDs[cellType],
args.mesh, dname)
if numTypes[part] > 1:
g.write(' \n')
# connectivities file
writeConnectivities(xfname, partitions[cellType][part],
len(nodeIDs[cellType]))
writeAttribute(g, part)
g.write(' \n')
writeFooter(g)
g.close()