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/ui/utils.py
ADDED
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
"""Utility functions for the mmgpy UI."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
from math import floor, log10
|
|
7
|
+
from typing import TYPE_CHECKING, Any
|
|
8
|
+
|
|
9
|
+
import numpy as np
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from mmgpy import Mesh
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def round_to_significant(x: float, sig: int = 2) -> float:
|
|
18
|
+
"""Round a number to a specified number of significant figures.
|
|
19
|
+
|
|
20
|
+
Parameters
|
|
21
|
+
----------
|
|
22
|
+
x : float
|
|
23
|
+
The number to round.
|
|
24
|
+
sig : int, default=2
|
|
25
|
+
Number of significant figures.
|
|
26
|
+
|
|
27
|
+
Returns
|
|
28
|
+
-------
|
|
29
|
+
float
|
|
30
|
+
The rounded number.
|
|
31
|
+
|
|
32
|
+
Examples
|
|
33
|
+
--------
|
|
34
|
+
>>> round_to_significant(0.0123456, 2)
|
|
35
|
+
0.012
|
|
36
|
+
>>> round_to_significant(1234.5678, 3)
|
|
37
|
+
1230.0
|
|
38
|
+
>>> round_to_significant(0.0, 2)
|
|
39
|
+
0.0
|
|
40
|
+
|
|
41
|
+
"""
|
|
42
|
+
if x == 0:
|
|
43
|
+
return 0.0
|
|
44
|
+
return round(x, -int(floor(log10(abs(x)))) + (sig - 1))
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def get_mesh_diagonal(mesh: Mesh | None) -> float:
|
|
48
|
+
"""Get the diagonal length of the mesh bounding box.
|
|
49
|
+
|
|
50
|
+
Parameters
|
|
51
|
+
----------
|
|
52
|
+
mesh : Mesh | None
|
|
53
|
+
The mesh to measure.
|
|
54
|
+
|
|
55
|
+
Returns
|
|
56
|
+
-------
|
|
57
|
+
float
|
|
58
|
+
The diagonal length, or 1.0 if mesh is None.
|
|
59
|
+
|
|
60
|
+
Examples
|
|
61
|
+
--------
|
|
62
|
+
>>> get_mesh_diagonal(None)
|
|
63
|
+
1.0
|
|
64
|
+
|
|
65
|
+
"""
|
|
66
|
+
if mesh is None:
|
|
67
|
+
return 1.0
|
|
68
|
+
bounds = mesh.get_bounds()
|
|
69
|
+
size = bounds[1] - bounds[0]
|
|
70
|
+
return float(np.linalg.norm(size))
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def to_float(val: Any) -> float | None:
|
|
74
|
+
"""Safely convert a value to float.
|
|
75
|
+
|
|
76
|
+
Handles None, empty string, and other edge cases.
|
|
77
|
+
|
|
78
|
+
Parameters
|
|
79
|
+
----------
|
|
80
|
+
val : Any
|
|
81
|
+
The value to convert.
|
|
82
|
+
|
|
83
|
+
Returns
|
|
84
|
+
-------
|
|
85
|
+
float | None
|
|
86
|
+
The converted float, or None if conversion not possible.
|
|
87
|
+
|
|
88
|
+
Examples
|
|
89
|
+
--------
|
|
90
|
+
>>> to_float(3.14)
|
|
91
|
+
3.14
|
|
92
|
+
>>> to_float("2.5")
|
|
93
|
+
2.5
|
|
94
|
+
>>> to_float(None) is None
|
|
95
|
+
True
|
|
96
|
+
>>> to_float("") is None
|
|
97
|
+
True
|
|
98
|
+
|
|
99
|
+
"""
|
|
100
|
+
if val is None or val == "":
|
|
101
|
+
return None
|
|
102
|
+
try:
|
|
103
|
+
return float(val)
|
|
104
|
+
except (ValueError, TypeError):
|
|
105
|
+
return None
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
# Default state values for the UI
|
|
109
|
+
DEFAULT_STATE: dict[str, Any] = {
|
|
110
|
+
# UI state
|
|
111
|
+
"drawer_open": True,
|
|
112
|
+
"active_tab": "remesh",
|
|
113
|
+
# Mesh state
|
|
114
|
+
"mesh_loaded": False,
|
|
115
|
+
"mesh_info": "",
|
|
116
|
+
"mesh_kind": "",
|
|
117
|
+
"mesh_stats": None,
|
|
118
|
+
"info_panel_open": True,
|
|
119
|
+
# Remeshing parameters (None = use MMG defaults)
|
|
120
|
+
"hmin": None,
|
|
121
|
+
"hmax": None,
|
|
122
|
+
"hsiz": None,
|
|
123
|
+
"hausd": None,
|
|
124
|
+
"hgrad": None,
|
|
125
|
+
"ar": None,
|
|
126
|
+
"verbose": 1,
|
|
127
|
+
# Advanced parameters
|
|
128
|
+
"mem": None,
|
|
129
|
+
"nreg": False,
|
|
130
|
+
# Options
|
|
131
|
+
"selected_options": [],
|
|
132
|
+
"nosurf": False,
|
|
133
|
+
"use_preset": "default",
|
|
134
|
+
"remesh_mode": "standard",
|
|
135
|
+
"remesh_source": "original",
|
|
136
|
+
# Levelset/Lagrangian
|
|
137
|
+
"levelset_formula": "x**2 + y**2 + z**2 - 0.25",
|
|
138
|
+
"levelset_isovalue": 0.0,
|
|
139
|
+
"displacement_scale": 0.1,
|
|
140
|
+
"use_solution_as_metric": False,
|
|
141
|
+
"use_solution_as_levelset": False,
|
|
142
|
+
"solution_type": "",
|
|
143
|
+
# Sizing
|
|
144
|
+
"sizing_mode": "sphere",
|
|
145
|
+
"sizing_constraints": [],
|
|
146
|
+
# Results
|
|
147
|
+
"validation_report": None,
|
|
148
|
+
"remesh_result": None,
|
|
149
|
+
"is_remeshing": False,
|
|
150
|
+
# Viewer settings
|
|
151
|
+
"show_edges": True,
|
|
152
|
+
"show_scalar": "none",
|
|
153
|
+
"show_original_mesh": False,
|
|
154
|
+
"has_original_mesh": False,
|
|
155
|
+
"color_map": "RdYlBu",
|
|
156
|
+
"opacity": 1.0,
|
|
157
|
+
"smooth_shading": False,
|
|
158
|
+
# Slice view
|
|
159
|
+
"slice_enabled": False,
|
|
160
|
+
"slice_axis": 0,
|
|
161
|
+
"slice_threshold": 0.5,
|
|
162
|
+
# File state
|
|
163
|
+
"file_upload": None,
|
|
164
|
+
"sol_file_upload": None,
|
|
165
|
+
"mesh_filename": "",
|
|
166
|
+
"sol_filename": "",
|
|
167
|
+
"solution_fields": {},
|
|
168
|
+
"export_format": "vtk",
|
|
169
|
+
# Camera
|
|
170
|
+
"current_view": "isometric",
|
|
171
|
+
"parallel_projection": False,
|
|
172
|
+
# Theme
|
|
173
|
+
"theme_name": "light", # "light" or "dark" - system preference detected on load
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
# Default scalar field options
|
|
177
|
+
DEFAULT_SCALAR_FIELD_OPTIONS: list[dict[str, str]] = [
|
|
178
|
+
{"title": "No Color", "value": "none"},
|
|
179
|
+
{"type": "subheader", "title": "-- Quality --"},
|
|
180
|
+
{"title": "In-Radius Ratio", "value": "quality"},
|
|
181
|
+
{"title": "Scaled Jacobian", "value": "pv_quality"},
|
|
182
|
+
{"type": "subheader", "title": "-- Sizing --"},
|
|
183
|
+
{"title": "Edge Length", "value": "edge_length"},
|
|
184
|
+
{"type": "subheader", "title": "-- Orientation --"},
|
|
185
|
+
{"title": "Face Orientation", "value": "face_sides"},
|
|
186
|
+
{"type": "subheader", "title": "-- Other --"},
|
|
187
|
+
{"title": "Area/Volume", "value": "area_volume"},
|
|
188
|
+
{"title": "Refs", "value": "refs"},
|
|
189
|
+
]
|
|
190
|
+
|
|
191
|
+
# Default remesh mode items (Optimize Only is handled by the toggle button)
|
|
192
|
+
DEFAULT_REMESH_MODE_ITEMS: list[dict[str, str]] = [
|
|
193
|
+
{"title": "Standard Remesh", "value": "standard"},
|
|
194
|
+
{"title": "Levelset Discretization", "value": "levelset"},
|
|
195
|
+
{"title": "Lagrangian Motion", "value": "lagrangian"},
|
|
196
|
+
]
|
|
197
|
+
|
|
198
|
+
# Preset ratios for adaptive defaults
|
|
199
|
+
PRESET_RATIOS: dict[str, dict[str, float | bool | None]] = {
|
|
200
|
+
"default": {"clear_all": True}, # Let MMG use its internal defaults
|
|
201
|
+
"fine": {"hmax_ratio": 1 / 50, "hausd_ratio": 1 / 1000, "hgrad": 1.2},
|
|
202
|
+
"medium": {"hmax_ratio": 1 / 25, "hausd_ratio": 1 / 500, "hgrad": 1.3},
|
|
203
|
+
"coarse": {"hmax_ratio": 1 / 10, "hausd_ratio": 1 / 200, "hgrad": 1.5},
|
|
204
|
+
"optimize": {"optim": True, "noinsert": True}, # Optimize only, no size change
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def compute_preset_values(preset: str, diagonal: float) -> dict[str, Any]:
|
|
209
|
+
"""Compute parameter values for a preset based on mesh diagonal.
|
|
210
|
+
|
|
211
|
+
Parameters
|
|
212
|
+
----------
|
|
213
|
+
preset : str
|
|
214
|
+
Preset name: "default", "fine", "medium", "coarse".
|
|
215
|
+
diagonal : float
|
|
216
|
+
The mesh bounding box diagonal length.
|
|
217
|
+
|
|
218
|
+
Returns
|
|
219
|
+
-------
|
|
220
|
+
dict[str, Any]
|
|
221
|
+
Parameter values for the preset.
|
|
222
|
+
|
|
223
|
+
Examples
|
|
224
|
+
--------
|
|
225
|
+
>>> values = compute_preset_values("medium", 10.0)
|
|
226
|
+
>>> "hmax" in values
|
|
227
|
+
True
|
|
228
|
+
>>> values["hgrad"]
|
|
229
|
+
1.3
|
|
230
|
+
|
|
231
|
+
"""
|
|
232
|
+
if preset not in PRESET_RATIOS:
|
|
233
|
+
return {}
|
|
234
|
+
|
|
235
|
+
ratios = PRESET_RATIOS[preset]
|
|
236
|
+
values: dict[str, Any] = {}
|
|
237
|
+
|
|
238
|
+
# Default preset: clear all size parameters to let MMG use its defaults
|
|
239
|
+
if ratios.get("clear_all"):
|
|
240
|
+
values["hsiz"] = None
|
|
241
|
+
values["hmin"] = None
|
|
242
|
+
values["hmax"] = None
|
|
243
|
+
values["hausd"] = None
|
|
244
|
+
values["hgrad"] = None
|
|
245
|
+
values["ar"] = None
|
|
246
|
+
return values
|
|
247
|
+
|
|
248
|
+
# Optimize preset: set optimization flags
|
|
249
|
+
if ratios.get("optim"):
|
|
250
|
+
values["optim"] = True
|
|
251
|
+
if ratios.get("noinsert"):
|
|
252
|
+
values["noinsert"] = True
|
|
253
|
+
return values
|
|
254
|
+
|
|
255
|
+
if "hmax_ratio" in ratios:
|
|
256
|
+
values["hmax"] = round_to_significant(diagonal * ratios["hmax_ratio"])
|
|
257
|
+
if "hausd_ratio" in ratios:
|
|
258
|
+
values["hausd"] = round_to_significant(diagonal * ratios["hausd_ratio"])
|
|
259
|
+
if "hgrad" in ratios:
|
|
260
|
+
values["hgrad"] = ratios["hgrad"]
|
|
261
|
+
|
|
262
|
+
return values
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
def reset_solution_state() -> dict[str, Any]:
|
|
266
|
+
"""Get state values for resetting solution-related state.
|
|
267
|
+
|
|
268
|
+
Returns
|
|
269
|
+
-------
|
|
270
|
+
dict[str, Any]
|
|
271
|
+
State values to reset solution state.
|
|
272
|
+
|
|
273
|
+
"""
|
|
274
|
+
return {
|
|
275
|
+
"sol_filename": "",
|
|
276
|
+
"solution_fields": {},
|
|
277
|
+
"use_solution_as_metric": False,
|
|
278
|
+
"use_solution_as_levelset": False,
|
|
279
|
+
"solution_type": "",
|
|
280
|
+
}
|