Skip to content
Open
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
17 changes: 17 additions & 0 deletions flow360/component/simulation/framework/updater.py
Original file line number Diff line number Diff line change
Expand Up @@ -636,6 +636,23 @@ def fix_write_single_file_for_paraview_format(params_as_dict):
return params_as_dict


def _to_25_9_0(params_as_dict):
"""
Remove deprecated meshing defaults key `remove_non_manifold_faces`.

Note: this function is intentionally not registered in VERSION_MILESTONES until
the Python client version reaches 25.9.0.
"""
meshing = params_as_dict.get("meshing")
if not isinstance(meshing, dict):
return params_as_dict
meshing_defaults = meshing.get("defaults")
if not isinstance(meshing_defaults, dict):
return params_as_dict
meshing_defaults.pop("remove_non_manifold_faces", None)
return params_as_dict


VERSION_MILESTONES = [
(Flow360Version("24.11.1"), _to_24_11_1),
(Flow360Version("24.11.7"), _to_24_11_7),
Expand Down
40 changes: 23 additions & 17 deletions flow360/component/simulation/meshing_param/meshing_specs.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
ConditionalField,
ContextField,
ParamsValidationInfo,
add_validation_warning,
contextual_field_validator,
)
from flow360.component.simulation.validation.validation_utils import (
Expand Down Expand Up @@ -159,12 +160,6 @@ class MeshingDefaults(Flow360BaseModel):
+ "per face with :class:`~flow360.GeometryRefinement`.",
)

remove_non_manifold_faces: bool = pd.Field(
False,
description="Flag to remove non-manifold and interior faces. "
+ "This option is only supported when using geometry AI.",
)

remove_hidden_geometry: bool = pd.Field(
False,
description="Flag to remove hidden geometry that is not visible to flow. "
Expand All @@ -178,6 +173,28 @@ class MeshingDefaults(Flow360BaseModel):
+ "This option is only supported when using geometry AI.",
)

@pd.model_validator(mode="before")
@classmethod
def remove_deprecated_arguments(cls, value):
"""
Detect when invoking the constructor of the MeshingDefaults()
(Warning: contrary to deserializing data, which is supposed to be handled by the updater.py)
If the user added the remove_non_manifold_faces in the argument, pop the argument and give warning
that this is no longer supported.
"""
if not isinstance(value, dict):
return value

if "remove_non_manifold_faces" in value:
value.pop("remove_non_manifold_faces", None)
message = (
"`meshing.defaults.remove_non_manifold_faces` is no longer supported and has been "
+ "ignored. Set `meshing.defaults.remove_hidden_geometry` instead."
)
add_validation_warning(message)

return value

