PyHOPE 0.1.0__tar.gz → 0.8.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 (74) hide show
  1. pyhope-0.8.0/AUTHORS.md +24 -0
  2. {pyhope-0.1.0 → pyhope-0.8.0}/PKG-INFO +56 -18
  3. {pyhope-0.1.0 → pyhope-0.8.0}/README.md +50 -14
  4. {pyhope-0.1.0 → pyhope-0.8.0}/pyhope/__init__.py +7 -7
  5. {pyhope-0.1.0 → pyhope-0.8.0}/pyhope/basis/basis_basis.py +24 -4
  6. {pyhope-0.1.0 → pyhope-0.8.0}/pyhope/basis/basis_connect.py +9 -4
  7. {pyhope-0.1.0 → pyhope-0.8.0}/pyhope/basis/basis_jacobian.py +25 -8
  8. {pyhope-0.1.0 → pyhope-0.8.0}/pyhope/basis/basis_watertight.py +10 -9
  9. pyhope-0.8.0/pyhope/check/check.py +79 -0
  10. pyhope-0.8.0/pyhope/check/check_health.py +314 -0
  11. pyhope-0.8.0/pyhope/check/check_install.py +463 -0
  12. {pyhope-0.1.0 → pyhope-0.8.0}/pyhope/common/common.py +39 -39
  13. {pyhope-0.1.0 → pyhope-0.8.0}/pyhope/common/common_parallel.py +32 -12
  14. {pyhope-0.1.0 → pyhope-0.8.0}/pyhope/common/common_progress.py +7 -4
  15. {pyhope-0.1.0 → pyhope-0.8.0}/pyhope/common/common_tools.py +12 -1
  16. {pyhope-0.1.0 → pyhope-0.8.0}/pyhope/common/common_vars.py +40 -8
  17. {pyhope-0.1.0 → pyhope-0.8.0}/pyhope/gmsh/gmsh_install.py +3 -3
  18. {pyhope-0.1.0 → pyhope-0.8.0}/pyhope/io/io.py +128 -84
  19. pyhope-0.8.0/pyhope/io/io_debug.py +345 -0
  20. {pyhope-0.1.0 → pyhope-0.8.0}/pyhope/io/io_vars.py +13 -13
  21. pyhope-0.8.0/pyhope/io/io_xdmf.py +249 -0
  22. {pyhope-0.1.0 → pyhope-0.8.0}/pyhope/mesh/connect/connect.py +47 -34
  23. {pyhope-0.1.0 → pyhope-0.8.0}/pyhope/mesh/connect/connect_mortar.py +5 -2
  24. {pyhope-0.1.0 → pyhope-0.8.0}/pyhope/mesh/connect/connect_rbtree.py +9 -8
  25. pyhope-0.8.0/pyhope/mesh/fem/fem.py +450 -0
  26. {pyhope-0.1.0 → pyhope-0.8.0}/pyhope/mesh/mesh.py +15 -8
  27. {pyhope-0.1.0 → pyhope-0.8.0}/pyhope/mesh/mesh_builtin.py +56 -15
  28. {pyhope-0.1.0 → pyhope-0.8.0}/pyhope/mesh/mesh_common.py +9 -8
  29. pyhope-0.8.0/pyhope/mesh/mesh_duplicates.py +291 -0
  30. {pyhope-0.1.0 → pyhope-0.8.0}/pyhope/mesh/mesh_external.py +14 -5
  31. {pyhope-0.1.0 → pyhope-0.8.0}/pyhope/mesh/mesh_orient.py +8 -3
  32. {pyhope-0.1.0 → pyhope-0.8.0}/pyhope/mesh/mesh_sides.py +29 -2
  33. {pyhope-0.1.0 → pyhope-0.8.0}/pyhope/mesh/mesh_sort.py +228 -110
  34. {pyhope-0.1.0 → pyhope-0.8.0}/pyhope/mesh/mesh_vars.py +31 -10
  35. {pyhope-0.1.0 → pyhope-0.8.0}/pyhope/mesh/reader/reader_gambit.py +94 -36
  36. {pyhope-0.1.0 → pyhope-0.8.0}/pyhope/mesh/reader/reader_gmsh.py +107 -112
  37. {pyhope-0.1.0 → pyhope-0.8.0}/pyhope/mesh/reader/reader_hopr.py +57 -44
  38. {pyhope-0.1.0 → pyhope-0.8.0}/pyhope/mesh/topology/mesh_topology.py +13 -13
  39. {pyhope-0.1.0 → pyhope-0.8.0}/pyhope/mesh/transform/mesh_transform.py +6 -3
  40. {pyhope-0.1.0 → pyhope-0.8.0}/pyhope/meshio/meshio_ordering.py +44 -41
  41. {pyhope-0.1.0 → pyhope-0.8.0}/pyhope/output/output.py +47 -21
  42. pyhope-0.8.0/pyhope/readintools/__init__.py +0 -0
  43. {pyhope-0.1.0 → pyhope-0.8.0}/pyhope/readintools/commandline.py +32 -8
  44. {pyhope-0.1.0 → pyhope-0.8.0}/pyhope/readintools/readintools.py +118 -57
  45. {pyhope-0.1.0 → pyhope-0.8.0}/pyhope/script/pyhope_cli.py +15 -14
  46. pyhope-0.8.0/pyproject.toml +168 -0
  47. pyhope-0.1.0/pyhope/mesh/fem/fem.py +0 -324
  48. pyhope-0.1.0/pyhope/mesh/mesh_duplicates.py +0 -193
  49. pyhope-0.1.0/pyproject.toml +0 -89
  50. {pyhope-0.1.0 → pyhope-0.8.0}/LICENSE.md +0 -0
  51. {pyhope-0.1.0 → pyhope-0.8.0}/pyhope/__main__.py +0 -0
  52. {pyhope-0.1.0 → pyhope-0.8.0}/pyhope/basis/__init__.py +0 -0
  53. {pyhope-0.1.0/pyhope/common → pyhope-0.8.0/pyhope/check}/__init__.py +0 -0
  54. {pyhope-0.1.0/pyhope/config → pyhope-0.8.0/pyhope/common}/__init__.py +0 -0
  55. {pyhope-0.1.0/pyhope/io → pyhope-0.8.0/pyhope/config}/__init__.py +0 -0
  56. {pyhope-0.1.0 → pyhope-0.8.0}/pyhope/config/config.py +0 -0
  57. {pyhope-0.1.0 → pyhope-0.8.0}/pyhope/gmsh/gmsh_override.py +0 -0
  58. {pyhope-0.1.0/pyhope/mesh → pyhope-0.8.0/pyhope/io}/__init__.py +0 -0
  59. {pyhope-0.1.0 → pyhope-0.8.0}/pyhope/io/formats/cgns.py +0 -0
  60. {pyhope-0.1.0 → pyhope-0.8.0}/pyhope/io/formats/gmsh.py +0 -0
  61. {pyhope-0.1.0 → pyhope-0.8.0}/pyhope/io/formats/meshio.py +0 -0
  62. {pyhope-0.1.0 → pyhope-0.8.0}/pyhope/io/formats/vtk.py +0 -0
  63. {pyhope-0.1.0/pyhope/output → pyhope-0.8.0/pyhope/mesh}/__init__.py +0 -0
  64. {pyhope-0.1.0 → pyhope-0.8.0}/pyhope/mesh/elements/elements_ordering.py +0 -0
  65. {pyhope-0.1.0 → pyhope-0.8.0}/pyhope/mesh/elements/elements_shapefunctions.py +0 -0
  66. {pyhope-0.1.0 → pyhope-0.8.0}/pyhope/mesh/topology/mesh_serendipity.py +0 -0
  67. {pyhope-0.1.0 → pyhope-0.8.0}/pyhope/mesh/topology/mesh_splittohex.py +0 -0
  68. {pyhope-0.1.0 → pyhope-0.8.0}/pyhope/mesh/transform/templates/convtest.py +0 -0
  69. {pyhope-0.1.0 → pyhope-0.8.0}/pyhope/mesh/transform/templates/cylinder.py +0 -0
  70. {pyhope-0.1.0 → pyhope-0.8.0}/pyhope/mesh/transform/templates/default.py +0 -0
  71. {pyhope-0.1.0 → pyhope-0.8.0}/pyhope/mesh/transform/templates/phill.py +0 -0
  72. {pyhope-0.1.0 → pyhope-0.8.0}/pyhope/mesh/transform/templates/sphere.py +0 -0
  73. {pyhope-0.1.0 → pyhope-0.8.0}/pyhope/meshio/meshio_convert.py +0 -0
  74. {pyhope-0.1.0/pyhope/readintools → pyhope-0.8.0/pyhope/output}/__init__.py +0 -0
