"""
In this script, we define a mesh in a unit cube,
⭕ To access the source code, click on the [source] button at the right
side or click on
:download:`[crazy_mesh_hybrid.py]</contents/LIBRARY/ptc/mathischeap_ptc/crazy_mesh_hybrid.py>`.
Dependence may exist. In case of error, check import and install required
packages or download required scripts. © mathischeap.com
"""
from abc import ABC
import numpy as np
from crazy_mesh import CrazyMesh, CrazyMeshGlobalNumbering, CrazyMeshGlobalBoundaryDOFs
from coordinate_transformation_surface import extract_surface_coordinate_transformations_of
[docs]
class CrazyMeshHybrid(CrazyMesh):
"""The hybrid version of class :class:`CrazyMesh`.
"""
[docs]
def surface_CT_of_element_index(self, surface_name, i, j, k):
"""For the ``surface_name`` side of element
:math:`\\Omega_{i,j,k}`.
:param surface_name: 'N' (:math:`\\xi^-`), 'S' (:math:`\\xi^+`),
'W'(:math:`\\eta^-`), 'E' (:math:`\\eta^+`),
'B' (:math:`\\varsigma^-`) or 'F' (:math:`\\varsigma^+`)?
:param i: Element index ``i``.
:param j: Element index ``j``.
:param k: Element index ``k``.
:return:
"""
assert len(surface_name) == 1 and surface_name in 'NSWEBF', \
f"surface_name={surface_name} wrong, be one of NSWEBF."
indicator = surface_name + ':' + str(i) + '-' + str(j) + '-' + str(k)
if indicator in self._cache_:
return self._cache_[indicator]
element_CT = self.CT_of_element_index(i, j, k)
surface_CT = extract_surface_coordinate_transformations_of(
element_CT, which_sides=surface_name)[0]
self._cache_[indicator] = surface_CT
return surface_CT
[docs]
def surface_CT_of_element_number(self, surface_name, m):
"""For the ``surface_name`` side of element
:math:`\\Omega_{m}`.
:param surface_name: 'N' (:math:`\\xi^-`), 'S' (:math:`\\xi^+`),
'W'(:math:`\\eta^-`), 'E' (:math:`\\eta^+`),
'B' (:math:`\\varsigma^-`) or 'F' (:math:`\\varsigma^+`)?
:param m: Element :math:`\\Omega_{m}`.
:return:
"""
K = self.K
assert 0 <= m < K**3 and m % 1 == 0, \
f"m={m} is wrong, must be an integer between 0 and {K**3}."
k = m // K**2
j = ( m - k * K**2 ) // K
i = m - k * K**2 - j * K
return self.surface_CT_of_element_index(surface_name, i, j, k)
[docs]
class CrazyMeshHybridGlobalNumbering(CrazyMeshGlobalNumbering):
"""The hybrid version of class :class:`CrazyMeshGlobalNumbering`.
"""
@property
def NP(self):
"""Generate a global numbering for the dofs of an element in
:math:`\\text{NP}_{N}(\\Omega)` on a crazy mesh of
:math:`K^3` elements.
"""
K, N = self.K, self.N
num_basis = (N + 1) ** 3
total_dofs = num_basis * K**3
GM_NP = np.arange(total_dofs, dtype='int').reshape(
(K ** 3, num_basis), order='C'
)
return GM_NP
@property
def EP(self):
"""Generate a global numbering for the dofs of an element in
:math:`\\text{EP}_{N-1}(\\Omega)` on a hybrid crazy mesh of
:math:`K^3` elements.
"""
K, N = self.K, self.N
num_basis = N * (N + 1)**2 * 3
total_dofs = num_basis * K**3
GM_EP = np.arange(total_dofs, dtype='int').reshape(
(K ** 3, num_basis), order='C'
)
return GM_EP
@property
def FP(self):
"""Generate a global numbering for the dofs of an element in
:math:`\\text{FP}_{N-1}(\Omega)` on a hybrid crazy mesh of
:math:`K^3` elements.
"""
K, N = self.K, self.N
num_basis = N**2 * (N + 1) * 3
total_dofs = num_basis * K**3
GM_FP = np.arange(total_dofs, dtype='int').reshape(
(K ** 3, num_basis), order='C'
)
return GM_FP
@property
def VP(self):
"""Generate a global numbering for the dofs of an element in
:math:`\\text{VP}_{N-1}(\Omega)` on a hybrid crazy mesh of
:math:`K^3` elements.
Note that for :math:`\\text{VP}_{N-1}(\Omega)`, we obviously
can use the numbering for the non-hybrid crazy mesh such it is
discontinuous any way.
"""
K, N = self.K, self.N
num_basis = N**3
total_dofs = num_basis * K**3
GM_FP = np.arange(total_dofs, dtype='int').reshape(
(K ** 3, num_basis), order='C'
)
return GM_FP
@property
def TN(self):
"""Generate a global numbering for a discrete trace variable in
:math:`\\text{TN}_{N}(\\partial\\Omega)` on a hybrid crazy mesh of
:math:`K^3` elements.
"""
raise NotImplementedError("Could you code it?")
@property
def TE(self):
"""Generate a global numbering for a discrete trace variable in
:math:`\\text{TE}_{N-1}(\\partial\\Omega)` on a hybrid crazy mesh of
:math:`K^3` elements.
"""
raise NotImplementedError("Could you code it?")
@property
def TF(self):
"""Generate a global numbering for a discrete trace variable in
:math:`\\text{TF}_{N-1}(\\partial\\Omega)` on a hybrid crazy mesh of
:math:`K^3` elements.
"""
K, N = self.K, self.N
NN_KK = N**2 * K**2
NK = N * K
x_face = np.arange(NN_KK * (K+1), dtype='int'
).reshape((K+1, NK, NK), order='F')
y_face = np.arange(NN_KK * (K+1), dtype='int'
).reshape((NK, K+1, NK), order='F') + NN_KK * (K+1)
z_face = np.arange(NN_KK * (K+1), dtype='int'
).reshape((NK, NK, K+1), order='F') + 2 * NN_KK * (K+1)
GM_TF = np.zeros((K ** 3, 6 * N**2), dtype='int') # K^3 elements, on each element has 6 sides, and each sides have N^2 faces
for k in range(K):
for j in range(K):
for i in range(K):
m = i + j * K + k * K**2
GM_TF[m] = np.concatenate(
(x_face[i , j * N:(j + 1) * N, k * N:(k + 1) * N].ravel('F'),
x_face[i+1, j * N:(j + 1) * N, k * N:(k + 1) * N].ravel('F'),
y_face[i * N:(i + 1) * N, j , k * N:(k + 1) * N].ravel('F'),
y_face[i * N:(i + 1) * N, j+1, k * N:(k + 1) * N].ravel('F'),
z_face[i * N:(i + 1) * N, j * N:(j + 1) * N, k ].ravel('F'),
z_face[i * N:(i + 1) * N, j * N:(j + 1) * N, k+1].ravel('F')))
return GM_TF
[docs]
class CrazyMeshHybridGlobalBoundaryDOFs(CrazyMeshGlobalBoundaryDOFs, ABC):
"""The hybrid version of class :class:`CrazyMeshGlobalBoundaryDOFs`.
"""
@property
def TN(self):
"""Find the dofs of an element in
:math:`\\text{TN}_{N}(\\partial\\Omega)` which are on boundary of the
crazy mesh of :math:`K^3` elements.
:returns: A dict whose keys are 'x_minus', 'x_plus', 'y_minus',
'y_plus', 'z_minus', 'z_plus', and whose values are the
global numbering of the dofs on the boundaries indicated
by the keys.
"""
raise NotImplementedError("Could you code it?")
@property
def TE(self):
"""Find the dofs of an element in
:math:`\\text{TE}_{N-1}(\\partial\\Omega)` which are on boundary of the
crazy mesh of :math:`K^3` elements.
:returns: A dict whose keys are 'x_minus', 'x_plus', 'y_minus',
'y_plus', 'z_minus', 'z_plus', and whose values are the
global numbering of the dofs on the boundaries indicated
by the keys.
"""
raise NotImplementedError("Could you code it?")
@property
def TF(self):
"""Find the dofs of an element in
:math:`\\text{TF}_{N-1}(\\partial\\Omega)` which are on boundary of the
crazy mesh of :math:`K^3` elements.
:returns: A dict whose keys are 'x_minus', 'x_plus', 'y_minus',
'y_plus', 'z_minus', 'z_plus', and whose values are the
global numbering of the dofs on the boundaries indicated
by the keys.
"""
K, N = self.K, self.N
NN_KK = N**2 * K**2
NK = N * K
x_face = np.arange(NN_KK * (K+1), dtype='int'
).reshape((K+1, NK, NK), order='F')
y_face = np.arange(NN_KK * (K+1), dtype='int'
).reshape((NK, K+1, NK), order='F') + NN_KK * (K+1)
z_face = np.arange(NN_KK * (K+1), dtype='int'
).reshape((NK, NK, K+1), order='F') + 2 * NN_KK * (K+1)
DOFs_on_boundary = dict()
DOFs_on_boundary['x_minus'] = x_face[0, :, :].ravel('F')
DOFs_on_boundary['x_plus'] = x_face[-1, :, :].ravel('F')
DOFs_on_boundary['y_minus'] = y_face[:, 0, :].ravel('F')
DOFs_on_boundary['y_plus'] = y_face[:, -1, :].ravel('F')
DOFs_on_boundary['z_minus'] = z_face[:, :, 0].ravel('F')
DOFs_on_boundary['z_plus'] = z_face[:, :, -1].ravel('F')
return DOFs_on_boundary
[docs]
class CrazyMeshHybridLocalBoundaryDOFs(object):
"""Like the :class:`CrazyMeshHybridGlobalBoundaryDOFs`, but we now
return the local numbering on each boundary.
:param K: The crazy mesh is of :math:`K^3` elements.
:param N: The degree :math:`N`. of the to be used mimetic polynomial
basis functions.
:type K: int
:type N: int
"""
def __init__(self, K, N):
self.K, self.N = K, N
@property
def NP(self):
"""Find the dofs of an element in
:math:`\\text{NP}_{N}(\\Omega)` which are on boundary of the
crazy mesh of :math:`K^3` elements.
:returns: A dict whose keys are 'x_minus', 'x_plus', 'y_minus',
'y_plus', 'z_minus', 'z_plus', and whose values are the
global numbering of the dofs on the boundaries indicated
by the keys.
"""
raise NotImplementedError("Could you code it?")
@property
def EP(self):
"""Find the dofs of an element in
:math:`\\text{EP}_{N-1}(\\Omega)` which are on boundary of the
crazy mesh of :math:`K^3` elements.
:returns: A dict whose keys are 'x_minus', 'x_plus', 'y_minus',
'y_plus', 'z_minus', 'z_plus', and whose values are the
global numbering of the dofs on the boundaries indicated
by the keys.
"""
raise NotImplementedError("Could you code it?")
@property
def FP(self):
"""Find the dofs of an element in
:math:`\\text{FP}_{N-1}(\\Omega)` which are on boundary of the
crazy mesh of :math:`K^3` elements.
:returns: A dict whose keys are 'x_minus', 'x_plus', 'y_minus',
'y_plus', 'z_minus', 'z_plus', and whose values are the
global numbering of the dofs on the boundaries indicated
by the keys.
"""
K, N = self.K, self.N
local_DOFs_on_boundary = dict()
local_DOFs_on_boundary['x_minus'] = dict()
local_DOFs_on_boundary['x_plus'] = dict()
local_DOFs_on_boundary['y_minus'] = dict()
local_DOFs_on_boundary['y_plus'] = dict()
local_DOFs_on_boundary['z_minus'] = dict()
local_DOFs_on_boundary['z_plus'] = dict()
elements_global_numbering = np.arange(K ** 3, dtype='int').reshape(
(K, K, K), order='F'
)
local_numbering_x = np.arange(N ** 2 * (N+1), dtype='int').reshape(
(N+1, N, N), order='F'
)
local_numbering_y = np.arange(N ** 2 * (N+1), 2* N ** 2 * (N+1), dtype='int').reshape(
(N, N+1, N), order='F'
)
local_numbering_z = np.arange(2 *N ** 2 * (N+1), 3 *N ** 2 * (N+1), dtype='int').reshape(
(N, N, N+1), order='F'
)
# elements near North, x^-
elements = elements_global_numbering[0, :, :].ravel('F')
for i in elements:
local_DOFs_on_boundary['x_minus'][i] = local_numbering_x[0, :, :].ravel('F')
# elements near South, x^+
elements = elements_global_numbering[-1, :, :].ravel('F')
for i in elements:
local_DOFs_on_boundary['x_plus'][i] = local_numbering_x[-1, :, :].ravel('F')
# elements near North, y^-
elements = elements_global_numbering[:, 0, :].ravel('F')
for i in elements:
local_DOFs_on_boundary['y_minus'][i] = local_numbering_y[:, 0, :].ravel('F')
# elements near South, y^+
elements = elements_global_numbering[:, -1, :].ravel('F')
for i in elements:
local_DOFs_on_boundary['y_plus'][i] = local_numbering_y[:, -1, :].ravel('F')
# elements near North, z^-
elements = elements_global_numbering[:, :, 0].ravel('F')
for i in elements:
local_DOFs_on_boundary['z_minus'][i] = local_numbering_z[:, :, 0].ravel('F')
# elements near South, z^+
elements = elements_global_numbering[:, :, -1].ravel('F')
for i in elements:
local_DOFs_on_boundary['z_plus'][i] = local_numbering_z[:, :, -1].ravel('F')
return local_DOFs_on_boundary
@property
def TN(self):
"""Find the dofs of an element in
:math:`\\text{TN}_{N}(\\partial\\Omega)` which are on boundary of the
crazy mesh of :math:`K^3` elements.
:returns: A dict whose keys are 'x_minus', 'x_plus', 'y_minus',
'y_plus', 'z_minus', 'z_plus', and whose values are the
global numbering of the dofs on the boundaries indicated
by the keys.
"""
raise NotImplementedError("Could you code it?")
@property
def TE(self):
"""Find the dofs of an element in
:math:`\\text{TE}_{N-1}(\\partial\\Omega)` which are on boundary of the
crazy mesh of :math:`K^3` elements.
:returns: A dict whose keys are 'x_minus', 'x_plus', 'y_minus',
'y_plus', 'z_minus', 'z_plus', and whose values are the
global numbering of the dofs on the boundaries indicated
by the keys.
"""
raise NotImplementedError("Could you code it?")
@property
def TF(self):
"""Find the dofs of an element in
:math:`\\text{TF}_{N-1}(\\partial\\Omega)` which are on boundary of the
crazy mesh of :math:`K^3` elements.
:returns: A dict whose keys are 'x_minus', 'x_plus', 'y_minus',
'y_plus', 'z_minus', 'z_plus', and whose values are the
global numbering of the dofs on the boundaries indicated
by the keys.
"""
K, N = self.K, self.N
local_DOFs_on_boundary = dict()
local_DOFs_on_boundary['x_minus'] = dict()
local_DOFs_on_boundary['x_plus'] = dict()
local_DOFs_on_boundary['y_minus'] = dict()
local_DOFs_on_boundary['y_plus'] = dict()
local_DOFs_on_boundary['z_minus'] = dict()
local_DOFs_on_boundary['z_plus'] = dict()
elements_global_numbering = np.arange(K**3, dtype='int').reshape(
(K, K, K), order='F'
)
# elements near North, x^-
elements = elements_global_numbering[0, :, :].ravel('F')
for i in elements:
local_DOFs_on_boundary['x_minus'][i] = [_ for _ in range(N**2)]
# elements near South, x^+
elements = elements_global_numbering[-1, :, :].ravel('F')
for i in elements:
local_DOFs_on_boundary['x_plus'][i] = [N**2+_ for _ in range(N**2)]
# elements near North, y^-
elements = elements_global_numbering[:, 0, :].ravel('F')
for i in elements:
local_DOFs_on_boundary['y_minus'][i] = [2*N**2+_ for _ in range(N**2)]
# elements near South, y^+
elements = elements_global_numbering[:, -1, :].ravel('F')
for i in elements:
local_DOFs_on_boundary['y_plus'][i] = [3*N**2+_ for _ in range(N**2)]
# elements near North, z^-
elements = elements_global_numbering[:, :, 0].ravel('F')
for i in elements:
local_DOFs_on_boundary['z_minus'][i] = [4*N**2+_ for _ in range(N**2)]
# elements near South, z^+
elements = elements_global_numbering[:, :, -1].ravel('F')
for i in elements:
local_DOFs_on_boundary['z_plus'][i] = [5*N**2+_ for _ in range(N**2)]
return local_DOFs_on_boundary
if __name__ == '__main__':
import doctest
doctest.testmod()
K = 2
N = 2
GN = CrazyMeshHybridGlobalNumbering(K, N)
BN = CrazyMeshHybridGlobalBoundaryDOFs(K, N)
LBN = CrazyMeshHybridLocalBoundaryDOFs(K, N)
tf = LBN.TF
print(tf)
tf = LBN.FP
print(tf)