Skip to content

Commit 9392f2c

Browse files
Added warning for multi-zone GAI cases with remove_hidden_geometry = True (#1896)
1 parent 9954f9a commit 9392f2c

3 files changed

Lines changed: 298 additions & 0 deletions

File tree

flow360/component/simulation/meshing_param/params.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -477,6 +477,29 @@ def _warn_min_passage_size_without_remove_hidden_geometry(self) -> Self:
477477
)
478478
return self
479479

480+
@contextual_model_validator(mode="after")
481+
def _warn_multi_zone_remove_hidden_geometry(self) -> Self:
482+
"""Warn when remove_hidden_geometry is enabled with multiple farfield/custom volume zones."""
483+
if not self.defaults.remove_hidden_geometry: # pylint: disable=no-member
484+
return self
485+
if self.volume_zones is None:
486+
return self
487+
# AF and WTF each generate their own farfield zone but UDF does not,
488+
# so it doesn't contribute to the zone count
489+
has_non_udf_farfield = any(
490+
isinstance(zone, (AutomatedFarfield, WindTunnelFarfield))
491+
for zone in self.volume_zones # pylint: disable=not-an-iterable
492+
)
493+
count = len(_collect_all_custom_volumes(self.volume_zones)) + (
494+
1 if has_non_udf_farfield else 0
495+
)
496+
if count > 1:
497+
add_validation_warning(
498+
"Multiple farfield/custom volume zones detected. Removal of hidden geometry "
499+
"for multi-zone cases is not fully supported and may not work as intended."
500+
)
501+
return self
502+
480503
@property
481504
def farfield_method(self):
482505
"""Returns the farfield method used."""