@@ -0,0 +1,24 @@
1
+ <div class="no-extra-css"></div>
2
+ # Authors
3
+
4
+ PyHOPE's development is coordinated by the Numerics Research Group (NRG) at
5
+ University of Stuttgart. The primary contact is Patrick Kopper, who is the *principal developer*
6
+ and main contributor. In addition, there are *contributors* who have
7
+ provided substantial additions or modifications. Together, these two groups form
8
+ authors as mentioned in the [LICENSE.md](LICENSE.md) file.
9
+
10
+ ## Principal Developers
11
+ * [Patrick Kopper](https://www.iag.uni-stuttgart.de/en/institute/team/Kopper-00001/),
12
+ University of Stuttgart, Germany
13
+
14
+ ## Contributors
15
+ The following people contributed major additions or modifications to PyHOPE and
16
+ are listed in alphabetical order:
17
+
18
+ * Daniel Appel
19
+ * Marcel Blind
20
+ * Stephen Copplestone
21
+ * Patrick Kopper
22
+ * Marius Kurz
23
+ * Felix Rodach
24
+ * Anna Schwarz
@@ -1,8 +1,10 @@
1
- Metadata-Version: 2.3
1
+ Metadata-Version: 2.4
2
2
  Name: PyHOPE
3
- Version: 0.1.0
3
+ Version: 0.8.0
4
4
  Summary: Python High-Order Preprocessing Environment
5
5
  License: GPL-3.0-only
6
+ License-File: AUTHORS.md
7
+ License-File: LICENSE.md
6
8
  Keywords: PyHOPE,mesh generator
7
9
  Author: Numerics Research Group (NRG)
8
10
  Author-email: numerics@iag.uni-stuttgart.de
@@ -24,17 +26,17 @@ Requires-Dist: numpy (>=2.0.0)
24
26
  Requires-Dist: packaging
25
27
  Requires-Dist: plotext
26
28
  Requires-Dist: scipy
27
- Requires-Dist: sortedcontainers
28
29
  Requires-Dist: typing-extensions
30
+ Project-URL: Documentation, https://hopr-framework.github.io/PyHOPE
29
31
  Project-URL: Homepage, https://numericsresearchgroup.org
30
- Project-URL: Repository, https://gitlab.iag.uni-stuttgart.de/flexi/codes/pyhope
32
+ Project-URL: Repository, https://github.com/hopr-framework/pyhope
31
33
  Description-Content-Type: text/markdown
32
34
 
33
35
  PyHOPE (Python High-Order Preprocessing Environment) is an open-source Python framework for the generation of three-dimensional unstructured high-order meshes. These meshes are needed by high-order numerical methods like Discontinuous Galerkin, Spectral Element Methods, or pFEM, in order to retain their accuracy if the computational domain includes curved boundaries.
34
36
 
35
37
  PyHOPE has been developed by the Numerics Research Group (NRG) lead by Prof. Andrea Beck at the Institute of Aerodynamics and Gas Dynamics at the University of Stuttgart, Germany.
36
38
 
37
- PyHOPE is heavily inspired by [HOPR (High Order Preprocessor)](https://github.com/hopr-framework/hopr) and shares the same input/output format. For more information and tutorials, please visit the [HOPR documentation](https://hopr.readthedocs.io). Furthermore, PyHOPE utilizes [Gmsh](https://gmsh.info) for the initial mesh generation and conversion before converting it to its internal representation.
39
+ PyHOPE is heavily inspired by [HOPR (High Order Preprocessor)](https://github.com/hopr-framework/hopr) and shares the same input/output format. Furthermore, PyHOPE utilizes [Gmsh](https://gmsh.info) for the initial mesh generation and conversion before converting it to its internal representation. The internal representation is loosely based on [meshio](https://github.com/nschloe/meshio) but augmented with additional information required for high-order meshes.
38
40
 
39
41
  This is a scientific project. If you use PyHOPE for publications or presentations in science, please support the project by citing our publications given at [numericsresearchgroup.org](https://numericsresearchgroup.org/publications.html).
40
42
 
@@ -49,22 +51,47 @@ PyHOPE is built using standard Python packages. You can install PyHOPE by follow
49
51
  ```
50
52
  If you choose not to use a virtual environment, skip this step and proceed directly to the installation of PyHOPE.
51
53
 
54
+ > [!IMPORTANT]
55
+ > For new shell sessions, the virtual environment must be re-sourced using `source venv/bin/activate` before using `pyhope` commands.
56
+
52
57
  2. **Install PyHOPE**
53
58
  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.
54
59
  ```bash
55
60
  python -m pip install pyhope
56
61
  ```
57
62
 
58
- 3. **Run PyHOPE**
59
- PyHOPE is available as a command-line tool. After installation, its functionalities can be accessed directly from the terminal.
60
- ```bash
61
- pyhope --help
62
- ```
63
+ # Testing
64
+ PyHOPE features internal health checks to verify that everything works as expected. The checks can be invoked directly from the terminal.
65
+ ```bash
66
+ pyhope --verify [tutorials] # Run all health checks
67
+ pyhope --verify-health # Run Python health checks
68
+ pyhope --verify-install [tutorials] # Run PyHOPE mesh generation checks
69
+ ```
70
+
71
+ > [!NOTE]
72
+ > By default, PyHOPE looks for the `tutorials` directory relative to the current working directory or the git root. If neither exists, PyHOPE downloads the tests from GitHub while using available authentication methods.
73
+
74
+ > [!IMPORTANT]
75
+ > Be aware that the PyHOPE repository uses [Git Large File Storage (LFS)](https://git-lfs.com) for some of its larger mesh files.
63
76
 
64
- > 🛈 Remark: For new shell sessions, the virtual environment must be re-sourced using `source venv/bin/activate` before using `pyhope` commands.
77
+ # Getting Help
78
+ PyHOPE help output is formatted to serve as self-hosting [INI format](https://hopr-framework.github.io/PyHOPE/user-guide/parameter-file). A list of all options and the default values can be accessed by running the following command.
79
+ ```bash
80
+ pyhope --help
81
+ ```
82
+
83
+ # Documentation
84
+ Refer to the [PyHOPE documentation](https://hopr-framework.github.io/PyHOPE) for the getting started guide, examples, and usage instructions. PyHOPE is heavily inspired by [HOPR (High Order Preprocessor)](https://github.com/hopr-framework/hopr) and shares the same input/output format. For technical details and the reference mesh format specification, see the [HOPR documentation](https://hopr.readthedocs.io).
65
85
 
66
86
  # Usage
67
- PyHOPE is invoked from the command line. Run parameters are read from a configuration file. The following output is obtained when running the example configuration file `tutorials/1-01-cartbox/parameter.ini`.
87
+ PyHOPE can either be invoked directly from the command line or used as a Python library.
88
+
89
+ ## Command Line Usage
90
+ PyHOPE can be invoked from the command line. After installation, its functionalities can be accessed directly from the terminal by passing a valid configuration file.
91
+ ```bash
92
+ pyhope [parameter.ini]
93
+ ```
94
+ The following output is obtained when running the example configuration file `tutorials/1-01-cartbox/parameter.ini`.
68
95
  ```
69
96
  $ pyhope tutorials/1-01-cartbox/parameter.ini
70
97
  ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
@@ -72,11 +99,12 @@ $ pyhope tutorials/1-01-cartbox/parameter.ini
72
99
  ┃ PyHOPE version x.x.x
73
100
  ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
74
101
  │ INIT PROGRAM...
75
- │ nThreads │ 10 │ DEFAULT │
102
+ │ nThreads │ nProcs-2 │ DEFAULT │
76
103
  ├─────────────────────────────────────────────
77
104
  │ INIT OUTPUT...
78
105
  │ ProjectName │ 1-01-cartbox │ *CUSTOM │
79
106
  │ OutputFormat │ 0 [HDF5] │ *CUSTOM │
107
+ │ DebugMesh │ T │ *CUSTOM │
80
108
  │ DebugVisu │ F │ *CUSTOM │
81
109
  ├─────────────────────────────────────────────
82
110
  │ INIT MESH...
@@ -87,7 +115,7 @@ $ pyhope tutorials/1-01-cartbox/parameter.ini
87
115
  ├────
88
116
  │ nZones │ 1 │ *CUSTOM │
89
117
  ├── Generating zone 1
90
- │ Corner │ (/0.,0.,0. ,,1.,0.,0. ,,1.,1... │ *CUSTOM │
118
+ │ Corner │ (/0.,0.,0.,,1.,0.,0.,,1.,1.,... │ *CUSTOM │
91
119
  │ nElems │ (/8,8,8/) │ *CUSTOM │
92
120
  │ ElemType │ 108 [hexahedron] │ *CUSTOM │
93
121
  │ StretchType │ (/0,0,0/) │ DEFAULT │
@@ -108,9 +136,9 @@ $ pyhope tutorials/1-01-cartbox/parameter.ini
108
136
  │ BoundaryName │ BC_zplus │ *CUSTOM │
109
137
  │ BoundaryType │ (/9,0,0,0/) │ *CUSTOM │
110
138
  ├────
111
- │ vv │ (/1., 0., 0./) │ *CUSTOM │
112
- │ vv │ (/0., 1., 0./) │ *CUSTOM │
113
- │ vv │ (/0., 0., 1./) │ *CUSTOM │
139
+ │ vv │ (/1.,0.,0./) │ *CUSTOM │
140
+ │ vv │ (/0.,1.,0./) │ *CUSTOM │
141
+ │ vv │ (/0.,0.,1./) │ *CUSTOM │
114
142
  ├────
115
143
  ├── Generated mesh with 512 cells
116
144
  ├─────────────────────────────────────────────
@@ -126,7 +154,7 @@ $ pyhope tutorials/1-01-cartbox/parameter.ini
126
154
  ├─────────────────────────────────────────────
127
155
  │ SORT MESH...
128
156
  ├────
129
- doSortIJKFalse │ DEFAULT │
157
+ MeshSorting1 [SFC] │ DEFAULT │
130
158
  ├────
131
159
  ├── Sorting elements along space-filling curve
132
160
  ├─────────────────────────────────────────────
@@ -177,8 +205,18 @@ $ pyhope tutorials/1-01-cartbox/parameter.ini
177
205
  │ Curved Hexahedra : 512
178
206
  ├────
179
207
  ├── Writing HDF5 mesh to "1-01-cartbox_mesh.h5"
208
+ ├── Writing XDMF mesh to "1-01-cartbox_DebugMesh.xdmf"
180
209
  ┢━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
181
210
  ┃ PyHOPE completed in [0.25 sec]
182
211
  ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
183
212
  ```
184
213
 
214
+ ## Python Library Usage
215
+ PyHOPE can be included in other Python libraries. PyHOPE exposes its functionally via runtime contexts defined by [Context Managers](https://docs.python.org/3/library/stdtypes.html#typecontextmanager). The following Python code loads a HOPR HDF5 mesh and derived quantities. For a complete list of currently implemented functions, see the [source code](https://github.com/hopr-framework/PyHOPE/blob/main/pyhope/__init__.py).
216
+ ```python
217
+ from pyhope import Basis, Mesh
218
+ with Mesh('1-01-cartbox_mesh.h5') as m:
219
+ elems = m.elems
220
+ lobatto_nodes = Basis.legendre_gauss_lobatto_nodes(order=m.nGeo)
221
+ ```
222
+
@@ -2,7 +2,7 @@ PyHOPE (Python High-Order Preprocessing Environment) is an open-source Python fr
2
2
 
3
3
  PyHOPE has been developed by the Numerics Research Group (NRG) lead by Prof. Andrea Beck at the Institute of Aerodynamics and Gas Dynamics at the University of Stuttgart, Germany.
4
4
 
5
- PyHOPE is heavily inspired by [HOPR (High Order Preprocessor)](https://github.com/hopr-framework/hopr) and shares the same input/output format. For more information and tutorials, please visit the [HOPR documentation](https://hopr.readthedocs.io). Furthermore, PyHOPE utilizes [Gmsh](https://gmsh.info) for the initial mesh generation and conversion before converting it to its internal representation.
5
+ PyHOPE is heavily inspired by [HOPR (High Order Preprocessor)](https://github.com/hopr-framework/hopr) and shares the same input/output format. Furthermore, PyHOPE utilizes [Gmsh](https://gmsh.info) for the initial mesh generation and conversion before converting it to its internal representation. The internal representation is loosely based on [meshio](https://github.com/nschloe/meshio) but augmented with additional information required for high-order meshes.
6
6
 
7
7
  This is a scientific project. If you use PyHOPE for publications or presentations in science, please support the project by citing our publications given at [numericsresearchgroup.org](https://numericsresearchgroup.org/publications.html).
8
8
 
@@ -17,22 +17,47 @@ PyHOPE is built using standard Python packages. You can install PyHOPE by follow
17
17
  ```
18
18
  If you choose not to use a virtual environment, skip this step and proceed directly to the installation of PyHOPE.
19
19
 
20
+ > [!IMPORTANT]
21
+ > For new shell sessions, the virtual environment must be re-sourced using `source venv/bin/activate` before using `pyhope` commands.
22
+
20
23
  2. **Install PyHOPE**
21
24
  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.
22
25
  ```bash
23
26
  python -m pip install pyhope
24
27
  ```
25
28
 
26
- 3. **Run PyHOPE**
27
- PyHOPE is available as a command-line tool. After installation, its functionalities can be accessed directly from the terminal.
28
- ```bash
29
- pyhope --help
30
- ```
29
+ # Testing
30
+ PyHOPE features internal health checks to verify that everything works as expected. The checks can be invoked directly from the terminal.
31
+ ```bash
32
+ pyhope --verify [tutorials] # Run all health checks
33
+ pyhope --verify-health # Run Python health checks
34
+ pyhope --verify-install [tutorials] # Run PyHOPE mesh generation checks
35
+ ```
36
+
37
+ > [!NOTE]
38
+ > By default, PyHOPE looks for the `tutorials` directory relative to the current working directory or the git root. If neither exists, PyHOPE downloads the tests from GitHub while using available authentication methods.
39
+
40
+ > [!IMPORTANT]
41
+ > Be aware that the PyHOPE repository uses [Git Large File Storage (LFS)](https://git-lfs.com) for some of its larger mesh files.
31
42
 
32
- > 🛈 Remark: For new shell sessions, the virtual environment must be re-sourced using `source venv/bin/activate` before using `pyhope` commands.
43
+ # Getting Help
44
+ PyHOPE help output is formatted to serve as self-hosting [INI format](https://hopr-framework.github.io/PyHOPE/user-guide/parameter-file). A list of all options and the default values can be accessed by running the following command.
45
+ ```bash
46
+ pyhope --help
47
+ ```
48
+
49
+ # Documentation
50
+ Refer to the [PyHOPE documentation](https://hopr-framework.github.io/PyHOPE) for the getting started guide, examples, and usage instructions. PyHOPE is heavily inspired by [HOPR (High Order Preprocessor)](https://github.com/hopr-framework/hopr) and shares the same input/output format. For technical details and the reference mesh format specification, see the [HOPR documentation](https://hopr.readthedocs.io).
33
51
 
34
52
  # Usage
35
- PyHOPE is invoked from the command line. Run parameters are read from a configuration file. The following output is obtained when running the example configuration file `tutorials/1-01-cartbox/parameter.ini`.
53
+ PyHOPE can either be invoked directly from the command line or used as a Python library.
54
+
55
+ ## Command Line Usage
56
+ PyHOPE can be invoked from the command line. After installation, its functionalities can be accessed directly from the terminal by passing a valid configuration file.
57
+ ```bash
58
+ pyhope [parameter.ini]
59
+ ```
60
+ The following output is obtained when running the example configuration file `tutorials/1-01-cartbox/parameter.ini`.
36
61
  ```
37
62
  $ pyhope tutorials/1-01-cartbox/parameter.ini
38
63
  ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
@@ -40,11 +65,12 @@ $ pyhope tutorials/1-01-cartbox/parameter.ini
40
65
  ┃ PyHOPE version x.x.x
41
66
  ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
42
67
  │ INIT PROGRAM...
43
- │ nThreads │ 10 │ DEFAULT │
68
+ │ nThreads │ nProcs-2 │ DEFAULT │
44
69
  ├─────────────────────────────────────────────
45
70
  │ INIT OUTPUT...
46
71
  │ ProjectName │ 1-01-cartbox │ *CUSTOM │
47
72
  │ OutputFormat │ 0 [HDF5] │ *CUSTOM │
73
+ │ DebugMesh │ T │ *CUSTOM │
48
74
  │ DebugVisu │ F │ *CUSTOM │
49
75
  ├─────────────────────────────────────────────
50
76
  │ INIT MESH...
@@ -55,7 +81,7 @@ $ pyhope tutorials/1-01-cartbox/parameter.ini
55
81
  ├────
56
82
  │ nZones │ 1 │ *CUSTOM │
57
83
  ├── Generating zone 1
58
- │ Corner │ (/0.,0.,0. ,,1.,0.,0. ,,1.,1... │ *CUSTOM │
84
+ │ Corner │ (/0.,0.,0.,,1.,0.,0.,,1.,1.,... │ *CUSTOM │
59
85
  │ nElems │ (/8,8,8/) │ *CUSTOM │
60
86
  │ ElemType │ 108 [hexahedron] │ *CUSTOM │
61
87
  │ StretchType │ (/0,0,0/) │ DEFAULT │
@@ -76,9 +102,9 @@ $ pyhope tutorials/1-01-cartbox/parameter.ini
76
102
  │ BoundaryName │ BC_zplus │ *CUSTOM │
77
103
  │ BoundaryType │ (/9,0,0,0/) │ *CUSTOM │
78
104
  ├────
79
- │ vv │ (/1., 0., 0./) │ *CUSTOM │
80
- │ vv │ (/0., 1., 0./) │ *CUSTOM │
81
- │ vv │ (/0., 0., 1./) │ *CUSTOM │
105
+ │ vv │ (/1.,0.,0./) │ *CUSTOM │
106
+ │ vv │ (/0.,1.,0./) │ *CUSTOM │
107
+ │ vv │ (/0.,0.,1./) │ *CUSTOM │
82
108
  ├────
83
109
  ├── Generated mesh with 512 cells
84
110
  ├─────────────────────────────────────────────
@@ -94,7 +120,7 @@ $ pyhope tutorials/1-01-cartbox/parameter.ini
94
120
  ├─────────────────────────────────────────────
95
121
  │ SORT MESH...
96
122
  ├────
97
- doSortIJKFalse │ DEFAULT │
123
+ MeshSorting1 [SFC] │ DEFAULT │
98
124
  ├────
99
125
  ├── Sorting elements along space-filling curve
100
126
  ├─────────────────────────────────────────────
@@ -145,7 +171,17 @@ $ pyhope tutorials/1-01-cartbox/parameter.ini
145
171
  │ Curved Hexahedra : 512
146
172
  ├────
147
173
  ├── Writing HDF5 mesh to "1-01-cartbox_mesh.h5"
174
+ ├── Writing XDMF mesh to "1-01-cartbox_DebugMesh.xdmf"
148
175
  ┢━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
149
176
  ┃ PyHOPE completed in [0.25 sec]
150
177
  ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
151
178
  ```
179
+
180
+ ## Python Library Usage
181
+ PyHOPE can be included in other Python libraries. PyHOPE exposes its functionally via runtime contexts defined by [Context Managers](https://docs.python.org/3/library/stdtypes.html#typecontextmanager). The following Python code loads a HOPR HDF5 mesh and derived quantities. For a complete list of currently implemented functions, see the [source code](https://github.com/hopr-framework/PyHOPE/blob/main/pyhope/__init__.py).
182
+ ```python
183
+ from pyhope import Basis, Mesh
184
+ with Mesh('1-01-cartbox_mesh.h5') as m:
185
+ elems = m.elems
186
+ lobatto_nodes = Basis.legendre_gauss_lobatto_nodes(order=m.nGeo)
187
+ ```
@@ -66,8 +66,8 @@ MeshContainer = namedtuple('Mesh',
66
66
  ])
67
67
 
68
68
 
69
- @contextmanager
70
- def Mesh(*args, stdout=False, stderr=True):
69
+ @contextmanager # pragma: no cover
70
+ def Mesh(*args: str, stdout: bool = False, stderr: bool = True):
71
71
  """ Mesh context manager to generate a mesh from a given file
72
72
 
73
73
  Args:
@@ -156,8 +156,8 @@ def Mesh(*args, stdout=False, stderr=True):
156
156
 
157
157
  finally:
158
158
  # Cleanup resources after exiting the context
159
- mesh_vars.mesh = None
160
- mesh_vars.nGeo = None
161
- mesh_vars.bcs = None
162
- mesh_vars.elems = None
163
- mesh_vars.sides = None
159
+ del mesh_vars.mesh
160
+ del mesh_vars.nGeo
161
+ del mesh_vars.bcs
162
+ del mesh_vars.elems
163
+ del mesh_vars.sides
@@ -25,6 +25,7 @@
25
25
  # ----------------------------------------------------------------------------------------------------------------------------------
26
26
  # Standard libraries
27
27
  # ----------------------------------------------------------------------------------------------------------------------------------
28
+ from typing import Union
28
29
  # ----------------------------------------------------------------------------------------------------------------------------------
29
30
  # Third-party libraries
30
31
  # ----------------------------------------------------------------------------------------------------------------------------------
@@ -111,7 +112,7 @@ def polynomial_derivative_matrix(order: int, xGP: np.ndarray) -> np.ndarray:
111
112
  return D
112
113
 
113
114
 
114
- def lagrange_interpolation_polys(x: float, order: int, xGP: np.ndarray, wBary: np.ndarray) -> np.ndarray:
115
+ def lagrange_interpolation_polys(x: Union[float, np.ndarray], order: int, xGP: np.ndarray, wBary: np.ndarray) -> np.ndarray:
115
116
  """ Computes all Lagrange functions evaluated at position x in [-1;1]
116
117
  > Algorithm 34, Kopriva
117
118
  """
@@ -147,10 +148,10 @@ def change_basis_3D(Vdm: np.ndarray, x3D_In: np.ndarray) -> np.ndarray:
147
148
  """ Interpolate a 3D tensor product Lagrange basis defined by (N_in+1) 1D interpolation point positions xi_In(0:N_In)
148
149
  to another 3D tensor product node positions (number of nodes N_out+1)
149
150
  defined by (N_out+1) interpolation point positions xi_Out(0:N_Out)
150
- xi is defined in the 1DrefElem xi=[-1,1]
151
+ xi is defined in the 1D reference element xi=[-1,1]
151
152
  """
152
153
  # First contraction along the iN_In axis (axis 1 of Vdm, axis 1 of x3D_In)
153
- x3D_Buf1 = np.tensordot(Vdm, x3D_In, axes=(1, 1))
154
+ x3D_Buf1 = np.tensordot(Vdm, x3D_In , axes=(1, 1))
154
155
  x3D_Buf1 = np.moveaxis(x3D_Buf1, 0, 1) # Correct the shape to (dim1, n_Out, n_In, n_In)
155
156
 
156
157
  # Second contraction along the jN_In axis (axis 1 of Vdm, axis 2 of x3D_Buf1)
@@ -160,6 +161,8 @@ def change_basis_3D(Vdm: np.ndarray, x3D_In: np.ndarray) -> np.ndarray:
160
161
  # Third contraction along the kN_In axis (axis 1 of Vdm, axis 3 of x3D_Buf2)
161
162
  x3D_Out = np.tensordot(Vdm, x3D_Buf2, axes=(1, 3))
162
163
  x3D_Out = np.moveaxis(x3D_Out , 0, 3) # Correct the shape to (dim1, n_Out, n_Out, n_Out)
164
+ # PERF: This is actually slower than the individual contractions
165
+ # x3D_Out = np.einsum('pi,qj,rk,dijk->dpqr', Vdm, Vdm, Vdm, x3D_In, optimize=True)
163
166
 
164
167
  return x3D_Out
165
168
 
@@ -177,22 +180,32 @@ def change_basis_2D(Vdm: np.ndarray, x2D_In: np.ndarray) -> np.ndarray:
177
180
  # Second contraction along the jN_In axis (axis 1 of Vdm, axis 2 of x2D_Buf1)
178
181
  x2D_Out = np.tensordot(Vdm, x2D_Buf1, axes=(1, 2))
179
182
  x2D_Out = np.moveaxis(x2D_Out, 0, 2) # Correct the shape to (dim1, n_Out, n_Out, n_In)
183
+ # PERF: This is actually slower than the individual contractions
184
+ # x2D_Out = np.einsum('pi,qj,dij->dpq', Vdm, Vdm, x2D_In, optimize=True)
180
185
 
181
186
  return x2D_Out
182
187
 
183
188
 
184
189
  def evaluate_jacobian(xGeo_In: np.ndarray, VdmGLtoAP: np.ndarray, D_EqToGL: np.ndarray) -> np.ndarray:
190
+ """ Calculate the Jacobian of the mapping for a given element
191
+ """
185
192
  # Perform tensor contraction for the first derivative (Xi direction)
186
193
  dXdXiGL = np.tensordot(D_EqToGL, xGeo_In, axes=(1, 1))
187
194
  dXdXiGL = np.moveaxis(dXdXiGL , 1, 0) # Correct the shape to (3, nGeoRef, nGeoRef, nGeoRef)
195
+ # PERF: This is actually slower than the individual contractions
196
+ # dXdXiGL = np.einsum('pi,dijk->dpjk', D_EqToGL, xGeo_In, optimize=True)
188
197
 
189
198
  # Perform tensor contraction for the second derivative (Eta direction)
190
199
  dXdEtaGL = np.tensordot(D_EqToGL, xGeo_In, axes=(1, 2))
191
200
  dXdEtaGL = np.moveaxis(dXdEtaGL , 1, 0) # Correct the shape to (3, nGeoRef, nGeoRef, nGeoRef)
201
+ # PERF: This is actually slower than the individual contractions
202
+ # dXdEtaGL = np.einsum('qj,dijk->dqik', D_EqToGL, xGeo_In, optimize=True)
192
203
 
193
204
  # Perform tensor contraction for the third derivative (Zeta direction)
194
205
  dXdZetaGL = np.tensordot(D_EqToGL, xGeo_In, axes=(1, 3))
195
206
  dXdZetaGL = np.moveaxis(dXdZetaGL, 1, 0) # Correct the shape to (3, nGeoRef, nGeoRef, nGeoRef)
207
+ # PERF: This is actually slower than the individual contractions
208
+ # dXdZetaGL = np.einsum('rk,dijk->drij', D_EqToGL, xGeo_In, optimize=True)
196
209
 
197
210
  # Change basis for each direction
198
211
  dXdXiAP = change_basis_3D(VdmGLtoAP, dXdXiGL )
@@ -200,10 +213,17 @@ def evaluate_jacobian(xGeo_In: np.ndarray, VdmGLtoAP: np.ndarray, D_EqToGL: np.n
200
213
  dXdZetaAP = change_basis_3D(VdmGLtoAP, dXdZetaGL)
201
214
 
202
215
  # Precompute cross products between dXdEtaAP and dXdZetaAP for all points
203
- cross_eta_zeta = np.cross(dXdEtaAP, dXdZetaAP, axis=0) # Shape: (3, nGeoRef, nGeoRef, nGeoRef)
216
+ # cross_eta_zeta = np.cross(dXdEtaAP, dXdZetaAP, axis=0) # Shape: (3, nGeoRef, nGeoRef, nGeoRef)
217
+ # > Manually compute cross product
218
+ cross_eta_zeta = np.empty_like(dXdEtaAP)
219
+ cross_eta_zeta[0] = dXdEtaAP[1] * dXdZetaAP[2] - dXdEtaAP[2] * dXdZetaAP[1]
220
+ cross_eta_zeta[1] = dXdEtaAP[2] * dXdZetaAP[0] - dXdEtaAP[0] * dXdZetaAP[2]
221
+ cross_eta_zeta[2] = dXdEtaAP[0] * dXdZetaAP[1] - dXdEtaAP[1] * dXdZetaAP[0]
204
222
 
205
223
  # Fill output Jacobian array
206
224
  jacOut = np.einsum('ijkl,ijkl->jkl', dXdXiAP, cross_eta_zeta)
225
+ # PERF: This is actually slower than the individual contractions
226
+ # jacOut = np.sum(dXdXiAP * cross_eta_zeta, axis=0)
207
227
 
208
228
  return jacOut
209
229
 
@@ -27,12 +27,17 @@
27
27
  # ----------------------------------------------------------------------------------------------------------------------------------
28
28
  import re
29
29
  import sys
30
- from typing import Final
30
+ from typing import Final, cast
31
31
  # ----------------------------------------------------------------------------------------------------------------------------------
32
32
  # Third-party libraries
33
33
  # ----------------------------------------------------------------------------------------------------------------------------------
34
34
  import numpy as np
35
- import numpy.typing as npt
35
+ # ----------------------------------------------------------------------------------------------------------------------------------
36
+ # Typing libraries
37
+ # ----------------------------------------------------------------------------------------------------------------------------------
38
+ import typing
39
+ if typing.TYPE_CHECKING:
40
+ import numpy.typing as npt
36
41
  # ----------------------------------------------------------------------------------------------------------------------------------
37
42
  # Local imports
38
43
  # ----------------------------------------------------------------------------------------------------------------------------------
@@ -43,7 +48,7 @@ from pyhope.mesh.mesh_common import face_to_nodes
43
48
 
44
49
 
45
50
  def check_sides(elem,
46
- ) -> list[bool | int | np.ndarray]:
51
+ ) -> list[bool | int | np.ndarray]:
47
52
  results = []
48
53
  elems: Final[list] = mesh_vars.elems
49
54
  sides: Final[list] = mesh_vars.sides
@@ -130,7 +135,7 @@ def CheckConnect() -> None:
130
135
  elems: Final[list] = mesh_vars.elems
131
136
 
132
137
  # Only consider hexahedrons
133
- if any(e.type % 100 != 8 for e in elems):
138
+ if any(cast(int, e.type) % 100 != 8 for e in elems):
134
139
  elemTypes = list(set([e.type for e in elems if e.type % 100 != 8]))
135
140
  print(hopout.warn('Ignored element type: {}'.format(
136
141
  [re.sub(r"\d+$", "", mesh_vars.ELEMTYPE.inam[e][0]) for e in elemTypes]
@@ -142,8 +142,18 @@ def CheckJacobians() -> None:
142
142
  # Prepare elements for parallel processing
143
143
  tasks = []
144
144
 
145
- # Cache the mapping
146
- linCache = {}
145
+ # Pre-compute LINTEN mappings for all element types
146
+ linCache = {}
147
+ elemOrder = 100 if mesh_vars.nGeo == 1 else 200
148
+ elemTypes = tuple([s + elemOrder for s in (4, 5, 6, 8)])
149
+ for elemType in elemTypes:
150
+ try:
151
+ _, mapLin = LINTEN(elemType, order=mesh_vars.nGeo)
152
+ mapLin = np.array(tuple(mapLin[np.int64(i)] for i in range(len(mapLin))))
153
+ linCache[elemType] = mapLin
154
+ # Only hexahedrons supported for specific nGeo
155
+ except ValueError:
156
+ pass
147
157
 
148
158
  for elem in elems:
149
159
  elemType = elem.type
@@ -153,12 +163,7 @@ def CheckJacobians() -> None:
153
163
  continue
154
164
 
155
165
  # Get the mapping
156
- if elemType in linCache:
157
- mapLin = linCache[elemType]
158
- else:
159
- _, mapLin = LINTEN(elemType, order=mesh_vars.nGeo)
160
- mapLin = np.array(tuple(mapLin[np.int64(i)] for i in range(len(mapLin))))
161
- linCache[elemType] = mapLin
166
+ mapLin = linCache[elemType]
162
167
 
163
168
  # Fill the NodeCoords
164
169
  nodeCoords = np.empty((nGeo ** 3, 3), dtype=np.float64)
@@ -197,3 +202,15 @@ def CheckJacobians() -> None:
197
202
  # Plot the histogram of the Jacobians
198
203
  if len(jacs) > 0:
199
204
  plot_histogram(np.array(jacs))
205
+
206
+ # Append the Jacobians to the elements
207
+ jacIter = iter(jacs)
208
+ for elem in elems:
209
+ elemType = elem.type
210
+
211
+ # Only consider hexahedrons
212
+ if int(elemType) % 100 != 8:
213
+ elem.jacobian = 1.
214
+ continue
215
+
216
+ elem.jacobian = next(jacIter)
@@ -57,7 +57,12 @@ def eval_nsurf(XGeo: np.ndarray, Vdm: np.ndarray, DGP: np.ndarray, weights: np.n
57
57
 
58
58
  # Compute the cross product at each Gauss point
59
59
  VDMSize = Vdm.shape[-1]
60
- nVec = np.cross(dXdxiGP, dXdetaGP, axis=0) # Shape: (3, N_GP*N_GP)
60
+ # nVec = np.cross(dXdxiGP, dXdetaGP, axis=0) # Shape: (3, N_GP*N_GP)
61
+ # > Manually compute cross product
62
+ nVec = np.empty_like(dXdxiGP)
63
+ nVec[0] = dXdxiGP[1] * dXdetaGP[2] - dXdxiGP[2] * dXdetaGP[1]
64
+ nVec[1] = dXdxiGP[2] * dXdetaGP[0] - dXdxiGP[0] * dXdetaGP[2]
65
+ nVec[2] = dXdxiGP[0] * dXdetaGP[1] - dXdxiGP[1] * dXdetaGP[0]
61
66
  nVec = nVec.reshape(3, VDMSize, VDMSize) # Reshape to (3, N_GP+1, N_GP+1)
62
67
 
63
68
  # Compute the weighted normals
@@ -86,10 +91,6 @@ def check_sides(elem,
86
91
  elemType = elem.type
87
92
 
88
93
  for SideID in elem.sides:
89
- # TODO: THIS IS CURRENTLY IGNORED, MEANING WE CHECK EVERY CONNECTION DOUBLE
90
- # if checked[SideID]:
91
- # continue
92
-
93
94
  side = sides[SideID]
94
95
 
95
96
  # Only connected sides and not small mortar sides
@@ -109,7 +110,6 @@ def check_sides(elem,
109
110
  # Calculate the L2 norm of the side and take the maximum
110
111
  sideTol = np.linalg.norm(nSurf, ord=2)
111
112
  tol = np.max((elemTol, sideTol)) * mesh_vars.tolInternal
112
- # checked[SideID] = True
113
113
 
114
114
  # Mortar sides are the following virtual sides
115
115
  nMortar = 4 if mortarType == 1 else 2
@@ -122,7 +122,6 @@ def check_sides(elem,
122
122
  nbelem = elems[nbside.elemID]
123
123
  idx = nbelem.nodes[face_to_nodes(nbside.face, nbelem.type, nGeo)]
124
124
  nnbSurf += eval_nsurf(np.transpose(points[idx], axes=(2, 0, 1)), VdmEqToGP, DGP, weights)
125
- # checked[nbside] = True
126
125
 
127
126
  # Check if side normals are within tolerance
128
127
  nSurfErr = np.sum(np.abs(nnbSurf + nSurf))
@@ -130,6 +129,10 @@ def check_sides(elem,
130
129
 
131
130
  # Internal side
132
131
  elif side.connection >= 0:
132
+ # Only process the side with the smaller ID
133
+ if SideID > side.connection:
134
+ continue
135
+
133
136
  # Ignore the virtual mortar sides
134
137
  if side.locMortar is not None:
135
138
  continue
@@ -142,7 +145,6 @@ def check_sides(elem,
142
145
  # Calculate the L2 norm of the side and take the maximum
143
146
  sideTol = np.linalg.norm(nSurf, ord=2)
144
147
  tol = np.max((elemTol, sideTol)) * mesh_vars.tolInternal
145
- # checked[SideID] = True
146
148
 
147
149
  # Connected side
148
150
  nbside = sides[side.connection]
@@ -151,7 +153,6 @@ def check_sides(elem,
151
153
  # nnbSurf = eval_nsurf(np.moveaxis(points[nbnodes], 2, 0), VdmEqToGP, DGP, weights)
152
154
  idx = nbelem.nodes[face_to_nodes(nbside.face, nbelem.type, nGeo)]
153
155
  nnbSurf = eval_nsurf(np.transpose(points[idx]), VdmEqToGP, DGP, weights)
154
- # checked[nbside] = True
155
156
 
156
157
  # Check if side normals are within tolerance
157
158
  nSurfErr = np.sum(np.abs(nnbSurf + nSurf))