"""mesh building block - Face class definition"""
import os
from typing import Any, Union, Tuple, IO
if os.environ.get('__USE_CUDA', '0') == '1':
import cupy as np
from cupy import ndarray
else:
import numpy as np
from numpy import ndarray
import re
from g3d.defs import G3DWorld
from g3d.defs import FaceOrientation
from g3d.defs import INTFTypes
from g3d.defs import AngleType
from g3d.defs import FunctionByPass
from g3d.mesh.geo import Coordinates
from g3d.mesh.geo import Coord
from g3d.flow.placeholders import FMP
[docs]
class Face(Coordinates):
def __init__(self, *args: Any, **kwargs: Any) -> None:
self._it = None
self._face_type = None
self._check = "not checked"
self._alpha = 0.
self._delta = Coord()
self._im = 0
self._i0 = 0
self._j0 = 0
self._k0 = 0
self._idir = 0
self._jdir = 0
self._npi = 0
self._npj = 0
super().__init__(*args, **kwargs)
self.__reset_FMP()
def _set_from_args(self, *args: Any, **kwargs: Any) -> ndarray:
bps = self._get_function_evaluate_status(**kwargs)
if bps is FunctionByPass.SELF:
args_stat = np.ones(len(args))
else:
args_stat = super()._set_from_args(*args, bps=FunctionByPass.BYPASS)
if bps in [FunctionByPass.EVALUATE, FunctionByPass.SELF]:
if np.sum(args_stat) > 0:
face_def = []
for i in range(len(args)):
if args_stat[i] == 1:
arg = args[i]
if type(arg) in [int, np.int64, np.int32]:
args_stat[i] = 0
if type(arg) in [np.int64, np.int32]:
face_def.append(arg.item())
else:
face_def.append(arg)
elif type(arg) is Face:
# if caught by a super class, the child class instance of copy will be used
args_stat[i] = 0
self.copy(arg)
elif type(arg) is list:
args_stat[i] = 0
if self._list_is_all_ints(arg):
self.__init_def(arg)
if len(face_def) >= 8:
# will be caught by the for-loop below in a sub call to _set_from_args
self.__init_def(face_def)
return args_stat
def __init_def(self, fd: list) -> None:
if len(fd) == 8:
self._im, self._i0, self._j0, self._k0, self._idir, self._jdir, self._npi, self._npj = fd
elif len(fd) == 9: # rotation angle specified
self._im, self._i0, self._j0, self._k0, self._idir, self._jdir, self._npi, self._npj, self._alpha = fd
elif len(fd) == 11: # translation data specified
self._im, self._i0, self._j0, self._k0, self._idir, self._jdir, self._npi, self._npj, \
self._delta.x, self._delta.y, self._delta.z = fd
def _set_default(self) -> None:
self.__reset_def()
super()._set_default()
self._tag = 'g3d_face'
def __reset_def(self) -> None:
self._im = 0
self._i0 = 0
self._j0 = 0
self._k0 = 0
self._idir = 0
self._jdir = 0
self._npi = 0
self._npj = 0
def __reset_FMP(self) -> None:
self._FMP = FMP()
[docs]
def set_from_key_value(self, key: str, value: Any) -> bool:
if super().set_from_key_value(key, value):
return True
elif key == 'delta_x':
self._delta.x = value
return True
elif key == 'delta_y':
self._delta.y = value
return True
elif key == 'delta_z':
self._delta.z = value
return True
elif key == 'alpha':
self._alpha = value
return True
elif key == 'it':
self._it = value
return True
elif key == 'face_type':
self._face_type = value
return True
return False
def __initFace(self, f: Union['Face', list] = None) -> None:
if f is not None:
if type(f) is Face:
self.copy(f)
elif len(f) == 8:
self._im, self._i0, self._j0, self._k0, self._idir, self._jdir, self._npi, self._npj = f
elif len(f) == 9: # rotation angle specified
self._im, self._i0, self._j0, self._k0, self._idir, self._jdir, self._npi, self._npj, self._alpha = f
elif len(f) == 11: # translation data specified
self._im, self._i0, self._j0, self._k0, self._idir, self._jdir, self._npi, self._npj, \
self._delta.x, self._delta.y, self._delta.z = f
else:
self._im, self._i0, self._j0, self._k0, self._idir, self._jdir, self._npi, self._npj = [0, 0, 0, 0, 0,
0, 0, 0]
else:
self._im, self._i0, self._j0, self._k0, self._idir, self._jdir, self._npi, self._npj = [0, 0, 0, 0,
0, 0, 0, 0]
[docs]
def allocate_XYZ(self, N: int = None) -> None:
self._size = self._npi * self._npj
super().allocate_XYZ(N)
@property
def im(self) -> int:
"""
:return:
"""
return self._im
@im.setter
def im(self, i_val: int) -> None:
self._im = i_val
@property
def i0(self) -> int:
"""
:return:
"""
return self._i0
@i0.setter
def i0(self, i_val: int) -> None:
self._i0 = i_val
@property
def j0(self) -> int:
"""
:return:
"""
return self._j0
@j0.setter
def j0(self, i_val: int) -> None:
self._j0 = i_val
@property
def k0(self) -> int:
"""
:return:
"""
return self._k0
@k0.setter
def k0(self, i_val: int) -> None:
self._k0 = i_val
@property
def idir(self) -> int:
"""
:return:
"""
return self._idir
@idir.setter
def idir(self, i_val: int) -> None:
self._idir = i_val
@property
def jdir(self) -> int:
"""
:return:
"""
return self._jdir
@jdir.setter
def jdir(self, i_val: int) -> None:
self._jdir = i_val
@property
def npi(self) -> int:
"""
:return:
"""
return self._npi
@npi.setter
def npi(self, i_val: int) -> None:
self._npi = i_val
@property
def npj(self) -> int:
"""
:return:
"""
return self._npj
@npj.setter
def npj(self, i_val: int) -> None:
self._npj = i_val
@property
def it(self) -> Union[int, None]:
"""
:return:
"""
return self._it
@it.setter
def it(self, i_val: int) -> None:
self._it = i_val
@property
def face_type(self) -> str:
"""
:return:
"""
return self._face_type
@face_type.setter
def face_type(self, val: str) -> None:
self._face_type = val
@property
def check(self) -> str:
"""
:return:
"""
return self._check
@check.setter
def check(self, cs: str) -> None:
self._check = cs
@property
def alpha(self) -> float:
"""
:return:
"""
return self._alpha
@alpha.setter
def alpha(self, a: float) -> None:
self._alpha = a
@property
def delta(self) -> Coord:
"""
:return:
"""
return self._delta
@property
def FMP(self) -> FMP:
"""
:return:
"""
return self._FMP
@property
def FMP_size(self) -> int:
"""
:return:
"""
return self._FMP.size
[docs]
def set_def(self, f: Union['Face', list] = None) -> None:
"""
:param f:
"""
self.__initFace(f)
[docs]
def read_def_from_file(self, f: IO[str]) -> None:
"""
:param f:
"""
regex_face_def = re.compile(r'^\s*(?P<im>\d+)\s+(?P<i0>\d+)\s+(?P<j0>\d+)\s+(?P<k0>\d+)'
r'\s+(?P<idir>\d+)\s+(?P<jdir>\d+)\s+(?P<npi>\d+)\s+(?P<npj>\d+)')
while True:
line = f.readline()
match = regex_face_def.search(line)
if match:
self._im = int(match.group('im'))
self._i0 = int(match.group('i0'))
self._j0 = int(match.group('j0'))
self._k0 = int(match.group('k0'))
self._idir = int(match.group('idir'))
self._jdir = int(match.group('jdir'))
self._npi = int(match.group('npi'))
self._npj = int(match.group('npj'))
break
[docs]
def print(self) -> None:
print(' face type: %s' % self._face_type)
print(' face tag : %s' % self._tag)
print(' im=%4d, i0=%4d, j0=%4d, k0=%4d, idir=%3d, jdir=%3d, npi=%4d, npj=%4d' % (self._im,
self._i0,
self._j0,
self._k0,
self._idir,
self._jdir,
self._npi,
self._npj))
print(' check status: %s' % self._check)
[docs]
def get_face_count(self) -> int:
"""
:return:
"""
return (self._npi - 1) * (self._npj - 1)
[docs]
def get_node_count(self) -> int:
"""
:return:
"""
return self._npi * self._npj
[docs]
def get_cell_count(self) -> int:
"""
:return:
"""
return self.get_face_count()
[docs]
def swap_normal(self) -> None:
"""
"""
tmp = self._idir
self._idir = self._jdir
self._jdir = tmp
tmp = self._npi
self._npi = self._npj
self._npj = tmp
[docs]
def copy(self, f: 'Face') -> None:
super().copy(f)
self.copy_def(f)
self._it = f.it
self._face_type = f.face_type
if f.size > 0:
self.allocate_XYZ()
self.copy_XYZ(f)
[docs]
def copy_def(self, f: 'Face') -> None:
"""
:param f:
"""
self._im = f.im
self._i0 = f.i0
self._j0 = f.j0
self._k0 = f.k0
self._idir = f.idir
self._jdir = f.jdir
self._npi = f.npi
self._npj = f.npj
[docs]
def get_def(self, normal: Any = FaceOrientation.OUTWARD_FACING_NORMAL) -> Tuple[int, ...]:
"""
:param normal:
:return:
"""
if normal == FaceOrientation.INWARD_FACING_NORMAL:
return self._im, self._i0, self._j0, self._k0, self._jdir, self._idir, self._npj, self._npi
else:
return self._im, self._i0, self._j0, self._k0, self._idir, self._jdir, self._npi, self._npj
[docs]
def get_delta(self) -> list[float]:
"""
:return:
"""
return [self._delta.x, self._delta.y, self._delta.z]
def __get_lp(self, i: int, j: int) -> int: # local 0-based indices
return i + self._npi * j
[docs]
def get_node(self, i: int, j: int) -> int:
"""
:param i:
:param j:
:return:
"""
return self.__get_lp(i, j)
[docs]
def get_index(self, i: int, j: int) -> int: # local 1-based indices
"""
:param i:
:param j:
:return:
"""
return self.__get_lp(i - 1, j - 1)
[docs]
def generate_offset_Face(self, offset: float) -> 'Face':
"""
:param offset:
:return:
"""
b = Face(self)
N = len(self._X)
nx = np.zeros(N)
ny = np.zeros(N)
nz = np.zeros(N)
for jp in range(self._npj - 1):
for ip in range(self._npi - 1):
lp = ip + jp * self._npi
x1 = self._X[lp]
y1 = self._Y[lp]
z1 = self._Z[lp]
x2 = self._X[lp + 1]
y2 = self._Y[lp + 1]
z2 = self._Z[lp + 1]
x3 = self._X[lp + self._npi]
y3 = self._Y[lp + self._npi]
z3 = self._Z[lp + self._npi]
x4 = self._X[lp + 1 + self._npi]
y4 = self._Y[lp + 1 + self._npi]
z4 = self._Z[lp + 1 + self._npi]
nx[lp] += (y2 - y1) * (z3 - z1) - (y3 - y1) * (z2 - z1)
ny[lp] += (x3 - x1) * (z2 - z1) - (x2 - x1) * (z3 - z1)
nz[lp] += (x2 - x1) * (y3 - y1) - (x3 - x1) * (y2 - y1)
nx[lp + 1] += (y4 - y2) * (z1 - z2) - (y1 - y2) * (z4 - z2)
ny[lp + 1] += (x1 - x2) * (z4 - z2) - (x4 - x2) * (z1 - z2)
nz[lp + 1] += (x4 - x2) * (y1 - y2) - (x1 - x2) * (y4 - y2)
nx[lp + self._npi] += (y1 - y3) * (z4 - z3) - (y4 - y3) * (z1 - z3)
ny[lp + self._npi] += (x4 - x3) * (z1 - z3) - (x1 - x3) * (z4 - z3)
nz[lp + self._npi] += (x1 - x3) * (y4 - y3) - (x4 - x3) * (y1 - y3)
nx[lp + self._npi + 1] += (y3 - y4) * (z2 - z4) - (z3 - z4) * (y2 - y4)
ny[lp + self._npi + 1] += (x2 - x4) * (z3 - z4) - (x3 - x4) * (z2 - z4)
nz[lp + self._npi + 1] += (x3 - x4) * (y2 - y4) - (x2 - x4) * (y3 - y4)
X_offset, Y_offset, Z_offset = b.get_XYZ()
for jp in range(self._npj):
for ip in range(self._npi):
lp = ip + jp * self._npi
r = np.sqrt(nx[lp] ** 2 + ny[lp] ** 2 + nz[lp] ** 2)
r = max(r, 1.e-20)
X_offset[lp] = self._X[lp] + offset * nx[lp] / r
Y_offset[lp] = self._Y[lp] + offset * ny[lp] / r
Z_offset[lp] = self._Z[lp] + offset * nz[lp] / r
return b
[docs]
def shift(self, **kwargs: Any) -> None:
"""
:param kwargs:
:return:
"""
x_shift = 0.
y_shift = 0.
z_shift = 0.
if len(kwargs) != 0:
g3d_world = G3DWorld()
for key, value in kwargs.items():
if g3d_world.display.verbose:
print('%s == %s' % (key, str(value)))
if key == 'x_shift':
x_shift = value
elif key == 'y_shift':
y_shift = value
elif key == 'z_shift':
z_shift = value
else:
print('warning! parameter %s is not defined' % key)
if x_shift == 0. and y_shift == 0. and z_shift == 0.:
print('warning! no shift specified')
return
for jp in range(self._npj):
for ip in range(self._npi):
lp = self.__get_lp(ip, jp)
self._X[lp] += x_shift
self._Y[lp] += y_shift
self._Z[lp] += z_shift
[docs]
def rotate(self, **kwargs: Any) -> None:
"""
:param kwargs:
:return:
"""
alpha = 0.
beta = 0.
gamma = 0.
angle_dim = AngleType.DEGREES
if len(kwargs) != 0:
g3d_world = G3DWorld()
for key, value in kwargs.items():
if g3d_world.display.verbose:
print('%s == %s' % (key, str(value)))
if key == 'alpha':
alpha = value
elif key == 'beta':
beta = value
elif key == 'gamma':
gamma = value
elif key == 'angle_dim':
angle_dim = value
else:
print('warning! parameter %s is not defined' % key)
if alpha == 0. and beta == 0. and gamma == 0.:
print('warning! no rotation angle specified')
return
if angle_dim == AngleType.DEGREES:
alpha *= np.pi / 180.
beta *= np.pi / 180.
gamma *= np.pi / 180.
eps = 1.e-6
csa = np.cos(alpha)
ssa = np.sin(alpha)
csb = np.cos(beta)
ssb = np.sin(beta)
csg = np.cos(gamma)
ssg = np.sin(gamma)
for jp in range(self._npj):
for ip in range(self._npi):
lp = self.__get_lp(ip, jp)
if np.abs(alpha) > eps:
yy = self._Y[lp]
zz = self._Z[lp]
self._Y[lp] = csa * yy - ssa * zz
self._Z[lp] = ssa * yy + csa * zz
if np.abs(beta) > eps:
xx = self._X[lp]
zz = self._Z[lp]
self._Z[lp] = csb * zz + ssb * xx
self._X[lp] = -ssb * zz + csb * xx
if np.abs(gamma) > eps:
xx = self._X[lp]
yy = self._Y[lp]
self._X[lp] = csg * xx - ssg * yy
self._Y[lp] = ssg * xx + csg * yy
# FluxMolecule related functions
[docs]
def allocate_FMP(self, mf: int = 2) -> None:
"""
:param mf:
"""
self._FMP.allocate(size=self.get_face_count() * mf)
[docs]
def deallocate_FMP(self) -> None:
"""
"""
self.__reset_FMP()
[docs]
def check_FMP_cell_metrics(self, ic: int, check_sum: Union[list, ndarray]) -> None:
"""
:param ic:
:param check_sum:
"""
print(' face tag: %s' % self._tag)
self._FMP.check_cell_metrics(ic, check_sum)
[docs]
class Glue:
"""
"""
def __init__(self, face_def_A: Union['Face', list] = None, face_def_B: Union['Face', list] = None) -> None:
self.__A = Face(face_def_A)
self.__B = Face(face_def_B)
self._type = INTFTypes.UNDEFINED
if self.__A.alpha != 0. or self.__B.alpha != 0.:
self._type = INTFTypes.ROTPERINT
if self.__A.delta.length() != 0 or self.__B.delta.length() != 0:
self._type = INTFTypes.TRANSPERINT
@property
def type(self) -> Any:
"""
:return:
"""
return self._type
[docs]
def get_face_defs(self) -> Tuple[Face, Face]:
"""
:return:
"""
return self.__A, self.__B
[docs]
def set_face_defs(self, face_def_A: Union['Face', list] = None, face_def_B: Union['Face', list] = None) -> None:
"""
:param face_def_A:
:param face_def_B:
"""
self.__A.set_def(face_def_A)
self.__B.set_def(face_def_B)