Skip to content

Commit 044b6a6

Browse files
RobPasMuechadqueenRevathyvenugopal162
authored
Implement beam deletion (#207)
Co-authored-by: Chad Queen <chadqueen82@gmail.com> Co-authored-by: Revathy Venugopal <104772255+Revathyvenugopal162@users.noreply.github.com>
1 parent 2922bcf commit 044b6a6

File tree

5 files changed

+184
-15
lines changed

5 files changed

+184
-15
lines changed

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ def __init__(
168168
self._end = end
169169
self._profile = profile
170170
self._parent_component = parent_component
171+
self._is_alive = True
171172

172173
@property
173174
def id(self) -> str:
@@ -194,9 +195,15 @@ def parent_component(self) -> Union["Component", None]:
194195
"""Component node that the beam`` is under."""
195196
return self._parent_component
196197

198+
@property
199+
def is_alive(self) -> bool:
200+
"""Boolean indicating whether the beam is still alive on the server side."""
201+
return self._is_alive
202+
197203
def __repr__(self) -> str:
198204
"""String representation of the beam."""
199205
lines = [f"ansys.geometry.core.designer.Beam {hex(id(self))}"]
206+
lines.append(f" Exists : {self.is_alive}")
200207
lines.append(
201208
f" Start : [{','.join([str(x) for x in self.start])}] in meters"
202209
)

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

Lines changed: 81 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -674,10 +674,10 @@ def delete_component(self, component: Union["Component", str]) -> None:
674674
675675
Parameters
676676
----------
677-
id : Union[Component, str]
678-
Name of the component or instance to delete.
677+
component : Union[Component, str]
678+
ID of the component or instance to delete.
679679
"""
680-
id = component.id if not isinstance(component, str) else component
680+
id = component if isinstance(component, str) else component.id
681681
component_requested = self.search_component(id)
682682

683683
if component_requested:
@@ -708,10 +708,10 @@ def delete_body(self, body: Union[Body, str]) -> None:
708708
709709
Parameters
710710
----------
711-
id : Union[Body, str]
712-
Name of the body or instance to delete.
711+
body : Union[Body, str]
712+
ID of the body or instance to delete.
713713
"""
714-
id = body.id if not isinstance(body, str) else body
714+
id = body if isinstance(body, str) else body.id
715715
body_requested = self.search_body(id)
716716

717717
if body_requested:
@@ -730,6 +730,44 @@ def delete_body(self, body: Union[Body, str]) -> None:
730730
)
731731
pass
732732

733+
@protect_grpc
734+
@check_input_types
735+
def delete_beam(self, beam: Union[Beam, str]) -> None:
736+
"""Deletes an existing beam belonging to this component (or its children).
737+
738+
Notes
739+
-----
740+
If the beam does not belong to this component (or its children), it
741+
will not be deleted.
742+
743+
Parameters
744+
----------
745+
beam : Union[Beam, str]
746+
ID of the beam or instance to delete.
747+
"""
748+
id = beam if isinstance(beam, str) else beam.id
749+
beam_requested = self.search_beam(id)
750+
751+
if beam_requested:
752+
# If the beam belongs to this component (or nested components)
753+
# call the server deletion mechanism
754+
#
755+
# Server-side, the same deletion request has to be performed
756+
# as for deleting a Body
757+
#
758+
self._commands_stub.DeleteBeam(EntityIdentifier(id=beam_requested.id))
759+
760+
# If the beam was deleted from the server side... "kill" it
761+
# on the client side
762+
beam_requested._is_alive = False
763+
self._grpc_client.log.debug(f"Beam {beam_requested.id} has been deleted.")
764+
else:
765+
self._grpc_client.log.warning(
766+
f"Beam {id} not found in this component (or sub-components)."
767+
+ " Ignoring deletion request."
768+
)
769+
pass
770+
733771
@check_input_types
734772
def search_component(self, id: str) -> Union["Component", None]:
735773
"""Search nested components recursively for a component.
@@ -787,17 +825,46 @@ def search_body(self, id: str) -> Union[Body, None]:
787825
# If you reached this point... this means that no body was found!
788826
return None
789827

828+
@check_input_types
829+
def search_beam(self, id: str) -> Union[Beam, None]:
830+
"""Search beams in component and nested components recursively for a beam.
831+
832+
Parameters
833+
----------
834+
id : str
835+
The ``Beam`` ID we are searching for.
836+
837+
Returns
838+
-------
839+
Union[Beam, None]
840+
The ``Beam`` with the requested ID. If not found, it will return ``None``.
841+
"""
842+
# Search in component's beams
843+
for beam in self.beams:
844+
if beam.id == id and beam.is_alive:
845+
return beam
846+
847+
# If no luck, search on nested components
848+
result = None
849+
for component in self.components:
850+
result = component.search_beam(id)
851+
if result:
852+
return result
853+
854+
# If you reached this point... this means that no beam was found!
855+
return None
856+
790857
def _kill_component_on_client(self) -> None:
791-
"""Set the ``is_alive`` property of nested components and bodies to ``False``.
858+
"""Sets the ``is_alive`` property of nested objects to ``False``.
792859
793860
Notes
794861
-----
795862
This method is recursive. It is only to be used by the
796863
``delete_component()`` method and itself."""
797864

798-
# Kill all its bodies
799-
for body in self.bodies:
800-
body._is_alive = False
865+
# Kill all its bodies, beams and coordinate systems
866+
for elem in [*self.bodies, *self.beams, *self._coordinate_systems]:
867+
elem._is_alive = False
801868

802869
# Now, go to the nested components and kill them as well
803870
for component in self.components:
@@ -971,13 +1038,15 @@ def plot(
9711038
def __repr__(self) -> str:
9721039
"""String representation of the component."""
9731040
alive_bodies = [1 if body.is_alive else 0 for body in self.bodies]
1041+
alive_beams = [1 if beam.is_alive else 0 for beam in self.beams]
1042+
alive_coords = [1 if cs.is_alive else 0 for cs in self.coordinate_systems]
9741043
alive_comps = [1 if comp.is_alive else 0 for comp in self.components]
9751044
lines = [f"ansys.geometry.core.designer.Component {hex(id(self))}"]
9761045
lines.append(f" Name : {self.name}")
9771046
lines.append(f" Exists : {self.is_alive}")
9781047
lines.append(f" Parent component : {self.parent_component.name}")
9791048
lines.append(f" N Bodies : {sum(alive_bodies)}")
980-
lines.append(f" N Beams : {len(self.beams)}")
1049+
lines.append(f" N Beams : {sum(alive_beams)}")
1050+
lines.append(f" N Coordinate Systems : {sum(alive_coords)}")
9811051
lines.append(f" N Components : {sum(alive_comps)}")
982-
lines.append(f" N Coordinate Systems : {len(self.coordinate_systems)}")
9831052
return "\n".join(lines)

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ def __init__(
7676
]
7777
),
7878
)
79+
self._is_alive = True
7980

