mmgpy 0.5.0__cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl

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 (109) hide show
  1. mmgpy/__init__.py +296 -0
  2. mmgpy/__main__.py +13 -0
  3. mmgpy/_io.py +535 -0
  4. mmgpy/_logging.py +290 -0
  5. mmgpy/_mesh.py +2286 -0
  6. mmgpy/_mmgpy.cpython-311-x86_64-linux-gnu.so +0 -0
  7. mmgpy/_mmgpy.pyi +2140 -0
  8. mmgpy/_options.py +304 -0
  9. mmgpy/_progress.py +850 -0
  10. mmgpy/_pyvista.py +410 -0
  11. mmgpy/_result.py +143 -0
  12. mmgpy/_transfer.py +273 -0
  13. mmgpy/_validation.py +669 -0
  14. mmgpy/_version.py +3 -0
  15. mmgpy/_version.py.in +3 -0
  16. mmgpy/bin/mmg2d_O3 +0 -0
  17. mmgpy/bin/mmg3d_O3 +0 -0
  18. mmgpy/bin/mmgs_O3 +0 -0
  19. mmgpy/interactive/__init__.py +24 -0
  20. mmgpy/interactive/sizing_editor.py +790 -0
  21. mmgpy/lagrangian.py +394 -0
  22. mmgpy/lib/libmmg2d.so +0 -0
  23. mmgpy/lib/libmmg2d.so.5 +0 -0
  24. mmgpy/lib/libmmg2d.so.5.8.0 +0 -0
  25. mmgpy/lib/libmmg3d.so +0 -0
  26. mmgpy/lib/libmmg3d.so.5 +0 -0
  27. mmgpy/lib/libmmg3d.so.5.8.0 +0 -0
  28. mmgpy/lib/libmmgs.so +0 -0
  29. mmgpy/lib/libmmgs.so.5 +0 -0
  30. mmgpy/lib/libmmgs.so.5.8.0 +0 -0
  31. mmgpy/lib/libvtkCommonColor-9.5.so.1 +0 -0
  32. mmgpy/lib/libvtkCommonComputationalGeometry-9.5.so.1 +0 -0
  33. mmgpy/lib/libvtkCommonCore-9.5.so.1 +0 -0
  34. mmgpy/lib/libvtkCommonDataModel-9.5.so.1 +0 -0
  35. mmgpy/lib/libvtkCommonExecutionModel-9.5.so.1 +0 -0
  36. mmgpy/lib/libvtkCommonMath-9.5.so.1 +0 -0
  37. mmgpy/lib/libvtkCommonMisc-9.5.so.1 +0 -0
  38. mmgpy/lib/libvtkCommonSystem-9.5.so.1 +0 -0
  39. mmgpy/lib/libvtkCommonTransforms-9.5.so.1 +0 -0
  40. mmgpy/lib/libvtkDICOMParser-9.5.so.1 +0 -0
  41. mmgpy/lib/libvtkFiltersCellGrid-9.5.so.1 +0 -0
  42. mmgpy/lib/libvtkFiltersCore-9.5.so.1 +0 -0
  43. mmgpy/lib/libvtkFiltersExtraction-9.5.so.1 +0 -0
  44. mmgpy/lib/libvtkFiltersGeneral-9.5.so.1 +0 -0
  45. mmgpy/lib/libvtkFiltersGeometry-9.5.so.1 +0 -0
  46. mmgpy/lib/libvtkFiltersHybrid-9.5.so.1 +0 -0
  47. mmgpy/lib/libvtkFiltersHyperTree-9.5.so.1 +0 -0
  48. mmgpy/lib/libvtkFiltersModeling-9.5.so.1 +0 -0
  49. mmgpy/lib/libvtkFiltersParallel-9.5.so.1 +0 -0
  50. mmgpy/lib/libvtkFiltersReduction-9.5.so.1 +0 -0
  51. mmgpy/lib/libvtkFiltersSources-9.5.so.1 +0 -0
  52. mmgpy/lib/libvtkFiltersStatistics-9.5.so.1 +0 -0
  53. mmgpy/lib/libvtkFiltersTexture-9.5.so.1 +0 -0
  54. mmgpy/lib/libvtkFiltersVerdict-9.5.so.1 +0 -0
  55. mmgpy/lib/libvtkIOCellGrid-9.5.so.1 +0 -0
  56. mmgpy/lib/libvtkIOCore-9.5.so.1 +0 -0
  57. mmgpy/lib/libvtkIOGeometry-9.5.so.1 +0 -0
  58. mmgpy/lib/libvtkIOImage-9.5.so.1 +0 -0
  59. mmgpy/lib/libvtkIOLegacy-9.5.so.1 +0 -0
  60. mmgpy/lib/libvtkIOParallel-9.5.so.1 +0 -0
  61. mmgpy/lib/libvtkIOParallelXML-9.5.so.1 +0 -0
  62. mmgpy/lib/libvtkIOXML-9.5.so.1 +0 -0
  63. mmgpy/lib/libvtkIOXMLParser-9.5.so.1 +0 -0
  64. mmgpy/lib/libvtkImagingCore-9.5.so.1 +0 -0
  65. mmgpy/lib/libvtkImagingSources-9.5.so.1 +0 -0
  66. mmgpy/lib/libvtkParallelCore-9.5.so.1 +0 -0
  67. mmgpy/lib/libvtkParallelDIY-9.5.so.1 +0 -0
  68. mmgpy/lib/libvtkRenderingCore-9.5.so.1 +0 -0
  69. mmgpy/lib/libvtkdoubleconversion-9.5.so.1 +0 -0
  70. mmgpy/lib/libvtkexpat-9.5.so.1 +0 -0
  71. mmgpy/lib/libvtkfmt-9.5.so.1 +0 -0
  72. mmgpy/lib/libvtkjpeg-9.5.so.1 +0 -0
  73. mmgpy/lib/libvtkjsoncpp-9.5.so.1 +0 -0
  74. mmgpy/lib/libvtkkissfft-9.5.so.1 +0 -0
  75. mmgpy/lib/libvtkloguru-9.5.so.1 +0 -0
  76. mmgpy/lib/libvtklz4-9.5.so.1 +0 -0
  77. mmgpy/lib/libvtklzma-9.5.so.1 +0 -0
  78. mmgpy/lib/libvtkmetaio-9.5.so.1 +0 -0
  79. mmgpy/lib/libvtkpng-9.5.so.1 +0 -0
  80. mmgpy/lib/libvtkpugixml-9.5.so.1 +0 -0
  81. mmgpy/lib/libvtksys-9.5.so.1 +0 -0
  82. mmgpy/lib/libvtktiff-9.5.so.1 +0 -0
  83. mmgpy/lib/libvtktoken-9.5.so.1 +0 -0
  84. mmgpy/lib/libvtkverdict-9.5.so.1 +0 -0
  85. mmgpy/lib/libvtkzlib-9.5.so.1 +0 -0
  86. mmgpy/metrics.py +596 -0
  87. mmgpy/progress.py +69 -0
  88. mmgpy/py.typed +0 -0
  89. mmgpy/repair/__init__.py +37 -0
  90. mmgpy/repair/_core.py +226 -0
  91. mmgpy/repair/_elements.py +241 -0
  92. mmgpy/repair/_vertices.py +219 -0
  93. mmgpy/sizing.py +370 -0
  94. mmgpy/ui/__init__.py +97 -0
  95. mmgpy/ui/__main__.py +87 -0
  96. mmgpy/ui/app.py +1837 -0
  97. mmgpy/ui/parsers.py +501 -0
  98. mmgpy/ui/remeshing.py +448 -0
  99. mmgpy/ui/samples.py +249 -0
  100. mmgpy/ui/utils.py +280 -0
  101. mmgpy/ui/viewer.py +587 -0
  102. mmgpy-0.5.0.dist-info/METADATA +186 -0
  103. mmgpy-0.5.0.dist-info/RECORD +109 -0
  104. mmgpy-0.5.0.dist-info/WHEEL +6 -0
  105. mmgpy-0.5.0.dist-info/entry_points.txt +13 -0
  106. mmgpy-0.5.0.dist-info/licenses/LICENSE +38 -0
  107. share/man/man1/mmg2d.1.gz +0 -0
  108. share/man/man1/mmg3d.1.gz +0 -0
  109. share/man/man1/mmgs.1.gz +0 -0
