Skip to content

Commit 5e7f276

Browse files
feat: caching bodies to avoid unnecessary object creation (#1513)
Co-authored-by: pyansys-ci-bot <[email protected]>
1 parent eb88ca3 commit 5e7f276

File tree

4 files changed

+65
-1
lines changed

4 files changed

+65
-1
lines changed

doc/changelog.d/1513.added.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
caching bodies to avoid unnecessary object creation

src/ansys/geometry/core/designer/body.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1097,6 +1097,7 @@ def copy(self, parent: "Component", name: str = None) -> "Body": # noqa: D102
10971097
response.master_id, copy_name, self._grpc_client, is_surface=self.is_surface
10981098
)
10991099
parent._master_component.part.bodies.append(tb)
1100+
parent._clear_cached_bodies()
11001101
body_id = f"{parent.id}/{tb.id}" if parent.parent_component else tb.id
11011102
return Body(body_id, response.name, parent, tb)
11021103

src/ansys/geometry/core/designer/component.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
"""Provides for managing components."""
2323

2424
from enum import Enum, unique
25+
from functools import cached_property
2526
from typing import TYPE_CHECKING, Any, Optional, Union
2627
import uuid
2728

@@ -239,6 +240,11 @@ def __init__(
239240

240241
self._master_component.occurrences.append(self)
241242

243+
def _clear_cached_bodies(self) -> None:
244+
"""Clear the cached bodies."""
245+
if "bodies" in self.__dict__:
246+
del self.__dict__["bodies"]
247+
242248
@property
243249
def id(self) -> str:
244250
"""ID of the component."""
@@ -259,7 +265,7 @@ def components(self) -> list["Component"]:
259265
"""List of ``Component`` objects inside of the component."""
260266
return self._components
261267

262-
@property
268+
@cached_property
263269
def bodies(self) -> list[Body]:
264270
"""List of ``Body`` objects inside of the component."""
265271
bodies = []
@@ -509,6 +515,7 @@ def extrude_sketch(
509515
response = self._bodies_stub.CreateExtrudedBody(request)
510516
tb = MasterBody(response.master_id, name, self._grpc_client, is_surface=False)
511517
self._master_component.part.bodies.append(tb)
518+
self._clear_cached_bodies()
512519
return Body(response.id, response.name, self, tb)
513520

514521
@min_backend_version(24, 2, 0)
@@ -558,6 +565,7 @@ def sweep_sketch(
558565
response = self._bodies_stub.CreateSweepingProfile(request)
559566
tb = MasterBody(response.master_id, name, self._grpc_client, is_surface=False)
560567
self._master_component.part.bodies.append(tb)
568+
self._clear_cached_bodies()
561569
return Body(response.id, response.name, self, tb)
562570

563571
@min_backend_version(24, 2, 0)
@@ -605,6 +613,7 @@ def sweep_chain(
605613
response = self._bodies_stub.CreateSweepingChain(request)
606614
tb = MasterBody(response.master_id, name, self._grpc_client, is_surface=True)
607615
self._master_component.part.bodies.append(tb)
616+
self._clear_cached_bodies()
608617
return Body(response.id, response.name, self, tb)
609618

610619
@min_backend_version(24, 2, 0)
@@ -720,6 +729,7 @@ def extrude_face(
720729

721730
tb = MasterBody(response.master_id, name, self._grpc_client, is_surface=False)
722731
self._master_component.part.bodies.append(tb)
732+
self._clear_cached_bodies()
723733
return Body(response.id, response.name, self, tb)
724734

725735
@protect_grpc
@@ -753,6 +763,7 @@ def create_sphere(self, name: str, center: Point3D, radius: Distance) -> Body:
753763
response = self._bodies_stub.CreateSphereBody(request)
754764
tb = MasterBody(response.master_id, name, self._grpc_client, is_surface=False)
755765
self._master_component.part.bodies.append(tb)
766+
self._clear_cached_bodies()
756767
return Body(response.id, response.name, self, tb)
757768

758769
@protect_grpc
@@ -819,6 +830,7 @@ def create_body_from_loft_profile(
819830
response = self._bodies_stub.CreateExtrudedBodyFromLoftProfiles(request)
820831
tb = MasterBody(response.master_id, name, self._grpc_client, is_surface=False)
821832
self._master_component.part.bodies.append(tb)
833+
self._clear_cached_bodies()
822834
return Body(response.id, response.name, self, tb)
823835

824836
@protect_grpc
@@ -856,6 +868,7 @@ def create_surface(self, name: str, sketch: Sketch) -> Body:
856868

857869
tb = MasterBody(response.master_id, name, self._grpc_client, is_surface=True)
858870
self._master_component.part.bodies.append(tb)
871+
self._clear_cached_bodies()
859872
return Body(response.id, response.name, self, tb)
860873

861874
@protect_grpc
@@ -896,6 +909,7 @@ def create_surface_from_face(self, name: str, face: Face) -> Body:
896909

897910
tb = MasterBody(response.master_id, name, self._grpc_client, is_surface=True)
898911
self._master_component.part.bodies.append(tb)
912+
self._clear_cached_bodies()
899913
return Body(response.id, response.name, self, tb)
900914

901915
@protect_grpc
@@ -936,6 +950,7 @@ def create_body_from_surface(self, name: str, trimmed_surface: TrimmedSurface) -
936950

937951
tb = MasterBody(response.master_id, name, self._grpc_client, is_surface=response.is_surface)
938952
self._master_component.part.bodies.append(tb)
953+
self._clear_cached_bodies()
939954
return Body(response.id, response.name, self, tb)
940955

941956
@check_input_types
@@ -1136,6 +1151,7 @@ def delete_body(self, body: Body | str) -> None:
11361151
# on the client side
11371152
body_requested._is_alive = False
11381153
self._grpc_client.log.debug(f"Body {body_requested.id} has been deleted.")
1154+
self._clear_cached_bodies()
11391155
else:
11401156
self._grpc_client.log.warning(
11411157
f"Body {id} is not found in this component (or subcomponents)."

tests/integration/test_design.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2738,3 +2738,49 @@ def test_surface_body_creation(modeler: Modeler):
27382738
assert len(design.bodies) == 6
27392739
assert not body.is_surface
27402740
assert body.faces[0].area.m == pytest.approx(39.4784176044 * 2)
2741+
2742+
2743+
def test_cached_bodies(modeler: Modeler):
2744+
"""Test verifying that bodies are cached correctly.
2745+
2746+
Whenever a new body is created, modified etc. we should make sure that the cache is updated.
2747+
"""
2748+
design = modeler.create_design("ModelingDemo")
2749+
2750+
# Define a sketch
2751+
origin = Point3D([0, 0, 10])
2752+
plane = Plane(origin, direction_x=[1, 0, 0], direction_y=[0, 1, 0])
2753+
2754+
# Create a sketch
2755+
sketch_box = Sketch(plane)
2756+
sketch_box.box(Point2D([20, 20]), 30 * UNITS.m, 30 * UNITS.m)
2757+
2758+
sketch_cylinder = Sketch(plane)
2759+
sketch_cylinder.circle(Point2D([20, 20]), 5 * UNITS.m)
2760+
2761+
design.extrude_sketch(name="BoxBody", sketch=sketch_box, distance=Distance(30, unit=UNITS.m))
2762+
design.extrude_sketch(
2763+
name="CylinderBody",
2764+
sketch=sketch_cylinder,
2765+
distance=Distance(60, unit=UNITS.m),
2766+
)
2767+
2768+
my_bodies = design.bodies
2769+
my_bodies_2 = design.bodies
2770+
2771+
# We should make sure that the object memory addresses are the same
2772+
for body1, body2 in zip(my_bodies, my_bodies_2):
2773+
assert body1 is body2 # We are comparing the memory addresses
2774+
assert id(body1) == id(body2)
2775+
2776+
design.extrude_sketch(
2777+
name="CylinderBody2",
2778+
sketch=sketch_cylinder,
2779+
distance=Distance(20, unit=UNITS.m),
2780+
direction="-",
2781+
)
2782+
my_bodies_3 = design.bodies
2783+
2784+
for body1, body3 in zip(my_bodies, my_bodies_3):
2785+
assert body1 is not body3
2786+
assert id(body1) != id(body3)

0 commit comments

Comments
 (0)