Skip to content

Commit 8e17457

Browse files
Merge remote-tracking branch 'origin/main' into develop
2 parents d613f69 + be082a9 commit 8e17457

14 files changed

Lines changed: 579 additions & 32 deletions

File tree

flow360/component/simulation/framework/updater.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -778,6 +778,17 @@ def _rename_in_entity(entity):
778778
return params_as_dict
779779

780780

781+
def _to_25_9_3(params_as_dict):
782+
"""Rename ``type_name`` to ``wall_function_type`` in ``use_wall_function`` dicts."""
783+
for model in params_as_dict.get("models", []):
784+
if model.get("type") != "Wall":
785+
continue
786+
wall_fn = model.get("use_wall_function")
787+
if isinstance(wall_fn, dict) and "type_name" in wall_fn:
788+
wall_fn["wall_function_type"] = wall_fn.pop("type_name")
789+
return params_as_dict
790+
791+
781792
def _to_25_10_0(params_as_dict):
782793
"""Migrate to 25.10.0: output_format string to list, add vtkhdf/ensight support."""
783794

@@ -831,6 +842,7 @@ def _migrate_output_format_to_list(params_as_dict):
831842
(Flow360Version("25.9.0"), _to_25_9_0),
832843
(Flow360Version("25.9.1"), _to_25_9_1),
833844
(Flow360Version("25.9.2"), _to_25_9_2),
845+
(Flow360Version("25.9.3"), _to_25_9_3),
834846
(Flow360Version("25.10.0"), _to_25_10_0),
835847
] # A list of the Python API version tuple with their corresponding updaters.
836848

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
"""Shared validation helpers for meshing parameters."""
2+
3+
import flow360.component.simulation.units as u
4+
from flow360.component.simulation.meshing_param.volume_params import UniformRefinement
5+
from flow360.component.simulation.primitives import Box, Cylinder
6+
7+
8+
def validate_snappy_uniform_refinement_entities(refinement: UniformRefinement):
9+
"""Validate that a UniformRefinement's entities are compatible with snappyHexMesh.
10+
11+
Raises ValueError if any Box has a non-axis-aligned rotation or any Cylinder is hollow.
12+
"""
13+
# pylint: disable=no-member
14+
for entity in refinement.entities.stored_entities:
15+
if (
16+
isinstance(entity, Box)
17+
and entity.angle_of_rotation.to("deg") % (360 * u.deg) != 0 * u.deg
18+
):
19+
raise ValueError(
20+
"UniformRefinement for snappy accepts only Boxes with axes aligned"
21+
+ " with the global coordinate system (angle_of_rotation=0)."
22+
)
23+
if (
24+
isinstance(entity, Cylinder)
25+
and entity.inner_radius is not None
26+
and entity.inner_radius.to("m") != 0 * u.m
27+
):
28+
raise ValueError(
29+
"UniformRefinement for snappy accepts only full cylinders (where inner_radius = 0)."
30+
)

