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.
Files changed (77) hide show
  1. {pyhope-0.10.0 → pyhope-1.0.0}/AUTHORS.md +2 -0
  2. {pyhope-0.10.0 → pyhope-1.0.0}/PKG-INFO +8 -4
  3. {pyhope-0.10.0 → pyhope-1.0.0}/README.md +2 -2
  4. {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/__init__.py +42 -32
  5. {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/basis/basis_basis.py +10 -8
  6. {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/basis/basis_connect.py +19 -15
  7. {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/basis/basis_jacobian.py +14 -11
  8. {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/basis/basis_watertight.py +28 -21
  9. {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/check/check_health.py +36 -28
  10. {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/check/check_install.py +109 -142
  11. {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/check/check_unittest.py +30 -22
  12. {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/common/common.py +12 -11
  13. pyhope-1.0.0/pyhope/common/common_git.py +195 -0
  14. {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/common/common_numba.py +6 -5
  15. {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/common/common_parallel.py +9 -8
  16. {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/common/common_progress.py +1 -1
  17. {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/common/common_template.py +3 -6
  18. {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/common/common_tools.py +18 -14
  19. {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/common/common_vars.py +13 -15
  20. pyhope-1.0.0/pyhope/gmsh/gmsh.py +70 -0
  21. {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/gmsh/gmsh_install.py +9 -10
  22. {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/io/formats/meshio.py +4 -6
  23. {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/io/formats/vtk.py +1 -2
  24. {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/io/io.py +38 -32
  25. {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/io/io_debug.py +22 -23
  26. {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/io/io_gmsh.py +2 -2
  27. {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/io/io_vars.py +7 -0
  28. {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/io/io_xdmf.py +4 -9
  29. {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/mesh/connect/connect.py +26 -26
  30. {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/mesh/connect/connect_mortar.py +32 -33
  31. {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/mesh/connect/connect_rbtree.py +14 -13
  32. {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/mesh/elements/elements_ordering.py +4 -5
  33. {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/mesh/elements/elements_shapefunctions.py +2 -2
  34. {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/mesh/extrude/mesh_extrude.py +278 -153
  35. {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/mesh/fem/fem.py +58 -66
  36. {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/mesh/mesh.py +27 -5
  37. {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/mesh/mesh_builtin.py +24 -18
  38. {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/mesh/mesh_common.py +96 -102
  39. {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/mesh/mesh_duplicates.py +7 -9
  40. {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/mesh/mesh_external.py +12 -12
  41. {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/mesh/mesh_mortar.py +7 -9
  42. {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/mesh/mesh_orient.py +4 -5
  43. {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/mesh/mesh_sides.py +7 -7
  44. {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/mesh/mesh_sort.py +81 -39
  45. {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/mesh/mesh_vars.py +27 -18
  46. {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/mesh/reader/reader_gambit.py +34 -21
  47. {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/mesh/reader/reader_gmsh.py +133 -116
  48. {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/mesh/reader/reader_hopr.py +15 -15
  49. {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/mesh/sort/sort_hilbert.py +171 -9
  50. {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/mesh/topology/mesh_serendipity.py +5 -5
  51. {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/mesh/topology/mesh_splittohex.py +65 -69
  52. {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/mesh/topology/mesh_topology.py +71 -70
  53. {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/mesh/transform/mesh_transform.py +11 -4
  54. {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/mesh/transform/templates/convtest.py +17 -11
  55. {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/mesh/transform/templates/cylinder.py +8 -3
  56. {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/mesh/transform/templates/sphere.py +8 -1
  57. {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/mesh/transform/templates/sphere_box.py +8 -1
  58. {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/meshio/meshio_convert.py +27 -26
  59. {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/meshio/meshio_nodes.py +2 -3
  60. {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/meshio/meshio_ordering.py +20 -18
  61. {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/output/output.py +18 -11
  62. {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/readintools/commandline.py +5 -13
  63. {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/readintools/readintools.py +122 -121
  64. {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/script/pyhope_cli.py +1 -2
  65. {pyhope-0.10.0 → pyhope-1.0.0}/pyproject.toml +56 -5
  66. {pyhope-0.10.0 → pyhope-1.0.0}/LICENSE.md +0 -0
  67. {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/__main__.py +0 -0
  68. {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/check/check.py +0 -0
  69. {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/common/common_unique.py +0 -0
  70. {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/config/config.py +0 -0
  71. {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/gmsh/gmsh_override.py +0 -0
  72. {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/io/formats/cgns.py +0 -0
  73. {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/io/formats/gmsh.py +0 -0
  74. {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/mesh/extrude/templates/linear.py +0 -0
  75. {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/mesh/transform/templates/default.py +0 -0
  76. {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/mesh/transform/templates/phill.py +0 -0
  77. {pyhope-0.10.0 → pyhope-1.0.0}/pyhope/mesh/transform/templates/sine.py +0 -0
@@ -18,8 +18,10 @@ are listed in alphabetical order:
18
18
  * Daniel Appel
19
19
  * Marcel Blind
20
20
  * Stephen Copplestone
21
+ * Justin Du Plessis
21
22
  * Patrick Kopper
22
23
  * Marius Kurz
23
24
  * Leon Teichröb
24
25
  * Felix Rodach
25
26
  * Anna Schwarz
27
+ * Michel Tirapelle
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PyHOPE
3
- Version: 0.10.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: Repository, https://github.com/hopr-framework/pyhope
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
  [![CI](https://github.com/hopr-framework/PyHOPE/actions/workflows/ci.yml/badge.svg)](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
- with open(os.devnull, 'w') as null:
155
- if not stdout:
156
- stack.enter_context(redirect_stdout(null))
157
- if not stderr:
158
- stack.enter_context(redirect_stderr(null))
159
-
160
- # Perform the reduced PyHOPE initialization
161
- with DefineConfig() as dc:
162
- config.prms = dc
163
- DefineCommon()
164
- DefineIO()
165
- DefineMesh()
166
-
167
- with ReadConfig(args[0]) as rc:
168
- config.params = rc
169
-
170
- # Read-in required parameters
171
- InitCommon()
172
- InitIO()
173
- InitMesh()
174
-
175
- # Generate the actual mesh
176
- GenerateMesh()
177
-
178
- # Build our data structures
179
- GenerateSides()
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( sp.special.eval_jacobi(i , 0, 0, c) for i in range(order)))
317
- dfZETA_all = np.array(tuple(0.5*(i+1)*sp.special.eval_jacobi(i-1, 1, 1, c) for i in range(order)))
318
- # fETA(j) = P_j^(0,0)(tuple(), dfETA(j) = 0.5*(j+1)*P_{j-1}^{(1,1)}(b)
319
- fETA_all = np.array(tuple( sp.special.eval_jacobi(j , 0, 0, b) for j in range(order)))
320
- dfETA_all = np.array(tuple(0.5*(j+1)*sp.special.eval_jacobi(j-1, 1, 1, b) for j in range(order)))
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 % 100 != 8:
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) % 100 != 8 for e in elems):
161
- elemTypes = list(set([e.type for e in elems if e.type % 100 != 8]))
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
- elif side.connection < 0:
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}'.format(s) for s in points[ nodes[ 0, 0]]) + ']')) # noqa: E271
224
- print(hopout.warn('- Coordinates : [' + ' '.join('{:12.3f}'.format(s) for s in points[ nodes[ 0, -1]]) + ']')) # noqa: E271
225
- print(hopout.warn('- Coordinates : [' + ' '.join('{:12.3f}'.format(s) for s in points[ nodes[-1, 0]]) + ']')) # noqa: E271
226
- print(hopout.warn('- Coordinates : [' + ' '.join('{:12.3f}'.format(s) for s in points[ nodes[-1, -1]]) + ']')) # noqa: E271
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}'.format(s) for s in points[nbnodes[ 0, 0]]) + ']')) # noqa: E271
230
- print(hopout.warn('- Coordinates : [' + ' '.join('{:12.3f}'.format(s) for s in points[nbnodes[ 0, -1]]) + ']')) # noqa: E271
231
- print(hopout.warn('- Coordinates : [' + ' '.join('{:12.3f}'.format(s) for s in points[nbnodes[-1, 0]]) + ']')) # noqa: E271
232
- print(hopout.warn('- Coordinates : [' + ' '.join('{:12.3f}'.format(s) for s in points[nbnodes[-1, -1]]) + ']')) # noqa: E271
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, VdmGLtoAP, D_EqToGL, elem_type):
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
- elif elem_type == HEX_TYPE:
59
+ if elem_type == HEX_TYPE:
56
60
  return evaluate_jacobian( nodeCoords, VdmGLtoAP, D_EqToGL)
57
- else:
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] = set([e.type % 100 for e in elems])
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([s + elemOrder for s in elemBases])
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) % 100
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, VdmEqToGP, DGP, weights) -> None:
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
- elif side.connection < 0:
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 % 100 != 8 for e in elems):
292
- elemTypes = list(set([e.type for e in elems if e.type % 100 != 8]))
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
- elif side.connection < 0:
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}'.format(nSurfErr, tol)
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}'.format(s) for s in nSurf) + ']')) # noqa: E271
358
- print(hopout.warn('- Coordinates : [' + ' '.join('{:12.3f}'.format(s) for s in points[ nodes[ 0, 0]]) + ']')) # noqa: E271
359
- print(hopout.warn('- Coordinates : [' + ' '.join('{:12.3f}'.format(s) for s in points[ nodes[ 0, -1]]) + ']')) # noqa: E271
360
- print(hopout.warn('- Coordinates : [' + ' '.join('{:12.3f}'.format(s) for s in points[ nodes[-1, 0]]) + ']')) # noqa: E271
361
- print(hopout.warn('- Coordinates : [' + ' '.join('{:12.3f}'.format(s) for s in points[ nodes[-1, -1]]) + ']')) # noqa: E271
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}'.format(s) for s in nbnSurf) + ']')) # noqa: E271
365
- print(hopout.warn('- Coordinates : [' + ' '.join('{:12.3f}'.format(s) for s in points[nbnodes[ 0, 0]]) + ']')) # noqa: E271
366
- print(hopout.warn('- Coordinates : [' + ' '.join('{:12.3f}'.format(s) for s in points[nbnodes[ 0, -1]]) + ']')) # noqa: E271
367
- print(hopout.warn('- Coordinates : [' + ' '.join('{:12.3f}'.format(s) for s in points[nbnodes[-1, 0]]) + ']')) # noqa: E271
368
- print(hopout.warn('- Coordinates : [' + ' '.join('{:12.3f}'.format(s) for s in points[nbnodes[-1, -1]]) + ']')) # noqa: E271
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