From 7e560879f8b43c012f0c54fe6f038b142a9949ff Mon Sep 17 00:00:00 2001 From: Umut Soysal Date: Tue, 28 Oct 2025 17:09:11 -0500 Subject: [PATCH 01/18] adding tracker to components. --- .../geometry/core/_grpc/_services/v0/prepare_tools.py | 9 ++++++++- src/ansys/geometry/core/tools/prepare_tools.py | 3 ++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/ansys/geometry/core/_grpc/_services/v0/prepare_tools.py b/src/ansys/geometry/core/_grpc/_services/v0/prepare_tools.py index b783b952c4..9140b46e69 100644 --- a/src/ansys/geometry/core/_grpc/_services/v0/prepare_tools.py +++ b/src/ansys/geometry/core/_grpc/_services/v0/prepare_tools.py @@ -27,7 +27,7 @@ from ..base.conversions import from_measurement_to_server_length from ..base.prepare_tools import GRPCPrepareToolsService -from .conversions import build_grpc_id +from .conversions import build_grpc_id, serialize_tracker_command_response class GRPCPrepareToolsServiceV0(GRPCPrepareToolsService): @@ -66,6 +66,7 @@ def extract_volume_from_faces(self, **kwargs) -> dict: # noqa: D102 return { "success": response.success, "created_bodies": [body.id for body in response.created_bodies], + "tracker_response": response.changes, } @protect_grpc @@ -81,10 +82,16 @@ def extract_volume_from_edge_loops(self, **kwargs) -> dict: # noqa: D102 # Call the gRPC service response = self.stub.ExtractVolumeFromEdgeLoops(request) + serialized_tracker_response = serialize_tracker_command_response( + response=response.complete_command_response + ) + # Return the response - formatted as a dictionary return { "success": response.success, "created_bodies": [body.id for body in response.created_bodies], + "complete_command_response": serialized_tracker_response, + } @protect_grpc diff --git a/src/ansys/geometry/core/tools/prepare_tools.py b/src/ansys/geometry/core/tools/prepare_tools.py index 30c4d66046..a0b1fc22a7 100644 --- a/src/ansys/geometry/core/tools/prepare_tools.py +++ b/src/ansys/geometry/core/tools/prepare_tools.py @@ -125,7 +125,8 @@ def extract_volume_from_faces( if response.get("success"): bodies_ids = response.get("created_bodies") if len(bodies_ids) > 0: - parent_design._update_design_inplace() + #parent_design._update_design_inplace() + parent_design._update_from_tracker(response.get("tracker_response")) return get_bodies_from_ids(parent_design, bodies_ids) else: self._grpc_client.log.info("Failed to extract volume from faces...") From ec15305fe09d1eab6f299579dcca485fb66938f7 Mon Sep 17 00:00:00 2001 From: Umut Soysal Date: Mon, 3 Nov 2025 11:05:26 -0600 Subject: [PATCH 02/18] parts and components update methods are added. parts and components update methods are added. --- .../core/_grpc/_services/v0/conversions.py | 44 +- .../core/_grpc/_services/v0/prepare_tools.py | 6 +- src/ansys/geometry/core/designer/design.py | 384 ++++++++++++++++-- .../geometry/core/tools/prepare_tools.py | 2 +- 4 files changed, 406 insertions(+), 30 deletions(-) diff --git a/src/ansys/geometry/core/_grpc/_services/v0/conversions.py b/src/ansys/geometry/core/_grpc/_services/v0/conversions.py index 2d7f8fd82c..37531b7034 100644 --- a/src/ansys/geometry/core/_grpc/_services/v0/conversions.py +++ b/src/ansys/geometry/core/_grpc/_services/v0/conversions.py @@ -1389,6 +1389,26 @@ def serialize_body(body): "parent_id": body.parent_id, "is_surface": body.is_surface, } + + def serialize_component(component): + return { + "id": component.id, + + "can_suppress": component.can_suppress, + "transform_to_master": { + "m00": component.transform_to_master.m00, + "m11": component.transform_to_master.m11, + "m22": component.transform_to_master.m22, + "m33": component.transform_to_master.m33, + }, + "master_id": component.master_id, + "parent_id": component.parent_id, + } + + def serialize_part(part): + return { + "id": part.id, + } def serialize_entity_identifier(entity): """Serialize an EntityIdentifier object into a dictionary.""" @@ -1399,6 +1419,27 @@ def serialize_entity_identifier(entity): response = kwargs["response"] return { "success": response.success, + + "created_parts": [ + serialize_part(part) for part in getattr(response, "created_parts", []) + ], + "modified_parts": [ + serialize_part(part) for part in getattr(response, "modified_parts", []) + ], + "deleted_parts": [ + serialize_entity_identifier(entity) for entity in getattr(response, "deleted_parts", []) + ], + "created_components": [ + serialize_component(component) for component in getattr(response, "created_components", []) + ], + "modified_components": [ + serialize_component(component) for component in getattr(response, "modified_components", []) + ], + "deleted_components": [ + serialize_entity_identifier(entity) + for entity in getattr(response, "deleted_components", []) + ], + "created_bodies": [ serialize_body(body) for body in getattr(response, "created_bodies", []) ], @@ -1406,7 +1447,6 @@ def serialize_entity_identifier(entity): serialize_body(body) for body in getattr(response, "modified_bodies", []) ], "deleted_bodies": [ - serialize_entity_identifier(entity) - for entity in getattr(response, "deleted_bodies", []) + serialize_entity_identifier(entity) for entity in getattr(response, "deleted_bodies", []) ], } diff --git a/src/ansys/geometry/core/_grpc/_services/v0/prepare_tools.py b/src/ansys/geometry/core/_grpc/_services/v0/prepare_tools.py index 9140b46e69..14dc40f1f2 100644 --- a/src/ansys/geometry/core/_grpc/_services/v0/prepare_tools.py +++ b/src/ansys/geometry/core/_grpc/_services/v0/prepare_tools.py @@ -62,11 +62,15 @@ def extract_volume_from_faces(self, **kwargs) -> dict: # noqa: D102 # Call the gRPC service response = self.stub.ExtractVolumeFromFaces(request) + serialized_tracker_response = serialize_tracker_command_response( + response=response.changes + ) + # Return the response - formatted as a dictionary return { "success": response.success, "created_bodies": [body.id for body in response.created_bodies], - "tracker_response": response.changes, + "complete_command_response": serialized_tracker_response, } @protect_grpc diff --git a/src/ansys/geometry/core/designer/design.py b/src/ansys/geometry/core/designer/design.py index a44cdb2c95..906e4561fb 100644 --- a/src/ansys/geometry/core/designer/design.py +++ b/src/ansys/geometry/core/designer/design.py @@ -1358,16 +1358,235 @@ def _update_design_inplace(self) -> None: # Read the existing design self.__read_existing_design() - def _update_from_tracker(self, tracker_response: list[dict]): - """Update the design with the changed bodies while preserving unchanged ones.""" + def _update_from_tracker(self, tracker_response: dict): + """Update the design with the changed entities while preserving unchanged ones. + + Parameters + ---------- + tracker_response : dict + Dictionary containing lists of created, modified, and deleted entities + including parts, components, bodies, faces, edges, and other geometry entities. + Processing order: parts → components → bodies → deletions (reverse dependency order). + """ self._grpc_client.log.debug( f"Starting _update_from_tracker with response: {tracker_response}" ) - self._handle_modified_bodies(tracker_response.get("modified_bodies", [])) - self._handle_deleted_bodies(tracker_response.get("deleted_bodies", [])) - self._handle_created_bodies(tracker_response.get("created_bodies", [])) + + # Handle parts first (foundational dependencies) + self.update_parts( + created_parts=tracker_response.get("created_parts", []), + modified_parts=tracker_response.get("modified_parts", []), + deleted_parts=tracker_response.get("deleted_parts", []) + ) + + # Handle components (depend on parts) + self._update_components( + created_components=tracker_response.get("created_components", []), + modified_components=tracker_response.get("modified_components", []), + deleted_components=tracker_response.get("deleted_components", []) + ) + + # Handle bodies (depend on parts/components) + self._update_bodies( + created_bodies=tracker_response.get("created_bodies", []), + modified_bodies=tracker_response.get("modified_bodies", []), + deleted_bodies=tracker_response.get("deleted_bodies", []) + ) + + def update_parts(self, created_parts=None, modified_parts=None, deleted_parts=None): + """Update parts with consolidated handling of created, modified, and deleted parts. + + Parameters + ---------- + created_parts : list, optional + List of created part information from tracker response. + modified_parts : list, optional + List of modified part information from tracker response. + deleted_parts : list, optional + List of deleted part information from tracker response. + """ + if created_parts: + self._handle_created_parts(created_parts) + if modified_parts: + self._handle_modified_parts(modified_parts) + if deleted_parts: + self._handle_deleted_parts(deleted_parts) + + def _update_components(self, created_components=None, modified_components=None, deleted_components=None): + """Update components with consolidated handling of created, modified, and deleted components. + + Parameters + ---------- + created_components : list, optional + List of created component information from tracker response. + modified_components : list, optional + List of modified component information from tracker response. + deleted_components : list, optional + List of deleted component information from tracker response. + """ + if created_components: + self._handle_created_components(created_components) + if modified_components: + self._handle_modified_components(modified_components) + if deleted_components: + self._handle_deleted_components(deleted_components) + + def _update_bodies(self, created_bodies=None, modified_bodies=None, deleted_bodies=None): + """Update bodies with consolidated handling of created, modified, and deleted bodies. + + Parameters + ---------- + created_bodies : list, optional + List of created body information from tracker response. + modified_bodies : list, optional + List of modified body information from tracker response. + deleted_bodies : list, optional + List of deleted body information from tracker response. + """ + if created_bodies: + self._handle_created_bodies(created_bodies) + if modified_bodies: + self._handle_modified_bodies(modified_bodies) + if deleted_bodies: + self._handle_deleted_bodies(deleted_bodies) + + # ================== PART HANDLERS ================== + + def _handle_created_parts(self, created_parts): + """Handle creation of new parts from tracker response.""" + for part_info in created_parts: + part_id = part_info["id"] + part_name = part_info.get("name", f"Part_{part_id}") + self._grpc_client.log.debug( + f"Processing created part: ID={part_id}, Name='{part_name}'" + ) + + # Check if part already exists + if self._find_existing_part(part_id): + self._grpc_client.log.debug( + f"Created part '{part_name}' (ID: {part_id}) already exists." + ) + continue + + # Create new part + new_part = Part(part_id, part_name, [], []) + # TODO: Add part to appropriate collection/registry + self._grpc_client.log.debug( + f"Created new part '{part_name}' (ID: {part_id})" + ) + + def _handle_modified_parts(self, modified_parts): + """Handle modification of existing parts from tracker response.""" + for part_info in modified_parts: + part_id = part_info["id"] + part_name = part_info.get("name", f"Part_{part_id}") + self._grpc_client.log.debug( + f"Processing modified part: ID={part_id}, Name='{part_name}'" + ) + + # Try to find and update the part + updated = self._find_and_update_part(part_info) + if not updated: + self._grpc_client.log.warning( + f"Could not find part to update: '{part_name}' (ID: {part_id})" + ) + + def _handle_deleted_parts(self, deleted_parts): + """Handle deletion of parts from tracker response.""" + for part_info in deleted_parts: + part_id = part_info["id"] + self._grpc_client.log.debug(f"Processing deleted part: ID={part_id}") + + # Try to find and remove the part + removed = self._find_and_remove_part(part_info) + if not removed: + self._grpc_client.log.warning( + f"Could not find part to delete: ID={part_id}" + ) + + # ================== COMPONENT HANDLERS ================== + + def _handle_created_components(self, created_components): + """Handle creation of new components from tracker response.""" + for component_info in created_components: + component_id = component_info["id"] + component_name = component_info.get("name", f"Component_{component_id}") + self._grpc_client.log.debug( + f"Processing created component: ID={component_id}, Name='{component_name}'" + ) + + # Check if component already exists + if any(comp.id == component_id for comp in self.components): + self._grpc_client.log.debug( + f"Created component '{component_name}' (ID: {component_id}) already exists." + ) + continue + + # Try to add the component to the appropriate parent + added = self._find_and_add_component(component_info, self.components) + if not added: + self._grpc_client.log.warning( + f"Could not find parent for component '{component_name}' (ID: {component_id})" + ) + + def _handle_modified_components(self, modified_components): + """Handle modification of existing components from tracker response.""" + for component_info in modified_components: + component_id = component_info["id"] + component_name = component_info.get("name", f"Component_{component_id}") + self._grpc_client.log.debug( + f"Processing modified component: ID={component_id}, Name='{component_name}'" + ) + + # Try to find and update the component + updated = self._find_and_update_component(component_info, self.components) + if not updated: + self._grpc_client.log.warning( + f"Could not find component to update: '{component_name}' (ID: {component_id})" + ) + + def _handle_deleted_components(self, deleted_components): + """Handle deletion of components from tracker response.""" + for component_info in deleted_components: + component_id = component_info["id"] + self._grpc_client.log.debug(f"Processing deleted component: ID={component_id}") + + # Try to find and remove the component + removed = self._find_and_remove_component(component_info, self.components) + if not removed: + self._grpc_client.log.warning( + f"Could not find component to delete: ID={component_id}" + ) + + # ================== BODY HANDLERS ================== + + def _handle_created_bodies(self, created_bodies): + """Handle creation of new bodies from tracker response.""" + for body_info in created_bodies: + body_id = body_info["id"] + body_name = body_info["name"] + is_surface = body_info.get("is_surface", False) + self._grpc_client.log.debug( + f"Processing created body: ID={body_id}, Name='{body_name}'" + ) + + if any(body.id == body_id for body in self.bodies): + self._grpc_client.log.debug( + f"Created body '{body_name}' (ID: {body_id}) already exists at root level." + ) + continue + + added = self._find_and_add_body(body_info, self.components) + if not added: + new_body = MasterBody(body_id, body_name, self._grpc_client, is_surface=is_surface) + self._master_component.part.bodies.append(new_body) + self._clear_cached_bodies() + self._grpc_client.log.debug( + f"Added new body '{body_name}' (ID: {body_id}) to root level." + ) def _handle_modified_bodies(self, modified_bodies): + """Handle modification of existing bodies from tracker response.""" for body_info in modified_bodies: body_id = body_info["id"] body_name = body_info["name"] @@ -1391,6 +1610,7 @@ def _handle_modified_bodies(self, modified_bodies): break def _handle_deleted_bodies(self, deleted_bodies): + """Handle deletion of bodies from tracker response.""" for body_info in deleted_bodies: body_id = body_info["id"] self._grpc_client.log.debug(f"Processing deleted body: ID={body_id}") @@ -1415,31 +1635,142 @@ def _handle_deleted_bodies(self, deleted_bodies): if self._find_and_remove_body(body_info, component): break - def _handle_created_bodies(self, created_bodies): - for body_info in created_bodies: - body_id = body_info["id"] - body_name = body_info["name"] - is_surface = body_info.get("is_surface", False) + # ================== HELPER METHODS ================== + # + # Processing order for tracker updates: + # 1. Parts (foundational - no dependencies) + # 2. Components (depend on parts via master_component.part) + # 3. Bodies (depend on parts/components as containers) + # 4. Deletions (reverse order to avoid dependency issues) + + def _find_existing_part(self, part_id): + """Find if a part with the given ID already exists.""" + # Search through master component parts + if hasattr(self, '_master_component') and self._master_component: + if self._master_component.part.id == part_id: + return self._master_component.part + + # Search through all component master parts + for component in self._get_all_components(): + if (hasattr(component, '_master_component') and + component._master_component and + component._master_component.part.id == part_id): + return component._master_component.part + + return None + + def _get_all_components(self): + """Get all components in the hierarchy recursively.""" + all_components = [] + + def _collect_components(components): + for comp in components: + all_components.append(comp) + _collect_components(comp.components) + + _collect_components(self.components) + return all_components + + def _find_and_update_part(self, part_info): + """Find and update an existing part.""" + part_id = part_info["id"] + existing_part = self._find_existing_part(part_id) + + if existing_part: + # Update part properties + if "name" in part_info: + existing_part._name = part_info["name"] self._grpc_client.log.debug( - f"Processing created body: ID={body_id}, Name='{body_name}'" + f"Updated part '{existing_part.name}' (ID: {part_id})" ) - - if any(body.id == body_id for body in self.bodies): + return True + + return False + + def _find_and_remove_part(self, part_info): + """Find and remove a part from the design.""" + part_id = part_info["id"] + existing_part = self._find_existing_part(part_id) + + if existing_part: + # Mark as not alive (if applicable) + if hasattr(existing_part, '_is_alive'): + existing_part._is_alive = False + self._grpc_client.log.debug( + f"Removed part (ID: {part_id})" + ) + # TODO: Implement actual removal logic based on where parts are stored + return True + + return False + + def _find_and_add_component(self, component_info, parent_components): + """Recursively find the appropriate parent and add a new component to it.""" + parent_id = component_info.get("parent_id") + + # Check if this should be added to the root design + if parent_id == self.id: + # TODO: Create new component and add to self.components + # This requires proper Component instantiation logic + self._grpc_client.log.debug( + f"Would add component '{component_info['id']}' to root design" + ) + return True + + # Search through existing components for the parent + for component in parent_components: + if component.id == parent_id: + # TODO: Create new component and add to component.components self._grpc_client.log.debug( - f"Created body '{body_name}' (ID: {body_id}) already exists at root level." + f"Would add component '{component_info['id']}' to component '{component.name}'" ) - continue - - added = self._find_and_add_body(body_info, self.components) - if not added: - new_body = MasterBody(body_id, body_name, self._grpc_client, is_surface=is_surface) - self._master_component.part.bodies.append(new_body) - self._clear_cached_bodies() + return True + + if self._find_and_add_component(component_info, component.components): + return True + + return False + + def _find_and_update_component(self, component_info, components): + """Recursively find and update an existing component in the hierarchy.""" + component_id = component_info["id"] + + for component in components: + if component.id == component_id: + # Update component properties + if "name" in component_info: + component._name = component_info["name"] self._grpc_client.log.debug( - f"Added new body '{body_name}' (ID: {body_id}) to root level." + f"Updated component '{component.name}' (ID: {component.id})" ) - + return True + + if self._find_and_update_component(component_info, component.components): + return True + + return False + + def _find_and_remove_component(self, component_info, components, parent_component=None): + """Recursively find and remove a component from the hierarchy.""" + component_id = component_info["id"] + + for i, component in enumerate(components): + if component.id == component_id: + component._is_alive = False + components.pop(i) + self._grpc_client.log.debug( + f"Removed component '{component.name}' (ID: {component_id}) " + f"from {'root design' if parent_component is None else parent_component.name}" + ) + return True + + if self._find_and_remove_component(component_info, component.components, component): + return True + + return False + def _update_body(self, existing_body, body_info): + """Update an existing body with new information from tracker response.""" self._grpc_client.log.debug( f"Updating body '{existing_body.name}' " f"(ID: {existing_body.id}) with new info: {body_info}" @@ -1448,16 +1779,16 @@ def _update_body(self, existing_body, body_info): existing_body._template._is_surface = body_info.get("is_surface", False) def _find_and_add_body(self, body_info, components): + """Recursively find the appropriate component and add a new body to it.""" for component in components: parent_id_for_body = component._master_component.part.id - if parent_id_for_body == body_info["parent_id"]: + if parent_id_for_body == body_info.get("parent_id"): new_body = MasterBody( body_info["id"], body_info["name"], self._grpc_client, is_surface=body_info.get("is_surface", False), ) - # component.bodies.append(new_body) component._master_component.part.bodies.append(new_body) component._clear_cached_bodies() self._grpc_client.log.debug( @@ -1472,6 +1803,7 @@ def _find_and_add_body(self, body_info, components): return False def _find_and_update_body(self, body_info, component): + """Recursively find and update an existing body in the component hierarchy.""" for body in component.bodies: if body.id == body_info["id"]: self._update_body(body, body_info) @@ -1488,11 +1820,11 @@ def _find_and_update_body(self, body_info, component): return False def _find_and_remove_body(self, body_info, component): + """Recursively find and remove a body from the component hierarchy.""" for body in component.bodies: body_info_id = body_info["id"] if body.id == f"{component.id}/{body_info_id}": body._is_alive = False - # component.bodies.remove(body) for bd in component._master_component.part.bodies: if bd.id == body_info_id: component._master_component.part.bodies.remove(bd) diff --git a/src/ansys/geometry/core/tools/prepare_tools.py b/src/ansys/geometry/core/tools/prepare_tools.py index a0b1fc22a7..fc314c33da 100644 --- a/src/ansys/geometry/core/tools/prepare_tools.py +++ b/src/ansys/geometry/core/tools/prepare_tools.py @@ -126,7 +126,7 @@ def extract_volume_from_faces( bodies_ids = response.get("created_bodies") if len(bodies_ids) > 0: #parent_design._update_design_inplace() - parent_design._update_from_tracker(response.get("tracker_response")) + parent_design._update_from_tracker(response.get("complete_command_response")) return get_bodies_from_ids(parent_design, bodies_ids) else: self._grpc_client.log.info("Failed to extract volume from faces...") From 52d1011b7ba867a8d1888119addf025d2e35ccf9 Mon Sep 17 00:00:00 2001 From: Umut Soysal Date: Wed, 5 Nov 2025 16:48:52 -0600 Subject: [PATCH 03/18] temp hack to deal with flukes. --- src/ansys/geometry/core/_grpc/_services/v0/admin.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/ansys/geometry/core/_grpc/_services/v0/admin.py b/src/ansys/geometry/core/_grpc/_services/v0/admin.py index 902a9a6b49..8727c4bb30 100644 --- a/src/ansys/geometry/core/_grpc/_services/v0/admin.py +++ b/src/ansys/geometry/core/_grpc/_services/v0/admin.py @@ -71,10 +71,8 @@ def get_backend(self, **kwargs) -> dict: # noqa: D102 if hasattr(response, "version"): ver = response.version backend_version = semver.Version(ver.major_release, ver.minor_release, ver.service_pack) - api_server_build_info = f"{ver.build_number}" if ver.build_number != 0 else "N/A" - product_build_info = ( - response.backend_version_info.strip() if response.backend_version_info else "N/A" - ) + api_server_build_info = "N/A" #f"{ver.build_number}" if ver.build_number != 0 else "N/A" + product_build_info = "N/A" #f"{response.backend_version_info.strip()}" if response.backend_version_info else "N/A" else: # pragma: no cover # If the version is not available, set a default version backend_version = semver.Version(24, 1, 0) @@ -87,7 +85,7 @@ def get_backend(self, **kwargs) -> dict: # noqa: D102 "version": backend_version, "api_server_build_info": api_server_build_info, "product_build_info": product_build_info, - "additional_info": {k: v for k, v in response.additional_build_info.items()}, + "additional_info": "N/A" # {k: v for k, v in response.additional_build_info.items()}, } @protect_grpc From f78c49121384d6ea5fa3e1716a98afcd45eed95e Mon Sep 17 00:00:00 2001 From: Umut Soysal Date: Wed, 5 Nov 2025 16:57:51 -0600 Subject: [PATCH 04/18] response name is changed in backend. --- .../core/_grpc/_services/v0/conversions.py | 26 ++++++++++++------- .../geometry/core/tools/prepare_tools.py | 2 +- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/ansys/geometry/core/_grpc/_services/v0/conversions.py b/src/ansys/geometry/core/_grpc/_services/v0/conversions.py index 37531b7034..f784fd4034 100644 --- a/src/ansys/geometry/core/_grpc/_services/v0/conversions.py +++ b/src/ansys/geometry/core/_grpc/_services/v0/conversions.py @@ -1393,16 +1393,24 @@ def serialize_body(body): def serialize_component(component): return { "id": component.id, - - "can_suppress": component.can_suppress, - "transform_to_master": { - "m00": component.transform_to_master.m00, - "m11": component.transform_to_master.m11, - "m22": component.transform_to_master.m22, - "m33": component.transform_to_master.m33, + "name": getattr(component, "name", ""), + "display_name": getattr(component, "display_name", ""), + "part_occurrence": { + "id": getattr(component.part_occurrence, "id", "") if hasattr(component, "part_occurrence") else "", + "name": getattr(component.part_occurrence, "name", "") if hasattr(component, "part_occurrence") else "", + } if hasattr(component, "part_occurrence") else None, + "placement": { + "m00": getattr(component.placement, "m00", 1.0) if hasattr(component, "placement") else 1.0, + "m11": getattr(component.placement, "m11", 1.0) if hasattr(component, "placement") else 1.0, + "m22": getattr(component.placement, "m22", 1.0) if hasattr(component, "placement") else 1.0, + "m33": getattr(component.placement, "m33", 1.0) if hasattr(component, "placement") else 1.0, }, - "master_id": component.master_id, - "parent_id": component.parent_id, + "part_master": { + "id": getattr(component.part_master, "id", "") if hasattr(component, "part_master") else "", + "name": getattr(component.part_master, "name", "") if hasattr(component, "part_master") else "", + } if hasattr(component, "part_master") else None, + "master_id": getattr(component, "master_id", ""), + "parent_id": getattr(component, "parent_id", ""), } def serialize_part(part): diff --git a/src/ansys/geometry/core/tools/prepare_tools.py b/src/ansys/geometry/core/tools/prepare_tools.py index fc314c33da..285c6c8a26 100644 --- a/src/ansys/geometry/core/tools/prepare_tools.py +++ b/src/ansys/geometry/core/tools/prepare_tools.py @@ -125,8 +125,8 @@ def extract_volume_from_faces( if response.get("success"): bodies_ids = response.get("created_bodies") if len(bodies_ids) > 0: + parent_design._update_from_tracker(response.get("changes")) #parent_design._update_design_inplace() - parent_design._update_from_tracker(response.get("complete_command_response")) return get_bodies_from_ids(parent_design, bodies_ids) else: self._grpc_client.log.info("Failed to extract volume from faces...") From f6c12728d2166bbe972a4a2d974b9000beff2785 Mon Sep 17 00:00:00 2001 From: Umut Soysal Date: Wed, 5 Nov 2025 17:37:04 -0600 Subject: [PATCH 05/18] rename fix --- src/ansys/geometry/core/tools/prepare_tools.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ansys/geometry/core/tools/prepare_tools.py b/src/ansys/geometry/core/tools/prepare_tools.py index 285c6c8a26..d82d6f0ac9 100644 --- a/src/ansys/geometry/core/tools/prepare_tools.py +++ b/src/ansys/geometry/core/tools/prepare_tools.py @@ -125,7 +125,7 @@ def extract_volume_from_faces( if response.get("success"): bodies_ids = response.get("created_bodies") if len(bodies_ids) > 0: - parent_design._update_from_tracker(response.get("changes")) + parent_design._update_from_tracker(response.get("complete_command_response")) #parent_design._update_design_inplace() return get_bodies_from_ids(parent_design, bodies_ids) else: From 0e470ab8aac6308b42e3422185dc4693c79bcdab Mon Sep 17 00:00:00 2001 From: Umut Soysal Date: Wed, 5 Nov 2025 18:56:48 -0600 Subject: [PATCH 06/18] update methods to return the componets and parts --- src/ansys/geometry/core/designer/design.py | 225 +++++++++++++++++---- 1 file changed, 190 insertions(+), 35 deletions(-) diff --git a/src/ansys/geometry/core/designer/design.py b/src/ansys/geometry/core/designer/design.py index 906e4561fb..cdd5635e8f 100644 --- a/src/ansys/geometry/core/designer/design.py +++ b/src/ansys/geometry/core/designer/design.py @@ -1372,25 +1372,33 @@ def _update_from_tracker(self, tracker_response: dict): f"Starting _update_from_tracker with response: {tracker_response}" ) + # Track created entities for use in subsequent steps + created_parts_dict = {} + created_components_dict = {} + created_bodies_dict = {} + # Handle parts first (foundational dependencies) - self.update_parts( + created_parts_dict = self.update_parts( created_parts=tracker_response.get("created_parts", []), modified_parts=tracker_response.get("modified_parts", []), deleted_parts=tracker_response.get("deleted_parts", []) ) # Handle components (depend on parts) - self._update_components( + created_components_dict = self._update_components( created_components=tracker_response.get("created_components", []), modified_components=tracker_response.get("modified_components", []), - deleted_components=tracker_response.get("deleted_components", []) + deleted_components=tracker_response.get("deleted_components", []), + created_parts=created_parts_dict ) # Handle bodies (depend on parts/components) - self._update_bodies( + created_bodies_dict = self._update_bodies( created_bodies=tracker_response.get("created_bodies", []), modified_bodies=tracker_response.get("modified_bodies", []), - deleted_bodies=tracker_response.get("deleted_bodies", []) + deleted_bodies=tracker_response.get("deleted_bodies", []), + created_parts=created_parts_dict, + created_components=created_components_dict ) def update_parts(self, created_parts=None, modified_parts=None, deleted_parts=None): @@ -1404,15 +1412,24 @@ def update_parts(self, created_parts=None, modified_parts=None, deleted_parts=No List of modified part information from tracker response. deleted_parts : list, optional List of deleted part information from tracker response. + + Returns + ------- + dict + Dictionary of created parts with part_id as key and Part object as value. """ + created_parts_dict = {} + if created_parts: - self._handle_created_parts(created_parts) + created_parts_dict = self._handle_created_parts(created_parts) if modified_parts: self._handle_modified_parts(modified_parts) if deleted_parts: self._handle_deleted_parts(deleted_parts) + + return created_parts_dict - def _update_components(self, created_components=None, modified_components=None, deleted_components=None): + def _update_components(self, created_components=None, modified_components=None, deleted_components=None, created_parts=None): """Update components with consolidated handling of created, modified, and deleted components. Parameters @@ -1423,15 +1440,26 @@ def _update_components(self, created_components=None, modified_components=None, List of modified component information from tracker response. deleted_components : list, optional List of deleted component information from tracker response. + created_parts : dict, optional + Dictionary of created parts from previous step. + + Returns + ------- + dict + Dictionary of created components with component_id as key and Component object as value. """ + created_components_dict = {} + if created_components: - self._handle_created_components(created_components) + created_components_dict = self._handle_created_components(created_components, created_parts) if modified_components: self._handle_modified_components(modified_components) if deleted_components: self._handle_deleted_components(deleted_components) + + return created_components_dict - def _update_bodies(self, created_bodies=None, modified_bodies=None, deleted_bodies=None): + def _update_bodies(self, created_bodies=None, modified_bodies=None, deleted_bodies=None, created_parts=None, created_components=None): """Update bodies with consolidated handling of created, modified, and deleted bodies. Parameters @@ -1442,18 +1470,39 @@ def _update_bodies(self, created_bodies=None, modified_bodies=None, deleted_bodi List of modified body information from tracker response. deleted_bodies : list, optional List of deleted body information from tracker response. + created_parts : dict, optional + Dictionary of created parts from previous step. + created_components : dict, optional + Dictionary of created components from previous step. + + Returns + ------- + dict + Dictionary of created bodies with body_id as key and Body object as value. """ + created_bodies_dict = {} + if created_bodies: - self._handle_created_bodies(created_bodies) + created_bodies_dict = self._handle_created_bodies(created_bodies, created_parts, created_components) if modified_bodies: self._handle_modified_bodies(modified_bodies) if deleted_bodies: self._handle_deleted_bodies(deleted_bodies) + + return created_bodies_dict # ================== PART HANDLERS ================== def _handle_created_parts(self, created_parts): - """Handle creation of new parts from tracker response.""" + """Handle creation of new parts from tracker response. + + Returns + ------- + dict + Dictionary of created parts with part_id as key and Part object as value. + """ + created_parts_dict = {} + for part_info in created_parts: part_id = part_info["id"] part_name = part_info.get("name", f"Part_{part_id}") @@ -1470,11 +1519,14 @@ def _handle_created_parts(self, created_parts): # Create new part new_part = Part(part_id, part_name, [], []) + created_parts_dict[part_id] = new_part # TODO: Add part to appropriate collection/registry self._grpc_client.log.debug( f"Created new part '{part_name}' (ID: {part_id})" ) + return created_parts_dict + def _handle_modified_parts(self, modified_parts): """Handle modification of existing parts from tracker response.""" for part_info in modified_parts: @@ -1506,8 +1558,23 @@ def _handle_deleted_parts(self, deleted_parts): # ================== COMPONENT HANDLERS ================== - def _handle_created_components(self, created_components): - """Handle creation of new components from tracker response.""" + def _handle_created_components(self, created_components, created_parts=None): + """Handle creation of new components from tracker response. + + Parameters + ---------- + created_components : list + List of created component information from tracker response. + created_parts : dict, optional + Dictionary of created parts from previous step. + + Returns + ------- + dict + Dictionary of created components with component_id as key and Component object as value. + """ + created_components_dict = {} + for component_info in created_components: component_id = component_info["id"] component_name = component_info.get("name", f"Component_{component_id}") @@ -1523,11 +1590,15 @@ def _handle_created_components(self, created_components): continue # Try to add the component to the appropriate parent - added = self._find_and_add_component(component_info, self.components) - if not added: + new_component = self._find_and_add_component(component_info, self.components, created_parts) + if new_component: + created_components_dict[component_id] = new_component + else: self._grpc_client.log.warning( f"Could not find parent for component '{component_name}' (ID: {component_id})" ) + + return created_components_dict def _handle_modified_components(self, modified_components): """Handle modification of existing components from tracker response.""" @@ -1560,8 +1631,25 @@ def _handle_deleted_components(self, deleted_components): # ================== BODY HANDLERS ================== - def _handle_created_bodies(self, created_bodies): - """Handle creation of new bodies from tracker response.""" + def _handle_created_bodies(self, created_bodies, created_parts=None, created_components=None): + """Handle creation of new bodies from tracker response. + + Parameters + ---------- + created_bodies : list + List of created body information from tracker response. + created_parts : dict, optional + Dictionary of created parts from previous step. + created_components : dict, optional + Dictionary of created components from previous step. + + Returns + ------- + dict + Dictionary of created bodies with body_id as key and Body object as value. + """ + created_bodies_dict = {} + for body_info in created_bodies: body_id = body_info["id"] body_name = body_info["name"] @@ -1576,14 +1664,19 @@ def _handle_created_bodies(self, created_bodies): ) continue - added = self._find_and_add_body(body_info, self.components) - if not added: + new_body = self._find_and_add_body(body_info, self.components, created_parts, created_components) + if not new_body: new_body = MasterBody(body_id, body_name, self._grpc_client, is_surface=is_surface) self._master_component.part.bodies.append(new_body) self._clear_cached_bodies() self._grpc_client.log.debug( f"Added new body '{body_name}' (ID: {body_id}) to root level." ) + + if new_body: + created_bodies_dict[body_id] = new_body + + return created_bodies_dict def _handle_modified_bodies(self, modified_bodies): """Handle modification of existing bodies from tracker response.""" @@ -1704,32 +1797,76 @@ def _find_and_remove_part(self, part_info): return False - def _find_and_add_component(self, component_info, parent_components): - """Recursively find the appropriate parent and add a new component to it.""" + def _find_and_add_component(self, component_info, parent_components, created_parts=None): + """Recursively find the appropriate parent and add a new component to it. + + Parameters + ---------- + component_info : dict + Information about the component to create. + parent_components : list + List of parent components to search. + created_parts : dict, optional + Dictionary of created parts from previous step. + + Returns + ------- + Component or None + The newly created component if successful, None otherwise. + """ parent_id = component_info.get("parent_id") # Check if this should be added to the root design if parent_id == self.id: # TODO: Create new component and add to self.components - # This requires proper Component instantiation logic + # Create the Component object + # Find the master part for this component (if available) + master_part = None + if created_parts and component_info.get("master_id"): + master_part = created_parts.get(component_info["master_id"]) + # Create the master component if master_part is found + master_component = None + if master_part: + master_component = MasterComponent( + component_info["master_id"], + f"master_{component_info['name']}", + master_part + ) + # Create the Component object with master_component + new_component = Component( + id=component_info["id"], + name=component_info["name"], + parent=self, + grpc_client=self._grpc_client, + master_component=master_component + ) + self.components.append(new_component) self._grpc_client.log.debug( f"Would add component '{component_info['id']}' to root design" ) - return True - + return new_component + # Search through existing components for the parent for component in parent_components: if component.id == parent_id: # TODO: Create new component and add to component.components + new_component = Component( + id=component_info["id"], + name=component_info["name"], + parent=component, + grpc_client=self._grpc_client + ) + component.components.append(new_component) self._grpc_client.log.debug( f"Would add component '{component_info['id']}' to component '{component.name}'" ) - return True - - if self._find_and_add_component(component_info, component.components): - return True + return new_component + + result = self._find_and_add_component(component_info, component.components, created_parts) + if result: + return result - return False + return None def _find_and_update_component(self, component_info, components): """Recursively find and update an existing component in the hierarchy.""" @@ -1778,8 +1915,25 @@ def _update_body(self, existing_body, body_info): existing_body.name = body_info["name"] existing_body._template._is_surface = body_info.get("is_surface", False) - def _find_and_add_body(self, body_info, components): - """Recursively find the appropriate component and add a new body to it.""" + def _find_and_add_body(self, body_info, components, created_parts=None, created_components=None): + """Recursively find the appropriate component and add a new body to it. + + Parameters + ---------- + body_info : dict + Information about the body to create. + components : list + List of components to search. + created_parts : dict, optional + Dictionary of created parts from previous step. + created_components : dict, optional + Dictionary of created components from previous step. + + Returns + ------- + MasterBody or None + The newly created body if successful, None otherwise. + """ for component in components: parent_id_for_body = component._master_component.part.id if parent_id_for_body == body_info.get("parent_id"): @@ -1795,12 +1949,13 @@ def _find_and_add_body(self, body_info, components): f"Added new body '{new_body.name}' (ID: {new_body.id}) " f"to component '{component.name}' (ID: {component.id})" ) - return True + return new_body - if self._find_and_add_body(body_info, component.components): - return True + result = self._find_and_add_body(body_info, component.components, created_parts, created_components) + if result: + return result - return False + return None def _find_and_update_body(self, body_info, component): """Recursively find and update an existing body in the component hierarchy.""" From 42a75d5f1cb45c9e4925445452c82a6fe4c25538 Mon Sep 17 00:00:00 2001 From: Umut Soysal Date: Thu, 6 Nov 2025 11:59:49 -0600 Subject: [PATCH 07/18] adding created components. --- src/ansys/geometry/core/designer/design.py | 300 +++++++++++++++++---- 1 file changed, 242 insertions(+), 58 deletions(-) diff --git a/src/ansys/geometry/core/designer/design.py b/src/ansys/geometry/core/designer/design.py index cdd5635e8f..e50fe59ede 100644 --- a/src/ansys/geometry/core/designer/design.py +++ b/src/ansys/geometry/core/designer/design.py @@ -1374,32 +1374,220 @@ def _update_from_tracker(self, tracker_response: dict): # Track created entities for use in subsequent steps created_parts_dict = {} + created_master_components_dict = {} created_components_dict = {} created_bodies_dict = {} - # Handle parts first (foundational dependencies) - created_parts_dict = self.update_parts( - created_parts=tracker_response.get("created_parts", []), - modified_parts=tracker_response.get("modified_parts", []), - deleted_parts=tracker_response.get("deleted_parts", []) - ) + # ================== HANDLE PARTS ================== - # Handle components (depend on parts) - created_components_dict = self._update_components( - created_components=tracker_response.get("created_components", []), - modified_components=tracker_response.get("modified_components", []), - deleted_components=tracker_response.get("deleted_components", []), - created_parts=created_parts_dict - ) + # Handle created parts + for part_info in tracker_response.get("created_parts", []): + part_id = part_info["id"] + part_name = part_info.get("name", f"Part_{part_id}") + self._grpc_client.log.debug( + f"Processing created part: ID={part_id}, Name='{part_name}'" + ) + + # Check if part already exists + existing_part = self._find_existing_part(part_id) + if existing_part: + self._grpc_client.log.debug( + f"Created part '{part_name}' (ID: {part_id}) already exists." + ) + continue + + # Create new part + new_part = Part(part_id, part_name, [], []) + created_parts_dict[part_id] = new_part + # TODO: Add part to appropriate collection/registry + self._grpc_client.log.debug( + f"Created new part '{part_name}' (ID: {part_id})" + ) - # Handle bodies (depend on parts/components) - created_bodies_dict = self._update_bodies( - created_bodies=tracker_response.get("created_bodies", []), - modified_bodies=tracker_response.get("modified_bodies", []), - deleted_bodies=tracker_response.get("deleted_bodies", []), - created_parts=created_parts_dict, - created_components=created_components_dict - ) + # Handle modified parts + # Do nothing for now, because this will almost always have the root part. + + # Handle deleted parts + for part_info in tracker_response.get("deleted_parts", []): + part_id = part_info["id"] + self._grpc_client.log.debug(f"Processing deleted part: ID={part_id}") + + existing_part = self._find_existing_part(part_id) + if existing_part: + # Mark as not alive (if applicable) + if hasattr(existing_part, '_is_alive'): + existing_part._is_alive = False + self._grpc_client.log.debug(f"Removed part (ID: {part_id})") + # TODO: Implement actual removal logic based on where parts are stored + else: + self._grpc_client.log.warning(f"Could not find part to delete: ID={part_id}") + + # ================== HANDLE COMPONENTS ================== + + + + # Handle created master components + for component_info in tracker_response.get("created_components", []): + + # Check and create master components. + if component_info.get("id") == component_info.get("master_id"): + # This is a MasterComponent + master_part_id = component_info.get("part_master").get("id") + master_part = created_parts_dict.get(master_part_id) or self._find_existing_part(master_part_id) + if not master_part: + self._grpc_client.log.warning( + f"Could not find part for MasterComponent ID={component_info.get('id')}" + ) + continue + + new_master = MasterComponent( + component_info["id"], + component_info.get("name", f"MasterComponent_{component_info['id']}"), + master_part, + component_info.get("placement"), + ) + created_master_components_dict[component_info["id"]] = new_master + self._grpc_client.log.debug( + f"Created new MasterComponent: ID={new_master.id}, Name='{new_master.name}'" + ) + continue + + + # Handle created occurrence components + for component_info in tracker_response.get("created_components", []): + + # This is an OccurrenceComponent + master_part_id = component_info.get("part_master").get("id") + master_part = created_parts_dict.get(master_part_id) or self._find_existing_part(master_part_id) + if not master_part: + self._grpc_client.log.warning( + f"Could not find part for Component ID={component_info.get('id')}" + ) + continue + + # component = Component( + # component_info["name"], + # parent_component=None, + # grpc_client=self._grpc_client, + # preexisting_id=component_info["id"], + # master_component=created_master_components_dict.get(component_info.get("master_id")), + # read_existing_comp=True, + # ) + + # created_components_dict[component_info["id"]] = component + # self._grpc_client.log.debug( + # f"Created new Component: ID={component.id}, Name='{component.name}'" + # ) + + # # Find and assign parent component + parent_id = component_info.get("parent_id") + self._find_and_add_component(component_info, parent_id, self.components, created_components_dict) + + + + # Handle modified components + for component_info in tracker_response.get("modified_components", []): + component_id = component_info["id"] + component_name = component_info.get("name", f"Component_{component_id}") + self._grpc_client.log.debug( + f"Processing modified component: ID={component_id}, Name='{component_name}'" + ) + + # Try to find and update the component + updated = self._find_and_update_component(component_info, self.components) + if not updated: + self._grpc_client.log.warning( + f"Could not find component to update: '{component_name}' (ID: {component_id})" + ) + + # Handle deleted components + for component_info in tracker_response.get("deleted_components", []): + component_id = component_info["id"] + self._grpc_client.log.debug(f"Processing deleted component: ID={component_id}") + + # Try to find and remove the component + removed = self._find_and_remove_component(component_info, self.components) + if not removed: + self._grpc_client.log.warning( + f"Could not find component to delete: ID={component_id}" + ) + + # ================== HANDLE BODIES ================== + + # Handle created bodies + for body_info in tracker_response.get("created_bodies", []): + body_id = body_info["id"] + body_name = body_info["name"] + is_surface = body_info.get("is_surface", False) + self._grpc_client.log.debug( + f"Processing created body: ID={body_id}, Name='{body_name}'" + ) + + if any(body.id == body_id for body in self.bodies): + self._grpc_client.log.debug( + f"Created body '{body_name}' (ID: {body_id}) already exists at root level." + ) + continue + + new_body = self._find_and_add_body(body_info, self.components, created_parts_dict, created_components_dict) + if not new_body: + new_body = MasterBody(body_id, body_name, self._grpc_client, is_surface=is_surface) + self._master_component.part.bodies.append(new_body) + self._clear_cached_bodies() + self._grpc_client.log.debug( + f"Added new body '{body_name}' (ID: {body_id}) to root level." + ) + + if new_body: + created_bodies_dict[body_id] = new_body + + # Handle modified bodies + for body_info in tracker_response.get("modified_bodies", []): + body_id = body_info["id"] + body_name = body_info["name"] + self._grpc_client.log.debug( + f"Processing modified body: ID={body_id}, Name='{body_name}'" + ) + updated = False + + for body in self.bodies: + if body.id == body_id: + self._update_body(body, body_info) + updated = True + self._grpc_client.log.debug( + f"Modified body '{body_name}' (ID: {body_id}) updated at root level." + ) + break + + if not updated: + for component in self.components: + if self._find_and_update_body(body_info, component): + break + + # Handle deleted bodies + for body_info in tracker_response.get("deleted_bodies", []): + body_id = body_info["id"] + self._grpc_client.log.debug(f"Processing deleted body: ID={body_id}") + removed = False + + for body in self.bodies: + if body.id == body_id: + body._is_alive = False + for bd in self._master_component.part.bodies: + if bd.id == body_id: + self._master_component.part.bodies.remove(bd) + break + self._clear_cached_bodies() + removed = True + self._grpc_client.log.info( + f"Deleted body (ID: {body_id}) removed from root level." + ) + break + + if not removed: + for component in self.components: + if self._find_and_remove_body(body_info, component): + break def update_parts(self, created_parts=None, modified_parts=None, deleted_parts=None): """Update parts with consolidated handling of created, modified, and deleted parts. @@ -1797,7 +1985,7 @@ def _find_and_remove_part(self, part_info): return False - def _find_and_add_component(self, component_info, parent_components, created_parts=None): + def _find_and_add_component(self, component_info, parent_components, created_parts=None, created_master_components=None): """Recursively find the appropriate parent and add a new component to it. Parameters @@ -1808,6 +1996,8 @@ def _find_and_add_component(self, component_info, parent_components, created_par List of parent components to search. created_parts : dict, optional Dictionary of created parts from previous step. + created_master_components : dict, optional + Dictionary of created master components from current step. Returns ------- @@ -1815,59 +2005,53 @@ def _find_and_add_component(self, component_info, parent_components, created_par The newly created component if successful, None otherwise. """ parent_id = component_info.get("parent_id") + master_id = component_info.get("master_id") + + # Find the master component for this component + master_component = None + if created_master_components and master_id: + master_component = created_master_components.get(master_id) # Check if this should be added to the root design if parent_id == self.id: - # TODO: Create new component and add to self.components - # Create the Component object - # Find the master part for this component (if available) - master_part = None - if created_parts and component_info.get("master_id"): - master_part = created_parts.get(component_info["master_id"]) - # Create the master component if master_part is found - master_component = None - if master_part: - master_component = MasterComponent( - component_info["master_id"], - f"master_{component_info['name']}", - master_part - ) # Create the Component object with master_component new_component = Component( - id=component_info["id"], + parent_component= None, name=component_info["name"], - parent=self, + template=self, grpc_client=self._grpc_client, - master_component=master_component + master_component=master_component, + preexisting_id=component_info["id"], + read_existing_comp=True, ) self.components.append(new_component) self._grpc_client.log.debug( - f"Would add component '{component_info['id']}' to root design" + f"Added component '{component_info['id']}' to root design" ) return new_component # Search through existing components for the parent for component in parent_components: - if component.id == parent_id: - # TODO: Create new component and add to component.components - new_component = Component( - id=component_info["id"], - name=component_info["name"], - parent=component, - grpc_client=self._grpc_client - ) - component.components.append(new_component) - self._grpc_client.log.debug( - f"Would add component '{component_info['id']}' to component '{component.name}'" - ) - return new_component - - result = self._find_and_add_component(component_info, component.components, created_parts) - if result: - return result - + # if component.id == parent_id: + # new_component = Component( + # name=component_info["name"], + # template=component, + # grpc_client=self._grpc_client, + # master_component=master_component, + # preexisting_id=component_info["id"], + # read_existing_comp=True, + # ) + # component.components.append(new_component) + # self._grpc_client.log.debug( + # f"Added component '{component_info['id']}' to component '{component.name}'" + # ) + # return new_component + + self._find_and_add_component(component_info, component.components, created_parts, created_master_components) + return None + # This method is subject to change based on how component updates are defined. def _find_and_update_component(self, component_info, components): """Recursively find and update an existing component in the hierarchy.""" component_id = component_info["id"] From d1045ffee2b460eeb15d258784ba489606cf27b7 Mon Sep 17 00:00:00 2001 From: Umut Soysal Date: Thu, 6 Nov 2025 12:38:55 -0600 Subject: [PATCH 08/18] Update prepare_tools.py --- src/ansys/geometry/core/tools/prepare_tools.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/ansys/geometry/core/tools/prepare_tools.py b/src/ansys/geometry/core/tools/prepare_tools.py index d82d6f0ac9..3eb7e08d55 100644 --- a/src/ansys/geometry/core/tools/prepare_tools.py +++ b/src/ansys/geometry/core/tools/prepare_tools.py @@ -26,6 +26,7 @@ from beartype import beartype as check_input_types from pint import Quantity +import ansys.geometry.core as pyansys_geometry from ansys.geometry.core.connection import GrpcClient from ansys.geometry.core.connection.backend import BackendType from ansys.geometry.core.errors import GeometryRuntimeError @@ -125,8 +126,11 @@ def extract_volume_from_faces( if response.get("success"): bodies_ids = response.get("created_bodies") if len(bodies_ids) > 0: - parent_design._update_from_tracker(response.get("complete_command_response")) - #parent_design._update_design_inplace() + + if pyansys_geometry.USE_TRACKER_TO_UPDATE_DESIGN: + parent_design._update_from_tracker(response.get("complete_command_response")) + else: + parent_design._update_design_inplace() return get_bodies_from_ids(parent_design, bodies_ids) else: self._grpc_client.log.info("Failed to extract volume from faces...") From e751e5ad7060ed4d2dd593b164e1c2040aaa9b35 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 6 Nov 2025 18:40:34 +0000 Subject: [PATCH 09/18] chore: auto fixes from pre-commit hooks --- .../geometry/core/_grpc/_services/v0/admin.py | 8 +- .../core/_grpc/_services/v0/conversions.py | 61 +-- .../core/_grpc/_services/v0/prepare_tools.py | 5 +- src/ansys/geometry/core/designer/design.py | 356 +++++++++--------- .../geometry/core/tools/prepare_tools.py | 1 - 5 files changed, 234 insertions(+), 197 deletions(-) diff --git a/src/ansys/geometry/core/_grpc/_services/v0/admin.py b/src/ansys/geometry/core/_grpc/_services/v0/admin.py index 8727c4bb30..0e455ec8c9 100644 --- a/src/ansys/geometry/core/_grpc/_services/v0/admin.py +++ b/src/ansys/geometry/core/_grpc/_services/v0/admin.py @@ -71,8 +71,10 @@ def get_backend(self, **kwargs) -> dict: # noqa: D102 if hasattr(response, "version"): ver = response.version backend_version = semver.Version(ver.major_release, ver.minor_release, ver.service_pack) - api_server_build_info = "N/A" #f"{ver.build_number}" if ver.build_number != 0 else "N/A" - product_build_info = "N/A" #f"{response.backend_version_info.strip()}" if response.backend_version_info else "N/A" + api_server_build_info = ( + "N/A" # f"{ver.build_number}" if ver.build_number != 0 else "N/A" + ) + product_build_info = "N/A" # f"{response.backend_version_info.strip()}" if response.backend_version_info else "N/A" else: # pragma: no cover # If the version is not available, set a default version backend_version = semver.Version(24, 1, 0) @@ -85,7 +87,7 @@ def get_backend(self, **kwargs) -> dict: # noqa: D102 "version": backend_version, "api_server_build_info": api_server_build_info, "product_build_info": product_build_info, - "additional_info": "N/A" # {k: v for k, v in response.additional_build_info.items()}, + "additional_info": "N/A", # {k: v for k, v in response.additional_build_info.items()}, } @protect_grpc diff --git a/src/ansys/geometry/core/_grpc/_services/v0/conversions.py b/src/ansys/geometry/core/_grpc/_services/v0/conversions.py index f784fd4034..d6f0d11c0f 100644 --- a/src/ansys/geometry/core/_grpc/_services/v0/conversions.py +++ b/src/ansys/geometry/core/_grpc/_services/v0/conversions.py @@ -1389,30 +1389,50 @@ def serialize_body(body): "parent_id": body.parent_id, "is_surface": body.is_surface, } - + def serialize_component(component): return { "id": component.id, "name": getattr(component, "name", ""), "display_name": getattr(component, "display_name", ""), "part_occurrence": { - "id": getattr(component.part_occurrence, "id", "") if hasattr(component, "part_occurrence") else "", - "name": getattr(component.part_occurrence, "name", "") if hasattr(component, "part_occurrence") else "", - } if hasattr(component, "part_occurrence") else None, + "id": getattr(component.part_occurrence, "id", "") + if hasattr(component, "part_occurrence") + else "", + "name": getattr(component.part_occurrence, "name", "") + if hasattr(component, "part_occurrence") + else "", + } + if hasattr(component, "part_occurrence") + else None, "placement": { - "m00": getattr(component.placement, "m00", 1.0) if hasattr(component, "placement") else 1.0, - "m11": getattr(component.placement, "m11", 1.0) if hasattr(component, "placement") else 1.0, - "m22": getattr(component.placement, "m22", 1.0) if hasattr(component, "placement") else 1.0, - "m33": getattr(component.placement, "m33", 1.0) if hasattr(component, "placement") else 1.0, + "m00": getattr(component.placement, "m00", 1.0) + if hasattr(component, "placement") + else 1.0, + "m11": getattr(component.placement, "m11", 1.0) + if hasattr(component, "placement") + else 1.0, + "m22": getattr(component.placement, "m22", 1.0) + if hasattr(component, "placement") + else 1.0, + "m33": getattr(component.placement, "m33", 1.0) + if hasattr(component, "placement") + else 1.0, }, "part_master": { - "id": getattr(component.part_master, "id", "") if hasattr(component, "part_master") else "", - "name": getattr(component.part_master, "name", "") if hasattr(component, "part_master") else "", - } if hasattr(component, "part_master") else None, + "id": getattr(component.part_master, "id", "") + if hasattr(component, "part_master") + else "", + "name": getattr(component.part_master, "name", "") + if hasattr(component, "part_master") + else "", + } + if hasattr(component, "part_master") + else None, "master_id": getattr(component, "master_id", ""), "parent_id": getattr(component, "parent_id", ""), } - + def serialize_part(part): return { "id": part.id, @@ -1427,27 +1447,25 @@ def serialize_entity_identifier(entity): response = kwargs["response"] return { "success": response.success, - - "created_parts": [ - serialize_part(part) for part in getattr(response, "created_parts", []) - ], + "created_parts": [serialize_part(part) for part in getattr(response, "created_parts", [])], "modified_parts": [ - serialize_part(part) for part in getattr(response, "modified_parts", []) + serialize_part(part) for part in getattr(response, "modified_parts", []) ], "deleted_parts": [ serialize_entity_identifier(entity) for entity in getattr(response, "deleted_parts", []) ], "created_components": [ - serialize_component(component) for component in getattr(response, "created_components", []) + serialize_component(component) + for component in getattr(response, "created_components", []) ], "modified_components": [ - serialize_component(component) for component in getattr(response, "modified_components", []) + serialize_component(component) + for component in getattr(response, "modified_components", []) ], "deleted_components": [ serialize_entity_identifier(entity) for entity in getattr(response, "deleted_components", []) ], - "created_bodies": [ serialize_body(body) for body in getattr(response, "created_bodies", []) ], @@ -1455,6 +1473,7 @@ def serialize_entity_identifier(entity): serialize_body(body) for body in getattr(response, "modified_bodies", []) ], "deleted_bodies": [ - serialize_entity_identifier(entity) for entity in getattr(response, "deleted_bodies", []) + serialize_entity_identifier(entity) + for entity in getattr(response, "deleted_bodies", []) ], } diff --git a/src/ansys/geometry/core/_grpc/_services/v0/prepare_tools.py b/src/ansys/geometry/core/_grpc/_services/v0/prepare_tools.py index 14dc40f1f2..8adf4faf7a 100644 --- a/src/ansys/geometry/core/_grpc/_services/v0/prepare_tools.py +++ b/src/ansys/geometry/core/_grpc/_services/v0/prepare_tools.py @@ -62,9 +62,7 @@ def extract_volume_from_faces(self, **kwargs) -> dict: # noqa: D102 # Call the gRPC service response = self.stub.ExtractVolumeFromFaces(request) - serialized_tracker_response = serialize_tracker_command_response( - response=response.changes - ) + serialized_tracker_response = serialize_tracker_command_response(response=response.changes) # Return the response - formatted as a dictionary return { @@ -95,7 +93,6 @@ def extract_volume_from_edge_loops(self, **kwargs) -> dict: # noqa: D102 "success": response.success, "created_bodies": [body.id for body in response.created_bodies], "complete_command_response": serialized_tracker_response, - } @protect_grpc diff --git a/src/ansys/geometry/core/designer/design.py b/src/ansys/geometry/core/designer/design.py index e50fe59ede..3dbbd2b597 100644 --- a/src/ansys/geometry/core/designer/design.py +++ b/src/ansys/geometry/core/designer/design.py @@ -1360,7 +1360,7 @@ def _update_design_inplace(self) -> None: def _update_from_tracker(self, tracker_response: dict): """Update the design with the changed entities while preserving unchanged ones. - + Parameters ---------- tracker_response : dict @@ -1371,15 +1371,15 @@ def _update_from_tracker(self, tracker_response: dict): self._grpc_client.log.debug( f"Starting _update_from_tracker with response: {tracker_response}" ) - + # Track created entities for use in subsequent steps created_parts_dict = {} created_master_components_dict = {} created_components_dict = {} created_bodies_dict = {} - + # ================== HANDLE PARTS ================== - + # Handle created parts for part_info in tracker_response.get("created_parts", []): part_id = part_info["id"] @@ -1387,7 +1387,7 @@ def _update_from_tracker(self, tracker_response: dict): self._grpc_client.log.debug( f"Processing created part: ID={part_id}, Name='{part_name}'" ) - + # Check if part already exists existing_part = self._find_existing_part(part_id) if existing_part: @@ -1395,70 +1395,67 @@ def _update_from_tracker(self, tracker_response: dict): f"Created part '{part_name}' (ID: {part_id}) already exists." ) continue - + # Create new part new_part = Part(part_id, part_name, [], []) created_parts_dict[part_id] = new_part # TODO: Add part to appropriate collection/registry - self._grpc_client.log.debug( - f"Created new part '{part_name}' (ID: {part_id})" - ) - + self._grpc_client.log.debug(f"Created new part '{part_name}' (ID: {part_id})") + # Handle modified parts # Do nothing for now, because this will almost always have the root part. - + # Handle deleted parts for part_info in tracker_response.get("deleted_parts", []): part_id = part_info["id"] self._grpc_client.log.debug(f"Processing deleted part: ID={part_id}") - + existing_part = self._find_existing_part(part_id) if existing_part: # Mark as not alive (if applicable) - if hasattr(existing_part, '_is_alive'): + if hasattr(existing_part, "_is_alive"): existing_part._is_alive = False self._grpc_client.log.debug(f"Removed part (ID: {part_id})") # TODO: Implement actual removal logic based on where parts are stored else: self._grpc_client.log.warning(f"Could not find part to delete: ID={part_id}") - - # ================== HANDLE COMPONENTS ================== - + # ================== HANDLE COMPONENTS ================== # Handle created master components - for component_info in tracker_response.get("created_components", []): - + for component_info in tracker_response.get("created_components", []): # Check and create master components. if component_info.get("id") == component_info.get("master_id"): # This is a MasterComponent - master_part_id = component_info.get("part_master").get("id") - master_part = created_parts_dict.get(master_part_id) or self._find_existing_part(master_part_id) - if not master_part: - self._grpc_client.log.warning( - f"Could not find part for MasterComponent ID={component_info.get('id')}" - ) - continue - - new_master = MasterComponent( - component_info["id"], - component_info.get("name", f"MasterComponent_{component_info['id']}"), - master_part, - component_info.get("placement"), - ) - created_master_components_dict[component_info["id"]] = new_master - self._grpc_client.log.debug( - f"Created new MasterComponent: ID={new_master.id}, Name='{new_master.name}'" + master_part_id = component_info.get("part_master").get("id") + master_part = created_parts_dict.get(master_part_id) or self._find_existing_part( + master_part_id + ) + if not master_part: + self._grpc_client.log.warning( + f"Could not find part for MasterComponent ID={component_info.get('id')}" ) continue - - # Handle created occurrence components - for component_info in tracker_response.get("created_components", []): + new_master = MasterComponent( + component_info["id"], + component_info.get("name", f"MasterComponent_{component_info['id']}"), + master_part, + component_info.get("placement"), + ) + created_master_components_dict[component_info["id"]] = new_master + self._grpc_client.log.debug( + f"Created new MasterComponent: ID={new_master.id}, Name='{new_master.name}'" + ) + continue + # Handle created occurrence components + for component_info in tracker_response.get("created_components", []): # This is an OccurrenceComponent master_part_id = component_info.get("part_master").get("id") - master_part = created_parts_dict.get(master_part_id) or self._find_existing_part(master_part_id) + master_part = created_parts_dict.get(master_part_id) or self._find_existing_part( + master_part_id + ) if not master_part: self._grpc_client.log.warning( f"Could not find part for Component ID={component_info.get('id')}" @@ -1478,13 +1475,13 @@ def _update_from_tracker(self, tracker_response: dict): # self._grpc_client.log.debug( # f"Created new Component: ID={component.id}, Name='{component.name}'" # ) - + # # Find and assign parent component parent_id = component_info.get("parent_id") - self._find_and_add_component(component_info, parent_id, self.components, created_components_dict) - + self._find_and_add_component( + component_info, parent_id, self.components, created_components_dict + ) - # Handle modified components for component_info in tracker_response.get("modified_components", []): component_id = component_info["id"] @@ -1492,28 +1489,28 @@ def _update_from_tracker(self, tracker_response: dict): self._grpc_client.log.debug( f"Processing modified component: ID={component_id}, Name='{component_name}'" ) - + # Try to find and update the component updated = self._find_and_update_component(component_info, self.components) if not updated: self._grpc_client.log.warning( f"Could not find component to update: '{component_name}' (ID: {component_id})" ) - + # Handle deleted components for component_info in tracker_response.get("deleted_components", []): component_id = component_info["id"] self._grpc_client.log.debug(f"Processing deleted component: ID={component_id}") - + # Try to find and remove the component removed = self._find_and_remove_component(component_info, self.components) if not removed: self._grpc_client.log.warning( f"Could not find component to delete: ID={component_id}" ) - + # ================== HANDLE BODIES ================== - + # Handle created bodies for body_info in tracker_response.get("created_bodies", []): body_id = body_info["id"] @@ -1529,7 +1526,9 @@ def _update_from_tracker(self, tracker_response: dict): ) continue - new_body = self._find_and_add_body(body_info, self.components, created_parts_dict, created_components_dict) + new_body = self._find_and_add_body( + body_info, self.components, created_parts_dict, created_components_dict + ) if not new_body: new_body = MasterBody(body_id, body_name, self._grpc_client, is_surface=is_surface) self._master_component.part.bodies.append(new_body) @@ -1537,10 +1536,10 @@ def _update_from_tracker(self, tracker_response: dict): self._grpc_client.log.debug( f"Added new body '{body_name}' (ID: {body_id}) to root level." ) - + if new_body: created_bodies_dict[body_id] = new_body - + # Handle modified bodies for body_info in tracker_response.get("modified_bodies", []): body_id = body_info["id"] @@ -1563,7 +1562,7 @@ def _update_from_tracker(self, tracker_response: dict): for component in self.components: if self._find_and_update_body(body_info, component): break - + # Handle deleted bodies for body_info in tracker_response.get("deleted_bodies", []): body_id = body_info["id"] @@ -1591,7 +1590,7 @@ def _update_from_tracker(self, tracker_response: dict): def update_parts(self, created_parts=None, modified_parts=None, deleted_parts=None): """Update parts with consolidated handling of created, modified, and deleted parts. - + Parameters ---------- created_parts : list, optional @@ -1600,26 +1599,32 @@ def update_parts(self, created_parts=None, modified_parts=None, deleted_parts=No List of modified part information from tracker response. deleted_parts : list, optional List of deleted part information from tracker response. - + Returns ------- dict Dictionary of created parts with part_id as key and Part object as value. """ created_parts_dict = {} - + if created_parts: created_parts_dict = self._handle_created_parts(created_parts) if modified_parts: self._handle_modified_parts(modified_parts) if deleted_parts: self._handle_deleted_parts(deleted_parts) - + return created_parts_dict - def _update_components(self, created_components=None, modified_components=None, deleted_components=None, created_parts=None): + def _update_components( + self, + created_components=None, + modified_components=None, + deleted_components=None, + created_parts=None, + ): """Update components with consolidated handling of created, modified, and deleted components. - + Parameters ---------- created_components : list, optional @@ -1630,26 +1635,35 @@ def _update_components(self, created_components=None, modified_components=None, List of deleted component information from tracker response. created_parts : dict, optional Dictionary of created parts from previous step. - + Returns ------- dict Dictionary of created components with component_id as key and Component object as value. """ created_components_dict = {} - + if created_components: - created_components_dict = self._handle_created_components(created_components, created_parts) + created_components_dict = self._handle_created_components( + created_components, created_parts + ) if modified_components: self._handle_modified_components(modified_components) if deleted_components: self._handle_deleted_components(deleted_components) - + return created_components_dict - def _update_bodies(self, created_bodies=None, modified_bodies=None, deleted_bodies=None, created_parts=None, created_components=None): + def _update_bodies( + self, + created_bodies=None, + modified_bodies=None, + deleted_bodies=None, + created_parts=None, + created_components=None, + ): """Update bodies with consolidated handling of created, modified, and deleted bodies. - + Parameters ---------- created_bodies : list, optional @@ -1662,59 +1676,59 @@ def _update_bodies(self, created_bodies=None, modified_bodies=None, deleted_bodi Dictionary of created parts from previous step. created_components : dict, optional Dictionary of created components from previous step. - + Returns ------- dict Dictionary of created bodies with body_id as key and Body object as value. """ created_bodies_dict = {} - + if created_bodies: - created_bodies_dict = self._handle_created_bodies(created_bodies, created_parts, created_components) + created_bodies_dict = self._handle_created_bodies( + created_bodies, created_parts, created_components + ) if modified_bodies: self._handle_modified_bodies(modified_bodies) if deleted_bodies: self._handle_deleted_bodies(deleted_bodies) - + return created_bodies_dict # ================== PART HANDLERS ================== - + def _handle_created_parts(self, created_parts): """Handle creation of new parts from tracker response. - + Returns ------- dict Dictionary of created parts with part_id as key and Part object as value. """ created_parts_dict = {} - + for part_info in created_parts: part_id = part_info["id"] part_name = part_info.get("name", f"Part_{part_id}") self._grpc_client.log.debug( f"Processing created part: ID={part_id}, Name='{part_name}'" ) - + # Check if part already exists if self._find_existing_part(part_id): self._grpc_client.log.debug( f"Created part '{part_name}' (ID: {part_id}) already exists." ) continue - + # Create new part new_part = Part(part_id, part_name, [], []) created_parts_dict[part_id] = new_part # TODO: Add part to appropriate collection/registry - self._grpc_client.log.debug( - f"Created new part '{part_name}' (ID: {part_id})" - ) - + self._grpc_client.log.debug(f"Created new part '{part_name}' (ID: {part_id})") + return created_parts_dict - + def _handle_modified_parts(self, modified_parts): """Handle modification of existing parts from tracker response.""" for part_info in modified_parts: @@ -1723,71 +1737,71 @@ def _handle_modified_parts(self, modified_parts): self._grpc_client.log.debug( f"Processing modified part: ID={part_id}, Name='{part_name}'" ) - + # Try to find and update the part updated = self._find_and_update_part(part_info) if not updated: self._grpc_client.log.warning( f"Could not find part to update: '{part_name}' (ID: {part_id})" ) - + def _handle_deleted_parts(self, deleted_parts): """Handle deletion of parts from tracker response.""" for part_info in deleted_parts: part_id = part_info["id"] self._grpc_client.log.debug(f"Processing deleted part: ID={part_id}") - + # Try to find and remove the part removed = self._find_and_remove_part(part_info) if not removed: - self._grpc_client.log.warning( - f"Could not find part to delete: ID={part_id}" - ) - + self._grpc_client.log.warning(f"Could not find part to delete: ID={part_id}") + # ================== COMPONENT HANDLERS ================== - + def _handle_created_components(self, created_components, created_parts=None): """Handle creation of new components from tracker response. - + Parameters ---------- created_components : list List of created component information from tracker response. created_parts : dict, optional Dictionary of created parts from previous step. - + Returns ------- dict Dictionary of created components with component_id as key and Component object as value. """ created_components_dict = {} - + for component_info in created_components: component_id = component_info["id"] component_name = component_info.get("name", f"Component_{component_id}") self._grpc_client.log.debug( f"Processing created component: ID={component_id}, Name='{component_name}'" ) - + # Check if component already exists if any(comp.id == component_id for comp in self.components): self._grpc_client.log.debug( f"Created component '{component_name}' (ID: {component_id}) already exists." ) continue - + # Try to add the component to the appropriate parent - new_component = self._find_and_add_component(component_info, self.components, created_parts) + new_component = self._find_and_add_component( + component_info, self.components, created_parts + ) if new_component: created_components_dict[component_id] = new_component else: self._grpc_client.log.warning( f"Could not find parent for component '{component_name}' (ID: {component_id})" ) - + return created_components_dict - + def _handle_modified_components(self, modified_components): """Handle modification of existing components from tracker response.""" for component_info in modified_components: @@ -1796,32 +1810,32 @@ def _handle_modified_components(self, modified_components): self._grpc_client.log.debug( f"Processing modified component: ID={component_id}, Name='{component_name}'" ) - + # Try to find and update the component updated = self._find_and_update_component(component_info, self.components) if not updated: self._grpc_client.log.warning( f"Could not find component to update: '{component_name}' (ID: {component_id})" ) - + def _handle_deleted_components(self, deleted_components): """Handle deletion of components from tracker response.""" for component_info in deleted_components: component_id = component_info["id"] self._grpc_client.log.debug(f"Processing deleted component: ID={component_id}") - + # Try to find and remove the component removed = self._find_and_remove_component(component_info, self.components) if not removed: self._grpc_client.log.warning( f"Could not find component to delete: ID={component_id}" ) - + # ================== BODY HANDLERS ================== - + def _handle_created_bodies(self, created_bodies, created_parts=None, created_components=None): """Handle creation of new bodies from tracker response. - + Parameters ---------- created_bodies : list @@ -1830,14 +1844,14 @@ def _handle_created_bodies(self, created_bodies, created_parts=None, created_com Dictionary of created parts from previous step. created_components : dict, optional Dictionary of created components from previous step. - + Returns ------- dict Dictionary of created bodies with body_id as key and Body object as value. """ created_bodies_dict = {} - + for body_info in created_bodies: body_id = body_info["id"] body_name = body_info["name"] @@ -1852,7 +1866,9 @@ def _handle_created_bodies(self, created_bodies, created_parts=None, created_com ) continue - new_body = self._find_and_add_body(body_info, self.components, created_parts, created_components) + new_body = self._find_and_add_body( + body_info, self.components, created_parts, created_components + ) if not new_body: new_body = MasterBody(body_id, body_name, self._grpc_client, is_surface=is_surface) self._master_component.part.bodies.append(new_body) @@ -1860,10 +1876,10 @@ def _handle_created_bodies(self, created_bodies, created_parts=None, created_com self._grpc_client.log.debug( f"Added new body '{body_name}' (ID: {body_id}) to root level." ) - + if new_body: created_bodies_dict[body_id] = new_body - + return created_bodies_dict def _handle_modified_bodies(self, modified_bodies): @@ -1917,77 +1933,77 @@ def _handle_deleted_bodies(self, deleted_bodies): break # ================== HELPER METHODS ================== - # + # # Processing order for tracker updates: # 1. Parts (foundational - no dependencies) # 2. Components (depend on parts via master_component.part) # 3. Bodies (depend on parts/components as containers) # 4. Deletions (reverse order to avoid dependency issues) - + def _find_existing_part(self, part_id): """Find if a part with the given ID already exists.""" # Search through master component parts - if hasattr(self, '_master_component') and self._master_component: + if hasattr(self, "_master_component") and self._master_component: if self._master_component.part.id == part_id: return self._master_component.part - + # Search through all component master parts for component in self._get_all_components(): - if (hasattr(component, '_master_component') and - component._master_component and - component._master_component.part.id == part_id): + if ( + hasattr(component, "_master_component") + and component._master_component + and component._master_component.part.id == part_id + ): return component._master_component.part - + return None - + def _get_all_components(self): """Get all components in the hierarchy recursively.""" all_components = [] - + def _collect_components(components): for comp in components: all_components.append(comp) _collect_components(comp.components) - + _collect_components(self.components) return all_components - + def _find_and_update_part(self, part_info): """Find and update an existing part.""" part_id = part_info["id"] existing_part = self._find_existing_part(part_id) - + if existing_part: # Update part properties if "name" in part_info: existing_part._name = part_info["name"] - self._grpc_client.log.debug( - f"Updated part '{existing_part.name}' (ID: {part_id})" - ) + self._grpc_client.log.debug(f"Updated part '{existing_part.name}' (ID: {part_id})") return True - + return False - + def _find_and_remove_part(self, part_info): """Find and remove a part from the design.""" part_id = part_info["id"] existing_part = self._find_existing_part(part_id) - + if existing_part: # Mark as not alive (if applicable) - if hasattr(existing_part, '_is_alive'): + if hasattr(existing_part, "_is_alive"): existing_part._is_alive = False - self._grpc_client.log.debug( - f"Removed part (ID: {part_id})" - ) + self._grpc_client.log.debug(f"Removed part (ID: {part_id})") # TODO: Implement actual removal logic based on where parts are stored return True - + return False - - def _find_and_add_component(self, component_info, parent_components, created_parts=None, created_master_components=None): + + def _find_and_add_component( + self, component_info, parent_components, created_parts=None, created_master_components=None + ): """Recursively find the appropriate parent and add a new component to it. - + Parameters ---------- component_info : dict @@ -1998,7 +2014,7 @@ def _find_and_add_component(self, component_info, parent_components, created_par Dictionary of created parts from previous step. created_master_components : dict, optional Dictionary of created master components from current step. - + Returns ------- Component or None @@ -2006,17 +2022,17 @@ def _find_and_add_component(self, component_info, parent_components, created_par """ parent_id = component_info.get("parent_id") master_id = component_info.get("master_id") - + # Find the master component for this component master_component = None if created_master_components and master_id: master_component = created_master_components.get(master_id) - + # Check if this should be added to the root design if parent_id == self.id: # Create the Component object with master_component new_component = Component( - parent_component= None, + parent_component=None, name=component_info["name"], template=self, grpc_client=self._grpc_client, @@ -2025,37 +2041,37 @@ def _find_and_add_component(self, component_info, parent_components, created_par read_existing_comp=True, ) self.components.append(new_component) - self._grpc_client.log.debug( - f"Added component '{component_info['id']}' to root design" - ) + self._grpc_client.log.debug(f"Added component '{component_info['id']}' to root design") return new_component # Search through existing components for the parent for component in parent_components: # if component.id == parent_id: - # new_component = Component( - # name=component_info["name"], - # template=component, - # grpc_client=self._grpc_client, - # master_component=master_component, - # preexisting_id=component_info["id"], - # read_existing_comp=True, - # ) - # component.components.append(new_component) - # self._grpc_client.log.debug( - # f"Added component '{component_info['id']}' to component '{component.name}'" - # ) - # return new_component - - self._find_and_add_component(component_info, component.components, created_parts, created_master_components) - + # new_component = Component( + # name=component_info["name"], + # template=component, + # grpc_client=self._grpc_client, + # master_component=master_component, + # preexisting_id=component_info["id"], + # read_existing_comp=True, + # ) + # component.components.append(new_component) + # self._grpc_client.log.debug( + # f"Added component '{component_info['id']}' to component '{component.name}'" + # ) + # return new_component + + self._find_and_add_component( + component_info, component.components, created_parts, created_master_components + ) + return None - + # This method is subject to change based on how component updates are defined. def _find_and_update_component(self, component_info, components): """Recursively find and update an existing component in the hierarchy.""" component_id = component_info["id"] - + for component in components: if component.id == component_id: # Update component properties @@ -2065,16 +2081,16 @@ def _find_and_update_component(self, component_info, components): f"Updated component '{component.name}' (ID: {component.id})" ) return True - + if self._find_and_update_component(component_info, component.components): return True - + return False - + def _find_and_remove_component(self, component_info, components, parent_component=None): """Recursively find and remove a component from the hierarchy.""" component_id = component_info["id"] - + for i, component in enumerate(components): if component.id == component_id: component._is_alive = False @@ -2084,12 +2100,12 @@ def _find_and_remove_component(self, component_info, components, parent_componen f"from {'root design' if parent_component is None else parent_component.name}" ) return True - + if self._find_and_remove_component(component_info, component.components, component): return True - + return False - + def _update_body(self, existing_body, body_info): """Update an existing body with new information from tracker response.""" self._grpc_client.log.debug( @@ -2099,9 +2115,11 @@ def _update_body(self, existing_body, body_info): existing_body.name = body_info["name"] existing_body._template._is_surface = body_info.get("is_surface", False) - def _find_and_add_body(self, body_info, components, created_parts=None, created_components=None): + def _find_and_add_body( + self, body_info, components, created_parts=None, created_components=None + ): """Recursively find the appropriate component and add a new body to it. - + Parameters ---------- body_info : dict @@ -2112,7 +2130,7 @@ def _find_and_add_body(self, body_info, components, created_parts=None, created_ Dictionary of created parts from previous step. created_components : dict, optional Dictionary of created components from previous step. - + Returns ------- MasterBody or None @@ -2135,7 +2153,9 @@ def _find_and_add_body(self, body_info, components, created_parts=None, created_ ) return new_body - result = self._find_and_add_body(body_info, component.components, created_parts, created_components) + result = self._find_and_add_body( + body_info, component.components, created_parts, created_components + ) if result: return result diff --git a/src/ansys/geometry/core/tools/prepare_tools.py b/src/ansys/geometry/core/tools/prepare_tools.py index 3eb7e08d55..d083bfdf62 100644 --- a/src/ansys/geometry/core/tools/prepare_tools.py +++ b/src/ansys/geometry/core/tools/prepare_tools.py @@ -126,7 +126,6 @@ def extract_volume_from_faces( if response.get("success"): bodies_ids = response.get("created_bodies") if len(bodies_ids) > 0: - if pyansys_geometry.USE_TRACKER_TO_UPDATE_DESIGN: parent_design._update_from_tracker(response.get("complete_command_response")) else: From 4936133f6023c29b8a7b92e634b7494c22e61f5c Mon Sep 17 00:00:00 2001 From: Umut Soysal Date: Thu, 6 Nov 2025 13:44:56 -0600 Subject: [PATCH 10/18] Update __init__.py --- src/ansys/geometry/core/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ansys/geometry/core/__init__.py b/src/ansys/geometry/core/__init__.py index 1b1608da6f..3dfacfec4c 100644 --- a/src/ansys/geometry/core/__init__.py +++ b/src/ansys/geometry/core/__init__.py @@ -71,5 +71,5 @@ DOCUMENTATION_BUILD: bool = os.environ.get("PYANSYS_GEOMETRY_DOC_BUILD", "false").lower() == "true" """Global flag for the documentation to use the proper PyVista Jupyter backend.""" -USE_TRACKER_TO_UPDATE_DESIGN: bool = False +USE_TRACKER_TO_UPDATE_DESIGN: bool = True """Global constant for checking whether to use the tracker to update designs.""" From 83b07dc68169251631459276450a58a09560e189 Mon Sep 17 00:00:00 2001 From: Umut Soysal Date: Fri, 7 Nov 2025 11:57:55 -0600 Subject: [PATCH 11/18] revert change --- src/ansys/geometry/core/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ansys/geometry/core/__init__.py b/src/ansys/geometry/core/__init__.py index 3dfacfec4c..1b1608da6f 100644 --- a/src/ansys/geometry/core/__init__.py +++ b/src/ansys/geometry/core/__init__.py @@ -71,5 +71,5 @@ DOCUMENTATION_BUILD: bool = os.environ.get("PYANSYS_GEOMETRY_DOC_BUILD", "false").lower() == "true" """Global flag for the documentation to use the proper PyVista Jupyter backend.""" -USE_TRACKER_TO_UPDATE_DESIGN: bool = True +USE_TRACKER_TO_UPDATE_DESIGN: bool = False """Global constant for checking whether to use the tracker to update designs.""" From 9f3de0830a3d0988f1b71025202d9571170287c9 Mon Sep 17 00:00:00 2001 From: Umut Soysal Date: Fri, 7 Nov 2025 11:59:24 -0600 Subject: [PATCH 12/18] clean up --- src/ansys/geometry/core/_grpc/_services/v0/admin.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/ansys/geometry/core/_grpc/_services/v0/admin.py b/src/ansys/geometry/core/_grpc/_services/v0/admin.py index 0e455ec8c9..afcfd740d0 100644 --- a/src/ansys/geometry/core/_grpc/_services/v0/admin.py +++ b/src/ansys/geometry/core/_grpc/_services/v0/admin.py @@ -71,9 +71,7 @@ def get_backend(self, **kwargs) -> dict: # noqa: D102 if hasattr(response, "version"): ver = response.version backend_version = semver.Version(ver.major_release, ver.minor_release, ver.service_pack) - api_server_build_info = ( - "N/A" # f"{ver.build_number}" if ver.build_number != 0 else "N/A" - ) + api_server_build_info = f"{ver.build_number}" if ver.build_number != 0 else "N/A" product_build_info = "N/A" # f"{response.backend_version_info.strip()}" if response.backend_version_info else "N/A" else: # pragma: no cover # If the version is not available, set a default version From 7dae04f7f5a47d7ddb245119e71c64cda9d14609 Mon Sep 17 00:00:00 2001 From: Umut Soysal Date: Fri, 7 Nov 2025 12:00:40 -0600 Subject: [PATCH 13/18] Update admin.py --- src/ansys/geometry/core/_grpc/_services/v0/admin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ansys/geometry/core/_grpc/_services/v0/admin.py b/src/ansys/geometry/core/_grpc/_services/v0/admin.py index afcfd740d0..8e2f6fee50 100644 --- a/src/ansys/geometry/core/_grpc/_services/v0/admin.py +++ b/src/ansys/geometry/core/_grpc/_services/v0/admin.py @@ -72,7 +72,7 @@ def get_backend(self, **kwargs) -> dict: # noqa: D102 ver = response.version backend_version = semver.Version(ver.major_release, ver.minor_release, ver.service_pack) api_server_build_info = f"{ver.build_number}" if ver.build_number != 0 else "N/A" - product_build_info = "N/A" # f"{response.backend_version_info.strip()}" if response.backend_version_info else "N/A" + product_build_info = f"{response.backend_version_info.strip()}" if response.backend_version_info else "N/A" else: # pragma: no cover # If the version is not available, set a default version backend_version = semver.Version(24, 1, 0) @@ -85,7 +85,7 @@ def get_backend(self, **kwargs) -> dict: # noqa: D102 "version": backend_version, "api_server_build_info": api_server_build_info, "product_build_info": product_build_info, - "additional_info": "N/A", # {k: v for k, v in response.additional_build_info.items()}, + "additional_info": {k: v for k, v in response.additional_build_info.items()}, } @protect_grpc From f9e31f324eb9f33c1dc27a7ad9ae515a8a9bbc03 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 7 Nov 2025 18:00:54 +0000 Subject: [PATCH 14/18] chore: auto fixes from pre-commit hooks --- src/ansys/geometry/core/_grpc/_services/v0/admin.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/ansys/geometry/core/_grpc/_services/v0/admin.py b/src/ansys/geometry/core/_grpc/_services/v0/admin.py index 8e2f6fee50..36578f327e 100644 --- a/src/ansys/geometry/core/_grpc/_services/v0/admin.py +++ b/src/ansys/geometry/core/_grpc/_services/v0/admin.py @@ -72,7 +72,11 @@ def get_backend(self, **kwargs) -> dict: # noqa: D102 ver = response.version backend_version = semver.Version(ver.major_release, ver.minor_release, ver.service_pack) api_server_build_info = f"{ver.build_number}" if ver.build_number != 0 else "N/A" - product_build_info = f"{response.backend_version_info.strip()}" if response.backend_version_info else "N/A" + product_build_info = ( + f"{response.backend_version_info.strip()}" + if response.backend_version_info + else "N/A" + ) else: # pragma: no cover # If the version is not available, set a default version backend_version = semver.Version(24, 1, 0) From c752837f86cde605037d824648a0189a313457cb Mon Sep 17 00:00:00 2001 From: Umut Soysal Date: Fri, 7 Nov 2025 12:01:27 -0600 Subject: [PATCH 15/18] Update admin.py --- src/ansys/geometry/core/_grpc/_services/v0/admin.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ansys/geometry/core/_grpc/_services/v0/admin.py b/src/ansys/geometry/core/_grpc/_services/v0/admin.py index 8e2f6fee50..902a9a6b49 100644 --- a/src/ansys/geometry/core/_grpc/_services/v0/admin.py +++ b/src/ansys/geometry/core/_grpc/_services/v0/admin.py @@ -72,7 +72,9 @@ def get_backend(self, **kwargs) -> dict: # noqa: D102 ver = response.version backend_version = semver.Version(ver.major_release, ver.minor_release, ver.service_pack) api_server_build_info = f"{ver.build_number}" if ver.build_number != 0 else "N/A" - product_build_info = f"{response.backend_version_info.strip()}" if response.backend_version_info else "N/A" + product_build_info = ( + response.backend_version_info.strip() if response.backend_version_info else "N/A" + ) else: # pragma: no cover # If the version is not available, set a default version backend_version = semver.Version(24, 1, 0) From f8718f7a5116dfb33a91b1a3f84c548de9f068f0 Mon Sep 17 00:00:00 2001 From: Umut Soysal Date: Mon, 10 Nov 2025 09:38:15 -0600 Subject: [PATCH 16/18] Update test_design.py --- tests/integration/test_design.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/integration/test_design.py b/tests/integration/test_design.py index 11acbbca5b..2608f20280 100644 --- a/tests/integration/test_design.py +++ b/tests/integration/test_design.py @@ -4089,3 +4089,15 @@ def test_combine_merge(modeler: Modeler): design._update_design_inplace() assert len(design.bodies) == 1 assert box1.volume.m == pytest.approx(Quantity(2.5, UNITS.m**3).m, rel=1e-6, abs=1e-8) + + +def test_design_update_with_tracker_response(modeler: Modeler): + design = modeler.open_file(FILES_DIR / "hollowCylinder1.dsco") + + assert len(design.components) == 1 + body = design.components[0].bodies[0] + inside_faces = [body.faces[0]] + sealing_faces = [body.faces[1], body.faces[2]] + modeler.prepare_tools.extract_volume_from_faces(sealing_faces, inside_faces) + assert len(design.components) == 2 + From 387882e4b2b8526b02ee0f310a297e3415f9d3df Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 10 Nov 2025 15:38:31 +0000 Subject: [PATCH 17/18] chore: auto fixes from pre-commit hooks --- tests/integration/test_design.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/integration/test_design.py b/tests/integration/test_design.py index 2608f20280..d739971d36 100644 --- a/tests/integration/test_design.py +++ b/tests/integration/test_design.py @@ -4100,4 +4100,3 @@ def test_design_update_with_tracker_response(modeler: Modeler): sealing_faces = [body.faces[1], body.faces[2]] modeler.prepare_tools.extract_volume_from_faces(sealing_faces, inside_faces) assert len(design.components) == 2 - From 5b8451ed1f74ab22e0034b3daa8966a2863723e2 Mon Sep 17 00:00:00 2001 From: pyansys-ci-bot <92810346+pyansys-ci-bot@users.noreply.github.com> Date: Tue, 11 Nov 2025 09:17:03 +0000 Subject: [PATCH 18/18] chore: adding changelog file 2359.added.md [dependabot-skip] --- doc/changelog.d/2359.added.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 doc/changelog.d/2359.added.md diff --git a/doc/changelog.d/2359.added.md b/doc/changelog.d/2359.added.md new file mode 100644 index 0000000000..11e1be1648 --- /dev/null +++ b/doc/changelog.d/2359.added.md @@ -0,0 +1 @@ +Tracking updates