Skip to content

Commit 5c07053

Browse files
fix(): mirror selector registry coverage mismatch (#1682)
1 parent 4e50283 commit 5c07053

File tree

3 files changed

+111
-17
lines changed

3 files changed

+111
-17
lines changed

flow360/component/simulation/framework/entity_expansion_utils.py

Lines changed: 38 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,38 @@
1818
from flow360.component.simulation.framework.entity_registry import EntityRegistry
1919

2020

21+
def _register_mirror_entities_in_registry(registry: "EntityRegistry", mirror_status: Any) -> None:
22+
"""Register mirror-related entities (planes + derived mirrored entities) into registry.
23+
24+
This helper is shared by both dict-based and params-based registry builders to ensure
25+
consistent selector expansion coverage.
26+
"""
27+
if not mirror_status:
28+
return
29+
30+
# pylint: disable=import-outside-toplevel
31+
from flow360.component.simulation.draft_context.mirror import (
32+
MirrorPlane,
33+
MirrorStatus,
34+
)
35+
36+
# Dict path: deserialize to MirrorStatus
37+
if isinstance(mirror_status, dict):
38+
mirror_status = MirrorStatus.model_validate(mirror_status)
39+
40+
# Object path: MirrorStatus (or compatible) with is_empty()
41+
if hasattr(mirror_status, "is_empty") and mirror_status.is_empty():
42+
return
43+
44+
for plane in getattr(mirror_status, "mirror_planes", []) or []:
45+
if isinstance(plane, MirrorPlane):
46+
registry.register(plane)
47+
for mirrored_group in getattr(mirror_status, "mirrored_geometry_body_groups", []) or []:
48+
registry.register(mirrored_group)
49+
for mirrored_surface in getattr(mirror_status, "mirrored_surfaces", []) or []:
50+
registry.register(mirrored_surface)
51+
52+
2153
def expand_entity_list_in_context(
2254
entity_list,
2355
params,
@@ -129,21 +161,7 @@ def get_registry_from_params(params) -> EntityRegistry:
129161
# Register mirror entities from mirror_status so selector expansion can include mirrored types
130162
# (e.g. SurfaceSelector can expand to include MirroredSurface).
131163
mirror_status = getattr(asset_cache, "mirror_status", None)
132-
if mirror_status is None or mirror_status.is_empty():
133-
return registry
134-
135-
# pylint: disable=import-outside-toplevel
136-
from flow360.component.simulation.draft_context.mirror import MirrorPlane
137-
138-
for plane in mirror_status.mirror_planes:
139-
if isinstance(plane, MirrorPlane):
140-
registry.register(plane)
141-
142-
for mirrored_group in mirror_status.mirrored_geometry_body_groups:
143-
registry.register(mirrored_group)
144-
145-
for mirrored_surface in mirror_status.mirrored_surfaces:
146-
registry.register(mirrored_surface)
164+
_register_mirror_entities_in_registry(registry, mirror_status)
147165

148166
return registry
149167

@@ -227,4 +245,9 @@ def get_entity_info_and_registry_from_dict(params_as_dict: dict) -> tuple:
227245
entity_info = parse_entity_info_model(entity_info_dict)
228246
registry = EntityRegistry.from_entity_info(entity_info)
229247

248+
# Register mirror entities from mirror_status so selector expansion can include mirrored types
249+
# (e.g. SurfaceSelector can expand to include MirroredSurface) during validation.
250+
mirror_status_dict = asset_cache.get("mirror_status")
251+
_register_mirror_entities_in_registry(registry, mirror_status_dict)
252+
230253
return entity_info, registry

tests/simulation/framework/test_entity_dict_database.py

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,10 @@ def _load_simulation_json(relative_path: str) -> dict:
3333
class _AssetCache:
3434
"""Simple object to hold asset cache data."""
3535

36-
def __init__(self, project_entity_info, selectors):
36+
def __init__(self, project_entity_info, selectors, mirror_status=None):
3737
self.project_entity_info = project_entity_info
3838
self.selectors = selectors
39+
self.mirror_status = mirror_status
3940

4041

4142
class _DummyParams:
@@ -52,8 +53,15 @@ def __init__(self, params_dict: dict, entity_info_obj=None):
5253
entity_info_obj = parse_entity_info_model(entity_info_dict)
5354

5455
selectors = asset_cache_dict.get("selectors")
56+
mirror_status = None
57+
mirror_status_dict = asset_cache_dict.get("mirror_status")
58+
if isinstance(mirror_status_dict, dict) and mirror_status_dict:
59+
# pylint: disable=import-outside-toplevel
60+
from flow360.component.simulation.draft_context.mirror import MirrorStatus
61+
62+
mirror_status = MirrorStatus.model_validate(mirror_status_dict)
5563
self.private_attribute_asset_cache = _AssetCache(
56-
project_entity_info=entity_info_obj, selectors=selectors
64+
project_entity_info=entity_info_obj, selectors=selectors, mirror_status=mirror_status
5765
)
5866

5967
def model_dump(self, **kwargs):
@@ -89,6 +97,56 @@ def _build_simple_params_dict():
8997
}
9098

9199

100+
def _build_simple_params_dict_with_mirror_status():
101+
params_as_dict = _build_simple_params_dict()
102+
params_as_dict["private_attribute_asset_cache"]["mirror_status"] = {
103+
"mirror_planes": [
104+
{
105+
"name": "plane-1",
106+
"normal": [0, 1, 0],
107+
"center": {"value": [0, 0, 0], "units": "m"},
108+
"private_attribute_entity_type_name": "MirrorPlane",
109+
"private_attribute_id": "mirror-plane-1",
110+
}
111+
],
112+
"mirrored_geometry_body_groups": [
113+
{
114+
"name": "body-1_<mirror>",
115+
"geometry_body_group_id": "body-1",
116+
"mirror_plane_id": "mirror-plane-1",
117+
"private_attribute_entity_type_name": "MirroredGeometryBodyGroup",
118+
"private_attribute_id": "mirrored-body-1",
119+
}
120+
],
121+
"mirrored_surfaces": [
122+
{
123+
"name": "wall_<mirror>",
124+
"surface_id": "wall",
125+
"mirror_plane_id": "mirror-plane-1",
126+
"private_attribute_entity_type_name": "MirroredSurface",
127+
"private_attribute_id": "mirrored-surface-1",
128+
}
129+
],
130+
}
131+
return params_as_dict
132+
133+
134+
def test_get_registry_from_params_matches_dict_with_mirror_status():
135+
params_as_dict = _build_simple_params_dict_with_mirror_status()
136+
dummy_params = _DummyParams(params_as_dict)
137+
138+
_, dict_registry = get_entity_info_and_registry_from_dict(params_as_dict)
139+
instance_registry = get_registry_from_params(dummy_params)
140+
141+
dict_mirrored_surfaces = dict_registry.find_by_type_name("MirroredSurface")
142+
instance_mirrored_surfaces = instance_registry.find_by_type_name("MirroredSurface")
143+
assert _entity_names(dict_mirrored_surfaces) == _entity_names(instance_mirrored_surfaces)
144+
145+
dict_planes = dict_registry.find_by_type_name("MirrorPlane")
146+
instance_planes = instance_registry.find_by_type_name("MirrorPlane")
147+
assert _entity_names(dict_planes) == _entity_names(instance_planes)
148+
149+
92150
def test_get_registry_for_geometry_entity_info():
93151
"""
94152
Test get_entity_info_and_registry_from_dict with GeometryEntityInfo.

tests/simulation/services/test_entity_processing_service.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,8 @@ def test_expand_entity_list_in_context_includes_mirrored_entities_from_mirror_st
280280
)
281281
],
282282
private_attribute_asset_cache=AssetCache(
283+
use_inhouse_mesher=True,
284+
use_geometry_AI=True,
283285
project_entity_info=SurfaceMeshEntityInfo(
284286
boundaries=[Surface(name="front", private_attribute_id="s-1")]
285287
),
@@ -291,6 +293,17 @@ def test_expand_entity_list_in_context_includes_mirrored_entities_from_mirror_st
291293
),
292294
)
293295

296+
# Validate schema-level correctness (skip contextual validation since this test doesn't
297+
# provide full Case-level required fields like meshing/models/operating_condition).
298+
validated, errors, _warnings = validate_model(
299+
params_as_dict=params.model_dump(exclude_none=True),
300+
validated_by=ValidationCalledBy.LOCAL,
301+
root_item_type=None,
302+
validation_level=None,
303+
)
304+
assert errors is None
305+
assert validated is not None
306+
294307
expanded = expand_entity_list_in_context(params.outputs[0].entities, params, return_names=False)
295308
expanded_type_names = {entity.private_attribute_entity_type_name for entity in expanded}
296309
assert "Surface" in expanded_type_names

0 commit comments

Comments
 (0)