flow360/component/simulation/meshing_param/params.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@
2222
MeshingDefaults,
2323
VolumeMeshingDefaults,
2424
)
25+
from flow360.component.simulation.meshing_param.meshing_validators import (
26+
validate_snappy_uniform_refinement_entities,
27+
)
2528
from flow360.component.simulation.meshing_param.volume_params import (
2629
AutomatedFarfield,
2730
AxisymmetricRefinement,
@@ -589,6 +592,19 @@ def _check_sizing_against_octree_series(self, param_info: ParamsValidationInfo):
589592

590593
return self
591594

595+
@contextual_model_validator(mode="after")
596+
def _check_snappy_uniform_refinement_entities(self, param_info: ParamsValidationInfo):
597+
"""Validate projected UniformRefinement entities are compatible with snappyHexMesh."""
598+
if not param_info.use_snappy:
599+
return self
600+
for refinement in self.refinements: # pylint: disable=not-an-iterable
601+
if (
602+
isinstance(refinement, UniformRefinement)
603+
and refinement.project_to_surface is not False
604+
):
605+
validate_snappy_uniform_refinement_entities(refinement)
606+
return self
607+
592608

593609
SurfaceMeshingParams = Annotated[
594610
Union[snappy.SurfaceMeshingParams], pd.Field(discriminator="type_name")

flow360/component/simulation/meshing_param/snappy/snappy_params.py

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@
44

55
import pydantic as pd
66

7-
import flow360.component.simulation.units as u
87
from flow360.component.simulation.framework.base_model import Flow360BaseModel
98
from flow360.component.simulation.meshing_param.meshing_specs import OctreeSpacing
9+
from flow360.component.simulation.meshing_param.meshing_validators import (
10+
validate_snappy_uniform_refinement_entities,
11+
)
1012
from flow360.component.simulation.meshing_param.snappy.snappy_mesh_refinements import (
1113
BodyRefinement,
1214
RegionRefinement,
@@ -22,7 +24,6 @@
2224
SurfaceMeshingDefaults,
2325
)
2426
from flow360.component.simulation.meshing_param.volume_params import UniformRefinement
25-
from flow360.component.simulation.primitives import Box, Cylinder
2627
from flow360.component.simulation.unit_system import LengthType
2728
from flow360.component.simulation.validation.validation_context import (
2829
ParamsValidationInfo,
@@ -124,20 +125,7 @@ def _check_uniform_refinement_entities(self):
124125
return self
125126
for refinement in self.refinements:
126127
if isinstance(refinement, UniformRefinement):
127-
# No expansion needed since we only allow Draft entities here.
128-
for entity in refinement.entities.stored_entities:
129-
if (
130-
isinstance(entity, Box)
131-
and entity.angle_of_rotation.to("deg") % (360 * u.deg) != 0 * u.deg
132-
):
133-
raise ValueError(
134-
"UniformRefinement for snappy accepts only Boxes with axes aligned"
135-
+ " with the global coordinate system (angle_of_rotation=0)."
136-
)
137-
if isinstance(entity, Cylinder) and entity.inner_radius.to("m") != 0 * u.m:
138-
raise ValueError(
139-
"UniformRefinement for snappy accepts only full cylinders (where inner_radius = 0)."
140-
)
128+
validate_snappy_uniform_refinement_entities(refinement)
141129

142130
return self
143131

flow360/component/simulation/meshing_param/volume_params.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,26 @@ def check_entities_used_with_beta_mesher(cls, values, param_info: ParamsValidati
103103

104104
return values
105105

106+
@contextual_field_validator("entities", mode="after")
107+
@classmethod
108+
def check_entities_used_with_snappy(cls, values, param_info: ParamsValidationInfo):
109+
"""Check that only Box, Cylinder, and Sphere entities are used with snappyHexMesh."""
110+
111+
if values is None:
112+
return values
113+
if not param_info.use_snappy:
114+
return values
115+
116+
expanded = param_info.expand_entity_list(values)
117+
for entity in expanded:
118+
if not isinstance(entity, (Box, Cylinder, Sphere)):
119+
raise ValueError(
120+
f"`{type(entity).__name__}` entity for `UniformRefinement` is not supported "
121+
"with snappyHexMesh. Only `Box`, `Cylinder`, and `Sphere` are allowed."
122+
)
123+
124+
return values
125+
106126
@contextual_model_validator(mode="after")
107127
def check_project_to_surface_with_snappy(self, param_info: ParamsValidationInfo):
108128
"""Check that project_to_surface is used only with snappy."""

flow360/component/simulation/models/surface_models.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -341,13 +341,13 @@ class WallFunction(Flow360BaseModel):
341341
342342
>>> fl.Wall(
343343
... entities=volume_mesh["fluid/wall"],
344-
... use_wall_function=fl.WallFunction(type_name="InnerLayer"),
344+
... use_wall_function=fl.WallFunction(wall_function_type="InnerLayer"),
345345
... )
346346
347347
====
348348
"""
349349

350-
type_name: Literal["BoundaryLayer", "InnerLayer"] = pd.Field(
350+
wall_function_type: Literal["BoundaryLayer", "InnerLayer"] = pd.Field(
351351
"BoundaryLayer",
352352
description="Type of wall function model. "
353353
+ "'BoundaryLayer' uses integral flat plate boundary layer theory to predict wall shear stress. "
@@ -377,7 +377,7 @@ class Wall(BoundaryBase):
377377
378378
>>> fl.Wall(
379379
... entities=volume_mesh["8"],
380-
... use_wall_function=fl.WallFunction(type_name="InnerLayer"),
380+
... use_wall_function=fl.WallFunction(wall_function_type="InnerLayer"),
381381
... )
382382
383383
- :code:`Wall` with wall function and wall rotation:

flow360/component/simulation/translator/solver_translator.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1588,7 +1588,7 @@ def boundary_spec_translator(model: SurfaceModelTypes, op_acoustic_to_static_pre
15881588
if isinstance(model, Wall):
15891589
if model.use_wall_function is not None:
15901590
boundary["type"] = "WallFunction"
1591-
boundary["wallModelType"] = model.use_wall_function.type_name
1591+
boundary["wallModelType"] = model.use_wall_function.wall_function_type
15921592
else:
15931593
boundary["type"] = "NoSlipWall"
15941594
if model.velocity is not None:

tests/simulation/params/meshing_validation/test_meshing_param_validation.py

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,10 @@
6969
beta_mesher_context.is_beta_mesher = True
7070
beta_mesher_context.project_length_unit = "mm"
7171

72+
snappy_context = ParamsValidationInfo({}, [])
73+
snappy_context.use_snappy = True
74+
snappy_context.is_beta_mesher = True
75+
7276

7377
def test_structured_box_only_in_beta_mesher():
7478
# raises when beta mesher is off
@@ -870,7 +874,20 @@ def test_sphere_in_uniform_refinement():
870874
],
871875
)
872876

873-
# raises without beta mesher
877+
# also allowed with snappy
878+
with ValidationContext(VOLUME_MESH, snappy_context):
879+
with CGS_unit_system:
880+
sphere = Sphere(
881+
name="s_snappy",
882+
center=(0, 0, 0),
883+
radius=1.0,
884+
)
885+
UniformRefinement(
886+
entities=[sphere],
887+
spacing=0.1,
888+
)
889+
890+
# raises without beta mesher or snappy
874891
with pytest.raises(
875892
pd.ValidationError,
876893
match=r"`Sphere` entity for `UniformRefinement` is supported only with beta mesher",
@@ -888,6 +905,45 @@ def test_sphere_in_uniform_refinement():
888905
)
889906

890907

908+
def test_uniform_refinement_snappy_entity_restrictions():
909+
"""With snappy, UniformRefinement only accepts Box, Cylinder, and Sphere entities."""
910+
# Box, Cylinder, Sphere all allowed
911+
with ValidationContext(VOLUME_MESH, snappy_context):
912+
with CGS_unit_system:
913+
UniformRefinement(
914+
entities=[
915+
Box(center=(0, 0, 0), size=(1, 1, 1), name="box"),
916+
Cylinder(
917+
name="cyl",
918+
axis=(0, 0, 1),
919+
center=(0, 0, 0),
920+
height=1.0,
921+
outer_radius=0.5,
922+
),
923+
Sphere(name="sph", center=(0, 0, 0), radius=1.0),
924+
],
925+
spacing=0.1,
926+
)
927+
928+
# AxisymmetricBody rejected with snappy
929+
with pytest.raises(
930+
pd.ValidationError,
931+
match=r"`AxisymmetricBody` entity for `UniformRefinement` is not supported with snappyHexMesh",
932+
):
933+
with ValidationContext(VOLUME_MESH, snappy_context):
934+
with CGS_unit_system:
935+
axisymmetric_body = AxisymmetricBody(
936+
name="axisymm",
937+
axis=(0, 0, 1),
938+
center=(0, 0, 0),
939+
profile_curve=[(-1, 0), (-1, 1), (1, 1), (1, 0)],
940+
)
941+
UniformRefinement(
942+
entities=[axisymmetric_body],
943+
spacing=0.1,
944+
)
945+
946+
891947
def test_require_mesh_zones():
892948
with SI_unit_system:
893949
ModularMeshingWorkflow(

0 commit comments

Comments
 (0)