flow360/component/simulation/translator/surface_meshing_translator.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -670,6 +670,7 @@ def _get_volume_zones(volume_zones_list: list[dict]):
670670
"AutomatedFarfield",
671671
"UserDefinedFarfield",
672672
"WindTunnelFarfield",
673+
"CustomZones",
673674
):
674675
volume_zones_translated.append(item)
675676
elif item["type"] in (

tests/simulation/params/meshing_validation/test_meshing_param_validation.py

Lines changed: 274 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2334,6 +2334,280 @@ def test_per_face_min_passage_size_warning_without_remove_hidden_geometry():
23342334
assert warnings == []
23352335

23362336

2337+
def test_multi_zone_remove_hidden_geometry_warning():
2338+
"""Test that remove_hidden_geometry with multiple farfield/custom volume zones triggers a warning."""
2339+
2340+
# Test 1: remove_hidden_geometry=True with AutomatedFarfield + CustomZones → warning
2341+
with SI_unit_system:
2342+
params = SimulationParams(
2343+
meshing=MeshingParams(
2344+
defaults=MeshingDefaults(
2345+
geometry_accuracy=0.01 * u.m,
2346+
surface_max_edge_length=0.1 * u.m,
2347+
remove_hidden_geometry=True,
2348+
),
2349+
volume_zones=[
2350+
AutomatedFarfield(enclosed_entities=[Surface(name="face1")]),
2351+
CustomZones(
2352+
name="custom_zones",
2353+
entities=[
2354+
CustomVolume(
2355+
name="zone1",
2356+
bounding_entities=[Surface(name="face1"), Surface(name="face2")],
2357+
)
2358+
],
2359+
),
2360+
],
2361+
),
2362+
private_attribute_asset_cache=AssetCache(
2363+
use_geometry_AI=True,
2364+
use_inhouse_mesher=True,
2365+
project_length_unit=1 * u.m,
2366+
),
2367+
)
2368+
_, errors, warnings = validate_model(
2369+
params_as_dict=params.model_dump(mode="json"),
2370+
validated_by=ValidationCalledBy.LOCAL,
2371+
root_item_type="Geometry",
2372+
validation_level="SurfaceMesh",
2373+
)
2374+
assert errors is None
2375+
assert len(warnings) == 1
2376+
assert (
2377+
"removal of hidden geometry for multi-zone cases is not fully supported"
2378+
in warnings[0]["msg"].lower()
2379+
)
2380+
2381+
# Test 2: remove_hidden_geometry=True with a single AutomatedFarfield zone → no warning
2382+
with SI_unit_system:
2383+
params = SimulationParams(
2384+
meshing=MeshingParams(
2385+
defaults=MeshingDefaults(
2386+
geometry_accuracy=0.01 * u.m,
2387+
surface_max_edge_length=0.1 * u.m,
2388+
remove_hidden_geometry=True,
2389+
),
2390+
volume_zones=[AutomatedFarfield()],
2391+
),
2392+
private_attribute_asset_cache=AssetCache(
2393+
use_geometry_AI=True,
2394+
use_inhouse_mesher=True,
2395+
project_length_unit=1 * u.m,
2396+
),
2397+
)
2398+
_, errors, warnings = validate_model(
2399+
params_as_dict=params.model_dump(mode="json"),
2400+
validated_by=ValidationCalledBy.LOCAL,
2401+
root_item_type="Geometry",
2402+
validation_level="SurfaceMesh",
2403+
)
2404+
assert errors is None
2405+
assert warnings == []
2406+
2407+
# Test 3: remove_hidden_geometry=False with AutomatedFarfield + CustomZones → no warning
2408+
with SI_unit_system:
2409+
params = SimulationParams(
2410+
meshing=MeshingParams(
2411+
defaults=MeshingDefaults(
2412+
geometry_accuracy=0.01 * u.m,
2413+
surface_max_edge_length=0.1 * u.m,
2414+
remove_hidden_geometry=False,
2415+
),
2416+
volume_zones=[
2417+
AutomatedFarfield(enclosed_entities=[Surface(name="face1")]),
2418+
CustomZones(
2419+
name="custom_zones",
2420+
entities=[
2421+
CustomVolume(
2422+
name="zone1",
2423+
bounding_entities=[Surface(name="face1"), Surface(name="face2")],
2424+
)
2425+
],
2426+
),
2427+
],
2428+
),
2429+
private_attribute_asset_cache=AssetCache(
2430+
use_geometry_AI=True,
2431+
use_inhouse_mesher=True,
2432+
project_length_unit=1 * u.m,
2433+
),
2434+
)
2435+
_, errors, warnings = validate_model(
2436+
params_as_dict=params.model_dump(mode="json"),
2437+
validated_by=ValidationCalledBy.LOCAL,
2438+
root_item_type="Geometry",
2439+
validation_level="SurfaceMesh",
2440+
)
2441+
assert errors is None
2442+
assert warnings == []
2443+
2444+
# Test 4: remove_hidden_geometry=True with a single CustomZones containing multiple CustomVolumes → warning
2445+
with SI_unit_system:
2446+
params = SimulationParams(
2447+
meshing=MeshingParams(
2448+
defaults=MeshingDefaults(
2449+
geometry_accuracy=0.01 * u.m,
2450+
surface_max_edge_length=0.1 * u.m,
2451+
remove_hidden_geometry=True,
2452+
),
2453+
volume_zones=[
2454+
CustomZones(
2455+
name="custom_zones",
2456+
entities=[
2457+
CustomVolume(
2458+
name="zone1",
2459+
bounding_entities=[Surface(name="face1"), Surface(name="face2")],
2460+
),
2461+
CustomVolume(
2462+
name="zone2",
2463+
bounding_entities=[Surface(name="face3"), Surface(name="face4")],
2464+
),
2465+
],
2466+
),
2467+
],
2468+
),
2469+
private_attribute_asset_cache=AssetCache(
2470+
use_geometry_AI=True,
2471+
use_inhouse_mesher=True,
2472+
project_length_unit=1 * u.m,
2473+
),
2474+
)
2475+
_, errors, warnings = validate_model(
2476+
params_as_dict=params.model_dump(mode="json"),
2477+
validated_by=ValidationCalledBy.LOCAL,
2478+
root_item_type="Geometry",
2479+
validation_level="SurfaceMesh",
2480+
)
2481+
assert errors is None
2482+
assert len(warnings) == 1
2483+
assert (
2484+
"removal of hidden geometry for multi-zone cases is not fully supported"
2485+
in warnings[0]["msg"].lower()
2486+
)
2487+
2488+
# Test 5: remove_hidden_geometry=True with UDF + 1 CV → no warning (UDF doesn't contribute a zone)
2489+
with SI_unit_system:
2490+
params = SimulationParams(
2491+
meshing=MeshingParams(
2492+
defaults=MeshingDefaults(
2493+
geometry_accuracy=0.01 * u.m,
2494+
surface_max_edge_length=0.1 * u.m,
2495+
remove_hidden_geometry=True,
2496+
),
2497+
volume_zones=[
2498+
UserDefinedFarfield(
2499+
enclosed_entities=[Surface(name="face1"), Surface(name="face2")]
2500+
),
2501+
CustomZones(
2502+
name="custom_zones",
2503+
entities=[
2504+
CustomVolume(
2505+
name="zone1",
2506+
bounding_entities=[Surface(name="face1"), Surface(name="face2")],
2507+
)
2508+
],
2509+
),
2510+
],
2511+
),
2512+
private_attribute_asset_cache=AssetCache(
2513+
use_geometry_AI=True,
2514+
use_inhouse_mesher=True,
2515+
project_length_unit=1 * u.m,
2516+
),
2517+
)
2518+
_, errors, warnings = validate_model(
2519+
params_as_dict=params.model_dump(mode="json"),
2520+
validated_by=ValidationCalledBy.LOCAL,
2521+
root_item_type="Geometry",
2522+
validation_level="SurfaceMesh",
2523+
)
2524+
assert errors is None
2525+
assert warnings == []
2526+
2527+
# Test 6: remove_hidden_geometry=True with UDF + 2 CVs → warning (2 actual zones)
2528+
with SI_unit_system:
2529+
params = SimulationParams(
2530+
meshing=MeshingParams(
2531+
defaults=MeshingDefaults(
2532+
geometry_accuracy=0.01 * u.m,
2533+
surface_max_edge_length=0.1 * u.m,
2534+
remove_hidden_geometry=True,
2535+
),
2536+
volume_zones=[
2537+
UserDefinedFarfield(
2538+
enclosed_entities=[Surface(name="face1"), Surface(name="face2")]
2539+
),
2540+
CustomZones(
2541+
name="custom_zones",
2542+
entities=[
2543+
CustomVolume(
2544+
name="zone1",
2545+
bounding_entities=[Surface(name="face1"), Surface(name="face2")],
2546+
),
2547+
CustomVolume(
2548+
name="zone2",
2549+
bounding_entities=[Surface(name="face3"), Surface(name="face4")],
2550+
),
2551+
],
2552+
),
2553+
],
2554+
),
2555+
private_attribute_asset_cache=AssetCache(
2556+
use_geometry_AI=True,
2557+
use_inhouse_mesher=True,
2558+
project_length_unit=1 * u.m,
2559+
),
2560+
)
2561+
_, errors, warnings = validate_model(
2562+
params_as_dict=params.model_dump(mode="json"),
2563+
validated_by=ValidationCalledBy.LOCAL,
2564+
root_item_type="Geometry",
2565+
validation_level="SurfaceMesh",
2566+
)
2567+
assert errors is None
2568+
assert len(warnings) == 1
2569+
assert (
2570+
"removal of hidden geometry for multi-zone cases is not fully supported"
2571+
in warnings[0]["msg"].lower()
2572+
)
2573+
2574+
# Test 7: remove_hidden_geometry=True with a single CV and implicit farfield → no warning (1 actual zone)
2575+
with SI_unit_system:
2576+
params = SimulationParams(
2577+
meshing=MeshingParams(
2578+
defaults=MeshingDefaults(
2579+
geometry_accuracy=0.01 * u.m,
2580+
surface_max_edge_length=0.1 * u.m,
2581+
remove_hidden_geometry=True,
2582+
),
2583+
volume_zones=[
2584+
CustomZones(
2585+
name="custom_zones",
2586+
entities=[
2587+
CustomVolume(
2588+
name="zone1",
2589+
bounding_entities=[Surface(name="face1"), Surface(name="face2")],
2590+
)
2591+
],
2592+
),
2593+
],
2594+
),
2595+
private_attribute_asset_cache=AssetCache(
2596+
use_geometry_AI=True,
2597+
use_inhouse_mesher=True,
2598+
project_length_unit=1 * u.m,
2599+
),
2600+
)
2601+
_, errors, warnings = validate_model(
2602+
params_as_dict=params.model_dump(mode="json"),
2603+
validated_by=ValidationCalledBy.LOCAL,
2604+
root_item_type="Geometry",
2605+
validation_level="SurfaceMesh",
2606+
)
2607+
assert errors is None
2608+
assert warnings == []
2609+
2610+
23372611
def test_geometry_accuracy_with_non_unit_project_length_scale():
23382612
"""geometry_accuracy validation must account for the project-length scale factor.
23392613

0 commit comments

Comments
 (0)