From 97eb31adfd9dde4f1ecfa364d7408b09d17bfafd Mon Sep 17 00:00:00 2001 From: PyAnsys CI Bot <92810346+pyansys-ci-bot@users.noreply.github.com> Date: Mon, 17 Feb 2025 15:31:37 +0100 Subject: [PATCH 01/61] chore: update CHANGELOG for v0.9.0 (#1753) --- doc/changelog.d/1258.test.md | 1 - doc/changelog.d/1706.maintenance.md | 1 - doc/changelog.d/1707.added.md | 1 - doc/changelog.d/1708.added.md | 1 - doc/changelog.d/1709.dependencies.md | 1 - doc/changelog.d/1711.fixed.md | 1 - doc/changelog.d/1712.added.md | 1 - doc/changelog.d/1713.test.md | 1 - doc/changelog.d/1714.fixed.md | 1 - doc/changelog.d/1715.fixed.md | 1 - doc/changelog.d/1716.added.md | 1 - doc/changelog.d/1717.maintenance.md | 1 - doc/changelog.d/1719.dependencies.md | 1 - doc/changelog.d/1720.dependencies.md | 1 - doc/changelog.d/1722.maintenance.md | 1 - doc/changelog.d/1723.added.md | 1 - doc/changelog.d/1725.fixed.md | 1 - doc/changelog.d/1726.dependencies.md | 1 - doc/changelog.d/1727.test.md | 1 - doc/changelog.d/1728.dependencies.md | 1 - doc/changelog.d/1729.dependencies.md | 1 - doc/changelog.d/1730.documentation.md | 1 - doc/changelog.d/1732.added.md | 1 - doc/changelog.d/1736.dependencies.md | 1 - doc/changelog.d/1737.maintenance.md | 1 - doc/changelog.d/1739.maintenance.md | 1 - doc/changelog.d/1740.added.md | 1 - doc/changelog.d/1741.added.md | 1 - doc/changelog.d/1742.dependencies.md | 1 - doc/changelog.d/1743.dependencies.md | 1 - doc/changelog.d/1744.dependencies.md | 1 - doc/changelog.d/1745.test.md | 1 - doc/changelog.d/1746.dependencies.md | 1 - doc/changelog.d/1747.dependencies.md | 1 - doc/changelog.d/1749.dependencies.md | 1 - doc/changelog.d/1750.maintenance.md | 1 - doc/changelog.d/1753.maintenance.md | 1 + doc/source/changelog.rst | 67 +++++++++++++++++++++++++++ 38 files changed, 68 insertions(+), 36 deletions(-) delete mode 100644 doc/changelog.d/1258.test.md delete mode 100644 doc/changelog.d/1706.maintenance.md delete mode 100644 doc/changelog.d/1707.added.md delete mode 100644 doc/changelog.d/1708.added.md delete mode 100644 doc/changelog.d/1709.dependencies.md delete mode 100644 doc/changelog.d/1711.fixed.md delete mode 100644 doc/changelog.d/1712.added.md delete mode 100644 doc/changelog.d/1713.test.md delete mode 100644 doc/changelog.d/1714.fixed.md delete mode 100644 doc/changelog.d/1715.fixed.md delete mode 100644 doc/changelog.d/1716.added.md delete mode 100644 doc/changelog.d/1717.maintenance.md delete mode 100644 doc/changelog.d/1719.dependencies.md delete mode 100644 doc/changelog.d/1720.dependencies.md delete mode 100644 doc/changelog.d/1722.maintenance.md delete mode 100644 doc/changelog.d/1723.added.md delete mode 100644 doc/changelog.d/1725.fixed.md delete mode 100644 doc/changelog.d/1726.dependencies.md delete mode 100644 doc/changelog.d/1727.test.md delete mode 100644 doc/changelog.d/1728.dependencies.md delete mode 100644 doc/changelog.d/1729.dependencies.md delete mode 100644 doc/changelog.d/1730.documentation.md delete mode 100644 doc/changelog.d/1732.added.md delete mode 100644 doc/changelog.d/1736.dependencies.md delete mode 100644 doc/changelog.d/1737.maintenance.md delete mode 100644 doc/changelog.d/1739.maintenance.md delete mode 100644 doc/changelog.d/1740.added.md delete mode 100644 doc/changelog.d/1741.added.md delete mode 100644 doc/changelog.d/1742.dependencies.md delete mode 100644 doc/changelog.d/1743.dependencies.md delete mode 100644 doc/changelog.d/1744.dependencies.md delete mode 100644 doc/changelog.d/1745.test.md delete mode 100644 doc/changelog.d/1746.dependencies.md delete mode 100644 doc/changelog.d/1747.dependencies.md delete mode 100644 doc/changelog.d/1749.dependencies.md delete mode 100644 doc/changelog.d/1750.maintenance.md create mode 100644 doc/changelog.d/1753.maintenance.md diff --git a/doc/changelog.d/1258.test.md b/doc/changelog.d/1258.test.md deleted file mode 100644 index 7cde69eb55..0000000000 --- a/doc/changelog.d/1258.test.md +++ /dev/null @@ -1 +0,0 @@ -verifying issue with empty intersect and temporal body creation \ No newline at end of file diff --git a/doc/changelog.d/1706.maintenance.md b/doc/changelog.d/1706.maintenance.md deleted file mode 100644 index 6f4c1c0089..0000000000 --- a/doc/changelog.d/1706.maintenance.md +++ /dev/null @@ -1 +0,0 @@ -update CHANGELOG for v0.8.2 \ No newline at end of file diff --git a/doc/changelog.d/1707.added.md b/doc/changelog.d/1707.added.md deleted file mode 100644 index 2602f1720f..0000000000 --- a/doc/changelog.d/1707.added.md +++ /dev/null @@ -1 +0,0 @@ -design activation changes \ No newline at end of file diff --git a/doc/changelog.d/1708.added.md b/doc/changelog.d/1708.added.md deleted file mode 100644 index 52cde5ead5..0000000000 --- a/doc/changelog.d/1708.added.md +++ /dev/null @@ -1 +0,0 @@ -add contributors \ No newline at end of file diff --git a/doc/changelog.d/1709.dependencies.md b/doc/changelog.d/1709.dependencies.md deleted file mode 100644 index 3c05c97bf8..0000000000 --- a/doc/changelog.d/1709.dependencies.md +++ /dev/null @@ -1 +0,0 @@ -bump ansys-api-geometry from 0.4.33 to 0.4.34 \ No newline at end of file diff --git a/doc/changelog.d/1711.fixed.md b/doc/changelog.d/1711.fixed.md deleted file mode 100644 index 277af98686..0000000000 --- a/doc/changelog.d/1711.fixed.md +++ /dev/null @@ -1 +0,0 @@ -re enable fmd tests \ No newline at end of file diff --git a/doc/changelog.d/1712.added.md b/doc/changelog.d/1712.added.md deleted file mode 100644 index 0d2f01063a..0000000000 --- a/doc/changelog.d/1712.added.md +++ /dev/null @@ -1 +0,0 @@ -Implementation of inspect & repair geometry \ No newline at end of file diff --git a/doc/changelog.d/1713.test.md b/doc/changelog.d/1713.test.md deleted file mode 100644 index 4c10d27211..0000000000 --- a/doc/changelog.d/1713.test.md +++ /dev/null @@ -1 +0,0 @@ -Expand pattern tests \ No newline at end of file diff --git a/doc/changelog.d/1714.fixed.md b/doc/changelog.d/1714.fixed.md deleted file mode 100644 index e58c41f4d9..0000000000 --- a/doc/changelog.d/1714.fixed.md +++ /dev/null @@ -1 +0,0 @@ -support body mirror on linux \ No newline at end of file diff --git a/doc/changelog.d/1715.fixed.md b/doc/changelog.d/1715.fixed.md deleted file mode 100644 index 9af9372ee1..0000000000 --- a/doc/changelog.d/1715.fixed.md +++ /dev/null @@ -1 +0,0 @@ -use sketch plane for imprint/project curves \ No newline at end of file diff --git a/doc/changelog.d/1716.added.md b/doc/changelog.d/1716.added.md deleted file mode 100644 index e90bde071a..0000000000 --- a/doc/changelog.d/1716.added.md +++ /dev/null @@ -1 +0,0 @@ -launch core service from envar \ No newline at end of file diff --git a/doc/changelog.d/1717.maintenance.md b/doc/changelog.d/1717.maintenance.md deleted file mode 100644 index c655a0905a..0000000000 --- a/doc/changelog.d/1717.maintenance.md +++ /dev/null @@ -1 +0,0 @@ -pre-commit automatic update \ No newline at end of file diff --git a/doc/changelog.d/1719.dependencies.md b/doc/changelog.d/1719.dependencies.md deleted file mode 100644 index 883bfafc7c..0000000000 --- a/doc/changelog.d/1719.dependencies.md +++ /dev/null @@ -1 +0,0 @@ -bump ansys-sphinx-theme[autoapi] from 1.2.6 to 1.2.7 in the docs-deps group \ No newline at end of file diff --git a/doc/changelog.d/1720.dependencies.md b/doc/changelog.d/1720.dependencies.md deleted file mode 100644 index 0f3ca21a1e..0000000000 --- a/doc/changelog.d/1720.dependencies.md +++ /dev/null @@ -1 +0,0 @@ -bump ansys-api-geometry from 0.4.34 to 0.4.35 \ No newline at end of file diff --git a/doc/changelog.d/1722.maintenance.md b/doc/changelog.d/1722.maintenance.md deleted file mode 100644 index 61a75a0ef5..0000000000 --- a/doc/changelog.d/1722.maintenance.md +++ /dev/null @@ -1 +0,0 @@ -update SECURITY.md versions supported \ No newline at end of file diff --git a/doc/changelog.d/1723.added.md b/doc/changelog.d/1723.added.md deleted file mode 100644 index 27a925f003..0000000000 --- a/doc/changelog.d/1723.added.md +++ /dev/null @@ -1 +0,0 @@ -workflow enhancements for better tool results \ No newline at end of file diff --git a/doc/changelog.d/1725.fixed.md b/doc/changelog.d/1725.fixed.md deleted file mode 100644 index 5bec5d4ba9..0000000000 --- a/doc/changelog.d/1725.fixed.md +++ /dev/null @@ -1 +0,0 @@ -revert boolean ops logic and hold-off on commands-based implementation (temporarily) \ No newline at end of file diff --git a/doc/changelog.d/1726.dependencies.md b/doc/changelog.d/1726.dependencies.md deleted file mode 100644 index aae2364dc8..0000000000 --- a/doc/changelog.d/1726.dependencies.md +++ /dev/null @@ -1 +0,0 @@ -bump ansys-sphinx-theme[autoapi] from 1.2.7 to 1.3.0 in the docs-deps group \ No newline at end of file diff --git a/doc/changelog.d/1727.test.md b/doc/changelog.d/1727.test.md deleted file mode 100644 index d4caa81efc..0000000000 --- a/doc/changelog.d/1727.test.md +++ /dev/null @@ -1 +0,0 @@ -set body name \ No newline at end of file diff --git a/doc/changelog.d/1728.dependencies.md b/doc/changelog.d/1728.dependencies.md deleted file mode 100644 index 16cad54f2c..0000000000 --- a/doc/changelog.d/1728.dependencies.md +++ /dev/null @@ -1 +0,0 @@ -bump ansys-sphinx-theme[autoapi] from 1.3.0 to 1.3.1 in the docs-deps group \ No newline at end of file diff --git a/doc/changelog.d/1729.dependencies.md b/doc/changelog.d/1729.dependencies.md deleted file mode 100644 index 7cfa83a813..0000000000 --- a/doc/changelog.d/1729.dependencies.md +++ /dev/null @@ -1 +0,0 @@ -bump ansys-api-geometry from 0.4.35 to 0.4.36 \ No newline at end of file diff --git a/doc/changelog.d/1730.documentation.md b/doc/changelog.d/1730.documentation.md deleted file mode 100644 index deb88451eb..0000000000 --- a/doc/changelog.d/1730.documentation.md +++ /dev/null @@ -1 +0,0 @@ -update CONTRIBUTING.md \ No newline at end of file diff --git a/doc/changelog.d/1732.added.md b/doc/changelog.d/1732.added.md deleted file mode 100644 index 07b49458c3..0000000000 --- a/doc/changelog.d/1732.added.md +++ /dev/null @@ -1 +0,0 @@ -add face color, round info, bring measure tools to linux \ No newline at end of file diff --git a/doc/changelog.d/1736.dependencies.md b/doc/changelog.d/1736.dependencies.md deleted file mode 100644 index 5c12517a19..0000000000 --- a/doc/changelog.d/1736.dependencies.md +++ /dev/null @@ -1 +0,0 @@ -bump trame-vtk from 2.8.14 to 2.8.15 \ No newline at end of file diff --git a/doc/changelog.d/1737.maintenance.md b/doc/changelog.d/1737.maintenance.md deleted file mode 100644 index c655a0905a..0000000000 --- a/doc/changelog.d/1737.maintenance.md +++ /dev/null @@ -1 +0,0 @@ -pre-commit automatic update \ No newline at end of file diff --git a/doc/changelog.d/1739.maintenance.md b/doc/changelog.d/1739.maintenance.md deleted file mode 100644 index 9e32bd93f9..0000000000 --- a/doc/changelog.d/1739.maintenance.md +++ /dev/null @@ -1 +0,0 @@ -keep simba-plugin-geometry tag \ No newline at end of file diff --git a/doc/changelog.d/1740.added.md b/doc/changelog.d/1740.added.md deleted file mode 100644 index c7191a660d..0000000000 --- a/doc/changelog.d/1740.added.md +++ /dev/null @@ -1 +0,0 @@ -conservative approach to single design per modeler \ No newline at end of file diff --git a/doc/changelog.d/1741.added.md b/doc/changelog.d/1741.added.md deleted file mode 100644 index bdcedd10cd..0000000000 --- a/doc/changelog.d/1741.added.md +++ /dev/null @@ -1 +0,0 @@ -export glb \ No newline at end of file diff --git a/doc/changelog.d/1742.dependencies.md b/doc/changelog.d/1742.dependencies.md deleted file mode 100644 index 14c7ab84ee..0000000000 --- a/doc/changelog.d/1742.dependencies.md +++ /dev/null @@ -1 +0,0 @@ -bump jupytext from 1.16.6 to 1.16.7 in the docs-deps group \ No newline at end of file diff --git a/doc/changelog.d/1743.dependencies.md b/doc/changelog.d/1743.dependencies.md deleted file mode 100644 index d499e54ae5..0000000000 --- a/doc/changelog.d/1743.dependencies.md +++ /dev/null @@ -1 +0,0 @@ -bump ansys-api-geometry from 0.4.36 to 0.4.37 \ No newline at end of file diff --git a/doc/changelog.d/1744.dependencies.md b/doc/changelog.d/1744.dependencies.md deleted file mode 100644 index 184bfb5bf9..0000000000 --- a/doc/changelog.d/1744.dependencies.md +++ /dev/null @@ -1 +0,0 @@ -bump myst-parser from 4.0.0 to 4.0.1 in the docs-deps group \ No newline at end of file diff --git a/doc/changelog.d/1745.test.md b/doc/changelog.d/1745.test.md deleted file mode 100644 index 5a737e92ac..0000000000 --- a/doc/changelog.d/1745.test.md +++ /dev/null @@ -1 +0,0 @@ -activate 8 linux tests \ No newline at end of file diff --git a/doc/changelog.d/1746.dependencies.md b/doc/changelog.d/1746.dependencies.md deleted file mode 100644 index d9c564d265..0000000000 --- a/doc/changelog.d/1746.dependencies.md +++ /dev/null @@ -1 +0,0 @@ -bump ansys-api-geometry from 0.4.37 to 0.4.38 \ No newline at end of file diff --git a/doc/changelog.d/1747.dependencies.md b/doc/changelog.d/1747.dependencies.md deleted file mode 100644 index 186365ae6f..0000000000 --- a/doc/changelog.d/1747.dependencies.md +++ /dev/null @@ -1 +0,0 @@ -bump numpy from 2.2.2 to 2.2.3 \ No newline at end of file diff --git a/doc/changelog.d/1749.dependencies.md b/doc/changelog.d/1749.dependencies.md deleted file mode 100644 index daaa01a760..0000000000 --- a/doc/changelog.d/1749.dependencies.md +++ /dev/null @@ -1 +0,0 @@ -bump panel from 1.6.0 to 1.6.1 \ No newline at end of file diff --git a/doc/changelog.d/1750.maintenance.md b/doc/changelog.d/1750.maintenance.md deleted file mode 100644 index ed0a9ae502..0000000000 --- a/doc/changelog.d/1750.maintenance.md +++ /dev/null @@ -1 +0,0 @@ -enhancements to GLB export and object ``plot()`` methods \ No newline at end of file diff --git a/doc/changelog.d/1753.maintenance.md b/doc/changelog.d/1753.maintenance.md new file mode 100644 index 0000000000..97e5671828 --- /dev/null +++ b/doc/changelog.d/1753.maintenance.md @@ -0,0 +1 @@ +update CHANGELOG for v0.9.0 \ No newline at end of file diff --git a/doc/source/changelog.rst b/doc/source/changelog.rst index 3583da64d4..cabf5ee6b2 100644 --- a/doc/source/changelog.rst +++ b/doc/source/changelog.rst @@ -9,6 +9,73 @@ This document contains the release notes for the PyAnsys Geometry project. .. towncrier release notes start +`0.9.0 `_ - 2025-02-17 +===================================================================================== + +Added +^^^^^ + +- design activation changes `#1707 `_ +- add contributors `#1708 `_ +- Implementation of inspect & repair geometry `#1712 `_ +- launch core service from envar `#1716 `_ +- workflow enhancements for better tool results `#1723 `_ +- add face color, round info, bring measure tools to linux `#1732 `_ +- conservative approach to single design per modeler `#1740 `_ +- export glb `#1741 `_ + + +Dependencies +^^^^^^^^^^^^ + +- bump ansys-api-geometry from 0.4.33 to 0.4.34 `#1709 `_ +- bump ansys-sphinx-theme[autoapi] from 1.2.6 to 1.2.7 in the docs-deps group `#1719 `_ +- bump ansys-api-geometry from 0.4.34 to 0.4.35 `#1720 `_ +- bump ansys-sphinx-theme[autoapi] from 1.2.7 to 1.3.0 in the docs-deps group `#1726 `_ +- bump ansys-sphinx-theme[autoapi] from 1.3.0 to 1.3.1 in the docs-deps group `#1728 `_ +- bump ansys-api-geometry from 0.4.35 to 0.4.36 `#1729 `_ +- bump trame-vtk from 2.8.14 to 2.8.15 `#1736 `_ +- bump jupytext from 1.16.6 to 1.16.7 in the docs-deps group `#1742 `_ +- bump ansys-api-geometry from 0.4.36 to 0.4.37 `#1743 `_ +- bump myst-parser from 4.0.0 to 4.0.1 in the docs-deps group `#1744 `_ +- bump ansys-api-geometry from 0.4.37 to 0.4.38 `#1746 `_ +- bump numpy from 2.2.2 to 2.2.3 `#1747 `_ +- bump panel from 1.6.0 to 1.6.1 `#1749 `_ + + +Documentation +^^^^^^^^^^^^^ + +- update CONTRIBUTING.md `#1730 `_ + + +Fixed +^^^^^ + +- re enable fmd tests `#1711 `_ +- support body mirror on linux `#1714 `_ +- use sketch plane for imprint/project curves `#1715 `_ +- revert boolean ops logic and hold-off on commands-based implementation (temporarily) `#1725 `_ + + +Maintenance +^^^^^^^^^^^ + +- update CHANGELOG for v0.8.2 `#1706 `_ +- pre-commit automatic update `#1717 `_, `#1737 `_ +- update SECURITY.md versions supported `#1722 `_ +- keep simba-plugin-geometry tag `#1739 `_ +- enhancements to GLB export and object ``plot()`` methods `#1750 `_ + + +Test +^^^^ + +- verifying issue with empty intersect and temporal body creation `#1258 `_ +- Expand pattern tests `#1713 `_ +- set body name `#1727 `_ +- activate 8 linux tests `#1745 `_ + `0.8.2 `_ - 2025-01-29 ===================================================================================== From 7e560879f8b43c012f0c54fe6f038b142a9949ff Mon Sep 17 00:00:00 2001 From: Umut Soysal Date: Tue, 28 Oct 2025 17:09:11 -0500 Subject: [PATCH 02/61] 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 03/61] 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 04/61] 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 05/61] 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 06/61] 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 07/61] 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 08/61] 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 09/61] 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 10/61] 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 11/61] 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 12/61] 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 13/61] 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 14/61] 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 15/61] 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 16/61] 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 17/61] 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 18/61] 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 19/61] 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 From 98e2fec2cb8044851370dae5cdd07e6698bf25cc Mon Sep 17 00:00:00 2001 From: smereu Date: Tue, 9 Dec 2025 16:32:05 -0600 Subject: [PATCH 20/61] fixes for tracker --- .../core/_grpc/_services/v1/conversions.py | 30 +++++++++++-------- .../core/_grpc/_services/v1/repair_tools.py | 2 +- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/src/ansys/geometry/core/_grpc/_services/v1/conversions.py b/src/ansys/geometry/core/_grpc/_services/v1/conversions.py index b3d7d7b032..20ac2b148f 100644 --- a/src/ansys/geometry/core/_grpc/_services/v1/conversions.py +++ b/src/ansys/geometry/core/_grpc/_services/v1/conversions.py @@ -48,7 +48,6 @@ DatumPointEntity as GRPCDesignPoint, DrivingDimensionEntity as GRPCDrivingDimension, EdgeTessellation as GRPCEdgeTessellation, - EnhancedRepairToolMessage as GRPCEnhancedRepairToolResponse, Geometries as GRPCGeometries, Knot as GRPCKnot, MaterialEntity as GRPCMaterial, @@ -72,6 +71,10 @@ from ansys.api.discovery.v1.operations.prepare_pb2 import ( EnclosureOptions as GRPCEnclosureOptions, ) +from ansys.api.discovery.v1.operations.repair_pb2 import ( + RepairToolMessage as GRPCRepairToolResponse, +) + import pint from ansys.geometry.core.errors import GeometryRuntimeError @@ -1767,29 +1770,30 @@ def get_tracker_response_with_created_bodies(response) -> dict: return serialized_response -def serialize_repair_command_response(response: GRPCEnhancedRepairToolResponse) -> dict: - """Serialize a EnhancedRepairToolResponse object into a dictionary. +def serialize_repair_command_response(response: GRPCRepairToolResponse) -> dict: + """Serialize a RepairToolResponse object into a dictionary. Parameters ---------- - response : GRPCEnhancedRepairToolResponse - The gRPC EnhancedRepairToolResponse object to serialize. - A dictionary representation of the EnhancedRepairToolResponse object. + response : GRPCRepairToolResponse + The gRPC RepairToolResponse object to serialize. + A dictionary representation of the RepairToolResponse object. """ + found = hasattr(response.result, 'found') return { - "success": response.tracked_command_response.command_response.success, - "found": response.found, - "repaired": response.repaired, - "complete_command_response": serialize_tracked_command_response(response.tracked_changes), + "success": response.result.tracked_command_response.command_response.success, + "found":getattr(response.result, 'found', -1), + "repaired": getattr(response.result, 'repaired', -1), + "complete_command_response": serialize_tracked_command_response(response.result.tracked_command_response ), "created_bodies_monikers": [ - created_body.id for created_body in response.tracked_changes.get("created_bodies", []) + created_body.id for created_body in getattr(response.result.tracked_command_response.tracked_changes, "created_bodies", []) ], "modified_bodies_monikers": [ modified_body.id - for modified_body in response.tracked_changes.get("modified_bodies", []) + for modified_body in getattr(response.result.tracked_command_response.tracked_changes, "modified_bodies", []) ], "deleted_bodies_monikers": [ - deleted_body.id for deleted_body in response.tracked_changes.get("deleted_bodies", []) + deleted_body.id for deleted_body in getattr(response.result.tracked_command_response.tracked_changes, "deleted_bodies", []) ], } diff --git a/src/ansys/geometry/core/_grpc/_services/v1/repair_tools.py b/src/ansys/geometry/core/_grpc/_services/v1/repair_tools.py index ead83d9165..9f0c6827dc 100644 --- a/src/ansys/geometry/core/_grpc/_services/v1/repair_tools.py +++ b/src/ansys/geometry/core/_grpc/_services/v1/repair_tools.py @@ -176,7 +176,7 @@ def find_small_faces(self, **kwargs) -> dict: # noqa: D102 response = self.stub.FindSmallFaces(request) # Return the response - formatted as a dictionary - return response_problem_area_for_face(response) + return response_problem_area_for_edge(response) @protect_grpc def find_stitch_faces(self, **kwargs) -> dict: # noqa: D102 From 8336a3560de043006eeb813eb0a49b809c59cafb Mon Sep 17 00:00:00 2001 From: smereu Date: Tue, 9 Dec 2025 22:53:48 -0600 Subject: [PATCH 21/61] fix enhancedsharetopology --- src/ansys/geometry/core/_grpc/_services/v1/prepare_tools.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ansys/geometry/core/_grpc/_services/v1/prepare_tools.py b/src/ansys/geometry/core/_grpc/_services/v1/prepare_tools.py index 768c615093..4484f0056a 100644 --- a/src/ansys/geometry/core/_grpc/_services/v1/prepare_tools.py +++ b/src/ansys/geometry/core/_grpc/_services/v1/prepare_tools.py @@ -142,14 +142,14 @@ def enhanced_share_topology(self, **kwargs) -> dict: # noqa: D102 ) # Call the gRPC service - response = self.stub.EnhancedShareTopology(request).response_data + response = self.stub.EnhancedShareTopology(request) tracked_response = serialize_tracked_command_response(response.tracked_command_response) # Return the response - formatted as a dictionary return { "success": response.tracked_command_response.command_response.success, - "found": response.found, - "repaired": response.repaired, + "found": getattr(response, "found", -1), + "repaired": getattr(response, "repaired", -1), "created_bodies_monikers": [ created_body.get("id").id for created_body in tracked_response.get("created_bodies", []) From 01800654bc01f51ed62981499b38e26fbcf3a317 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 10 Dec 2025 15:26:29 +0000 Subject: [PATCH 22/61] chore: auto fixes from pre-commit hooks --- .../core/_grpc/_services/v1/conversions.py | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/ansys/geometry/core/_grpc/_services/v1/conversions.py b/src/ansys/geometry/core/_grpc/_services/v1/conversions.py index 20ac2b148f..716c3ff688 100644 --- a/src/ansys/geometry/core/_grpc/_services/v1/conversions.py +++ b/src/ansys/geometry/core/_grpc/_services/v1/conversions.py @@ -74,7 +74,6 @@ from ansys.api.discovery.v1.operations.repair_pb2 import ( RepairToolMessage as GRPCRepairToolResponse, ) - import pint from ansys.geometry.core.errors import GeometryRuntimeError @@ -1779,21 +1778,31 @@ def serialize_repair_command_response(response: GRPCRepairToolResponse) -> dict: The gRPC RepairToolResponse object to serialize. A dictionary representation of the RepairToolResponse object. """ - found = hasattr(response.result, 'found') + found = hasattr(response.result, "found") return { "success": response.result.tracked_command_response.command_response.success, - "found":getattr(response.result, 'found', -1), - "repaired": getattr(response.result, 'repaired', -1), - "complete_command_response": serialize_tracked_command_response(response.result.tracked_command_response ), + "found": getattr(response.result, "found", -1), + "repaired": getattr(response.result, "repaired", -1), + "complete_command_response": serialize_tracked_command_response( + response.result.tracked_command_response + ), "created_bodies_monikers": [ - created_body.id for created_body in getattr(response.result.tracked_command_response.tracked_changes, "created_bodies", []) + created_body.id + for created_body in getattr( + response.result.tracked_command_response.tracked_changes, "created_bodies", [] + ) ], "modified_bodies_monikers": [ modified_body.id - for modified_body in getattr(response.result.tracked_command_response.tracked_changes, "modified_bodies", []) + for modified_body in getattr( + response.result.tracked_command_response.tracked_changes, "modified_bodies", [] + ) ], "deleted_bodies_monikers": [ - deleted_body.id for deleted_body in getattr(response.result.tracked_command_response.tracked_changes, "deleted_bodies", []) + deleted_body.id + for deleted_body in getattr( + response.result.tracked_command_response.tracked_changes, "deleted_bodies", [] + ) ], } From f7453637d82dd971dfd4cf35d8f4e4d513b861f2 Mon Sep 17 00:00:00 2001 From: pyansys-ci-bot <92810346+pyansys-ci-bot@users.noreply.github.com> Date: Wed, 10 Dec 2025 15:42:17 +0000 Subject: [PATCH 23/61] chore: adding changelog file 2359.added.md [dependabot-skip] --- doc/changelog.d/2359.added.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/changelog.d/2359.added.md b/doc/changelog.d/2359.added.md index 11e1be1648..dfa64883e6 100644 --- a/doc/changelog.d/2359.added.md +++ b/doc/changelog.d/2359.added.md @@ -1 +1 @@ -Tracking updates +Feat: tracking updates From bc629a4fb8de0b1ad21b2954332cd91fdac9d8da Mon Sep 17 00:00:00 2001 From: pyansys-ci-bot <92810346+pyansys-ci-bot@users.noreply.github.com> Date: Wed, 10 Dec 2025 16:01:13 +0000 Subject: [PATCH 24/61] chore: adding changelog file 2456.maintenance.md [dependabot-skip] --- doc/changelog.d/2456.maintenance.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 doc/changelog.d/2456.maintenance.md diff --git a/doc/changelog.d/2456.maintenance.md b/doc/changelog.d/2456.maintenance.md new file mode 100644 index 0000000000..0605eb3f1e --- /dev/null +++ b/doc/changelog.d/2456.maintenance.md @@ -0,0 +1 @@ +Chore: fix repair/prepare v1 issues From 15cf35541f905fe48b83de8353fa02529f8aeca7 Mon Sep 17 00:00:00 2001 From: smereu Date: Wed, 10 Dec 2025 14:51:13 -0600 Subject: [PATCH 25/61] Push remaining outstanding changes for repair tool --- .../core/_grpc/_services/v1/conversions.py | 15 +++++++-------- .../core/_grpc/_services/v1/repair_tools.py | 18 +++++++++--------- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/src/ansys/geometry/core/_grpc/_services/v1/conversions.py b/src/ansys/geometry/core/_grpc/_services/v1/conversions.py index 716c3ff688..ef85fddb75 100644 --- a/src/ansys/geometry/core/_grpc/_services/v1/conversions.py +++ b/src/ansys/geometry/core/_grpc/_services/v1/conversions.py @@ -1778,30 +1778,29 @@ def serialize_repair_command_response(response: GRPCRepairToolResponse) -> dict: The gRPC RepairToolResponse object to serialize. A dictionary representation of the RepairToolResponse object. """ - found = hasattr(response.result, "found") return { - "success": response.result.tracked_command_response.command_response.success, - "found": getattr(response.result, "found", -1), - "repaired": getattr(response.result, "repaired", -1), + "success": response.tracked_command_response.command_response.success, + "found": getattr(response, "found", -1), + "repaired": getattr(response, "repaired", -1), "complete_command_response": serialize_tracked_command_response( - response.result.tracked_command_response + response.tracked_command_response ), "created_bodies_monikers": [ created_body.id for created_body in getattr( - response.result.tracked_command_response.tracked_changes, "created_bodies", [] + response.tracked_command_response.tracked_changes, "created_bodies", [] ) ], "modified_bodies_monikers": [ modified_body.id for modified_body in getattr( - response.result.tracked_command_response.tracked_changes, "modified_bodies", [] + response.tracked_command_response.tracked_changes, "modified_bodies", [] ) ], "deleted_bodies_monikers": [ deleted_body.id for deleted_body in getattr( - response.result.tracked_command_response.tracked_changes, "deleted_bodies", [] + response.tracked_command_response.tracked_changes, "deleted_bodies", [] ) ], } diff --git a/src/ansys/geometry/core/_grpc/_services/v1/repair_tools.py b/src/ansys/geometry/core/_grpc/_services/v1/repair_tools.py index 9f0c6827dc..f1d6e63b5c 100644 --- a/src/ansys/geometry/core/_grpc/_services/v1/repair_tools.py +++ b/src/ansys/geometry/core/_grpc/_services/v1/repair_tools.py @@ -157,7 +157,7 @@ def find_missing_faces(self, **kwargs) -> dict: # noqa: D102 response = self.stub.FindMissingFaces(request) # Return the response - formatted as a dictionary - return response_problem_area_for_face(response) + return response_problem_area_for_edge(response) @protect_grpc def find_small_faces(self, **kwargs) -> dict: # noqa: D102 @@ -176,7 +176,7 @@ def find_small_faces(self, **kwargs) -> dict: # noqa: D102 response = self.stub.FindSmallFaces(request) # Return the response - formatted as a dictionary - return response_problem_area_for_edge(response) + return response_problem_area_for_face(response) @protect_grpc def find_stitch_faces(self, **kwargs) -> dict: # noqa: D102 @@ -241,7 +241,7 @@ def find_and_fix_short_edges(self, **kwargs): # noqa: D102 # Call the gRPC service response = self.stub.FindAndFixShortEdges(request) - return serialize_repair_command_response(response.response_data) + return serialize_repair_command_response(response.result) @protect_grpc def find_and_fix_extra_edges(self, **kwargs) -> dict: # noqa: D102 @@ -256,7 +256,7 @@ def find_and_fix_extra_edges(self, **kwargs) -> dict: # noqa: D102 # Call the gRPC service response = self.stub.FindAndFixExtraEdges(request) - return serialize_repair_command_response(response.response_data) + return serialize_repair_command_response(response.result) @protect_grpc def find_and_fix_split_edges(self, **kwargs) -> dict: # noqa: D102 @@ -272,7 +272,7 @@ def find_and_fix_split_edges(self, **kwargs) -> dict: # noqa: D102 # Call the gRPC service response = self.stub.FindAndFixSplitEdges(request) - return serialize_repair_command_response(response.response_data) + return serialize_repair_command_response(response.result) @protect_grpc def find_and_fix_simplify(self, **kwargs) -> dict: # noqa: D102 @@ -287,7 +287,7 @@ def find_and_fix_simplify(self, **kwargs) -> dict: # noqa: D102 # Call the gRPC service response = self.stub.FindAndSimplify(request) - return serialize_repair_command_response(response.response_data) + return serialize_repair_command_response(response.result) @protect_grpc def find_and_fix_stitch_faces(self, **kwargs) -> dict: # noqa: D102 @@ -308,7 +308,7 @@ def find_and_fix_stitch_faces(self, **kwargs) -> dict: # noqa: D102 # Call the gRPC service response = self.stub.FindAndFixStitchFaces(request) - return serialize_repair_command_response(response.response_data) + return serialize_repair_command_response(response.result) @protect_grpc def inspect_geometry(self, **kwargs) -> dict: # noqa: D102 @@ -338,7 +338,7 @@ def repair_geometry(self, **kwargs) -> dict: # noqa: D102 response = self.stub.RepairGeometry(request) # Return the response - formatted as a dictionary - return serialize_repair_command_response(response) + return serialize_repair_command_response(response.result) @protect_grpc def fix_duplicate_faces(self, **kwargs) -> dict: # noqa: D102 @@ -473,7 +473,7 @@ def fix_unsimplified_faces(self, **kwargs) -> dict: # noqa: D102 response = self.stub.FixAdjustSimplify(request) # Return the response - formatted as a dictionary - return serialize_repair_command_response(response) + return serialize_repair_command_response(response.result) @protect_grpc def fix_interference(self, **kwargs) -> dict: # noqa: D102 From 3af3d1a69c47e93936ca1e229e7eeeec5b2c9973 Mon Sep 17 00:00:00 2001 From: smereu Date: Wed, 10 Dec 2025 15:10:26 -0600 Subject: [PATCH 26/61] Fix remaining failures in repair tools --- src/ansys/geometry/core/_grpc/_services/v0/repair_tools.py | 4 ++-- src/ansys/geometry/core/_grpc/_services/v1/repair_tools.py | 2 +- src/ansys/geometry/core/tools/repair_tools.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ansys/geometry/core/_grpc/_services/v0/repair_tools.py b/src/ansys/geometry/core/_grpc/_services/v0/repair_tools.py index 50712881f1..b766246912 100644 --- a/src/ansys/geometry/core/_grpc/_services/v0/repair_tools.py +++ b/src/ansys/geometry/core/_grpc/_services/v0/repair_tools.py @@ -517,7 +517,7 @@ def inspect_geometry(self, **kwargs) -> dict: # noqa: D102 from ansys.api.geometry.v0.repairtools_pb2 import InspectGeometryRequest # Create the request - assumes all inputs are valid and of the proper type - request = InspectGeometryRequest(bodies=kwargs.get("bodies", [])) + request = InspectGeometryRequest(bodies=kwargs.get("body_ids", [])) # Call the gRPC service inspect_result_response = self.stub.InspectGeometry(request) @@ -530,7 +530,7 @@ def repair_geometry(self, **kwargs) -> dict: # noqa: D102 from ansys.api.geometry.v0.repairtools_pb2 import RepairGeometryRequest # Create the request - assumes all inputs are valid and of the proper type - request = RepairGeometryRequest(bodies=kwargs.get("bodies")) + request = RepairGeometryRequest(bodies=kwargs.get("body_ids")) # Call the gRPC service response = self.stub.RepairGeometry(request) diff --git a/src/ansys/geometry/core/_grpc/_services/v1/repair_tools.py b/src/ansys/geometry/core/_grpc/_services/v1/repair_tools.py index f1d6e63b5c..849621d1f9 100644 --- a/src/ansys/geometry/core/_grpc/_services/v1/repair_tools.py +++ b/src/ansys/geometry/core/_grpc/_services/v1/repair_tools.py @@ -331,7 +331,7 @@ def repair_geometry(self, **kwargs) -> dict: # noqa: D102 # Create the request - assumes all inputs are valid and of the proper type request = RepairGeometryRequest( - body_ids=[build_grpc_id(body) for body in kwargs.get("bodies")] + body_ids=[build_grpc_id(body) for body in kwargs.get("body_ids")] ) # Call the gRPC service diff --git a/src/ansys/geometry/core/tools/repair_tools.py b/src/ansys/geometry/core/tools/repair_tools.py index 2a76abd612..378555ce3f 100644 --- a/src/ansys/geometry/core/tools/repair_tools.py +++ b/src/ansys/geometry/core/tools/repair_tools.py @@ -875,7 +875,7 @@ def __create_inspect_result_from_response( inspect_results = [] design = self._modeler.get_active_design() for inspect_geometry_result in inspect_geometry_results: - body = get_bodies_from_ids(design, [inspect_geometry_result["body"]["id"]]) + body = get_bodies_from_ids(design, [inspect_geometry_result["body"]["id"].id]) issues = self.__create_issues_from_response(inspect_geometry_result.get("issues")) inspect_result = InspectResult( grpc_client=self._grpc_client, body=body[0], issues=issues From 0901b2d3975c9d128e9bd94ecc810d5edac544dc Mon Sep 17 00:00:00 2001 From: smereu Date: Wed, 10 Dec 2025 17:27:10 -0600 Subject: [PATCH 27/61] fix logo detection and respond to code review --- .../core/_grpc/_services/v1/conversions.py | 44 +------------------ .../core/_grpc/_services/v1/prepare_tools.py | 12 +++-- 2 files changed, 10 insertions(+), 46 deletions(-) diff --git a/src/ansys/geometry/core/_grpc/_services/v1/conversions.py b/src/ansys/geometry/core/_grpc/_services/v1/conversions.py index ef85fddb75..343c7212f9 100644 --- a/src/ansys/geometry/core/_grpc/_services/v1/conversions.py +++ b/src/ansys/geometry/core/_grpc/_services/v1/conversions.py @@ -1729,46 +1729,6 @@ def serialize_entity_identifier(entity): ], } - -def get_standard_tracker_response(response) -> dict: - """Get a standard dictionary response from a TrackerCommandResponse gRPC object. - - Parameters - ---------- - response : TrackerCommandResponse - The gRPC TrackerCommandResponse object. - - Returns - ------- - dict - A dictionary representing the standard tracker response - """ - return { - "success": response.command_response.success, - "tracker_response": serialize_tracked_command_response(response.tracked_changes), - } - - -def get_tracker_response_with_created_bodies(response) -> dict: - """Get a dictionary response from a TrackerCommandResponse gRPC object including created bodies. - - Parameters - ---------- - response : TrackerCommandResponse - The gRPC TrackerCommandResponse object. - - Returns - ------- - dict - A dictionary representing the tracker response with created bodies. - """ - serialized_response = get_standard_tracker_response(response) - serialized_response["created_bodies"] = serialized_response["tracker_response"].get( - "created_bodies", [] - ) - return serialized_response - - def serialize_repair_command_response(response: GRPCRepairToolResponse) -> dict: """Serialize a RepairToolResponse object into a dictionary. @@ -1786,13 +1746,13 @@ def serialize_repair_command_response(response: GRPCRepairToolResponse) -> dict: response.tracked_command_response ), "created_bodies_monikers": [ - created_body.id + created_body.id.id for created_body in getattr( response.tracked_command_response.tracked_changes, "created_bodies", [] ) ], "modified_bodies_monikers": [ - modified_body.id + modified_body.id.id for modified_body in getattr( response.tracked_command_response.tracked_changes, "modified_bodies", [] ) diff --git a/src/ansys/geometry/core/_grpc/_services/v1/prepare_tools.py b/src/ansys/geometry/core/_grpc/_services/v1/prepare_tools.py index 4484f0056a..3dd37848df 100644 --- a/src/ansys/geometry/core/_grpc/_services/v1/prepare_tools.py +++ b/src/ansys/geometry/core/_grpc/_services/v1/prepare_tools.py @@ -31,7 +31,6 @@ build_grpc_id, from_enclosure_options_to_grpc_enclosure_options, from_length_to_grpc_quantity, - get_standard_tracker_response, serialize_tracked_command_response, ) @@ -190,7 +189,7 @@ def find_logos(self, **kwargs) -> dict: # noqa: D102 # Return the response - formatted as a dictionary return { - "id": response.id, + "id": getattr(response, "id", None), "face_ids": [face.id for face in response.logo_faces], } @@ -223,7 +222,9 @@ def find_and_remove_logos(self, **kwargs) -> dict: # noqa: D102 response = self.stub.FindAndRemoveLogos(request) # Return the response - formatted as a dictionary - return get_standard_tracker_response(response) + return { + "success": response.tracked_command_response.command_response.success + } @protect_grpc def remove_logo(self, **kwargs): # noqa: D102 @@ -238,7 +239,10 @@ def remove_logo(self, **kwargs): # noqa: D102 response = self.stub.RemoveLogo(request) # Return the response - formatted as a dictionary - return get_standard_tracker_response(response) + return { + "success": response.tracked_command_response.command_response.success + } + @protect_grpc def detect_helixes(self, **kwargs) -> dict: # noqa: D102 From 9556f0f8175a248a2e1e141ae353dc7673fa6707 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 10 Dec 2025 23:27:47 +0000 Subject: [PATCH 28/61] chore: auto fixes from pre-commit hooks --- .../geometry/core/_grpc/_services/v1/conversions.py | 1 + .../geometry/core/_grpc/_services/v1/prepare_tools.py | 9 ++------- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/ansys/geometry/core/_grpc/_services/v1/conversions.py b/src/ansys/geometry/core/_grpc/_services/v1/conversions.py index 343c7212f9..0de2f8eb66 100644 --- a/src/ansys/geometry/core/_grpc/_services/v1/conversions.py +++ b/src/ansys/geometry/core/_grpc/_services/v1/conversions.py @@ -1729,6 +1729,7 @@ def serialize_entity_identifier(entity): ], } + def serialize_repair_command_response(response: GRPCRepairToolResponse) -> dict: """Serialize a RepairToolResponse object into a dictionary. diff --git a/src/ansys/geometry/core/_grpc/_services/v1/prepare_tools.py b/src/ansys/geometry/core/_grpc/_services/v1/prepare_tools.py index 3dd37848df..8881aacec9 100644 --- a/src/ansys/geometry/core/_grpc/_services/v1/prepare_tools.py +++ b/src/ansys/geometry/core/_grpc/_services/v1/prepare_tools.py @@ -222,9 +222,7 @@ def find_and_remove_logos(self, **kwargs) -> dict: # noqa: D102 response = self.stub.FindAndRemoveLogos(request) # Return the response - formatted as a dictionary - return { - "success": response.tracked_command_response.command_response.success - } + return {"success": response.tracked_command_response.command_response.success} @protect_grpc def remove_logo(self, **kwargs): # noqa: D102 @@ -239,10 +237,7 @@ def remove_logo(self, **kwargs): # noqa: D102 response = self.stub.RemoveLogo(request) # Return the response - formatted as a dictionary - return { - "success": response.tracked_command_response.command_response.success - } - + return {"success": response.tracked_command_response.command_response.success} @protect_grpc def detect_helixes(self, **kwargs) -> dict: # noqa: D102 From 2c9976007dc833fea8fe3326a24c5da9485d129d Mon Sep 17 00:00:00 2001 From: smereu Date: Thu, 11 Dec 2025 10:52:28 -0600 Subject: [PATCH 29/61] Misc. fixes for proper handling of ids Misc. fixes for proper handling of ids --- .../core/_grpc/_services/v1/prepare_tools.py | 16 +++++++--------- .../core/_grpc/_services/v1/repair_tools.py | 10 +++++----- src/ansys/geometry/core/tools/repair_tools.py | 2 +- 3 files changed, 13 insertions(+), 15 deletions(-) diff --git a/src/ansys/geometry/core/_grpc/_services/v1/prepare_tools.py b/src/ansys/geometry/core/_grpc/_services/v1/prepare_tools.py index 3dd37848df..04cb4a0765 100644 --- a/src/ansys/geometry/core/_grpc/_services/v1/prepare_tools.py +++ b/src/ansys/geometry/core/_grpc/_services/v1/prepare_tools.py @@ -195,7 +195,10 @@ def find_logos(self, **kwargs) -> dict: # noqa: D102 @protect_grpc def find_and_remove_logos(self, **kwargs) -> dict: # noqa: D102 - from ansys.api.discovery.v1.operations.prepare_pb2 import FindLogoOptions, FindLogosRequest + from ansys.api.discovery.v1.operations.prepare_pb2 import ( + FindAndRemoveLogosRequest, + FindLogoOptions, + ) # Check height objects min_height = ( @@ -210,7 +213,7 @@ def find_and_remove_logos(self, **kwargs) -> dict: # noqa: D102 ) # Create the request - assumes all inputs are valid and of the proper type - request = FindLogosRequest( + request = FindAndRemoveLogosRequest( body_ids=[build_grpc_id(body) for body in kwargs["bodies"]], options=FindLogoOptions( min_height=min_height, @@ -222,9 +225,7 @@ def find_and_remove_logos(self, **kwargs) -> dict: # noqa: D102 response = self.stub.FindAndRemoveLogos(request) # Return the response - formatted as a dictionary - return { - "success": response.tracked_command_response.command_response.success - } + return {"success": response.tracked_command_response.command_response.success} @protect_grpc def remove_logo(self, **kwargs): # noqa: D102 @@ -239,10 +240,7 @@ def remove_logo(self, **kwargs): # noqa: D102 response = self.stub.RemoveLogo(request) # Return the response - formatted as a dictionary - return { - "success": response.tracked_command_response.command_response.success - } - + return {"success": response.tracked_command_response.command_response.success} @protect_grpc def detect_helixes(self, **kwargs) -> dict: # noqa: D102 diff --git a/src/ansys/geometry/core/_grpc/_services/v1/repair_tools.py b/src/ansys/geometry/core/_grpc/_services/v1/repair_tools.py index 849621d1f9..a462b6a9aa 100644 --- a/src/ansys/geometry/core/_grpc/_services/v1/repair_tools.py +++ b/src/ansys/geometry/core/_grpc/_services/v1/repair_tools.py @@ -493,7 +493,7 @@ def fix_interference(self, **kwargs) -> dict: # noqa: D102 def __serialize_inspect_result_response(self, response) -> dict: # noqa: D102 def serialize_body(body): return { - "id": body.id, + "id": body.id.id, "name": body.name, "can_suppress": body.can_suppress, "transform_to_master": { @@ -502,13 +502,13 @@ def serialize_body(body): "m22": body.transform_to_master.m22, "m33": body.transform_to_master.m33, }, - "master_id": body.master_id, - "parent_id": body.parent_id, + "master_id": body.master_id.id, + "parent_id": body.parent_id.id, } def serialize_face(face): return { - "id": face.id, + "id": face.id.id, "surface_type": face.surface_type, "export_id": face.export_id, "is_reversed": getattr(face, "is_reversed", False), @@ -517,7 +517,7 @@ def serialize_face(face): def serialize_edge(edge): return { - "id": edge.id, + "id": edge.id.id, "curve_type": edge.curve_type, "export_id": edge.export_id, "length": edge.length, diff --git a/src/ansys/geometry/core/tools/repair_tools.py b/src/ansys/geometry/core/tools/repair_tools.py index 378555ce3f..2a76abd612 100644 --- a/src/ansys/geometry/core/tools/repair_tools.py +++ b/src/ansys/geometry/core/tools/repair_tools.py @@ -875,7 +875,7 @@ def __create_inspect_result_from_response( inspect_results = [] design = self._modeler.get_active_design() for inspect_geometry_result in inspect_geometry_results: - body = get_bodies_from_ids(design, [inspect_geometry_result["body"]["id"].id]) + body = get_bodies_from_ids(design, [inspect_geometry_result["body"]["id"]]) issues = self.__create_issues_from_response(inspect_geometry_result.get("issues")) inspect_result = InspectResult( grpc_client=self._grpc_client, body=body[0], issues=issues From c36be3193b7dfd2f894fc18eaf914ae947a6d185 Mon Sep 17 00:00:00 2001 From: Jacob Kerstetter Date: Thu, 11 Dec 2025 13:12:20 -0500 Subject: [PATCH 30/61] fixing backward compat test --- src/ansys/geometry/core/_grpc/_services/v0/repair_tools.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/ansys/geometry/core/_grpc/_services/v0/repair_tools.py b/src/ansys/geometry/core/_grpc/_services/v0/repair_tools.py index b766246912..817daa9396 100644 --- a/src/ansys/geometry/core/_grpc/_services/v0/repair_tools.py +++ b/src/ansys/geometry/core/_grpc/_services/v0/repair_tools.py @@ -30,6 +30,7 @@ from ..base.repair_tools import GRPCRepairToolsService from .conversions import ( serialize_tracker_command_response, + build_grpc_id, ) @@ -517,7 +518,9 @@ def inspect_geometry(self, **kwargs) -> dict: # noqa: D102 from ansys.api.geometry.v0.repairtools_pb2 import InspectGeometryRequest # Create the request - assumes all inputs are valid and of the proper type - request = InspectGeometryRequest(bodies=kwargs.get("body_ids", [])) + request = InspectGeometryRequest( + bodies=[build_grpc_id(id) for id in kwargs.get("body_ids", [])] + ) # Call the gRPC service inspect_result_response = self.stub.InspectGeometry(request) From d7214701b9b3e20399bc87535b85ca6ca555054b Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 11 Dec 2025 18:12:37 +0000 Subject: [PATCH 31/61] chore: auto fixes from pre-commit hooks --- src/ansys/geometry/core/_grpc/_services/v0/repair_tools.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ansys/geometry/core/_grpc/_services/v0/repair_tools.py b/src/ansys/geometry/core/_grpc/_services/v0/repair_tools.py index 817daa9396..188a39cc92 100644 --- a/src/ansys/geometry/core/_grpc/_services/v0/repair_tools.py +++ b/src/ansys/geometry/core/_grpc/_services/v0/repair_tools.py @@ -29,8 +29,8 @@ from ..base.conversions import from_measurement_to_server_angle, from_measurement_to_server_length from ..base.repair_tools import GRPCRepairToolsService from .conversions import ( - serialize_tracker_command_response, build_grpc_id, + serialize_tracker_command_response, ) From b044bae5fb69ae5615eb67691b324abd38736113 Mon Sep 17 00:00:00 2001 From: Umut Soysal Date: Thu, 11 Dec 2025 14:05:01 -0600 Subject: [PATCH 32/61] Update conversions.py --- .../core/_grpc/_services/v1/conversions.py | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/src/ansys/geometry/core/_grpc/_services/v1/conversions.py b/src/ansys/geometry/core/_grpc/_services/v1/conversions.py index 0de2f8eb66..3c6ef163d2 100644 --- a/src/ansys/geometry/core/_grpc/_services/v1/conversions.py +++ b/src/ansys/geometry/core/_grpc/_services/v1/conversions.py @@ -1683,6 +1683,54 @@ 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, + "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, + }, + "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): + return { + "id": part.id, + } def serialize_entity_identifier(entity): """Serialize an EntityIdentifier object into a dictionary.""" @@ -1692,6 +1740,25 @@ def serialize_entity_identifier(entity): return { "success": getattr(response.command_response, "success", False), + "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.tracked_changes, "created_bodies", []) ], From c79f1925d2be21cd48cf9358bb1547f90e3a0883 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 11 Dec 2025 20:05:16 +0000 Subject: [PATCH 33/61] chore: auto fixes from pre-commit hooks --- src/ansys/geometry/core/_grpc/_services/v1/conversions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ansys/geometry/core/_grpc/_services/v1/conversions.py b/src/ansys/geometry/core/_grpc/_services/v1/conversions.py index 3c6ef163d2..e74c3ec701 100644 --- a/src/ansys/geometry/core/_grpc/_services/v1/conversions.py +++ b/src/ansys/geometry/core/_grpc/_services/v1/conversions.py @@ -1683,7 +1683,7 @@ def serialize_body(body): "parent_id": body.parent_id, "is_surface": body.is_surface, } - + def serialize_component(component): return { "id": component.id, From 7a5a951361f9df32842b60930962735efc433c36 Mon Sep 17 00:00:00 2001 From: Umut Soysal Date: Thu, 11 Dec 2025 14:07:41 -0600 Subject: [PATCH 34/61] Update prepare_tools.py --- src/ansys/geometry/core/_grpc/_services/v1/prepare_tools.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/ansys/geometry/core/_grpc/_services/v1/prepare_tools.py b/src/ansys/geometry/core/_grpc/_services/v1/prepare_tools.py index 04cb4a0765..035384a12b 100644 --- a/src/ansys/geometry/core/_grpc/_services/v1/prepare_tools.py +++ b/src/ansys/geometry/core/_grpc/_services/v1/prepare_tools.py @@ -67,10 +67,14 @@ def extract_volume_from_faces(self, **kwargs) -> dict: # noqa: D102 # Call the gRPC service response = self.stub.ExtractVolumeFromFaces(request) + serialized_tracker_response = serialize_tracked_command_response(response=response.changes) + + # Return the response - formatted as a dictionary return { "success": response.tracked_command_response.command_response.success, "created_bodies": [body.id.id for body in response.created_bodies], + "complete_command_response": serialized_tracker_response, } @protect_grpc From 054815651874d48828a52c6fd2b47f01cf9cc2a6 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 11 Dec 2025 20:08:05 +0000 Subject: [PATCH 35/61] chore: auto fixes from pre-commit hooks --- src/ansys/geometry/core/_grpc/_services/v1/prepare_tools.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ansys/geometry/core/_grpc/_services/v1/prepare_tools.py b/src/ansys/geometry/core/_grpc/_services/v1/prepare_tools.py index 035384a12b..375e3559ea 100644 --- a/src/ansys/geometry/core/_grpc/_services/v1/prepare_tools.py +++ b/src/ansys/geometry/core/_grpc/_services/v1/prepare_tools.py @@ -69,7 +69,6 @@ def extract_volume_from_faces(self, **kwargs) -> dict: # noqa: D102 serialized_tracker_response = serialize_tracked_command_response(response=response.changes) - # Return the response - formatted as a dictionary return { "success": response.tracked_command_response.command_response.success, From f964a4d586d1097a73a2ac79c490a100209782ab Mon Sep 17 00:00:00 2001 From: Umut Soysal Date: Thu, 11 Dec 2025 14:11:19 -0600 Subject: [PATCH 36/61] Update repair_tools.py --- src/ansys/geometry/core/_grpc/_services/v0/repair_tools.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ansys/geometry/core/_grpc/_services/v0/repair_tools.py b/src/ansys/geometry/core/_grpc/_services/v0/repair_tools.py index 8bd97b1481..29794d5e08 100644 --- a/src/ansys/geometry/core/_grpc/_services/v0/repair_tools.py +++ b/src/ansys/geometry/core/_grpc/_services/v0/repair_tools.py @@ -533,7 +533,9 @@ def repair_geometry(self, **kwargs) -> dict: # noqa: D102 from ansys.api.geometry.v0.repairtools_pb2 import RepairGeometryRequest # Create the request - assumes all inputs are valid and of the proper type - request = RepairGeometryRequest(bodies=kwargs.get("bodies")) + request = RepairGeometryRequest( + bodies=[build_grpc_id(id) for id in kwargs.get("body_ids", [])] + ) # Call the gRPC service response = self.stub.RepairGeometry(request) From 4fec65b01e98324157107b708ac5785e0d5a5364 Mon Sep 17 00:00:00 2001 From: Umut Soysal Date: Thu, 11 Dec 2025 15:08:24 -0600 Subject: [PATCH 37/61] Update prepare_tools.py --- src/ansys/geometry/core/tools/prepare_tools.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/ansys/geometry/core/tools/prepare_tools.py b/src/ansys/geometry/core/tools/prepare_tools.py index 1dad9ab3d5..49c47d47b4 100644 --- a/src/ansys/geometry/core/tools/prepare_tools.py +++ b/src/ansys/geometry/core/tools/prepare_tools.py @@ -155,7 +155,10 @@ 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() + if not pyansys_geom.USE_TRACKER_TO_UPDATE_DESIGN: + parent_design._update_design_inplace() + else: + 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 87772dea3b09662f91e27457a387fafade023928 Mon Sep 17 00:00:00 2001 From: Umut Soysal Date: Thu, 11 Dec 2025 15:19:47 -0600 Subject: [PATCH 38/61] 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 d681891f2aa7084337ebafc12b3007092d1d8e37 Mon Sep 17 00:00:00 2001 From: Umut Soysal Date: Thu, 11 Dec 2025 15:44:44 -0600 Subject: [PATCH 39/61] test case --- src/ansys/geometry/core/__init__.py | 2 +- tests/integration/files/hollowCylinder1.dsco | Bin 0 -> 128965 bytes tests/integration/test_design.py | 35 +++++++++++++++++++ 3 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 tests/integration/files/hollowCylinder1.dsco 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.""" diff --git a/tests/integration/files/hollowCylinder1.dsco b/tests/integration/files/hollowCylinder1.dsco new file mode 100644 index 0000000000000000000000000000000000000000..f9c30886317022658144e409732c62d28e932c40 GIT binary patch literal 128965 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
6u5Y$^+wfHRwC-nQXe(q;wp< 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 0, "Design should have at least one component" + + # Get the body and faces + body = design.components[0].bodies[0] + inside_faces = [body.faces[0]] + sealing_faces = [body.faces[1], body.faces[2]] + + # Extract volume from faces - this should trigger design update tracking + modeler.prepare_tools.extract_volume_from_faces(sealing_faces, inside_faces) + + # Verify design was updated with new component + assert len(design.components) > initial_component_count, ( + "Design should have more components after extract_volume_from_faces" + ) + + # Verify first component still has bodies + assert len(design.components[0].bodies) > 0, "Component 0 should have bodies" + assert design.components[0].bodies[0].name, "Body in component 0 should have a name" + + # Verify new component was created with the extracted body + assert len(design.components[1].bodies) > 0, "Component 1 should have bodies" + assert design.components[1].bodies[0].name, "Body in component 1 should have a name" \ No newline at end of file From 459cebfb91741d9517b1e2d4ef08c3497d45107a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 11 Dec 2025 21:46:51 +0000 Subject: [PATCH 40/61] chore: auto fixes from pre-commit hooks --- tests/integration/test_design.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/integration/test_design.py b/tests/integration/test_design.py index 0d9a00ffa2..12d0607526 100644 --- a/tests/integration/test_design.py +++ b/tests/integration/test_design.py @@ -4338,6 +4338,7 @@ def test_design_point_get_named_selections(modeler: Modeler): else: assert len(ns_list) == 0 # No named selection for this design point + def test_check_design_update(modeler: Modeler): """Test that design updates are tracked when USE_TRACKER_TO_UPDATE_DESIGN is enabled.""" # import ansys.geometry.core as pyansys_geo @@ -4371,4 +4372,4 @@ def test_check_design_update(modeler: Modeler): # Verify new component was created with the extracted body assert len(design.components[1].bodies) > 0, "Component 1 should have bodies" - assert design.components[1].bodies[0].name, "Body in component 1 should have a name" \ No newline at end of file + assert design.components[1].bodies[0].name, "Body in component 1 should have a name" From 2a80786d83f271ae4ef5147f50a31ff4d0df8031 Mon Sep 17 00:00:00 2001 From: Umut Soysal Date: Thu, 11 Dec 2025 16:35:33 -0600 Subject: [PATCH 41/61] Update hollowCylinder1.dsco --- tests/integration/files/hollowCylinder1.dsco | Bin 128965 -> 98944 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/tests/integration/files/hollowCylinder1.dsco b/tests/integration/files/hollowCylinder1.dsco index f9c30886317022658144e409732c62d28e932c40..e0cceaf05756ab81b84f295ca831518f938e834d 100644 GIT binary patch delta 39637 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%0A&(>s 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 delta 65032 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 Date: Sat, 13 Dec 2025 16:29:37 -0600 Subject: [PATCH 42/61] object name difference in v1 --- 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 49c47d47b4..6f47377e8c 100644 --- a/src/ansys/geometry/core/tools/prepare_tools.py +++ b/src/ansys/geometry/core/tools/prepare_tools.py @@ -158,7 +158,7 @@ def extract_volume_from_faces( if not pyansys_geom.USE_TRACKER_TO_UPDATE_DESIGN: parent_design._update_design_inplace() else: - 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 d5ed2f9bea46058517d1f24df14fbe148840f6db Mon Sep 17 00:00:00 2001 From: Umut Soysal Date: Sat, 13 Dec 2025 16:29:58 -0600 Subject: [PATCH 43/61] added comment and rename --- src/ansys/geometry/core/_grpc/_services/v1/prepare_tools.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/ansys/geometry/core/_grpc/_services/v1/prepare_tools.py b/src/ansys/geometry/core/_grpc/_services/v1/prepare_tools.py index 375e3559ea..39a8fdb22f 100644 --- a/src/ansys/geometry/core/_grpc/_services/v1/prepare_tools.py +++ b/src/ansys/geometry/core/_grpc/_services/v1/prepare_tools.py @@ -66,8 +66,9 @@ def extract_volume_from_faces(self, **kwargs) -> dict: # noqa: D102 # Call the gRPC service response = self.stub.ExtractVolumeFromFaces(request) - - serialized_tracker_response = serialize_tracked_command_response(response=response.changes) + + # Convert grpc tracked command response to serialized format. + serialized_tracker_response = serialize_tracked_command_response(response=response.tracked_command_response) # Return the response - formatted as a dictionary return { From e82b5f14cbe435d0871031e7e6676f253bc0ed1f Mon Sep 17 00:00:00 2001 From: Umut Soysal Date: Sat, 13 Dec 2025 16:30:23 -0600 Subject: [PATCH 44/61] type hints, refactorings --- .../core/_grpc/_services/v1/conversions.py | 348 +++++++++++------- 1 file changed, 223 insertions(+), 125 deletions(-) diff --git a/src/ansys/geometry/core/_grpc/_services/v1/conversions.py b/src/ansys/geometry/core/_grpc/_services/v1/conversions.py index e74c3ec701..da625e994c 100644 --- a/src/ansys/geometry/core/_grpc/_services/v1/conversions.py +++ b/src/ansys/geometry/core/_grpc/_services/v1/conversions.py @@ -44,6 +44,8 @@ Quantity as GRPCQuantity, ) from ansys.api.discovery.v1.design.designmessages_pb2 import ( + BodyEntity as GRPCBodyEntity, + ComponentEntity as GRPCComponentEntity, CurveGeometry as GRPCCurveGeometry, DatumPointEntity as GRPCDesignPoint, DrivingDimensionEntity as GRPCDrivingDimension, @@ -55,6 +57,7 @@ Matrix as GRPCMatrix, NurbsCurve as GRPCNurbsCurve, NurbsSurface as GRPCNurbsSurface, + PartEntity as GRPCPartEntity, Surface as GRPCSurface, Tessellation as GRPCTessellation, TessellationOptions as GRPCTessellationOptions, @@ -1654,146 +1657,241 @@ def from_enclosure_options_to_grpc_enclosure_options( ) -def serialize_tracked_command_response(response: GRPCTrackedCommandResponse) -> dict: - """Serialize a TrackedCommandResponse object into a dictionary. +def serialize_body(body: GRPCBodyEntity) -> dict: + """Serialize a GRPCBodyEntity object into a dictionary. + + It is not directly converted to a pygeometry object because we'll assign it is place and + construct the object while updating the design object by the tracker output. Parameters ---------- - response : GRPCTrackedCommandResponse - The gRPC TrackedCommandResponse object to serialize. + body : GRPCBodyEntity + The gRPC BodyEntity object to serialize. Returns ------- dict - A dictionary representation of the TrackedCommandResponse object. + A dictionary representation of the BodyEntity object without gRPC dependencies. """ + # Extract basic fields + body_id = body.id.id + body_name = body.name + body_can_suppress = body.can_suppress + body_master_id = body.master_id.id.id if hasattr(body.master_id, "id") and hasattr(body.master_id.id, "id") else (body.master_id.id if hasattr(body.master_id, "id") else "") + body_parent_id = body.parent_id.id.id if hasattr(body.parent_id, "id") and hasattr(body.parent_id.id, "id") else (body.parent_id.id if hasattr(body.parent_id, "id") else "") + body_is_surface = body.is_surface + + # Extract transform_to_master matrix + transform_m00 = body.transform_to_master.m00 + transform_m11 = body.transform_to_master.m11 + transform_m22 = body.transform_to_master.m22 + transform_m33 = body.transform_to_master.m33 + + transform_to_master = { + "m00": transform_m00, + "m11": transform_m11, + "m22": transform_m22, + "m33": transform_m33, + } + + return { + "id": body_id, + "name": body_name, + "can_suppress": body_can_suppress, + "transform_to_master": transform_to_master, + "master_id": body_master_id, + "parent_id": body_parent_id, + "is_surface": body_is_surface, + } - def serialize_body(body): - return { - "id": body.id, - "name": body.name, - "can_suppress": body.can_suppress, - "transform_to_master": { - "m00": body.transform_to_master.m00, - "m11": body.transform_to_master.m11, - "m22": body.transform_to_master.m22, - "m33": body.transform_to_master.m33, - }, - "master_id": body.master_id, - "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, - "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, - }, - "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_component(component: GRPCComponentEntity) -> dict: + """Serialize a GRPCComponentEntity object into a dictionary. - def serialize_part(part): - return { - "id": part.id, - } + Parameters + ---------- + component : GRPCComponentEntity + The gRPC ComponentEntity object to serialize. - def serialize_entity_identifier(entity): - """Serialize an EntityIdentifier object into a dictionary.""" - return { - "id": entity.id, + Returns + ------- + dict + A dictionary representation of the ComponentEntity object without gRPC dependencies. + """ + def extract_id(obj): + if hasattr(obj, "id"): + if hasattr(obj.id, "id"): + return obj.id.id + return obj.id + return "" + + # Extract basic fields + component_id = component.id.id + component_name = component.name + component_display_name = component.display_name + + # Extract part_occurrence + part_occurrence = None + if hasattr(component, "part_occurrence"): + part_occurrence_id = extract_id(component.part_occurrence) + part_occurrence_name = component.part_occurrence.name + part_occurrence = { + "id": part_occurrence_id, + "name": part_occurrence_name, } + + # Extract placement matrix + placement_m00 = 1.0 + placement_m11 = 1.0 + placement_m22 = 1.0 + placement_m33 = 1.0 + if hasattr(component, "placement"): + placement_m00 = component.placement.m00 + placement_m11 = component.placement.m11 + placement_m22 = component.placement.m22 + placement_m33 = component.placement.m33 + + placement = { + "m00": placement_m00, + "m11": placement_m11, + "m22": placement_m22, + "m33": placement_m33, + } + + # Extract part_master + part_master = None + if hasattr(component, "part_master"): + part_master_id = extract_id(component.part_master) + part_master_name = component.part_master.name + part_master = { + "id": part_master_id, + "name": part_master_name, + } + + # Extract master_id and parent_id + master_id = extract_id(component.master_id) if hasattr(component, "master_id") else "" + parent_id = extract_id(component.parent_id) if hasattr(component, "parent_id") else "" + + return { + "id": component_id, + "name": component_name, + "display_name": component_display_name, + "part_occurrence": part_occurrence, + "placement": placement, + "part_master": part_master, + "master_id": master_id, + "parent_id": parent_id, + } + + +def serialize_part(part: GRPCPartEntity) -> dict: + """Serialize a GRPCPartEntity object into a dictionary. + + Parameters + ---------- + part : GRPCPartEntity + The gRPC PartEntity object to serialize. + Returns + ------- + dict + A dictionary representation of the PartEntity object without gRPC dependencies. + """ return { - "success": getattr(response.command_response, "success", False), - "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.tracked_changes, "created_bodies", []) - ], - "modified_bodies": [ - serialize_body(body) - for body in getattr(response.tracked_changes, "modified_bodies", []) - ], - "deleted_bodies": [ - serialize_entity_identifier(entity) - for entity in getattr(response.tracked_changes, "deleted_bodies", []) - ], - "created_faces": [ - serialize_entity_identifier(entity) - for entity in getattr(response.tracked_changes, "created_face_ids", []) - ], - "modified_faces": [ - serialize_entity_identifier(entity) - for entity in getattr(response.tracked_changes, "modified_face_ids", []) - ], - "deleted_faces": [ - serialize_entity_identifier(entity) - for entity in getattr(response.tracked_changes, "deleted_face_ids", []) - ], - "created_edges": [ - serialize_entity_identifier(entity) - for entity in getattr(response.tracked_changes, "created_edge_ids", []) - ], - "modified_edges": [ - serialize_entity_identifier(entity) - for entity in getattr(response.tracked_changes, "modified_edge_ids", []) - ], - "deleted_edges": [ - serialize_entity_identifier(entity) - for entity in getattr(response.tracked_changes, "deleted_edge_ids", []) - ], + "id": part.id.id, + } + + +def serialize_entity_identifier(entity: EntityIdentifier) -> dict: + """Serialize an EntityIdentifier object into a dictionary. + + Parameters + ---------- + entity : EntityIdentifier + The gRPC EntityIdentifier object to serialize. + + Returns + ------- + dict + A dictionary representation of the EntityIdentifier object without gRPC dependencies. + """ + return { + "id": entity.id, + } + + +def serialize_tracked_command_response(response: GRPCTrackedCommandResponse) -> dict: + """Serialize a TrackedCommandResponse object into a dictionary. + + Parameters + ---------- + response : GRPCTrackedCommandResponse + The gRPC TrackedCommandResponse object to serialize. + + Returns + ------- + dict + A dictionary representation of the TrackedCommandResponse object. + """ + # Extract command response success status + success = getattr(response.command_response, "success", False) + + # Extract tracked changes + tracked_changes = response.tracked_changes + + # Extract and serialize parts + created_parts = [ + serialize_part(part) + for part in getattr(tracked_changes, "created_parts", []) + ] + modified_parts = [ + serialize_part(part) + for part in getattr(tracked_changes, "modified_parts", []) + ] + deleted_parts = [ + serialize_entity_identifier(entity) + for entity in getattr(tracked_changes, "deleted_parts", []) + ] + + # Extract and serialize components + created_components = [ + serialize_component(component) + for component in getattr(tracked_changes, "created_components", []) + ] + modified_components = [ + serialize_component(component) + for component in getattr(tracked_changes, "modified_components", []) + ] + deleted_components = [ + serialize_entity_identifier(entity) + for entity in getattr(tracked_changes, "deleted_components", []) + ] + + # Extract and serialize bodies + created_bodies = [ + serialize_body(body) + for body in getattr(tracked_changes, "created_bodies", []) + ] + modified_bodies = [ + serialize_body(body) + for body in getattr(tracked_changes, "modified_bodies", []) + ] + deleted_bodies = [ + serialize_entity_identifier(entity) + for entity in getattr(tracked_changes, "deleted_bodies", []) + ] + + return { + "success": success, + "created_parts": created_parts, + "modified_parts": modified_parts, + "deleted_parts": deleted_parts, + "created_components": created_components, + "modified_components": modified_components, + "deleted_components": deleted_components, + "created_bodies": created_bodies, + "modified_bodies": modified_bodies, + "deleted_bodies": deleted_bodies, } From 48687e812a20bb68ce3fb4602b03eb7f314a9aa1 Mon Sep 17 00:00:00 2001 From: Umut Soysal Date: Sat, 13 Dec 2025 16:31:22 -0600 Subject: [PATCH 45/61] type hints, clean ups --- src/ansys/geometry/core/designer/design.py | 103 +++++++++++++-------- 1 file changed, 62 insertions(+), 41 deletions(-) diff --git a/src/ansys/geometry/core/designer/design.py b/src/ansys/geometry/core/designer/design.py index 1d357e0e8e..bfa06d9538 100644 --- a/src/ansys/geometry/core/designer/design.py +++ b/src/ansys/geometry/core/designer/design.py @@ -1408,6 +1408,8 @@ 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. + This method is alternative to update_design_inplace method. + Parameters ---------- tracker_response : dict @@ -1430,6 +1432,7 @@ def _update_from_tracker(self, tracker_response: dict): # Handle created parts for part_info in tracker_response.get("created_parts", []): part_id = part_info["id"] + # fall back to string if id is not an object with id attribute. 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}'" @@ -1525,8 +1528,8 @@ def _update_from_tracker(self, tracker_response: dict): # # 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_to_design( + component_info, self.components, created_parts_dict, created_master_components_dict ) # Handle modified components @@ -1573,7 +1576,7 @@ def _update_from_tracker(self, tracker_response: dict): ) continue - new_body = self._find_and_add_body( + new_body = self._find_and_add_body_to_design( body_info, self.components, created_parts_dict, created_components_dict ) if not new_body: @@ -1837,7 +1840,7 @@ def _handle_created_components(self, created_components, created_parts=None): continue # Try to add the component to the appropriate parent - new_component = self._find_and_add_component( + new_component = self._find_and_add_component_to_design( component_info, self.components, created_parts ) if new_component: @@ -1849,9 +1852,9 @@ def _handle_created_components(self, created_components, created_parts=None): return created_components_dict - def _handle_modified_components(self, modified_components): + def _handle_modified_components(self, serialized_modified_components): """Handle modification of existing components from tracker response.""" - for component_info in modified_components: + for component_info in serialized_modified_components: component_id = component_info["id"] component_name = component_info.get("name", f"Component_{component_id}") self._grpc_client.log.debug( @@ -1913,7 +1916,7 @@ def _handle_created_bodies(self, created_bodies, created_parts=None, created_com ) continue - new_body = self._find_and_add_body( + new_body = self._find_and_add_body_to_design( body_info, self.components, created_parts, created_components ) if not new_body: @@ -2046,9 +2049,13 @@ def _find_and_remove_part(self, part_info): return False - def _find_and_add_component( - self, component_info, parent_components, created_parts=None, created_master_components=None - ): + def _find_and_add_component_to_design( + self, + component_info: dict, + parent_components: list["Component"], + created_parts: dict[str, Part] | None = None, + created_master_components: dict[str, MasterComponent] | None = None, + ) -> "Component | None": """Recursively find the appropriate parent and add a new component to it. Parameters @@ -2056,7 +2063,7 @@ def _find_and_add_component( component_info : dict Information about the component to create. parent_components : list - List of parent components to search. + List of potential parent components to search. created_parts : dict, optional Dictionary of created parts from previous step. created_master_components : dict, optional @@ -2067,7 +2074,11 @@ def _find_and_add_component( Component or None The newly created component if successful, None otherwise. """ - parent_id = component_info.get("parent_id") + # Early return if there are no components to search through + if not parent_components: + return None + + new_component_parent_id = component_info.get("parent_id") master_id = component_info.get("master_id") # Find the master component for this component @@ -2076,7 +2087,7 @@ def _find_and_add_component( master_component = created_master_components.get(master_id) # Check if this should be added to the root design - if parent_id == self.id: + if new_component_parent_id == self.id: # Create the Component object with master_component new_component = Component( parent_component=None, @@ -2093,24 +2104,27 @@ def _find_and_add_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 + if component.id == new_component_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( + # Recursively search in child components + result = self._find_and_add_component_to_design( component_info, component.components, created_parts, created_master_components ) + if result: + return result return None @@ -2162,35 +2176,42 @@ 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_to_design( + self, + tracked_body_info: dict, + components: list["Component"], + created_parts: dict[str, Part] | None = None, + created_components: dict[str, "Component"] | None = None, + ) -> MasterBody | 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 + components : list[Component] List of components to search. - created_parts : dict, optional + created_parts : dict[str, Part], optional Dictionary of created parts from previous step. - created_components : dict, optional + created_components : dict[str, Component], optional Dictionary of created components from previous step. Returns ------- - MasterBody or None + MasterBody | None The newly created body if successful, None otherwise. """ + if not components: + return None + for component in components: parent_id_for_body = component._master_component.part.id - if parent_id_for_body == body_info.get("parent_id"): + if parent_id_for_body == tracked_body_info.get("parent_id"): new_body = MasterBody( - body_info["id"], - body_info["name"], + tracked_body_info["id"], + tracked_body_info["name"], self._grpc_client, - is_surface=body_info.get("is_surface", False), + is_surface=tracked_body_info.get("is_surface", False), ) component._master_component.part.bodies.append(new_body) component._clear_cached_bodies() @@ -2200,8 +2221,8 @@ def _find_and_add_body( ) return new_body - result = self._find_and_add_body( - body_info, component.components, created_parts, created_components + result = self._find_and_add_body_to_design( + tracked_body_info, component.components, created_parts, created_components ) if result: return result From 18a859e8256cd690e1ae4c28d5a1f76d0e67d2c9 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 13 Dec 2025 22:31:42 +0000 Subject: [PATCH 46/61] chore: auto fixes from pre-commit hooks --- .../core/_grpc/_services/v1/conversions.py | 67 ++++++++++--------- .../core/_grpc/_services/v1/prepare_tools.py | 6 +- src/ansys/geometry/core/designer/design.py | 4 +- 3 files changed, 41 insertions(+), 36 deletions(-) diff --git a/src/ansys/geometry/core/_grpc/_services/v1/conversions.py b/src/ansys/geometry/core/_grpc/_services/v1/conversions.py index da625e994c..102e05a39a 100644 --- a/src/ansys/geometry/core/_grpc/_services/v1/conversions.py +++ b/src/ansys/geometry/core/_grpc/_services/v1/conversions.py @@ -1660,7 +1660,7 @@ def from_enclosure_options_to_grpc_enclosure_options( def serialize_body(body: GRPCBodyEntity) -> dict: """Serialize a GRPCBodyEntity object into a dictionary. - It is not directly converted to a pygeometry object because we'll assign it is place and + It is not directly converted to a pygeometry object because we'll assign it is place and construct the object while updating the design object by the tracker output. Parameters @@ -1677,23 +1677,31 @@ def serialize_body(body: GRPCBodyEntity) -> dict: body_id = body.id.id body_name = body.name body_can_suppress = body.can_suppress - body_master_id = body.master_id.id.id if hasattr(body.master_id, "id") and hasattr(body.master_id.id, "id") else (body.master_id.id if hasattr(body.master_id, "id") else "") - body_parent_id = body.parent_id.id.id if hasattr(body.parent_id, "id") and hasattr(body.parent_id.id, "id") else (body.parent_id.id if hasattr(body.parent_id, "id") else "") + body_master_id = ( + body.master_id.id.id + if hasattr(body.master_id, "id") and hasattr(body.master_id.id, "id") + else (body.master_id.id if hasattr(body.master_id, "id") else "") + ) + body_parent_id = ( + body.parent_id.id.id + if hasattr(body.parent_id, "id") and hasattr(body.parent_id.id, "id") + else (body.parent_id.id if hasattr(body.parent_id, "id") else "") + ) body_is_surface = body.is_surface - + # Extract transform_to_master matrix transform_m00 = body.transform_to_master.m00 transform_m11 = body.transform_to_master.m11 transform_m22 = body.transform_to_master.m22 transform_m33 = body.transform_to_master.m33 - + transform_to_master = { "m00": transform_m00, "m11": transform_m11, "m22": transform_m22, "m33": transform_m33, } - + return { "id": body_id, "name": body_name, @@ -1718,6 +1726,7 @@ def serialize_component(component: GRPCComponentEntity) -> dict: dict A dictionary representation of the ComponentEntity object without gRPC dependencies. """ + def extract_id(obj): if hasattr(obj, "id"): if hasattr(obj.id, "id"): @@ -1729,7 +1738,7 @@ def extract_id(obj): component_id = component.id.id component_name = component.name component_display_name = component.display_name - + # Extract part_occurrence part_occurrence = None if hasattr(component, "part_occurrence"): @@ -1739,7 +1748,7 @@ def extract_id(obj): "id": part_occurrence_id, "name": part_occurrence_name, } - + # Extract placement matrix placement_m00 = 1.0 placement_m11 = 1.0 @@ -1750,14 +1759,14 @@ def extract_id(obj): placement_m11 = component.placement.m11 placement_m22 = component.placement.m22 placement_m33 = component.placement.m33 - + placement = { "m00": placement_m00, "m11": placement_m11, "m22": placement_m22, "m33": placement_m33, } - + # Extract part_master part_master = None if hasattr(component, "part_master"): @@ -1767,11 +1776,11 @@ def extract_id(obj): "id": part_master_id, "name": part_master_name, } - + # Extract master_id and parent_id master_id = extract_id(component.master_id) if hasattr(component, "master_id") else "" parent_id = extract_id(component.parent_id) if hasattr(component, "parent_id") else "" - + return { "id": component_id, "name": component_name, @@ -1835,52 +1844,46 @@ def serialize_tracked_command_response(response: GRPCTrackedCommandResponse) -> """ # Extract command response success status success = getattr(response.command_response, "success", False) - + # Extract tracked changes tracked_changes = response.tracked_changes - + # Extract and serialize parts - created_parts = [ - serialize_part(part) - for part in getattr(tracked_changes, "created_parts", []) - ] + created_parts = [serialize_part(part) for part in getattr(tracked_changes, "created_parts", [])] modified_parts = [ - serialize_part(part) - for part in getattr(tracked_changes, "modified_parts", []) + serialize_part(part) for part in getattr(tracked_changes, "modified_parts", []) ] deleted_parts = [ - serialize_entity_identifier(entity) + serialize_entity_identifier(entity) for entity in getattr(tracked_changes, "deleted_parts", []) ] - + # Extract and serialize components created_components = [ - serialize_component(component) + serialize_component(component) for component in getattr(tracked_changes, "created_components", []) ] modified_components = [ - serialize_component(component) + serialize_component(component) for component in getattr(tracked_changes, "modified_components", []) ] deleted_components = [ - serialize_entity_identifier(entity) + serialize_entity_identifier(entity) for entity in getattr(tracked_changes, "deleted_components", []) ] - + # Extract and serialize bodies created_bodies = [ - serialize_body(body) - for body in getattr(tracked_changes, "created_bodies", []) + serialize_body(body) for body in getattr(tracked_changes, "created_bodies", []) ] modified_bodies = [ - serialize_body(body) - for body in getattr(tracked_changes, "modified_bodies", []) + serialize_body(body) for body in getattr(tracked_changes, "modified_bodies", []) ] deleted_bodies = [ - serialize_entity_identifier(entity) + serialize_entity_identifier(entity) for entity in getattr(tracked_changes, "deleted_bodies", []) ] - + return { "success": success, "created_parts": created_parts, diff --git a/src/ansys/geometry/core/_grpc/_services/v1/prepare_tools.py b/src/ansys/geometry/core/_grpc/_services/v1/prepare_tools.py index 39a8fdb22f..f453a62e5f 100644 --- a/src/ansys/geometry/core/_grpc/_services/v1/prepare_tools.py +++ b/src/ansys/geometry/core/_grpc/_services/v1/prepare_tools.py @@ -66,9 +66,11 @@ def extract_volume_from_faces(self, **kwargs) -> dict: # noqa: D102 # Call the gRPC service response = self.stub.ExtractVolumeFromFaces(request) - + # Convert grpc tracked command response to serialized format. - serialized_tracker_response = serialize_tracked_command_response(response=response.tracked_command_response) + serialized_tracker_response = serialize_tracked_command_response( + response=response.tracked_command_response + ) # Return the response - formatted as a dictionary return { diff --git a/src/ansys/geometry/core/designer/design.py b/src/ansys/geometry/core/designer/design.py index bfa06d9538..b3c69fb673 100644 --- a/src/ansys/geometry/core/designer/design.py +++ b/src/ansys/geometry/core/designer/design.py @@ -2077,7 +2077,7 @@ def _find_and_add_component_to_design( # Early return if there are no components to search through if not parent_components: return None - + new_component_parent_id = component_info.get("parent_id") master_id = component_info.get("master_id") @@ -2203,7 +2203,7 @@ def _find_and_add_body_to_design( """ if not components: return None - + for component in components: parent_id_for_body = component._master_component.part.id if parent_id_for_body == tracked_body_info.get("parent_id"): From ab4335f0d50e0e4661cb0d630bea11f6ca8535e0 Mon Sep 17 00:00:00 2001 From: Umut Soysal Date: Sat, 13 Dec 2025 17:01:53 -0600 Subject: [PATCH 47/61] clean up --- src/ansys/geometry/core/designer/design.py | 361 +-------------------- 1 file changed, 1 insertion(+), 360 deletions(-) diff --git a/src/ansys/geometry/core/designer/design.py b/src/ansys/geometry/core/designer/design.py index bfa06d9538..97acbcbbb6 100644 --- a/src/ansys/geometry/core/designer/design.py +++ b/src/ansys/geometry/core/designer/design.py @@ -1512,22 +1512,7 @@ def _update_from_tracker(self, tracker_response: dict): ) 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") + # Find and assign parent component self._find_and_add_component_to_design( component_info, self.components, created_parts_dict, created_master_components_dict ) @@ -1638,350 +1623,6 @@ def _update_from_tracker(self, tracker_response: dict): 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. - - 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. - - 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, - ): - """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. - 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 - ) - 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, - ): - """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. - 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: - 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})") - - 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: - 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, 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_to_design( - 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, serialized_modified_components): - """Handle modification of existing components from tracker response.""" - for component_info in serialized_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, 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"] - 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_to_design( - 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.""" - for body_info in 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 - - 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}") - 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 - # ================== HELPER METHODS ================== # # Processing order for tracker updates: From e18019c0313f9cbc0b18cd75577999dfb80a5f79 Mon Sep 17 00:00:00 2001 From: Umut Soysal Date: Mon, 15 Dec 2025 14:53:49 -0600 Subject: [PATCH 48/61] rename --- src/ansys/geometry/core/designer/design.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/ansys/geometry/core/designer/design.py b/src/ansys/geometry/core/designer/design.py index e8bf9d9430..4312b171d5 100644 --- a/src/ansys/geometry/core/designer/design.py +++ b/src/ansys/geometry/core/designer/design.py @@ -1547,10 +1547,10 @@ def _update_from_tracker(self, tracker_response: dict): # ================== 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) + for created_body_info in tracker_response.get("created_bodies", []): + body_id = created_body_info["id"] + body_name = created_body_info["name"] + is_surface = created_body_info.get("is_surface", False) self._grpc_client.log.debug( f"Processing created body: ID={body_id}, Name='{body_name}'" ) @@ -1562,7 +1562,7 @@ def _update_from_tracker(self, tracker_response: dict): continue new_body = self._find_and_add_body_to_design( - body_info, self.components, created_parts_dict, created_components_dict + created_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) @@ -1909,3 +1909,4 @@ def _find_and_remove_body(self, body_info, component): return True return False + From 97b1dbdde2b3b102eae1829da4006e0c1ab4dc3b Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 15 Dec 2025 20:54:05 +0000 Subject: [PATCH 49/61] chore: auto fixes from pre-commit hooks --- src/ansys/geometry/core/designer/design.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ansys/geometry/core/designer/design.py b/src/ansys/geometry/core/designer/design.py index 4312b171d5..1c4a377e0f 100644 --- a/src/ansys/geometry/core/designer/design.py +++ b/src/ansys/geometry/core/designer/design.py @@ -1909,4 +1909,3 @@ def _find_and_remove_body(self, body_info, component): return True return False - From b462cf22aabcb256701c6757be0ea09c730ea0d5 Mon Sep 17 00:00:00 2001 From: Umut Soysal Date: Mon, 15 Dec 2025 16:13:15 -0600 Subject: [PATCH 50/61] test_update --- src/ansys/geometry/core/designer/design.py | 8 +++++--- .../files/intersect-with-2-components 2.scdocx | Bin 0 -> 50972 bytes 2 files changed, 5 insertions(+), 3 deletions(-) create mode 100644 tests/integration/files/intersect-with-2-components 2.scdocx diff --git a/src/ansys/geometry/core/designer/design.py b/src/ansys/geometry/core/designer/design.py index 1c4a377e0f..9787b4caf5 100644 --- a/src/ansys/geometry/core/designer/design.py +++ b/src/ansys/geometry/core/designer/design.py @@ -1848,16 +1848,18 @@ def _find_and_add_body_to_design( for component in components: parent_id_for_body = component._master_component.part.id if parent_id_for_body == tracked_body_info.get("parent_id"): - new_body = MasterBody( + new_master_body = MasterBody( tracked_body_info["id"], tracked_body_info["name"], self._grpc_client, is_surface=tracked_body_info.get("is_surface", False), ) - component._master_component.part.bodies.append(new_body) + + component._master_component.part.bodies.append(new_master_body) + component._clear_cached_bodies() self._grpc_client.log.debug( - f"Added new body '{new_body.name}' (ID: {new_body.id}) " + f"Added new body '{new_master_body.name}' (ID: {new_master_body.id}) " f"to component '{component.name}' (ID: {component.id})" ) return new_body diff --git a/tests/integration/files/intersect-with-2-components 2.scdocx b/tests/integration/files/intersect-with-2-components 2.scdocx new file mode 100644 index 0000000000000000000000000000000000000000..906c7af91eb0873e7117d2e9bb1f22c6c45d272a GIT binary patch literal 50972 zcmeEvbzD_X*YBnTr3C>|LKF)m1*ANbgor4h5&{y3l9G@R0R=G;0|gbtzz*y{!oWhx zL@X2o3jqrYxohon#^3h9`#jHkKlk3x{o~zyX3jqAyVlH}nKf%>ప}*Gir6@{- zI+tbVd2C_-ce~)*06vORriN0k(S8AJ>u|r&2yNGZ5O#!LtTs^Q`USJK5@Q2ZcPLP! zx1n_JYs2btiN!jyOA=(7x4#iMkv?yj>dJX8^74%uKHHA6?)zxwXfLutpX|rBXcLV;@k2x;>$aF1>V%B_KBq_^&I3NJiO`BHM;e`0D? zz$&%K%uj8199)vjm9#&}2FG;uoAy`XS&kcy)<8+vPAIiu_b<@URZBi`tbyIB*wWDNW6lWA?yp;Bo;x&Z)9tcf z%AY>IezkkX{7EXs26KM$L%7=&-z9^z3cZX1|^7E*>m)Om~5XmZFp3hext&ipxA~ zPuGnZlQ^YkPJH{5FOdUc7gCZToA2LStPyDU&R~}8s|R8~&YA|!FsP7xm-xQx=3vJS zU-XA6o^(^4!q=pNwK9NiQB;YTVLQI42~ZRUQK%K)7c zVnd@M&DC_ZbktPYkpWSGp^?GnYVmPF<0h!FCYrgh!~NobBQ_*7I#v}#B4f?fLgM10 zO|-RRIZCG$70r$WBq%B-!Y?jXD=H?K=%Kcrj*g)=Jk398sQsO$s#{Vt`%j7q2?<)U zsMP_e)mi~j5q}G7hepP+V

(7p3&=_kZ6GT_ZX|9iHV~^p7(!Nq+*E8VHW3>^w1aE}*%YEZWK+m( zAhjhpd>>H} z5$Lz)dqeCCu@A()4|}_Ru8~0R{t($ZiCr>J|5#{Y*!y5_guN5?R@i$TVZ9KpmB?jl z1^6{V*tcNc!LJ2c&3X@htpu($NP>2aYY+G}J=7oInjU`L1g=vkVr?GVI<|3a-`KMG zwG+6;;SsbXT=T%My`i>>Yj5~<6u3Sjm$gT1huHqG?P06K)`qPNTNt)2Y*W~lupMCw z!j^+A23rcY6Ko&YF0egdYrs~3EdbAbJn!+0$1@$za5}s3+{SYn&t*J^@%&YU!6Gzv z=tubVnK*jGf2fA5FqPR_04*3j@#`s@{k3)g*Y6m>m?*s;@N374*%}u`s0FVQ zxCRINeOwd6ulIV+dU}2>mz1rcQH7ovdum*JgMBad#r*oK-y-a7vG-MiJ`np*>{GFi zRfC=jdrIs%B_d?iSpSCo9rl0NYhf>jy_&8Vx-OBlH~#%a(02GW91~!z2Cd=fEL+2Y z-*F8GdGT@0FB7rExe-ol1%y1|w3b826HaRZggoK2)JmKW^7V?CXmt4pb zPF`{$PdItmg*@TpWf$^dEZ!s%JJkSCnx;)OinG}kWV38%SsAx}7Ytb{z_Bto8WTB{=D38%F#LY{D1DL-38%SAAx}8Xg$jAXX)aXA z6HarjLY{D%YZvl_o5|+zg*@Rj*DvG=r@3Y!PdLqG3wgq6E?dYGPIKi#o^bL~33@PF`psPdIt4g*@T3)=J0| zPHU-zJmIt!OUM&WYrTX#;j~sv$Pz0B zPHVk{JmIvKOvn>XYsrK>;j~sw$P-R$orOH%wANb46HaSYg*@T37F@^^j@McIoM$0V zIITq&@`Te`QXx+`jsx&>poKi)I1a!ytU{h}9H-#tKnr=o$?GZP38%SkAx}8Xl?!>o zX|7$!6HZ=LAx}7YRfYUtxXdFfLE#wKOwcbLWa9V3D%tD@UTFWft38%Hv zLY{CuPw{iag*@SSp5y0~3wgq^o!}ZtAx}81r4;gn(^_;PPdKeb7xIMDT6ZB&IDM9i zkSClzi$%y2PM_r>~Is5ht&`kSCnH{z9H`^70FL!pTc1O3^$BO++;9XGcUDC?RDi6j7!O ziF8wR%wvY*n2T`Cv!2@KdCU}%;g}9O;+TowdadhzYqy?r&gb{}{`vi$`*B@u>%HFl ze%kH(x~}`W*Ly9+B1T+x#X?40cEvhITz18(MO=2p(nVZ$#Ue&rcEyrLTz19sMO=2p zLPlJ6#j-|RcE!R*Tz16*MqGB~>WaAR%CQx3*`<|;xa`W+6>-_|BL`T-WtUbk;<8JN z7;)L9WsJD&(n3aDc4;vqF1xgr5tm(B&4|m+J_Z`uh|A7C2^!pp%g#PKdD0^;JNqc) zVUM`%?319ujkxUWGm!^A;tgin#3D zCy_%V;<9reMxCFC%g%imIXWUPJNJ3y0ExKl?DLbOD&n&9oPiu!5tp6k2-IPU_#gH> z2TH`11BLr&>M%uIcJ8Ceu@Z6FxlbpDOT=ZD)*|AvvrmPFBjU14>k)C;r4@;|?9!@4 zTy|+uA}+hMED@JoT8oIwE-gpIWtWyC;<8IC5^>qt2Pelu#AW9>Dmfe?F1xg>5tp6k zwB(40xa`upMqGAjT_Y|#&)KPC5pmh2g^jrEJm;s5MZ{(2IX^iRA}%{|2-LBNxa`u( zMqGAja3e0eG`ta)U0TtI%Px&-#ATNjHR7^MLmP3~rG<^S?9$jqTz2Iuh`8*^g%EMs zl?x%_vP&x*aoMF&j=1d7B1c?yX`Lf3J8>rD@QAqV#JP|oB;v9wmq^5Am)1GrvP-KQ zaoMF2j=1d70!LhSX^kT;I~opRzATi>Vx29V%=$I0QKtbN2T{+|Xjsc)Sy5x4=UCff zK8Z3;H5!NtZ0K>W>*ZO3sl|Yn13d&Wuk|eAwZ63tUAyq_>oGL2n86mV)o5XHeMA#d zi-8&pXgC^L>*$fR9qnu=Hi(hk%wuI^-36*FkS%-*>$_S{wFN3IP;a4Jx6#w$5dV`_ z19b*j#ru1dZGVecT&2%w?Xk8*@D8xwl|0PUR@fwei-sx4wg`H;4(I=&`}8*Inr3N)BQ`C)vNy5xJ5hWxuB8!jYB@n=I-_ z+q+PA0WHWBd#|l6inyn>W3BIL{SmI`Am{pV_8upgo`XsZDYG{97p7T+Cy~a~Um&*g zWNW8bf3im$ud2Jy%QYOtoX)a;?b#NCO!bA7y_=c~f3hfKvb3LR??T-LB1O-#_afIf z>WzP;+eq~mx>;=U9FNeRW8XHE8N?}H?(xd2*IKy9G!}pHyzhT84pkP2WWL5Dny;~p z@7Gw|>9zKAoxKaS7Kli_-rkE!3v;dAX#HI4Pp~%M<67t2XK{*XjviCx@ zLH&j2Ty^oBG0|?2!~S`D4{9$wYwacLpSAvJ(_5h1K(#>}?W;C^q1&isKd;#+c7f?O zF0}TB^%uHw;|+_mC1RWU3rj3wn~e9=UqGwzj6c0j$#7wr?PU%AZMI@T}q z81!{4%9u!W^cxK<>X?Xh^c(A2oN+_@X=IV&iPZLD8f(Z-^b(QFW{OdNYVi(V$0y@S0Yx{YS`)6w301B+~L zVy%Vw_u9+uZtA)|YEIUa27d9W_ta%Ec9EIG7Pr9Sh>% z$#-ARW*+p38~B{YjJB$N1$pz=@%f5*iaCneiP{y+L(D(u1*O|yPFbbbXz4RYs#QU5 z|DuiQ=rmG|9nA&I1gUNXbpZzZsE%f9Z#yfaJ7YUzI%7FwIAb?sHsf{DZlH>z{=lX- zo}%4Qtb7`K8F^9AG1@ZDGQKjRrg|3WH@5L{lkt*qk};Ce5zPkUAY&k-U#e$89fga1 zjALwLOk+G_Bx3|)^h)vc=rht-#YmOvTM%1+laEJ?LySL+J&Zc3#>K-v!aU+J_Nj&o z;|L=NBL|}f;{{^{qXeUaMh3H-)g?ot27*oZPmtFu9mI1SgEmcThsL$O?*|BG)WCigOn7t z^?j|?(B}438v^}NC!2%OZm?=%71hmV5$Z}L{YGooZ%BJYEefhq?%DfZN)Ou z*DlmqL1)y&&cJGd)di~!Rv+uynb2>fT22T1${^KgI>LT~=|01!+DpB>&PJ-SG{a}? zR6i-z=0IC{uKkwORzbIsY9C$dvnslwt8CUxwTtH2oJoD6TU^J{&t^7ex>R@QUauj- z+{Wz0Y$cr#ia=(d7wl6_`i-SNlQ6Sf=(EVG+9~LMQk|ZUebh&>*TsGVG#k`TVRUA+ zX7rwJ?})C4u^0UYdY@JmO?<>;1Z6~Jgk4tA%Ev~=%A|a2<|7~@;!^vYH?gq`9Y?E5 zhOUJ>Fn%Qs$7A-oT*I+#Wvau$pJT0QFFi4`V*1lA?)=bRI{$mxwKIx!-WlUH;O31l zcy1Qq++#!>o8CX&gRK7k*x)OVQqEtY`Ry2zx(Yc6_;+`VG*zUtm^f2QTM;#xb6cD{Xn&KHhv#Ib2F0sghG z9#^dQ`fTSnD5c!W!tsqbHtl7=zoY(?;(IS%%)tm{;+svWuFjt`yo2+F;~Q~o ziq(Su&B{K-N8aq_{1p#$biQzWBaTgbP4ExcbIam`*YtA!;PX;$Ug7vg9GmvC;Mco& z)8f_h`=|3in{w+4$2a2Gv{wdydFM{W!#>!~`8NLe?=KwRh-1@UE&Rr(wY6P^F&#g{o>IKC0brf0G6Uwe7W zs(o6VRLDv^H>^{T! zGl$Q0zHodaj!nO{+pA)~;+*_P49N&mz zlNOKjFWKnr>L)%+{B8~IaK3PSBaTg4K=_|5@38RgXA^($d3QQrIKC0bCaod-d%8co z@P%dR^Pf8He&-9vH{#f&6@cI3^E!(*tDf$kzdhD=zHodaj!jww_(N}Py6C!X65rN8 z&KHhv#IZ>W0so9whb%hp)oFhI#k)P`eBt;;9GkQj@ULBK%A$*JpX&Tsv!8OlaC{?< zObvcEx+n7mjblu}P}||MJ&9UG#DPgPecQXYV^- zIKC0bCaouYJ4$cfy|42>c=Z$K3&%I&*rYXuy!e?qr8>3ucK)n|pF3YTz7fYJttN7UJ{)bgHT&hK~gH_jK1Z^W@FmK}bdrmaeQjTz+p zZqvSZzHodaj!m)h@W1}FZE5C^fzF>e=SSxY$2a2G6l)H@!SF7nvqlu0e@XV6^M&IZ zacqiZhu`$F9;GWLZtMI>8&~>$xNv+Uj*VDT)%p6R&o-skpWn*)cK_jg;rK=zn_~4j z|G`%kONTtVne(T1uI+r`_(mL?V$tC*Z@gn^*@5ky|Hw~kIbS%w5yz%jclZY!vvcXT zKCPXuFC5>9V^gd>{6p^Btu(o?f%E4zt?zu{_(mL?V)@~>8M#Mkn_25Rzv+^7 zoG%>Th+|W%6Z~=ejVe8|cx~s~{g?BF;~Q~oilu@-Z^Hhiu5115_rHrP8#-S&z7fZ! zST6YIY<6I2lMTLf{$ckua=vhUBaTh6X7JZ|c6@2*Z67#);t!3TFC5>9V^b^?{3mA~ zR(k99r=4H1ys7hr;~Q~oinW5@c+%meR#P5w{=*kHbG~qVBaTh6UhwZa@5s^)EpK0tIKC0brdTuh4L(1nG`a2^=XYJw!ui7SjW{;Nvca$a%W9V?)cJIJ{`=m}D|O%8{}hgI#IZ>u0)MQnT}P}~>f?5+xt*Oa z9N&nGuRPv4-jDaX=kjiU@WIRYf8f}pg@Kd*eg$4PZm`84zdw1v@r^h(X+_}VpO5ox z|HJvh@r^h({(Y1OJpcEp;=MkYwYBqw;~Q~o(!#*WzaP#&wMw(MjRXCgX);% zYU``qw^cxw4L*X;~Q~ojL*sw zosZAXU%cA@=L^R-;@FrUlt((BADlmN;6UdK$2a2Gn4gqKI-j4MfAH*`oG%>Th+|`Z zR37PkessR=pE+MRz7fa9{H#31`TShu-{O)jhd5t2z7fa9`apS%^YwxAtH%#@zHoda zj!jx9&Y7=IoZq$6uFe;ZZ^W@lYXv`FA36WA79*T59N&mzlhzA`NHvyI5ugi;OF-z&R%Vu9^M&IZ zact6R!MB6^SLc85>ICNt$2a2Gqy>YV-yb`F>N69aFC5>9W0O`5etv)M{P7D9b-r+X zBaTg4F!=fYfb(tt+4;ipjW{-G&EV(z6V9J^&k@cSj&H=VDOMhSzCYsphpsr%`NHvy zI5x%N!_W6;oZs)}qn$4t--u(A765*}Kji$yS0C$q;rK=zo3se<^ZhC3uRrZL=L^R- z;@G5xfS>P=Ip6m0oi7~Uh+~r$1Ae|g=ltK#Jkj~W@r^h(X+7ZQ`-9FOKk6js3&%I& z*w~*`os@ik()pKUCp%v_z7fZ!Tu_`p-yd~;(*>FHh2tA>Y|1qSKi{8q{=OTZ;(X!w zMjYD-Wp!5a{bA?Z^9$z-$2a2G*q>INpL~DX`M2IN)A_>jjW{;;$5qEC-ye7WpX$wW zzHodaj*b0!)$z&q=bb;b^J&f(j&H=V@q9pae)8u7&L42Y>CP99Z^W_jd_r}Y^5+xI zzx$`e7mjblvGIIFb(-?$BhI(yKYo7U_(mKX&u3JpDStlW{I{<-!}-GTjW{-*52=n* z{(Q*!%RWnd;rK=z8_%ayXDfd`<@|X~&+_vN$2a2Gcs`~&T>0}c=O1;!+0GY^Z^W_j zd`@+^^5=8Tue&7ih2tA>Y&;)Sov!@(p!4ndm!Draz7fa9^GVfN%%4v>zs~IQoG%>T zh-2gVsOmiC&qtlV^;3y29N&mzJ9pT{RSzhe0%etzNjMjRXQ5vp^a z$45B-xq~lrzHodaj*a*X)oIS-Gn{|l#fdK*--u%)K16kz^Y{?w+w)UDzi@mbj*a*f z)p5?_Q=C6;-z%Ii9N&mzBR)oTvh(;D=YKde@rC0XacsorsE&3XpX2TKunNzPw)O5zL0H{#fckNWod<$amI^Y|&h z|F!4OetzNjM%-;y$MT4SmN-9;&vO2GJ74R3;_aVb1S;K;jF> zH{#fcPaAjTqt4Ic)0{v0vFrT&!tsqbHsa&n-S4l?&*S5q-?1v~BMQei;@F7KYuD=* z=jZWx&L2NK@rC0Xacsl~4tsEp^Yi#X=iBpl|NVvI8*yyJC%!pty7Tk+MCVs;b))lz z;~Q~o#77SQ^hD?9@sZBIpnu{E$2a2Gh|e7L<1pvv@tMw_ecOCLzi@mbj*a-xW7lot z{5(Fiz;|)PW;Z)uIKC0bMto|ie{1LG@u|*V*gf%u;~Q~oiofNYO3{D zAF5xhXPUhfZ{EmWp$tSHh&m8m8v4Md);6>rJw-EDRHWJh8(9T`jXg#kUEszQ)O(`p zQ!8t&txx(uDrmH^A9R9kExsJB9D2d_roo`52Guk=T8w#$X{Xu~m7dUMQ0=L+^{L)d zXNz%1xkIEo+Cl1Wpxo$g@$adA0SZFYgJ=k;{M6HW>OO5_je1Wg342-VZGA86SFiij zx1!)WEUI-BDyaH|x{%6G+gab=dMZB+$ZL4)V6V^d2USzOE+z&P_V2ZmRX0K2cqAf&OI6T*pjIdX`d#$I{wS;?`mT)hxwLp~qUZy7@ zR-c+rR5}`MKPXG6|CH)4pc+Imh;DGKwQ;UD8Rx1&6oUu2UhqKEoh1DUm7l2kMC~Uk zKTWhAJ<6fh4$EsdOtM##U7NCc-KQf>{fA-@-5{#LDY<6l7<)z4Cp0TmemdTDD@h?p zy(g+ZO|ynd5UpT&-KUc)&61^-p8Csc!H z4YM#s=-p zd~2)fJKba&h@^#}!V}e<&<|4KiRw=1aj5V_bth^(QQ?W|PSkk1$J)KQX5v13g<=rh zAgaL!bIr~}_6n^aiot4E3!^|!os$4B<+<6HysiC2T6 zuE(dQ3t6Qf{M__|UzpnHb5{+1;fg_YgH(7zF-UzURD)kzPraCLtf3V|G5D>u?_5Vh zB@h%zXiKQ-^n+;x(I%lv`pNVqsiq7{K~#ch1yKtAZarG1@|sTnTWw%HQ!~|fJrhc% z`leAq&x986-_>+#=xRVTfhYp0=tNy7syb2831uKvotnFv?>}h+TbeEk1z&5^MzuEm zOB?$^7uePmF=(Y$uj#Z&Wk=IWQS%1HR43E_QS%1H)Ml;#L=PooNo52du2EK z>0Y^owJojR!ulT8wz9s5^;=u(Y5msLZ)0s+>$kDKm$ly3_p-i^wZ7K(vA&vyw$gtgtRA7TASYkOEf()vBE?PdL*){nBbxAmi}A8l=n z^`oud$J)Nu?_>Rb*7mo)+LjEpY^{(ql4#7(n$`D}04o4yer9}TdS>`lT1jdJmDj#P zFNt0}=_Q$onSq&knQ@tEnPHh(sd>dr$_&cP$&ATNS+mX+I<(>|sZqlUjT#zA^t$LD zQM00deBDR5RI>^-?YlmLtmTFUR#dD=|g>z*~+sGBnmCF^IYEq$Rqy`ZhHtJAy_bZPpjw_8Tj4O*PiYtjL zh%1LHhAV|Dge!w9f-8Y5fNwtEc)sa;!}(_Ojpm!2zQKH-`F`_#=ljp~!1clP!u7-T z#P!AX#`VYb$o0wf%Js|j%=OLn&h^iD!1%y;!T3QG$GTrrj%~otS`{}vi?ASSzcd@^$hD9);p|!SP!Avr!E)kC)QJFja9P?4KnL9)@!WasPV=6 zj`be8W!8genprQRAw}zqwv_cI>rd9BtWU?<59?Rfv#f7f@1m(juS&f!bl0q(V_h=V zZWL7S?{y{=YD|u1MU~Nf8c(C`wQ+jxc}gOg!>cjSGa%SenuKx?ti!+ z;{J&HCGMZNpW^*s^=gwn~qSJ&I8a73e-uK%p4$Vxr7Jg1|%WY+u``~8^0 zx-)+6&Ht=DbVlj7;-%cvW&>;2bxoyk?mr`rP4Ca^`2N`7Z*FW~A6dHge%DOe=YNLV zgyS1=Y)8DiM`5q$Zg<~ShYTuxQ@d4(@9$R~|Liv5_(mMtb#q4+ULLi;ee2&;ReJ9A z&7I$C&`oX=j&H=VoqXcR!s2(U-S>{}h0^fjyEy;SfoUI6IKC0bcJ(hK3iX>lXSLaVADoZqbU$f8TXcg-K;kMo7&8*yxd>+D>3ukA|bPuupY zMMq6_%}w^_ISrjJ9N&mzJ9EOoLe*jE@4DTLQx4ExPe|*W6_T?rY~Z;rK=z z+gVlH7q&ZT4fkF7<9iFgILW`!*hTY4uCrx@Il=@{O(BCLG_0W81rSRbg23#Q*NvCWY-@OZ>aH z>EnFi_(mLCuM-M|ZyF?i#~%(T>~=-s_dmFw^M&IZacr0G)wgi(4(ac|f3r2KCcopF zwQSB0J2+oBz7fZ^@|E6&X2taHw|ixus?tK&TxR=r80>uE_(mMt)_r>ymb_Kzz5^Z_ zR(1J9u6eWZXQ>~ox7zlCOdI$t=x5yv)eLC?Zz zU;X0zYlhFOx_i*k&R?t1KF$}8Z^W@JY1y-|_MSgGzwt(QRGl@>HETBh?dN>q_(mMt zb?vq)^j!K+=kM3%)vBj^P4x4B((7R73&%I&*j{{d%fjCG{=@kfwD`R0u3HXt{;j`H za=vhUBaW?guPqBzz0+7b_4jY9zItY?^KJfjzHodaj&0@G?u8ep{msu`>+yQUXWQ)M z{PXsn>U`n&MjYFT{kj#FZTN2b{Jz_uc=A@eIe+Bx>CP99Z{vVtd-3Y7g|>fv%lQKa zZd6>l%Mj;(b4-^0{yC0s#Ie=+p>yHOw_kMr%PTf54%*8#|5=NLvz#v+--u&tymIrx zR>wc({C2l*SsXFga}H$FC(m}iaC{?aA_jJt`>mTO}$2a2G zUjAXzLh11PoZsm10mZY%bo9UfkwedMzHodaj%}CDn-=yMber?Y>QuQS9r7cCeCm7{t3kk=I!VFCwIQd z`NHvyIJU>HXjl09_NLBnGxx;e>znp-zOA2~FC5>9V_SD{yTYt%{_gjR=S)7i`2Lk2 z`u**r=icso;rK=z+o)~Y6&|_uN#}PNKBG9`WRKO!E-T&XeBt;;9NV0B#veY<`BksZ zC>}d{LqGqQZ`|#C;rK>e{9ZR~SI#eR|I)Pe1Ap+r%lLob*!ca9dS|8YFXX?Uz1)IKC0b#^>`wyMvsce?HE)`w!;}$2a2G`1hgi8*AnK-=~V-|IphXcD`_YBaV&l z2X)=x=id+K_q+8`=L^R-;@J3pQr8WB{{3|RoF5){zHodaj*aVsyz=n#>%;k_;xo<{ zj&H=Vaeb0k9)5m(I=?}O=bSGb--u&ld?0T>{Cs?HzTJO0UpT%I$Hw?XT>$v`_~iV? zFTdh^;rK=z8{;E+|KaE3qx0YY^Q+Dmj&H=VF+Ni_3w}O67wu(MX!eHlh2tA>Y|IbT z<$|Bj56(Yi?Ay*4j&H=VF+cUq_I7?gKRN%dhVM9EIKC0b#{9V6a}%AP&yUWx`&Z`+ z$2a2Gn4g=SbBgox`Puo^D?W6-aC{?oez%`u$7i z3&%I&*jOK4`t`NW&)0{}xBGYJ3&%I&*jS&gb;r%l&)28UAK&m>=L^R-;@DUpx9f7N z^Yitw^E)5+UHbdyIKC0b#`^rX;dePdU!ObwyuE*PzHodaj*a_+SDt^s`T6~U^B=tS zSLX}IH{#g1KN&XRA?N4!C(i%<%azU-j&H=Vaes8whKrn^-yb>O_CJ^hDum-3actb5 zP2K!a=jZol&aZQME$0izH{#g1Km76A#Lw>!o&WikYdBvxz7fa9{V8?n`Caq-Q|Gr> zxTf=k;~Q~o+#gf-9)5m*?EEc1Sj+js@r^h(?$5{Hl=%7mx${R(uH$^+_(mKX`vbRq zocQ_vfb(tt$N9qXjW{;;C#Y+~@1O5aIDgD$^_?#q--u&le}uX=@bmo<=a=RuzHoda zj*a~p>I%Wn_h+1caDKi&>ioAW zn>k-Nz7fa9{_Gbk<~l#$pLKran8X*3Z^W^&KTKT|e*b)b*!i}9=;s%XZ^W^&KTTZ} z`1$^{^Vi&^h4Y2u8*yyxk5d-~e!f5M{K=OjzHodaj*b0!>c+y)_vfA8{P0$Oe&P5= z92?IEdfs%L^YiBe&hK_%8|MqhH{#fMKGEaqDbCNIPdNYWR}x=1z7fa9^O54LeVm^^ zA923zzxw%w;~Q~oJfGR~rT)&(pU*gd)Y~1LFC5>9W8?YIW*b#GKYu>t{GHbA=zQV$ zMjRW@r+VJm-ue0SDPO-%{%sTI3&%I&*myp6@rc^a&!3Mu|I~JyIbS%w5y!^!xgXoC z;r#shob&J9Iq`+#8*ywrAAEVibH4u1pAS0U_MiRy!tsqbHl9z8{_RZX=g%jdf9s)L zoi7~Uh-2gVXyNnO&d;BZI=}s_#21cl#If;w_M&qeIzN9t>-${0mEo&+}_=d?Suc&)?yfKYzFV zdq2N$d?SwSXN&#%Z_N0AW5)ON%FSXG z7h`_^8*}>qcFbwZ0jwA+EcV@=d)VvW^4ElgHnB$R=vVeX-K;5w(@Y#$YFSCO!4;zk zXh)V>RvNtRb_)f>?o7P1{#YU%xnP5rIm z+J#Hjoa4`c%5mk(;~AEoQI+E}UOcNBd-*e{a{R{|&Z_3wQ?1|X6uv#CQ;pwW`SkRR z%(xs`YFUZ*=h>Q`K>^y4rIwY>n|@X`&#v^03DAx#wXD?koinR>Hg(3GYZvw%KF9A- z%5mj0eDCK3S-3}{D|T2@j%Nj)P2v?EI`E7hKKW;M^&-oCJI z;ql`e*5E6jsm+dEF`$NVWT|B( zYQGaZdcl%O1?ld~ap~<918NvYmReS#_B^qpvxgm4knX=6m)>77poVc|sb!@t54>Oj zv7^h|9#&A^fpT2=2Aw5~ zf?`As3KT`R-$$;vDbTVcT_=n3(9fj zD^Lu#VH{a%S&7=l#C|JA9?*^~wX8&KT4L+j!%&QTIZkb1>}O0pwjkYoIWE2Z@^_Cd zNLOEuOHcp(vSSOSTsL2iOE2Gjp92fZOHz(2ABkdw4dcjC%SzOSCbqcW`Ue)27o{9m zK9mPuYf^af>B?%uII`5TlJZ6C87iP1S!!9S;m#*j^Xyg6NCEB0Qp-w5&OEM~XRBA; zvO(dW*^f28pUNk#XS|T@$WqHnU8c>b=GpK|%NrN=IO6LXeC3nYGiJzkWT|DPSx;xx zJe$@tJU~0L)UwjEf7!j7XMcJ|2WUr@T2}h!?Srd%ws*xDoeQJxPc>l5apj}0`mA?h zr-p-Te157|pl4Xd<;YUYO3R<_QO&b2J);7&BTFqS9libL)jZqM9wML}S!(I~%ig2* z2m$TLQp-xJ?Y-^ZQwl?F*`vnir+QTF_B^_<`3C73csXD7rnCoY7)O>`y8hTZ<@(!w z+?*QkuXkeu9(EVZnp+Thw_1hgYdEi0+^ z_ivvcQ+Va&Z)?22>WOKO6tW#zYFSCO%kS%XOrhJ@O>6xAswXz-pecnn_T9Y(u6kj7 zzRHVI{{G5`!oQF5o|NOtcOngoVH{a%>E9Ra3+GqflX6`7PNbnRj3Y}e{r%A1@b^>R zlX6`7PNd;6j3Y}eeg0^F_YU@W);I{7)O>` zR=Rl5Y1PF3DMk;_jx4pT^iJ#9)x`E4*P}r}d1=dW<)c-MpeW^ z3d&1cjw>InVjKl+rCN@$re1LXjsipNHv44uu1GFPcEi19UB({(Bsqzw+ zT$Kd*fx@X_t*D#JOwXCFk!-_!!v?EI`E4}vX>}p~c3s2TBDDQANu6)DKj9aIm zyu#(U@(Hs(Z@D1pmCAADi&hMvVH{a%X?;)Zpkn+0?Z{F~_XorVvc7$O=M-aEjw_$E zViXPI$Wlw|XJQ*!KP#_vIj(%ttk0D4Zr>WL`E2(lenYFX)^o(ESGn{?s@tqPl6*0u&; z^+YDF*Rt@Bf3&E9t6szGFYIz^w;H(WxhO`&-kT#!Ei0|Ot8F#0C5iz7v?EI`E6v`d zc{Q;kqn_Np(BrI{>krk7I_{NXVXN9hYMlRsyk3-ITzGRkvedHDR}D9b0?drMkA|@2`5c+`pW9^amb8T#l<=t)Af;#*w9#?yq>Z%l*+7OFpf^SG`*9uU>ub z(i-bG)uUy9N_88`-=F;{?Cg(Gcfl}@EVZnpdJT#Z2eczgEi1AAMQl0yN4K81c8&Q} z^%&THQr(4ezUnO~2G}r;EVZ;gA$C|XzJPXQsipM|vB9j5{(g3fu`I_`&xQH<`Z{OV zSbwTsi(;6K%aNs)=6_)-T*;Mce@G$WqHns>h=kXFxl$)YAHm*l5;o zkDPj64ZiB}us%Hf@Dzhu&R4yiu8(h9*sxB`@j> zYFTOD_j*+mJGoPXtqY&b*{KF!_2LwR3E7S;wX8I=Pv2@{H`l1_QF!Op9c%DaFHSL_ zknPA)%Sz+V8c%tcCLY|-sp}`?p>HXev2Bo>WL~wj$h7>EH(T7On83* zv31S%+PlzsLaHHKj;o&N-FNRk)@WEio4#sfY`hfo)}yBu5POueATm6j2~n>vedHDNk2Wh zfY?CAzyaEkrIz+biJfDAbl_%d)p&o^3ugbb-6g38ayei1fZ3n?u2za6Eyqo>IFtpAi3y&P9Qbk-lrdtQz!-?=pGhH+%6 zrS%Qkch)z`dtQz!-?=pOhH+%6rS%uud)Cj&TVIYVU%fQshH+%6rS&)3b7{l@?Z{F~ z>vy!}tRI!vz8qIRduiMar=GxtWTBKz8qIRd)B|oTVIYVUp@Pa(wLXy>@Q-M zM%*xtEVZ;hiME{mP1Q9h$5qdO^?%!IC-pOoBTLP`zxQ{k#x4y!pdDFi>HY%kJogt< zZ>h;wy#nrEUV5e`&ixB^Y0Pr*eP3BX?7U*!0qw|A%SzR?Us^zH{E~6|6=v1$SPf`LmReR)J@vMw{R;1Y+r9>_ zdhN`g%4=WFS3Y~@U*&Kq$C-bz^Zv@KUyduEzH+n}#*w9#=3jEV@cS#Te>tvv{>sr} z7)O>``umgHh4q>03Y6ojC!icHhH+%6rO%h#F09{F*Pt9%Jp<)vF^nTiEv?_k?ZWy^ zbrs5S)l*Q80mC@5)YASDxe3a143y*Szma=@{iVnE*}lg2NA;H2zxut&pc=U9DJe&S zy*EdeTG}5Yw*vcXy}PG;8s)g^DJe&TVH{a%Y5$De4(uO&JaE?<=T|)?_Mblfd}Iw= z^^&-MQeCw2_g6hM?$1>BtQ=RpGvxpsIS3%zk)@WN z?~%KJ=X(QBYF>k{dS}Xk0NIW#wevz>9DaTchL^*;C56BjNr-bw|o^)f?gYX_MM5YkWSc z=f?Bfap$zFfvaAda!}ZNb7ZNd=dtuX=3k|DJhwp$4vcYiF+U zuVVb`SYE)jtfh>EKu!2BBKS>RJvLvp`2vMh&Wh26lI z6hS>00>OmeyJXIU}HiaXXa zx*zD7G7qx6xa7+mZ@Dv9<#6BE^SF~k_YixJ!}3h-Dbwa8&*FZ#XV5*|-sN!1-%f_z zBkcXi%SkTxDb|x$w>*b?Z_ndSe&6Hlb3MUw?M}6pGJBH0eOd(>+{xp9w6*D;<#W2d zSAWX_T4im9=i{7V7s;x8?k9LocXIZgYVS46a`Y0Pf4cQEttX@VpFC^tpX}f0PnOG_ zOu}c_zZp44$?JZO_2lv`&*whNbGnm<^n81-i!7r%S>03CQgXRp?AhGO>3+7gOFe_> zrS@KHSccOzJ*zu8JXg={KD6SBioe*u{ZQ)%+jVuNT~lOvCz~=k-mkLk?pJ%R_rpBT zI~j_vwNL$e&!#%patD)Bb)Mx{U6tp(q33!h|MGmh{`y&7;exfBtuI(l)^{?#-{Se+ z|7w|9$vAwQy(97a*W1tS_TJlB7T4`P^E)|D|M&UdipAJnuWXe}A_3`o;1Ce{XH2X9r$soP`##yTID-)-TBOzdyq;U-?W$rM0K6 zr=CTv%AOT#RMxVeH7eKi49{!YpS3I3vR23XwX9#;T3ydgT-S0Gm*;|SVA-F^0KL9v zf?mI}p}lHkeM9RTTO<2BIpBY(ApbiVrJGd#YU57R%5l~O0f64{_Zbi$=W!73* zzpR2h@U1Gz1mD{Fw${i5Pd@ni*4kTN-}-ebH@57{Ev$9)yvrReAM=0C3r|*fvNw0J zjP_kDBXtA&>1uzgeN6j?27)<|dMC_w%yjE{?%q_>g!!zc&sxl4O+((_mOd{rD^Y<2 z6&dpm^9^%N$|Kyt#zArm_w$*9svlJRV3ru{xrxb9On&n;7cdj7p68sr##`F>OrGPh zHu@^ZIk}L>*yurSWO5`Q;bSwSa>{c~9_4L)>}AYltkpQnD4TMglY{wqA4?fS89Ny_ z88NARLDdUJN5)0QL&iakfz-9Q+{QabJdJjYb&PS;uwYzcL`(V5$s3(UFUBs$EXFFv zD2-3lrJyRs>N(QMNu5R<#v4W(Mj1vIMi$1DC+(Zg=%JB=af9)Kaf0!Iae?uGv4By4 zoayg*?rd^slP~>kzpA;KzwkWU@A3b+RN?(?1JyRPp!-*Ud)t8%K7i=4db>s794i^U2% zeO=Ef%{q;B8>=u@WvNyVm3UZ#vG!_FmaCsN)vCPK8``^&r+*7y85Ml>lWO0v{$cIY z$!pc5IyO7ndS=(WM$Hzh<=pOO*E=hZR9|La&(+PkgOvlh_{rgYn5_-S@6FnQ9N(-4 zQk|C*Y}U_g{vY78IkP+SGx@`rzsVs^{(bU_Uu1JE`Nr3@cVkAaYx5&{$y3dg>wOkv zR;0Q>%xS*8u^+adg*N*z3#Hm5Pxy?%3__lLW*GABZ<6O$XYODoU}kvVei-Y?$DV3| zeCne+x%L^a$>Gj;P9FDPe4J*K{@u>TNE~zQ|5CvS%7{v?{RU-u>h5e6fIPyHbXnqaKkF2N|YsGL! zJjQTGTru2=k&n1y0cEn>0cElBf ztv$eqYY$L+h!NKwqV@nI&NDdn02N~%amAP`20r47ftSW9;?g)N#y#SSaaRm|#1%uY z7|w_*hEp-15myYTVn`#d7*fSBMqDwBijjclWB20G%3 zfmRH4#1%s=jY!0!MkM0Wh)9DHacNMb@rbxI9*S{}xMG|YLmhEq*vn(6Bd!=~#c)TQ z81?cP?uaXfTQTAhSB$tcb`h7xP8z+4OQR=^U&N*HlSVD#(x^!T7xAcpi?}p!<#{4C zyH`|XMQQXDBOUpQkyea##1&($810BFMq4q&5myYcVw5A!->*DIIpT^@Rt$B-`TU8Y z)-$q*>lvAzu|-_Z*z}Ar;%H=eMyO{D5!W*YJ;R7NF~B^-&@<$S>lw11K}Q^o49}qT z3_Id_hOHQkh_n772179x5odivjD=z}BCZ$>#ehUyF(8T|i8$*&Vn`H&5^=?#NTU~V zY4r4rH{xiNc*d(|#1YpsVm%{`xSo+Jh9Tm_AQQu&7>I}ygG>yBVkjc67z#bZkGP)U zD@GvV#2}Z)2t-^l0*XP1xMC0#gA#GYpeTkV;=~{m!=f0Nh!cZM42)thBCZ$=#dt)V z807L8kBBSALop-~R}6_X>Jcy3sG}8+IO{j;it&xOVtl1hkGM4I(zr)l8h6ELMO-mj z(y&Hc8dhmkBQA}qG-eT(#!MQvh_gN@*RVxg8a8S0BF_2-4W2aK5tqhW8uN%tV=fJO z#HAru3~s~~gR25myYjV$3717;|uPB>XEJyEIG@mxf6is)$QN zC5=?XSzn=%QVe>;6@#uA_J}KnUC)psu4l-41|M-Wz&wN3Gv-H_DTXWJ#Bi6#a7A1(T#6BkxMIWr?1(lAF{8fIyrBQ6cJG}IB7 zhFTilh)csOjc~-HMmXZq2up(;acPj1<0azC@uD0t5m$~F<(P@Ma?D7B9dT)}rSXn< z)Obf+8gFUHBhLN}+Hz?KBQ6c0G>Q?IMo}8ah)d%rjby~7k(9zuBpd1VlR}Kc{aEQ2aI4DO$#Fe8#ISL}K z90kgO5OL)|P!6VuD+iM_z7gm778+k^gd;AEur$UIm&RB*q#_=3NJU&Zq?Ds7;>uB_ z97PdVjv{H)BQA})H1HAU`D%|c4Sd9tD5b%SIPnc=Fr^`lIPno^ zNTm^sxHN*&AV!?$|K%FQh)aVgjbp^6ag@d;;?meigBo#ZP^ICCxHLS{07YCHAZe5% zE{(D@MiH0BNE+sdM-6ktqlP)+(l9GWM8uUNLOCiTt{fH0K@oA~ppb?+;?ginBOURm zk&d`D($Zi@TpDcUSc$lDtSE;|#FfKEIbb5L95Bkk6LIC>Q4XMpD+iEr&_rB0Xq2NN z;>yvW91js!jtAw4h&b^Nkd!W_2y=*RaUHI@Z>Et<|%>uEznQuZ~>p}l)Ui+kl*UE9&bv>i=N*U`lK4K0o^sX8`xRR>y* zRu)y*(pno=b+oByYp>c_-`4u}7D3m+B3(OJ{9s4>>0}CyP8KV>sVg~JxsIciy~F0l z?_$xjM9_BgNZM`|jo8iN5mQv+diH8d`@Pq*zJW#K_OSC3WlK!sR;J|Gs!Y$()1qFt zv7c@29k;FMWqlXxyIM?RZ;Qh1ZE?6M8n7B76p8e#{!Rb zO$RZ;6YRYXv8daFtsQFp!Pal&@s>n?9%lb;BP~*R4{MXH-@|&;9Y^G`!$;aHG#w~A zj<(NbihU+1JBs#mjA=TKv1sCBUE6WIy+YH0vf~6-au8oU-IN{EtnX{>WRD*`**@!2 zEFKXh$EhCKd8&C+UxA3vna0`CqB{p#JI(rm9)(OM_S0R{af-eAlkrcneuhPVo?%hR zXIKR2nf7zGMJJ!_Y7R6Vn^-%~wF2ka_iTtoEDyDIf$K1ms^g+OX7pl<{6yJ-zT*;W zm*z3em)Wb!P18Y4^PblJZ2g|r?`7%@;+wCqsAi&^SJ!YH;Bn4Gw_a=iHrKg|BWXBL za1e=lgK0NzuxsW<``OSUpf|KDXuf^EH`yml95m6d|6ap!yTwP};StirM&D(T(f3r` zUvY26ebyeZ{yys;wDyqo=ra~rtmz`xWt6P14w{Td|Dz7$EZ1TXW&4bM%DY%B>|krp zSwGmLtDiGH1~Jw|SHIwj40IV!SbN!Z8!uZd_VpGsJJ*#NbM4Q?7Dc;CpK+yYG_JJI z^L6_)-?V7jH$0-2*lZLU<+T>xvk2{F*50>%naA3`Z}Hkh+6%8uywpeqN4Md!;XYs_uoukue=t>Rr8mKjzxw z;~iILykn}EW|hq=n^kUPeRJzKw${S>jjeBDt))kvGA=c@3+>* z`ui)|RNiZm`E4s7wbsu1M=RP@{?l4}>;GBNzVcfeFFI77V6CI|Csh1<-NvsKn^}bO zulA{KUb)i#cAYElvew1=yDGYvK85)Ay7tr6J|Q$5_5s@uGaWM@Ga)l0bK81$%`tNk z4{!CEawTGWpPkSkGWReSF*j{u-pn@WIG8h-L(mAeun`co6|+WP8>fhYXI^M;*T&BF zGss4oVK$zk+hFA1%`AJGg^`?*ow1p*nz8!;8;Kd253wtsu@>FN9oq=(b z5i+wM#y`fxf%e;>$zb$5%if)_3Oxp68S(H#?VX8(XOy}!*If_~zoC7{&{&`fBOd-} zA4eAWh`|VQzx^u_3(t75(B8Q`9v&S9*Zg36M`GgV+PCCw`;nf4E0QaeYm947dJ3*2 zuB-+2{U8>e>m_L^7J3{!SIDpS37|u|(>^V}(QDgJUH=C2&E*@*H?@g>1Nlb2W54Oo z_APqUn44B^YKost>^oG~z7c3xS^u!^VZD=dJhgmvqm>Qo8nmjcW189Ofwc;26jmjy zMo^Qo0%7gJnuE25))`65)6>=$+xlvPl>}=D)(-7#y}*irl>#e-oowV}{%79b%XJYu z`TWkj&V0^X&MeOCjS`a?o4FcIgyv^vXJ%z)V`gFIVCG)t+@#?-+2+^YKBqF5Y93`4 zO?6Dr@yzhKk~xyOQS&0R;srJbG7B>MG3zngG1D=}tey(?c*;1+7~0gwMRYohn~bCP zm{tglPBR}D84no;H3p(pW8~AQ$C#INJ87I-UAKe!ERAH0V@bbLr;?$oybedx0jUlL zdX7qK8(Uk`+Sm3!W34F$$xL=+#q_5Q6ZxSSB>wlbYiAVeyfemYz|9+9@Z5jGxyOh& zHobqk2U-37u_;EX?EP(WtEjN|_uPcS%ijMF9GmuV;5>Nt$;Ainc+2s%pIhL3;rK=z zTX~+ueNI2Q__s5c=I_7I`NHvyI5zE3asJ(JKdHEM`wokE-EW-+o*POyz7fZ!Juo=U zww+d7dCVy1ulZbG=L^R-;@I>I7XC9Wrxn-QX|(h0^K-s%d?SvnJP-G^uO3&d_xfz- zHz=jtgu?NSI5s_l=KOcmpHh79#cQ13e@V)nC>-C2W79Km_=8?Oy0~k*>zvO+c;Z2!9R=Z#C0;+U@N?%2$2a2G6hjN=rSHcSD}Siv{05~joi7~Uh+|U>FZ?yG8d)sPY2f@1 zd)BJPCLG_0V^fSUz7tL!TDyIB3&%I& z*vj*`P2YHj;(~KJIRBCln>$}Pz7fZ!JzUOT+y04uo7dI(bB1?tzHodaj!k>a@V{Bv zr})U5-JHMTfsW1>j&H=VX^#i~0efy)eDIoH&L4bU%FQSo--u(=9uoX|7jIg;dVc?O z{%2EeMdA2H9Gmv2;4kmosd(52+d1FHAOHP@;~Q~o<#`YrpWdQadC@M;|GjU@{V5#Z zh-1?pHs^2Bv2k(INyDAL%MQEy`Gw;factVdhJWXzhQ&s=k8u9zMteA4IKC0braf%< zKYm)fcun`w&cElgvCbEcZ^W@_4;%jVO@FMqbjAM8pS$o-=L^R-;@HabaK2LUZB_gA z#yQ`{Z|4igH{#f|N6h&zoxZec_Ra@8f8v~}&KHhv#Icp<>73R0sj41J4|D#EjZbmD zaC{?x$2a2GvmRUoHK<(7D~o&VTHMtDP?#--u&V3<2l=>7eTi8+M=J{F%e&I$t=x z5yz$&2l$m^&nQfol=wFPJ6|}y5yz%y@bD*mH>O{+pA)~;+*_P49N&mzE6?M;WTUsM zpZF~CyEVAO`NHvyI5x!?aQ;u0cUbuLvxz_WygQvQ9N&mzQw#(Ad%8co@P%dR^Pf8H ze&-9vH{#gzj2?cA&+9DOta`eC{+1fr`NHvyI5x!qz#n>R(?!>9llZp&alUYTBaTfm z2=LE%b;zRgUY+LWU%cC6&KHhv#IY#`0{*pYO<8pD?NgmUYxYyl7mjblu_?v^{@^W7 zSv30ow>?B!Q18uHN;=XdS&lJkY*8*yxkA>{lm?!0x;O|Km3 z{O&uv>U`n&MjV@B9N|BC?!Ak8?|X#vZT;wc;rK=zo1Wpp-{7|w7oE`YQ0LEH`L^?g z;~Q~odPWF;qfg&k)UWph=f7R?p7Vv{8*yxU1_}T2*FIhJasPvyf6r&{J6|}y5yz$& z3;1@F-n@HX=YR0(C(aj+Z^W@F#sl)=XX=#d)ZW|qvlf2teBt;;9GhZH;P<}2eyQWk zJ)LjsXXgvYH{#e7!vcRqoefJZ?-}O&em8&PeBt;;9Gjkz!tc|xRcWs=gPh-O+V{>E zj&H=V=@~5iuRm>DnmJ^k^C!;v(fPvhjW{+vTeF zO)u+Fx?_0h2tA>Y>I(}fACes z(jkv-=KQIhYdc>!z7fZ!7#H}<8}C?Jc3^wwKl0OB&KHhv#IcpvVLRZMolCd%Y3=;= z+OF+<;rK=zn__r4{~`D7R+?Pc!1?o<)_1;ed?SucF+%X$jNGHN&8&5u-*m}3&KHhv z#IflaHvDn>jVe8|cx~s~{g?BF;~Q~o<#hz+P1wKGb*-QM{&#U@L+1;}H{#gzjGptK zv)O^AO*Z(_`G?)t$oazYjW{;N2*6+C+3}^Jw|(IJi9a-UzHodaj!iKL@SmJ{Sm~|X zpLTx5@}|xgj&H=VmDh=AJn8UKt0@mT|KW?9IbS%w5yvJC8|T04ydz6Hw7lK2Lr8+?9DX>#2;&hNUUh4Y2u8*yyX=)tf5%W%8-(mNsASSm!_XLTl#>$2a2Gq=D!Bk4-8zHodaj!ham_}kqzz4ZM0b)9eb@6H#FZ^W@lqX&O_=NYB>&&_q-?Atc= zKZWBPact56!f$o`jMDDE@9n%&_s#uJ;rK=zn>2Rt$J*L;#EPXpZnv7-+4;ipjkx%# z6O-fpc&~dd@Ad~Dyo~<`j!havIQj2a;C15$Tm13+Q)dR>h+~rm4^IC1IN$a^oG%>T zh+~t+4}SjdQ^o5KW^L_!;rK=zn>2=S^6!W95BajE^M&IZact5s!q2~-&YwDMTjvYM zH{#gJ>#*h5hx6AvvXAqH;~Q~o%CW%t^Xt?33!m-leBt;;9Gh}Hz|Y4A=iB~|^M&IZ zacs&V0Y4v~oZoLuf9DIwH{#gJ>+I#@qw^cxw4L*X;~Q~ojL)jWmygfRU%cA@=L^R- z;@FfUhu=S+ADlmN;6UdK$2a2GlmiBSK0i7C;MqGlUpT%I$5viPF`pluZ~JG?7mjbl zu_?z5=g;ToBCjvma)|SV;~Q~o%E1FCUmrNXdi+r53&%I&*rYLqpRZ4x-?h`O&KHhv z#IcpJf`po$aci7$e!tsqbHfem}=j%h~+y0^Rh2tA> zY|;q9&)28UUr}e2^M&IZact#zqVx5!^Cz@R^MG)CBaTfPD9)d+&z(Pe@aC{?< zO&Tru`Tc?OTmExj=L^R-;@G4if}h`?IDbLK{>~SUZ^W@lqXa*{KXSh9zdBzyz7fYJ zjTHR+{>=G1SR^ZP^Rum9db&KHhv#IZ>O2Hy_uU!DKKs}r0r9N&mz zQw|x(`Teo;r#>^$`NHvyI5y?DfuG->JAeGbL!B=i--u&V4ixzL{($ps|JnJ%@r^h( z^M&IZact74!_W7poWK6G4MDp!tsqbw(>eT`Tnf)_ucpu=L^R-;@Fgfhx6zA!_K$o7tR-sZ^W@F z#}E8`f7CP99Z^W@FhYxe?H=Td;a6+7mjbl zv6a`k%Ae0T|LrTzaK3PSBaTfu6gYqWe8~CBK1+Pz_(mL?ay-D#pHDe|UemMu{KD~# zI5ufq;pflCoPX2>XFFdwz7fY(UPmK;KIi zY|;qB&!0~^zs~IQoG%>Th+|WZ2>AK)QRi>{RN@QAH{#fog93j3eAfAkD=+Z#3&%I& z*pwpze*S#e`B$BEq4R~~8*yyPApt*sKJEOm_a(k?d?SucIZWZ_&&Qo_&kz0l!tsqb zHlEK97_qJ2ljqOpoxkWdKE?Ut_PxUS!tsqbHsWJCFK+DoJU+(xAI?mC;rK=z zn{puY`{(gF&hNV9FMfXE_(mL?axBBo9V^a=c_<4Mi^Vgk{_`>mx zI5y=lhM&hr`TegwfA;eW$2a2Gl%o`W9-rm>^LD=0`NHvyI5y(L&iwq1zVP$-Fz5F^ zAn}Fc8*yyP!3sZ*Pjmk0$FB493&%I&*p%ZHejXp^{Ek&=A5l2I5yz$+rttIlJm-%e zp7_G?jW{;q1BX30$N&C$e4z8~`MdxA!tsqbHsTZCoHpJ0d3>VttGBw*`NHvyI5y>o z<@e9yBb|Ri|HK!LZ^W@F2QB>@`0In*w04sWpb! z52}%&rWYAzKd45~4yIZlBkUFGK{SLY2=}l)X$beQNPVY? z1cj0iJt1mBszISW7;6m;;aFFppdw6l84hwaLedhJ*M>U8R4NlqrGcXGP-}cH`A_;mszRY|TfH_E3PRL_$GLX!c+p`7rs+gIkBt-u(%i3wyqklNv6%a{3NJXeKObJ0v6O@D0fkHcTwl&m)Xb8*eL7i_Z zi3_~m$^Tw6_-9j5T;V#3q!zp~*Wp}MaiwW;&{bS*TEVMLCwPr(2CuVM*XOk==Gv=y zrmaCaSYG!D-Nj9=zd&_ylWBLTn?jwZTTIJC{S@jv-D=vNR7VA+;GL!}OIkrHsi2lZ zGe||1d+aAE2k&>~;Qgi>yx-M>Xb4ddF0dXAVYM}?JD~@n!c)n5^d05(ogOuv*JGx| zdDMC;J3W!t(0I~bJ>@l7sO|KWX+WNKUE#C#>N(RBq6K;0)gU#sgfE*qgz8Szc%s4+ z>OnMwuUVtI6E&Ww@bqS`KU!k1-ZFg%bvaho527JK(Uw$%@8ue$_f4hrfon_95q@C$ z!K5*v+SA9mcIgv)^{Ht|(43TO3Cn9eeQ7E})PraUQ4pdYq~a3_LMlC>AEesTKXc8~ zxAuw}Pv}$ry?*c~(?p@CLm7pR5bYZ(mX)p~L{EsCuo9V@DOze-`#-PgRNwS2N%zvg zUj6ShgH22i)6{$kUl2WvZ8zk~Gyt?gv} zK4AkVC#3Weu%Z9)(^3Mn6+K4A7=e-)`nZZoAo2C?QZ=D>qlDK!}^id z?`dr>>-V&Nl(oIBA7%Y$Yh$b*ZT&vh_O*T=>-V#^zxCC&WT<891Xc>QZC$|1fE58N z0agIa{LJ{w^vv+g?9AxQqRE(U+p3 zq~?`sU7?keMvgjHsU~2mbM>!uan!a#v5c;gdRA1^Q$4Gjd_-m>W&~#Bty#MYrE#i_ zmugl$<|8E|BqJju;woJn^@h+`qJ=}j9iRZj8u$JNe@T8Ds-0UEvYVt z%663ojuC~Cgb{?1gApUv9%3Y51Xx{jxvDM|S29;HS1wm9SL*7usHj8L-LE{ZIIc9V zFs>}FD6S-~Ag&y)7_Jnq5Uvca2(ARK0KWNr*Te`s1+A4&sF4LfwOtT)lcvL0o9I@W$zzp|cXeam{6^)KsT*2k=uSwFL$rluZx zY}Vh>XtQ2t{my!x^*!r->AI=Yht?O(FZUB@fYE|;|H1u8JNs#4KWN0cpW*(7`yK9o zxF6#Fh#G6$KXE^W{+RnM?!UMn<4AXF=sG#jbyBm7I$gZR2mPo0ubr>LYk2XxlQsN! z-PRg>UbnKg+u$|U+`#@^yW-R_=Z$%Mr&CrOX3+MvE9|#zQL+1x;|?4@VA8mWle_P9 z^tdArnRwuo?njS5{GjniPntO8m~K-iPik{rvx-fx;T>uo%SS$Q_@i5_zjDsg2XFu6 zVUsRi^QK$RKY!Z%(T^9-+U%0MuIVuOgQe3x>-61*ht6)(>&Qtb9?)g)zis=)-8=pE z+plMS^2^QNE<1MV481gX}(vpxgE~jwZ)lr|2Avf^K0HXWalBBwz%_-igiY= z-{6faW;L3-#+H45KK;xO&)@B~7Hgl`aQu$vzkKSMb;tj5?8mcD{(4B;uO1mW=e{~i z-@JL~7l*%k&zznQ?(*0}H$V7d{p-8dy=B?9uitjlvF{!~;F25fdGDcjE_-|Q8_TzN zKcHeAjwy5_~=cM3Jw)hB5mdKc=l22{PKx0RNWX7sdxT-u?L`i`Ts;Qqb;&Y8i90=%{RaDII-tnrdUS7%xw8m1a%5 zk2UH(HQ!V|Z5WGs5^(8bmLK&bO_r67L~^`a-0YQ38?dFO3_-1pb*xKS;G$EfbBmw7 z=@+vPRM$0|D$}04_wA~%ul|c(eeK0B2FAjU=p8p4Z}Vz?d;hA=Y{#0~bnEmh&pZ+G zPH8DB6J5yw)?C{QrTQs!Ost!tN*rwhonjfZ1~(^?@fL<6&hoD?r*@N3bvkIh42%+w zk(YdZH&dmZlT;*4KQ5EbHp(_C@bo29#r+|a*nQh%_H(NCYTmbcg*xPjL#)GenxGMM zm2um;_K8au;dI&jF;uCqna&er6I)MQ3Oae8Vv+K5vThKpvQZ_>0Z(~S$d>s0#n+b9 zrDqZ)9&)Jrv{NNE2_-8MWb$85E)naXZN4k^5ZlaoS%swFf=Z&8_5-#!)UA_D*&M++ z*<@6SmhC=jDpy%}8mz8T1Wz+>dK&yFN2W61xOSc;KSi`79#F?0GmKP8$}l6?e2L zU6om#h%n568&%Youu}3RM%E>S`MI4VvF?2Gh6u_~71w@;L{d8^b2MQ?gZJ|`6-V^~ zrS=JG1HUB(6W0K5?zS{XWo@~U2Rw6cYU3D$YXpoF*(4UEF{PSD=|M9uc)xCkw!65P zZoD%o&S|sSP)LkEdB@^Xc8kuJ6GL!z&jK-C>JzVsDWZk9?GS@m@AC&v+@LwN$s1&v zSkZ>a_*#)r-to1g08OqSO?0KWAbOvGU+S@-+GeL)<}|CFr(fhcKjpfhMQ-McJGPnJ zs=LK9|>$QU$lWw&;l}XRdgtj_!9`ks-QhX#@ zAlDsu;s)vRcK2PBy{b6yU`}!vy~I=Ma+wFHR}mW`_B8#ZVIR9ZP@C)3J-M2pxYLc{ zz58x@-kR%+DF>zL9FUHhJmX1)ZmA2&q~Y>rDb617B15pqgRMH{-oynX9>d?+~|P$;(TbvCyiW$ekcz zPf>x*V@@XrUgbMn)4gUrH8|92{nev5zFD40(uZ-XIG$bpftb(9uT<=sHwVvaJRtD- zIK{ax<}sI+zc;p^HP>6aYWdX~TM>RvKIHYy6``09=O0t`-!1!SrP~B+T0+YeUvFXG zog@Byn}D>dZ%L$k>s((b|YpHz7<3|uvZeu>O1 zNZ{CIZ@X>WBXgR;|HZv_vQQ025{i*iP>TVGxEUwG`M)!xLRwW zT~Qc5cY6;PD%CBLQCWFaT5AN$m1$k6m@=B8xR0W2%a%T=h%WD1a|W3 z^5ARJ+oZd2aHRr>QCEyGU>I8C;SQnZxQYc&(zz>Lb5}%QtkDiAcL!JGPxc5zU7c0K z2TW6E^i)xl*Yh@?PrHsfHF)ne)t90q%hu*Aqo>H!ABQ4TG9|QsMXn!5DM2o zCam{#DAY>sWK>ZxgldweT2_8a6H=*{o*o0OT9 z?aX^sXWt27xcI1{zI{tSt#QD8v$Wcyb8lgOVPyPl2!#Ta_5bf*QxDE^HR`ENo$#lS zZhq^x8ExW!)~L<$48}~M#Vp3boz`ojUOY6Ih&`27!o9VM@@(8F75it;SIv@=@$us` zYU=rG+1U>hQ&V#p9ADqMDw~)hJ3cJ#po47AxP77Op*P#7#^4i8533eymrW>6+KN#q zEKu0u_A{7Y=le}R3ZatSU)~NQrb0{V=y2^RDfNGJ_xtcInfj~Rb?%kq-0mnt-K0yjR zNj2zHb3cO2}0FUGjRb85of(leLin55FMX_ouU&h#+jPc{PY^nD$ z|2^pu)_1QfHx@Bn;swNQmKNzF9-)w(yRj2rU-=yid?|P38~h$~SdrkH{1$=THwoE@ z-Ea9PsY-M|cga3J2vF|uQ+`&`r3#0?xSL>hX9fL`QEVc9tY&U1TQ)yl`ueO*b4=HH zo_RmBeZPpvk-|CG1aSm?N{JGf^Rc854D4c#n zSmmn3*{v-ZPOj?5ffKsHJ5QdxVkZyR+*sUHad@hEsXm)^A|aT_@7=qdCyyK1!^yX% zJ|)#mq54$JnG`-q(+S$Xpd@U_BcsJTM;37lnz*Gtc_9BdqoVb>&tgm1@%6Zrx!%Ol z!8t$-1+Y#CN117V5LTGzW_7*If?X=<>x&maydgJwHkG_Q=DE{nI8)BMs>4c5^_VS; zOhrsvYbi6IZkvr3^{&)vzJ@s=nt?$GYWfTjxmXP>`JDwe<}X3Lg@NaRz>+y;=iBFZzSL} zz-m;p!QI-Dla!a&=GgN7820e-7?z}-3`pMDrYN$xUuTx&B&uLNSU_*h!cWVJbr}sf zNNuc#vz-&76LBuisPnTgP3GtKS!pD7RrTGr3j}g{0ek(kKGzB4;BZ6W>+s}p0_-Ls zwOkE+iGc`^@vy)jmg9Uk)`g9%6)@|@EjzD0W5i)_ zaWCHObpz63=dnIcgLLr(3#*!%O+Z^f>At3H8f#nPo(Oz-{O8g&shTG+JjY12WMAcB^Q8hCUEA*Mr15SS4@OL+);5Rl(P8k*GrPR_!$G zQvBa%wN(p?jaDOT!Xiv6MlQIy71EBKtnpjOFOsQ&oU>`T;uWyd#!;5>xZEDtRmPu) zBCM}+f)qLEmJni1%8@QfE+eDQFm^SkPL--^-?`k;UoeLCJdWL%!%H{oh>EMld5Ctr z7lf*<*0_u3bU?9h--?VSs#eUsb)331AGF)x`S3dTE|QkzI3uruLZl@DVB9)AeJ1Yi z+9OIlZUa4sncdJl3kx0wQ4zudf>H;d)Il^|ePwlw9zf>fjXLUq-DjfiR6d%SIZqGR zI+5RFwJcwO-AFQptXhcZnJ>IuH>GFeo!2oPm^V*+^{Rpn2pJ_?b9KYzk1+>WtPNRn zuDn&P>!bKq_9Sax9)Duj13xg|_*ki<=)^NmeA9QwQ4Y|`QichEH|sC1Q{uTeT9#1r zaR-}*6?dF|FC?iJu*9+-x@@wJ(M2HgtIcs?S8#+^Bg1qerFNWEw5oyR4;-jUD6w=9 z|Fv?<2ApH1j#2fRVO-ow@ssQWqZ){X`5d8*ov0Iacb>jnqydEIux@CnBXxH!E6{tN zr{blXk@^jz4zhbqVQLu_v=XR#Js+PEf7{&BQlmkq0T&^Mxp9uFDhv7~{P~SCzN>0d zY`3N2QcsELg@iCIFE>Z&40Zx9s0RW*M(Ev>+{|%158wyl3@xDU`1GYD_I+GHlk;1y zbQK)dmXpJNfGgd|WY|_Xe;BNQ{n!@S>rH+%^ zU{|{a@{nN+dz=9!UZjCW&z3QCrxUuwrb#lC9g<1c#$lE7@hI@r>GAAer}Vem_oV0@ z$}F$&bk}U5!+5Q)S*`gXZV)BTH`)A!wuE?O-r7P>Z>>!2!Slgl^TffJFY=TYOTUCtZzQvY#B+8VMagL^Y(Wau5SoALX-2W zUiMhhG7v60d^6UM_P2f7d5u?k@9wLnjk-_ac>@76Z%aV@cPs!2$@8XY!3HQKIXh?i zv7)lAqwrEpp2=pJjg2YT2;&Z;4E_4^`FM^(1de^CW!qzA)i=_@047L*VZQ^k4>wz? zU#O2U5jbAw<+?6BKe~0#Q&^)l&*-%P#x~-+GNpktOz(8v!;I#b(*oz7$OF$e?zbfJ z^fwVOdS+EpH(c>%^qy*Q7w)ArDxH3k_!$~lC`7>PSVfL+h-=6#)EK8KXcGM3pRkp6S6d5!nXMQtg*i# zomr3;8D8mjBn@+aI=5cNpc6$YKXB!nTM6L3X?7!w#(`1r^Lx?(%aOEGQFZ7~U5Gb# z$3Pu)dAL0PQn_Zwc=QPYdqWH3w9mjS>f%eO)Dj!_w4iTkCUGtyp9aOHPv!S zdhP~8dVw4P1Md&xJjV9j|6kOdb&#LJSn&G#<2~jllOP1crd6K!O z?m55B$9ZvAlMrA{FN6SoOOQm-V1RH=#!&Xb#}dIQFIHS;vy9SZ>xVH8_bkr)_!uI( z9vpz?!$CMi?ur*u7`T`waUQ+xa7WXr-@dwltiI(=N16|d1 z3hmXdmY>za+mk=UW~we5CAg(5xA8?zc)3X^)tS%&A!}hHlt5xNU-&xAAArjhr>7eq zH$<2^mfnp+zw2K4l-++r|6QEnQ+ZspL^r!MKLTau#Qrk?u?)PpSTEoegPyZ;Z|a>Q0K)eg8vaR$JR>!Dlr!t&+>VhG}A3 z#OM)LPOc0qS5!?Ic;bOmd@D`Ew4qpVD(y5L zgu1aYG>bO0)(A`z zp5j)dL#7Kib+V8&0k1D<52CEYpO>xds7L4aKjCsUCU^>8%*lE$?d%t-)we_PxV z2x1}Q#!KxUE=fxx!#;q-#%j&jZH=epKpAhV#CwK?EEgu}Q-&a3hpTebc>2(R(He4d zvhLF#Nxz=GPey;LLPSEM&95+F+mE`MF!J=7jmcU0WDim+Hs%1SPk0OEq?-VCi(TWQ z?f5tgSEGPzJKXO6K6E4m?B6_-+K!PIkp~glMq~Vfv_7QMLu)9Kg*fY| zdILk9fIMRl`KA~i%NbY8`vC)aR&{AANd3eQy1L_nOcS>vhB;TiFefB%cfFbIw>vMm zs;Q&bm;U9ceAfqRX}e^uTJtW8H0SaCD(6veWrI9#&ESv1poQ#rJG}F2?V>^G-i^=q zJksDE!xVKFkwbFO5ECHJEV6=;re_yo=|AdWXBT5aNfvr9x#{-!(=2JO*4DcCpdfBv z8Os%JU;i7v4+^HYsvm!uzXdu0J(%fU%uFxFG52v}_LreJ!>%gBt5+R`C^X)zc6q5A zQ02X;CkZUV5it3jYn?}Vzq|mz*e7%~U1n{DT?K4f8jsP^RR}6s9HW9?$l|v~+q{47 zg_s)%7)ac`^>uWn7g%y|TI^l@*lWN9nV<@P$-c9>BLv(Srw78xeSCACJQ>Kjs6|JB zW2@f}hkLPb;o-A-3-H~=XA$hp|74eW@FCoSWRz9`wGiC@r8%n2y7{wx+YLR{P366S z3c~sXR{}Y=Vmjdbc{7NXvEwnUAvx39tk1e5ko-)rF7p=2Z6wY>M@NI6S{_z#Exj!$ zaHSqX!mLI3k(^JeoWT(i8;?!> zmI4cF1D*LK#7t3X;zGost12LfTi61IiXQ1_X-H28EHx;7X4mxZjpa*5WwSR}0D2+76tvTif3Fe;E98yzVWI%cr7tr{8!}1TG3Db)`R+^6 zj;yDa6XDh0b@;a7Iz1s@@y7Jff|ozAvo0#GqgEd>_|8IGLysW&6A=dQJvRg^4lyjq zON^k}^a8z*=Ym4D(qg=BZaX{uo8di6wx7Ozd+0#7uNhRKI4QhLN4IWFZ8~5ue735J zX}I_$XwwrDU=E zwr#=Pla4uT5xMUQ8szw|^G7_}LWoD4H!JuW^(jS|q;Ezj(pui_Bf%$oHyQ!dKPOBc zRcuo~$TJGxdl{Z>N-NZodXBBAWXhyIqib)TFC#NeqD(O^PA2YCTbCoSt1^(crBh=r zOb>Jz$62#<^NdtfI3Wg*1oZ`GO!E)Sn1Oi_NH@YgyF~pHNshWK=2%}=TltKyOLer! zn0X3vfXYmqlQtO9B~_w*`l2^jS6yOXpJu7a zl8+?|#U#-2%m)*B|Cv$^@1N^(&5+m%}0V2BtYa8lIBMam?BkeJp*?AC>I(T z1TUy^S6a~F2hPkqJk|v1J}yVyoue<8VE|D#J|?7MZeC&l=}>-Jd)*5cCWpIE!y{(A z(&lL2;ym2bm?Ci8!u-8+PF|i$l0)C8M;FX0RDz$VT>4xn;P!FAzheJdz}F!cdLSf( z1B$J}++M$4M0QnG5;3~(L{8wJJBHdXR9#enk zD9Vs|T}7pYzG~RMV{O@#lUatoT)yYAS)0KEUtzN}13VAmyPHUWQEUEyeSV|LC2AAh(OccOE?i(rXlc24Jmh6u>h2vv zxiWR2&kP?~j%Ri)w@FGDBM518W-UFW&yyq@{46UD`%T@mD2RTtqY_W6`h@AJvRXK0&Pl%ucKW%XqA9?zH*ycmqg7bP2Z zsi%*V~eQ=c}p?audkkbG861aAjBU-^U<7u=1*rQ&E-l=RhslBr#+83Me?% z?L%XR@xtOAk2(5te2e3V=9mC$Aa*H}4}#4WU$$Wn0y35cXdC9~ljjO8eF>-uDYDfu zR}>mKg~gZ@R#fF10zg-!DgnU4#_?@$HfD@NgGPi611u2tm<-9YG@Jd;#0xv+zD{LZ z2taHu9M}RBRy_}aqL2v780Kbs-vm}%3U-HBKrp~n58A*aS-)|y#Ke`RF;g*FhJU%# zZO6@McpalC06=Hti|{6VSt&?JanU3Vv%D;{d5(~*GV?72v2CsSH}S+!3XY-Egy-zq zQMAU@Yo%diNa7~dONv}v)mb;K0g4r#(EATTlB z*Vh}gsUz2xlww?0d$rsJXAu8!K@KCJ-A?YYmQ&T0%(k%fp+KB1_zd9fYBQlldD8dX zg$G+2_&}p4i!)W@2I~!)TB`BrnGZ-ICC1O;yGVR&{M%THPs`1N4=sUFDWIJ$7fLM8 zB~ut z*;#Bx$i%Esz2)xKVS`>!YniWClT9q1Z$IH7D(lXU&%(EuyRj;}0h)xF*4^nLR6;VW z6>53}ge1+N`*;?*^roPOAMl*7=5iYR_=y>`UBWW$pUV1^Mr&T_zIjVdAG<@u>^E7)citc1b5bi4 zPO04V247PGDoPrP1qzlyN272KuYi%V-#6N$Q8?E22x}*dhYN<+$^rRbuTt@U3DBZ- z<-gT|4*>4u02n@dBtXjojc~^B9YN+jJoR7juanecMwBT+3)$>ttq#1(J`~f4P>5X- zv2Grb_weYyS*%EMuG#t_Pb4F!;gyctXO2%e&zwSHVnibJA3)z{BX_{PpZ6bUpxTJ}uujq9UGnF;roZJ)}o2h=Tf>@_aTTwB#BE>H2ANAG0Ca#E(h;^4G=R{Qwd zDUPOVR@@&|mo5yIkGVTh)Xv+{Q?EJ=)|&N*Ez_Z*$r($$Uw+MWv$u@6@wkbA!&JR} z0&RNc+os!el3Vr1t&Q};oh~272eIln2uoFB13OWkMn__Uq>lKFPBNg&I@b2_Z8vc{UV9oXXAl2)Jf3yM4=loY|FG)cc9Q zXv;UU=eOuX1_q5{nx4rsllne+MXR{Zqa0O?^jPASi6K3Z7}&~?Ci3LB%t=;Qk2?Jd zn_(FxwD?3WvV7?3@JN3kO&(Eg?dO&P{S;oS7I(^^hDZ`}UbT;@!&x?JRkg@S>D5cl zuk{QxiO;KQo_+LGmS@zTVa6?GYJ)F+9Y;|-EiFdw0PP) z8%GvpXBl=}zo&Nq`Q*(rPtM5}*BVL*sq1v*GOEcZ$UWl7qJZbUyV*eWO5qV)CDZrJ z{&R_q(mEQ{>w8|k`zF&)(D#>7{Y9c&6a*>BxX3BuC)IfukPQdV-%s*ozg!)qi4qpR zPf9X)KexCbHQ!$+!9(WsF=wxWKe^sIE4gU8a$sqkk>&+W>2$-Fmiq=T0?R&*d3Db` zDkR^#Vcri7&1Em+IMHN)yaoDc8T{nd1yf?%SK#M|WZ!#u?R7WRV7hc5w(EK)8s91B zkyEGBQC{eHtlpZ^DQ?I_S7Uc@#$u23Xc&jiqUy;6Q2iY4+MuQYBRQSmb_hNU0`2Kw zjll4^Al%_LaCbPLwJRFIg91Z55NLM?@Pdt%jkqujCM+xlM+n&pid%`>h*(<*BCLc( zMB&ypBD^S!i;Xqvzx2vtO`VkNVRWb`0*wI!H>BD4d0}iU2&A>EjRVq7n$5%AmPd?D zRz~Wu^26|wA9b^UWsn#N8*6Dcdv|w~1Ro#D1MSS~inimkvF1ZKBU}(jcMKmtFFzmK zKli#L&@PzY7WjVUpF6!V4*%Tc<>kfeCHNg!00!gdyQZ$D3#v{RIFADo;|@n!BmTnw zrv&EvWehJ!6$!Gmbw#_t-N8eU+TUFZB8mL5TiL+2!rc*eu4o??zQcAS-NCl#y2IT) zFh8Bp^FbjF4{fZ!lb5hYBfwHNEFgIackr4t+uv6V1urS;DYMB4zyySNV4^(y0($(S z5`sb!0;0SE0-}O2n2@m)*lkB}4uOAQx}twnBVqGze9d2H9Be>l4z>=6@3%N1e7s!I zHs48tg`MF@I}cEh4)1k_W8Brj5e09xQTX1hzpHck>+FB?76wW4@CzUE77&ww3G<2w ziwX$v|IGVuAQ>q%!qeeUmi{7%@vw44SiApq03l`C)SD$B0-QjsV8&e~OO%-o|5%v*k`kW7k;i}%q=>MEdpNtZ zD0zdz^J8LpV{F)1{^EXEl{6b1g>rTPWew~rK2TEG4*TMF1rIO%-xNGFD*mISC(`DZ zNy78j1UWQEzSF|kJD@P!|G~`>YWC0}_RAgvrEjSvEadmdE0nh!dCIv-!`41=WpQ-=}{Et=qsVo?@_e)s^ zm%|0&`}yB@#t%jROR1m5>p#Z&msRDT(bfdpiUu_d3)p^l4Y&&^uzWwK86aH2g$eBg zDpj<*fEB1l5MZSG|KpzJ`#vW@g#f|h0W&ZD&VHcQy&;kb9+7|<5Qn)DN4K9y{#Yt7 ze=B}oa1Q>8WpeK+?kx!52x3tj0ontP{)wpsJM&P}{3!iz3}&Kn!ZsVIm=1T+Bcj=nSV*5Rn;8!MQ30`F9LHh5GAEuK0yeVEh0`j9ZKlAx*%Uq30@@^7=i7vtb zm{9Zc_L}bqo}vTB+7)Ex!)NpNUx%{zyM~tEjq*(gp-zE~<@gyA$QS(srn)QA!QBk@go!`~g)Fv>F|p5jOuD{ZN=hO0WQ;Vn8wCJ%U~Ze&xqcviR?a z={mT0{8%D>bcPDj*7di2td%|63JTWu$#JxgPk>*c@mp*igo~>?;;O3^#(@8~n8{iS zr4k_5VPepuKf?FMpD^9wR?Y|u3xqI2z{ZMSghyCN)S5>~2nOe|5)%;Pfr%hQMG^cW zA~2Y^g@uhf|L=rw4fkmT3RXu5?mS1T6_@)%Q*^%vL?dj}Tz@R{7~UU?+OLwB`{_(u z6bPvY&i$j%Ie&uwXU?GPeK26U*P(~-JJt`xNZ(t7&=KH%bO2K1|HK;ZiLlYNMj#Ow z1A*URA~{<`UBCfIqyCY<215U}nJfGO6OKf>f+jR*>ikv-{mc^7IzMfkN`FAq{fDK_ z_e8fpa3!%l3H4(I0H?FRs<)LtA*1aNJJSk|LGYsOe;q1crH3J5AitO3&iNBh0BE!O z1Ji$o3gL}%KBD3NT}U=HkZV%l;o%N4{b?(;KLUIC=XIC=-*xu~&wl~SgG*%u!NbG- zD4;qRzW>w2I|BIALjEh@_KT-|-M1mY;eK?{9rHU1_;&)LEtr!Cs$S3*`n3~}rZ4`~ zSl}2r-Vt(tV;xOq{1xwL9^KD)?I(`l9nGrywTVYFDt?9`K8^RUEpQ~i;#Vq%rNF82 zeWU&B^T&sd;1McE9svG|a`f@x&oEg>QU3HW@z

+vDLBp*)egU>{vBbH@}!*cAUAIHm_Q!sX7FjtGSHV z(bv<}(;FY)=VvrtSJzKXRr~+Ub42)sM%qLL#7D3r;~0;@=$i0EG>S^feHuM~qkw=Q z|3G~sz47C9{Eh$3)BJDMvvveK&M(j}&QCioBtF7F(k~Qp?_EuQK&|M=VD97j8T%PR zRp|xj8taVL^)vdv`*;CSG3;^CeYGXmm}$qdW9Ekjuw(xz5|50we}JwZTgMRUQeSs` zkU^lazp?K432fs4w!fjVjviYpIyNFOAevh~e_hZ@LmfSRw!Z#&eQxz@|E1{@|296Y zmg|hAr73EI2Sv&MZF!l^`>(oOn_1KR=Z42BTb?InPjOK@`Ppy$)s2rLoL;DJEM4%S zX487nwQH*KS7sHLy=eS!sk`|5l70MV(pBv>RzEJ>bYlI7k^2XXcH3@97598e>xt_5 zuG^9GA+#WI`nT?*^LD7H+-z7rTl-N;TA6cW;j_-7t6#^*eB~2&U3)Bwf81w@^$9u4 zySh6Kof6u|w>^9I?8WwkftQCII`A&*YvYaY7n^RS$Cc?gIJ|B+C@-e6w7p>T?yCW& z#W`=Cqe3e~ySEH11wbmFV@RNsCI1JAD;qq*s#!fS(NkFG1M3cPm2E5lu{ zSi8Pa;qzE2Yq{%dCg}yP74sMYw|kcd-m%dOQyXlscGYHkTc;7$vJP{-FZFvds--=& zqH2Ko@<+0w#kM(~H+L0;ow%E2_{C4-LD3Eb$Bl`Sh2KK+ToNX`#GFWZaP!KE;w_4S!|dbJOuCw0Pnwy0N}%|_^3xA*DV%*# zA0GDn!1dZ0X{Ua4YPWVbUD5x2wP#-UbT93x=3S}tWA_ZnQ?c8r@jUf(q41uZqNuzB zce|@~Qp;{1+*6d7v8Ul))ySaHoS%X0H*$mH#g5H4 zc~$*B{@Qu(_}7!$J+0rCo)Eq+KW3Ps+p%Z`A=SI7t?P2NT)v4upA;oBQuX2rP4A%B zLk8|~Ev@U{X;+H-R@_Kah zPSez{o#ur$ev`|jRvmn5{&r^gaKEaPZ{ogGj8~a@M%G|>dibFw`iawhBA1(_4>*2U z$n{ip=7yxodqH*W7p{sN7BX6!w|R0&{VMfrhx}F9!_9v>e;3?+@mhG%h&TJ5zYl0J zI~p@RTtD2<{H>Pc5ciwH7ng3k({GUEj4Z=kr?z+d6Lo$#&pdi}$k>ZJLVq20d*pvv zsoUnXZN?(4U-?nHz6xwoIwI@2ar29zmjh=UTsm|{@%S&Nu2U|9Hkt;n{pq$mZEWlN zgk76{yjXrE+-LY5v8WD*aca%6pNucBQJGWsENJb-3tPYU+wj`>)W{2MCtH91>P)u# zeRipF-kO=_f@|L$GGDss`OwM7rnD$3Ovv;(_Hww7%Qg24!mP;y1KreOc9jXwEg5~; zMP10zWyQ?4$7Y)^8uNX-cvI6_ceDDrh3yAm~BH(uf|e!3z1 zan0OqEe78r%1-R}K5eXCl{iUx-S-~lf|$J`D!a#&RIAjCSvuB8vEoj)Eqo}wwLN*$ zEpq%i`S(w6x~+BxQ<5albEI>np)`sDte85eW*X*^O%kGgnc zTDU9UH^Va7>jToGE;&BSpi*Rfyq7HH?}$ho^GlwlJw}@OhW4(&<}`Jm{nr}S55J_* z>2f*y$-}Dbtz#qatS;~=3BUPm-;g`nx=wQrT=+O@s{MqlhHV086Ci@)<#@3Nu$EW-0lrq|ed zwHSD$UK_jUhU?wiLy(OWbv!|Th{pY1-wTI{49-Y@5A+D^0i+V=Jp;re?At8BU4 z{X+do^{4XB``ll*uePxcij8<%e!sn8;sy5RWa}l~4KG%Yv{K+-cLiS28LOPfyRCdQD3*%p=$Q>EUhjH=p$F7L#5ZVP6^|l(b&7cIV!; zj%~N9mY0V=+%>~9B0O=hdw z-OFRAZu7C$Yun_|HRIvtirI2AMrHR?b2fRnx?)a6-h!`RFK?Go-akM7lHrXD0gL3u zB{cio*uAf2;mHFz)x}E(+)f-aZm!EXUDS&&4dnID@wjDp*PiV?bw=;ehTyMYROhP zxto}tTh_^1Orx^2ROY6nh>1wgDG9iEFW53c@U4y1Gm*@F zd`DM*wvabUv@BY6Zm+jA_1z%rhv0zLHFs~{U;0?&&Nf3cn|XSo+Slu+EI6`mkjNCdJWlY*?*(!xEYFWEpuKOtf#D4rxoT# z-%|3(xNuD``Nxrm@4nu*5PC9oyJ6)VkGxH$rxpA6Q}4c{t}ySE?2v=6Ct0ixp0!^k z(pg(xNT`xstJprt^x(z!RiYLqvmLL*))o7g`&rf6pKa#Xn_y}sc6;CH3)j2C9={uZ z>fER~m3$_unFnju=$C8EGu$g8X}$fI=g#(B{Hu%Dr=-#+ic}r?yfCQzQ)%bmg?Aca z-R2+5Rz4haZ1jYcx7sddG}aab7`Hz*?ps3Z6mYALj?{U(T$oT6LrL`JFU@HM@r|il zzg!AWU3h-ez0-mpg`HI+ggw{iDTNqUwpqN(J~hnBr{Tqcbss+lPIiqB@4Ptb+@$Gc zLx%fWj`ApJG1V;Fc3qDDd3DUY%OA#+npYR^kTR{i?s~%c>YTBmvNdl^4#ia;nLl62 zpvx**@#@V>7k@7KSekszDnV(?{AUuYK4*ChtCu=rD0r-;YNb+e%#n&DPtdm+9aiZ&)@1l&mM&F&ctEh)wiRu6Bsb=yoi}}B9 zT^j$*xz6K!YeBPoLiabD$BUhp9~sk6eyQBE{_*+eb`1r)8C?W)+`jH|}YC$39_gdpYp#ooUZ4mhF40I$^i}(6H@$2h1z?$q=5S zJZsS>k2L$?JqvTYN2Y(w&fj_a^2_fxUmVPjTC&>i`|PC4E$j>4nX|^!d%SPW8Z8~X z*F|u7h4!*xxAN}Z6Z8$dkk;g=7Cf!#(elgggTz|`=2$isetQyW=bd7$weNGp@T0Tl z#o1lD_d8<5*&i#zKlZmCle^DIy6WDo=mFhEGI9p0o$~IjQ_C0Fvpc_ed-R{IaPX$; z7vZLaT=^04r_Owq(meb8lT(^Z|Kgmh8$X20-?CR7{af?Falz;tZw^4pw81j^5*ZWSC)G`D^^__l)OHz zc;eP+2OlL}o8fvVKBe4Q#s0mV<>Xl*vv%*xiLticd?o2*ezxG)^AB?}mc6r>%~{awVAJ$Z(aZL{g3_b!wqH}Jr$DlspaL#i_$|HtWHn+ zp#EUepu(<}=JVIO4@Nc}wVhsUJi{&UW`67D)_ot>*12^aHFOy=WBNdwY1gB6Dx}|e zY3u1`$5y&!b2;qy{^UiUWj|iF(aAVI`pR3O=Z-T6{(2iZzf|a;SH9GRy{}d~ucyLJ z&1t>#N_3&46WcWWO8AIj+AUsd4Qw9YU$SJjPt8cy=qY8T_hQ~3l8D|lpuUCb@+r9Z zJNl@4=V7tJ6yd#B*dK*PR0RCcVEvvoJd7=`z3;T(eB1X&eZu<78fuD0${iJ(vTgjr z^BDraK1=wj)ZRP2p;*si*G4mO@0r1ueSX_EedE9NVZ;D_F%^FGbuZtPRew0k7jgg9 zdx45VQzzG!aA>gQG)zjap@1o^z5r8Z((qSe;XCw_`ef2N@$>E8Wu+oE#s8@?TOy8?YpT7)Hc zwGO?rR3pGDMP-G6vC!A42dAir3zl^atXICZ<(lYiy{D_Dd|g?QE9igP^D*1a#DTp& zbI#A1uMHGD%WpQk?n>UdC{$wJmVF}+E!z@xUF7HL@x?PdCLO${kS(qB%q(t`O_FN3 z`caj=IhFIWAI=#3d7^f>Q}!{VNY7grCRW|ESynC-WisyVj~A<~R>x`&&y&k)vhr(R z(PpmwK5=A`?Sik$`xWP!dJQ}%w6bN6tX#Kk_MvzynU=kkEu$XShNPwlsax^)S2ta~ zO)KlcTfU8(cij3J*YoyK( zN|r*za@MXcpN&*%DRm(HV$0Qs@7$Jq{yM?-jW*gg*lvD7-K8PFXYLCx`ZlcJDCPQ% z0(EDymwk#-*v#mN)t^x*d(!;($4pD+%PENV0c#2k=skPxAX1u?^$j(TgEt?`qmnMX2p%}Nmai#e^vhwIdZ|q&b%idZggzjxS`*V!Sdbd8-kh^ zD()Jyuuv#}rpDG1-Hf;Wei{_K@0hXdx`0lmcedlD;QJMu*R!XvI_vvY7926UyO?rP zifXPH&_1(b*FN*Pf%Q`?4yd~xDs#N8Sn^7+K+)v4(cdd9TX%hVECEZq2K>ENSh#DH zvHyOR*U|Zsj#Os5{ja5?6RytpAF?Or_K+3}Z8xjg-S^F=t>1Qk{LAL`a-KqiPL7&u zr{DbE^U&8e!v`0hZmICPGJaU+{xg-XC9G=K=hm#0N$dB0T3McO58l6zWIA|SF7#5 zplZGQru}u->w=>$j!8LD7ofSms=ttVOs zaO~)6%P^aRC)h#P1sqy+m-M_pn40!JGrV+~>i&*r?(NPet%lIp_dfO#lw_jA3XnMxmZsM-go!8oGznHIHb4=~S zQe(NM7Js|1V`^vR+8)2!-~UZ;T+*$dI+oQpj_lOW&pBc}R$*?)gj&_sO_wj3Jg=`} zdsOF_huA9YzV+?Rqbb69IujJ$$b5S^Y^2Ie7c-0des4t#U31Nl#Z+d1wFW5Au zdFB4BLo;U^xeU1MV5xmc-`ZZg+`D;OkR5wy;DfHb?TuKH<4G58dw#dF{2Ywx|7OWY6}c-wrL^YjI~LHEU=f zyXkWBi^6-29{DZ$v-w_i&rxfW+~FemA@Rh(Gxu!2cPubom1e0W1(fcej{k*O3m@IM;? zY^&L;whL1fhT(rVupK)J)`DY_U{O9MPS+n+_}S2xeEt8`YNeGvY%!Rqy17_Rb#-!Z zQ?+%NGD$TchV2)}4pj9|QjLfTWQT{dW5@=As=lEPtgjo%gP5zey)XAGe(LD@>gvOS zYb!t$B}R>~wzhTe0h=){{#Fv~N^UP%Jx+f`{P? zwCw+f?g9i$=}0K6Z)l+J-(*?Ns=O~d!k-<8nqWIA3lO z+zh1f;kbGF;u3uw@Otu7hEP8OlpsFEUw=Th$gwb4rPH!HY0^Z*geX2fN`S8)+zP|y zAcws_H307LBI2C;=~NGr1?coeDSjHp#}lV)DN$>O{^6nUh;gtFLrcax42X=1i}8z$ zjR@sbNjNqEwt`p?l#?C9sR>SD{?PoxqXOoB#y0Z=8v~ahA15cw8+Re`Qv&|Psh)n6 zHcdbVN&0c5Al9RLgeeUgkffw(vc?gS->26pt*ivYV9Bt z6yq1ceg)ZBTXHlPtJ||bv>QGFKWt#~Q8Ex_^1*G-0IEOGv5q1)<*X~aI0z6qZnN@J z)8QMU;~^HMo0t#wAjBvIC{GWv4yFddA0MU21vR8Vx*u?h{Yh~M4ROj%QWT>M=oIBZ zln>Gu0IMNQ4S{g#YHNOeE!3#!mT_>sz(WQ z8S@xIsX#0Z+4E7V5aZ_%?xT#81kxy)O_^35393huQU}5?2%PFuqviTRYU4N^K@IAf zQ;WkWU0@UCDjgqK$`PQEYEYMGHAw<0fDeWml%_N=0ZDis8Bkjps3Ls6fByOdA^`#B zEYKuGoDULI2l0^{aeN$-zdVI@Rg^9u<}M3m!N*5Cjt!^+b+1qLj07SQIQ0Mx@ZYU% zQmc>(T)k>NU5~C_%t8$wQy4yb^|b%p-XIOd;DaJ5(w~F4Iyr6{B}{h?Fe2gXV83u* z+Mvf$*crpEJyFF)snOIJYPt&j%ya}b&AcF8J^@Zc4hL(25?Hx>(Dw4wSjr6z3k5Z* zW1_2XpzB8WfwA9&rH2P_8ypwB0;hEq$k2I!(fMRs=F1bRbNMBQt^{W5D?~ zl#`wj=bHlO+jtr15IVFZKgjimyy)IMnG&>i_%8+)Yim#J;}XEhxxj?O{eSYu$y=QB zT>2>S_vr8G6teTyK#IcfuSS2p{~7(c(~AH9M*kh46#vQS4`ct|qkn%|g$6)xK!cCU z%=zQo{~7%;eR1ySk2)y%|9te9f<8l!{;TPy#E!!m=5b)6NB>1MAPx2si-2>1|Hehk z9Qpi5R2fHqYxvFwl_X22Jud0ZNL`VG1(grKr-qfqt!K$yky|`8cg=q}gHWP+`qMKg zKB#6KZh!6TyyfH{-7A5AI}I4{XBvRz#u1e>4G^Wruz$u81waP>nFb7`dc)!~~m ziVOprj3P)LNe{L}LkNi%m?;%6v*)M0YqO)kOodd z_`rIrL4gP#X%j9RJkMBKw^1cVLYjjBAkPGYG{|dG<0viQ(gvf<2ZQ{$fxyU_vM4}- z#?p^NL3Q~eJ zI8*A8z%{BD-#-llqa&T(FpwDihmYh-_@v?Kuu?UkxJFPIGx(-W!Q`t)lZvC|wdk%Z z4HzF~PEDj(P)049&w_#ps2Q!E62Qu-bQ8K(EP)xPztWHxA7uqy(gu>(pxcZ^X-EPI zHI$E1r6xfv4GhSp3hW~x2V3|CDj*F~NY(~mEM*6M1j*xYJjM*||75BS5(eDs3NaZNqya`Y7JMHGeZUdES!3$908!MGKdD;-q(eCYj78#1 zOQukTfa7}=Qm4YRID-~Cf<2?)8>x)p)HL8g36xZZG*CDAfaXHE!l%Z6)y%-v$JN17 z20;@*U|Iml;v@Y@g0yTOatsnC;R=n0Nf<){Ztyvd^hVCptg2-2!)-_947~2F6O84x(FZV1(HMi+@wP&cSo%t9S#GyBSd=pQG^zOqoK=jyZmWT8a4L!t3`Ia$nv zX2UGpf#w098l)8eX+=nZ=wL{~^G_CDU`^HO=jCMK0lI-%7yxZHeEz8@-d+h@jH}~M z1xkS^PDZ^Ua4Jd{G|8BLUQQOXDGR{x1;B&y=_`xBD++k&iG$91Wp!;P%3x& zc{y45P?mt<2s4}RB7Z9CUzdW;2ciy^6o@Lr-5dyNi5)E2s;IBCrSixUEJ|MtC0oC>AF z{=@gKS=44a3oIP0Xut!_<(j^!Q~`~kz2pP>6e=9LApJ%bg6}Qk^|)_j;hb;dInV4# zZKB0Eb?r;dq9Q;H-{a=ee7X}je7;mZ&4(?>2Ri3TMRNHfIDFiK}qT-=l`%&{bZ1Er^2V-zFs3PiX5CC!T`KKH5T3xQS zMH>y*pPrshe}(S&H;(;9uGf0S0C+YD2*W`r0RpE6_@GEu@MMXcC&PY2Y9N8y0Bqv) zXv_y?mZy^7nG<3B#AluaPmDA5KZDJGVLH-*8?Hb7JDvX8E9zkBL4vr*)Xc3 z@1%PrNs=a?+)O$u_25YZ>7?T!VS2@bi~afN+olqjWJNv+9bf3<=`k-ENSqp-48~w6 z)Tj)nMx|+G;OKEOwHDaKK?HSb0VqfcBp^-e(}F(k;{)GR)b zNubiWJxre3NF(@+d_bQ=rE}9uqWiIqKF1cMbG{jJzNK-#>F7b9(FA!udg2xhpMNy5 z@6n#O*FbKZi+pmwcCa`Z#d3)LvF?(&j%V|Ne>Z!23}GveXIIEs;vxy_=>|v&*<@?dL;p9AJ;!gaGuGT zekMI58F+`w0e+kq;1Ya4h~cgGKUgl40#45L-(4<~#Ju$<7-Aj%yti}arB^QXU1^5&*rQ3NJ+D^x!jlfJG(-CF_7&$gTCePd zm&M7LA^m=8vT0xV$SW&p9Mjj_wzQW#rjPVtIYFitW`(_QZTsbBNasHpVcK(IQ!o6T z<_a^Ui%yO;%~0Fe3t!c|!VKv@zu1{Z#O~>Z4~So4hIE^~uBIcDi+ka&@3PF0{`Il9 z>9UjCdf@}~mYE@4$!)0Vm6u0);dNBH8Pe4(HBHYo9qNTg{a$2-biD=>)0$)Zd*Ra? zGR%-3apJm(>-4j|@ZrN_%#glVz0;(2=c!(}t#pDJ(lZB%n&#X&-V5(Ky1)$SC0b%8 z_6yGU!WWhJnITmd;dz6d z&5#}@=yi6~{Hk7fX8H^>q~|}?H8xy*u@|mz!pjWlCXOAYaZ{>$;abm4%#eQG@NDTD zrR%-$ip!Q}NcXVHD7CA++6#Bgv^PWgt|?CEluBxP;o+Oq&5*8>t8?!Bpx*e3tD0s= zUocko+;4{)z2v)|>6;2-|3i^Z?m0WqtL} znmH^}q+5&}QPx-go;ldn6zPjTD3|rszg)frnb_|Ik&q*v}VF6+y_owEI5 zg7iuY>;c3otGbc2g_Wqpk=0`bRqIU$vEEFC6s`>8QU?J*W4=QGbz+`ctuYRxcd&2kF>V>2JBOUwyWqHkBIQD;}qy3n(MyD5!_5PUjNXPMG=nxHBJM>>vw zu_*(4;W+*w9mmhUa2!98j^o#m=e^}|{6ad8-+etFj^9Yf@xQO~;rNeq9Dn;tAID#$ z#wv#@X$^kv`i{Ripz{?ym_fbUO8$M=`M>@&W!}q~WceAA$@`kpT`_*h}MwAV*W=fI}YEn)o3MrYVDgLlIVB`5_FYDMNun z3EskSBMd#M_#sw=wW?uoha1*NQ3DQi)#8VsPE*u@1NWx+A&jOeqk&@#tmgAW(4Z-} zSB(3@{1CKgiWYF-4lzFjU7Df`9D1+-&ktcdO&Jdy2C!z2?wCe2#Rxbiz={e#1QVKK z0vx848Qhz}nl3(}IdI@6C_e-XnqmPQmayK&55bzISObR*ESmB|u%#)sz+ne#NBj^R zXo>@HOonwEeh5=&$`s&mf~9uiMXo?GPxWWn%KZF@HWd?A74>H`( zg8d47LU-WsfMs)j2wpVBi{8KThI?<=pg|rV;P8b-IDQCzG{p~ghx}nRmLEbOO$h`J zHY`{3LkOlR!N3s$dkFmWJ_4o_1{`x?{huE~1kDox9Fef}#}6TzrbGjWAS{3K(+le; z6$2bEP+dV^ajl1FD(WfGQPfbPov52cFHtLrMxs6vT|`YJT8KJG^bfU zIzsdVwS#B|>IKmW)Ci&ts0&07Pz#6#VEdD{$Cf9pj%`lb8(W*SG`2HoW85hst&43- z+7(-sv?y+rU|XW;*^KMEaN%|dUUuB|FZ_H3-~V2pN@vkbumHyWq?qepDFtb)6FdEI z3z=H+?KLiO{=ps670q~Yq>=RT9+LeleWWqhzqs<{-1N_q^zl7_k^UbXX-xY(F(#GO z?f8s~hm@i`!I762M;g<9k6WC=N?AF&0_8$V57Y7lM_yhWY0UBGMr{(Sqs^C=hx(!A z368wHIMSHolisZPEcNLtY57@Oq|f1V5FB}VailTFFWF%+EPmE*?(=KW@&rd-UL0x6 z@%gk?1gq%4d0IZH$&r>PIP&u1NMnxQpE813^AH)#&{CRMs@w^|f8jpL^^6Oj&(DDRFUP$gQ9BIt=2S4rBiC4k`X?f_sv^>F)mlsDG z^ZlaVf!c{G(P6awkmm}tJi(Ed7e^ZN{i!PJ_(c86C|bUGtST)}aOCC1k;Z(#YQL91 z@wsapEngfnik2rh^77(HW4?c_nzLZy*0G7Sd`PJlEl+Ud<;9W4e80TmY%?*$VgW6$ z$!|!@6C8PYailTdKSOtmO?u9C>+hq%q%5XRGWpAETN^%b#3pL(3B! zd3kZ9G2g#;oqcAe+{%#$`$@|a9C>+hq%q&m0WTx+6NkCd@&xC~|Aixs`Th>iS5BUv zKh%?!Cphx*;z(nzPeA=tkovoF#E+IIIP&u1NMo*FK>JjX_FM5Sgq9~b^77(HW3F$2 zepC?sakhx2o=gE6-0k!mL<^g1V>(89BIt;9q1nwr2jP5r_%BSM_yhW zY0UK>=${p&|HAl1%M%=Vd2ysM*SEkvR1o`dq$G=$Cphx*;z;9J|Em1QeyyP8368vA zzy89J##|o*`&misXIxh{El+Ud<;9W4T>k_6TuJOVjK8!z!I762M;dc|5XOf}GJY7d zHqi0}r}O8*k;Ys48j9){Z=hN~8M_yhWY0ULe7#}Oi_}QIRK+6*xd3kZ9G1pIF ze6A$pH_RVsd4eM^FOD?k`YOyHD#`reLfuYUp5VyKizAJ>ehl-IN-}>bj@d)Y6C8PY zailTVmtlTXN#;*katCO6f+H_4jx^@_GtAE_$@~rGKeRl-k(U>zX^i!Cm>*V>`Qv1t zW3)V3e@9+k9BIt;cbK16lKHD9|0!CY;K<91BaOK}5A)+nGJlrXeU_FdIP&u1NMr6l z!2G=DM#~c%d3kZ9G506n{h*S(KiqCRPsKjj%*qvZ*Xyu3KlnEN;I{#FU^hxGdw%s**)f+H_4j|HsuYTK>wBXS6)Q zk(U=o8gqY;tKYQzUbmODJi(Ed7e^X%|C8IlXnC0b)A9sIUS1q&%>7Yr|EA?53f|N5 z1V>(89BF7@$o?tUzR>c{79F%a!Rfp>_UR9fH0J&=*S@i^{d8Y^qU8yWyu3KlnES_E z`%25h`wJ~kaOCC1k;dF#=Gu2!{)@&>TAtv@%Znq8xqr?bUugNp`rouX!I762M;dc~ zojbnK@{f)A=zdIa(89BEAd0`B-v%U3=Yq~!^Yyu3Kl znEnUc`2{V1q@*7$PjKYr#gWGJPvFjPX!-4nL}+<})A@7eM}Kgn@%Ufh{EEim{gIX@ zIP!v8{e>fq>7T)!-_i0J>SDA!!I762M;gR`(89BEAd74H0+mWTIWTAtv@%Znq8>EFVg-_!E;5i+zq!I762M;gj( z%Nw%>(eeaGUS1q&O#dG4`wcBWR$88xCphx*;z(oq|8U>0XnA;lr{xKbyu3KlnEp-N z_di-*?4%+sPjKYr#gWGJf8xGhqI`OH)=*lW;9PlpKmCIvjp-i+&xh~7bp9XQm1%i` zBQGybr^)dDg8IStZ#sQgf1u@we;D%e;z(oqhe7+``#+t3!>?hqJi(Ed7e^Y?e+={k z*B|KoFVqdEF)mlsDG(|-``1Fk>O`De*#(DDRFUS1q&JpP3&V!skz zYtr%rM_$mYzi^~6{SUe8Z*=~!{zJ+hq%r*~!9L^qA6ze( z3-c3P|EBZrNie15as3u(89BEAdW|*I{;Qf)#zpB!TmM1v!^5RHi z`ai?`824Z3{9*l&mM1v!^5RHi`gg2DWhxJcdp5VyKizDs7 z+FuMi;6%$49C^9>gP1;rIMNP+(?5Dwp!Yw=#~*jC1;Bqo5dP55LJ0P*`_aA@B5>Cq z{3y`Z0)O%cf6RyTs|}*Ck1a;~THwzkNrE2*`dUaqkf!&w(a!>Z6k#CkYat7FgTRjh zeJ$i5$kTh==w~4h``Ze%uLYj=!~>7$YcUjp62y2?NEw0(#JK0J3Sk(;=xd<{VK}|_ zjeZuxVc&ZM?Q1a-?nZ$h1^QZyhA@WS2S-1PF|Y?dmiD#4W8&k$kAiase;oKv;GQ`8 zS!lz4xDM@Wp$B*R;75VJ7ULlp(EH=)XJG()S1AU+K75eP>iJ_7ME2*)8l z2Js09Cm}uo@hJ$WAwC8183<<~J_B(ngmVy=LRt2E^46*FdO+xCY`n2=x%xLEHf0Cd3U8 z--2)(;#&~kfp8b%I}qQ4&Y_D<}T*c-7IV(-IVhrJDZ8TKyh zRoI)b7h&(gUW2^_dkOXq>=oD>uos~Aqt>Igqn4v~qgJCfqZXs~qSm6eqL!j|qE@0d zq86g|q1K_cp_ZX`p;n0E3hqe!G zA=*Z?m1sNBmZEJ%A1JiFXp7NL3T-voZuF%>+m5y#Z9k3$I5wb97LFY_mZ0Ajjx{*; zpl=tBO*mGe{}+yBIJV(fHxd4DEJQyt94m3`L|-!;TXC$#u@}c;9GlT+4aaUA%h9h5 z$9f$5aV~&!1Dq?Me;m#waBhJ{r&G_&GbKF;>5q2 z>EFb}iGMTGe}{<^|8Ay#5ECc<;Y|M{CQkgbnf^6QocO0R{dbr+@o#7P2QhKtU(WP@ zVdBKUp2xq2%aYC_{`pM*9i}|-k7xR)FmZA|fa(9j#L4*rrhg3+C;t6R{}Com&Nndq zQ<(S)?)eC&{|gf*=M$Lz9ZZ~@&tUqGFmZCegXy2b#L4*zrvC*KC+AC;{vAx5oKM-o zJs-l`zsdO!rhf!eo}78pFPR_S6_ve{7IUk4nZ+K3FxBbZZH0J&`Q=Xj9WA2|b zadN(oxj)au$@x0w{xcIN=L?zp+f1CCPh{?&GjVc0khwq1#L4+c%)hVkkDRakdw-jx zkj`SBZ^Uz)y!A)UcQW^fi8PMyIA{Z%GT&gU}sZ<#nbAIsdIWa8v}FmwNtiIeli%>7j+PR{o-_YavkIp56O zpJd|Xd^B_alZlh_$;|ydCQi<0^XwmTS<+eLd^dA{k||HlS2OqDm^e9K&fMQ);^cff zbN`Tulk?%s{V^s^&c`$N-6MYPJSN%$1gnR&)a{< z`F!U75>p<>Z#@6c+`nSt;p8UQ9bAO46lizn>?w>Gm^7|Id{TU`sejkIm|HH(|?^7`MH<&p2 zeGcaS2@@y3@4?)kVdCWXHJJMkOq~3_2y=gfiId+aVeX$Waq{~h%>4l-PJSPSx&Oe# z$?vN$_cxe0`F#`S`aKgTzwg4_A7J9-_hFd(4@{i=J_~bwo{5v+zhSQbGja0!G5>SE z@qf-Y{?GYF&MDjfIp4@R;rjpbd?Rx$j2{dX`92xezsT7~C%A{DZ2Ai0u8$SNC*Jns z(xbR^{KU03t2r;rtmd%GUTn+!Yu${{qXuT2>kB7z{CvJ^iy5yxp<_CI;e?LQ#oX7# z-@Zl)(l~4KoacPNT?@qWJ%xL$1H3;5_gD_RzXSK2^xz9zG2F7^XYaD6R{~2q2J-CW znKNcm68isiry%XFJj)@nvs+j+_w&n+u{Vp&Q1 zU|A0A-#vmn&w>3Tmm&?zLfVyy@7_{H>jJ^?Ie2lTk@R_SOdo0gX<3==ie)10dY@6& zM?7U+z%46&lCm;%EFH>9`(|J{p*&_p7xB5^N+&peLHzG_{ToLff^+oZADsLBKQ3zv zlmjnp*Lp@-v5m1DM4y>DoX9OJej*Pk3ny<*If0BiOzRzKm%eb&(Q<-=42se9gf|!n zj&%v&k;KGl%75Cf*hlfgGGY6Xc74JqE7k|Itl`|U;wP4s)H|jFS2`!8R}wm@3k2ax z=Y;f1LjSw0f8(h8n9e`_^&gzj|I@N!TjGUf#qyD|GIbdHE-5Si_Y9&7i=eDTek0tY zF5tBu?$Ng4l?wNyJ~QDS>l3e~aF6-nwF&MqKfDUy9`nPi2<}NATLJgPwyc4B>~DBw z!~Ng?#m^ur^Vv!$jux0>p8w+a=XWb(*zjM?V(?#j{k#8{*f!(gb=2sa-FSI9YIdw`_Xu8dksrL5^3mRpzFj}R%@8p~s`|PL&z5WBU4crBEuN<))yt8DI z?gNo&>l_Xm6-T{WvNJz+()CUCvGQMYXU__qUVmfk)s2~fZJ$1jm1TXBnD_MKiJGVz z7Y8{#wmoaVcfDFMV}4b=}#Mb7#)HFIYQh^uZjlg&VyMZhV>7QSS4k z)bK-?;)LyO-y7sBd=7Rglp4M6;y-Vm|KRuNDVMtZ6u%77fgg_h8sl-u&#o$On`7kf ziy%HrN&D6>A3<|CapVV zoj!Yo$y`y{%eIauYMnNUtyulA;z8>BwO)>w^|ZsK#tx!;xz4h_8%t>&|L zvi6FE(reXicP1a`HzSNya`$_5>BfDf)w_JMSKHV36AqnwxB9}qrK9{KCI@RL&Tz^+ z6=rZj;Ka{&L$*t=IHw|=eA!bed*I^GfjPQ61Y>PS3#-e;t_WDFUnT8yUp0M$yI#;x znaLIzIXjn?zSLc=c2!kSdaC(a+whwHInvP+f@?YzTFunr#8gi6ujG3kH^skh-3UJi z!2yxMnIgmI6;zGCAa=RKnaWnk_Lt>bHer(N{HzG~fuk~?=d0F;^cy1;uzDc>IsI=} z`UfmG8aglQO2Mdknb-3tO;A?3c*&tEAUZ@Wb=tZU8Ml{*4nOQG<7FrLz_@?MEG?;1 zdJ%4G#|T_r;1v|6=UqL8trou1-`KnL!PGS(3BxO0$1@w6CcF$jGTYJN;I6fz!y9ij?iO5rcYs(;{PvXY(l$w% zXDhp0YPahb6dNC6%f}w5@o}rS+5IYJ{X=2nEpN5Ai44xHiks$lcNFXB%O82C^_usF zN!<&%eJsi2zUkS;d)pq)jn*w+Xndwr_LJ<*KFSyjVbW?A*`o)mlzxJv576L`*X?NpRWJT;ICk z7)$=a%=JcFE)EvC-X^_*9r{_m<5}j^$}`7vZa4}Xdp0(D$;x3;nL+Ox#MN9QM=oNQiVv|zN-Z`n5^ zPm~0N>>o4XkVGhJ*rE@w-wDsS=5aXEBKW|gAI@8pp7C1=AFlqzpWHpYG_}&mVfN~5wkZK4x3dupS8ebmglzn`_0}Ryt&xs zq~__UJO1k(Kk0d;cS#04AA3f0K}+`RMUHk=a_^T*9&@)aJcoC#h1xf=kIjrf>%DwW z^Zu|Vb>$=GFH;Y#nD~IN#7MiixGE=FUBTqtw<=EJbwELze@5YaH(1m-gBnWA}E$x4S;ML-uyO z*?RNQXZM+V9=!fCW`ZqedeRvmls}8I2X{o@IwBbfR-yKlwB19UR=H3sr$9`e&@W}tE+S7PMn|O z@@e_=^~noczGWtQw|pypC3AJpo#%J5jZS^8FZDhw`RSfP<4>wxdY)K9LZbZB{*Q`f zo_q|eQcOK69iR}FS z23?CC1FjPg^IBURvA%?&=X!PWCAP$b3 zC;LM)-~HTG@!5Rgg0m5y7q+I= zm$&-huWtX}zAR4f$!XmhrJg$XP20k#qIZK|e_zn*L0LQ+UX#}I?dLnuoIx&~%l)IO zOLJc>bycoNUeNP!*5jM)t99!R!2f?(`npY};Mddaae1d3hEy)NboY(6vkc2 z$*tcnd|7a{*(>KQm7Zvo|9!#jipCQ?-QB6LjCNgA4Nj2QFF>{M8Mg1;u+!6OQ}akS zVJglqdDZ))8*8KV6N+8@)|8t*G}R2w_fk?7qn24FkN9$5cW1cmx%}pl5}CE~Mhj-d zcQm+4nrkNvk`kff)avYeK9;_m`Fwun=eJKgjdP5;roPFB%=ZqjX^x#z-*vBOc9cQb ziK<2Cmk!>O=M>d>K%(lavDW}9WY)!@Z*opeQ=%F!M7NuyGW5>%aHl3dK%+@kYT>bCQA+juHHd*apw_kZo1v2UjS zmK@>FFY`L4>b75nyOi+aiHnvW5IB1v-Gb7cG-}H0 zH<7mv2o&rP^Ixj43An2kZF8RUV}8r&PlajH)DA)09|2bWr&lItzq`9QjIt4bBHuPv zCb?zCY$3BxkMwg3)=pU-x?F^kQo7?e@wdQ6j>f4V&QG2YYrxFR35!@?-P;r5^^JN|*U21agHfqw^95IJG{D7ni$-9Pty z&3okw|4iC|#ZSczZOIuRlWgJ}Tix2warx4&H%-4psD{pEtkFYXy_?-RM`a6ZUh0m@ z&J9x3w&CY9x7OvE)Lb3jUHvO{*wcL9e&>Hx$<>S&Y~vsKIj&+tqHwYJwoTvltv9F5 z_%e2Keo@DsQLoHy{^|~G&Rf4M@k#mu>49$jRA(m^r_KK!UecB`aK-BTzZSmwI#2lL zVj*fv(T0M;DR+Yp?CacV8+PWNg_m~tu4At3+*8K2re{N9Zpa)j`X-n+Mqtr}%&qxS zIiDq`C@F0G-p#N2E{H1l@maR~bCYA-k3Dx zdVAmQ>fWdJeB##?W4u2orhn^)trBKc|Kid0)K`rHstpq>BUA5Pt66I#aDK4BvJq5W z&)c#)%bllA`@KD-z3@_d_zQ3A)_S2T8EEguqVqqK+7rAL~C%%4k`d%|wY+P$L^aKVH>0 zZ&sPt9;M&2V!$Y>E<&R6m!uR`{p;XLU+>%vl#25F-zzNg0#!$Tu3DT}%o;O%`Nz{Q zf64|?nR;oFleE;?Bd6ESH8|Ea>m{q}cQW<5yz;E*Hmmbu=`uxIx9w?UW#3+PPIE#{ z;~N*5^&$IXU>H|@d;?VZJbEgM;Ds#Wtl-2G)`XsTBCm+3u6YPG+13$*pT zd)2xyO(MOzacpQ+i*)mZv8o-p-W|G?BiR8W0Mjmp&a`dvM_@vbTBH=KGgjPe(+*ni_u_1TnVS&2_2 zfA-wmF!YUcn}+buDbF?D@Er+qsOgxe z(tUPTNwAFF%i1mo3y#<>sHv0qPR|dvmNS;CYsF9>u}k&(a6^xfZJFS;?8K)O$~f?*~UmXuaVV$24&IJJlaEH8GitJ3x`N z2F?z)1$XRCecilewvz{}X1od&7As7|e0!`~U$$pWo4TqdGWL+ z`|FAN;FG&R7LnhG<3+<7op^bzuE5aQ!f{?WJJj~fUP(k1E)T9QavZ=mc2$V6!duzO z*p9BbZHI5o85v1u5^CHeT+h9Eq;A2j1TaLVL^#O@F0Q5dr8+@^@Tuw$Z=5yu3(nHz z;F@zI(dh-6vyy~s$FU_pQto#}$#yNrWEpt|1kY$HByM*AWQV&U_^}gmiy~*@MzGj9 zXQ|A%%*zJ0bg`@gssO{uUI0whZiYV0r#{c%;f;Ll@!X6Vvx%IXDAbuJGiu!m+_20O zg`(VzU%A0Lj3vVcSWGkw3Juj}c4PNvr)p_ujbN!y4t})O6}mjH4)gm_9@)vPwGg|Y zKW?l&(&>_(@WhE0m_IV323P#O5%T3*zOT)g`SZ*MmU0OW3eu=o<8Au#ROvwPUaMxq zo(Rxit}`dBuN4=oa*L zIS={*7it)K1MMY4lFpCy-Cxtjp38J|tvx+UbCwP`NgFSB!Ej;0LVGh5m#N$(beGf{Z zyz$g3Y{>_4MsYpidkWn}cd4d`usQGsG7!fC_T(9X6aAzBq2g#gtlKwTFX4WGjr1dx zoU!4~4BEQuZs9t^LZn=^ZCg^Q+fU5&4)E7%LMUosN` z_W9$^RM?0Rr|%FSV2>Cs?lschHSG;SUz~>_I_4PG3`abrZ%EWl>{F&0x;jKE$}N{M zuQ1*@hwqw8Uq>{grt9b#-r82yn!j_tb?aLZCrIYqdPa%mD()ht#yVdlLbRXeB}5W; zQwcYLkC9H&N!Rn$?=v^Qjbx{sH7Hvq%(`@T?L2$=oKX=WF5KZzg&6lV-zUrnSQCTI zt=xiv6=z|n@IyMBL#;5VQKkM+yS0Adg~zbdkhzfjttb6+rKawIDfysXqV6KMu9;fd zv%8^9)SuR7$E3@zGMAu~b_Ufk>rzj62&h7{T5Gu1a0B)|%- zED>Xo=rb^nSqFzv358r?C{gJZ1#Mjpxgw*QBzIe3$0D;zUK52SdN5RUxZ35V4{Lu> znRZZ$so_X^`MPV*h>OdnG#kpdf`4EEhImr9vc&7*t-Ed-n@=r8sqbwS0V^L7N#pvX zqR~jniVW*;cRclu2spU^xVSEBfzM}*63bUR2HEbjV%SaeP_@gn6+!4#*!OgKVgvz! zEm(Le zmxaMqIw|67e4yzo8z<$9NFlA4jB-{Zf#ZRcXDoy=YWreeYw;4U-NlX+@lKZcT9jSr zD&&Rt$I*rM89H#~e!)iZe}CDa(7u0*TCxsr&ud^ZImBivB@p&%FQljWg3&%4X9x=y zC8p&D!(NnD^*cmwM0NA)VII@P#}8mur#c^r*db?f^WG8qi_y*dDG-svIjLbypLl6w zUaTV@x(ZOP&5f0<<|t!F(hVb>233MWGc*g0pjJT}*u@z1ZtW@D#%G2_w(L)cVWyg5 z%JY;?6&1j6woo$BcLq?GJTaJT3yR4d{H}R*GpG<@-2xTT7mJ=oQ@KP}+UW;0Z`54w zyk5bEW6KiA(-kF9DujP1Vj=Z>#sVsOXu!7PS9nJ3*XUay$QD?fD*tiV{c5etv44QM zWbOxx3DFxEqILZe9~fc4Q4Ct9GB!DMZ-vdE=a^(sz@@JGS>r%dX7;9nR-)zD>FXdP zU#)&D)Ru}7q}&A_?aTB)qEA{xE7wv`{YkAwNXx_K$dS0G zf-79oCsOA<9V5d5_IN?|UW(pYB`Nk0Ltypw?nK}1_m;XljT}FIOpNlifk~cvS5mw; zEQ0$Ua?AN_OP?wYIgu%z)$=W{3VpD$Emq`qhOX|arflaDsVw-T?~laS6tuQEz(Z@o za7L2oVXbzJ;ge!*3(Vyj?I)k*W+gtB^-+pS6y91 z!J~iFrI8VZV0>Uw;IT-sc3(=(Br#wm*FSzk3XJxJj?e-pN~{idYoK)WW2VPE>%P34 z{hU41$M2yBZ<-%lf{$)~3X)7xkfr5o23zL|vG33GqmX?V_1>5VCjH(su`XSukG;mE z^O6SNSf`#q(6+n!ywyCXo-Uh)yKI1kdW`<-)wysxaNe3yaeZf}$OIN?wqIDZS;q(t zHPzy^!~}P#4$VOUoYX-cyL1ke!#x_osh8s2F&tMMn`_zD*6Op-bD7VM-G^zKir;Zt z4%odq*G2t~JQS`dS)uh3#(ZTb-ts6!y(R>4C==wNCX9pESqzMvt=>Ouz!c%jV6m~E4(WRi!QPRW$m z>OQ|?RB%CllS@8SRg3wnTqHJRbRa8Kvm(N%h;hQ+R8=DS*5f5FV>iIfIAZTsWl1Xq zg9eUN1azd@wDSgvVi1LOd>p`+1$Xz0mYCNPRf5IZvCMqGK1ur2T76>dgdUcZ7s@~D zH(dN6h@}K`NUQPqW&$hp`^!5AeG^TSmiY}ZkG+|h0!`Zlb#+m5sVt+Sc-gB%@;X7~ z!_+}l8A(gxAy@Jcy1j5EqR>%h+E{Q)DLV=V>5H6k9^MLgI82=-qk#y z+hz}%&7t(sV}^jw*|0Y~n$>kmR#!H}4KaWuz}a7qbM}qvho+r%D_F4)3b+M;c3C~-ny;2vmpHv{vt4%^R25CeI5cb#O86)1F1 zt2X-aDgJR99P3Us;H+7A_NhXn9&GZ|gm9h6P_*CkKODuy%LRWWm1t9dn168wC_+_C;NbTs88R2E!1e z$Zj~BX%(m+b_1kCaA?fXNO@fjn$)laQkztx>oewJGao2_!~`@K*Q-Ri`;UguBbu** zT5;nqjrz+3I`WgE^4e<)rXJvX(1dCH#b$d!&O}BNiQ^*~-_#tUm)6zGmP=e&+m<(t zl@)ocXwUP2H497ks?Q+|&`=x56Mp>i*oqx&Jxpc$-G}WQ3%o z@W)~w4{sR4tO!F*$S@a)Cb`n#Pm^R#v~wAg_qPv(`Al=!$+1q>UJ@Y{x@o$vuMnM} zFz{o!WrSshmmIp4&;-BdOf2mv?LU-ilVWXmwl_3E~kt$}3 z0n0-TYMxa~4dvN?xNo)|lGi|u%}ib(`z1Twok_nZz#uT5g!yi@wPU-Z$)xF0LL*kQW>?U0~#B zn0$pilvMt>y!>q*mdZ31<923jOFfInwUH|0iCgt2t`TP!k-EOovpgK6YZE8jXwQp+ zm^U3)&?N1Q-JbaM6Y0aNk6*#9O6P0o)`lG4&!UM~cUm6Wk`NYW%;t4~Fb^q2`I187G@capOd9fpahu;hBv ze5>>ns5Ul*r>q~RfYe^SwSvn0MPaS3-j2p1#@?8Y_Hy1;+4d!y1-8`Ss2$fG_iyc> zn-A2kf2^@xt~vEpYy=BSjauVdC7`9012MuedeDp(k5@dg{^eOQs=T#n2ca^PvM$lO z6ciPVQ-IL84B?@o$ZZNkLi0cqrK`$Gp@Wdm&#;V7QQe^kkGm}a(x8cWKnR05OYoi@wL9Xag8@pAI z8TN$I&7vrXY{@t1mN>x76l0=?G+)?>3f~h_Ja(yUaKvs`o-=6I*r}?6mh=RnJ!$0C z;cGBlimcrf?L>GoQV)i7oGLYqTnjT(jY&E;9B9;QF1sW2bwUikUeNeNqSZ_>9%hxG zA(`BflaBwJJ?^l)uiGP6u?;=qc{Dv!{ZnGWp5N9hDqb54e{Q1tirql(Gv zh9y1U50-KmN?eUEx!x=5nUT^_M;aD1X?U#xn;rY(o082%o7L%pduYf2gBu-q(uX2j zMD7c`kDyVF+x(J}7ZUa2wzA@-LiieDz8OqDI=FRK{=?~(q@Bpf$WJu^o4qe1kgy3U zZfsCuV`6z~A03c(be*81<1Aiqc6PoKaCrAF`OYe{k3QLI;V!)nU?{aaVJRq^|0tM=9kB55 z@Kltc`_7u|(9_fR(Ql5Mv1x^jxEh4i?ta5sPwJCQT$3lqUT@5&K1t;?+os_vUp~}T z{iPwMKBG?lsVPzDN#ZiZtD9I$S_W9^2bR9jZDfjkT6Td4>8DShVo)d)^U252ewP?_ z`K-TBP^4EiF892pCqJ;pK?`}$v4+fR=H2D!&Q&kd0anY2)~|uD)xGOm6^r66d33=% z8#ET%^FO2}2f-8AiUO`R%$eoeid(%ijH}BmI$2_JBrpZGCp&P( zSGuOPozd-ulvta({ zLuY*Rkn@I0A@w`2!FLYbta8b3S$#dGXg%_7BfIm$!f%pw6iarxnfSOoVq+d2l!-x1T#d`#ae&T}VPs+H6ogM}i%(rt)MlkU03hny;= zamyCk1`fG_KH1k^>MYY!U*tY-N|b{j>Dt~<>%j$@$lDms~UPcUf$oiEWKZKiPiE@-w0XtZKWklwMFKJk@+1r-6E#n`50^TO^Re!9sBw)4e2k$CO?hK7L7VNELdVO@1FDoc-(XBhv~S-9e*mZt7CPU z&59VC3%#}EO)QxljN!6&^Stlx6}!=*BajrI5-omeNLW7UuZy% zBRm}l-?a6R9}n{RF~(TaxRDzo_PHmkDNBApOf0~0wV95?EGt&qIlj!=RAaVN(|zNm z%W}R}adn@hrn_}!z6P2lPBi~#K*O3x>szCWC!bmRsmQwvXo(LstT#fH!`}a3?)38U zFw0SZBTmw0)dhqjccrX1c%mdYKc6klmwO#IApSCae|b4uGI;aiJDL5=kA)|P566Rt zJ^d(9%bgpZQg705Tptj$VE#<_!lC~=ONX`O(JCIa$z_d|m1&7Yxj(LP=O|99hY~17 zC>agAg48y4|1sHH6GKvtEvaX}eI9x!k$zEQZ~r~l-5LeoS&~6Nh0_Kwz#MA3cT7XI z#Dm5~)Yd_r2xdeiLaKWVbW@XZtgscbnW!Y#_H1RX-9@l)jRRV_@a=&6i{97wTh%Y$5*)S za_6_xLQyCwg=4@f44TI0mpJZ?TKT3p{rH`mj~+E(zoC_2oTfyud`unERxkRdj;1zE7(0t`-$eD!8 zBAG-pP%tuW<5MH)E~g~lo;Tt%wdaWdmdZL3TAeRwyhFb~7Fp+fEm#2H?7mdK(2E}! zVVqvMAMCYb?iI`na2ekf+2rCkGx`i}G{KTz5i&Gc6Qubc4WZB5~Id^Ud+@6Am~&)?vQAC#GDS1_l(7Qef-)a3DdX zFkcg`<$9BS?n{K=u7jLp8$Lh+OPd2GN356wHxUK7W}HogmBToMkq|VPO3yY@Si*-^ zD3U_ccva)oP`E(JLadfpI)+$atV(|)SK{sayS}@#hV7ZcZH?UtOU^ltM#msXkJo<& z2+XiDXIB&W6`h(y+%CPV$Qa@~;E_=3b~2WkJAK`I@fZN-l~aM#VcRMOTB)CZm(9g} zs4IWpOzy$i6Pgb*uR9nJ?bYn<6ZDbvzT$k#UCJMudn(Vd?~xW1oIR`ztpjfLU#MJ5 z)WG~`RLc#hdQjw4?C7|mo1ibImJHn%3u8(a(rlGHt!5m9h|ileP?~K8- z!soezxm%uBe;A*;=ff0XN3~)!OM5Cw`f1}_)Dv6E>VW#N2NeNc=!h@VLJo@dzFkR4dfpgyYGF&~LEr1mC^jg9h!{eu6#j4hl+ruT$N&!mD*(qy zlHA_}Pm?8U-fu%6Zey;Mw0hr0ca0fD8)`Pxz+kU>nBfQ>ju(uZ#Dutrc}CH4RM|T( zu;=(K3er&HV~z9Wsg_r&iNOTp{K3lRplsbqVD-aD<t>rLkD*_WUi?Dm&0F6t6>bZX5UqgJJcqPsxk~ zy(D6Er+Mc^V7gb0G~b@2S>0ghnc;7z!4>p2dC^2s+;&QR@%@r@J25WeEt8|$!qrOm z(IBnB%hAQ_0DE1Du-^Rb^6;ZqQfU?Q#(M39VB!?Nw{7l|wjgw1(ma78$+SKp86%n2 zy)pr(7qz;~>mWz^ipsq_`YSg6f6sT&D3%;k!f_3(K@$b8DMUeTntaa&9v5?|j&nVP zX0eiCuNGScc|TezYLp4CY{d6vFFcA-4a{nimxo@h_GHWyv8KHc7tn%dEd)!{{ni8frLDkTEJ(F%GG* zJJiXU9oJcR`y!n?XCiSg5EQ6L zGP$xBqRvE^P?I1wg|9&Eg7*2B%2qdy2Jhpq!0|uO#&9%v-_{Xj>+I$2>LqA{cKc63 zoCLq+eH5HDnD&7I&_)2DIoTr#0hXR97cZeB$b$G&{|UY(TFdPdg7#ve&1=?GTcp(8 z=nE(H%G727(TOo`i836sL#M>O)99B-yA$^-g}Rx}5H8Ocz(RsU#>Ye2udF_(c*l7^ zygX9u>FBhrOysAMo2(I|6Jzo-gl1!U53UJlUAWBHa&2UQN&TMeG-al_p{6${zh6Xw z_G;+M$?-|7K60QhFj+}ZG;z?9??7jCvqm8Hf}~QCS$X>tTWkAv*6QI(hFBi(CXh4trRbl1|!ykCj+6^)JdV|#VPlv zwMDa*+L7EX`9#j*#jT8}QqswYamS_1Z*RWbj8md95(#X7#gfifqHa}V$kbP!bD~yd zoXMbSKva;x<3yu+qp&fpDG}`K$Gm&?nk=g)+Wd^-W4f2#y>3d~Ntrblv6*^RcUZ)&TL19$!uu!HyA(%5 zeXp&&V~T|o1;6V-&lJk>aJF_p33;JBucK{IUP7)YjJ2IL##+eM-4i9?0fhmhJTYkK zf{m@PH3}&qhO`kC7PA+(leUo-7L!6r+oEhFq>&;hK@TriJ6n(cqyfuSb>|d(U0o2@ zQJ!8;OWr1QtOeg@uf@G_OEP?rJT7cJsnmyV;`t!v9AJub-E_1R+x0AS&(MJzcFa&>=+H z-=c+(B>t^f*&$x7F(?OjPd|hZo^Lk{#LE?oHOAZPmlOJa9w_{wo$XKJWo$iBP$@eE zM7#_Jx+cf|_Z1^imGk-++2uu$BH{u_5doy6zKF1lxTuV{n4qvEQd9~lW_k`1+YuZ* z@Sm9Oo_|&&WB1P(?7z;S?I6m~_Gr}4TbxmTKJK1&KZ%42yI8w9cteVWzt_du3!?=+ zQRr4X<)3W*U7hP+XaAq+{u@aCoG0o!8m~)#5$WY^4aZNiyd2RUUVQ(FHcOlTPy^JuSwmJNi-42^@(G@Dy4If7 zUhXbvJLufs9!FF{P*_kzMDpLU4}tubRs6fMP>#fJWznv9-}>|Xzn1o&KJ&Lyzdot| zAhzEe^gjfr1M%t!87l(fAERyU3O#$FUvl|WQSOkB_wJ8Zz3@!k@hk6w(_^!&4?H3~d^txVu`%pnhoqxwt(1?BA z4wXkiSWsvBmu(;V|Bnu}yngHn4zrSw&P*Te;EI<@^1?3ju_oM2gCVmvq z)7rxkZR;gu52dg|%`s%vzf13EQuD9u!fyX-tfMK;zvCUvp8XYX{>~A+qq(%dvv@QW z>sOfRs9&`DNMhFSq~J?IiuH4YG~t&m6B?xcBKITx_uo;D_WpkX0r;WrFDUM6^zgK^>W&gGMrCR9Ad;fboud6|X&tCiadg2<$#?%_{NB;}L C{N0@Z literal 0 HcmV?d00001 From d6f4de3866d25db899edfa1d0f0e20b1ac875f4e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 15 Dec 2025 22:13:30 +0000 Subject: [PATCH 51/61] chore: auto fixes from pre-commit hooks --- src/ansys/geometry/core/designer/design.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ansys/geometry/core/designer/design.py b/src/ansys/geometry/core/designer/design.py index 9787b4caf5..78c8b42960 100644 --- a/src/ansys/geometry/core/designer/design.py +++ b/src/ansys/geometry/core/designer/design.py @@ -1854,7 +1854,7 @@ def _find_and_add_body_to_design( self._grpc_client, is_surface=tracked_body_info.get("is_surface", False), ) - + component._master_component.part.bodies.append(new_master_body) component._clear_cached_bodies() From dc30974248bb6999fc86b490ee95b4768a37655d Mon Sep 17 00:00:00 2001 From: Umut Soysal Date: Mon, 15 Dec 2025 19:16:55 -0600 Subject: [PATCH 52/61] Update test_design.py --- tests/integration/test_design.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/integration/test_design.py b/tests/integration/test_design.py index 12d0607526..91d72d2e36 100644 --- a/tests/integration/test_design.py +++ b/tests/integration/test_design.py @@ -4341,10 +4341,10 @@ def test_design_point_get_named_selections(modeler: Modeler): def test_check_design_update(modeler: Modeler): """Test that design updates are tracked when USE_TRACKER_TO_UPDATE_DESIGN is enabled.""" - # import ansys.geometry.core as pyansys_geo - - # Enable design tracking - # pyansys_geo.USE_TRACKER_TO_UPDATE_DESIGN = True + + # Enable design tracking explicitly for this test. + import ansys.geometry.core as pyansys_geo + pyansys_geo.USE_TRACKER_TO_UPDATE_DESIGN = True # Open a disco file design = modeler.open_file(Path(FILES_DIR, "hollowCylinder1.dsco")) From 6f00363ada0262c5daf882353d60a90319320a11 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 16 Dec 2025 01:18:08 +0000 Subject: [PATCH 53/61] chore: auto fixes from pre-commit hooks --- tests/integration/test_design.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/integration/test_design.py b/tests/integration/test_design.py index 91d72d2e36..d2443c1e85 100644 --- a/tests/integration/test_design.py +++ b/tests/integration/test_design.py @@ -4341,9 +4341,10 @@ def test_design_point_get_named_selections(modeler: Modeler): def test_check_design_update(modeler: Modeler): """Test that design updates are tracked when USE_TRACKER_TO_UPDATE_DESIGN is enabled.""" - + # Enable design tracking explicitly for this test. import ansys.geometry.core as pyansys_geo + pyansys_geo.USE_TRACKER_TO_UPDATE_DESIGN = True # Open a disco file From 53ab46415057d4666e47af5851b7ab6df9a04a87 Mon Sep 17 00:00:00 2001 From: Umut Soysal Date: Tue, 16 Dec 2025 13:16:34 -0600 Subject: [PATCH 54/61] v0 clean up --- .../core/_grpc/_services/v0/conversions.py | 67 ------------------- .../core/_grpc/_services/v0/prepare_tools.py | 8 --- 2 files changed, 75 deletions(-) diff --git a/src/ansys/geometry/core/_grpc/_services/v0/conversions.py b/src/ansys/geometry/core/_grpc/_services/v0/conversions.py index 816bd08daa..b30decbaf9 100644 --- a/src/ansys/geometry/core/_grpc/_services/v0/conversions.py +++ b/src/ansys/geometry/core/_grpc/_services/v0/conversions.py @@ -1428,54 +1428,6 @@ def serialize_body(body): "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, - "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, - }, - "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): - return { - "id": part.id, - } - def serialize_entity_identifier(entity): """Serialize an EntityIdentifier object into a dictionary.""" return { @@ -1485,25 +1437,6 @@ 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", []) ], 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 a194396c41..ffbe6ef9cd 100644 --- a/src/ansys/geometry/core/_grpc/_services/v0/prepare_tools.py +++ b/src/ansys/geometry/core/_grpc/_services/v0/prepare_tools.py @@ -66,13 +66,10 @@ 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], - "complete_command_response": serialized_tracker_response, } @protect_grpc @@ -88,15 +85,10 @@ 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 From 4bfd4d65b4db69c8107363ef3684e1c123ad78c7 Mon Sep 17 00:00:00 2001 From: pyansys-ci-bot <92810346+pyansys-ci-bot@users.noreply.github.com> Date: Tue, 16 Dec 2025 19:17:39 +0000 Subject: [PATCH 55/61] chore: adding changelog file 2359.added.md [dependabot-skip] --- doc/changelog.d/2359.added.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/changelog.d/2359.added.md b/doc/changelog.d/2359.added.md index dfa64883e6..11e1be1648 100644 --- a/doc/changelog.d/2359.added.md +++ b/doc/changelog.d/2359.added.md @@ -1 +1 @@ -Feat: tracking updates +Tracking updates From 7d0f611c5411a5d4d1435f70ab2d974fab481d5b Mon Sep 17 00:00:00 2001 From: Umut Soysal Date: Tue, 16 Dec 2025 13:51:03 -0600 Subject: [PATCH 56/61] Update test_design.py --- tests/integration/test_design.py | 44 ++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/tests/integration/test_design.py b/tests/integration/test_design.py index d2443c1e85..add9700d89 100644 --- a/tests/integration/test_design.py +++ b/tests/integration/test_design.py @@ -4374,3 +4374,47 @@ def test_check_design_update(modeler: Modeler): # Verify new component was created with the extracted body assert len(design.components[1].bodies) > 0, "Component 1 should have bodies" assert design.components[1].bodies[0].name, "Body in component 1 should have a name" + +def test_design_update_with_booleans(modeler: Modeler): + """Test that design updates are tracked when performing boolean operations.""" + # Open a design file with multiple components + design = modeler.open_file(Path(FILES_DIR, "intersect-with-2-components 2.scdocx")) + + # Check initial state + initial_num_components = len(design.components) + assert initial_num_components >= 3, "Design should have at least 3 components" + + # Record initial body counts + initial_bodies_comp0 = len(design.components[0].bodies) + initial_bodies_comp1 = len(design.components[1].bodies) + initial_bodies_comp2 = len(design.components[2].bodies) + + assert initial_bodies_comp0 > 0, "Component 0 should have at least one body" + assert initial_bodies_comp1 > 0, "Component 1 should have at least one body" + + # Get bodies for boolean operation + b0 = design.components[0].bodies[0] + b1 = design.components[1].bodies[0] + + # Perform unite operation + b0.unite(b1) + + # Check state after boolean operation + final_num_components = len(design.components) + + # Component 0 should still exist with the united body + final_bodies_comp0 = len(design.components[0].bodies) + assert final_bodies_comp0 > 0, "Component 0 should still have bodies after unite" + + # Get the new body and verify it has faces + new_body = design.components[0].bodies[0] + assert len(new_body.faces) > 0, "United body should have faces" + + # Component 1 should have one less body after unite + final_bodies_comp1 = len(design.components[1].bodies) + assert final_bodies_comp1 == initial_bodies_comp1 - 1, "Component 1 should have one less body after unite" + + # Component 2 should remain unchanged + final_bodies_comp2 = len(design.components[2].bodies) + assert final_bodies_comp2 == initial_bodies_comp2, "Component 2 should remain unchanged" + \ No newline at end of file From 2b5e581d4ea2b0f9b59b509291a573e8c888ff67 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 16 Dec 2025 19:51:24 +0000 Subject: [PATCH 57/61] chore: auto fixes from pre-commit hooks --- tests/integration/test_design.py | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/tests/integration/test_design.py b/tests/integration/test_design.py index add9700d89..5911236c28 100644 --- a/tests/integration/test_design.py +++ b/tests/integration/test_design.py @@ -4375,6 +4375,7 @@ def test_check_design_update(modeler: Modeler): assert len(design.components[1].bodies) > 0, "Component 1 should have bodies" assert design.components[1].bodies[0].name, "Body in component 1 should have a name" + def test_design_update_with_booleans(modeler: Modeler): """Test that design updates are tracked when performing boolean operations.""" # Open a design file with multiple components @@ -4383,38 +4384,39 @@ def test_design_update_with_booleans(modeler: Modeler): # Check initial state initial_num_components = len(design.components) assert initial_num_components >= 3, "Design should have at least 3 components" - + # Record initial body counts initial_bodies_comp0 = len(design.components[0].bodies) initial_bodies_comp1 = len(design.components[1].bodies) initial_bodies_comp2 = len(design.components[2].bodies) - + assert initial_bodies_comp0 > 0, "Component 0 should have at least one body" assert initial_bodies_comp1 > 0, "Component 1 should have at least one body" - + # Get bodies for boolean operation b0 = design.components[0].bodies[0] b1 = design.components[1].bodies[0] - + # Perform unite operation b0.unite(b1) - + # Check state after boolean operation final_num_components = len(design.components) - + # Component 0 should still exist with the united body final_bodies_comp0 = len(design.components[0].bodies) assert final_bodies_comp0 > 0, "Component 0 should still have bodies after unite" - + # Get the new body and verify it has faces new_body = design.components[0].bodies[0] assert len(new_body.faces) > 0, "United body should have faces" - + # Component 1 should have one less body after unite final_bodies_comp1 = len(design.components[1].bodies) - assert final_bodies_comp1 == initial_bodies_comp1 - 1, "Component 1 should have one less body after unite" - + assert final_bodies_comp1 == initial_bodies_comp1 - 1, ( + "Component 1 should have one less body after unite" + ) + # Component 2 should remain unchanged final_bodies_comp2 = len(design.components[2].bodies) assert final_bodies_comp2 == initial_bodies_comp2, "Component 2 should remain unchanged" - \ No newline at end of file From a9900233a8e0b40bd8eaece838a16eaf196bcc54 Mon Sep 17 00:00:00 2001 From: Umut Soysal Date: Tue, 16 Dec 2025 14:35:23 -0600 Subject: [PATCH 58/61] rename --- src/ansys/geometry/core/designer/design.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ansys/geometry/core/designer/design.py b/src/ansys/geometry/core/designer/design.py index 78c8b42960..7d6977950f 100644 --- a/src/ansys/geometry/core/designer/design.py +++ b/src/ansys/geometry/core/designer/design.py @@ -1862,7 +1862,7 @@ def _find_and_add_body_to_design( f"Added new body '{new_master_body.name}' (ID: {new_master_body.id}) " f"to component '{component.name}' (ID: {component.id})" ) - return new_body + return new_master_body result = self._find_and_add_body_to_design( tracked_body_info, component.components, created_parts, created_components From df19d44f6b19066eadff8d5f384124dbe5c99570 Mon Sep 17 00:00:00 2001 From: Umut Soysal Date: Wed, 17 Dec 2025 14:17:06 -0600 Subject: [PATCH 59/61] test clean up --- .../core/_grpc/_services/v1/conversions.py | 2 +- tests/integration/test_design.py | 38 +++++++++++++++---- 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/src/ansys/geometry/core/_grpc/_services/v1/conversions.py b/src/ansys/geometry/core/_grpc/_services/v1/conversions.py index 102e05a39a..e50b723799 100644 --- a/src/ansys/geometry/core/_grpc/_services/v1/conversions.py +++ b/src/ansys/geometry/core/_grpc/_services/v1/conversions.py @@ -1660,7 +1660,7 @@ def from_enclosure_options_to_grpc_enclosure_options( def serialize_body(body: GRPCBodyEntity) -> dict: """Serialize a GRPCBodyEntity object into a dictionary. - It is not directly converted to a pygeometry object because we'll assign it is place and + It is not directly converted to a pygeometry object because we'll assign it in place and construct the object while updating the design object by the tracker output. Parameters diff --git a/tests/integration/test_design.py b/tests/integration/test_design.py index 5911236c28..4c75681f96 100644 --- a/tests/integration/test_design.py +++ b/tests/integration/test_design.py @@ -4342,14 +4342,8 @@ def test_design_point_get_named_selections(modeler: Modeler): def test_check_design_update(modeler: Modeler): """Test that design updates are tracked when USE_TRACKER_TO_UPDATE_DESIGN is enabled.""" - # Enable design tracking explicitly for this test. - import ansys.geometry.core as pyansys_geo - - pyansys_geo.USE_TRACKER_TO_UPDATE_DESIGN = True - # Open a disco file - design = modeler.open_file(Path(FILES_DIR, "hollowCylinder1.dsco")) - + design = modeler.open_file(Path(FILES_DIR, "hollowCylinder1_sc.scdocx")) # Record initial state initial_component_count = len(design.components) assert initial_component_count > 0, "Design should have at least one component" @@ -4420,3 +4414,33 @@ def test_design_update_with_booleans(modeler: Modeler): # Component 2 should remain unchanged final_bodies_comp2 = len(design.components[2].bodies) assert final_bodies_comp2 == initial_bodies_comp2, "Component 2 should remain unchanged" + +def test_check_design_update_2(modeler: Modeler): + """Test that design updates are tracked when USE_TRACKER_TO_UPDATE_DESIGN is enabled.""" + + # Open a disco file + design = modeler.open_file(Path(FILES_DIR, "hollowCylinder2.dsco")) + # Record initial state + initial_component_count = len(design.components) + assert initial_component_count > 0, "Design should have at least one component" + + # Get the body and faces + body = design.components[0].bodies[0] + inside_faces = [body.faces[0]] + sealing_faces = [body.faces[1], body.faces[2]] + + # Extract volume from faces - this should trigger design update tracking + modeler.prepare_tools.extract_volume_from_faces(sealing_faces, inside_faces) + + # Verify design was updated with new component + assert len(design.components) > initial_component_count, ( + "Design should have more components after extract_volume_from_faces" + ) + + # Verify first component still has bodies + assert len(design.components[0].bodies) > 0, "Component 0 should have bodies" + assert design.components[0].bodies[0].name, "Body in component 0 should have a name" + + # Verify new component was created with the extracted body + assert len(design.components[1].bodies) > 0, "Component 1 should have bodies" + assert design.components[1].bodies[0].name, "Body in component 1 should have a name" From 660b3f700d974c5301c3f0ed67fd5b341c608849 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 17 Dec 2025 20:27:11 +0000 Subject: [PATCH 60/61] chore: auto fixes from pre-commit hooks --- tests/integration/test_design.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/integration/test_design.py b/tests/integration/test_design.py index 4c75681f96..74f88ed2f3 100644 --- a/tests/integration/test_design.py +++ b/tests/integration/test_design.py @@ -4415,6 +4415,7 @@ def test_design_update_with_booleans(modeler: Modeler): final_bodies_comp2 = len(design.components[2].bodies) assert final_bodies_comp2 == initial_bodies_comp2, "Component 2 should remain unchanged" + def test_check_design_update_2(modeler: Modeler): """Test that design updates are tracked when USE_TRACKER_TO_UPDATE_DESIGN is enabled.""" From b43971a326c08114d5781a24d5967759f822dd64 Mon Sep 17 00:00:00 2001 From: Umut Soysal Date: Wed, 17 Dec 2025 18:36:01 -0600 Subject: [PATCH 61/61] test files are added. --- .../files/hollowCylinder1_sc.scdocx | Bin 0 -> 79890 bytes tests/integration/files/hollowCylinder2.dsco | Bin 0 -> 149770 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 tests/integration/files/hollowCylinder1_sc.scdocx create mode 100644 tests/integration/files/hollowCylinder2.dsco diff --git a/tests/integration/files/hollowCylinder1_sc.scdocx b/tests/integration/files/hollowCylinder1_sc.scdocx new file mode 100644 index 0000000000000000000000000000000000000000..032056ac4fa2811a4d3aac1ea4745459abff0896 GIT binary patch literal 79890 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%0D=M$mNdG%B1nMYlj2Zql_14?HI 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%0D)hmkct^}}$lYYgfs6{a_&K^yWv?*#)42+*C z7VNut-~GBEX;ajq7#R2H6yp1@2lZc`alVFym^MW%>i*$P-Tc;|pO<&8SOdz_RFg|ZMr#ycD}=+9J65ued=>lrU-o1~w4ytc{zhjE@mCoMKbEsBA0o+AfI zo1zxQ!1(ctWA%ZRJWck`IM4AzOq-$>#lX1JXHVbTJvW=Yzl^iT7h>8JwI~M0*~1Hx zHbpIpf$l30Nvu(KF6#R1njqs6{a_&TA+jX;ajq7#M%7 ziPv}D<7cve#>;&gq4)OjG2t0E@1CZQDyK`s&S_KBq8J$GHE58uDQZy+jPn{ZNZJ&& zCPZ{FCM{C$j%Up`+yvzx66&p5BK(!Q}NYEcY~Kek<>f48Ez ziJoy@%&u9x1p7CX+8t6+GXl}wYUcX5rec?5M zCOqT328S0%o1zxQzR&TD`W)266JF)-ewLTCN0l*T4{#(51AV%ik7 zCY7RA8$_e&A_&PyAZ=ov4+AVz=sslN%&IM4B8=d>wm zQ4Eap96m_e6tyS@#$9^V-q)mRev|z(&N&Qus6{a_uJbDH8yj}rglC*{P#~sFQHx?=JYYx(-zhJyn&=t7eYm1;@X|~Zo^iZS zoCA@2|BSco;;}!Yt6M$duqkR$42qr@DUpEmNNHq`aTg zhPyN~IX>gOhK_^Nrl>_RFwSe_AZb(7q8J!o?f5;-Y@6x+8Rs>Ah-p*Qq8J$O(AZp` z6=OPnFz(r~xIV3^>EFlr!=by=rl#vm{yxSlZ#%G2t2K95w8mHbpIpfpN}314)~r z7RA8$?CS06x?C(#4>#F1QHx?={AK*xjfIEOX2o^hOK0qyIU=o!a(H1n*#3C}prL(Xx{y?@5NHkC{pv9FPdo^hOqoa35X z&-kg)wrLJ08=L4E=Nwk~?`ItS)Nx%)6FuXc!z$|;M?Y4uT00Xxlw#92Ir9E<{8I4$m`N8edX8BIId&Or|vP~8OL=p=g8#VKjXMg z&R74SiJoy>=W~uuZaw3e2Q8PT=F8{i8OJ;-=cwf78OJ;<=b+@~8OJ=z>1Gioua9xe z({K(=Zaw3er?F`~#YE3I=7FB=o@2r@j`M^)e!2J0ID7Pn!=|W3F))tvcw9bbJxJOV zwI~M0(N6Ats$jx1&K@R+X;ajq&Y!~j#2zJ(v?*#)=U?G%!aQxuO@E|;q)kzaVqhGf zBde7hW5P4e9xRAyQ`Dk`ozlC>-Jx(BLQ`DlaFTfjxdE5)GarS6IOq-$>b$%J%F3cml_O4>``WeSO z_0=97OnAnz&Sa4?%Vd3saja7$#ore>K)H|4ID4>&!=|W3U0;EB3+o^?uRk@>GmdqX zOQ+_W{QZpMa{%Yi=iWc#_#DAG^0|4&@j0YQQg4&~k8xaQ?P{au1LxK=j_WwS2S9F~ zaa_lB?cdF0|BT~0F?~Xi3C}n_$7LFG>~rs*aeNNs9Qxcm{WFfwF;hzS zG2t1<=On&IK<@oBj?YOyY{N|SjN^0I-d>?5JmdHrex^;73C}n_$8(N;?&C9#c>vDA z&&@NA&!ItuO!vxhLdmr92;Th)~M2Klq)S?&|$2wff@PfV|X;ajq7#PPo-l=JIOnAnz4$Ais$gO7_ z>!^H>fZROeSciRh#q|AQ9P=FISDL>6jAI^!?}3ne|BPcEWymH|J>!_CId{U8XB_j) ze2;m& zbxgj;LGJxCj&)EUw-zRP#<32~_gKiSXB_LSkDIhH(KC*9a=ynyZaw3eXU>|b-mfP& z&p76(9ZNklY5$C4o||*vbL$z$JZ#37mnM3~G0)36___6rW1jb`xg`hiro8W9P5a6 z8@QV28OJ(ha=3awo7{TFu?{fMJ->;bajY}gxLcU;jAI>^?;(+U|BPcD$P!vR`M zI6-kAPWbn64?`<*jKaNNTsZgPHM1t)C_Mb|kCvi+*%)2?sw)lNefZSi-FGnhM*nx7 z{UYQfttfnJ73tWvA zlMa4;4|3MQiRwwO9-e);T5Hqa0N)+F`#wg0>hGTYYUESJ?O^?ds}+8Ie{$}@xeC92 zeRA@_!P=1a0?$5Nd;zpC3v&5cQfVx_wEy@1{p!NQ5AR=V`m5mKhYJ?YLb&zeFZ|uJ zZ!diN@B?nD=a5{}$K zv=?~x;n*Ea`+^S>-u>Z5pKfl?euD7r!^=C0_6N5<+`CD_#S6cF5;;5JNI z4Ueby3x44VRQ{8HUlAUDh4wau_5}|=9Q<(a!?~||fOGrzhY1fq{GzkzRkkBva2qOf zgi{$l{yF48g;N+l{(0m`g<}{#ez^A+QCTXys{d#H{kFox4B?$nf!RAxAA7$?)-SC5J7X$?)-SC&w)u%JA{S zy}z5vKHQwV=RUl_zx(&s2@gN~zGvxgzF>6mtFB^r_b-ym7_R8cv={jI;hMfe`^rNO z!#zomT@BzLvBaP-4(|GTf> zAiVwXhJT{>8;*WBx4#MpH$44c$*m0+_cuBoeEo2Hf2SkE)&GM^4#gi7HQF4yRWG`1 zt@bkoGm4)zX4)^BeA+zPyj1d2%uBHVm4XxtQ2ePWs5PhfQ)90ErLoi&(ppd{Owod3 z5h_I~7NJ;-iWS9T6pL%qG$pipDkUlEH6^vy^iwH{))Y%qDMPU|MH{MZOZ9EFWhoY? zn2(Ab#e5X=Xv=BswU$&IC|Xi1q%E&?(mGP9K+%z6MJmn|D^e^+r4q$*6zys6m1+N# zwN)tEP^?O&8pWyy9M=~@VKkFQs{Hh z+p75x@WT%feJc7=^r7fK(Q~SK5%AHgy(Icb^pI+P1pM}DZ-~ASeIWWi^mf1JNWi<_ z&X^~$#OUeoN-Ye%4|*f?PUx-Bd;KMPA>>M=i(COUCkTBD`VKW0C|UF#YOVxw4c1b- zM(%-{)5HA%a(dLf3FIl96Kx)C9c>)#8~HhE?gVliZc|G_&V!nJ!)+D0H)!REebxWmh$l>To zxf;mlKn@3P^3UIq!@*vBKuSLr|JmdH~aGxWYXB_VX?xQ60jN^So4wlR_&R#~DXPmu~GS4`BC1sv*_Ik=Z zz8@P z*-IevjI)@}8o#@VYZ^Nh3CS>_pM zue8iF&R%VqXPmv*GS4`Bxn-Vl_A1Lf1GIOn3vJmZ{8D)Wrv zIso^9mU+f;9e^BGnP(i=DYy@`%rnkjPnl<&*Sck%ab7EzdB%CIUFI2Qud2*5&R$iS z{|7gEWM!T`vb>fm^NjOay38}qYppWRIIrc(Jmb99FY}D^TEEOQ&RznUXB?k%kRvJc zjB_rf%rlP9;kZwu%rnkjc$sIMz4kKCID747o^kg2%RJ+pD=hPjbFQ(>GtRlnGS4{Y zTFX4+oU1MKjB~EH%rnlp#xl=1=Q7JY;;l}#@S0G^Nh2XMdlf2uZ+wy&R!XrXPmt_GS4`BrDUFQ_FBn2 z=dB)j`B=d~3*Gc9XXRnjYGtOQrnP;56elpKEdj(~larOerJmc&|lzGP43n}xA zv)57P8E3DW%rnkjI+5*~=>PjI$S3<{4)%pv*JQ z_v(^)#`zvwGS4{YO2|Cpe6KE>!=UhmcXPk2} zWu9@)wUl|rIagEW8OJ;Za%5$mamdklP>d&V;%+fVV8NvF;9XVT$yJa z^Gvu8yv#F>c`W3>%RJ+l$HIN+Wu9@&)8Rh(GS4{X`EVb8nP(jHgt*6m%rlO8D&)A! zJmZ)L!+rK;o^i~BA%|Y(8OJ;ua`a`Mam=IQ9t<+iIObt-4+oiN9P_w%4uZ@xj(JAh zqd?{v$2=yUgCO&aV;&RtK#+OHF;9wnEXX|L_?(D)h{!zS_?(I7_{cot_?(L81j#(( z_?(1$XvjR{_#B4k{K!1x_#B3NbjUp8_?(A(fXF=KnCHhms$`yVtTW&qSu)Q!))DX= zCYk>SH{Jt9=J_5d_#BPrFv&dQ_#BOUtjIj$_?(V=xX3)?oNFQTjANb(IUF+2IOlrE zJmZ`zBJ+%Mu8Pbv&bcTu&p79@$UNhmYa#QDb1sL>GtRjjGS4{YipV_UmU;=d#K?<5;J~JtAbDan5y>dB!={RpuGTIy;_YA@huLF09Nmj&*)K$3o^A z$2vdmp&;{&!-s(9Sjar%oGUByjB^gI%rnk8yfV)?=ZeZaobM$f^Ne$@v&=KjxwpHvN4d)QII^Ja2c!5Xuz`>tc1_CsPQhrcoqY4Igk$l z&#MJFUM;DV7P$-m`{ywfApw zR(|dRp0a?uh1aB5gCd@`fF~{Bc?-GoHfoa(@xSG2AUgwh6|XN`w)M%e_-}rOgYeqI z!5cuY758DrvlW^eJ-4}^so*c3s{oH~bJ|xcC?rGkM_UA;|8jd1k!;!w@ISZZXeaD^6t;yMoXJT}v zceERMIPug4+=(5}TtF@daxdWf?MePhxPN<79!GC-{|1YE4m^1QPhEfuxB``MiWMkU z6xkc_1V;*Q@bAxEs4Ox$-~o-I|4^(klS94xHJ-T;M|Z=?(azPSkU!LS5 zu1#g2@Hh`7CGt6N&-FpHkD-*$fhRGjch<)97lx6;6Ha41e*xakQB=lI93>pb|2}uY zTjX%Sb2^Ux+Y`xyjHfTCckjkC7bcMl8Fy))LVLk;7vL04rhVaF-^kwhPk9^a^A=pm zn><%IwCB>*!hmWcblFCh({vH&OZ3gKv8L3ey#LB7+K^tFoi zf@dwjk-D1pg(oenp|YOh8j3@yY!JTI4fI}YB2OxEG}JqqZ>HMF(ZHSKQ%K`WPStu; zQbiWd@6TP>MDFPA!WX@r^l(fgXG6VPI-a?JoDJk&?4|b$*&A?d@1u7OepftyA)O+e z)VZI#u!}Mb4$!`KQQSdU8|u>+)Vs683wxOIHQC#pweV16XCMRP z1vz$I$eUZ8$}5W1Ddx`Az>^p7)CD|y;jzflfJ6K}?HBjP$MYAmjrY{g{oI9R!WE8t z(to9MWd!+tM~W;BJZS(v@o$us0k=4wzkoaq-zCY}ZcU}K@Hmi8=r_?{qMt+`iQW-88|Vko2cq{=pJ##R zD9jOk9Qro&Y3R?;lc5Jg@1^?bk)NUVRp_bI=Uc#Azftr@=!ek%pzlGiqdw!}faqZk z3Xi?|3>Wkx=t0nPpw~cufxZI01bPSV8PG4FKR`dAK665S{t8;S`m781?bX(dwyfrD zsLx74dqqoCpL>DlTd1uPnNw(sXoG6rhT86^xrx@6huRie7g`ls6SpL^BeWm18?+a+ zlTy?k&?3+h&;oG&kr>8ikR&fvYB5MY+-_g6B%8`5O)*e}i+6 z@GJ`C8sW+XS2F4|qgv4wOGk0-g6FIt&&ZmNfU5~yUEpd1S09DxNXXw%pXJm?Tp6g( zX^N#+Fj9>0>a&-;#d9{)XDp2tBewedB=y-G$gP}CuM*EzLEeV??4$W&R7Kve0xCkxqT?&@S-|5sYmZoiJK)o)9tuF#?^TcU8^b$Pyz7MwZ!P z6#4hrDaiX#pVRYL^!muyt4^-~IU9I(3VLVs*66)Q(jJl5gT5E}8_4&u*I0`l6Fn$; zRP?ZSH1?uzL|>_9zLgO@AbP|s`pd1U??N7jy%t?pQ*>XnA%_Fcw!Qo;A$ z^aIbO)33BCI`um>ZsW#t>G*a0%F((4x4MaEz^x7U6Zd9e9-lE}9vJUm{S5N^{((Wy z_fazH({U`?zqq#%^SSo_4-bq#%RsYl>L}g5tv3W;@Kl=6GmpA54~#!6LEnACDBZ&; zS;qaR3qA9wEAznkvl{drQb*{r8dl7}&-DrwNyB&MQCH@H@n=EQEmLQh?q_0Wq0f8D zSLm5XU6}{QpXH%HQEr$nf77l)Pw$`5GmpA54~*v$pkH}ukj~=DM4>NYQ17kGJnG6k zFrLeReye4I?(W$YLSO&-RB?RfQCH@H@mve^ftTWSZJbsKeZ4}{g`RoTm3d(NSswab z4l%l?U($sB-QKlA&phhNJTRUsfWEaur0&ejOG5wir270@=22JXf$>}g^u6?bbq5<> z5&E^^>Eigzqpr*Y z_Bsna^QbHHz<90+`jAfUx_v9Wg}%iM_1?V9qpr*YFydgf7A=7I5CJ@l(fef6LJu9wiSN$)50%%iT%1LL&-=r3qK`Bx|!EcDcW z3qA9wEAzm3Eg1UwBeVP`HtQqwk#mO#J@cq5^T2qm8TxT0j`@3J^%wflWyc6T^QbHH zz<8|~`tF@G{XMTm3H^iGNkY#&>dHJYUMq$^W%UMst33mRzG(SLLeD(v$~-V$YlZ&H z*+2ca*bEeUm+Mo7o_W-jd0@Pj3;p=`>Hgy$sd^f}gr0fSm3d&imJI!Xt$q9lcONQ_ zANl1^p=Ta-WgZx>RYTu1&ClO=)^MS}HfMp*GmpA54~*Ahp}&0I-M_ot2%(?%V4=`6 zkGe7sjMsXhf1CB$&&6q!&>uRpTl?J@cq5^T0S4568c5dsBbp znW}d!vQ_ArM_ri*#<_sdKY3j-{pLwk-(to#p=Ta-WgZyk8bZI*?LhjOJL>yCWY`{| zXC8HB9vJ5eKyUY=Kt@&lNb&yDvbNAOkGe7sjB^p7ZL0w{Rbm z(RI&Yq0jL>A@t0nuFM1DTm|R{E?k_^@==1&*Qk6>=$S`dnFq$X642XiOUc-HAx`Ms z8eI~4=22JXfpIPe^hc-f%JAtC`@g7r^LVP>_kDDm=b@4inHtDE&vV8kLnN~#LqujW zk41$-N~9uFs0?Lli;8HFAwnS}Br;{pS@*Kn@B8`e-#NeYI)9$L*JHo$>%Oc#tY3*{s8N0g7@kV}k14AVLQpXJ|&7r_VIQGUW^Kg!2&$R)-hhG{8)&oMFdLRsM~ z%J0Vwp?nO7Tw)wzm{tS$x4I`@jH{nP`ISG1Q9g!4E-?-5{E%MN_jOi6r} zn;gpb*h_-0!!aCkiE)TQttsa7wXP_O?|CPM@{#oi%ExfXCB`9!)#}6kU2@d$u1$g{ zKS+oI$D8Dg{4dr7vN|1{Yfe}tJdt2F~Yib zkKvF@j6)35ssTSgIS7B4$sgr6zvo8z7!J9_IK(im9PpdNF5+)lsG)qTWc1J>Z1TM-~3oNUSu<$8g9c#vz7j zVSqz8K2W~vFBz1N;gCy=Lk!c}0H1JtqWqu`Ih2p#kV}k14EhJ=gF)yYD4)q&5#?hz zLirdDxx_fc;QWC3m=MknC?C20gYq#Pa*1(>VOmXi{e<%q z%2&2lNBI~Ixx_fc;QWaBoDj~BD1T>>2Fk~9$R)-h2Ipt&d!h;FXO!P*tcCJ19CC?q zh{5=PeUCI@d_ehbIyxvH!y%U#hZu}c*!M^i#wV2TlAw$7F&uJKo{6D_OnlL{{`3aj==>9Pra*1(>VOllVKVf}<^0^jlQ9g!4 zE-?-Jd(}Dq?aD4#fBiEl%K88as zF%B_IYX*G6^$C=pU+IPNF&uJ7lg!y%U#hZt6i4}8M)8I-SF z?1S<#9CC?qh+$d);1jM7q5RIf{wN>AA(t437^X!4KH>Tl%4ZG3oAT%SYvTZtD@K88asF%B_I>j8Yi^+A;HY#EC3F&uJ< zafre7Nz5mOaD5WxXGDghd<=(NVjN=FcR|7a3D-waK5Jbh%ExfXCB`9!eb*H53D;*) zzU|&9l#k(%ON>J-_&(iLe58?VW$}fE!gYq#Pa*1(> z!S!*>$A@rz9OYl4jYatw4!OiQ#Nhfo=Ho-SK9BN)gyK*>hC?nf4l%etfcg9o?hl}R zt%va_AHyM+7>5|#pTK;W2=^yYe#PoGAHyM+7>5|#AHjT@2=_-&K63vD-9LsyE-?-< zxIcsWG!gF4p#1)<%P1eiA(t437~CJie4GgPhfw~vpWA#4hg@PDVsL*7^VuTYpF;Wh ztV!tpF&uJ4!OiQ#NhrY=JQCnKZ^3D zTekTa4!OiQ#Nhrc<}*pSKa27^Nm9}MV>sjz;}C=U!0;0=olY#CZ!y%U#hZxjH zU_SSR`UsTY?vjb}F&uJO)XIa{mXT4DgYPyU z!y%U#hZxjHEk2l0g7KSBKZUM;k^9f+{xKYKiE%WB_*i6talIPL;V)u zkV}k14C=!Yr#_>6LVXy@S2(@R$8g9c#vumvX^z=VD4$TDhVreN@1y(2aL6UbAqMqv zU+o^Dd_sL3%0HmGeH{_QA(t437}V$S$(NvfLVX^}cQ)DPV>sjz;}C=TK%<&;luxJ+ zMES`5cl7yVIOG!J5QF-}-jFbqPpD5s`Snr-C?CTiml%f_)JK|3TtxYV`bdUpaKL1;I^+54N_lDi~N8?03A}v4gNe(Wez@1jK~kTUoat>z3qDQ zoya3l27*2i)PbN&1AQPXvh6`y&{MFXiV8O5H#<_B&W_Y5LybD<0{7y;?+IL=_8}WL z(r)Vm!9jxu`2n3EFH&C)S~<`Q@*^4y@YDb|jRQ!H`F2e^xIKZ>6KFHQ?MVn}xBZ@k zkQ#ST?m(qGXa~V}1C$$LNd5b^e*q{6K|Kf>LU4YPL0a(RkVQ7|djch)JhCYutvu5H zx9^h@P6gFrfm??P4qTr=T?o!k8c3^-wBY=tMey)Af;<8}A*c#_H_0 z_4fLRPDCG3fq-7*C{l5M6wv~1*WDW-k3dxj+CoqknhJDf?^PKgAT~%i0VxoQPm(Q22Z1UK_^6a zvaLS>=O=J|0`DhqesV)v(4)8`+c|=Fg9q}+6V;~tx9^iTqW%NLAm|1`HRwywtoR|1 z!1W0n>^%EK0)sQPEQ4h#s;)Eg~;|l{!T@R24Y(a0S-^#?gaWlaCicDC(z@7 z!xOkWfyWa#Jb}9tcsx}iTNObwQH?wT#USVgK{Z%I(CpMAk3cI3iotqREeM*5|JDpP zBAUUMs2|I=TCka*w|Ir9|3EPaxIJv8 zLf;S_9Jo7yau6JzMhM<7-;qb71n-V9TS9dt-~N)WVypcLFfTF@%}r>E2ZS8X6IqGqB)^-Q2-qC+$)pl1Rt;Qy1S z(+*S(2%11p1cIXz_&R~B6F547G7wyy*ikj#|4AFjh3KL{!N*O)gKWG=%Y(F_4g_T& z=mY=L)9E0|0YobWo;RSFI)v!|!1D$aQ-Y`h5Ojdxfb z;z%orY*I)oiL}zlCWExnNGpqMa!4zSwDQQNfVA>RtB7n$NUMmn%E+dIw8}`Uifn2~ ztBSPh$fkj`>PV}JY+6XGiL^(MO&e*CAgvCv=_0KT(&{0bKGNzTtpTzfMOp)-HAJ>! zNNb3+M#y#?X^oK97}-pa));9`k? ztdZ6VX>E|r7HMse)(+Y1k+vS0GQf;sTO$b?GtipRp)&!P0l>%)BR-7uFv9K2fl3|x0YMiTi2Q(`5aw+KDsfC33BFdC zrxoZTw|)M$y{tfw2HHqabYt2`Vjn9|Y=bTmJcdA*2AW7r3khCUps5Cp8t5Rmb=TWE z$d|~u6Liv`Qv(kx(5Qh167;&De*`rv=pTE~Gu*ak6{u;yqGz!G(W_y8Rg>r$3C=`t z27)uswnr7Fc?9nv(5!)W4fCfWL-fS|sc!_AHPEYVds9J=hn@~S9C|jUZG;{SJr{Z` zKhi&;heFSU9tk}WdLZ;X=yA}~poc-vf*u7u33?Fp9OyC7Q=o@H&ww5QJpp z!;ub0I2_qe}Mi0{RR3D^e5r2;&2c7chQ+eigDQKNRTMF|{n18~26y~Q6$Pdh4VLl7HrW*9B z;5P=kYnVS1`;x(Y96a`5z7F$ua5n`_EX?;|{txQ`SRcT80oD(&o`CfQtT$l&0qYT1 zpTK$r)-SN0!8EwA{(>M)?2Xtf^TDif7`1yUG(v`#uGfdK;sE| zPsjnyC-T3@{69*%CFV!A$#-mY#=zG6Pk=g&C%c)0oci3WLk@Z4v^3FN|G_}~A{oOo;0 zEY7^SyM5x}(u))Nhe?W0TqYy!zQ}sh%HYmnnvU?jRH~Ql8;#7W5`4bLXX4*lhXPAVl<&Ma;ect(&5r&(lZS+A&*sG-xo}9@O*#q zraEbte4&H+!48kiceJ)THPXSa(s)hc+axvP8t)&8-z|Ul;vI9ZDc(JU((B|pu8Gvn zhWc-xztI!Czq;R2!tsvym0QDUG;x#U$LiTl-W2hipyL)vXO^~PlE$SKPUfN@MV<=SW4*Lh=w5^qy zz<-Nf#cYE|ZA*%kxxY(rFrK+(UUPWQMDx{K$=64^Xd>{3&%5AT-`N!P)2rt{`l#A7 z75ZHIY#1x~z)V1W{-}W%X*4tAH+%bI75O%oQxBixZ!=_UVD0N3KV{*jaA$7MQ;Vee z?86K0N-|BY8rio#o(opJQ>K0J?jFt3H@bO;Vqz+ttE#2~{#@L85->K&Y>jir8P;@E1KuDK@abDEXMH;7-kK;2LCQ226FrlePw)d9Eb zr{B1?vAIgHEHsKt@6jDiZ)DABAE_ARKKtJA9bNb&oKaztsVBKp;G?cUl6M+E``MrX${$t+p(2%*u21Y)n)E2`pc&_T5~7qnB(tP9>p&r`NqUYG^oaP!hLp` zl}3aJC0>0*6{VY%^S-2W@KX968`lH0`+iLrJ$-ik)}G)l>-4N~q10Oj1HWVnLM?W$ zj-?B@za^84H`o!^w(s%RD_*~T^qo+qX2g|WwZI!xboZQn5}NW%BixI>^~eRKyS<$o zmWR!bfAcVy=K1B4oFXPv`a+%lkl)(tZ2w;tekuHQWv^=4m}+l{jSbq+H5lg$B{p9< zS7j-CcxDtoNc&`2M#r)xERM;lC`XzezmCL9p!tH%|4IFZhE_M)TphQ-7SI-|?aA4k^j zyqh1;Gp&=`@p{!Tv4`oiTzb!qK!bt9o|@I+@57h9(<;?pq-%eB@+mr~;Ze2M*l9EB zO-Xz8#lR3Jq33i;-_tEM^mJdb{<;)+-=^_=z-5uX9RU{PL;Wsf*88dU{mZ z;bPm2RYq7P+3!8&KXgU+iR%X2rRS^E&8Dx`&;Azuo%yUnalrMK+^LIKCfuFnsHbK% zzNz0)P1`TrZ@e6N!#~?YeuXu$I{ybr3XNsoPV2Ha8Qnq`KHHtxTR1nF^T}q>^2al) zk|n`(r&~jO^6STimh@L`js}lO#>Ko~8Zx)2iPRZ-JCNR!A@H<*~(i^TtQi zaK4`pl1&^rE0kyG%#q-7#`W%2hoZOG+Q+Y!7DB5QZ#_2+Ci`vrR0eDXYt&m+ZGY{# z@h8sFwN!{zz-qJUDy1gYQS%mwdYIW%M)mQVEpB)#z%Up0&_->Hu zlTZULqnRM5!+#$Q%q%&6t7WZY8=mLZzVw*++m7qvdHREi?(aA*Jdto>bD}I~%X}=) z`=@=!wd3S;a_&vW)4OE8Cp=>_*fE&5qdtZ?+N|GDuiUIbP*Jj(L!XTM^k=<0OcN#f zWvK!ia?iMB?yKzHtNLW~r_h@@FN2lxR~^D5JN^}GkT-m-p_9BNzS#cncw58P zAbVI+jLYJTQBLkNwU`IoIFfUl`xI7p!Kgutj@U5l%YR=bc_4#`P?3!l88yy$IT`zS zdHag_x%zvaKJVz}A?khJ<^MF)Up6+npynRWJ7_Sd+GB1&!>*3Tb!Yw;^yTwqma$0pyKJ(pHqEkYrTHDz3tm6B>`7b zrMZ7O6Muj0k3H6EHyhC~^!PJtv?E*m|F8c0G2KyevF~}#*n7FZTv|Hep|6 z`-;~7<}ZfQH{0+xX>*AVhF|jvEAcw^?ED|P^L3tss`F)a4>|E$|MDn%NXTsLQ^=@l zUu4Q(`0%>jWK(G@IXrFkH5t=>nZdBXX`_pS&xTab4>5&{JBB?w-`v}QKc+_ZMOK+t3h{1GZRMkozF8fs%zi6mnOWoNOqWG{_(CR_xTxp{%=8KT%sysMgNo* zy{@yfmt^VDGSafk2zRFpdpl$v?JTJakuA_HsF)7>dq8G!c6RM_Q>?B5q37WA=o5&Zw^j>(snNjqz@m}K~PwdBEsjpRoyoc|<-YM$msbPEEJtnG_a*FM~ zR+M9{vM1fYZx?THtSvr~HoRS_?)J6(?`gbE)xEi&4{nualTwfdQXB5ktX`aY-Vo?@ zwE5L#y4$}3L|24THF+hOf7iJ5%PNfY9ZDyQx-4<#(~wX5oQz+_N$U{Nm1`z5%#S|5 zZ%c`)B~z$rb!@beYSNDQsPgpZr0|gW!KvD(i%JiNWR3}UZfMBU2i+y@dfP#6{_MO{ zV&P9JhVS?BpF3w&pGxIAk{;)iDm`_#&mwx#H5kVhj@fr2fa5bu#ijQqp&2bT2d)di-TpNAK z<-~oiY{hUJs;c^&d3?Ed#*0&s+SPS#uG#J4*co2mm>s=0&MqY|GT4ks{JSdDu*|PR zp4D`bCUg3Z-B-8LOe^)A9v6N8)s$;Ain2kL-e37Xt~Dpz^sf7;qpzW^rSY4xOzS7b zc!lb^xlRBXCuuK5L+wz=xeZc|>7hcGFvY^Rt#vDfraagC%S!?`YfMfa`58F3GD@+h zXzwtu#!c<&4W~CJ?0Tu5BFCA;@vLg&ClL_^r}5!Ni@%DRN0Sp~KJn|W_UCHYX|Gu> zT|TrJ^qppvZO_DsydmCOH!3Vl7-(ib`lzrg>>pGNu-e)QUf^+M)KF^`is84VBn z9e(xcflxYW#Kr}c@C_%f@89d$B<|Bnxs)9_{oP>fM_EG8+74Q}A6$8uszSYxZ$|dJ) zQD1rUHzj2bDWWVrQA_2?W5bkvQ&DWLmA~F4myWsjhA7?4)a!CqmF$tC;fsp`>a*X@ z8|Kt=*3=9P|Glfr#(%~Mh(2U?RTuAz z>@=NMZfpC%&!U(-@8hQZI{1WzK}m65PtSyj8GmUhgVH(HU`Buc32O!_jdh*)D0*^s zO0xn{O1C!3uj9uD0A;7b(c>|ubQFnE>mB>c*(!VDDt;lBQLX5 zhk0mopx^87H{)g-krQmf6Q`RzVzWGtJ}*4}!r|~tu5m=3>1L8L)w6X@NZ2$)1*;ts$^qL`*Fvk=D&Qzp~iC%2uSbNFnL=ZK=ZT zw}sW;|I>$@QVz!GWrhTekhKz9S?cb~8g^WlcDSw|EL}KfUuciRlSp3|Lo?=bRY!%ZY3EXFV?$Oleo%J)sn%u5M)+M5(RqVr*lW zqoe=UjXb8av$IP;Na?Dvo|TlSyfh6>dqY7%=7eU z?gw7wGyxHn*zWG}QjL!Ec*?1r_XZW;*+{u+#>;y6rH>y{^e!|D{`JIWamG6K%9WXp z>Euev$S0=-R{ly}|n=270`FQ-)~@LSTPQC$MUX{@`m zHPN?>CHkeAfJk~qy1o|g{QkD~{N1|?j7=`=OiwQ`J{7#v+`NFLq$FQPrnA4l`onE) zQ}I~Oz2u&CF^?swyK5*8Zy@82fELef{-1|$#QDAZG}97fI(Pe1iPF{eIs-a=D*CE* zwSQ08yB;>gZES=I2*^`6{Cm6;r!z{EX;@&TIZXfPv()1+M$PBd7!pQV?tC!wt}%K! zquRrPQ{|3p6eK4tFt!Xbpuw?|9!kdv2r685nA>k~G~e0zkQGCmQEt7m2lW4W^5Wvw z8--=6MJ)KWph+HT>yvvJqg~zlm~+fheCee}`XZyF1I4{_>yG-9`TcskirYtPNYf|L&pFXcpj3?vQ5OEb$yH8t%LIII};{j{4tO)%%n;R|UQ z#rw2QeYpS~1bXR(1Gpw$Z%x5}Q|=+kQMYfqo;&yUMrk!eY&DnW0Pm|)-Jh@fG^HNo zNlIekQwUbjxjMz+OEzV!{>>(~Cb4ztUs=bRne}=rKRcW3Bi38DtW&c$clh`O@X7@q zKes2htH`$TLZ;y%YtrHUk3R20-rL6WY9tAxG3Qr zx?H)bo}v@TC|%Ao!Pb4brJ~LKhZ=~W7V&7=1e!g(7B68sar*P)FRaU6gg;HYmg6Enp^!wbc>lp4+qx2XM!<_|n^ntqO*!&I@e(dq7P-_IaqOiC^IGb=E!H8v1W{kjLompx>wA zCOETCIq|8apy#HD7?ZULZ|S2v@myUub93vGl4R{KvTRKwJPb^Sx`Qci`{ljWwYr*{ zPGgnd`XRieJr-9=I~O}_{@~o5&B?~Fl@armdNr7yfn`KkUir(<2p$i zz%S+{MoTL|K4z>=iM-eETO6KnA}0iT*#U{ZnU-Pil)Fun8!V!|1>SXT_iuM^UlQ#5!)p!0EQ2(V;SuN7OIsH}SWwA9?ZSa)|x zi6C1)9rYCFn71a^>B0pDpPkq9dT46S+)D3}(TuE%ETPWcTwj!ES?s&Zcu6(v5u0Ql z=jF@ODQSzcrly~t1(4jj#a$gh;v=BW>&;BUB@S!aXld{vI9H%W^{+W z^|C+kd)HiRoKb!B^ABfy??pD%Azp2~JqI3hVV7L+2(*Wrci6p3?Pw>7*#s zBAFr;ApfbE#V>yATBC<5ebTu2qt9B8zx?C~FKWI(Z8X9A+-6MU`B$ouKPgnm8ypF5 zFcS}R3M=bAb(2pd&QGrj3-U4vP{Qdq205KZK7AUqW1qUfk$oqG#R^);1jG)FPq+(I z&W^A)JMz@ggyh|eSESOok?Iq7|Irl-QCFG;>3Ak4!S;gwAzxv3yB!XS3?V~uwbn<4 z%XfE0%{7u0WlB27nQlnAy!g~fQ6H0*_BS=7nueWSnk83Mor|_`SM*dd&NEwu{J|e) z!-rbpSxhfa-?NRTa;!+Da(Koa*?a+KG!d?S^PJk?`J$EzZoAp=l~7ZBgc>P{o8w40 z%WnOoq~?sYU%KJ~A|edd7N0*G7l!dSs~2zHIZ;O)Jd#o$smB%28ceOApf9xh5Ho!z zEoEusFYV-gPgGcP8RM=U+ehigS2@Y|c_6|aIaK}}j#G1bWA!fciO#Ae7iDZ6?lxU6 zjm8vdH;sl2xvS|M4}CnDI0sYX8AV)0u$pkj=3}eB&1VOg>aqu^5}dg27!|cwF`fTw zr=6@jwe&mghY-ebSQbLX@`59O-=6jJ2N)9^Dmw%7jA~NB>I`&KjyaaBQKl! z>XXx}?rI!I)fSBpA3SFDh~?knWVw(jv+*AF8&OZ`j3K>=t=c;~@dz zJf6N17Ce2Vo|=X}P0gN{4wi>E9OibVWmL;Z(!Vfd=XB34w54QFa$UI~e%eUW=tO55 zg@|+K3(BmSUCPUrc7`7*=G@f}Y3*@%<%2U$>VH;VP&HR@Jc6O?VX1}c!x8o8bbYyg zB<$?gGFl|?HhYWt)i!iY|7~*UqkC{c%<$oH2iXYHBxfe!jIWQa0+BbIq0V;7`&WFI zpnpw3^GrsFGPh7jL*2;2O#F}-19@ItT<87!)vPc9>*t^kugTB9$VX2_5tkq>Y1#cj zi%}~v$?ck^k!9Q4cK)_+l=v zxXBFZm`$Y3j$G%L-qc6VGv84(eM<4%HoFlOLOg}?K@Uxvb*7laiR%-F@ zmnD7$6gB^mxbq=-#7F<@`BNK`xV%ria~HCh1%x-)w6!~r#x;(UYg}x@*A-}gaZY)< z&{yzIw^boA#sJr0dT{1kZW!Z@gAG?!R{FRo=Z-iW(h|Zw;lx$i$b<%&AN^FWso7+( zfBz$6)tk1sW7Ne$qr(F?55E!}oqVrrCi>-^1bw`%IEPyN+e*5#3!;|7 zbq-Yqp4GOG2LfM?q$pCoa)v98crbHQ6DU!0&^+g;r~uR3i(YBqw$tD`bI(WRcg7)v9V=9F*iD_pH9O zgbSK~Ymoobyuc{TJUDM8vKqQm&Eec^roOoPdfV2ILvhihK8O5jvpc2(n_LHxY0U_8 zUlDoa`|4c*>qhdNo!->HVh2R>_l8w7L}iPFlj9=}kw!i4M+OFO!9csF>Y;)#)h}+b z3A|Cb_hu;11ztVd#{78Ffz%rnT*z82^`>t4rReAx z0f80fkj(%#dea9Nlnfuvh-azAzJ6-^$^C#@?AI5zCh?8S0b1&mBsC4@cfBs2qmGSH z_rF*Y7nf)CZt+ioNU|eYRBL%$vsT!P)`*tbExk$2!o7b&ip)$BM5XW5P%*aba}G*R z-iR@Ji+3VMhw`D$+o46dFKyHjGUcESb%!+He1edIQ{jy8W zsr&N5^QX0G$rSde+p3S*;QUFZiW?tZI!95>t8{U%k59E=AjyaJf%uILPXU2G%4{Qd zalNZ=JjHKSzx2qk5*~1+HGt@4qJ2*O#__EZ5a|Y4Ov~ z`d+VB+)ExAj%Q70`e`dpxj*S{cK=%J`pk^lBc_=*A7_kwCQ&Ei8$Vk#*gdryW!1i$ z{K%_kwxC6%?SaMW1Pj|pNACBVAH~wQ9&WG;WZH;cuVwx6vTuf)SHzQHU$}hf*D#zI zp8nk(!H*l#%MqJPKYM?;>&Z#gOyqP2_m5^w^29S{k0vjDbucS<5t*a%-eBbT?MmZR z)ra?R?`b-wA_FuZds258M=C~%w6^s}-{EvS|C@De+*2d`A1e!sN0iYsijYlK^su5c zd?+HGmCxGFYFTRSP|+h~8mOtrImRlVz2vPpd4bMGv}oX~MY68HZ&H@kQi$BSH2WJP z%(9Zw-N}N6-4hmDBCAr}LWTg)$L|NgP{xvWcg z&!HUg(I+kM6wv2SOM7jiW3O`u*O^NEhGpu(=->E=gF06V-{k3wo&Nr6BAwY)i6xhr znu&r>j`}TgZhF))9l-$5)PD?07My`Wfm;dvK%pOr+Me}hf=i|HB zKabN+2i(Zw+xPZr;yxyQjmIW}p{q3;^8Ma=u`&BM?uhmtl+WI4J@{zDFkt;`pT}Z_ zm+3zyK|!HE9a@4W&uA|$nX4Dv82y*Ge>nZ*ef#8+1DhTyA#5ob)iGqb6@`zEe0Y9= zPE3LE3Pq3T@V$i2wC{Jt9Z3!vh2G1jv%K#|BF*x=yExK@BX62QmQTu;|LId%KBbHN zK0dO1a)JEw^Rm2Z5xZzZD$AaSZ;nb zN*>dZ=gb=R?s#AoyLZG#yMz_LNOp{^(0)C4X+dw`pY4)@S|l>W%I5_r)$FjCrb;}! zK$&T{_Y)qk;!eerJ;}xB@XOMnbHBr~#}|e|Q+b>B_i$V{$SBiznV+{JXC-4?UO{im zqBigTIGioZ;*Gm(bKPbO5{^y2L2HxRZS_dE_BmzDNYQkXt}`@}gLIZ(C5%i`QkF-` zmkfDXJWA_iV`HHmJ4yGZ-o@{rpxAq_%QM;zPea*!knO7JVUAAd>nBcRA-(;CX5-m` zLYCXb9Jo|op3U4U%YoW^Rm1s z7ONzNC{m;+b$B}pl5J^l*K2VKEVXj>jDXD*CC8dUREIunC>sTrUg;^fCzqVsPUc}Vb2d3WL z5~Zd67H9NVbTOppB~SjddD&ZeXBRF$w)`yA#$(Bpbv4apbnc5nXmv*N)54-l&QZFm zrWf!%-)pJICoD?3PxB7Ne%YWjJUD+$`@tUuHu`gp;`LGCo5DAh7g}yFt`)mPAaAw~ zL!`^yxK3HKJ-J+a-`t-sZEX8993#l4Qd{P1&QrU~*lXYQOg$xvp(6%uig^#qw5)DR ztAue$N`B^*^5uH^^fNN*aryXs(=_^^h&=PSdgYMj)MqBfJUx?nA)5njS-#hG9;Eeq zR45Dvn9=Q9&Sott?9mPWk>T=ZnO4PS%wjsrGcWq)i+e0DzSOhojB;ql@OtRHF!{YL0*H8w^ zX7`^}NvM5itZx2I`qee6 zeR>-B@ZX=uk(Jum`IqLI5>C6%ez3Xv>QyhY;ODr0!(Pjv?VR4R^oIrbvdmjGIub(X z7@k)23PfdQ3B7N(PTJUzOSy|@rJ(Ra&d*az-RJS_BYO?#MDr^atTGKf^DAU`Ul_O_ z-oRxa^XYl*Yjbh7rl}>NdtIgK8ZS!{S6AC5oZcue{!EQpSxL-%F>%+z|GJ#S1v09} z9?iWtv3m8J;h%-%zW+Qd!Y?@F@?zg{Wk<8~Jc?)Z1apFQ&a+(CP*1Lhk@088cr5slm^|VDke(UVtkBfSxzX@8$Az_8`wD#z;4Le|9O}@YHZ;dMO2W#)r9ftM?xx zv?av6Xmqa53&wHpIYfu&x38c$y0v4e>RLv)-R+N)y<%+U8=Sao!;2*9do`z~I3z-Y z@_i)Ymno)ci#61J$^6Vd<*1ah$rMwXJ*>YH8~Zv_)|2Mpar;Desf^P<-sS5VhEnM3 zm-8qDvzwY`@XGkJcXnp*t8AK|Ia8nb?%nRhQ>TPdvyYV-zk7QsYH%<;F*sNV8HI|q z*Mbhv(*8`fTGVr{GwS?OpR1npUMydDIY05l+!?EW-%H7VzeiE+Ow0X!Me5&RTtuYH z=h+7@zJ2Ocz7{a}k;FUw`3an`)e&orx&Z1F7INw8ymi{ai(b(?DJz>j7JqUWW>vFw z6?A=#e|uBH_eZ;)q0N=Nyw`e0cF}2R`Fh8~DBRrIQ*uP#w_3)(Gjy}akXEw3eWQZD zrly@g?_+*&@S>iJhxy?jdN+CG0ww0>n|}OwF}SApFkeJHw4N$iB_nbD_fg~DV_mm= zWz5C?ak<6e6W2m?i;dRYS~fXxCohZ9Mc%Z#|K!#ncSpLGIQuSFS(l@N z)IL5xQ*HXcBiACI>1iK(Ekbuk_2b)M!6t1z{aXnuL2@B~KfK=jd))l|Rau`i6L)3? zx=hzk8eZ5^oSNo3uv@M4Z^S9I&)e_D`Pr@v)?8(i&k=gCsh;_VDvNUFM5Z*Ll;9)RJ9! z^yon1moHf z{HKKX@9*a2y~n!iVA^33Wd+w1iiV35f=(;zvo|{}bC&r!247bR1eNXzjcAcypB0~o zOEQUA_Rk-Zvc23__l)ZDWxrBwznAC6kH11V*ak27wkZYV|DYVJ1KXkNnD1RcC?|Ubm*(Ma#y98o5I;Zktv0`dbdt4O`R6abJb!zqstZ*V`NTw(3X8$WkYlx$w%a zgKPhyo-8M2I;yZPO`JT;CFFOqV5IqO+?UYXWIPtnJ8`*6uTtEpJ5n4*nud-v414r` zKCz_Bj=QE~D1PVC+CsZI*R0xd=8n-8JZ;jrFqOLSAAhpQ-%MMBzE|5!Pq>t7RrG$C zj8YTy#W`_pML8<8JobB??nQmwy3!N5i_7g-`{1$FUU}TCepv#vBo8D88D4!4 z%4qOuxp;eQf9~jCHWP0~-#vE!;&~LOyLu8-LNdkOI^E?t)ndMeN0ITcu0ABm{U&rR zI`ZrXLF%91Z%sQ3hw>ZjiX2smNF^EGIDVx&jM942#bl@;-Oe{;=5A0^;NzJ*8c`NG)9^l-kN6(eKFKDy}6r8{p39<(#UW2*YbG8v!>Hay1RLX?`ix}dcQ^G z%oNFidtiufOu4dIx+^L*ZtKR~K5_r4I%;x`k-u9?2={>$S)@%>V$N|&lJ`s<9g%M? zE1zPkQyss45h+#9@iD=LM%_E5%lznHeSS|Mk7yY^4b8O4iA5?F)_*xqV7xP0nJ)pP6ESDv1X-%_4ycVAM{zPd7W`tnLZmYS1jatS+b@q^TU3Ju2* zUT39J4)Vd>ui~Xi=8?(m3WfBa zPg`|UB*S8>{$Uf*_^7;PJ+ck!#1#u^egpeCNH^)tx zk1*~E`Y@bTWcO81`#An&twdW2Z@Qk*!)MC)+(+wOeRxl*IcsOBj+846k3%8?d&typ zk>3$}!LNC`milrW-+p)PnoJ?hCeh+oVf{|GYe-K^;7&|tyV{2onWTzl=O1;O%;ge~ zoL~?=xui;UVs0usrBx&5aO!O z$MfQV=66vOQ5(Mf7KuJ4{F;15v9FG8??VMwY0Ad3r~P+sgL-boz*SCK_oL<2OYSeb zPUU=~P!uWM=kM>=b&|v96IX6N3+}*pL_*n$SKGcwZ`=lrlP6xQex!iyM#RooXQ*zb*XGFPFyYnSHvOYQ1=JY~^31^njSL#WE9PprDgValb-Nmct{yvZ1u?g?AYIuj}KJKtp@#A3nH?@4=!0a?XQ|U z@pH7L>*J9j>*V|Q>4N?R3v^CbJ4B_~=`!MeJgqSyRX;cOQDg57_j6(bSvT}*B@XG| zl)K^Q&tJrPuqeJIy_i)dyJ6C(eSbrd;H^7`vr3l9^~$&JD8*jCPS-a5*!y0Z-Ja`q z{PUFbky)=i{@6Nq9oO4XkA0=)mhAF_b_C~&{Q09g|LpbFwp6x4ol5+;V~dOF64TQi zg$_4$hOy|^79HMX)L#p`zF23TKP01%54>&{vsVIV`kp%%Z_UkxGi+%l@g65(E?SE? z)H6h-kLNaD3Vg_lEJ)T#Qry>Y$*jlX1$JdVvTq{smH}zX^Uow5Rx3TEd;;FjoFbS&wh-GZ)(ZnBlKxbKzn*0|u==nXaV1wm-;| zc;-*UN$#&}C2x>%sSBRKpH}|bQ8aZ2ZuQ&dbN|l`%)YUH0Y`Bss4sP9eE&;L?X+Hs zpL&qYfm|C+(&ZjUUh&U6xD|cv)6ZAFwjSNH5;{*Si6~AYuhHU?)`j1scRQ-z zqPd{KzeYv>bGCc*S||5goWSlN!!5%TPtIj4`iuNz>w#B$q3hTtr=LGNqW)9YrHJhD z-8ULtAt8R|O=K?31rq)>C5E7GnHqo5;ktaaEB&;CIS+Kja9j`2>v-pDi;ho?y=@l* z!H(Y%+|SSiiNe9esBu<$VF@So##s4;9ZYNA2Mj5VE{N7W!m%k<{z%I5`mLk`Z;Fo1 z#~c+A26(AM(zwN_2W%%CZ;~)qI^+&rAqm)(6Cm33yy?r&9k=86In(HQMFY_3xLy-$?f zg&r@T&ONH&*`M`%JYz(uQdhX^WrsMAYWN3jc+<*v;b>l_v)N3(8umFHu0*+7%H}Af zlN0Dw*Gp=19=A9-if;9Pi6MzNwu(pa%|GW&T>vdTjxBe|?Ggt77zH^;g~t5u?$sFn6T(D8B0<)X6j z89REvd=hn+Ev4U?L3gg{&zQ9N@pV&N;gFf&Lq5#Le9}e^<>Y%kcI-G_rg#>!KXoa; z!o}PV4fircl8{9o3th$^T$~DDpBY{L;*kIXsQaYqCqtN+wtExaJ|_c@VlczGuZ57hBI}HTKdq zaJS*>Lk|K-!@uHM-wno5aT|_bRqN&8prvQu>G2)+`~sQ!?3VL6oMH8B^W)pNc=J^W z;j!ip&2rO?AH$P{*^xv0acfILKR&C;{_NSzu#PX+H?V3a@g}2GxBa?Tpy*4|uO>Ce z+grjrm92+qSeae^bOTC+= z+ZQ*;Lt<=RMMAen^wsy*Nl*MHO~*%97bZr7U8wi!tUnog8PTnM}Iw>Va|r) zo(N$r-j0%+`B9q4p}EEsnV4v6`Pd=KbOpJw(Bkj!-zWQ!`C|O)6PHY;x`dD?7YnJZ zbx%6JuQV@|3~w-xbjca{KD!fnAB;{S%hGHr^;2Ic-rbg=^F_+=^1c^1Hs%~6>yvt+ zNkbxoYfVmg{`lNuI_vN4fBL(}gJjPs8O$))v*V7dD%?!M3u*AG%eiz0m5>={&2Zza( z-8bxY#O2o98>7gCI!XszMgNs0)cXbf>~QFcrnt(9>vhAmGRu=cySUqWd8YrfZ;T75C(@7_EAi^DoPJBF*2WbTtXT>!nbQwwgFEIpmW>Z?7*s$n8)%OY;I zOP`-i*R0eDOOmfG`G$=wa3KeZ5n1lj>p#dM=Y6c$U9Z)$U9uo49>l+@sHuA*?RY>n z@fS`fthlF(N=eBGsQIscJK?)Q$Zb6OS571C>2aha=h zyldx@B}p}3$4 zcqFOAn>CoNI}Ve)XQ1E5`P@~q2RT@CNmB0AGyEO8>;Fe-DA?zHKC;j=yEGed_{f$$ zt;7PJpDw24g=^2R6dy#dfH$UYiJbzxCXE{};iGSO? zXt^XfW?NJHt?vfI9dR`)agNi6iD2`Po`2ccvAB9T;6!U!^%jMG#p1|+#mv^NUE)OG z>lYYq?>0-9N(KKY_2{Bb5P$G5)8GtR)#S<)%8r zTepx7?DlYlA?O)o#u)A?7<9-@ z%symWxD0P;C5$V-QWj3k-yW(a8Hs}%81h~)P+Z-;IN^0;TK0)^U)En})aJVQ4teG1 z(Fn%F2aTM3=9m$)R`f+$rw)4LL=OXgUAnbB3Nj&2UPa`ZvJETBpqZD>M%E0u zpEmOUTAp4Wo)g%yR_YF>wQZ55m1nB%4s(_a7gd0pXDjv$332`N5B&x5C6bL{H9j-t zx+zoIk5t_~yx24M ze9V+>-%-+!H(gTf!5=LwG_J4jw{TnUal0k0A%B!_(r^NN z+Q=q?vt{_$;Kt&g^tWn7XO?AqM7PLGxr%*FnfJe=3{0coGHbmHi|+~kdD4ma(0oJt z&aP^M>)hs>ww_0U2-cT>j1!1P>q9zAw*=Z>dO+lb(ELoYK|u^;_v5%)zjmwwJTwtXC%JZ;m4LQMz8y!^}9&SNaPheU_4% z;Tx5bJsCz1tm6$=e*Z)7iLg&ev^VQ$Pci^T|rfg~e z%fg~5Sxr5H^7*HngYX&M{N|EfMH$%>D^CT5BBd>N<(>0!2?>_L(JeV;W#BcFSZc}O zH+n5lK9ufo`3bI#(Z9}JUlN{!8Ysa$zc8GT=1+_jnLkBrx0#_>%MpUo%x^T z(o0z29CPcE{EO&0xVY;Ho~!$GpFX`aiwVQGa~Xzt-4`5=WqfW6avPE+WK|st&+(wF z8hB%rzofw=mU3L%-jH!{j{ccVyS9mh^D2oGE1kjT_4kRbbvMrC?yDRiOTyV-cLuLW z_uWH{U;CTf`^wHqo7cRwWTZ(8WU~Ul>NGmkxajCB-qrdC&Gypf%sFcd?rpXDc_4H9 zK%;ZXbj6@jzZ3fQ6`W;wcy1TRBtn^Z&ab}VI@bknJ#?_%Vh|y>l8d_3_1vUgzZzzt)t~z^m|=JHXGO z2|-1dikXgP4i~0Pi4H0-Pp`WA=JSnb;?19QAJ~AKMY=3`emSP$*5i{0oJ|LJD0>+9 zS$6a8`1yu>@`dl+{4YY^y8f2A{K&5VtE?Z8qx3AXZ&>SMa9+vjy}dZ}^79JxEa%2% zViSeY5pDDjxGca?S}oIC5Dy6w;S>o><%^`_C4J#Fvd8aUN~=r-+2aj(9H_#D!HkhX zVwi?OrlMWr!C%-LAZsQW$xr0-XZF#u8cq>cR{a=o2yY+2EI5@F%5C*)Jm9r+pN&pQ z)Uktx*S?fKYHei=yy<^C)K}-i>Z#nnx*osxcmiUP!Gk{ZHVb{q@@>5U0hwZFL5sA;ly>(QoS1On%ayill<0)MS9gr@ybo*fBf3F z-tBBrg8p2KcJr^^L|m);5xgXM(jULxrsN>&#K{@)GcAUd*6aT?dT}Fh{}23%XR{~$ zeoQa(F?j*xvRi+1h$nrw>3ri)k$>b_74SY$=<=d^?Uz2H5sts8o!Wyt{Ct_ocK%#) zh#rH}9+DGbc)N3&v0KtfD<4~zyCE|YEndzo)#*q{Zd6vhG$rsCuWaz0k2K{^b`K2l zJv*Ugo=;P8MO5+j!@+?M0&P0W^!*x+p3li&G`nD~etCAFzEtP!p!`VP^sipFJwz@P z)f8uM(B5WM7T~cZ_0x%KMXcAM>qtY47vIy~^b)a|feQj=bngd#g5JSkN(Lfg?2;mIwvl3*C>`59p~@xGY3#45UIb7~}%Fk{MJwnFL7%NkWlyl`A;~iK94@ z7rMEi%+ZX+5#A&~BZWCafh##4L6SK%T5^g^Oihxlq6q~wNDN6JDFV|NR0fUw?pXwq zN#G9>s7BsBnGTEIJ(ZT0MonX?fbHmX25m*aXG?*POL!EK6h?Sb0W^yLjsjK~IN&$|9J}c(INC22o z@TichQfrKzWVCmDxmiGH3WFVL$KbG>I7}ARj?H4z*$g$g$AH{iWKgOI^`%iE;S_!X zBtS-6hzJp@L>hHCv|Hua?|sV+8y`ZXY1s}C8|Xa+#7E?!kr|0WX8?}=yGjfHzK5&u zMx6COcBWA;p`bGl0}t|q;&>@ygyfYDoM$Ak(qSQ)G%7pF%9YGZP8N%RuK`=pfTxmC zTYQKR9r|ww(baz(%2cUPBSd#86KGQ8#k3P#q>T*+v-7D?D!u zScc+24Knp>EyzDtQvpbjg6;|LxxcqVI5W3YE0Gi5TiwLPpx3%lTSlE(6nt0fn zHtKl3Js3P}g&R#3u|*ElVK(-wIU%Oxfu<-Z74QSq1tw1IT5Y4H?hKfdt|kihoU8_d zyMjge*r{1lHS8%(9Wa-o^}j!}X|l&|g6iz6=vp7z5`+A8(Yg?-Qh{PnYHBwV#0Ks! DW9GRW literal 0 HcmV?d00001 diff --git a/tests/integration/files/hollowCylinder2.dsco b/tests/integration/files/hollowCylinder2.dsco new file mode 100644 index 0000000000000000000000000000000000000000..6c7a5d9a5f418b1d5c93c7c8147b0ed395f61dfa GIT binary patch literal 149770 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