import os
from numpy import zeros, cross, vstack, array
from numpy.linalg import norm # type: ignore
[docs]
def combine_surfs(surf_filenames, surf_out_filename=None):
"""
Combines multiple SURFs into a single file
Parameters
----------
surf_filenames : list[str]
list of surf filenames
surf_out_filename : str; default=None -> no writing
string of stl output filename
Returns
-------
surf : SurfReader
the surf object
"""
nodes = []
tris = []
quads = []
tri_props = []
quad_props = []
n0 = 0
for fname in surf_filenames:
surf = SurfReader()
surf.read_surf(fname)
nnodes = surf.nodes.shape[0]
nodes.append(surf.nodes)
tris.append(surf.tris + n0)
quads.append(surf.quads + n0)
tri_props.append(surf.tri_props)
quad_props.append(surf.quad_props)
n0 += nnodes
surf.nodes = vstack(nodes)
surf.tris = vstack(tris)
surf.quads = vstack(quads)
surf.tri_props = vstack(tri_props)
surf.quad_props = vstack(quad_props)
if surf_out_filename is not None:
surf.write_surf(surf_out_filename)
return surf
[docs]
class SurfReader:
def __init__(self, log=None, debug=False):
"""
Initializes the SurfReader object
Parameters
----------
debug : bool/None; default=True
used to set the logger if no logger is passed in
True: logs debug/info/error messages
False: logs info/error messages
None: logs error messages
log : logging module object / None
if log is set, debug is ignored and uses the
settings the logging object has
"""
self.log = log
self.debug = debug
self.nodes = None
self.node_props = None
#self.nodes_failed = None
self.tris = None
self.tri_props = None
self.quads = None
self.quad_props = None
self.nodes_failed = None
[docs]
def read_surf(self, surf_filename):
"""
# AFLR3,14.18.115,28.206.154,WIN/doc/ug_io/3d_input_output_grids.html
+---------+--------------------------------------------------------------+
| Grid BC | Description |
+=========+==============================================================+
| -6 | internal embedded/transparent surface that will be converted |
| | to internal/interior/volume faces with BL volume grid |
+---------+--------------------------------------------------------------+
| -5 | embedded/transparent surface with BL volume grid |
+---------+--------------------------------------------------------------+
| -1 | standard surface with BL volume grid; **wall** |
+---------+--------------------------------------------------------------+
| 0 | standard surface |
+---------+--------------------------------------------------------------+
| 1 | standard surface; **farfield** |
+---------+--------------------------------------------------------------+
| 2 | standard surface that intersects the BL region; |
| | **boundary layer** |
+---------+--------------------------------------------------------------+
| 3 | embedded/transparent surface or source surface that will be |
| | converted to source nodes; **source** |
+---------+--------------------------------------------------------------+
| 4 | embedded/transparent surface that intersects the BL region |
+---------+--------------------------------------------------------------+
| 5 | embedded/transparent surface |
+---------+--------------------------------------------------------------+
| 6 | internal embedded/transparent surface that will be converted |
| | to internal/interior/volume faces |
+---------+--------------------------------------------------------------+
| 7 | fixed surface that intersects and directly connects to the |
| | BL region; **transparent** |
+---------+--------------------------------------------------------------+
"""
#basename = os.path.splitext(surf_filename)[0]
#fail_surf_filename = basename +
#print(fail_surf_filename)
is_failed_surf = True if '.FAIL.surf' in surf_filename else False
# if '.FAIL.' in surf_filename:
# is_failed_surf = True
with open(surf_filename, 'r') as surf_file:
ntris, nquads, nnodes = surf_file.readline().strip().split()
ntris = int(ntris)
nquads = int(nquads)
nnodes = int(nnodes)
nodes = zeros((nnodes, 3), dtype='float64')
node_props = zeros((nnodes, 2), dtype='float64')
tris = zeros((ntris, 3), dtype='int32')
quads = zeros((nquads, 4), dtype='int32')
tri_props = zeros((ntris, 3), dtype='int32')
quad_props = zeros((nquads, 3), dtype='int32')
# read nodes
if is_failed_surf:
for inode in range(nnodes):
(x, y, z) = surf_file.readline().strip().split()
nodes[inode, :] = [x, y, z]
else:
for inode in range(nnodes):
x_y_z_initalnormalspacing_blthickness = surf_file.readline().strip().split()
if len(x_y_z_initalnormalspacing_blthickness) == 4:
(x, y, z, initial_normal_spacing) = x_y_z_initalnormalspacing_blthickness
bl_thickness = 0.0
elif len(x_y_z_initalnormalspacing_blthickness) == 5:
(x, y, z, initial_normal_spacing,
bl_thickness) = x_y_z_initalnormalspacing_blthickness
else:
raise NotImplementedError(x_y_z_initalnormalspacing_blthickness)
nodes[inode, :] = [x, y, z]
node_props[inode, :] = [initial_normal_spacing, bl_thickness]
# read tris
for itri in range(ntris):
(n1, n2, n3, surface_id, reconnection_flag,
grid_bc_flag) = surf_file.readline().strip().split()
tris[itri, :] = [n1, n2, n3]
tri_props[itri, :] = [surface_id, reconnection_flag, grid_bc_flag]
# read quads
for iquad in range(nquads):
(n1, n2, n3, n4, surface_id, reconnection_flag,
grid_bc_flag) = surf_file.readline().strip().split()
quads[iquad, :] = [n1, n2, n3, n4]
quad_props[iquad, :] = [surface_id, reconnection_flag, grid_bc_flag]
self.nodes = nodes
self.node_props = node_props
self.tris = tris
self.tri_props = tri_props
self.quads = quads
self.quad_props = quad_props
[docs]
def read_surf_failnode(self, surf_filename):
#print(surf_filename)
basename = os.path.splitext(surf_filename)[0]
fail_node_filename = basename + '.FAIL.node'
# nnodes = self.nodes.shape[0]
nodes_failed = []
if os.path.exists(fail_node_filename):
with open(fail_node_filename, 'r') as fail_file:
lines = fail_file.readlines()
for line in lines:
nid = int(line.split()[0])
nodes_failed.append(nid)
nodes_failed = array(nodes_failed, dtype='int32')
self.nodes_failed = nodes_failed
[docs]
def get_normals(self):
ntris = self.tris.shape[0]
nquads = self.quads.shape[0]
nelements = ntris + nquads
normals = zeros((nelements, 3), dtype='float64')
n1 = self.tris[:, 0] - 1
n2 = self.tris[:, 1] - 1
n3 = self.tris[:, 2] - 1
a = self.nodes[n2, :] - self.nodes[n1, :]
b = self.nodes[n3, :] - self.nodes[n1, :]
normal = cross(a, b)
mag = norm(normal, axis=1)
assert len(mag) == ntris, 'axis is wrong'
normal /= mag[:, None]
normals[:ntris, :] = normal
n1 = self.quads[:, 0] - 1
n2 = self.quads[:, 1] - 1
n3 = self.quads[:, 2] - 1
n4 = self.quads[:, 3] - 1
#print(n1)
#print(n3)
a = self.nodes[n3, :] - self.nodes[n1, :]
b = self.nodes[n4, :] - self.nodes[n2, :]
normal = cross(a, b)
mag = norm(normal, axis=1)
assert len(mag) == nquads, 'axis is wrong'
normal /= mag[:, None]
normals[ntris:, :] = normal
return normals
[docs]
class TagReader:
def __init__(self, log=None, debug=False):
self.log = log
self.debug = debug
[docs]
def read_tag_filename(self, tag_filename):
with open(tag_filename, 'r') as tag_file:
lines = tag_file.readlines()
lines = [
line.strip().split('#')[0].strip() for line in lines
if line.strip().split('#')[0].strip()
]
#print('\n'.join(lines))
#ID Group Visc Recon Rebuild Fixed Source Trans Delete Spacing Thcknss Layers
data = {}
for line in lines:
sline = line.split()
if len(sline) == 12:
(ID, name, is_visc, is_recon, is_rebuild, is_fixed, is_source,
is_trans, is_delete, bl_spacing, bl_thickness, nlayers) = sline
ID = int(ID)
is_visc = int(is_visc)
is_recon = int(is_recon)
is_rebuild = int(is_rebuild)
is_source = int(is_source)
is_trans = int(is_trans)
is_delete = int(is_delete)
bl_spacing = float(bl_spacing)
bl_thickness = float(bl_thickness)
nlayers = int(nlayers)
else:
msg = 'invalid data line in tags file=%r\n' % tag_filename
msg += 'split line = %s\n' % sline
msg += 'expected 12 values; got %s\n' % (len(sline))
try:
msg += ' ID = %s\n' % sline[0]
msg += ' name = %s\n' % sline[1]
msg += ' is_visc = %s\n' % sline[2]
msg += ' is_recon = %s\n' % sline[3]
msg += ' is_rebuild = %s\n' % sline[4]
msg += ' is_fixed = %s\n' % sline[5]
msg += ' is_source = %s\n' % sline[6]
msg += ' is_trans = %s\n' % sline[7]
msg += ' is_delete = %s\n' % sline[8]
msg += ' bl_spacing = %s\n' % sline[9]
msg += ' bl_thickness = %s\n' % sline[10]
msg += ' nlayers = %s\n' % sline[11]
except IndexError:
pass
raise RuntimeError(msg)
data[ID] = [name, is_visc, is_recon, is_rebuild, is_fixed, is_source,
is_trans, is_delete, bl_spacing, bl_thickness, nlayers]
return data