From 680d9468e0a924a67fd7206eb638253a91305148 Mon Sep 17 00:00:00 2001 From: piotrkluba Date: Tue, 7 Oct 2025 16:47:52 +0200 Subject: [PATCH 1/5] two level indexing of geometry with grouping for snappy --- flow360/component/geometry.py | 3 + flow360/component/simulation/entity_info.py | 66 +- .../simulation/framework/entity_registry.py | 24 +- .../meshing_param/surface_mesh_refinements.py | 4 +- flow360/component/simulation/primitives.py | 31 +- .../translator/surface_meshing_translator.py | 4 +- .../simulation.json | 3431 +++++++++++++++++ tests/simulation/asset/test_geometry.py | 68 + .../test_meshing_param_validation.py | 4 +- .../test_surface_meshing_translator.py | 116 +- 10 files changed, 3698 insertions(+), 53 deletions(-) create mode 100644 tests/data/geo-b2ca24af-f60d-4fb3-8120-c653f3e65be6/simulation.json diff --git a/flow360/component/geometry.py b/flow360/component/geometry.py index 6c9f1d071..2c2b04709 100644 --- a/flow360/component/geometry.py +++ b/flow360/component/geometry.py @@ -412,6 +412,9 @@ def group_bodies_by_tag(self, tag_name: str) -> None: "body", tag_name, self.internal_registry ) + def group_faces_for_snappy(self) -> None: + self.internal_registry = self._entity_info._group_faces_by_snappy_format(self.internal_registry) + def reset_face_grouping(self) -> None: """Reset the face grouping""" # pylint: disable=protected-access,no-member diff --git a/flow360/component/simulation/entity_info.py b/flow360/component/simulation/entity_info.py index ee60e537d..1a20f6123 100644 --- a/flow360/component/simulation/entity_info.py +++ b/flow360/component/simulation/entity_info.py @@ -24,6 +24,7 @@ GhostCircularPlane, GhostSphere, Surface, + SnappyBody ) from flow360.component.simulation.unit_system import LengthType from flow360.component.simulation.utils import BoundingBoxType, model_attribute_unlock @@ -40,6 +41,7 @@ pd.Field(discriminator="private_attribute_entity_type_name"), ] +GROUPED_SNAPPY = "Grouped with snappy name formatting." class EntityInfoModel(Flow360BaseModel, metaclass=ABCMeta): """Base model for asset entity info JSON""" @@ -142,7 +144,7 @@ class GeometryEntityInfo(EntityInfoModel): def group_in_registry( self, - entity_type_name: Literal["face", "edge", "body"], + entity_type_name: Literal["face", "edge", "body", "snappy_body"], attribute_name: str, registry: EntityRegistry, ) -> EntityRegistry: @@ -154,16 +156,32 @@ def group_in_registry( for item in entity_list: known_frozen_hashes = registry.fast_register(item, known_frozen_hashes) return registry - + + def _get_snappy_bodies( + self + ) -> List[SnappyBody]: + + snappy_body_mapping = {} + for patch in self.grouped_faces[self.face_attribute_names.index("faceId")]: + name_components = patch.name.split("::") + body_name = name_components[0] + if body_name not in snappy_body_mapping: + snappy_body_mapping[body_name] = [] + if patch not in snappy_body_mapping[body_name]: + snappy_body_mapping[body_name].append(patch) + + return [SnappyBody(name=snappy_body, stored_entities=body_entities) + for snappy_body, body_entities in snappy_body_mapping.items()] + def _get_list_of_entities( self, attribute_name: Union[str, None] = None, - entity_type_name: Literal["face", "edge", "body"] = None, - ) -> Union[List[Surface], List[Edge], List[GeometryBodyGroup]]: + entity_type_name: Literal["face", "edge", "body", "snappy_body"] = None, + ) -> Union[List[Surface], List[Edge], List[GeometryBodyGroup], List[SnappyBody]]: # Validations if entity_type_name is None: raise ValueError("Entity type name is required.") - if entity_type_name not in ["face", "edge", "body"]: + if entity_type_name not in ["face", "edge", "body", "snappy_body"]: raise ValueError( f"Invalid entity type name, expected 'body, 'face' or 'edge' but got {entity_type_name}." ) @@ -175,10 +193,12 @@ def _get_list_of_entities( entity_attribute_names = self.edge_attribute_names entity_full_list = self.grouped_edges specified_attribute_name = self.edge_group_tag - else: + elif entity_type_name == "body": entity_attribute_names = self.body_attribute_names entity_full_list = self.grouped_bodies specified_attribute_name = self.body_group_tag + else: + return self._get_snappy_bodies() # Use the supplied one if not None if attribute_name is not None: @@ -188,6 +208,8 @@ def _get_list_of_entities( if specified_attribute_name in entity_attribute_names: # pylint: disable=no-member, unsubscriptable-object return entity_full_list[entity_attribute_names.index(specified_attribute_name)] + if specified_attribute_name == GROUPED_SNAPPY: + return self._get_snappy_bodies() raise ValueError( f"The given attribute_name `{attribute_name}` is not found" @@ -199,6 +221,11 @@ def get_boundaries(self, attribute_name: str = None) -> list[Surface]: Get the full list of boundaries. If attribute_name is supplied then ignore stored face_group_tag and use supplied one. """ + if self.face_group_tag == GROUPED_SNAPPY and attribute_name is None: + all_boundaries = [] + for body in self._get_list_of_entities(attribute_name, "snappy_body"): + all_boundaries += body.stored_entities + return all_boundaries return self._get_list_of_entities(attribute_name, "face") def update_persistent_entities(self, *, asset_entity_registry: EntityRegistry) -> None: @@ -347,12 +374,39 @@ def _group_entity_by_tag( self.body_group_tag = tag_name return registry + + def _group_faces_by_snappy_format( + self, + registry: EntityRegistry = None + ): + if registry is None: + registry = EntityRegistry() + + if self.face_group_tag is not None: + existing_face_tag = self.face_group_tag + + if existing_face_tag: + if existing_face_tag != GROUPED_SNAPPY: + log.info( + f"Regrouping face entities using snappy name formatting (previous `{GROUPED_SNAPPY}`)." + ) + registry = self._reset_grouping(entity_type_name="face", registry=registry) + + registry = self.group_in_registry( + "snappy_body", attribute_name=GROUPED_SNAPPY, registry=registry + ) + + with model_attribute_unlock(self, "face_group_tag"): + self.face_group_tag = GROUPED_SNAPPY + + return registry def _reset_grouping( self, entity_type_name: Literal["face", "edge", "body"], registry: EntityRegistry ) -> EntityRegistry: if entity_type_name == "face": registry.clear(Surface) + registry.clear(SnappyBody) with model_attribute_unlock(self, "face_group_tag"): self.face_group_tag = None elif entity_type_name == "edge": diff --git a/flow360/component/simulation/framework/entity_registry.py b/flow360/component/simulation/framework/entity_registry.py index 7bcfa47a8..84899ab55 100644 --- a/flow360/component/simulation/framework/entity_registry.py +++ b/flow360/component/simulation/framework/entity_registry.py @@ -8,6 +8,28 @@ from flow360.component.simulation.framework.entity_base import EntityBase from flow360.component.utils import _naming_pattern_handler +class DoubleIndexableList(list): + def __getitem__(self, key: Union[str, slice, int]): + if isinstance(key, str): + returned_items = [] + for item in self: + try: + item_ret_value = item[key] + except KeyError: + item_ret_value = [] + except Exception: + raise ValueError(f"Trying to access something in {item} through string indexing, which is not allowed.") + if isinstance(item_ret_value, list): + returned_items += item_ret_value + else: + returned_items.append(item_ret_value) + if not returned_items: + raise ValueError( + f"No entity found in registry for parent entities: {', '.join([f'{entity.name}' for entity in self])} with given name/naming pattern: '{key}'." + ) + return returned_items + else: + return super(DoubleIndexableList, self).__getitem__(key) class EntityRegistryBucket: """By reference, a snippet of certain collection of a EntityRegistry instance that is inside the same bucket.""" @@ -121,7 +143,7 @@ def find_by_naming_pattern( Returns: List[EntityBase]: A list of entities whose names match the pattern. """ - matched_entities = [] + matched_entities = DoubleIndexableList() regex = _naming_pattern_handler(pattern=pattern) # pylint: disable=no-member for entity_list in self.internal_registry.values(): diff --git a/flow360/component/simulation/meshing_param/surface_mesh_refinements.py b/flow360/component/simulation/meshing_param/surface_mesh_refinements.py index dd0e358a0..680d1d93f 100644 --- a/flow360/component/simulation/meshing_param/surface_mesh_refinements.py +++ b/flow360/component/simulation/meshing_param/surface_mesh_refinements.py @@ -55,7 +55,7 @@ class SnappyBodyRefinement(SnappyEntityRefinement): # pylint: disable=no-member refinement_type: Literal["SnappyBodyRefinement"] = pd.Field("SnappyBodyRefinement", frozen=True) gap_resolution: Optional[LengthType.NonNegative] = pd.Field(None) - entities: List[SnappyBody] = pd.Field(alias="bodies") + entities: EntityList[SnappyBody] = pd.Field(alias="bodies") class SnappyRegionRefinement(SnappyEntityRefinement): @@ -119,7 +119,7 @@ class SnappySurfaceEdgeRefinement(Flow360BaseModel): min_elem: Optional[pd.NonNegativeInt] = pd.Field(None) min_len: Optional[LengthType.NonNegative] = pd.Field(None) included_angle: AngleType.Positive = pd.Field(150 * u.deg) - bodies: Optional[List[SnappyBody]] = pd.Field(None) + bodies: Optional[EntityList[SnappyBody]] = pd.Field(None) regions: Optional[EntityList[Surface]] = pd.Field(None) retain_on_smoothing: Optional[bool] = pd.Field(True) diff --git a/flow360/component/simulation/primitives.py b/flow360/component/simulation/primitives.py index f09eb0cda..dbce18cc5 100644 --- a/flow360/component/simulation/primitives.py +++ b/flow360/component/simulation/primitives.py @@ -34,6 +34,7 @@ get_validation_info, ) from flow360.component.types import Axis +from flow360.component.utils import _naming_pattern_handler BOUNDARY_FULL_NAME_WHEN_NOT_FOUND = "This boundary does not exist!!!" @@ -660,14 +661,40 @@ def __str__(self): return ",".join(sorted([self.pair[0].name, self.pair[1].name])) -class SnappyBody(Flow360BaseModel): +class SnappyBody(EntityBase, EntityList): """ Represents a group of faces forming a body for snappyHexMesh. Bodies and their regions are defined in the ASCII STL file by using the solid -> endsolid" keywords with a body::region naming scheme. """ - body_name: str = pd.Field() + private_attribute_registry_bucket_name: Literal["SurfaceGroupedEntityType"] = pd.Field("SurfaceGroupedEntityType", frozen=True) + private_attribute_entity_type_name: Literal["SnappyBody"] = pd.Field("SnappyBody", frozen=True) + + stored_entities: List[Surface] = pd.Field() + + @pd.model_validator(mode="before") + @classmethod + def _format_input_to_list(cls, input_data: dict): + """ + Flatten List[EntityBase] and put into stored_entities. Do not delete other args. + """ + if not isinstance(input_data, dict): + raise ValueError("Wrong definition for SnappyBody, should be a dictionary.") + input_data.update(super()._format_input_to_list(input_data)) + return input_data + + def __getitem__(self, key: str): + if len(self.stored_entities) == 1 and ("::" not in self.stored_entities[0].name): + regex = _naming_pattern_handler(pattern=key) + else: + regex = _naming_pattern_handler(pattern=f"{self.name}::{key}") + + matched_surfaces = [entity for entity in self.stored_entities if regex.match(entity.name)] + if not matched_surfaces: + print(key) + raise KeyError(f"No entity found in registry for parent entity: {self.name} with given name/naming pattern: '{key}'.") + return matched_surfaces @final diff --git a/flow360/component/simulation/translator/surface_meshing_translator.py b/flow360/component/simulation/translator/surface_meshing_translator.py index 567f88fb2..b2daee570 100644 --- a/flow360/component/simulation/translator/surface_meshing_translator.py +++ b/flow360/component/simulation/translator/surface_meshing_translator.py @@ -83,7 +83,7 @@ def apply_SnappyBodyRefinement(refinement: SnappyBodyRefinement, translated): """ Translate SnappyBodyRefinement to bodies. """ - applicable_bodies = [entity.body_name for entity in refinement.entities] + applicable_bodies = [entity.name for entity in refinement.entities.stored_entities] for body in translated["geometry"]["bodies"]: if body["bodyName"] in applicable_bodies: if refinement.gap_resolution is not None: @@ -142,7 +142,7 @@ def apply_SnappySurfaceEdgeRefinement( else: edges["edgeSpacing"] = refinement.spacing.value.item() applicable_bodies = ( - [entity.body_name for entity in refinement.bodies] if refinement.bodies is not None else [] + [entity.name for entity in refinement.bodies.stored_entities] if refinement.bodies is not None else [] ) applicable_regions = get_applicable_regions_dict(refinement_regions=refinement.regions) for body in translated["geometry"]["bodies"]: diff --git a/tests/data/geo-b2ca24af-f60d-4fb3-8120-c653f3e65be6/simulation.json b/tests/data/geo-b2ca24af-f60d-4fb3-8120-c653f3e65be6/simulation.json new file mode 100644 index 000000000..6055a469e --- /dev/null +++ b/tests/data/geo-b2ca24af-f60d-4fb3-8120-c653f3e65be6/simulation.json @@ -0,0 +1,3431 @@ +{ + "version": "25.8.0b3", + "unit_system": { + "name": "SI" + }, + "meshing": { + "type": "MeshingParams", + "refinement_factor": 1.0, + "gap_treatment_strength": 0.0, + "defaults": { + "geometry_accuracy": { + "value": 10.0, + "units": "mm" + }, + "surface_edge_growth_rate": 1.2, + "boundary_layer_growth_rate": 1.2, + "planar_face_tolerance": 1e-06, + "surface_max_aspect_ratio": 10.0, + "surface_max_adaptation_iterations": 50, + "curvature_resolution_angle": { + "value": 12.0, + "units": "degree" + }, + "preserve_thin_geometry": false + }, + "refinements": [], + "volume_zones": [ + { + "type": "AutomatedFarfield", + "name": "Farfield", + "method": "auto" + } + ] + }, + "reference_geometry": { + "moment_center": { + "value": [ + 0.0, + 0.0, + 0.0 + ], + "units": "mm" + }, + "moment_length": { + "value": [ + 1.0, + 1.0, + 1.0 + ], + "units": "mm" + }, + "area": { + "type_name": "number", + "value": 1.0, + "units": "mm**2" + } + }, + "operating_condition": { + "type_name": "AerospaceCondition", + "private_attribute_constructor": "default", + "private_attribute_input_cache": { + "alpha": { + "value": 0.0, + "units": "degree" + }, + "beta": { + "value": 0.0, + "units": "degree" + }, + "thermal_state": { + "type_name": "ThermalState", + "private_attribute_constructor": "default", + "private_attribute_input_cache": {}, + "temperature": { + "value": 288.15, + "units": "K" + }, + "density": { + "value": 1.225, + "units": "kg/m**3" + }, + "material": { + "type": "air", + "name": "air", + "dynamic_viscosity": { + "reference_viscosity": { + "value": 1.716e-05, + "units": "Pa*s" + }, + "reference_temperature": { + "value": 273.15, + "units": "K" + }, + "effective_temperature": { + "value": 110.4, + "units": "K" + } + } + } + } + }, + "alpha": { + "value": 0.0, + "units": "degree" + }, + "beta": { + "value": 0.0, + "units": "degree" + }, + "thermal_state": { + "type_name": "ThermalState", + "private_attribute_constructor": "default", + "private_attribute_input_cache": {}, + "temperature": { + "value": 288.15, + "units": "K" + }, + "density": { + "value": 1.225, + "units": "kg/m**3" + }, + "material": { + "type": "air", + "name": "air", + "dynamic_viscosity": { + "reference_viscosity": { + "value": 1.716e-05, + "units": "Pa*s" + }, + "reference_temperature": { + "value": 273.15, + "units": "K" + }, + "effective_temperature": { + "value": 110.4, + "units": "K" + } + } + } + } + }, + "models": [ + { + "type": "Wall", + "entities": { + "stored_entities": [ + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "name": "*", + "private_attribute_sub_components": [] + } + ] + }, + "name": "Wall", + "use_wall_function": false, + "heat_spec": { + "value": { + "value": 0.0, + "units": "W/m**2" + }, + "type_name": "HeatFlux" + }, + "roughness_height": { + "value": 0.0, + "units": "mm" + } + }, + { + "type": "Freestream", + "entities": { + "stored_entities": [ + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "GhostSurface", + "name": "farfield" + } + ] + }, + "name": "Freestream" + }, + { + "material": { + "type": "air", + "name": "air", + "dynamic_viscosity": { + "reference_viscosity": { + "value": 1.716e-05, + "units": "Pa*s" + }, + "reference_temperature": { + "value": 273.15, + "units": "K" + }, + "effective_temperature": { + "value": 110.4, + "units": "K" + } + } + }, + "initial_condition": { + "type_name": "NavierStokesInitialCondition", + "rho": "rho", + "u": "u", + "v": "v", + "w": "w", + "p": "p" + }, + "type": "Fluid", + "navier_stokes_solver": { + "absolute_tolerance": 1e-10, + "relative_tolerance": 0.0, + "order_of_accuracy": 2, + "equation_evaluation_frequency": 1, + "linear_solver": { + "max_iterations": 30 + }, + "CFL_multiplier": 1.0, + "kappa_MUSCL": -1.0, + "numerical_dissipation_factor": 1.0, + "limit_velocity": false, + "limit_pressure_density": false, + "type_name": "Compressible", + "low_mach_preconditioner": false, + "update_jacobian_frequency": 4, + "max_force_jac_update_physical_steps": 0 + }, + "turbulence_model_solver": { + "absolute_tolerance": 1e-08, + "relative_tolerance": 0.0, + "order_of_accuracy": 2, + "equation_evaluation_frequency": 4, + "linear_solver": { + "max_iterations": 20 + }, + "CFL_multiplier": 2.0, + "type_name": "SpalartAllmaras", + "reconstruction_gradient_limiter": 0.5, + "quadratic_constitutive_relation": false, + "modeling_constants": { + "type_name": "SpalartAllmarasConsts", + "C_DES": 0.72, + "C_d": 8.0, + "C_cb1": 0.1355, + "C_cb2": 0.622, + "C_sigma": 0.6666666666666666, + "C_v1": 7.1, + "C_vonKarman": 0.41, + "C_w2": 0.3, + "C_w4": 0.21, + "C_w5": 1.5, + "C_t3": 1.2, + "C_t4": 0.5, + "C_min_rd": 10.0 + }, + "update_jacobian_frequency": 4, + "max_force_jac_update_physical_steps": 0, + "rotation_correction": false, + "low_reynolds_correction": false + }, + "transition_model_solver": { + "type_name": "None" + } + } + ], + "time_stepping": { + "type_name": "Steady", + "max_steps": 2000, + "CFL": { + "type": "adaptive", + "min": 0.1, + "max": 10000.0, + "max_relative_change": 1.0, + "convergence_limiting_factor": 0.25 + } + }, + "user_defined_fields": [], + "outputs": [ + { + "output_fields": { + "items": [ + "Cp", + "yPlus", + "Cf", + "CfVec" + ] + }, + "private_attribute_id": "806b10fa-b888-4208-9e19-badec6aa42c2", + "frequency": -1, + "frequency_offset": 0, + "output_format": "paraview", + "name": "Surface output", + "entities": { + "stored_entities": [ + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "name": "*", + "private_attribute_sub_components": [] + } + ] + }, + "write_single_file": false, + "output_type": "SurfaceOutput" + } + ], + "private_attribute_asset_cache": { + "project_length_unit": { + "value": 1.0, + "units": "mm" + }, + "use_inhouse_mesher": false, + "use_geometry_AI": false, + "project_entity_info": { + "draft_entities": [], + "ghost_entities": [ + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "GhostSphere", + "private_attribute_id": "farfield", + "name": "farfield", + "private_attribute_full_name": null, + "center": [ + 0, + 0, + 0 + ], + "max_radius": 7684678.07 + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "GhostCircularPlane", + "private_attribute_id": "symmetric-1", + "name": "symmetric-1", + "private_attribute_full_name": null, + "center": [ + 25647.3643, + -25600.0, + 9886.287672999999 + ], + "max_radius": 7684678.07, + "normal_axis": [ + 0, + -1, + 0 + ] + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "GhostCircularPlane", + "private_attribute_id": "symmetric-2", + "name": "symmetric-2", + "private_attribute_full_name": null, + "center": [ + 25647.3643, + 25600.0, + 9886.287672999999 + ], + "max_radius": 7684678.07, + "normal_axis": [ + 0, + 1, + 0 + ] + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "GhostCircularPlane", + "private_attribute_id": "symmetric", + "name": "symmetric", + "private_attribute_full_name": null, + "center": [ + 25647.3643, + 0, + 9886.287672999999 + ], + "max_radius": 7684678.07, + "normal_axis": [ + 0, + 1, + 0 + ] + } + ], + "type_name": "GeometryEntityInfo", + "body_ids": [ + "f1_no_rad_frame.stl" + ], + "body_attribute_names": [ + "bodyId", + "groupByFile" + ], + "grouped_bodies": [ + [ + { + "private_attribute_registry_bucket_name": "GeometryBodyGroupEntityType", + "private_attribute_entity_type_name": "GeometryBodyGroup", + "private_attribute_id": "f1_no_rad_frame.stl", + "name": "f1_no_rad_frame.stl", + "private_attribute_tag_key": "bodyId", + "private_attribute_sub_components": [ + "f1_no_rad_frame.stl" + ], + "private_attribute_color": null, + "transformation": { + "type_name": "BodyGroupTransformation", + "origin": { + "value": [ + 0, + 0, + 0 + ], + "units": "m" + }, + "axis_of_rotation": [ + 1.0, + 0.0, + 0.0 + ], + "angle_of_rotation": { + "value": 0.0, + "units": "degree" + }, + "scale": [ + 1.0, + 1.0, + 1.0 + ], + "translation": { + "value": [ + 0, + 0, + 0 + ], + "units": "m" + }, + "private_attribute_matrix": null + }, + "mesh_exterior": true + } + ], + [ + { + "private_attribute_registry_bucket_name": "GeometryBodyGroupEntityType", + "private_attribute_entity_type_name": "GeometryBodyGroup", + "private_attribute_id": "f1_no_rad_frame.stl", + "name": "f1_no_rad_frame.stl", + "private_attribute_tag_key": "groupByFile", + "private_attribute_sub_components": [ + "f1_no_rad_frame.stl" + ], + "private_attribute_color": null, + "transformation": { + "type_name": "BodyGroupTransformation", + "origin": { + "value": [ + 0, + 0, + 0 + ], + "units": "m" + }, + "axis_of_rotation": [ + 1.0, + 0.0, + 0.0 + ], + "angle_of_rotation": { + "value": 0.0, + "units": "degree" + }, + "scale": [ + 1.0, + 1.0, + 1.0 + ], + "translation": { + "value": [ + 0, + 0, + 0 + ], + "units": "m" + }, + "private_attribute_matrix": null + }, + "mesh_exterior": true + } + ] + ], + "face_ids": [ + "body", + "body-aero-camera", + "body-camera", + "body-front-af", + "body-helmet", + "body-inner-nlyr", + "body-main-rad-duct", + "body-mirror", + "body-misc", + "body-nose", + "body-pit", + "body-rear-crash-bar", + "body-steering-wheel", + "diffuser", + "eb-engine", + "eb-exhaust", + "fr-int-brake-duct-lhs", + "fr-int-brake-duct-rhs", + "fr-int-caketin-lhs", + "fr-int-caketin-rhs", + "fr-int-winglet-lhs", + "fr-int-winglet-rhs", + "fr-susp-lwb-lhs", + "fr-susp-lwb-rhs", + "fr-susp-prod-lhs", + "fr-susp-prod-rhs", + "fr-susp-trod-lhs", + "fr-susp-trod-rhs", + "fr-susp-twb-lhs", + "fr-susp-twb-rhs", + "fr-wh-brake-disc-lhs", + "fr-wh-brake-disc-rhs", + "fr-wh-cover-OB-lhs", + "fr-wh-cover-OB-rhs", + "fr-wh-plinth-lhs", + "fr-wh-plinth-rhs", + "fr-wh-rim-lhs", + "fr-wh-rim-rhs", + "fr-wh-tyre-lhs", + "fr-wh-tyre-rhs", + "fw-brackets", + "fw-ep", + "fw-fine", + "fw-flap-1", + "fw-flap-2", + "fw-mp", + "rr-int-brake-duct-lhs", + "rr-int-brake-duct-rhs", + "rr-int-caketin-lhs", + "rr-int-caketin-rhs", + "rr-int-vanes-lhs", + "rr-int-vanes-rhs", + "rr-susp-lwb-lhs", + "rr-susp-lwb-rhs", + "rr-susp-prod-lhs", + "rr-susp-prod-rhs", + "rr-susp-trod-lhs", + "rr-susp-trod-rhs", + "rr-susp-twb-lhs", + "rr-susp-twb-rhs", + "rr-wh-brake-disc-lhs", + "rr-wh-brake-disc-rhs", + "rr-wh-cover-OB-lhs", + "rr-wh-cover-OB-rhs", + "rr-wh-plinth-lhs", + "rr-wh-plinth-rhs", + "rr-wh-rim-lhs", + "rr-wh-rim-rhs", + "rr-wh-tyre-lhs", + "rr-wh-tyre-rhs", + "rw-ep", + "rw-flap", + "rw-gf", + "rw-mp", + "rw-pillar", + "toint-rad-main-lhs-down", + "toint-rad-main-lhs-up", + "toint-rad-main-rhs-down", + "toint-rad-main-rhs-up", + "tunnel::ground", + "tunnel::inlet", + "tunnel::outlet", + "tunnel::sides", + "tunnel::top", + "uf", + "uf-strakes", + "velocity-inlet-exhaust-out", + "velocity-inlet-fr-int-duct-lhs-out", + "velocity-inlet-fr-int-duct-rhs-out", + "velocity-inlet-rr-int-duct-lhs-out", + "velocity-inlet-rr-int-duct-rhs-out", + "velocity-outlet-engine-intake-in", + "velocity-outlet-fr-int-duct-lhs-in", + "velocity-outlet-fr-int-duct-rhs-in", + "velocity-outlet-rr-int-duct-lhs-in", + "velocity-outlet-rr-int-duct-rhs-in" + ], + "face_attribute_names": [ + "groupByBodyId", + "faceId" + ], + "grouped_faces": [ + [ + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "f1_no_rad_frame.stl", + "name": "f1_no_rad_frame.stl", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "groupByBodyId", + "private_attribute_sub_components": [ + "body", + "body-aero-camera", + "body-camera", + "body-front-af", + "body-helmet", + "body-inner-nlyr", + "body-main-rad-duct", + "body-mirror", + "body-misc", + "body-nose", + "body-pit", + "body-rear-crash-bar", + "body-steering-wheel", + "diffuser", + "eb-engine", + "eb-exhaust", + "fr-int-brake-duct-lhs", + "fr-int-brake-duct-rhs", + "fr-int-caketin-lhs", + "fr-int-caketin-rhs", + "fr-int-winglet-lhs", + "fr-int-winglet-rhs", + "fr-susp-lwb-lhs", + "fr-susp-lwb-rhs", + "fr-susp-prod-lhs", + "fr-susp-prod-rhs", + "fr-susp-trod-lhs", + "fr-susp-trod-rhs", + "fr-susp-twb-lhs", + "fr-susp-twb-rhs", + "fr-wh-brake-disc-lhs", + "fr-wh-brake-disc-rhs", + "fr-wh-cover-OB-lhs", + "fr-wh-cover-OB-rhs", + "fr-wh-plinth-lhs", + "fr-wh-plinth-rhs", + "fr-wh-rim-lhs", + "fr-wh-rim-rhs", + "fr-wh-tyre-lhs", + "fr-wh-tyre-rhs", + "fw-brackets", + "fw-ep", + "fw-fine", + "fw-flap-1", + "fw-flap-2", + "fw-mp", + "rr-int-brake-duct-lhs", + "rr-int-brake-duct-rhs", + "rr-int-caketin-lhs", + "rr-int-caketin-rhs", + "rr-int-vanes-lhs", + "rr-int-vanes-rhs", + "rr-susp-lwb-lhs", + "rr-susp-lwb-rhs", + "rr-susp-prod-lhs", + "rr-susp-prod-rhs", + "rr-susp-trod-lhs", + "rr-susp-trod-rhs", + "rr-susp-twb-lhs", + "rr-susp-twb-rhs", + "rr-wh-brake-disc-lhs", + "rr-wh-brake-disc-rhs", + "rr-wh-cover-OB-lhs", + "rr-wh-cover-OB-rhs", + "rr-wh-plinth-lhs", + "rr-wh-plinth-rhs", + "rr-wh-rim-lhs", + "rr-wh-rim-rhs", + "rr-wh-tyre-lhs", + "rr-wh-tyre-rhs", + "rw-ep", + "rw-flap", + "rw-gf", + "rw-mp", + "rw-pillar", + "toint-rad-main-lhs-down", + "toint-rad-main-lhs-up", + "toint-rad-main-rhs-down", + "toint-rad-main-rhs-up", + "tunnel::ground", + "tunnel::inlet", + "tunnel::outlet", + "tunnel::sides", + "tunnel::top", + "uf", + "uf-strakes", + "velocity-inlet-exhaust-out", + "velocity-inlet-fr-int-duct-lhs-out", + "velocity-inlet-fr-int-duct-rhs-out", + "velocity-inlet-rr-int-duct-lhs-out", + "velocity-inlet-rr-int-duct-rhs-out", + "velocity-outlet-engine-intake-in", + "velocity-outlet-fr-int-duct-lhs-in", + "velocity-outlet-fr-int-duct-rhs-in", + "velocity-outlet-rr-int-duct-lhs-in", + "velocity-outlet-rr-int-duct-rhs-in" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + -51199.4164, + -25600.0, + -479.571754 + ], + [ + 102494.145, + 25600.0, + 20252.1471 + ] + ] + } + } + ], + [ + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "rr-wh-brake-disc-lhs", + "name": "rr-wh-brake-disc-lhs", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "rr-wh-brake-disc-lhs" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + 3528.53613, + -804.524292, + 224.419952 + ], + [ + 3768.73926, + -767.610718, + 465.21582 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "fr-int-winglet-lhs", + "name": "fr-int-winglet-lhs", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "fr-int-winglet-lhs" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + 130.478043, + -905.53125, + 507.116272 + ], + [ + 275.868744, + -586.723389, + 767.907349 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "fr-int-brake-duct-lhs", + "name": "fr-int-brake-duct-lhs", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "fr-int-brake-duct-lhs" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + -18.4428139, + -739.646606, + 265.103973 + ], + [ + 404.780579, + -504.262573, + 468.704041 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "fr-int-caketin-lhs", + "name": "fr-int-caketin-lhs", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "fr-int-caketin-lhs" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + -47.7378082, + -675.550354, + 103.112488 + ], + [ + 451.499847, + -605.055359, + 603.143921 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "fr-int-winglet-rhs", + "name": "fr-int-winglet-rhs", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "fr-int-winglet-rhs" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + 130.478043, + 586.723389, + 507.116272 + ], + [ + 275.868744, + 905.53125, + 767.907349 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "fr-int-brake-duct-rhs", + "name": "fr-int-brake-duct-rhs", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "fr-int-brake-duct-rhs" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + -18.4428139, + 504.262573, + 265.103973 + ], + [ + 404.780579, + 739.646606, + 468.704041 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "fr-int-caketin-rhs", + "name": "fr-int-caketin-rhs", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "fr-int-caketin-rhs" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + -47.7378082, + 605.055359, + 103.112488 + ], + [ + 451.499847, + 675.550354, + 603.143921 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "rr-wh-brake-disc-rhs", + "name": "rr-wh-brake-disc-rhs", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "rr-wh-brake-disc-rhs" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + 3528.53613, + 767.610718, + 224.419952 + ], + [ + 3768.73926, + 804.524292, + 465.21582 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "rr-wh-rim-rhs", + "name": "rr-wh-rim-rhs", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "rr-wh-rim-rhs" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + 3613.97632, + 927.772949, + 314.425476 + ], + [ + 3681.96411, + 989.407898, + 382.387024 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "rr-wh-tyre-rhs", + "name": "rr-wh-tyre-rhs", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "rr-wh-tyre-rhs" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + 3285.27051, + 524.285645, + -19.0590782 + ], + [ + 4010.59131, + 1006.66284, + 706.757629 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "rr-wh-cover-OB-rhs", + "name": "rr-wh-cover-OB-rhs", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "rr-wh-cover-OB-rhs" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + 3398.85815, + 947.596863, + 103.490883 + ], + [ + 3895.13379, + 1007.65698, + 599.763306 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "rr-susp-prod-lhs", + "name": "rr-susp-prod-lhs", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "rr-susp-prod-lhs" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + 3492.23975, + -638.711853, + 204.865982 + ], + [ + 3660.65845, + -109.020988, + 336.563782 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "rr-susp-trod-lhs", + "name": "rr-susp-trod-lhs", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "rr-susp-trod-lhs" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + 3638.37134, + -588.224548, + 278.633698 + ], + [ + 3772.90381, + -67.5311737, + 381.919067 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "rr-susp-prod-rhs", + "name": "rr-susp-prod-rhs", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "rr-susp-prod-rhs" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + 3492.23975, + 109.020988, + 204.865982 + ], + [ + 3660.65845, + 638.711853, + 336.563782 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "rr-susp-trod-rhs", + "name": "rr-susp-trod-rhs", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "rr-susp-trod-rhs" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + 3638.37134, + 67.5311737, + 278.633698 + ], + [ + 3772.90381, + 588.224548, + 381.919067 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "rr-susp-lwb-rhs", + "name": "rr-susp-lwb-rhs", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "rr-susp-lwb-rhs" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + 3087.22388, + 57.8537598, + 185.083481 + ], + [ + 3835.37451, + 657.063843, + 346.585876 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "rr-susp-twb-rhs", + "name": "rr-susp-twb-rhs", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "rr-susp-twb-rhs" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + 3231.52783, + 87.1017914, + 320.16684 + ], + [ + 3737.80615, + 577.314026, + 411.765198 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "rr-wh-rim-lhs", + "name": "rr-wh-rim-lhs", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "rr-wh-rim-lhs" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + 3613.97632, + -989.407898, + 314.425476 + ], + [ + 3681.96411, + -927.772949, + 382.387024 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "rr-wh-cover-OB-lhs", + "name": "rr-wh-cover-OB-lhs", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "rr-wh-cover-OB-lhs" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + 3398.85815, + -1007.65698, + 103.490883 + ], + [ + 3895.13379, + -947.596863, + 599.763306 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "rr-wh-tyre-lhs", + "name": "rr-wh-tyre-lhs", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "rr-wh-tyre-lhs" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + 3285.27051, + -1006.66284, + -19.0590782 + ], + [ + 4010.59131, + -524.285645, + 706.757629 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "rr-susp-twb-lhs", + "name": "rr-susp-twb-lhs", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "rr-susp-twb-lhs" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + 3231.52783, + -577.314026, + 320.16684 + ], + [ + 3737.80615, + -87.1017914, + 411.765198 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "rr-susp-lwb-lhs", + "name": "rr-susp-lwb-lhs", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "rr-susp-lwb-lhs" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + 3087.22388, + -657.063843, + 185.083481 + ], + [ + 3835.37451, + -57.8537598, + 346.585876 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "velocity-outlet-fr-int-duct-lhs-in", + "name": "velocity-outlet-fr-int-duct-lhs-in", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "velocity-outlet-fr-int-duct-lhs-in" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + -18.2780266, + -624.477478, + 294.479309 + ], + [ + -18.0379314, + -549.215576, + 409.860748 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "velocity-outlet-rr-int-duct-lhs-in", + "name": "velocity-outlet-rr-int-duct-lhs-in", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "velocity-outlet-rr-int-duct-lhs-in" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + 3381.08179, + -506.241455, + 339.74649 + ], + [ + 3386.45068, + -458.97641, + 500.824493 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "velocity-inlet-rr-int-duct-lhs-out", + "name": "velocity-inlet-rr-int-duct-lhs-out", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "velocity-inlet-rr-int-duct-lhs-out" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + 3795.29932, + -536.357239, + 356.644897 + ], + [ + 3890.96411, + -509.965546, + 524.550781 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "velocity-outlet-rr-int-duct-rhs-in", + "name": "velocity-outlet-rr-int-duct-rhs-in", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "velocity-outlet-rr-int-duct-rhs-in" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + 3381.08179, + 458.97641, + 339.74649 + ], + [ + 3386.45068, + 506.241455, + 500.824493 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "velocity-inlet-rr-int-duct-rhs-out", + "name": "velocity-inlet-rr-int-duct-rhs-out", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "velocity-inlet-rr-int-duct-rhs-out" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + 3795.29932, + 509.965546, + 356.644897 + ], + [ + 3890.96411, + 536.357239, + 524.550781 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "velocity-inlet-fr-int-duct-lhs-out", + "name": "velocity-inlet-fr-int-duct-lhs-out", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "velocity-inlet-fr-int-duct-lhs-out" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + 387.942505, + -617.122375, + 295.317963 + ], + [ + 404.739441, + -601.971924, + 417.000702 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "velocity-outlet-fr-int-duct-rhs-in", + "name": "velocity-outlet-fr-int-duct-rhs-in", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "velocity-outlet-fr-int-duct-rhs-in" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + -18.2780266, + 549.215576, + 294.479309 + ], + [ + -18.0379314, + 624.477478, + 409.860748 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "velocity-inlet-fr-int-duct-rhs-out", + "name": "velocity-inlet-fr-int-duct-rhs-out", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "velocity-inlet-fr-int-duct-rhs-out" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + 387.942505, + 601.971924, + 295.317963 + ], + [ + 404.739441, + 617.122375, + 417.000702 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "rr-int-vanes-lhs", + "name": "rr-int-vanes-lhs", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "rr-int-vanes-lhs" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + 3540.86426, + -588.80304, + 19.7100334 + ], + [ + 4001.40381, + -410.7836, + 346.564362 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "rr-int-caketin-lhs", + "name": "rr-int-caketin-lhs", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "rr-int-caketin-lhs" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + 3404.07007, + -544.245911, + 88.9916992 + ], + [ + 3900.88843, + -524.285645, + 585.785583 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "rr-int-brake-duct-lhs", + "name": "rr-int-brake-duct-lhs", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "rr-int-brake-duct-lhs" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + 3380.96558, + -596.996582, + 336.769257 + ], + [ + 3891.61572, + -456.024445, + 538.573975 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "rr-int-vanes-rhs", + "name": "rr-int-vanes-rhs", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "rr-int-vanes-rhs" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + 3540.86426, + 410.7836, + 19.7100334 + ], + [ + 4001.40381, + 588.80304, + 346.564362 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "rr-int-caketin-rhs", + "name": "rr-int-caketin-rhs", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "rr-int-caketin-rhs" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + 3404.07007, + 524.285645, + 88.9916992 + ], + [ + 3900.88843, + 544.245911, + 585.785583 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "rr-int-brake-duct-rhs", + "name": "rr-int-brake-duct-rhs", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "rr-int-brake-duct-rhs" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + 3380.96558, + 456.024445, + 336.769257 + ], + [ + 3891.61572, + 596.996582, + 538.573975 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "fr-susp-trod-lhs", + "name": "fr-susp-trod-lhs", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "fr-susp-trod-lhs" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + -32.343914, + -659.824585, + 342.139618 + ], + [ + 136.061615, + -127.707825, + 418.902679 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "fr-wh-brake-disc-lhs", + "name": "fr-wh-brake-disc-lhs", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "fr-wh-brake-disc-lhs" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + 82.6637268, + -844.129395, + 243.654556 + ], + [ + 322.747253, + -800.744324, + 484.96344 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "fr-wh-rim-lhs", + "name": "fr-wh-rim-lhs", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "fr-wh-rim-lhs" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + 170.600281, + -991.287476, + 338.5466 + ], + [ + 238.547424, + -928.948364, + 406.453278 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "fr-susp-prod-lhs", + "name": "fr-susp-prod-lhs", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "fr-susp-prod-lhs" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + 233.320587, + -647.498047, + 364.649658 + ], + [ + 341.080322, + -121.885841, + 547.665649 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "fr-wh-tyre-lhs", + "name": "fr-wh-tyre-lhs", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "fr-wh-tyre-lhs" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + -162.240814, + -1008.24963, + -2.54475045 + ], + [ + 567.570251, + -608.473816, + 729.472351 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "fr-wh-cover-OB-lhs", + "name": "fr-wh-cover-OB-lhs", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "fr-wh-cover-OB-lhs" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + -40.2233124, + -1009.67889, + 129.541199 + ], + [ + 449.751495, + -939.006958, + 618.820923 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "fr-susp-twb-lhs", + "name": "fr-susp-twb-lhs", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "fr-susp-twb-lhs" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + 65.1378555, + -705.414734, + 470.525513 + ], + [ + 674.463806, + -146.351547, + 523.297974 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "fr-susp-lwb-lhs", + "name": "fr-susp-lwb-lhs", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "fr-susp-lwb-lhs" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + 21.9377422, + -696.497864, + 311.893402 + ], + [ + 631.25531, + -137.314163, + 359.434753 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "fr-wh-brake-disc-rhs", + "name": "fr-wh-brake-disc-rhs", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "fr-wh-brake-disc-rhs" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + 82.6637268, + 800.744324, + 243.654556 + ], + [ + 322.747253, + 844.129395, + 484.96344 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "fr-wh-rim-rhs", + "name": "fr-wh-rim-rhs", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "fr-wh-rim-rhs" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + 170.600281, + 928.948364, + 338.5466 + ], + [ + 238.547424, + 991.287476, + 406.453278 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "fr-wh-tyre-rhs", + "name": "fr-wh-tyre-rhs", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "fr-wh-tyre-rhs" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + -162.240814, + 608.473816, + -2.54475045 + ], + [ + 567.570251, + 1008.24963, + 729.472351 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "fr-wh-cover-OB-rhs", + "name": "fr-wh-cover-OB-rhs", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "fr-wh-cover-OB-rhs" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + -40.2233124, + 939.006958, + 129.541199 + ], + [ + 449.751495, + 1009.67889, + 618.820923 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "fr-susp-twb-rhs", + "name": "fr-susp-twb-rhs", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "fr-susp-twb-rhs" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + 65.1378555, + 146.351547, + 470.525513 + ], + [ + 674.463806, + 705.414734, + 523.297974 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "fr-susp-lwb-rhs", + "name": "fr-susp-lwb-rhs", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "fr-susp-lwb-rhs" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + 21.9377422, + 137.314163, + 311.893402 + ], + [ + 631.25531, + 696.497864, + 359.434753 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "fr-susp-trod-rhs", + "name": "fr-susp-trod-rhs", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "fr-susp-trod-rhs" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + -32.343914, + 127.707825, + 342.139618 + ], + [ + 136.061615, + 659.824585, + 418.902679 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "fr-susp-prod-rhs", + "name": "fr-susp-prod-rhs", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "fr-susp-prod-rhs" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + 233.320587, + 121.885841, + 364.649658 + ], + [ + 341.080322, + 647.498047, + 547.665649 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "body-front-af", + "name": "body-front-af", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "body-front-af" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + -286.884003, + -257.778931, + 471.992004 + ], + [ + -27.3599186, + 257.940063, + 515.808105 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "body-mirror", + "name": "body-mirror", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "body-mirror" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + 1217.56311, + -624.887695, + 630.0 + ], + [ + 1416.33704, + 624.748535, + 733.204956 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "body-rear-crash-bar", + "name": "body-rear-crash-bar", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "body-rear-crash-bar" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + 3075.84814, + -124.042969, + 51.6927795 + ], + [ + 4238.66211, + 123.92334, + 505.511597 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "body-nose", + "name": "body-nose", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "body-nose" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + -1110.39441, + -213.967285, + 171.416901 + ], + [ + 937.599487, + 213.967285, + 630.508484 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "body-pit", + "name": "body-pit", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "body-pit" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + 93.6389999, + -307.56897, + 65.4090576 + ], + [ + 1986.84497, + 307.449341, + 688.054932 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "body-misc", + "name": "body-misc", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "body-misc" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + 47.7120705, + -32.9937744, + 364.313019 + ], + [ + 3589.21216, + 32.8741455, + 837.305603 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "body-aero-camera", + "name": "body-aero-camera", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "body-aero-camera" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + 1990.12012, + -131.107178, + 972.437866 + ], + [ + 2212.55811, + 131.107788, + 1077.61609 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "body", + "name": "body", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "body" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + 668.940002, + -709.276917, + 146.790924 + ], + [ + 3574.80249, + 709.276917, + 977.956787 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "body-camera", + "name": "body-camera", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "body-camera" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + 2074.57397, + -234.837036, + 888.095337 + ], + [ + 2255.86377, + 234.837036, + 932.726624 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "body-inner-nlyr", + "name": "body-inner-nlyr", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "body-inner-nlyr" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + 1665.6521, + -706.27948, + 160.300827 + ], + [ + 3573.76782, + 706.27948, + 729.13739 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "body-steering-wheel", + "name": "body-steering-wheel", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "body-steering-wheel" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + 1198.47046, + -138.332077, + 505.832886 + ], + [ + 1331.07812, + 144.985077, + 661.674988 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "body-helmet", + "name": "body-helmet", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "body-helmet" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + 1565.5127, + -140.424026, + 608.354309 + ], + [ + 1921.86829, + 127.111374, + 888.247375 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "diffuser", + "name": "diffuser", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "diffuser" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + 2644.19019, + -720.440308, + 21.2196312 + ], + [ + 3991.97485, + 720.440308, + 348.488983 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "eb-exhaust", + "name": "eb-exhaust", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "eb-exhaust" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + 3468.92798, + -52.4777527, + 389.730377 + ], + [ + 4170.2749, + 52.4777527, + 550.748718 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "velocity-inlet-exhaust-out", + "name": "velocity-inlet-exhaust-out", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "velocity-inlet-exhaust-out" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + 3954.79443, + -41.7258301, + 437.802002 + ], + [ + 3961.96875, + 41.6550102, + 520.770508 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "velocity-outlet-engine-intake-in", + "name": "velocity-outlet-engine-intake-in", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "velocity-outlet-engine-intake-in" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + 2501.02319, + -73.9224548, + 728.33667 + ], + [ + 2572.08179, + 73.9239426, + 799.237183 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "eb-engine", + "name": "eb-engine", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "eb-engine" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + 2471.24243, + -353.990387, + 96.7360992 + ], + [ + 3469.77515, + 353.990387, + 494.733337 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "fw-ep", + "name": "fw-ep", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "fw-ep" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + -751.499939, + -967.758789, + 91.8769531 + ], + [ + -313.960022, + 967.619629, + 483.095978 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "fw-fine", + "name": "fw-fine", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "fw-fine" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + -613.639099, + -997.751709, + 256.858368 + ], + [ + -339.892548, + 997.751709, + 364.61496 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "fw-brackets", + "name": "fw-brackets", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "fw-brackets" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + -811.409851, + -718.640747, + 117.748993 + ], + [ + -506.989899, + 718.501587, + 244.408997 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "fw-flap-2", + "name": "fw-flap-2", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "fw-flap-2" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + -716.65509, + -919.516602, + 123.274307 + ], + [ + -318.399963, + 919.516602, + 325.514954 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "fw-flap-1", + "name": "fw-flap-1", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "fw-flap-1" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + -868.590576, + -919.516602, + 106.23748 + ], + [ + -488.7099, + 919.516602, + 232.348755 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "fw-mp", + "name": "fw-mp", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "fw-mp" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + -1038.42041, + -898.516846, + 91.6744843 + ], + [ + -613.330017, + 898.516846, + 200.241943 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "toint-rad-main-lhs-up", + "name": "toint-rad-main-lhs-up", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "toint-rad-main-lhs-up" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + 1820.08325, + -545.423523, + 211.815872 + ], + [ + 2284.84033, + -261.048706, + 528.243774 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "body-main-rad-duct", + "name": "body-main-rad-duct", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "body-main-rad-duct" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + 1665.66272, + -595.297302, + 203.621841 + ], + [ + 2306.65625, + 595.297302, + 558.621887 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "toint-rad-main-rhs-up", + "name": "toint-rad-main-rhs-up", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "toint-rad-main-rhs-up" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + 1820.08325, + 261.048706, + 211.815872 + ], + [ + 2284.84033, + 545.423523, + 528.243774 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "toint-rad-main-rhs-down", + "name": "toint-rad-main-rhs-down", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "toint-rad-main-rhs-down" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + 1840.05518, + 254.425583, + 241.026443 + ], + [ + 2304.81226, + 538.800415, + 557.454346 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "toint-rad-main-lhs-down", + "name": "toint-rad-main-lhs-down", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "toint-rad-main-lhs-down" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + 1840.05518, + -538.800415, + 241.026443 + ], + [ + 2304.81226, + -254.425583, + 557.454346 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "rw-ep", + "name": "rw-ep", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "rw-ep" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + 3740.35718, + -633.95874, + 343.771637 + ], + [ + 4195.57812, + 633.839111, + 967.431946 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "rw-mp", + "name": "rw-mp", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "rw-mp" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + 3740.65234, + -567.830811, + 744.579773 + ], + [ + 4100.90186, + 567.711182, + 861.502075 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "rw-pillar", + "name": "rw-pillar", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "rw-pillar" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + 3637.01025, + -16.9159756, + 426.376282 + ], + [ + 4069.29688, + 16.8457813, + 908.431763 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "rw-flap", + "name": "rw-flap", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "rw-flap" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + 4057.47876, + -567.739868, + 866.495544 + ], + [ + 4195.57715, + 567.739868, + 967.418945 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "rw-gf", + "name": "rw-gf", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "rw-gf" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + 4186.35303, + -567.739868, + 966.577332 + ], + [ + 4195.57617, + 567.739868, + 973.272095 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "uf-strakes", + "name": "uf-strakes", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "uf-strakes" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + 907.636963, + -739.659058, + 30.3602257 + ], + [ + 2031.8623, + 739.659058, + 264.314636 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "uf", + "name": "uf", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "uf" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + 737.347412, + -766.421021, + 20.384058 + ], + [ + 3092.76221, + 766.421021, + 340.326324 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "tunnel::ground", + "name": "tunnel::ground", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "tunnel::ground" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + -51199.4164, + -25600.0, + -479.571754 + ], + [ + 102398.84, + 25600.0, + 252.374146 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "tunnel::inlet", + "name": "tunnel::inlet", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "tunnel::inlet" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + -51199.4164, + -25600.0, + 252.374146 + ], + [ + -51104.1109, + 25600.0, + 20252.1471 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "tunnel::outlet", + "name": "tunnel::outlet", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "tunnel::outlet" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + 102398.84, + -25600.0, + -479.571754 + ], + [ + 102494.145, + 25600.0, + 19520.2012 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "tunnel::sides", + "name": "tunnel::sides", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "tunnel::sides" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + -51199.4164, + -25600.0, + -479.571754 + ], + [ + 102494.145, + 25600.0, + 20252.1471 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "tunnel::top", + "name": "tunnel::top", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "tunnel::top" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + -51104.1109, + -25600.0, + 19520.2012 + ], + [ + 102494.145, + 25600.0, + 20252.1471 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "fr-wh-plinth-lhs", + "name": "fr-wh-plinth-lhs", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "fr-wh-plinth-lhs" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + 101.199685, + -922.789382, + -3.03971537 + ], + [ + 300.55574, + -684.894588, + 17.9096002 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "fr-wh-plinth-rhs", + "name": "fr-wh-plinth-rhs", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "fr-wh-plinth-rhs" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + 101.199685, + 684.894588, + -3.03971537 + ], + [ + 300.55574, + 922.789382, + 17.9096002 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "rr-wh-plinth-lhs", + "name": "rr-wh-plinth-lhs", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "rr-wh-plinth-lhs" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + 3547.08768, + -923.96201, + -19.4631855 + ], + [ + 3747.00768, + -589.461069, + 1.48881742 + ] + ] + } + }, + { + "private_attribute_registry_bucket_name": "SurfaceEntityType", + "private_attribute_entity_type_name": "Surface", + "private_attribute_id": "rr-wh-plinth-rhs", + "name": "rr-wh-plinth-rhs", + "private_attribute_full_name": null, + "private_attribute_is_interface": null, + "private_attribute_tag_key": "faceId", + "private_attribute_sub_components": [ + "rr-wh-plinth-rhs" + ], + "private_attribute_color": null, + "private_attributes": { + "type_name": "SurfacePrivateAttributes", + "bounding_box": [ + [ + 3547.08768, + 589.461069, + -19.4631855 + ], + [ + 3747.00768, + 923.96201, + 1.48881742 + ] + ] + } + } + ] + ], + "edge_ids": [], + "edge_attribute_names": [], + "grouped_edges": [], + "body_group_tag": "groupByFile", + "face_group_tag": "groupByBodyId", + "edge_group_tag": null, + "global_bounding_box": [ + [ + -51199.4164, + -25600.0, + -479.571754 + ], + [ + 102494.145, + 25600.0, + 20252.1471 + ] + ], + "default_geometry_accuracy": null + } + } +} \ No newline at end of file diff --git a/tests/simulation/asset/test_geometry.py b/tests/simulation/asset/test_geometry.py index aba220e4d..41a85343c 100644 --- a/tests/simulation/asset/test_geometry.py +++ b/tests/simulation/asset/test_geometry.py @@ -7,6 +7,7 @@ from flow360 import exceptions as ex from flow360.component.geometry import Geometry, GeometryMeta from flow360.component.resource_base import local_metadata_builder +from flow360.component.simulation.primitives import SnappyBody, Surface from flow360.examples import Cylinder3D assertions = unittest.TestCase("__init__") @@ -23,6 +24,29 @@ def change_test_dir(request, monkeypatch): monkeypatch.chdir(request.fspath.dirname) +@pytest.fixture +def stl_geo_meta(): + geo_meta = { + "id": "geo-b2ca24af-f60d-4fb3-8120-c653f3e65be6", + "name": "stl_mixed_convention", + "s3_path": "s3://flow360meshes-v1/users/user-id", + } + + geometry = Geometry.from_local_storage( + geometry_id=geo_meta["id"], + local_storage_path=os.path.join("../../data", geo_meta["id"]), + meta_data=GeometryMeta( + **local_metadata_builder( + id=geo_meta["id"], + name=geo_meta["name"], + cloud_path_prefix=geo_meta["s3_path"].rsplit("/", 1)[0], + status="processed", + ) + ), + ) + return geometry + + def test_draft_geometry_from_file(): with pytest.raises( ex.Flow360FileError, @@ -296,3 +320,47 @@ def test_geometry_rename_body_groups(): match=(f"No entity found in registry with given name/naming pattern: 'newAirplane_0002'."), ): assert geometry["newAirplane_0002"] + +def test_geometry_group_for_snappy(stl_geo_meta): + geo: Geometry = stl_geo_meta + + geo.group_faces_for_snappy() + + # body with one region + assert isinstance(geo["rr-wh-rim-lhs"], SnappyBody) + assert len(geo["rr-wh-rim-lhs"]["*"]) == 1 + assert isinstance(geo["rr-wh-rim-lhs"]["*"][0], Surface) + assert geo["rr-wh-rim-lhs"]["*"][0].name == "rr-wh-rim-lhs" + + # body with more regions + assert all([isinstance(region, Surface) for region in geo["tunnel"]["*"]]) + assert len(geo["tunnel"]["*"]) == 5 + + # registry wildcard + assert len(geo["uf*"]) == 2 + assert len(geo["velocity*"]) == 10 + + # double indexing with wildcard + assert len(geo["*nn*"]["*"]) == 6 + +def test_snappy_grouping_not_found_messages(stl_geo_meta): + geo: Geometry = stl_geo_meta + + geo.group_faces_for_snappy() + + with pytest.raises( + ValueError, + match=(f"No entity found in registry with given name/naming pattern: 'dummy'."), + ): + assert geo["dummy"] + + with pytest.raises( + ValueError, + match=(f"No entity found in registry for parent entities: body-inner-nlyr, tunnel with given name/naming pattern: 'dummy'."), + ): + assert geo["*nn*"]["dummy"] + + with pytest.raises( + KeyError + ): + assert geo["body-nose"]["dummy*"] \ 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 f2aa2cbfb..7a9d19330 100644 --- a/tests/simulation/params/meshing_validation/test_meshing_param_validation.py +++ b/tests/simulation/params/meshing_validation/test_meshing_param_validation.py @@ -439,7 +439,7 @@ def test_bad_refinements(): min_spacing=1 * u.mm, max_spacing=5 * u.mm, gap_resolution=0.01 * u.mm ), refinements=[ - SnappyBodyRefinement(min_spacing=6 * u.mm, bodies=[SnappyBody(body_name="bbb")]) + SnappyBodyRefinement(min_spacing=6 * u.mm, bodies=[SnappyBody(name="bbb", stored_entities=[])]) ], ) @@ -449,6 +449,6 @@ def test_bad_refinements(): min_spacing=1 * u.mm, max_spacing=5 * u.mm, gap_resolution=0.01 * u.mm ), refinements=[ - SnappyBodyRefinement(max_spacing=0.5 * u.mm, bodies=[SnappyBody(body_name="bbb")]) + SnappyBodyRefinement(max_spacing=0.5 * u.mm, bodies=[SnappyBody(name="bbb", stored_entities=[])]) ], ) diff --git a/tests/simulation/translator/test_surface_meshing_translator.py b/tests/simulation/translator/test_surface_meshing_translator.py index 204286ebc..7c90c4f24 100644 --- a/tests/simulation/translator/test_surface_meshing_translator.py +++ b/tests/simulation/translator/test_surface_meshing_translator.py @@ -134,6 +134,15 @@ def _get_meta_data(self): }, "mesh_unit": {"units": "mm", "value": 1.0}, } + elif self.fname == "tester_no_naming.stl": + return { + "surfaces": { + "body01_face001": {}, + "body01_face002": {}, + "body01_face003": {}, + }, + "mesh_unit": {"units": "mm", "value": 1.0}, + } else: raise ValueError("Invalid file name") @@ -246,7 +255,7 @@ def _get_entity_info(self): "body3::patch0", ], edge_ids=[], - face_attribute_names=["dummy"], + face_attribute_names=["faceId"], face_group_tag="dummy", grouped_faces=[ [ @@ -281,28 +290,59 @@ def _get_entity_info(self): ] ], ) + elif self.fname == "tester_no_naming.stl": + return GeometryEntityInfo( + face_ids=[ + "body01_face001", + "body01_face002", + "body01_face003" + ], + edge_ids=[], + face_attribute_names=["faceId"], + face_group_tag="faceId", + grouped_faces=[ + [ + Surface( + name="body01_face001", + private_attribute_sub_components=["body01_face001"], + ), + Surface( + name="body01_face002", + private_attribute_sub_components=["body01_face002"], + ), + Surface( + name="body01_face003", + private_attribute_sub_components=["body01_face003"], + ) + ] + ], + ) else: raise ValueError("Invalid file name") def _populate_registry(self): self.mesh_unit = LengthType.validate(self._get_meta_data()["mesh_unit"]) - for zone_name in self._get_meta_data()["edges"] if "edges" in self._get_meta_data() else []: - # pylint: disable=fixme - # TODO: private_attribute_sub_components is hacked to be just the grouped name, - # TODO: this should actually be the list of edgeIDs/faceIDs - self.internal_registry.register( - Edge(name=zone_name, private_attribute_sub_components=[zone_name]) - ) - for surface_name in ( - self._get_meta_data()["surfaces"] if "surfaces" in self._get_meta_data() else [] - ): - self.internal_registry.register( - Surface(name=surface_name, private_attribute_sub_components=[surface_name]) - ) - - def __init__(self, file_name: str): + if self.snappy: + self.internal_registry = self._get_entity_info()._group_faces_by_snappy_format() + else: + for zone_name in self._get_meta_data()["edges"] if "edges" in self._get_meta_data() else []: + # pylint: disable=fixme + # TODO: private_attribute_sub_components is hacked to be just the grouped name, + # TODO: this should actually be the list of edgeIDs/faceIDs + self.internal_registry.register( + Edge(name=zone_name, private_attribute_sub_components=[zone_name]) + ) + for surface_name in ( + self._get_meta_data()["surfaces"] if "surfaces" in self._get_meta_data() else [] + ): + self.internal_registry.register( + Surface(name=surface_name, private_attribute_sub_components=[surface_name]) + ) + + def __init__(self, file_name: str, for_snappy=False): super().__init__() self.fname = file_name + self.snappy = for_snappy self._populate_registry() @@ -529,7 +569,7 @@ def rotor_surface_mesh(): @pytest.fixture() def snappy_all_defaults(): - test_geometry = TempGeometry("tester.stl") + test_geometry = TempGeometry("tester.stl", True) with SI_unit_system: surf_meshing_params = SnappySurfaceMeshingParams( defaults=SnappySurfaceMeshingDefaults( @@ -550,7 +590,7 @@ def snappy_all_defaults(): @pytest.fixture() def snappy_basic_refinements(): - test_geometry = TempGeometry("tester.stl") + test_geometry = TempGeometry("tester.stl", True) with SI_unit_system: surf_meshing_params = SnappySurfaceMeshingParams( defaults=SnappySurfaceMeshingDefaults( @@ -561,13 +601,13 @@ def snappy_basic_refinements(): gap_resolution=2 * u.mm, min_spacing=5 * u.mm, max_spacing=10 * u.mm, - bodies=[SnappyBody(body_name="body1"), SnappyBody(body_name="body3")], + bodies=[test_geometry["body1"],test_geometry["body3"]], ), SnappyBodyRefinement( gap_resolution=0.5 * u.mm, min_spacing=1 * u.mm, max_spacing=2 * u.mm, - bodies=[SnappyBody(body_name="body2")], + bodies=[test_geometry["body2"]], proximity_spacing=0.2 * u.mm, ), SnappyRegionRefinement( @@ -575,28 +615,28 @@ def snappy_basic_refinements(): max_spacing=40 * u.mm, proximity_spacing=3 * u.mm, regions=[ - test_geometry["body0::patch0"], - test_geometry["body1::patch1"], + test_geometry["body0"]["patch0"], + test_geometry["body1"]["patch1"], ], ), SnappySurfaceEdgeRefinement( spacing=4 * u.mm, min_elem=3, included_angle=120 * u.deg, - bodies=[SnappyBody(body_name="body1")], + bodies=test_geometry["body1"], ), SnappySurfaceEdgeRefinement( spacing=4 * u.mm, min_elem=3, included_angle=120 * u.deg, - regions=[test_geometry["body0::patch0"]], + regions=[test_geometry["body0"]["patch0"]], ), SnappySurfaceEdgeRefinement( spacing=[3 * u.mm, 5 * u.mm], distances=[1 * u.mm, 3 * u.mm], min_len=6 * u.mm, - regions=[test_geometry["*patch1"]], - bodies=[SnappyBody(body_name="body3")], + regions=[test_geometry["*"]["patch1"]], + bodies=[test_geometry["body3"]], retain_on_smoothing=False, ), UniformRefinement( @@ -641,7 +681,7 @@ def snappy_basic_refinements(): @pytest.fixture() def snappy_coupled_refinements(): - test_geometry = TempGeometry("tester.stl") + test_geometry = TempGeometry("tester.stl", True) with SI_unit_system: surf_meshing_params = SnappySurfaceMeshingParams( defaults=SnappySurfaceMeshingDefaults( @@ -699,7 +739,7 @@ def snappy_coupled_refinements(): @pytest.fixture() def snappy_refinements_multiple_regions(): - test_geometry = TempGeometry("tester.stl") + test_geometry = TempGeometry("tester.stl", True) with SI_unit_system: surf_meshing_params = SnappySurfaceMeshingParams( defaults=SnappySurfaceMeshingDefaults( @@ -711,16 +751,16 @@ def snappy_refinements_multiple_regions(): max_spacing=40 * u.mm, proximity_spacing=3 * u.mm, regions=[ - test_geometry["body1::patch0"], - test_geometry["body1::patch1"], - test_geometry["body1::patch2"], + test_geometry["body1"]["patch0"], + test_geometry["body1"]["patch1"], + test_geometry["body1"]["patch2"], ], ), SnappySurfaceEdgeRefinement( spacing=4 * u.mm, min_elem=3, included_angle=120 * u.deg, - regions=[test_geometry["body0::patch0"], test_geometry["body0::patch1"]], + regions=[test_geometry["body0"]["patch0"], test_geometry["body0"]["patch1"]], retain_on_smoothing=False, ), ], @@ -740,7 +780,7 @@ def snappy_refinements_multiple_regions(): @pytest.fixture() def snappy_refinements_no_regions(): - test_geometry = TempGeometry("rotor.csm") + test_geometry = TempGeometry("tester_no_naming.stl", True) with SI_unit_system: surf_meshing_params = SnappySurfaceMeshingParams( defaults=SnappySurfaceMeshingDefaults( @@ -751,20 +791,20 @@ def snappy_refinements_no_regions(): gap_resolution=2 * u.mm, min_spacing=5 * u.mm, max_spacing=10 * u.mm, - bodies=[SnappyBody(body_name="body01_face001")], + bodies=[test_geometry["body01_face001"]], ), SnappyBodyRefinement( gap_resolution=0.5 * u.mm, min_spacing=1 * u.mm, max_spacing=2 * u.mm, - bodies=[SnappyBody(body_name="body01_face002")], + bodies=[test_geometry["body01_face002"]], proximity_spacing=0.2 * u.mm, ), SnappySurfaceEdgeRefinement( spacing=4 * u.mm, min_elem=3, included_angle=120 * u.deg, - bodies=[SnappyBody(body_name="body01_face003")], + bodies=[test_geometry["body01_face003"]], ), ], smooth_controls=SnappySmoothControls(), @@ -784,7 +824,7 @@ def snappy_refinements_no_regions(): @pytest.fixture() def snappy_settings(): - test_geometry = TempGeometry("tester.stl") + test_geometry = TempGeometry("tester.stl", True) with SI_unit_system: surf_meshing_params = SnappySurfaceMeshingParams( defaults=SnappySurfaceMeshingDefaults( @@ -840,7 +880,7 @@ def snappy_settings(): @pytest.fixture() def snappy_settings_off_position(): - test_geometry = TempGeometry("tester.stl") + test_geometry = TempGeometry("tester.stl", True) with SI_unit_system: surf_meshing_params = SnappySurfaceMeshingParams( defaults=SnappySurfaceMeshingDefaults( From 995881634f0e0c67b336a8f7236ded38d6711b14 Mon Sep 17 00:00:00 2001 From: piotrkluba Date: Tue, 7 Oct 2025 17:45:34 +0200 Subject: [PATCH 2/5] references to snappybody accepted as input to EntityList[Surface] --- .../simulation/framework/entity_base.py | 9 +++++++-- flow360/component/simulation/primitives.py | 2 +- .../snappy_refinements_multiple_regions.json | 20 +++++++++++++++++-- .../test_surface_meshing_translator.py | 10 ++++++++++ 4 files changed, 36 insertions(+), 5 deletions(-) diff --git a/flow360/component/simulation/framework/entity_base.py b/flow360/component/simulation/framework/entity_base.py index f13bc1b88..a358ee74c 100644 --- a/flow360/component/simulation/framework/entity_base.py +++ b/flow360/component/simulation/framework/entity_base.py @@ -275,11 +275,16 @@ def _format_input_to_list(cls, input_data: Union[dict, list]): entities_to_store = [] valid_types = cls._get_valid_entity_types() + if isinstance(input_data, EntityList) and input_data._get_valid_entity_types() == valid_types: + return {"stored_entities": input_data.stored_entities} + if isinstance(input_data, list): # A list of entities if input_data == []: raise ValueError("Invalid input type to `entities`, list is empty.") - for item in input_data: - if isinstance(item, list): # Nested list comes from assets + for item in input_data: # for entity grouping types + if isinstance(item, EntityList) and item._get_valid_entity_types() == valid_types: + entities_to_store.extend(item.stored_entities) + elif isinstance(item, list): # Nested list comes from assets _ = [cls._valid_individual_input(individual) for individual in item] # pylint: disable=fixme # TODO: Give notice when some of the entities are not selected due to `valid_types`? diff --git a/flow360/component/simulation/primitives.py b/flow360/component/simulation/primitives.py index dbce18cc5..e23fc6419 100644 --- a/flow360/component/simulation/primitives.py +++ b/flow360/component/simulation/primitives.py @@ -661,7 +661,7 @@ def __str__(self): return ",".join(sorted([self.pair[0].name, self.pair[1].name])) -class SnappyBody(EntityBase, EntityList): +class SnappyBody(EntityBase, EntityList[Surface]): """ Represents a group of faces forming a body for snappyHexMesh. Bodies and their regions are defined in the ASCII STL file by using the solid -> endsolid" diff --git a/tests/simulation/translator/ref/surface_meshing/snappy_refinements_multiple_regions.json b/tests/simulation/translator/ref/surface_meshing/snappy_refinements_multiple_regions.json index c94293644..dda4e3534 100644 --- a/tests/simulation/translator/ref/surface_meshing/snappy_refinements_multiple_regions.json +++ b/tests/simulation/translator/ref/surface_meshing/snappy_refinements_multiple_regions.json @@ -11,6 +11,10 @@ "regions": [ { "patchName": "patch0", + "spacing": { + "min": 10, + "max": 40 + }, "edges":{ "edgeSpacing": 4, "minElem": 3, @@ -20,6 +24,10 @@ }, { "patchName": "patch1", + "spacing": { + "min": 10, + "max": 40 + }, "edges":{ "edgeSpacing": 4, "minElem": 3, @@ -72,7 +80,11 @@ }, "regions": [ { - "patchName": "patch0" + "patchName": "patch0", + "spacing": { + "min": 5, + "max": 40 + } } ] }, @@ -85,7 +97,11 @@ }, "regions": [ { - "patchName": "patch0" + "patchName": "patch0", + "spacing": { + "min": 5, + "max": 40 + } } ] } diff --git a/tests/simulation/translator/test_surface_meshing_translator.py b/tests/simulation/translator/test_surface_meshing_translator.py index 7c90c4f24..ecb3473a0 100644 --- a/tests/simulation/translator/test_surface_meshing_translator.py +++ b/tests/simulation/translator/test_surface_meshing_translator.py @@ -756,6 +756,16 @@ def snappy_refinements_multiple_regions(): test_geometry["body1"]["patch2"], ], ), + SnappyRegionRefinement( + min_spacing=10 * u.mm, + max_spacing=40 * u.mm, + regions=test_geometry["body0"] + ), + SnappyRegionRefinement( + min_spacing=5 * u.mm, + max_spacing=40 * u.mm, + regions=[test_geometry["body2"], test_geometry["body3"]["patch0"]] + ), SnappySurfaceEdgeRefinement( spacing=4 * u.mm, min_elem=3, From 100a35c135385e177771c318979c8658c88b2f7c Mon Sep 17 00:00:00 2001 From: piotrkluba Date: Tue, 7 Oct 2025 18:05:24 +0200 Subject: [PATCH 3/5] formatter --- flow360/component/geometry.py | 8 +++++- flow360/component/simulation/entity_info.py | 27 +++++++++---------- .../simulation/framework/entity_base.py | 10 +++++-- .../simulation/framework/entity_registry.py | 18 +++++++++---- flow360/component/simulation/primitives.py | 8 ++++-- .../translator/surface_meshing_translator.py | 4 ++- tests/simulation/asset/test_geometry.py | 12 +++++---- .../test_meshing_param_validation.py | 8 ++++-- .../test_surface_meshing_translator.py | 20 ++++++-------- 9 files changed, 71 insertions(+), 44 deletions(-) diff --git a/flow360/component/geometry.py b/flow360/component/geometry.py index 2c2b04709..94e47875c 100644 --- a/flow360/component/geometry.py +++ b/flow360/component/geometry.py @@ -413,7 +413,13 @@ def group_bodies_by_tag(self, tag_name: str) -> None: ) def group_faces_for_snappy(self) -> None: - self.internal_registry = self._entity_info._group_faces_by_snappy_format(self.internal_registry) + """ + Group faces according to body::region convention for snappyHexMesh. + """ + # pylint: disable=protected-access + self.internal_registry = self._entity_info._group_faces_by_snappy_format( + self.internal_registry + ) def reset_face_grouping(self) -> None: """Reset the face grouping""" diff --git a/flow360/component/simulation/entity_info.py b/flow360/component/simulation/entity_info.py index 1a20f6123..b40a8a419 100644 --- a/flow360/component/simulation/entity_info.py +++ b/flow360/component/simulation/entity_info.py @@ -23,8 +23,8 @@ GeometryBodyGroup, GhostCircularPlane, GhostSphere, + SnappyBody, Surface, - SnappyBody ) from flow360.component.simulation.unit_system import LengthType from flow360.component.simulation.utils import BoundingBoxType, model_attribute_unlock @@ -43,6 +43,7 @@ GROUPED_SNAPPY = "Grouped with snappy name formatting." + class EntityInfoModel(Flow360BaseModel, metaclass=ABCMeta): """Base model for asset entity info JSON""" @@ -156,11 +157,9 @@ def group_in_registry( for item in entity_list: known_frozen_hashes = registry.fast_register(item, known_frozen_hashes) return registry - - def _get_snappy_bodies( - self - ) -> List[SnappyBody]: - + + def _get_snappy_bodies(self) -> List[SnappyBody]: + snappy_body_mapping = {} for patch in self.grouped_faces[self.face_attribute_names.index("faceId")]: name_components = patch.name.split("::") @@ -170,9 +169,11 @@ def _get_snappy_bodies( if patch not in snappy_body_mapping[body_name]: snappy_body_mapping[body_name].append(patch) - return [SnappyBody(name=snappy_body, stored_entities=body_entities) - for snappy_body, body_entities in snappy_body_mapping.items()] - + return [ + SnappyBody(name=snappy_body, stored_entities=body_entities) + for snappy_body, body_entities in snappy_body_mapping.items() + ] + def _get_list_of_entities( self, attribute_name: Union[str, None] = None, @@ -374,14 +375,12 @@ def _group_entity_by_tag( self.body_group_tag = tag_name return registry - - def _group_faces_by_snappy_format( - self, - registry: EntityRegistry = None - ): + + def _group_faces_by_snappy_format(self, registry: EntityRegistry = None): if registry is None: registry = EntityRegistry() + existing_face_tag = None if self.face_group_tag is not None: existing_face_tag = self.face_group_tag diff --git a/flow360/component/simulation/framework/entity_base.py b/flow360/component/simulation/framework/entity_base.py index a358ee74c..2c9c0d87b 100644 --- a/flow360/component/simulation/framework/entity_base.py +++ b/flow360/component/simulation/framework/entity_base.py @@ -266,6 +266,7 @@ def _valid_individual_input(cls, input_data): "Expected entity instance." ) + # pylint: disable=too-many-branches @pd.model_validator(mode="before") @classmethod def _format_input_to_list(cls, input_data: Union[dict, list]): @@ -275,13 +276,18 @@ def _format_input_to_list(cls, input_data: Union[dict, list]): entities_to_store = [] valid_types = cls._get_valid_entity_types() - if isinstance(input_data, EntityList) and input_data._get_valid_entity_types() == valid_types: + # pylint: disable=protected-access + if ( + isinstance(input_data, EntityList) + and input_data._get_valid_entity_types() == valid_types + ): return {"stored_entities": input_data.stored_entities} if isinstance(input_data, list): # A list of entities if input_data == []: raise ValueError("Invalid input type to `entities`, list is empty.") - for item in input_data: # for entity grouping types + for item in input_data: # for entity grouping types + # pylint: disable=protected-access if isinstance(item, EntityList) and item._get_valid_entity_types() == valid_types: entities_to_store.extend(item.stored_entities) elif isinstance(item, list): # Nested list comes from assets diff --git a/flow360/component/simulation/framework/entity_registry.py b/flow360/component/simulation/framework/entity_registry.py index 84899ab55..d3798fd82 100644 --- a/flow360/component/simulation/framework/entity_registry.py +++ b/flow360/component/simulation/framework/entity_registry.py @@ -8,7 +8,12 @@ from flow360.component.simulation.framework.entity_base import EntityBase from flow360.component.utils import _naming_pattern_handler + class DoubleIndexableList(list): + """ + An extension of a list that allows accessing elements inside it through a string key. + """ + def __getitem__(self, key: Union[str, slice, int]): if isinstance(key, str): returned_items = [] @@ -17,19 +22,22 @@ def __getitem__(self, key: Union[str, slice, int]): item_ret_value = item[key] except KeyError: item_ret_value = [] - except Exception: - raise ValueError(f"Trying to access something in {item} through string indexing, which is not allowed.") + except Exception as e: + raise ValueError( + f"Trying to access something in {item} through string indexing, which is not allowed." + ) from e if isinstance(item_ret_value, list): returned_items += item_ret_value else: returned_items.append(item_ret_value) if not returned_items: raise ValueError( - f"No entity found in registry for parent entities: {', '.join([f'{entity.name}' for entity in self])} with given name/naming pattern: '{key}'." + "No entity found in registry for parent entities: " + + f"{', '.join([f'{entity.name}' for entity in self])} with given name/naming pattern: '{key}'." ) return returned_items - else: - return super(DoubleIndexableList, self).__getitem__(key) + return super().__getitem__(key) + class EntityRegistryBucket: """By reference, a snippet of certain collection of a EntityRegistry instance that is inside the same bucket.""" diff --git a/flow360/component/simulation/primitives.py b/flow360/component/simulation/primitives.py index e23fc6419..4cbc851c1 100644 --- a/flow360/component/simulation/primitives.py +++ b/flow360/component/simulation/primitives.py @@ -668,7 +668,9 @@ class SnappyBody(EntityBase, EntityList[Surface]): keywords with a body::region naming scheme. """ - private_attribute_registry_bucket_name: Literal["SurfaceGroupedEntityType"] = pd.Field("SurfaceGroupedEntityType", frozen=True) + private_attribute_registry_bucket_name: Literal["SurfaceGroupedEntityType"] = pd.Field( + "SurfaceGroupedEntityType", frozen=True + ) private_attribute_entity_type_name: Literal["SnappyBody"] = pd.Field("SnappyBody", frozen=True) stored_entities: List[Surface] = pd.Field() @@ -693,7 +695,9 @@ def __getitem__(self, key: str): matched_surfaces = [entity for entity in self.stored_entities if regex.match(entity.name)] if not matched_surfaces: print(key) - raise KeyError(f"No entity found in registry for parent entity: {self.name} with given name/naming pattern: '{key}'.") + raise KeyError( + f"No entity found in registry for parent entity: {self.name} with given name/naming pattern: '{key}'." + ) return matched_surfaces diff --git a/flow360/component/simulation/translator/surface_meshing_translator.py b/flow360/component/simulation/translator/surface_meshing_translator.py index b2daee570..72bd421b9 100644 --- a/flow360/component/simulation/translator/surface_meshing_translator.py +++ b/flow360/component/simulation/translator/surface_meshing_translator.py @@ -142,7 +142,9 @@ def apply_SnappySurfaceEdgeRefinement( else: edges["edgeSpacing"] = refinement.spacing.value.item() applicable_bodies = ( - [entity.name for entity in refinement.bodies.stored_entities] if refinement.bodies is not None else [] + [entity.name for entity in refinement.bodies.stored_entities] + if refinement.bodies is not None + else [] ) applicable_regions = get_applicable_regions_dict(refinement_regions=refinement.regions) for body in translated["geometry"]["bodies"]: diff --git a/tests/simulation/asset/test_geometry.py b/tests/simulation/asset/test_geometry.py index 41a85343c..e974169ba 100644 --- a/tests/simulation/asset/test_geometry.py +++ b/tests/simulation/asset/test_geometry.py @@ -321,6 +321,7 @@ def test_geometry_rename_body_groups(): ): assert geometry["newAirplane_0002"] + def test_geometry_group_for_snappy(stl_geo_meta): geo: Geometry = stl_geo_meta @@ -343,6 +344,7 @@ def test_geometry_group_for_snappy(stl_geo_meta): # double indexing with wildcard assert len(geo["*nn*"]["*"]) == 6 + def test_snappy_grouping_not_found_messages(stl_geo_meta): geo: Geometry = stl_geo_meta @@ -356,11 +358,11 @@ def test_snappy_grouping_not_found_messages(stl_geo_meta): with pytest.raises( ValueError, - match=(f"No entity found in registry for parent entities: body-inner-nlyr, tunnel with given name/naming pattern: 'dummy'."), + match=( + f"No entity found in registry for parent entities: body-inner-nlyr, tunnel with given name/naming pattern: 'dummy'." + ), ): assert geo["*nn*"]["dummy"] - with pytest.raises( - KeyError - ): - assert geo["body-nose"]["dummy*"] \ No newline at end of file + with pytest.raises(KeyError): + assert geo["body-nose"]["dummy*"] 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 7a9d19330..490ed17cb 100644 --- a/tests/simulation/params/meshing_validation/test_meshing_param_validation.py +++ b/tests/simulation/params/meshing_validation/test_meshing_param_validation.py @@ -439,7 +439,9 @@ def test_bad_refinements(): min_spacing=1 * u.mm, max_spacing=5 * u.mm, gap_resolution=0.01 * u.mm ), refinements=[ - SnappyBodyRefinement(min_spacing=6 * u.mm, bodies=[SnappyBody(name="bbb", stored_entities=[])]) + SnappyBodyRefinement( + min_spacing=6 * u.mm, bodies=[SnappyBody(name="bbb", stored_entities=[])] + ) ], ) @@ -449,6 +451,8 @@ def test_bad_refinements(): min_spacing=1 * u.mm, max_spacing=5 * u.mm, gap_resolution=0.01 * u.mm ), refinements=[ - SnappyBodyRefinement(max_spacing=0.5 * u.mm, bodies=[SnappyBody(name="bbb", stored_entities=[])]) + SnappyBodyRefinement( + max_spacing=0.5 * u.mm, bodies=[SnappyBody(name="bbb", stored_entities=[])] + ) ], ) diff --git a/tests/simulation/translator/test_surface_meshing_translator.py b/tests/simulation/translator/test_surface_meshing_translator.py index ecb3473a0..f1b679ba9 100644 --- a/tests/simulation/translator/test_surface_meshing_translator.py +++ b/tests/simulation/translator/test_surface_meshing_translator.py @@ -292,11 +292,7 @@ def _get_entity_info(self): ) elif self.fname == "tester_no_naming.stl": return GeometryEntityInfo( - face_ids=[ - "body01_face001", - "body01_face002", - "body01_face003" - ], + face_ids=["body01_face001", "body01_face002", "body01_face003"], edge_ids=[], face_attribute_names=["faceId"], face_group_tag="faceId", @@ -313,7 +309,7 @@ def _get_entity_info(self): Surface( name="body01_face003", private_attribute_sub_components=["body01_face003"], - ) + ), ] ], ) @@ -325,7 +321,9 @@ def _populate_registry(self): if self.snappy: self.internal_registry = self._get_entity_info()._group_faces_by_snappy_format() else: - for zone_name in self._get_meta_data()["edges"] if "edges" in self._get_meta_data() else []: + for zone_name in ( + self._get_meta_data()["edges"] if "edges" in self._get_meta_data() else [] + ): # pylint: disable=fixme # TODO: private_attribute_sub_components is hacked to be just the grouped name, # TODO: this should actually be the list of edgeIDs/faceIDs @@ -601,7 +599,7 @@ def snappy_basic_refinements(): gap_resolution=2 * u.mm, min_spacing=5 * u.mm, max_spacing=10 * u.mm, - bodies=[test_geometry["body1"],test_geometry["body3"]], + bodies=[test_geometry["body1"], test_geometry["body3"]], ), SnappyBodyRefinement( gap_resolution=0.5 * u.mm, @@ -757,14 +755,12 @@ def snappy_refinements_multiple_regions(): ], ), SnappyRegionRefinement( - min_spacing=10 * u.mm, - max_spacing=40 * u.mm, - regions=test_geometry["body0"] + min_spacing=10 * u.mm, max_spacing=40 * u.mm, regions=test_geometry["body0"] ), SnappyRegionRefinement( min_spacing=5 * u.mm, max_spacing=40 * u.mm, - regions=[test_geometry["body2"], test_geometry["body3"]["patch0"]] + regions=[test_geometry["body2"], test_geometry["body3"]["patch0"]], ), SnappySurfaceEdgeRefinement( spacing=4 * u.mm, From ea7bf0a5e02bfa590c7de8b553d77f250e331efc Mon Sep 17 00:00:00 2001 From: piotrkluba Date: Tue, 14 Oct 2025 14:55:37 +0200 Subject: [PATCH 4/5] created a separate registry for snappy_body in geometry --- flow360/component/geometry.py | 27 +++++++++-- flow360/component/simulation/entity_info.py | 15 +++--- .../simulation/framework/entity_base.py | 14 +----- .../simulation/framework/entity_registry.py | 48 ++++++++++++++++++- flow360/component/simulation/primitives.py | 19 ++------ tests/simulation/asset/test_geometry.py | 26 ++++++---- .../test_meshing_param_validation.py | 4 +- .../test_surface_meshing_translator.py | 45 ++++++++++------- 8 files changed, 131 insertions(+), 67 deletions(-) diff --git a/flow360/component/geometry.py b/flow360/component/geometry.py index 42da4995c..e58432644 100644 --- a/flow360/component/geometry.py +++ b/flow360/component/geometry.py @@ -236,6 +236,11 @@ class Geometry(AssetBase): _entity_info_class = GeometryEntityInfo _cloud_resource_type_name = "Geometry" + # pylint: disable=redefined-builtin + def __init__(self, id: Union[str, None]): + super().__init__(id) + self.snappy_body_registry = None + @property def face_group_tag(self): "getter for face_group_tag" @@ -263,6 +268,16 @@ def body_group_tag(self): def body_group_tag(self, new_value: str): raise SyntaxError("Cannot set body_group_tag, use group_bodies_by_tag() instead.") + @property + def snappy_bodies(self): + """Getter for the snappy registry.""" + if hasattr(self, "snappy_body_registry") is False or self.snappy_body_registry is None: + raise Flow360ValueError( + "The faces in geometry are not grouped for snappy." + "Please use `group_faces_for_snappy` function to group them first." + ) + return self.snappy_body_registry + def get_dynamic_default_settings(self, simulation_dict: dict): """Get the default geometry settings from the simulation dict""" @@ -416,15 +431,21 @@ def group_faces_for_snappy(self) -> None: """ Group faces according to body::region convention for snappyHexMesh. """ - # pylint: disable=protected-access - self.internal_registry = self._entity_info._group_faces_by_snappy_format( - self.internal_registry + # pylint: disable=protected-access,no-member + self.internal_registry = self._entity_info._group_entity_by_tag( + "face", "faceId", self.internal_registry ) + # pylint: disable=protected-access + self.snappy_body_registry = self._entity_info._group_faces_by_snappy_format() def reset_face_grouping(self) -> None: """Reset the face grouping""" # pylint: disable=protected-access,no-member self.internal_registry = self._entity_info._reset_grouping("face", self.internal_registry) + if hasattr(self, "snappy_body_registry") is True and self.snappy_body_registry: + self.snappy_body_registry = self.snappy_body._reset_grouping( + "face", self.snappy_body_registry + ) def reset_edge_grouping(self) -> None: """Reset the edge grouping""" diff --git a/flow360/component/simulation/entity_info.py b/flow360/component/simulation/entity_info.py index b40a8a419..e5ba89042 100644 --- a/flow360/component/simulation/entity_info.py +++ b/flow360/component/simulation/entity_info.py @@ -7,7 +7,10 @@ import pydantic as pd from flow360.component.simulation.framework.base_model import Flow360BaseModel -from flow360.component.simulation.framework.entity_registry import EntityRegistry +from flow360.component.simulation.framework.entity_registry import ( + EntityRegistry, + SnappyBodyRegistry, +) from flow360.component.simulation.outputs.output_entities import ( Point, PointArray, @@ -170,7 +173,7 @@ def _get_snappy_bodies(self) -> List[SnappyBody]: snappy_body_mapping[body_name].append(patch) return [ - SnappyBody(name=snappy_body, stored_entities=body_entities) + SnappyBody(name=snappy_body, surfaces=body_entities) for snappy_body, body_entities in snappy_body_mapping.items() ] @@ -225,7 +228,7 @@ def get_boundaries(self, attribute_name: str = None) -> list[Surface]: if self.face_group_tag == GROUPED_SNAPPY and attribute_name is None: all_boundaries = [] for body in self._get_list_of_entities(attribute_name, "snappy_body"): - all_boundaries += body.stored_entities + all_boundaries += body.surfaces return all_boundaries return self._get_list_of_entities(attribute_name, "face") @@ -376,9 +379,8 @@ def _group_entity_by_tag( return registry - def _group_faces_by_snappy_format(self, registry: EntityRegistry = None): - if registry is None: - registry = EntityRegistry() + def _group_faces_by_snappy_format(self): + registry = SnappyBodyRegistry() existing_face_tag = None if self.face_group_tag is not None: @@ -400,6 +402,7 @@ def _group_faces_by_snappy_format(self, registry: EntityRegistry = None): return registry + @pd.validate_call def _reset_grouping( self, entity_type_name: Literal["face", "edge", "body"], registry: EntityRegistry ) -> EntityRegistry: diff --git a/flow360/component/simulation/framework/entity_base.py b/flow360/component/simulation/framework/entity_base.py index 2c9c0d87b..316065c5c 100644 --- a/flow360/component/simulation/framework/entity_base.py +++ b/flow360/component/simulation/framework/entity_base.py @@ -276,21 +276,11 @@ def _format_input_to_list(cls, input_data: Union[dict, list]): entities_to_store = [] valid_types = cls._get_valid_entity_types() - # pylint: disable=protected-access - if ( - isinstance(input_data, EntityList) - and input_data._get_valid_entity_types() == valid_types - ): - return {"stored_entities": input_data.stored_entities} - if isinstance(input_data, list): # A list of entities if input_data == []: raise ValueError("Invalid input type to `entities`, list is empty.") - for item in input_data: # for entity grouping types - # pylint: disable=protected-access - if isinstance(item, EntityList) and item._get_valid_entity_types() == valid_types: - entities_to_store.extend(item.stored_entities) - elif isinstance(item, list): # Nested list comes from assets + for item in input_data: + if isinstance(item, list): # Nested list comes from assets _ = [cls._valid_individual_input(individual) for individual in item] # pylint: disable=fixme # TODO: Give notice when some of the entities are not selected due to `valid_types`? diff --git a/flow360/component/simulation/framework/entity_registry.py b/flow360/component/simulation/framework/entity_registry.py index d3798fd82..a77332cb5 100644 --- a/flow360/component/simulation/framework/entity_registry.py +++ b/flow360/component/simulation/framework/entity_registry.py @@ -7,6 +7,7 @@ from flow360.component.simulation.framework.base_model import Flow360BaseModel from flow360.component.simulation.framework.entity_base import EntityBase from flow360.component.utils import _naming_pattern_handler +from flow360.exceptions import Flow360ValueError class DoubleIndexableList(list): @@ -151,7 +152,7 @@ def find_by_naming_pattern( Returns: List[EntityBase]: A list of entities whose names match the pattern. """ - matched_entities = DoubleIndexableList() + matched_entities = [] regex = _naming_pattern_handler(pattern=pattern) # pylint: disable=no-member for entity_list in self.internal_registry.values(): @@ -257,3 +258,48 @@ def find_by_asset_id(self, *, entity_id: str, entity_class: type[EntityBase]): def is_empty(self): """Return True if the registry is empty, False otherwise.""" return not self.internal_registry + + +class SnappyBodyRegistry(EntityRegistry): + """ + Extension of Entityregistry to be used with SnappyBody, allows double indexing. + """ + + def find_by_naming_pattern( + self, pattern: str, enforce_output_as_list: bool = True, error_when_no_match: bool = False + ) -> list[EntityBase]: + """ + Finds all registered entities whose names match a given pattern. + + Parameters: + pattern (str): A naming pattern, which can include '*' as a wildcard. + + Returns: + List[EntityBase]: A list of entities whose names match the pattern. + """ + matched_entities = DoubleIndexableList() + regex = _naming_pattern_handler(pattern=pattern) + # pylint: disable=no-member + for entity_list in self.internal_registry.values(): + matched_entities.extend(filter(lambda x: regex.match(x.name), entity_list)) + + if not matched_entities and error_when_no_match is True: + raise ValueError( + f"No entity found in registry with given name/naming pattern: '{pattern}'." + ) + if enforce_output_as_list is False and len(matched_entities) == 1: + return matched_entities[0] + + return matched_entities + + def __getitem__(self, key): + """ + Get the entity by name. + `key` is the name of the entity or the naming pattern if wildcard is used. + """ + if isinstance(key, str) is False: + raise Flow360ValueError(f"Entity naming pattern: {key} is not a string.") + + return self.find_by_naming_pattern( + key, enforce_output_as_list=False, error_when_no_match=True + ) diff --git a/flow360/component/simulation/primitives.py b/flow360/component/simulation/primitives.py index f9c90682e..8afe0bf05 100644 --- a/flow360/component/simulation/primitives.py +++ b/flow360/component/simulation/primitives.py @@ -717,7 +717,7 @@ def __str__(self): return ",".join(sorted([self.pair[0].name, self.pair[1].name])) -class SnappyBody(EntityBase, EntityList[Surface]): +class SnappyBody(EntityBase): """ Represents a group of faces forming a body for snappyHexMesh. Bodies and their regions are defined in the ASCII STL file by using the solid -> endsolid" @@ -729,26 +729,15 @@ class SnappyBody(EntityBase, EntityList[Surface]): ) private_attribute_entity_type_name: Literal["SnappyBody"] = pd.Field("SnappyBody", frozen=True) - stored_entities: List[Surface] = pd.Field() - - @pd.model_validator(mode="before") - @classmethod - def _format_input_to_list(cls, input_data: dict): - """ - Flatten List[EntityBase] and put into stored_entities. Do not delete other args. - """ - if not isinstance(input_data, dict): - raise ValueError("Wrong definition for SnappyBody, should be a dictionary.") - input_data.update(super()._format_input_to_list(input_data)) - return input_data + surfaces: List[Surface] = pd.Field() def __getitem__(self, key: str): - if len(self.stored_entities) == 1 and ("::" not in self.stored_entities[0].name): + if len(self.surfaces) == 1 and ("::" not in self.surfaces[0].name): regex = _naming_pattern_handler(pattern=key) else: regex = _naming_pattern_handler(pattern=f"{self.name}::{key}") - matched_surfaces = [entity for entity in self.stored_entities if regex.match(entity.name)] + matched_surfaces = [entity for entity in self.surfaces if regex.match(entity.name)] if not matched_surfaces: print(key) raise KeyError( diff --git a/tests/simulation/asset/test_geometry.py b/tests/simulation/asset/test_geometry.py index e974169ba..a9e535197 100644 --- a/tests/simulation/asset/test_geometry.py +++ b/tests/simulation/asset/test_geometry.py @@ -9,6 +9,7 @@ from flow360.component.resource_base import local_metadata_builder from flow360.component.simulation.primitives import SnappyBody, Surface from flow360.examples import Cylinder3D +from flow360.exceptions import Flow360ValueError assertions = unittest.TestCase("__init__") @@ -325,24 +326,29 @@ def test_geometry_rename_body_groups(): def test_geometry_group_for_snappy(stl_geo_meta): geo: Geometry = stl_geo_meta + with pytest.raises(Flow360ValueError): + geo.snappy_bodies + geo.group_faces_for_snappy() # body with one region - assert isinstance(geo["rr-wh-rim-lhs"], SnappyBody) - assert len(geo["rr-wh-rim-lhs"]["*"]) == 1 - assert isinstance(geo["rr-wh-rim-lhs"]["*"][0], Surface) - assert geo["rr-wh-rim-lhs"]["*"][0].name == "rr-wh-rim-lhs" + assert isinstance(geo.snappy_bodies["rr-wh-rim-lhs"], SnappyBody) + assert len(geo.snappy_bodies["rr-wh-rim-lhs"]["*"]) == 1 + assert isinstance(geo.snappy_bodies["rr-wh-rim-lhs"]["*"][0], Surface) + assert geo.snappy_bodies["rr-wh-rim-lhs"]["*"][0].name == "rr-wh-rim-lhs" # body with more regions - assert all([isinstance(region, Surface) for region in geo["tunnel"]["*"]]) - assert len(geo["tunnel"]["*"]) == 5 + assert all([isinstance(region, Surface) for region in geo.snappy_bodies["tunnel"]["*"]]) + assert len(geo.snappy_bodies["tunnel"]["*"]) == 5 # registry wildcard assert len(geo["uf*"]) == 2 assert len(geo["velocity*"]) == 10 + assert all([isinstance(region, Surface) for region in geo["velocity*"]]) + assert all([isinstance(region, SnappyBody) for region in geo.snappy_bodies["velocity*"]]) # double indexing with wildcard - assert len(geo["*nn*"]["*"]) == 6 + assert len(geo.snappy_bodies["*nn*"]["*"]) == 6 def test_snappy_grouping_not_found_messages(stl_geo_meta): @@ -354,7 +360,7 @@ def test_snappy_grouping_not_found_messages(stl_geo_meta): ValueError, match=(f"No entity found in registry with given name/naming pattern: 'dummy'."), ): - assert geo["dummy"] + assert geo.snappy_bodies["dummy"] with pytest.raises( ValueError, @@ -362,7 +368,7 @@ def test_snappy_grouping_not_found_messages(stl_geo_meta): f"No entity found in registry for parent entities: body-inner-nlyr, tunnel with given name/naming pattern: 'dummy'." ), ): - assert geo["*nn*"]["dummy"] + assert geo.snappy_bodies["*nn*"]["dummy"] with pytest.raises(KeyError): - assert geo["body-nose"]["dummy*"] + assert geo.snappy_bodies["body-nose"]["dummy*"] 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 5575a8435..e6a9ae280 100644 --- a/tests/simulation/params/meshing_validation/test_meshing_param_validation.py +++ b/tests/simulation/params/meshing_validation/test_meshing_param_validation.py @@ -607,7 +607,7 @@ def test_bad_refinements(): ), refinements=[ SnappyBodyRefinement( - min_spacing=6 * u.mm, bodies=[SnappyBody(name="bbb", stored_entities=[])] + min_spacing=6 * u.mm, bodies=[SnappyBody(name="bbb", surfaces=[])] ) ], ) @@ -619,7 +619,7 @@ def test_bad_refinements(): ), refinements=[ SnappyBodyRefinement( - max_spacing=0.5 * u.mm, bodies=[SnappyBody(name="bbb", stored_entities=[])] + max_spacing=0.5 * u.mm, bodies=[SnappyBody(name="bbb", surfaces=[])] ) ], ) diff --git a/tests/simulation/translator/test_surface_meshing_translator.py b/tests/simulation/translator/test_surface_meshing_translator.py index e1acf1c30..be4b6d64f 100644 --- a/tests/simulation/translator/test_surface_meshing_translator.py +++ b/tests/simulation/translator/test_surface_meshing_translator.py @@ -319,7 +319,10 @@ def _get_entity_info(self): def _populate_registry(self): self.mesh_unit = LengthType.validate(self._get_meta_data()["mesh_unit"]) if self.snappy: - self.internal_registry = self._get_entity_info()._group_faces_by_snappy_format() + self.internal_registry = self._get_entity_info()._group_entity_by_tag( + "face", "faceId", self.internal_registry + ) + self.snappy_bodies = self._get_entity_info()._group_faces_by_snappy_format() else: for zone_name in ( self._get_meta_data()["edges"] if "edges" in self._get_meta_data() else [] @@ -600,13 +603,16 @@ def snappy_basic_refinements(): gap_resolution=2 * u.mm, min_spacing=5 * u.mm, max_spacing=10 * u.mm, - bodies=[test_geometry["body1"], test_geometry["body3"]], + bodies=[ + test_geometry.snappy_bodies["body1"], + test_geometry.snappy_bodies["body3"], + ], ), SnappyBodyRefinement( gap_resolution=0.5 * u.mm, min_spacing=1 * u.mm, max_spacing=2 * u.mm, - bodies=[test_geometry["body2"]], + bodies=[test_geometry.snappy_bodies["body2"]], proximity_spacing=0.2 * u.mm, ), SnappyRegionRefinement( @@ -614,28 +620,28 @@ def snappy_basic_refinements(): max_spacing=40 * u.mm, proximity_spacing=3 * u.mm, regions=[ - test_geometry["body0"]["patch0"], - test_geometry["body1"]["patch1"], + test_geometry.snappy_bodies["body0"]["patch0"], + test_geometry["body1::patch1"], ], ), SnappySurfaceEdgeRefinement( spacing=4 * u.mm, min_elem=3, included_angle=120 * u.deg, - bodies=test_geometry["body1"], + bodies=test_geometry.snappy_bodies["body1"], ), SnappySurfaceEdgeRefinement( spacing=4 * u.mm, min_elem=3, included_angle=120 * u.deg, - regions=[test_geometry["body0"]["patch0"]], + regions=[test_geometry.snappy_bodies["body0"]["patch0"]], ), SnappySurfaceEdgeRefinement( spacing=[3 * u.mm, 5 * u.mm], distances=[1 * u.mm, 3 * u.mm], min_len=6 * u.mm, - regions=[test_geometry["*"]["patch1"]], - bodies=[test_geometry["body3"]], + regions=[test_geometry.snappy_bodies["*"]["patch1"]], + bodies=[test_geometry.snappy_bodies["body3"]], retain_on_smoothing=False, ), UniformRefinement( @@ -752,24 +758,27 @@ def snappy_refinements_multiple_regions(): max_spacing=40 * u.mm, proximity_spacing=3 * u.mm, regions=[ - test_geometry["body1"]["patch0"], - test_geometry["body1"]["patch1"], - test_geometry["body1"]["patch2"], + test_geometry["body1::patch0"], + test_geometry.snappy_bodies["body1"]["patch1"], + test_geometry["body1::patch2"], ], ), SnappyRegionRefinement( - min_spacing=10 * u.mm, max_spacing=40 * u.mm, regions=test_geometry["body0"] + min_spacing=10 * u.mm, max_spacing=40 * u.mm, regions=test_geometry["body0::*"] ), SnappyRegionRefinement( min_spacing=5 * u.mm, max_spacing=40 * u.mm, - regions=[test_geometry["body2"], test_geometry["body3"]["patch0"]], + regions=[ + test_geometry.snappy_bodies["body2"]["*"], + test_geometry["body3::patch0"], + ], ), SnappySurfaceEdgeRefinement( spacing=4 * u.mm, min_elem=3, included_angle=120 * u.deg, - regions=[test_geometry["body0"]["patch0"], test_geometry["body0"]["patch1"]], + regions=[test_geometry["body0::patch0"], test_geometry["body0::patch1"]], retain_on_smoothing=False, ), ], @@ -800,20 +809,20 @@ def snappy_refinements_no_regions(): gap_resolution=2 * u.mm, min_spacing=5 * u.mm, max_spacing=10 * u.mm, - bodies=[test_geometry["body01_face001"]], + bodies=[test_geometry.snappy_bodies["body01_face001"]], ), SnappyBodyRefinement( gap_resolution=0.5 * u.mm, min_spacing=1 * u.mm, max_spacing=2 * u.mm, - bodies=[test_geometry["body01_face002"]], + bodies=[test_geometry.snappy_bodies["body01_face002"]], proximity_spacing=0.2 * u.mm, ), SnappySurfaceEdgeRefinement( spacing=4 * u.mm, min_elem=3, included_angle=120 * u.deg, - bodies=[test_geometry["body01_face003"]], + bodies=[test_geometry.snappy_bodies["body01_face003"]], ), ], smooth_controls=SnappySmoothControls(), From 6094c368e524aabf611576880e7df7a6a79c831d Mon Sep 17 00:00:00 2001 From: piotrkluba Date: Thu, 16 Oct 2025 14:34:46 +0200 Subject: [PATCH 5/5] review corrections --- flow360/component/geometry.py | 4 +-- flow360/component/simulation/entity_info.py | 27 +-------------------- flow360/component/simulation/primitives.py | 5 ++++ 3 files changed, 8 insertions(+), 28 deletions(-) diff --git a/flow360/component/geometry.py b/flow360/component/geometry.py index e58432644..657269a41 100644 --- a/flow360/component/geometry.py +++ b/flow360/component/geometry.py @@ -271,7 +271,7 @@ def body_group_tag(self, new_value: str): @property def snappy_bodies(self): """Getter for the snappy registry.""" - if hasattr(self, "snappy_body_registry") is False or self.snappy_body_registry is None: + if self.snappy_body_registry is None: raise Flow360ValueError( "The faces in geometry are not grouped for snappy." "Please use `group_faces_for_snappy` function to group them first." @@ -442,7 +442,7 @@ def reset_face_grouping(self) -> None: """Reset the face grouping""" # pylint: disable=protected-access,no-member self.internal_registry = self._entity_info._reset_grouping("face", self.internal_registry) - if hasattr(self, "snappy_body_registry") is True and self.snappy_body_registry: + if self.snappy_body_registry is not None: self.snappy_body_registry = self.snappy_body._reset_grouping( "face", self.snappy_body_registry ) diff --git a/flow360/component/simulation/entity_info.py b/flow360/component/simulation/entity_info.py index e5ba89042..47cc354d2 100644 --- a/flow360/component/simulation/entity_info.py +++ b/flow360/component/simulation/entity_info.py @@ -44,8 +44,6 @@ pd.Field(discriminator="private_attribute_entity_type_name"), ] -GROUPED_SNAPPY = "Grouped with snappy name formatting." - class EntityInfoModel(Flow360BaseModel, metaclass=ABCMeta): """Base model for asset entity info JSON""" @@ -212,8 +210,6 @@ def _get_list_of_entities( if specified_attribute_name in entity_attribute_names: # pylint: disable=no-member, unsubscriptable-object return entity_full_list[entity_attribute_names.index(specified_attribute_name)] - if specified_attribute_name == GROUPED_SNAPPY: - return self._get_snappy_bodies() raise ValueError( f"The given attribute_name `{attribute_name}` is not found" @@ -225,11 +221,6 @@ def get_boundaries(self, attribute_name: str = None) -> list[Surface]: Get the full list of boundaries. If attribute_name is supplied then ignore stored face_group_tag and use supplied one. """ - if self.face_group_tag == GROUPED_SNAPPY and attribute_name is None: - all_boundaries = [] - for body in self._get_list_of_entities(attribute_name, "snappy_body"): - all_boundaries += body.surfaces - return all_boundaries return self._get_list_of_entities(attribute_name, "face") def update_persistent_entities(self, *, asset_entity_registry: EntityRegistry) -> None: @@ -382,23 +373,7 @@ def _group_entity_by_tag( def _group_faces_by_snappy_format(self): registry = SnappyBodyRegistry() - existing_face_tag = None - if self.face_group_tag is not None: - existing_face_tag = self.face_group_tag - - if existing_face_tag: - if existing_face_tag != GROUPED_SNAPPY: - log.info( - f"Regrouping face entities using snappy name formatting (previous `{GROUPED_SNAPPY}`)." - ) - registry = self._reset_grouping(entity_type_name="face", registry=registry) - - registry = self.group_in_registry( - "snappy_body", attribute_name=GROUPED_SNAPPY, registry=registry - ) - - with model_attribute_unlock(self, "face_group_tag"): - self.face_group_tag = GROUPED_SNAPPY + registry = self.group_in_registry("snappy_body", attribute_name="faceId", registry=registry) return registry diff --git a/flow360/component/simulation/primitives.py b/flow360/component/simulation/primitives.py index 8afe0bf05..d1800d4b0 100644 --- a/flow360/component/simulation/primitives.py +++ b/flow360/component/simulation/primitives.py @@ -728,6 +728,11 @@ class SnappyBody(EntityBase): "SurfaceGroupedEntityType", frozen=True ) private_attribute_entity_type_name: Literal["SnappyBody"] = pd.Field("SnappyBody", frozen=True) + private_attribute_id: str = pd.Field( + default_factory=generate_uuid, + frozen=True, + description="Unique identifier for the entity. Used by front end to track entities and enable auto update etc.", + ) surfaces: List[Surface] = pd.Field()