mmgpy/_transfer.py ADDED
@@ -0,0 +1,273 @@
1
+ """Field transfer utilities for mesh-to-mesh interpolation.
2
+
3
+ This module provides functions for transferring solution fields (scalars, vectors,
4
+ tensors) from one mesh to another via interpolation. This is essential for adaptive
5
+ simulation workflows where solutions must be preserved across remeshing operations.
6
+
7
+ Example:
8
+ >>> from mmgpy import Mesh
9
+ >>> from mmgpy._transfer import transfer_fields
10
+ >>>
11
+ >>> old_mesh = Mesh(old_vertices, old_cells)
12
+ >>> new_mesh = Mesh(new_vertices, new_cells)
13
+ >>>
14
+ >>> # Transfer temperature field
15
+ >>> new_temperature = transfer_fields(
16
+ ... source_vertices=old_mesh.get_vertices(),
17
+ ... source_elements=old_mesh.get_tetrahedra(),
18
+ ... target_points=new_mesh.get_vertices(),
19
+ ... fields={"temperature": temperature_array},
20
+ ... )
21
+
22
+ """
23
+
24
+ from __future__ import annotations
25
+
26
+ from typing import TYPE_CHECKING
27
+
28
+ import numpy as np
29
+ from scipy.spatial import Delaunay, cKDTree
30
+
31
+ if TYPE_CHECKING:
32
+ from numpy.typing import NDArray
33
+
34
+ _TOLERANCE = 1e-15
35
+ _TETRA_VERTICES = 4
36
+
37
+
38
+ def _compute_barycentric_tetra(
39
+ points: NDArray[np.float64],
40
+ tetra_vertices: NDArray[np.float64],
41
+ ) -> NDArray[np.float64]:
42
+ """Compute barycentric coordinates for points in tetrahedra.
43
+
44
+ Parameters
45
+ ----------
46
+ points : ndarray
47
+ Query points, shape (n_points, 3).
48
+ tetra_vertices : ndarray
49
+ Tetrahedron vertices, shape (n_points, 4, 3).
50
+ Each row contains the 4 vertices of the tetrahedron containing
51
+ the corresponding point.
52
+
53
+ Returns
54
+ -------
55
+ ndarray
56
+ Barycentric coordinates, shape (n_points, 4).
57
+ Each row sums to 1 and represents the weight of each tetrahedron vertex.
58
+
59
+ """
60
+ v0 = tetra_vertices[:, 0]
61
+ v1 = tetra_vertices[:, 1]
62
+ v2 = tetra_vertices[:, 2]
63
+ v3 = tetra_vertices[:, 3]
64
+
65
+ e1 = v1 - v0
66
+ e2 = v2 - v0
67
+ e3 = v3 - v0
68
+ p = points - v0
69
+
70
+ det = np.einsum("ij,ij->i", e1, np.cross(e2, e3))
71
+ det = np.where(np.abs(det) < _TOLERANCE, _TOLERANCE, det)
72
+
73
+ b1 = np.einsum("ij,ij->i", p, np.cross(e2, e3)) / det
74
+ b2 = np.einsum("ij,ij->i", e1, np.cross(p, e3)) / det
75
+ b3 = np.einsum("ij,ij->i", e1, np.cross(e2, p)) / det
76
+ b0 = 1.0 - b1 - b2 - b3
77
+
78
+ return np.column_stack([b0, b1, b2, b3])
79
+
80
+
81
+ def _compute_barycentric_tri(
82
+ points: NDArray[np.float64],
83
+ tri_vertices: NDArray[np.float64],
84
+ ) -> NDArray[np.float64]:
85
+ """Compute barycentric coordinates for points in triangles.
86
+
87
+ Parameters
88
+ ----------
89
+ points : ndarray
90
+ Query points, shape (n_points, 2) or (n_points, 3).
91
+ tri_vertices : ndarray
92
+ Triangle vertices, shape (n_points, 3, dim).
93
+ Each row contains the 3 vertices of the triangle containing
94
+ the corresponding point.
95
+
96
+ Returns
97
+ -------
98
+ ndarray
99
+ Barycentric coordinates, shape (n_points, 3).
100
+
101
+ """
102
+ v0 = tri_vertices[:, 0]
103
+ v1 = tri_vertices[:, 1]
104
+ v2 = tri_vertices[:, 2]
105
+
106
+ e0 = v1 - v0
107
+ e1 = v2 - v0
108
+ p = points - v0
109
+
110
+ d00 = np.einsum("ij,ij->i", e0, e0)
111
+ d01 = np.einsum("ij,ij->i", e0, e1)
112
+ d11 = np.einsum("ij,ij->i", e1, e1)
113
+ d20 = np.einsum("ij,ij->i", p, e0)
114
+ d21 = np.einsum("ij,ij->i", p, e1)
115
+
116
+ denom = d00 * d11 - d01 * d01
117
+ denom = np.where(np.abs(denom) < _TOLERANCE, _TOLERANCE, denom)
118
+
119
+ b1 = (d11 * d20 - d01 * d21) / denom
120
+ b2 = (d00 * d21 - d01 * d20) / denom
121
+ b0 = 1.0 - b1 - b2
122
+
123
+ return np.column_stack([b0, b1, b2])
124
+
125
+
126
+ def interpolate_field(
127
+ source_vertices: NDArray[np.float64],
128
+ source_elements: NDArray[np.int32],
129
+ target_points: NDArray[np.float64],
130
+ field: NDArray[np.float64],
131
+ *,
132
+ method: str = "linear",
133
+ ) -> NDArray[np.float64]:
134
+ """Interpolate a field from source mesh to target points.
135
+
136
+ Parameters
137
+ ----------
138
+ source_vertices : ndarray
139
+ Vertices of the source mesh, shape (n_source_vertices, dim).
140
+ source_elements : ndarray
141
+ Element connectivity of the source mesh.
142
+ Shape (n_elements, 4) for tetrahedra or (n_elements, 3) for triangles.
143
+ target_points : ndarray
144
+ Points where field values are needed, shape (n_target_points, dim).
145
+ field : ndarray
146
+ Field values at source vertices, shape (n_source_vertices,) or
147
+ (n_source_vertices, n_components).
148
+ method : str, default="linear"
149
+ Interpolation method: "linear" for barycentric interpolation,
150
+ "nearest" for nearest vertex interpolation.
151
+
152
+ Returns
153
+ -------
154
+ ndarray
155
+ Interpolated field values at target points.
156
+ Shape matches input field shape with n_target_points replacing
157
+ n_source_vertices.
158
+
159
+ """
160
+ n_element_verts = source_elements.shape[1]
161
+
162
+ field = np.atleast_2d(field)
163
+ if field.shape[0] == 1 and field.shape[1] == len(source_vertices):
164
+ field = field.T
165
+ n_components = field.shape[1]
166
+
167
+ delaunay = Delaunay(source_vertices)
168
+ simplex_indices = delaunay.find_simplex(target_points)
169
+
170
+ outside_mask = simplex_indices < 0
171
+
172
+ # Build KDTree once if needed (for nearest method or fallback for outside points)
173
+ tree: cKDTree | None = None
174
+ if method == "nearest" or np.any(outside_mask):
175
+ tree = cKDTree(source_vertices)
176
+
177
+ if method == "nearest":
178
+ assert tree is not None # noqa: S101
179
+ _, nearest_idx = tree.query(target_points)
180
+ return field[nearest_idx].reshape(len(target_points), n_components).squeeze()
181
+
182
+ result = np.zeros((len(target_points), n_components), dtype=np.float64)
183
+
184
+ inside_mask = ~outside_mask
185
+ inside_indices = np.where(inside_mask)[0]
186
+
187
+ if len(inside_indices) > 0:
188
+ inside_simplices = simplex_indices[inside_indices]
189
+ element_vertex_indices = delaunay.simplices[inside_simplices]
190
+
191
+ element_vertices = source_vertices[element_vertex_indices]
192
+
193
+ if n_element_verts == _TETRA_VERTICES:
194
+ bary = _compute_barycentric_tetra(
195
+ target_points[inside_indices],
196
+ element_vertices,
197
+ )
198
+ else:
199
+ bary = _compute_barycentric_tri(
200
+ target_points[inside_indices],
201
+ element_vertices,
202
+ )
203
+
204
+ field_at_vertices = field[element_vertex_indices]
205
+ result[inside_indices] = np.einsum("ij,ijk->ik", bary, field_at_vertices)
206
+
207
+ if np.any(outside_mask):
208
+ assert tree is not None # noqa: S101
209
+ outside_indices = np.where(outside_mask)[0]
210
+ _, nearest_idx = tree.query(target_points[outside_indices])
211
+ result[outside_indices] = field[nearest_idx]
212
+
213
+ return result.squeeze()
214
+
215
+
216
+ def transfer_fields(
217
+ source_vertices: NDArray[np.float64],
218
+ source_elements: NDArray[np.int32],
219
+ target_points: NDArray[np.float64],
220
+ fields: dict[str, NDArray[np.float64]],
221
+ *,
222
+ method: str = "linear",
223
+ ) -> dict[str, NDArray[np.float64]]:
224
+ """Transfer multiple fields from source mesh to target points.
225
+
226
+ Parameters
227
+ ----------
228
+ source_vertices : ndarray
229
+ Vertices of the source mesh.
230
+ source_elements : ndarray
231
+ Element connectivity of the source mesh.
232
+ target_points : ndarray
233
+ Points where field values are needed.
234
+ fields : dict[str, ndarray]
235
+ Dictionary mapping field names to field values at source vertices.
236
+ method : str, default="linear"
237
+ Interpolation method: "linear" or "nearest".
238
+
239
+ Returns
240
+ -------
241
+ dict[str, ndarray]
242
+ Dictionary mapping field names to interpolated values at target points.
243
+
244
+ Examples
245
+ --------
246
+ >>> result = transfer_fields(
247
+ ... source_vertices=old_vertices,
248
+ ... source_elements=old_tetrahedra,
249
+ ... target_points=new_vertices,
250
+ ... fields={
251
+ ... "temperature": temperature,
252
+ ... "velocity": velocity,
253
+ ... },
254
+ ... )
255
+ >>> new_temperature = result["temperature"]
256
+
257
+ """
258
+ return {
259
+ name: interpolate_field(
260
+ source_vertices,
261
+ source_elements,
262
+ target_points,
263
+ field,
264
+ method=method,
265
+ )
266
+ for name, field in fields.items()
267
+ }
268
+
269
+
270
+ __all__ = [
271
+ "interpolate_field",
272
+ "transfer_fields",
273
+ ]