PyHOPE 0.10.0__tar.gz → 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pyhope-0.10.0 → pyhope-1.0.0}/AUTHORS.md +2 -0
- {pyhope-0.10.0 → pyhope-1.0.0}/PKG-INFO +8 -4
- {pyhope-0.10.0 → pyhope-1.0.0}/README.md +2 -2
- {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/__init__.py +42 -32
- {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/basis/basis_basis.py +10 -8
- {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/basis/basis_connect.py +19 -15
- {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/basis/basis_jacobian.py +14 -11
- {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/basis/basis_watertight.py +28 -21
- {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/check/check_health.py +36 -28
- {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/check/check_install.py +109 -142
- {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/check/check_unittest.py +30 -22
- {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/common/common.py +12 -11
- pyhope-1.0.0/pyhope/common/common_git.py +195 -0
- {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/common/common_numba.py +6 -5
- {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/common/common_parallel.py +9 -8
- {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/common/common_progress.py +1 -1
- {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/common/common_template.py +3 -6
- {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/common/common_tools.py +18 -14
- {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/common/common_vars.py +13 -15
- pyhope-1.0.0/pyhope/gmsh/gmsh.py +70 -0
- {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/gmsh/gmsh_install.py +9 -10
- {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/io/formats/meshio.py +4 -6
- {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/io/formats/vtk.py +1 -2
- {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/io/io.py +38 -32
- {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/io/io_debug.py +22 -23
- {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/io/io_gmsh.py +2 -2
- {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/io/io_vars.py +7 -0
- {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/io/io_xdmf.py +4 -9
- {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/mesh/connect/connect.py +26 -26
- {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/mesh/connect/connect_mortar.py +32 -33
- {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/mesh/connect/connect_rbtree.py +14 -13
- {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/mesh/elements/elements_ordering.py +4 -5
- {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/mesh/elements/elements_shapefunctions.py +2 -2
- {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/mesh/extrude/mesh_extrude.py +278 -153
- {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/mesh/fem/fem.py +58 -66
- {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/mesh/mesh.py +27 -5
- {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/mesh/mesh_builtin.py +24 -18
- {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/mesh/mesh_common.py +96 -102
- {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/mesh/mesh_duplicates.py +7 -9
- {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/mesh/mesh_external.py +12 -12
- {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/mesh/mesh_mortar.py +7 -9
- {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/mesh/mesh_orient.py +4 -5
- {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/mesh/mesh_sides.py +7 -7
- {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/mesh/mesh_sort.py +81 -39
- {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/mesh/mesh_vars.py +27 -18
- {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/mesh/reader/reader_gambit.py +34 -21
- {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/mesh/reader/reader_gmsh.py +133 -116
- {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/mesh/reader/reader_hopr.py +15 -15
- {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/mesh/sort/sort_hilbert.py +171 -9
- {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/mesh/topology/mesh_serendipity.py +5 -5
- {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/mesh/topology/mesh_splittohex.py +65 -69
- {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/mesh/topology/mesh_topology.py +71 -70
- {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/mesh/transform/mesh_transform.py +11 -4
- {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/mesh/transform/templates/convtest.py +17 -11
- {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/mesh/transform/templates/cylinder.py +8 -3
- {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/mesh/transform/templates/sphere.py +8 -1
- {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/mesh/transform/templates/sphere_box.py +8 -1
- {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/meshio/meshio_convert.py +27 -26
- {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/meshio/meshio_nodes.py +2 -3
- {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/meshio/meshio_ordering.py +20 -18
- {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/output/output.py +18 -11
- {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/readintools/commandline.py +5 -13
- {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/readintools/readintools.py +122 -121
- {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/script/pyhope_cli.py +1 -2
- {pyhope-0.10.0 → pyhope-1.0.0}/pyproject.toml +56 -5
- {pyhope-0.10.0 → pyhope-1.0.0}/LICENSE.md +0 -0
- {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/__main__.py +0 -0
- {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/check/check.py +0 -0
- {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/common/common_unique.py +0 -0
- {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/config/config.py +0 -0
- {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/gmsh/gmsh_override.py +0 -0
- {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/io/formats/cgns.py +0 -0
- {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/io/formats/gmsh.py +0 -0
- {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/mesh/extrude/templates/linear.py +0 -0
- {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/mesh/transform/templates/default.py +0 -0
- {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/mesh/transform/templates/phill.py +0 -0
- {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/mesh/transform/templates/sine.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: PyHOPE
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 1.0.0
|
|
4
4
|
Summary: Python High-Order Preprocessing Environment
|
|
5
5
|
License-Expression: GPL-3.0-only
|
|
6
6
|
License-File: AUTHORS.md
|
|
@@ -8,6 +8,8 @@ License-File: LICENSE.md
|
|
|
8
8
|
Keywords: PyHOPE,mesh generator
|
|
9
9
|
Author: Numerics Research Group (NRG)
|
|
10
10
|
Author-email: numerics@iag.uni-stuttgart.de
|
|
11
|
+
Maintainer: Patrick Kopper
|
|
12
|
+
Maintainer-email: kopper@iag.uni-stuttgart.de
|
|
11
13
|
Requires-Python: >=3.10
|
|
12
14
|
Classifier: Development Status :: 4 - Beta
|
|
13
15
|
Classifier: Operating System :: POSIX :: Linux
|
|
@@ -29,9 +31,11 @@ Requires-Dist: packaging
|
|
|
29
31
|
Requires-Dist: plotext
|
|
30
32
|
Requires-Dist: scipy
|
|
31
33
|
Requires-Dist: typing-extensions
|
|
34
|
+
Project-URL: Changelog, https://github.com/hopr-framework/PyHOPE/blob/main/CHANGELOG.md
|
|
32
35
|
Project-URL: Documentation, https://hopr-framework.github.io/PyHOPE
|
|
33
36
|
Project-URL: Homepage, https://numericsresearchgroup.org
|
|
34
|
-
Project-URL:
|
|
37
|
+
Project-URL: Issues, https://github.com/hopr-framework/PyHOPE/issues
|
|
38
|
+
Project-URL: Repository, https://github.com/hopr-framework/PyHOPE
|
|
35
39
|
Description-Content-Type: text/markdown
|
|
36
40
|
|
|
37
41
|
[](https://github.com/hopr-framework/PyHOPE/actions/workflows/ci.yml)
|
|
@@ -61,7 +65,7 @@ PyHOPE is built using standard Python packages. You can install PyHOPE by follow
|
|
|
61
65
|
> For new shell sessions, the virtual environment must be re-sourced using `source venv/bin/activate` before using `pyhope` commands.
|
|
62
66
|
|
|
63
67
|
2. **Install PyHOPE**
|
|
64
|
-
PyHOPE is installed using `pip`, the Python package installer. This command fetches the PyHOPE package and its dependencies from PyPI (Python Package Index) and installs them.
|
|
68
|
+
PyHOPE is installed using `pip`, the Python package installer. This command fetches the PyHOPE package and its dependencies from [PyPI](https://pypi.org/project/PyHOPE) (Python Package Index) and installs them.
|
|
65
69
|
```bash
|
|
66
70
|
python -m pip install pyhope
|
|
67
71
|
```
|
|
@@ -235,7 +239,7 @@ with Mesh('1-01-cartbox_mesh.h5') as m:
|
|
|
235
239
|
```
|
|
236
240
|
|
|
237
241
|
# Cite
|
|
238
|
-
This is a scientific project. If you use PyHOPE for publications or presentations in science, please support the project by citing the following article.
|
|
242
|
+
This is a scientific project. If you use PyHOPE for publications or presentations in science, please support the project by citing the [following article](https://joss.theoj.org/papers/10.21105/joss.08769).
|
|
239
243
|
```bibtex
|
|
240
244
|
@article{kopper2025pyhope:joss,
|
|
241
245
|
title = {{PyHOPE}: A Python Toolkit for Three-Dimensional Unstructured High-Order Meshes},
|
|
@@ -25,7 +25,7 @@ PyHOPE is built using standard Python packages. You can install PyHOPE by follow
|
|
|
25
25
|
> For new shell sessions, the virtual environment must be re-sourced using `source venv/bin/activate` before using `pyhope` commands.
|
|
26
26
|
|
|
27
27
|
2. **Install PyHOPE**
|
|
28
|
-
PyHOPE is installed using `pip`, the Python package installer. This command fetches the PyHOPE package and its dependencies from PyPI (Python Package Index) and installs them.
|
|
28
|
+
PyHOPE is installed using `pip`, the Python package installer. This command fetches the PyHOPE package and its dependencies from [PyPI](https://pypi.org/project/PyHOPE) (Python Package Index) and installs them.
|
|
29
29
|
```bash
|
|
30
30
|
python -m pip install pyhope
|
|
31
31
|
```
|
|
@@ -199,7 +199,7 @@ with Mesh('1-01-cartbox_mesh.h5') as m:
|
|
|
199
199
|
```
|
|
200
200
|
|
|
201
201
|
# Cite
|
|
202
|
-
This is a scientific project. If you use PyHOPE for publications or presentations in science, please support the project by citing the following article.
|
|
202
|
+
This is a scientific project. If you use PyHOPE for publications or presentations in science, please support the project by citing the [following article](https://joss.theoj.org/papers/10.21105/joss.08769).
|
|
203
203
|
```bibtex
|
|
204
204
|
@article{kopper2025pyhope:joss,
|
|
205
205
|
title = {{PyHOPE}: A Python Toolkit for Three-Dimensional Unstructured High-Order Meshes},
|
|
@@ -26,24 +26,35 @@
|
|
|
26
26
|
# ----------------------------------------------------------------------------------------------------------------------------------
|
|
27
27
|
# Standard libraries
|
|
28
28
|
# ----------------------------------------------------------------------------------------------------------------------------------
|
|
29
|
+
from __future__ import annotations
|
|
29
30
|
from collections import namedtuple
|
|
31
|
+
from collections.abc import Callable
|
|
30
32
|
from contextlib import contextmanager
|
|
31
33
|
from functools import update_wrapper
|
|
32
34
|
from typing import final
|
|
35
|
+
from typing import ParamSpec, TypeVar
|
|
33
36
|
# ----------------------------------------------------------------------------------------------------------------------------------
|
|
34
37
|
# Third-party libraries
|
|
35
38
|
# ----------------------------------------------------------------------------------------------------------------------------------
|
|
36
39
|
# ----------------------------------------------------------------------------------------------------------------------------------
|
|
40
|
+
# Typing libraries
|
|
41
|
+
# ----------------------------------------------------------------------------------------------------------------------------------
|
|
42
|
+
import typing
|
|
43
|
+
if typing.TYPE_CHECKING:
|
|
44
|
+
from types import GeneratorType
|
|
45
|
+
# ----------------------------------------------------------------------------------------------------------------------------------
|
|
37
46
|
# Local imports
|
|
38
47
|
# ----------------------------------------------------------------------------------------------------------------------------------
|
|
48
|
+
P = ParamSpec('P')
|
|
49
|
+
R = TypeVar( 'R')
|
|
39
50
|
# ==================================================================================================================================
|
|
40
51
|
|
|
41
52
|
|
|
42
|
-
def _staticwrapper(func):
|
|
53
|
+
def _staticwrapper(func: Callable[P, R]) -> Callable[P, R]:
|
|
43
54
|
""" Custom helper to lift the (annotations, doc, etc.) to the staticmethod
|
|
44
55
|
"""
|
|
45
56
|
# Create a wrapper that carries the metadata
|
|
46
|
-
def wrapper(*args, **kwargs):
|
|
57
|
+
def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
|
|
47
58
|
return func(*args, **kwargs)
|
|
48
59
|
|
|
49
60
|
# Lift metadata (annotations, doc, etc.) to the wrapper
|
|
@@ -97,7 +108,7 @@ class Mapping:
|
|
|
97
108
|
|
|
98
109
|
|
|
99
110
|
# Define a named tuple to hold the mesh data
|
|
100
|
-
MeshContainer = namedtuple('Mesh',
|
|
111
|
+
MeshContainer = namedtuple('Mesh', # ty: ignore [mismatched-type-name]
|
|
101
112
|
['mesh', # The generated mesh object
|
|
102
113
|
'nGeo', # Polynomial order
|
|
103
114
|
'bcs', # Boundary conditions
|
|
@@ -108,7 +119,7 @@ MeshContainer = namedtuple('Mesh',
|
|
|
108
119
|
|
|
109
120
|
|
|
110
121
|
@contextmanager
|
|
111
|
-
def Mesh(*args: str, stdout: bool = False, stderr: bool = True):
|
|
122
|
+
def Mesh(*args: str, stdout: bool = False, stderr: bool = True) -> GeneratorType:
|
|
112
123
|
""" Mesh context manager to generate a mesh from a given file
|
|
113
124
|
|
|
114
125
|
Args:
|
|
@@ -150,34 +161,33 @@ def Mesh(*args: str, stdout: bool = False, stderr: bool = True):
|
|
|
150
161
|
raise ValueError(f'Mesh file not a valid HDF5 file: {arg}')
|
|
151
162
|
|
|
152
163
|
# Suppress output to standard output
|
|
153
|
-
with ExitStack() as stack:
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
ConnectMesh()
|
|
164
|
+
with ExitStack() as stack, open(os.devnull, 'w') as null:
|
|
165
|
+
if not stdout:
|
|
166
|
+
stack.enter_context(redirect_stdout(null))
|
|
167
|
+
if not stderr:
|
|
168
|
+
stack.enter_context(redirect_stderr(null))
|
|
169
|
+
|
|
170
|
+
# Perform the reduced PyHOPE initialization
|
|
171
|
+
with DefineConfig() as dc:
|
|
172
|
+
config.prms = dc
|
|
173
|
+
DefineCommon()
|
|
174
|
+
DefineIO()
|
|
175
|
+
DefineMesh()
|
|
176
|
+
|
|
177
|
+
with ReadConfig(args[0]) as rc:
|
|
178
|
+
config.params = rc
|
|
179
|
+
|
|
180
|
+
# Read-in required parameters
|
|
181
|
+
InitCommon()
|
|
182
|
+
InitIO()
|
|
183
|
+
InitMesh()
|
|
184
|
+
|
|
185
|
+
# Generate the actual mesh
|
|
186
|
+
GenerateMesh()
|
|
187
|
+
|
|
188
|
+
# Build our data structures
|
|
189
|
+
GenerateSides()
|
|
190
|
+
ConnectMesh()
|
|
181
191
|
|
|
182
192
|
# Export mesh variables
|
|
183
193
|
mesh = mesh_vars.mesh
|
|
@@ -142,7 +142,7 @@ if not NUMBA_AVAILABLE:
|
|
|
142
142
|
return 1.0 / wBary
|
|
143
143
|
else:
|
|
144
144
|
@jit(types.float64[:](types.int64, types.float64[:]), nopython=True, cache=True, nogil=True)
|
|
145
|
-
def barycentric_weights(_, xGP):
|
|
145
|
+
def barycentric_weights(_: Any, xGP: npt.NDArray[np.float64]) -> float: # noqa: ANN401
|
|
146
146
|
""" Compute the barycentric weights for a given node set
|
|
147
147
|
> Algorithm 30, Kopriva
|
|
148
148
|
"""
|
|
@@ -312,12 +312,14 @@ def polynomial_derivative_matrix_prism(order: int, xGP: npt.NDArray[np.float64])
|
|
|
312
312
|
D = np.zeros((3, nDOFs, nDOFs), dtype=np.float64)
|
|
313
313
|
|
|
314
314
|
# Precompute required Jacobi polynomials and derivatives
|
|
315
|
-
# fZETA(i) = P_i^(0,0)(c), dfZETA(i) = 0.5*(i+1)*P_{i-1}^{(1,1)}(c)
|
|
316
|
-
fZETA_all = np.array(tuple(
|
|
317
|
-
dfZETA_all = np.array(tuple(0.5*(i+1)*sp.special.eval_jacobi(i-1, 1, 1, c)
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
315
|
+
# fZETA(i) = P_i^(0,0)(c), dfZETA(i) = 0.5*(i+1)*P_{i-1}^{(1,1)}(c), with dfZETA(0)=0
|
|
316
|
+
fZETA_all = np.array(tuple(sp.special.eval_jacobi(i, 0, 0, c) for i in range(order)))
|
|
317
|
+
dfZETA_all = np.array(tuple(np.zeros_like(c) if i == 0 else 0.5*(i+1)*sp.special.eval_jacobi(i-1, 1, 1, c)
|
|
318
|
+
for i in range(order)))
|
|
319
|
+
# fETA(j) = P_j^(0,0)(b), dfETA(j) = 0.5*(j+1)*P_{j-1}^{(1,1)}(b), with dfETA(0)=0
|
|
320
|
+
fETA_all = np.array(tuple(sp.special.eval_jacobi(j, 0, 0, b) for j in range(order)))
|
|
321
|
+
dfETA_all = np.array(tuple(np.zeros_like(b) if j == 0 else 0.5*(j+1)*sp.special.eval_jacobi(j-1, 1, 1, b)
|
|
322
|
+
for j in range(order)))
|
|
321
323
|
|
|
322
324
|
jacobi_xi_polys = [[sp.special.jacobi(i, 2*j + 1, 0) for i in range(order - j)] for j in range(order)]
|
|
323
325
|
jacobi_xi_derivs = [[p.deriv() for p in row] for row in jacobi_xi_polys]
|
|
@@ -711,7 +713,7 @@ def evaluate_jacobian(xGeo_In: npt.NDArray[np.float64],
|
|
|
711
713
|
|
|
712
714
|
|
|
713
715
|
def evaluate_jacobian_simplex(xGeo_In: npt.NDArray[np.float64],
|
|
714
|
-
_: Any,
|
|
716
|
+
_: Any, # noqa: ANN401
|
|
715
717
|
D_EqToGL: npt.NDArray[np.float64]) -> npt.NDArray[np.float64]:
|
|
716
718
|
# Perform tensor contraction for each derivative
|
|
717
719
|
# Change basis for each direction
|
|
@@ -39,6 +39,7 @@ import numpy as np
|
|
|
39
39
|
# ----------------------------------------------------------------------------------------------------------------------------------
|
|
40
40
|
import typing
|
|
41
41
|
if typing.TYPE_CHECKING:
|
|
42
|
+
from pyhope.mesh.mesh_vars import ELEM
|
|
42
43
|
import numpy.typing as npt
|
|
43
44
|
# ----------------------------------------------------------------------------------------------------------------------------------
|
|
44
45
|
# Local imports
|
|
@@ -49,7 +50,7 @@ from pyhope.mesh.mesh_common import face_to_nodes
|
|
|
49
50
|
# ==================================================================================================================================
|
|
50
51
|
|
|
51
52
|
|
|
52
|
-
def check_sides(elem,
|
|
53
|
+
def check_sides(elem : ELEM,
|
|
53
54
|
failed_only: bool = False,
|
|
54
55
|
) -> Optional[list[tuple]]:
|
|
55
56
|
""" Check if connected sides have matching corner nodes
|
|
@@ -64,6 +65,9 @@ def check_sides(elem,
|
|
|
64
65
|
vvs: Final[list] = mesh_vars.vvs
|
|
65
66
|
points: Final[npt.NDArray] = mesh_vars.mesh.points
|
|
66
67
|
|
|
68
|
+
if elem.sides is None:
|
|
69
|
+
return None
|
|
70
|
+
|
|
67
71
|
for SideID in elem.sides:
|
|
68
72
|
master = sides[SideID]
|
|
69
73
|
# Only connected sides that are master sides and not small mortar sides
|
|
@@ -83,7 +87,7 @@ def check_sides(elem,
|
|
|
83
87
|
# Sanity check the flip with the other nodes
|
|
84
88
|
elem0 = elems[side[0].elemID]
|
|
85
89
|
elem1 = elems[side[1].elemID]
|
|
86
|
-
if elem1.type %
|
|
90
|
+
if elem1.type % 10 != 8:
|
|
87
91
|
continue
|
|
88
92
|
|
|
89
93
|
# Map the meshio nodes to the tensor-product nodes
|
|
@@ -95,7 +99,7 @@ def check_sides(elem,
|
|
|
95
99
|
try:
|
|
96
100
|
# Translate to periodic nodes if required
|
|
97
101
|
if side[0].bcid is not None and side[1].bcid is not None and bcs[side[1].bcid].type[0] == 1:
|
|
98
|
-
nbNodes = np.vectorize(lambda s: mesh_vars.periNodes[(s, bcs[side[1].bcid].name)], otypes=[int])(nbNodes)
|
|
102
|
+
nbNodes = np.vectorize(lambda s, side=side: mesh_vars.periNodes[(s, bcs[side[1].bcid].name)], otypes=[int])(nbNodes)
|
|
99
103
|
# Check if the node IDs match
|
|
100
104
|
success = np.array_equal(nodes, nbNodes)
|
|
101
105
|
# Fallback to comparison of physical coordinates
|
|
@@ -121,7 +125,7 @@ def check_sides(elem,
|
|
|
121
125
|
return results
|
|
122
126
|
|
|
123
127
|
|
|
124
|
-
def process_chunk(chunk) -> list:
|
|
128
|
+
def process_chunk(chunk: tuple) -> list:
|
|
125
129
|
"""Process a chunk of elements by checking surface normal orientation
|
|
126
130
|
"""
|
|
127
131
|
# Only keep failures to reduce memory and avoid building large arrays of successes
|
|
@@ -157,8 +161,8 @@ def CheckConnect() -> None:
|
|
|
157
161
|
elems: Final[list] = mesh_vars.elems
|
|
158
162
|
|
|
159
163
|
# Only consider hexahedrons
|
|
160
|
-
if any(cast(int, e.type) %
|
|
161
|
-
elemTypes = list(
|
|
164
|
+
if any(cast(int, e.type) % 10 != 8 for e in elems):
|
|
165
|
+
elemTypes = list({e.type for e in elems if e.type % 10 != 8})
|
|
162
166
|
print(hopout.warn('Ignored element type: {}'.format(
|
|
163
167
|
[re.sub(r"\d+$", "", mesh_vars.ELEMTYPE.inam[e][0]) for e in elemTypes]
|
|
164
168
|
)))
|
|
@@ -192,7 +196,7 @@ def CheckConnect() -> None:
|
|
|
192
196
|
if side.connection is None or side.sideType < 0:
|
|
193
197
|
continue
|
|
194
198
|
# Big mortar side is counted once
|
|
195
|
-
|
|
199
|
+
if side.connection < 0:
|
|
196
200
|
nconn += 1
|
|
197
201
|
# Internal side: only count the canonical representative and ignore virtual mortar sides
|
|
198
202
|
elif side.connection >= 0:
|
|
@@ -220,16 +224,16 @@ def CheckConnect() -> None:
|
|
|
220
224
|
# Print the information
|
|
221
225
|
strLen = max(len(str(side.sideID+1)), len(str(nbside.sideID+1)))
|
|
222
226
|
print(hopout.warn(f'> Element { elem.elemID+1:>{strLen}}, Side { side.face}, Side { side.sideID+1:>{strLen}}')) # noqa: E501
|
|
223
|
-
print(hopout.warn('- Coordinates : [' + ' '.join('{:12.3f}'
|
|
224
|
-
print(hopout.warn('- Coordinates : [' + ' '.join('{:12.3f}'
|
|
225
|
-
print(hopout.warn('- Coordinates : [' + ' '.join('{:12.3f}'
|
|
226
|
-
print(hopout.warn('- Coordinates : [' + ' '.join('{:12.3f}'
|
|
227
|
+
print(hopout.warn('- Coordinates : [' + ' '.join(f'{s:12.3f}' for s in points[ nodes[ 0, 0]]) + ']')) # noqa: E271
|
|
228
|
+
print(hopout.warn('- Coordinates : [' + ' '.join(f'{s:12.3f}' for s in points[ nodes[ 0, -1]]) + ']')) # noqa: E271
|
|
229
|
+
print(hopout.warn('- Coordinates : [' + ' '.join(f'{s:12.3f}' for s in points[ nodes[-1, 0]]) + ']')) # noqa: E271
|
|
230
|
+
print(hopout.warn('- Coordinates : [' + ' '.join(f'{s:12.3f}' for s in points[ nodes[-1, -1]]) + ']')) # noqa: E271
|
|
227
231
|
# print()
|
|
228
232
|
print(hopout.warn(f'> Element {nbelem.elemID+1:>{strLen}}, Side {nbside.face}, Side {nbside.sideID+1:>{strLen}}')) # noqa: E501
|
|
229
|
-
print(hopout.warn('- Coordinates : [' + ' '.join('{:12.3f}'
|
|
230
|
-
print(hopout.warn('- Coordinates : [' + ' '.join('{:12.3f}'
|
|
231
|
-
print(hopout.warn('- Coordinates : [' + ' '.join('{:12.3f}'
|
|
232
|
-
print(hopout.warn('- Coordinates : [' + ' '.join('{:12.3f}'
|
|
233
|
+
print(hopout.warn('- Coordinates : [' + ' '.join(f'{s:12.3f}' for s in points[nbnodes[ 0, 0]]) + ']')) # noqa: E271
|
|
234
|
+
print(hopout.warn('- Coordinates : [' + ' '.join(f'{s:12.3f}' for s in points[nbnodes[ 0, -1]]) + ']')) # noqa: E271
|
|
235
|
+
print(hopout.warn('- Coordinates : [' + ' '.join(f'{s:12.3f}' for s in points[nbnodes[-1, 0]]) + ']')) # noqa: E271
|
|
236
|
+
print(hopout.warn('- Coordinates : [' + ' '.join(f'{s:12.3f}' for s in points[nbnodes[-1, -1]]) + ']')) # noqa: E271
|
|
233
237
|
|
|
234
238
|
hopout.warning(f'Connectivity check failed for {len(results)} / {nconn} connections!')
|
|
235
239
|
sys.exit(1)
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
# Standard libraries
|
|
27
27
|
# ----------------------------------------------------------------------------------------------------------------------------------
|
|
28
28
|
from __future__ import annotations
|
|
29
|
+
from collections.abc import Callable
|
|
29
30
|
from typing import Final
|
|
30
31
|
# ----------------------------------------------------------------------------------------------------------------------------------
|
|
31
32
|
# Third-party libraries
|
|
@@ -49,13 +50,15 @@ SIMPLEX_TYPES: Final = {4, 5, 6}
|
|
|
49
50
|
HEX_TYPE: Final = 8
|
|
50
51
|
|
|
51
52
|
|
|
52
|
-
def evaluate_jacobian_dispatch(nodeCoords
|
|
53
|
+
def evaluate_jacobian_dispatch(nodeCoords: npt.NDArray[np.float64],
|
|
54
|
+
VdmGLtoAP : npt.NDArray[np.float64],
|
|
55
|
+
D_EqToGL : npt.NDArray[np.float64],
|
|
56
|
+
elem_type : int) -> npt.NDArray:
|
|
53
57
|
if elem_type in SIMPLEX_TYPES:
|
|
54
58
|
return evaluate_jacobian_simplex(nodeCoords, VdmGLtoAP, D_EqToGL)
|
|
55
|
-
|
|
59
|
+
if elem_type == HEX_TYPE:
|
|
56
60
|
return evaluate_jacobian( nodeCoords, VdmGLtoAP, D_EqToGL)
|
|
57
|
-
|
|
58
|
-
raise ValueError(f"Unsupported element type {elem_type}")
|
|
61
|
+
raise ValueError(f"Unsupported element type {elem_type}")
|
|
59
62
|
|
|
60
63
|
|
|
61
64
|
def plot_histogram(data: npt.NDArray[np.float64]) -> None:
|
|
@@ -97,14 +100,14 @@ def plot_histogram(data: npt.NDArray[np.float64]) -> None:
|
|
|
97
100
|
|
|
98
101
|
|
|
99
102
|
# Use Pool initializer to attach process-local data to the worker function
|
|
100
|
-
def init_worker(function, VdmGLtoAP: npt.NDArray[np.float64], D_EqToGL: npt.NDArray[np.float64]) -> None:
|
|
103
|
+
def init_worker(function: Callable, VdmGLtoAP: npt.NDArray[np.float64], D_EqToGL: npt.NDArray[np.float64]) -> None:
|
|
101
104
|
"""Initializer to set process-local attributes on the worker function
|
|
102
105
|
"""
|
|
103
106
|
function.VdmGLtoAP = VdmGLtoAP
|
|
104
107
|
function.D_EqToGL = D_EqToGL
|
|
105
108
|
|
|
106
109
|
|
|
107
|
-
def process_chunk(chunk) -> list[npt.NDArray[np.float64]]:
|
|
110
|
+
def process_chunk(chunk: tuple) -> list[npt.NDArray[np.float64]]:
|
|
108
111
|
"""Process a chunk of elements by evaluating the Jacobian for each
|
|
109
112
|
"""
|
|
110
113
|
chunk_results = []
|
|
@@ -155,7 +158,7 @@ def CheckJacobians() -> None:
|
|
|
155
158
|
nGeo: Final[int] = mesh_vars.nGeo + 1
|
|
156
159
|
elems: Final[list] = mesh_vars.elems
|
|
157
160
|
nodes: Final[npt.NDArray[np.float64]] = mesh_vars.mesh.points
|
|
158
|
-
elemBases: Final[set] =
|
|
161
|
+
elemBases: Final[set] = {e.type % 10 for e in elems}
|
|
159
162
|
|
|
160
163
|
# Compute the equidistant point set used by meshIO
|
|
161
164
|
xEq_fn = {4: lambda: equi_nodes_tetra(nGeo), # Tetrahedron
|
|
@@ -201,19 +204,19 @@ def CheckJacobians() -> None:
|
|
|
201
204
|
# Pre-compute LINTEN mappings for all element types
|
|
202
205
|
linCache = {}
|
|
203
206
|
elemOrder = 100 if mesh_vars.nGeo == 1 else 200
|
|
204
|
-
elemTypes = tuple(
|
|
207
|
+
elemTypes = tuple(s + elemOrder for s in elemBases)
|
|
205
208
|
for elemType in elemTypes:
|
|
206
209
|
try:
|
|
207
210
|
_, mapLin = LINTEN(elemType, order=mesh_vars.nGeo)
|
|
208
211
|
mapLin = np.array(tuple(mapLin[np.int64(i)] for i in range(len(mapLin))))
|
|
209
212
|
linCache[elemType] = mapLin
|
|
210
213
|
# Only hexahedrons supported for specific nGeo
|
|
211
|
-
except ValueError:
|
|
214
|
+
except ValueError: # noqa: PERF203
|
|
212
215
|
pass
|
|
213
216
|
|
|
214
217
|
for elem in elems:
|
|
215
218
|
elemType = elem.type
|
|
216
|
-
elemBase = int(elemType) %
|
|
219
|
+
elemBase = int(elemType) % 10
|
|
217
220
|
|
|
218
221
|
# Get the mapping
|
|
219
222
|
mapLin = linCache[elemType]
|
|
@@ -266,5 +269,5 @@ def CheckJacobians() -> None:
|
|
|
266
269
|
# Append the Jacobians to the elements
|
|
267
270
|
jacIter = iter(jacs)
|
|
268
271
|
for elem in elems:
|
|
269
|
-
elemType = elem.type
|
|
272
|
+
# elemType = elem.type
|
|
270
273
|
elem.jacobian = next(jacIter)
|
|
@@ -29,7 +29,7 @@ from __future__ import annotations
|
|
|
29
29
|
import gc
|
|
30
30
|
import re
|
|
31
31
|
from typing import Final, Optional, cast
|
|
32
|
-
from collections.abc import Iterable
|
|
32
|
+
from collections.abc import Callable, Iterable
|
|
33
33
|
# ----------------------------------------------------------------------------------------------------------------------------------
|
|
34
34
|
# Third-party libraries
|
|
35
35
|
# ----------------------------------------------------------------------------------------------------------------------------------
|
|
@@ -41,6 +41,7 @@ import typing
|
|
|
41
41
|
from pyhope.common.common_numba import NUMBA_AVAILABLE
|
|
42
42
|
if typing.TYPE_CHECKING or NUMBA_AVAILABLE:
|
|
43
43
|
import numpy.typing as npt
|
|
44
|
+
from pyhope.mesh.mesh_vars import ELEM
|
|
44
45
|
# ----------------------------------------------------------------------------------------------------------------------------------
|
|
45
46
|
# Local imports
|
|
46
47
|
# ----------------------------------------------------------------------------------------------------------------------------------
|
|
@@ -52,7 +53,10 @@ from pyhope.mesh.mesh_common import face_to_nodes
|
|
|
52
53
|
|
|
53
54
|
|
|
54
55
|
# Use Pool initializer to attach process-local data to the worker function
|
|
55
|
-
def init_worker(function
|
|
56
|
+
def init_worker(function : Callable,
|
|
57
|
+
VdmEqToGP: npt.NDArray[np.float64],
|
|
58
|
+
DGP : npt.NDArray[np.float64],
|
|
59
|
+
weights : npt.NDArray[np.float64]) -> None:
|
|
56
60
|
"""Initializer to set process-local attributes on the worker function
|
|
57
61
|
"""
|
|
58
62
|
function.VdmEqToGP = VdmEqToGP
|
|
@@ -122,7 +126,7 @@ def eval_nsurf(XGeo: npt.NDArray[np.float64],
|
|
|
122
126
|
return np.array((NSurf0, NSurf1, NSurf2), dtype=xGP.dtype)
|
|
123
127
|
|
|
124
128
|
|
|
125
|
-
def check_sides(elem,
|
|
129
|
+
def check_sides(elem : ELEM,
|
|
126
130
|
# points : npt.NDArray[np.float64],
|
|
127
131
|
VdmEqToGP: npt.NDArray[np.float64],
|
|
128
132
|
DGP : npt.NDArray[np.float64],
|
|
@@ -142,6 +146,9 @@ def check_sides(elem,
|
|
|
142
146
|
elemTol = np.cbrt(np.prod(np.ptp(points[elem.nodes], axis=0)))
|
|
143
147
|
elemType = elem.type
|
|
144
148
|
|
|
149
|
+
if elem.sides is None:
|
|
150
|
+
return None
|
|
151
|
+
|
|
145
152
|
for SideID in elem.sides:
|
|
146
153
|
side = sides[SideID]
|
|
147
154
|
|
|
@@ -151,12 +158,12 @@ def check_sides(elem,
|
|
|
151
158
|
continue
|
|
152
159
|
|
|
153
160
|
# Big mortar side
|
|
154
|
-
|
|
161
|
+
if side.connection < 0:
|
|
155
162
|
mortarType = abs(side.connection)
|
|
156
163
|
# INFO: This should be faster but I could not confirm the speedup in practice
|
|
157
164
|
# nSurf = eval_nsurf(np.moveaxis( points[ nodes], 2, 0), VdmEqToGP, DGP, weights)
|
|
158
165
|
# nSurf = eval_nsurf(np.transpose(np.take(points, nodes, axis=0), axes=(2, 0, 1)), VdmEqToGP, DGP, weights)
|
|
159
|
-
idx = elem.nodes[face_to_nodes(side.face, elemType, nGeo)]
|
|
166
|
+
idx = cast(np.ndarray, elem.nodes)[face_to_nodes(side.face, elemType, nGeo)]
|
|
160
167
|
nSurf = eval_nsurf(points[idx].transpose(2, 0, 1), VdmEqToGP, DGP, weights)
|
|
161
168
|
|
|
162
169
|
# Calculate the L2 norm of the side and take the maximum
|
|
@@ -191,7 +198,7 @@ def check_sides(elem,
|
|
|
191
198
|
|
|
192
199
|
# INFO: This should be faster but I could not confirm the speedup in practice
|
|
193
200
|
# nSurf = eval_nsurf(np.moveaxis( points[ nodes], 2, 0), VdmEqToGP, DGP, weights)
|
|
194
|
-
idx = elem.nodes[face_to_nodes(side.face, elemType, nGeo)]
|
|
201
|
+
idx = cast(np.ndarray, elem.nodes)[face_to_nodes(side.face, elemType, nGeo)]
|
|
195
202
|
nSurf = eval_nsurf(np.transpose(points[idx]), VdmEqToGP, DGP, weights)
|
|
196
203
|
|
|
197
204
|
# Calculate the L2 norm of the side and take the maximum
|
|
@@ -229,7 +236,7 @@ def check_sides(elem,
|
|
|
229
236
|
return results
|
|
230
237
|
|
|
231
238
|
|
|
232
|
-
def process_chunk(chunk) -> list:
|
|
239
|
+
def process_chunk(chunk: tuple) -> list:
|
|
233
240
|
"""Process a chunk of elements by checking surface normal orientation
|
|
234
241
|
"""
|
|
235
242
|
# Only keep failures to reduce memory and avoid building large arrays of successes
|
|
@@ -288,8 +295,8 @@ def CheckWatertight() -> None:
|
|
|
288
295
|
# checked = np.zeros((len(sides)), dtype=bool)
|
|
289
296
|
|
|
290
297
|
# Only consider hexahedrons
|
|
291
|
-
if any(e.type %
|
|
292
|
-
elemTypes = list(
|
|
298
|
+
if any(e.type % 10 != 8 for e in elems):
|
|
299
|
+
elemTypes = list({e.type for e in elems if e.type % 10 != 8})
|
|
293
300
|
print(hopout.warn('Ignored element type: {}'.format(
|
|
294
301
|
[re.sub(r"\d+$", "", mesh_vars.ELEMTYPE.inam[e][0]) for e in elemTypes]
|
|
295
302
|
)))
|
|
@@ -323,7 +330,7 @@ def CheckWatertight() -> None:
|
|
|
323
330
|
if side.connection is None or side.sideType < 0:
|
|
324
331
|
continue
|
|
325
332
|
# Big mortar side is counted once
|
|
326
|
-
|
|
333
|
+
if side.connection < 0:
|
|
327
334
|
nconn += 1
|
|
328
335
|
# Internal side: only count the canonical representative and ignore virtual mortar sides
|
|
329
336
|
elif side.connection >= 0:
|
|
@@ -348,24 +355,24 @@ def CheckWatertight() -> None:
|
|
|
348
355
|
print()
|
|
349
356
|
# Check if side is oriented inwards
|
|
350
357
|
errStr = 'Side is oriented inwards!' if nSurfErr < 0 \
|
|
351
|
-
else 'Surface normals are not within tolerance {:9.6e} > {:9.6e}'
|
|
358
|
+
else f'Surface normals are not within tolerance {nSurfErr:9.6e} > {tol:9.6e}'
|
|
352
359
|
print(hopout.warn(errStr, length=len(errStr)+16))
|
|
353
360
|
|
|
354
361
|
# Print the information
|
|
355
362
|
strLen = max(len(str(side.sideID+1)), len(str(nbside.sideID+1)))
|
|
356
363
|
print(hopout.warn(f'> Element { elem.elemID+1:>{strLen}}, Side { side.face}, Side { side.sideID+1:>{strLen}}')) # noqa: E501
|
|
357
|
-
print(hopout.warn('> Normal vector: [' + ' '.join('{:12.3f}'
|
|
358
|
-
print(hopout.warn('- Coordinates : [' + ' '.join('{:12.3f}'
|
|
359
|
-
print(hopout.warn('- Coordinates : [' + ' '.join('{:12.3f}'
|
|
360
|
-
print(hopout.warn('- Coordinates : [' + ' '.join('{:12.3f}'
|
|
361
|
-
print(hopout.warn('- Coordinates : [' + ' '.join('{:12.3f}'
|
|
364
|
+
print(hopout.warn('> Normal vector: [' + ' '.join(f'{s:12.3f}' for s in nSurf) + ']')) # noqa: E271
|
|
365
|
+
print(hopout.warn('- Coordinates : [' + ' '.join(f'{s:12.3f}' for s in points[ nodes[ 0, 0]]) + ']')) # noqa: E271
|
|
366
|
+
print(hopout.warn('- Coordinates : [' + ' '.join(f'{s:12.3f}' for s in points[ nodes[ 0, -1]]) + ']')) # noqa: E271
|
|
367
|
+
print(hopout.warn('- Coordinates : [' + ' '.join(f'{s:12.3f}' for s in points[ nodes[-1, 0]]) + ']')) # noqa: E271
|
|
368
|
+
print(hopout.warn('- Coordinates : [' + ' '.join(f'{s:12.3f}' for s in points[ nodes[-1, -1]]) + ']')) # noqa: E271
|
|
362
369
|
# print()
|
|
363
370
|
print(hopout.warn(f'> Element {nbelem.elemID+1:>{strLen}}, Side {nbside.face}, Side {nbside.sideID+1:>{strLen}}')) # noqa: E501
|
|
364
|
-
print(hopout.warn('> Normal vector: [' + ' '.join('{:12.3f}'
|
|
365
|
-
print(hopout.warn('- Coordinates : [' + ' '.join('{:12.3f}'
|
|
366
|
-
print(hopout.warn('- Coordinates : [' + ' '.join('{:12.3f}'
|
|
367
|
-
print(hopout.warn('- Coordinates : [' + ' '.join('{:12.3f}'
|
|
368
|
-
print(hopout.warn('- Coordinates : [' + ' '.join('{:12.3f}'
|
|
371
|
+
print(hopout.warn('> Normal vector: [' + ' '.join(f'{s:12.3f}' for s in nbnSurf) + ']')) # noqa: E271
|
|
372
|
+
print(hopout.warn('- Coordinates : [' + ' '.join(f'{s:12.3f}' for s in points[nbnodes[ 0, 0]]) + ']')) # noqa: E271
|
|
373
|
+
print(hopout.warn('- Coordinates : [' + ' '.join(f'{s:12.3f}' for s in points[nbnodes[ 0, -1]]) + ']')) # noqa: E271
|
|
374
|
+
print(hopout.warn('- Coordinates : [' + ' '.join(f'{s:12.3f}' for s in points[nbnodes[-1, 0]]) + ']')) # noqa: E271
|
|
375
|
+
print(hopout.warn('- Coordinates : [' + ' '.join(f'{s:12.3f}' for s in points[nbnodes[-1, -1]]) + ']')) # noqa: E271
|
|
369
376
|
|
|
370
377
|
hopout.error(f'Watertightness check failed for {len(results)} / {nconn} connections!')
|
|
371
378
|
|