Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions flow360/component/simulation/meshing_param/face_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,13 @@ class GeometryRefinement(Flow360BaseModel):
description="Threshold size below which all geometry gaps are automatically closed.",
)

min_passage_size: Optional[LengthType.Positive] = pd.Field(
None,
description="Minimum passage size that hidden geometry removal can resolve for this face group. "
"Internal regions connected by thin passages smaller than this size may not be detected. "
"If not specified, the value is derived from geometry_accuracy and sealing_size.",
)
Comment thread
cursor[bot] marked this conversation as resolved.

# Note: No checking on deleted surfaces since geometry accuracy on deleted surface does impact the volume mesh.

@contextual_model_validator(mode="after")
Expand Down
18 changes: 18 additions & 0 deletions flow360/component/simulation/meshing_param/params.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
SURFACE_MESH,
VOLUME_MESH,
ContextField,
add_validation_warning,
contextual_field_validator,
contextual_model_validator,
)
Expand Down Expand Up @@ -331,6 +332,23 @@ def _check_no_reused_volume_entities(self) -> Self:

return self

@contextual_model_validator(mode="after")
def _warn_min_passage_size_without_remove_hidden_geometry(self) -> Self:
"""Warn when GeometryRefinement specifies min_passage_size but remove_hidden_geometry is disabled."""
if self.defaults.remove_hidden_geometry: # pylint: disable=no-member
return self
for refinement in self.refinements or []:
if (
isinstance(refinement, GeometryRefinement)
and refinement.min_passage_size is not None
):
add_validation_warning(
f"GeometryRefinement '{refinement.name}' specifies 'min_passage_size' but "
"'remove_hidden_geometry' is not enabled in meshing defaults. "
"The per-face 'min_passage_size' will be ignored."
)
return self

@property
def farfield_method(self):
"""Returns the farfield method used."""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,12 @@
import pytest

from flow360 import u
from flow360.component.simulation.framework.param_utils import AssetCache
from flow360.component.simulation.meshing_param import snappy
from flow360.component.simulation.meshing_param.face_params import SurfaceRefinement
from flow360.component.simulation.meshing_param.face_params import (
GeometryRefinement,
SurfaceRefinement,
)
from flow360.component.simulation.meshing_param.meshing_specs import (
MeshingDefaults,
OctreeSpacing,
Expand Down Expand Up @@ -1924,3 +1928,92 @@ def test_min_passage_size_requires_remove_hidden_geometry():
)
assert defaults.min_passage_size is None
assert defaults.remove_hidden_geometry is True


def test_per_face_min_passage_size_warning_without_remove_hidden_geometry():
"""Test that per-face min_passage_size on GeometryRefinement warns when remove_hidden_geometry is disabled."""

# Test 1: min_passage_size on GeometryRefinement with remove_hidden_geometry=False → warning
with SI_unit_system:
params = SimulationParams(
meshing=MeshingParams(
defaults=MeshingDefaults(
geometry_accuracy=0.01 * u.m,
surface_max_edge_length=0.1 * u.m,
remove_hidden_geometry=False,
),
refinements=[
GeometryRefinement(
geometry_accuracy=0.01 * u.m,
min_passage_size=0.05 * u.m,
faces=[Surface(name="face1")],
),
],
),
private_attribute_asset_cache=AssetCache(use_geometry_AI=True, use_inhouse_mesher=True),
)
_, errors, warnings = validate_model(
params_as_dict=params.model_dump(mode="json"),
validated_by=ValidationCalledBy.LOCAL,
root_item_type="Geometry",
validation_level="SurfaceMesh",
)
assert errors is None
assert len(warnings) == 1
assert "min_passage_size" in warnings[0]["msg"]
assert "remove_hidden_geometry" in warnings[0]["msg"]

# Test 2: min_passage_size on GeometryRefinement with remove_hidden_geometry=True → no warning
with SI_unit_system:
params = SimulationParams(
meshing=MeshingParams(
defaults=MeshingDefaults(
geometry_accuracy=0.01 * u.m,
surface_max_edge_length=0.1 * u.m,
remove_hidden_geometry=True,
),
refinements=[
GeometryRefinement(
geometry_accuracy=0.01 * u.m,
min_passage_size=0.05 * u.m,
faces=[Surface(name="face1")],
),
],
),
private_attribute_asset_cache=AssetCache(use_geometry_AI=True, use_inhouse_mesher=True),
)
_, errors, warnings = validate_model(
params_as_dict=params.model_dump(mode="json"),
validated_by=ValidationCalledBy.LOCAL,
root_item_type="Geometry",
validation_level="SurfaceMesh",
)
assert errors is None
assert warnings == []

# Test 3: GeometryRefinement without min_passage_size, remove_hidden_geometry=False → no warning
with SI_unit_system:
params = SimulationParams(
meshing=MeshingParams(
defaults=MeshingDefaults(
geometry_accuracy=0.01 * u.m,
surface_max_edge_length=0.1 * u.m,
remove_hidden_geometry=False,
),
refinements=[
GeometryRefinement(
geometry_accuracy=0.01 * u.m,
faces=[Surface(name="face1")],
),
],
),
private_attribute_asset_cache=AssetCache(use_geometry_AI=True, use_inhouse_mesher=True),
)
_, errors, warnings = validate_model(
params_as_dict=params.model_dump(mode="json"),
validated_by=ValidationCalledBy.LOCAL,
root_item_type="Geometry",
validation_level="SurfaceMesh",
)
assert errors is None
assert warnings == []
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"value": 0.0,
"units": "1.0*m"
},
"remove_hidden_geometry": false,
"remove_hidden_geometry": true,
"planar_face_tolerance": 1e-06
},
"refinements": [
Expand Down Expand Up @@ -179,6 +179,10 @@
"geometry_accuracy": {
"value": 0.05,
"units": "1.0*m"
},
"min_passage_size": {
"value": 0.1,
"units": "1.0*m"
}
}
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1148,6 +1148,7 @@ def test_gai_surface_mesher_refinements():
boundary_layer_first_layer_thickness=0.01,
surface_max_aspect_ratio=0.01,
surface_max_adaptation_iterations=19,
remove_hidden_geometry=True,
),
volume_zones=[farfield],
refinements=[
Expand All @@ -1161,6 +1162,7 @@ def test_gai_surface_mesher_refinements():
GeometryRefinement(
name="Local_override",
geometry_accuracy=0.05 * u.m,
min_passage_size=0.1 * u.m,
faces=[geometry["body00001_face00001"]],
),
],
Expand Down
Loading