Skip to content

Commit c375ce8

Browse files
Python meshing refactor (#1415)
* Snappy multizone python interface and ModularMeshingWorkflow refactor * SimulationParams validator fix * fixed validation error * disable checking missing boundaries when snappy multizone enabled * porous media support of SeedpointZone * seedpoint zone in rotation * potential bug fixes * remove commented parts for prototyping * fix validation context init * formatting part 1 * formatting part 2 * formatting last part * updated doc strings * formatting + automatic detection of beta mesher for snappy * updated doc strings, ready for review * version bump * fix bug with translating surface output with missing boundaries * handle same boundary names in differen vm blocks * added cube notebook * deleted print debug line * exmple formatting --------- Co-authored-by: marcvivesmassana <marc.vives@flexcompute.com>
1 parent a159f21 commit c375ce8

37 files changed

+3753
-511
lines changed

examples/basic_simulations/meshing/cube_snappy.ipynb

Lines changed: 440 additions & 0 deletions
Large diffs are not rendered by default.

flow360/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,8 +155,8 @@
155155
Box,
156156
CustomVolume,
157157
Cylinder,
158-
MeshZone,
159158
ReferenceGeometry,
159+
SeedpointZone,
160160
SnappyBody,
161161
)
162162
from flow360.component.simulation.simulation_params import SimulationParams
@@ -330,7 +330,7 @@
330330
"SnappyRegionRefinement",
331331
"SnappySurfaceEdgeRefinement",
332332
"SnappyBody",
333-
"MeshZone",
333+
"SeedpointZone",
334334
"SnappySurfaceMeshingParams",
335335
"BetaVolumeMeshingParams",
336336
"BetaVolumeMeshingDefaults",

flow360/component/simulation/framework/param_utils.py

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ def register_entity_list(model: Flow360BaseModel, registry: EntityRegistry) -> N
152152
register_entity_list(field, registry)
153153

154154

