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/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
+ }