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.
- mmgpy/__init__.py +296 -0
- mmgpy/__main__.py +13 -0
- mmgpy/_io.py +535 -0
- mmgpy/_logging.py +290 -0
- mmgpy/_mesh.py +2286 -0
- mmgpy/_mmgpy.cpython-311-x86_64-linux-gnu.so +0 -0
- mmgpy/_mmgpy.pyi +2140 -0
- mmgpy/_options.py +304 -0
- mmgpy/_progress.py +850 -0
- mmgpy/_pyvista.py +410 -0
- mmgpy/_result.py +143 -0
- mmgpy/_transfer.py +273 -0
- mmgpy/_validation.py +669 -0
- mmgpy/_version.py +3 -0
- mmgpy/_version.py.in +3 -0
- mmgpy/bin/mmg2d_O3 +0 -0
- mmgpy/bin/mmg3d_O3 +0 -0
- mmgpy/bin/mmgs_O3 +0 -0
- mmgpy/interactive/__init__.py +24 -0
- mmgpy/interactive/sizing_editor.py +790 -0
- mmgpy/lagrangian.py +394 -0
- mmgpy/lib/libmmg2d.so +0 -0
- mmgpy/lib/libmmg2d.so.5 +0 -0
- mmgpy/lib/libmmg2d.so.5.8.0 +0 -0
- mmgpy/lib/libmmg3d.so +0 -0
- mmgpy/lib/libmmg3d.so.5 +0 -0
- mmgpy/lib/libmmg3d.so.5.8.0 +0 -0
- mmgpy/lib/libmmgs.so +0 -0
- mmgpy/lib/libmmgs.so.5 +0 -0
- mmgpy/lib/libmmgs.so.5.8.0 +0 -0
- mmgpy/lib/libvtkCommonColor-9.5.so.1 +0 -0
- mmgpy/lib/libvtkCommonComputationalGeometry-9.5.so.1 +0 -0
- mmgpy/lib/libvtkCommonCore-9.5.so.1 +0 -0
- mmgpy/lib/libvtkCommonDataModel-9.5.so.1 +0 -0
- mmgpy/lib/libvtkCommonExecutionModel-9.5.so.1 +0 -0
- mmgpy/lib/libvtkCommonMath-9.5.so.1 +0 -0
- mmgpy/lib/libvtkCommonMisc-9.5.so.1 +0 -0
- mmgpy/lib/libvtkCommonSystem-9.5.so.1 +0 -0
- mmgpy/lib/libvtkCommonTransforms-9.5.so.1 +0 -0
- mmgpy/lib/libvtkDICOMParser-9.5.so.1 +0 -0
- mmgpy/lib/libvtkFiltersCellGrid-9.5.so.1 +0 -0
- mmgpy/lib/libvtkFiltersCore-9.5.so.1 +0 -0
- mmgpy/lib/libvtkFiltersExtraction-9.5.so.1 +0 -0
- mmgpy/lib/libvtkFiltersGeneral-9.5.so.1 +0 -0
- mmgpy/lib/libvtkFiltersGeometry-9.5.so.1 +0 -0
- mmgpy/lib/libvtkFiltersHybrid-9.5.so.1 +0 -0
- mmgpy/lib/libvtkFiltersHyperTree-9.5.so.1 +0 -0
- mmgpy/lib/libvtkFiltersModeling-9.5.so.1 +0 -0
- mmgpy/lib/libvtkFiltersParallel-9.5.so.1 +0 -0
- mmgpy/lib/libvtkFiltersReduction-9.5.so.1 +0 -0
- mmgpy/lib/libvtkFiltersSources-9.5.so.1 +0 -0
- mmgpy/lib/libvtkFiltersStatistics-9.5.so.1 +0 -0
- mmgpy/lib/libvtkFiltersTexture-9.5.so.1 +0 -0
- mmgpy/lib/libvtkFiltersVerdict-9.5.so.1 +0 -0
- mmgpy/lib/libvtkIOCellGrid-9.5.so.1 +0 -0
- mmgpy/lib/libvtkIOCore-9.5.so.1 +0 -0
- mmgpy/lib/libvtkIOGeometry-9.5.so.1 +0 -0
- mmgpy/lib/libvtkIOImage-9.5.so.1 +0 -0
- mmgpy/lib/libvtkIOLegacy-9.5.so.1 +0 -0
- mmgpy/lib/libvtkIOParallel-9.5.so.1 +0 -0
- mmgpy/lib/libvtkIOParallelXML-9.5.so.1 +0 -0
- mmgpy/lib/libvtkIOXML-9.5.so.1 +0 -0
- mmgpy/lib/libvtkIOXMLParser-9.5.so.1 +0 -0
- mmgpy/lib/libvtkImagingCore-9.5.so.1 +0 -0
- mmgpy/lib/libvtkImagingSources-9.5.so.1 +0 -0
- mmgpy/lib/libvtkParallelCore-9.5.so.1 +0 -0
- mmgpy/lib/libvtkParallelDIY-9.5.so.1 +0 -0
- mmgpy/lib/libvtkRenderingCore-9.5.so.1 +0 -0
- mmgpy/lib/libvtkdoubleconversion-9.5.so.1 +0 -0
- mmgpy/lib/libvtkexpat-9.5.so.1 +0 -0
- mmgpy/lib/libvtkfmt-9.5.so.1 +0 -0
- mmgpy/lib/libvtkjpeg-9.5.so.1 +0 -0
- mmgpy/lib/libvtkjsoncpp-9.5.so.1 +0 -0
- mmgpy/lib/libvtkkissfft-9.5.so.1 +0 -0
- mmgpy/lib/libvtkloguru-9.5.so.1 +0 -0
- mmgpy/lib/libvtklz4-9.5.so.1 +0 -0
- mmgpy/lib/libvtklzma-9.5.so.1 +0 -0
- mmgpy/lib/libvtkmetaio-9.5.so.1 +0 -0
- mmgpy/lib/libvtkpng-9.5.so.1 +0 -0
- mmgpy/lib/libvtkpugixml-9.5.so.1 +0 -0
- mmgpy/lib/libvtksys-9.5.so.1 +0 -0
- mmgpy/lib/libvtktiff-9.5.so.1 +0 -0
- mmgpy/lib/libvtktoken-9.5.so.1 +0 -0
- mmgpy/lib/libvtkverdict-9.5.so.1 +0 -0
- mmgpy/lib/libvtkzlib-9.5.so.1 +0 -0
- mmgpy/metrics.py +596 -0
- mmgpy/progress.py +69 -0
- mmgpy/py.typed +0 -0
- mmgpy/repair/__init__.py +37 -0
- mmgpy/repair/_core.py +226 -0
- mmgpy/repair/_elements.py +241 -0
- mmgpy/repair/_vertices.py +219 -0
- mmgpy/sizing.py +370 -0
- mmgpy/ui/__init__.py +97 -0
- mmgpy/ui/__main__.py +87 -0
- mmgpy/ui/app.py +1837 -0
- mmgpy/ui/parsers.py +501 -0
- mmgpy/ui/remeshing.py +448 -0
- mmgpy/ui/samples.py +249 -0
- mmgpy/ui/utils.py +280 -0
- mmgpy/ui/viewer.py +587 -0
- mmgpy-0.5.0.dist-info/METADATA +186 -0
- mmgpy-0.5.0.dist-info/RECORD +109 -0
- mmgpy-0.5.0.dist-info/WHEEL +6 -0
- mmgpy-0.5.0.dist-info/entry_points.txt +13 -0
- mmgpy-0.5.0.dist-info/licenses/LICENSE +38 -0
- share/man/man1/mmg2d.1.gz +0 -0
- share/man/man1/mmg3d.1.gz +0 -0
- 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
|
+
]
|