diff --git a/flow360/component/simulation/framework/updater.py b/flow360/component/simulation/framework/updater.py index 7f206e7b2..574dcc9c1 100644 --- a/flow360/component/simulation/framework/updater.py +++ b/flow360/component/simulation/framework/updater.py @@ -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), diff --git a/flow360/component/simulation/meshing_param/meshing_specs.py b/flow360/component/simulation/meshing_param/meshing_specs.py index cd4ab0344..89560a6f5 100644 --- a/flow360/component/simulation/meshing_param/meshing_specs.py +++ b/flow360/component/simulation/meshing_param/meshing_specs.py @@ -19,6 +19,7 @@ ConditionalField, ContextField, ParamsValidationInfo, + add_validation_warning, contextual_field_validator, ) from flow360.component.simulation.validation.validation_utils import ( @@ -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. " @@ -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): @@ -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", @@ -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.""" diff --git a/flow360/component/simulation/translator/surface_meshing_translator.py b/flow360/component/simulation/translator/surface_meshing_translator.py index 6ea7fd9bb..1640fd586 100644 --- a/flow360/component/simulation/translator/surface_meshing_translator.py +++ b/flow360/component/simulation/translator/surface_meshing_translator.py @@ -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, diff --git a/tests/ref/simulation/service_init_geometry.json b/tests/ref/simulation/service_init_geometry.json index a72e06dde..4435b646c 100644 --- a/tests/ref/simulation/service_init_geometry.json +++ b/tests/ref/simulation/service_init_geometry.json @@ -23,7 +23,6 @@ "units": "m" }, "sliding_interface_tolerance": 0.01, - "remove_non_manifold_faces": false, "remove_hidden_geometry": false }, "outputs": [], diff --git a/tests/ref/simulation/service_init_surface_mesh.json b/tests/ref/simulation/service_init_surface_mesh.json index 3fec243f8..b066d9c7e 100644 --- a/tests/ref/simulation/service_init_surface_mesh.json +++ b/tests/ref/simulation/service_init_surface_mesh.json @@ -23,7 +23,6 @@ "units": "m" }, "sliding_interface_tolerance": 0.01, - "remove_non_manifold_faces": false, "remove_hidden_geometry": false }, "outputs": [], diff --git a/tests/simulation/data/geometry_airplane/simulation.json b/tests/simulation/data/geometry_airplane/simulation.json index 52725caef..fadf4b412 100644 --- a/tests/simulation/data/geometry_airplane/simulation.json +++ b/tests/simulation/data/geometry_airplane/simulation.json @@ -30,8 +30,7 @@ "sealing_size": { "value": 0.0, "units": "m" - }, - "remove_non_manifold_faces": false + } }, "refinements": [ { @@ -2539,4 +2538,4 @@ "use_inhouse_mesher": false, "use_geometry_AI": false } -} \ No newline at end of file +} diff --git a/tests/simulation/params/meshing_validation/test_meshing_param_validation.py b/tests/simulation/params/meshing_validation/test_meshing_param_validation.py index a14fbd572..c20932373 100644 --- a/tests/simulation/params/meshing_validation/test_meshing_param_validation.py +++ b/tests/simulation/params/meshing_validation/test_meshing_param_validation.py @@ -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({}, []) diff --git a/tests/simulation/params/test_meshing_defaults_deprecation.py b/tests/simulation/params/test_meshing_defaults_deprecation.py new file mode 100644 index 000000000..7b40eb583 --- /dev/null +++ b/tests/simulation/params/test_meshing_defaults_deprecation.py @@ -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] diff --git a/tests/simulation/service/data/dependency_geometry_sphere1_simulation.json b/tests/simulation/service/data/dependency_geometry_sphere1_simulation.json index 18b32130f..6a6296b76 100644 --- a/tests/simulation/service/data/dependency_geometry_sphere1_simulation.json +++ b/tests/simulation/service/data/dependency_geometry_sphere1_simulation.json @@ -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", diff --git a/tests/simulation/service/data/dependency_geometry_sphere2_simulation.json b/tests/simulation/service/data/dependency_geometry_sphere2_simulation.json index 2a76b7dd8..528e723ee 100644 --- a/tests/simulation/service/data/dependency_geometry_sphere2_simulation.json +++ b/tests/simulation/service/data/dependency_geometry_sphere2_simulation.json @@ -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", diff --git a/tests/simulation/service/data/root_geometry_cube_simulation.json b/tests/simulation/service/data/root_geometry_cube_simulation.json index 880212b27..faab23101 100644 --- a/tests/simulation/service/data/root_geometry_cube_simulation.json +++ b/tests/simulation/service/data/root_geometry_cube_simulation.json @@ -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", diff --git a/tests/simulation/test_updater.py b/tests/simulation/test_updater.py index 3214905ca..58a2ee301 100644 --- a/tests/simulation/test_updater.py +++ b/tests/simulation/test_updater.py @@ -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 @@ -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 diff --git a/tests/simulation/translator/data/gai_windtunnel_farfield_info/simulation.json b/tests/simulation/translator/data/gai_windtunnel_farfield_info/simulation.json index f63c753af..6fc2ea9af 100644 --- a/tests/simulation/translator/data/gai_windtunnel_farfield_info/simulation.json +++ b/tests/simulation/translator/data/gai_windtunnel_farfield_info/simulation.json @@ -30,8 +30,7 @@ "sealing_size": { "value": 0.0, "units": "m" - }, - "remove_non_manifold_faces": false + } }, "refinements": [], "volume_zones": [ @@ -969,4 +968,4 @@ "use_inhouse_mesher": true, "use_geometry_AI": true } -} \ No newline at end of file +} diff --git a/tests/simulation/translator/ref/Flow360_mirrored_surface_meshing.json b/tests/simulation/translator/ref/Flow360_mirrored_surface_meshing.json index 7fe9c5f14..58ab0ecad 100644 --- a/tests/simulation/translator/ref/Flow360_mirrored_surface_meshing.json +++ b/tests/simulation/translator/ref/Flow360_mirrored_surface_meshing.json @@ -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": { diff --git a/tests/simulation/translator/ref/surface_meshing/gai_surface_mesher.json b/tests/simulation/translator/ref/surface_meshing/gai_surface_mesher.json index a1b9fbdf6..653d6bd31 100644 --- a/tests/simulation/translator/ref/surface_meshing/gai_surface_mesher.json +++ b/tests/simulation/translator/ref/surface_meshing/gai_surface_mesher.json @@ -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 }, diff --git a/tests/simulation/translator/ref/surface_meshing/gai_windtunnel.json b/tests/simulation/translator/ref/surface_meshing/gai_windtunnel.json index e15c2b681..662590b76 100644 --- a/tests/simulation/translator/ref/surface_meshing/gai_windtunnel.json +++ b/tests/simulation/translator/ref/surface_meshing/gai_windtunnel.json @@ -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 },