8081
@property
8182
def id(self) -> str:
@@ -97,10 +98,16 @@ def parent_component(self) -> "Component":
9798
"""Parent component of the coordinate system."""
9899
return self._parent_component
99100

101+
@property
102+
def is_alive(self) -> bool:
103+
"""Boolean indicating whether the CoordinateSystem is still alive on the server side."""
104+
return self._is_alive
105+
100106
def __repr__(self):
101107
"""String representation of the coordinate system."""
102108
lines = [f"ansys.geometry.core.designer.CoordinateSystem {hex(id(self))}"]
103109
lines.append(f" Name : {self.name}")
110+
lines.append(f" Exists : {self.is_alive}")
104111
lines.append(f" Parent component : {self.parent_component.name}")
105112
lines.append(
106113
f" Frame origin : [{','.join([str(x) for x in self.frame.origin])}] in meters"

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

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,7 @@ def delete_named_selection(self, named_selection: Union[NamedSelection, str]) ->
259259
removal_name = named_selection.name
260260
removal_id = named_selection.id
261261

262+
self._grpc_client.log.debug(f"Named selection {removal_name} deletion request received.")
262263
self._named_selections_stub.Delete(EntityIdentifier(id=removal_id))
263264

264265
try:
@@ -290,7 +291,7 @@ def delete_component(self, component: Union["Component", str]) -> None:
290291
ValueError
291292
The design itself cannot be deleted.
292293
"""
293-
id = component.id if not isinstance(component, str) else component
294+
id = component if isinstance(component, str) else component.id
294295
if id == self.id:
295296
raise ValueError("The design itself cannot be deleted.")
296297
else:
@@ -442,6 +443,30 @@ def add_midsurface_offset(self, offset_type: MidSurfaceOffsetType, bodies: List[
442443
for body in ids_bodies:
443444
body._surface_offset = offset_type
444445

446+
@protect_grpc
447+
@check_input_types
448+
def delete_beam_profile(self, beam_profile: Union[BeamProfile, str]) -> None:
449+
"""Removes a beam profile on the active geometry server instance.
450+
451+
Parameters
452+
----------
453+
beam_profile : Union[BeamProfile, str]
454+
A beam profile name or instance that should be deleted.
455+
"""
456+
removal_name = beam_profile if isinstance(beam_profile, str) else beam_profile.name
457+
self._grpc_client.log.debug(f"Beam profile {removal_name} deletion request received.")
458+
removal_obj = self._beam_profiles.get(removal_name, None)
459+
460+
if removal_obj:
461+
self._commands_stub.DeleteBeamProfile(EntityIdentifier(id=removal_obj.id))
462+
self._beam_profiles.pop(removal_name)
463+
self._grpc_client.log.debug(f"Beam profile {removal_name} successfully deleted.")
464+
else:
465+
self._grpc_client.log.warning(
466+
f"Attempted beam profile deletion failed, with name {removal_name}."
467+
+ " Ignoring request."
468+
)
469+
445470
def __repr__(self):
446471
"""String representation of the design."""
447472
alive_bodies = [1 if body.is_alive else 0 for body in self.bodies]

tests/integration/test_design.py

Lines changed: 63 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,7 @@ def test_coordinate_system_creation(modeler: Modeler):
427427
nested_comp_cs1_str = str(nested_comp_cs1)
428428
assert "ansys.geometry.core.designer.CoordinateSystem" in nested_comp_cs1_str
429429
assert " Name : CompCS1" in nested_comp_cs1_str
430+
assert " Exists : True" in nested_comp_cs1_str
430431
assert " Parent component : NestedComponent" in nested_comp_cs1_str
431432
assert " Frame origin : [0.01,0.2,3.0] in meters" in nested_comp_cs1_str
432433
assert " Frame X-direction : " in nested_comp_cs1_str
@@ -974,6 +975,7 @@ def test_beams(modeler: Modeler, skip_not_on_linux_service):
974975
UnitVector3D([-1, -1, -1]),
975976
)
976977

978+
# Create a beam at the root component level
977979
beam_1 = design.create_beam(
978980
Point3D([9, 99, 999], UNITS.mm), Point3D([8, 88, 888], UNITS.mm), circle_profile_1
979981
)
@@ -983,11 +985,13 @@ def test_beams(modeler: Modeler, skip_not_on_linux_service):
983985
assert beam_1.end == Point3D([8, 88, 888], UNITS.mm)
984986
assert beam_1.profile == circle_profile_1
985987
assert beam_1.parent_component.id == design.id
988+
assert beam_1.is_alive
986989
assert len(design.beams) == 1
987990
assert design.beams[0] == beam_1
988991

989992
beam_1_str = str(beam_1)
990993
assert "ansys.geometry.core.designer.Beam" in beam_1_str
994+
assert " Exists : True" in beam_1_str
991995
assert " Start : [0.009" in beam_1_str
992996
assert " End : [0.008" in beam_1_str
993997
assert " Parent component : BeamCreation" in beam_1_str
@@ -1000,17 +1004,74 @@ def test_beams(modeler: Modeler, skip_not_on_linux_service):
10001004
assert " Direction x : [1.0,0.0,0.0]" in beam_1_str
10011005
assert " Direction y : [0.0,1.0,0.0]" in beam_1_str
10021006

1007+
# Now, let's create two beams at a nested component, with the same profile
10031008
nested_component = design.add_component("NestedComponent")
1004-
10051009
beam_2 = nested_component.create_beam(
10061010
Point3D([7, 77, 777], UNITS.mm), Point3D([6, 66, 666], UNITS.mm), circle_profile_2
10071011
)
1012+
beam_3 = nested_component.create_beam(
1013+
Point3D([8, 88, 888], UNITS.mm), Point3D([7, 77, 777], UNITS.mm), circle_profile_2
1014+
)
10081015

10091016
assert beam_2.id is not None
10101017
assert beam_2.profile == circle_profile_2
10111018
assert beam_2.parent_component.id == nested_component.id
1012-
assert len(nested_component.beams) == 1
1019+
assert beam_2.is_alive
1020+
assert beam_3.id is not None
1021+
assert beam_3.profile == circle_profile_2
1022+
assert beam_3.parent_component.id == nested_component.id
1023+
assert beam_3.is_alive
1024+
assert beam_2.id != beam_3.id
1025+
assert len(nested_component.beams) == 2
10131026
assert nested_component.beams[0] == beam_2
1027+
assert nested_component.beams[1] == beam_3
1028+
1029+
# Once the beams are created, let's try deleting it.
1030+
# For example, we shouldn't be able to delete beam_1 from the nested component.
1031+
nested_component.delete_beam(beam_1)
1032+
1033+
assert beam_2.is_alive
1034+
assert nested_component.beams[0].is_alive
1035+
assert beam_3.is_alive
1036+
assert nested_component.beams[1].is_alive
1037+
assert beam_1.is_alive
1038+
assert design.beams[0].is_alive
1039+
1040+
# Let's try deleting one of the beams from the nested component
1041+
nested_component.delete_beam(beam_2)
1042+
assert not beam_2.is_alive
1043+
assert not nested_component.beams[0].is_alive
1044+
assert beam_3.is_alive
1045+
assert nested_component.beams[1].is_alive
1046+
assert beam_1.is_alive
1047+
assert design.beams[0].is_alive
1048+
1049+
# Now, let's try deleting it from the design directly - this should be possible
1050+
design.delete_beam(beam_3)
1051+
assert not beam_2.is_alive
1052+
assert not nested_component.beams[0].is_alive
1053+
assert not beam_3.is_alive
1054+
assert not nested_component.beams[1].is_alive
1055+
assert beam_1.is_alive
1056+
assert design.beams[0].is_alive
1057+
1058+
# Finally, let's delete the beam from the root component
1059+
design.delete_beam(beam_1)
1060+
assert not beam_2.is_alive
1061+
assert not nested_component.beams[0].is_alive
1062+
assert not beam_3.is_alive
1063+
assert not nested_component.beams[1].is_alive
1064+
assert not beam_1.is_alive
1065+
assert not design.beams[0].is_alive
1066+
1067+
# Now, let's try deleting the beam profiles!
1068+
assert len(design.beam_profiles) == 2
1069+
design.delete_beam_profile("MyInventedBeamProfile")
1070+
assert len(design.beam_profiles) == 2
1071+
design.delete_beam_profile(circle_profile_1)
1072+
assert len(design.beam_profiles) == 1
1073+
design.delete_beam_profile(circle_profile_2)
1074+
assert len(design.beam_profiles) == 0
10141075

10151076

10161077
def test_midsurface_properties(modeler: Modeler):

0 commit comments

Comments
 (0)