@contextual_field_validator("number_of_boundary_layers", mode="after")
@classmethod
def invalid_number_of_boundary_layers(cls, value, param_info: ParamsValidationInfo):
Expand All @@ -203,7 +220,6 @@ def invalid_geometry_accuracy(cls, value, param_info: ParamsValidationInfo):
"resolve_face_boundaries",
"preserve_thin_geometry",
"sealing_size",
"remove_non_manifold_faces",
"remove_hidden_geometry",
"flooding_cell_size",
mode="after",
Expand All @@ -213,16 +229,6 @@ def ensure_geometry_ai_features(cls, value, info, param_info: ParamsValidationIn
"""Validate that the feature is only used when Geometry AI is enabled."""
return check_geometry_ai_features(cls, value, info, param_info)

@pd.model_validator(mode="after")
def validate_mutual_exclusion(self):
"""Ensure remove_non_manifold_faces and remove_hidden_geometry are not both True."""
if self.remove_non_manifold_faces and self.remove_hidden_geometry:
raise ValueError(
"'remove_non_manifold_faces' and 'remove_hidden_geometry' cannot both be True. "
"Please enable only one of these options."
)
return self

@pd.model_validator(mode="after")
def validate_flooding_cell_size_requires_remove_hidden_geometry(self):
"""Ensure flooding_cell_size is only specified when remove_hidden_geometry is True."""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -726,7 +726,6 @@ def _get_gai_setting_whitelist(input_params: SimulationParams) -> dict:
"surface_max_aspect_ratio": None,
"surface_max_adaptation_iterations": None,
"sealing_size": None,
"remove_non_manifold_faces": None,
"remove_hidden_geometry": None,
"flooding_cell_size": None,
"planar_face_tolerance": None,
Expand Down
1 change: 0 additions & 1 deletion tests/ref/simulation/service_init_geometry.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
"units": "m"
},
"sliding_interface_tolerance": 0.01,
"remove_non_manifold_faces": false,
"remove_hidden_geometry": false
},
"outputs": [],
Expand Down
1 change: 0 additions & 1 deletion tests/ref/simulation/service_init_surface_mesh.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
"units": "m"
},
"sliding_interface_tolerance": 0.01,
"remove_non_manifold_faces": false,
"remove_hidden_geometry": false
},
"outputs": [],
Expand Down
5 changes: 2 additions & 3 deletions tests/simulation/data/geometry_airplane/simulation.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,7 @@
"sealing_size": {
"value": 0.0,
"units": "m"
},
"remove_non_manifold_faces": false
}
},
"refinements": [
{
Expand Down Expand Up @@ -2539,4 +2538,4 @@
"use_inhouse_mesher": false,
"use_geometry_AI": false
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1537,62 +1537,6 @@ def test_wind_tunnel_farfield_requires_geometry_ai():
assert farfield.type == "WindTunnelFarfield"


def test_remove_non_manifold_faces_and_remove_hidden_geometry_mutual_exclusion():
"""Test that remove_non_manifold_faces and remove_hidden_geometry cannot both be True."""
gai_context = ParamsValidationInfo({}, [])
gai_context.use_geometry_AI = True

# Test 1: Both True should raise ValueError
with pytest.raises(
pd.ValidationError,
match=r"'remove_non_manifold_faces' and 'remove_hidden_geometry' cannot both be True",
):
with ValidationContext(SURFACE_MESH, gai_context):
with SI_unit_system:
MeshingDefaults(
geometry_accuracy=0.01 * u.m,
surface_max_edge_length=0.1 * u.m,
remove_non_manifold_faces=True,
remove_hidden_geometry=True,
)

# Test 2: Only remove_non_manifold_faces=True should work
with ValidationContext(SURFACE_MESH, gai_context):
with SI_unit_system:
defaults = MeshingDefaults(
geometry_accuracy=0.01 * u.m,
surface_max_edge_length=0.1 * u.m,
remove_non_manifold_faces=True,
remove_hidden_geometry=False,
)
assert defaults.remove_non_manifold_faces is True
assert defaults.remove_hidden_geometry is False

# Test 3: Only remove_hidden_geometry=True should work
with ValidationContext(SURFACE_MESH, gai_context):
with SI_unit_system:
defaults = MeshingDefaults(
geometry_accuracy=0.01 * u.m,
surface_max_edge_length=0.1 * u.m,
remove_non_manifold_faces=False,
remove_hidden_geometry=True,
)
assert defaults.remove_non_manifold_faces is False
assert defaults.remove_hidden_geometry is True

# Test 4: Both False should work (default case)
with ValidationContext(SURFACE_MESH, gai_context):
with SI_unit_system:
defaults = MeshingDefaults(
geometry_accuracy=0.01 * u.m,
surface_max_edge_length=0.1 * u.m,
remove_non_manifold_faces=False,
remove_hidden_geometry=False,
)
assert defaults.remove_non_manifold_faces is False
assert defaults.remove_hidden_geometry is False


def test_flooding_cell_size_requires_remove_hidden_geometry():
"""Test that flooding_cell_size can only be specified when remove_hidden_geometry is True."""
gai_context = ParamsValidationInfo({}, [])
Expand Down
19 changes: 19 additions & 0 deletions tests/simulation/params/test_meshing_defaults_deprecation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from flow360.component.simulation.meshing_param.meshing_specs import MeshingDefaults
from flow360.component.simulation.validation.validation_context import (
CASE,
ValidationContext,
)


def test_meshing_defaults_removes_deprecated_remove_non_manifold_faces():
payload = {"remove_non_manifold_faces": False}

with ValidationContext(levels=CASE) as validation_context:
defaults = MeshingDefaults.model_validate(payload)

dumped = defaults.model_dump(mode="json", by_alias=True)
assert "remove_non_manifold_faces" not in dumped

warning_messages = [warning["msg"] for warning in validation_context.validation_warnings]
assert len(warning_messages) == 1
assert "remove_non_manifold_faces" in warning_messages[0]
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
},
"planar_face_tolerance": 1e-06,
"preserve_thin_geometry": false,
"remove_non_manifold_faces": false,
"resolve_face_boundaries": false,
"sealing_size": {
"units": "m",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
},
"planar_face_tolerance": 1e-06,
"preserve_thin_geometry": false,
"remove_non_manifold_faces": false,
"resolve_face_boundaries": false,
"sealing_size": {
"units": "m",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
},
"planar_face_tolerance": 1e-06,
"preserve_thin_geometry": false,
"remove_non_manifold_faces": false,
"resolve_face_boundaries": false,
"sealing_size": {
"units": "m",
Expand Down
20 changes: 20 additions & 0 deletions tests/simulation/test_updater.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from flow360.component.simulation.framework.updater import (
VERSION_MILESTONES,
_find_update_path,
_to_25_9_0,
updater,
)
from flow360.component.simulation.framework.updater_utils import Flow360Version
Expand Down Expand Up @@ -1582,3 +1583,22 @@ def test_updater_to_25_8_4_write_single_file_false():
assert (
params_new["outputs"][0]["write_single_file"] is False
), "write_single_file should remain False"


def test_updater_to_25_9_0_remove_deprecated_remove_non_manifold_faces():
"""Test 25.9.0 updater step removes deprecated meshing.defaults.remove_non_manifold_faces key."""

params_as_dict = {
"version": "25.8.3",
"unit_system": {"name": "SI"},
"meshing": {
"defaults": {
"surface_max_edge_length": {"value": 0.2, "units": "m"},
"remove_non_manifold_faces": False,
}
},
}

params_new = _to_25_9_0(params_as_dict)
defaults = params_new["meshing"]["defaults"]
assert "remove_non_manifold_faces" not in defaults
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,7 @@
"sealing_size": {
"value": 0.0,
"units": "m"
},
"remove_non_manifold_faces": false
}
},
"refinements": [],
"volume_zones": [
Expand Down Expand Up @@ -969,4 +968,4 @@
"use_inhouse_mesher": true,
"use_geometry_AI": true
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
},
"planar_face_tolerance": 1e-06,
"preserve_thin_geometry": false,
"remove_non_manifold_faces": false,
"remove_hidden_geometry": false,
"resolve_face_boundaries": false,
"sealing_size": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
"value": 0.0,
"units": "1.0*m"
},
"remove_non_manifold_faces": false,
"remove_hidden_geometry": false,
"planar_face_tolerance": 1e-06
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
"value": 0.0,
"units": "1.0*m"
},
"remove_non_manifold_faces": false,
"remove_hidden_geometry": false,
"planar_face_tolerance": 0.001
},
Expand Down