155+
# pylint: disable=too-many-branches
155156
def _update_entity_full_name(
156157
model: Flow360BaseModel,
157158
target_entity_type: Union[type[_SurfaceEntityBase], type[_VolumeEntityBase]],
@@ -170,20 +171,36 @@ def _update_entity_full_name(
170171
field._update_entity_info_with_metadata(volume_mesh_meta_data)
171172

172173
if isinstance(field, EntityList):
174+
added_entities = []
173175
for entity in field.stored_entities:
174176
if isinstance(entity, target_entity_type):
175177
# pylint: disable=protected-access
176-
entity._update_entity_info_with_metadata(volume_mesh_meta_data)
178+
partial_additions = entity._update_entity_info_with_metadata(
179+
volume_mesh_meta_data
180+
)
181+
if partial_additions is not None:
182+
added_entities.extend(partial_additions)
183+
field.stored_entities.extend(added_entities)
177184

178185
elif isinstance(field, (list, tuple)):
186+
added_entities = []
179187
for item in field:
180188
if isinstance(item, target_entity_type):
181-
item._update_entity_info_with_metadata( # pylint: disable=protected-access
182-
volume_mesh_meta_data
189+
partial_additions = (
190+
item._update_entity_info_with_metadata( # pylint: disable=protected-access
191+
volume_mesh_meta_data
192+
)
183193
)
194+
if partial_additions is not None:
195+
added_entities.extend(partial_additions)
184196
elif isinstance(item, Flow360BaseModel):
185197
_update_entity_full_name(item, target_entity_type, volume_mesh_meta_data)
186198

199+
if isinstance(field, list):
200+
field.extend(added_entities)
201+
if isinstance(field, tuple):
202+
field += tuple(added_entities)
203+
187204
elif isinstance(field, Flow360BaseModel):
188205
_update_entity_full_name(field, target_entity_type, volume_mesh_meta_data)
189206

flow360/component/simulation/meshing_param/meshing_specs.py

Lines changed: 96 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,10 @@ def invalid_geometry_ai_features(cls, value, info):
166166

167167

168168
class BetaVolumeMeshingDefaults(Flow360BaseModel):
169+
"""
170+
Default/global settings for volume meshing parameters. To be used with class:`ModularMeshingWorkflow`.
171+
"""
172+
169173
##:: Default boundary layer settings
170174
boundary_layer_growth_rate: float = pd.Field(
171175
1.2,
@@ -199,6 +203,12 @@ class BetaVolumeMeshingDefaults(Flow360BaseModel):
199203

200204

201205
class SnappySurfaceMeshingDefaults(Flow360BaseModel):
206+
"""
207+
Default/global settings for snappyHexMesh surface meshing parameters.
208+
To be used with class:`ModularMeshingWorkflow`.
209+
"""
210+
211+
# pylint: disable=no-member
202212
min_spacing: LengthType.Positive = pd.Field()
203213
max_spacing: LengthType.Positive = pd.Field()
204214
gap_resolution: LengthType.Positive = pd.Field()
@@ -272,6 +282,7 @@ class SnappyQualityMetrics(Flow360BaseModel):
272282
If < 0: always deletes such cells.
273283
"""
274284

285+
# pylint: disable=no-member
275286
max_non_ortho: Optional[AngleType.Positive] = pd.Field(default=85 * u.deg)
276287
max_boundary_skewness: Optional[AngleType] = pd.Field(default=20 * u.deg)
277288
max_internal_skewness: Optional[AngleType] = pd.Field(default=50 * u.deg)
@@ -291,50 +302,73 @@ class SnappyQualityMetrics(Flow360BaseModel):
291302
@pd.field_validator("max_non_ortho", "max_concave", mode="after")
292303
@classmethod
293304
def disable_angle_metrics_w_defaults(cls, value):
305+
"""Disable a quality metric in OpenFOAM by setting a specific value."""
294306
if value is None:
295307
return 180 * u.deg
296308
if value > 180 * u.deg:
297309
raise ValueError("Value must be less that 180 degrees.")
298-
else:
299-
return value
310+
return value
300311

301312
@pd.field_validator("max_boundary_skewness", "max_internal_skewness", mode="after")
302313
@classmethod
303314
def disable_skewness_metric(cls, value):
315+
"""Disable a quality metric in OpenFOAM by setting a specific value."""
304316
if value is None:
305317
return -1 * u.deg
306318
if value.to("degree") <= 0 * u.deg and value.to("degree") != -1 * u.deg:
307319
raise ValueError(
308320
f"Maximum skewness must be positive (your value: {value}). To disable enter None or -1*u.deg."
309321
)
310-
else:
311-
return value
322+
return value
312323

313324
@pd.field_validator("min_vol", "min_tet_quality", "min_determinant", mode="after")
314325
@classmethod
315326
def disable_by_low_value(cls, value):
327+
"""Disable a quality metric in OpenFOAM by setting a specific value."""
316328
if value is None:
317329
return -1e30
318-
else:
319-
return value
330+
return value
320331

321332
@pd.field_validator("n_smooth_scale", "error_reduction", mode="after")
322333
@classmethod
323334
def disable_by_zero(cls, value):
335+
"""Disable a quality metric in OpenFOAM by setting a specific value."""
324336
if value is None:
325337
return 0
326-
else:
327-
return value
338+
return value
328339

329340

330341
class SnappyCastellatedMeshControls(Flow360BaseModel):
342+
"""
343+
snappyHexMesh castellation controls.
344+
345+
Parameters
346+
----------
347+
resolve_feature_angle : Optional[AngleType.Positive], default: 25°
348+
This parameter controls the local curvature refinement. The higher the value,
349+
the less features it captures. Applies maximum level of refinement to cells
350+
that can see intersections whose angle exceeds this value.
351+
352+
n_cells_between_levels: Optional[pd.NonNegativeInt], default: 1
353+
This parameter controls the transition between cell refinement levels. Number
354+
of buffer layers of cells between different levels of refinement.
355+
356+
min_refinement_cells: Optional[pd.NonNegativeInt], default: 10
357+
The refinement along the surfaces may spend many iterations on refinement of
358+
only few cells. Whenever the number of cells to be refined is less than or equal
359+
to this value, the refinement will stop. Unless the parameter is set to zero,
360+
at least one refining iteration will be performed.
361+
"""
362+
363+
# pylint: disable=no-member
331364
resolve_feature_angle: Optional[AngleType.Positive] = pd.Field(default=25 * u.deg)
332365
n_cells_between_levels: Optional[pd.NonNegativeInt] = pd.Field(1)
333366
min_refinement_cells: Optional[pd.NonNegativeInt] = pd.Field(10)
334367

335368
@pd.field_validator("resolve_feature_angle", mode="after")
336369
@classmethod
337370
def angle_limits(cls, value):
371+
"""Limit angular values."""
338372
if value is None:
339373
return value
340374
if value > 180 * u.deg:
@@ -343,6 +377,40 @@ def angle_limits(cls, value):
343377

344378

345379
class SnappySnapControls(Flow360BaseModel):
380+
"""
381+
snappyHexMesh snap controls.
382+
383+
Parameters
384+
----------
385+
n_smooth_patch: pd.NonNegativeInt, default: 3
386+
Number of patch smoothing iterations before finding correspondence to surface.
387+
388+
tolerance: pd.PositiveFloat, default: 2
389+
Ratio of distance for points to be attracted by surface feature point or edge,
390+
to local maximum edge length.
391+
392+
n_solve_iter: pd.NonNegativeInt, default: 30
393+
Number of mesh displacement relaxation iterations
394+
395+
n_relax_iter: pd.NonNegativeInt, default: 5
396+
Number of relaxation iterations during the snapping. If the mesh does not conform the geometry
397+
and all the iterations are spend, user may try to increase the number of iterations.
398+
399+
n_feature_snap_iter: pd.NonNegativeInt, default: 15
400+
Number of relaxation iterations used for snapping onto the features.
401+
If not specified, feature snapping will be disabled.
402+
403+
multi_region_feature_snap: bool, default: True
404+
When using explicitFeatureSnap and this switch is on, features between multiple
405+
surfaces will be captured. This is useful for multi-region meshing where the internal
406+
mesh must conform the region geometrical boundaries.
407+
408+
strict_region_snap: bool, default: False
409+
Attract points only to the surface they originate from. This can improve snapping of
410+
intersecting surfaces.
411+
"""
412+
413+
# pylint: disable=no-member
346414
n_smooth_patch: pd.NonNegativeInt = pd.Field(3)
347415
tolerance: pd.PositiveFloat = pd.Field(2)
348416
n_solve_iter: pd.NonNegativeInt = pd.Field(30)
@@ -353,6 +421,26 @@ class SnappySnapControls(Flow360BaseModel):
353421

354422

355423
class SnappySmoothControls(Flow360BaseModel):
424+
"""
425+
snappyHexMesh smoothing controls.
426+
427+
Parameters
428+
----------
429+
lambda_factor: Optional[pd.NonNegativeFloat], default: 0.7
430+
Lambda value within [0,1]
431+
432+
mu_factor: Optional[pd.NonNegativeFloat], default: 0.71
433+
Mu value within [0,1]
434+
435+
iterations: Optional[pd.NonNegativeInt], default: 5
436+
Number of smoothing iterations
437+
438+
min_elem: Optional[pd.NonNegativeInt], default: None
439+
min_len: Optional[LengthType.NonNegative], default: None
440+
included_angle: Optional[AngleType.NonNegative], default: 150°
441+
"""
442+
443+
# pylint: disable=no-member
356444
lambda_factor: Optional[pd.NonNegativeFloat] = pd.Field(0.7)
357445
mu_factor: Optional[pd.NonNegativeFloat] = pd.Field(0.71)
358446
iterations: Optional[pd.NonNegativeInt] = pd.Field(5)

0 commit comments

Comments
 (0)