2121# SOFTWARE.
2222"""Module for creating a named selection."""
2323
24- from beartype import beartype as check_input_types
24+ from typing import TYPE_CHECKING
2525
26+ from ansys .api .dbu .v0 .dbumodels_pb2 import EntityIdentifier
2627from ansys .api .geometry .v0 .namedselections_pb2 import CreateRequest
2728from ansys .api .geometry .v0 .namedselections_pb2_grpc import NamedSelectionsStub
2829from ansys .geometry .core .connection .client import GrpcClient
30+ from ansys .geometry .core .connection .conversions import grpc_point_to_point3d
2931from ansys .geometry .core .designer .beam import Beam
3032from ansys .geometry .core .designer .body import Body
3133from ansys .geometry .core .designer .designpoint import DesignPoint
3234from ansys .geometry .core .designer .edge import Edge
3335from ansys .geometry .core .designer .face import Face
3436from ansys .geometry .core .errors import protect_grpc
37+ from ansys .geometry .core .misc .auxiliary import (
38+ get_beams_from_ids ,
39+ get_bodies_from_ids ,
40+ get_edges_from_ids ,
41+ get_faces_from_ids ,
42+ )
43+
44+ if TYPE_CHECKING :
45+ from ansys .geometry .core .designer .design import Design
3546
3647
3748class NamedSelection :
@@ -46,6 +57,8 @@ class NamedSelection:
4657 ----------
4758 name : str
4859 User-defined name for the named selection.
60+ design : Design
61+ Design instance to which the named selection belongs.
4962 grpc_client : GrpcClient
5063 Active supporting Geometry service instance for design modeling.
5164 bodies : list[Body], default: None
@@ -61,10 +74,10 @@ class NamedSelection:
6174 """
6275
6376 @protect_grpc
64- @check_input_types
6577 def __init__ (
6678 self ,
6779 name : str ,
80+ design : "Design" ,
6881 grpc_client : GrpcClient ,
6982 bodies : list [Body ] | None = None ,
7083 faces : list [Face ] | None = None ,
@@ -75,6 +88,7 @@ def __init__(
7588 ):
7689 """Initialize the ``NamedSelection`` class."""
7790 self ._name = name
91+ self ._design = design
7892 self ._grpc_client = grpc_client
7993 self ._named_selections_stub = NamedSelectionsStub (self ._grpc_client .channel )
8094
@@ -97,19 +111,26 @@ def __init__(
97111 self ._beams = beams
98112 self ._design_points = design_points
99113
114+ # Store ids for later use... when verifying if the NS changed.
115+ self ._ids_cached = {
116+ "bodies" : [body .id for body in bodies ],
117+ "faces" : [face .id for face in faces ],
118+ "edges" : [edge .id for edge in edges ],
119+ "beams" : [beam .id for beam in beams ],
120+ "design_points" : [dp .id for dp in design_points ],
121+ }
122+
100123 if preexisting_id :
101124 self ._id = preexisting_id
102125 return
103126
104127 # All ids should be unique - no duplicated values
105128 ids = set ()
106129
107- # Loop over bodies, faces and edges
108- [ids .add (body .id ) for body in bodies ]
109- [ids .add (face .id ) for face in faces ]
110- [ids .add (edge .id ) for edge in edges ]
111- [ids .add (beam .id ) for beam in beams ]
112- [ids .add (dp .id ) for dp in design_points ]
130+ # Loop over all entities to get their ids
131+ for value in self ._ids_cached .values ():
132+ for entity_id in value :
133+ ids .add (entity_id )
113134
114135 named_selection_request = CreateRequest (name = name , members = ids )
115136 self ._grpc_client .log .debug ("Requesting creation of named selection." )
@@ -129,28 +150,84 @@ def name(self) -> str:
129150 @property
130151 def bodies (self ) -> list [Body ]:
131152 """All bodies in the named selection."""
153+ self .__verify_ns ()
154+ if self ._bodies is None :
155+ # Get all bodies from the named selection
156+ self ._bodies = get_bodies_from_ids (self ._design , self ._ids_cached ["bodies" ])
157+
132158 return self ._bodies
133159
134160 @property
135161 def faces (self ) -> list [Face ]:
136162 """All faces in the named selection."""
163+ self .__verify_ns ()
164+ if self ._faces is None :
165+ # Get all faces from the named selection
166+ self ._faces = get_faces_from_ids (self ._design , self ._ids_cached ["faces" ])
167+
137168 return self ._faces
138169
139170 @property
140171 def edges (self ) -> list [Edge ]:
141172 """All edges in the named selection."""
173+ self .__verify_ns ()
174+ if self ._edges is None :
175+ # Get all edges from the named selection
176+ self ._edges = get_edges_from_ids (self ._design , self ._ids_cached ["edges" ])
177+
142178 return self ._edges
143179
144180 @property
145181 def beams (self ) -> list [Beam ]:
146182 """All beams in the named selection."""
183+ self .__verify_ns ()
184+ if self ._beams is None :
185+ # Get all beams from the named selection
186+ self ._beams = get_beams_from_ids (self ._design , self ._ids_cached ["beams" ])
187+
147188 return self ._beams
148189
149190 @property
150191 def design_points (self ) -> list [DesignPoint ]:
151192 """All design points in the named selection."""
193+ self .__verify_ns ()
194+ if self ._design_points is None :
195+ # Get all design points from the named selection
196+ self ._design_points = [
197+ DesignPoint (dp_id , f"dp: { dp_id } " , grpc_point_to_point3d (dp_point ))
198+ for dp_id , dp_point in self ._ids_cached ["design_points" ]
199+ ]
200+
152201 return self ._design_points
153202
203+ def __verify_ns (self ) -> None :
204+ """Verify that the contents of the named selection are up to date."""
205+ if self ._grpc_client .backend_version < (25 , 2 , 0 ):
206+ self ._grpc_client .log .warning (
207+ "Accessing members of named selections is only"
208+ " consistent starting in version 2025 R2."
209+ )
210+ return
211+
212+ # Get all entities from the named selection
213+ resp = self ._named_selections_stub .Get (EntityIdentifier (id = self ._id ))
214+
215+ # Check if the named selection has changed
216+ ids = {
217+ "bodies" : [body .id for body in resp .bodies ],
218+ "faces" : [face .id for face in resp .faces ],
219+ "edges" : [edge .id for edge in resp .edges ],
220+ "beams" : [beam .id .id for beam in resp .beams ],
221+ "design_points" : [(dp .id , dp .points [0 ]) for dp in resp .design_points ],
222+ }
223+
224+ for key in ids :
225+ if ids [key ] != self ._ids_cached [key ]:
226+ # Clear the cache for that specific entity
227+ setattr (self , f"_{ key } " , None )
228+ # Update the cache
229+ self ._ids_cached [key ] = ids [key ]
230+
154231 def __repr__ (self ) -> str :
155232 """Represent the ``NamedSelection`` as a string."""
156233 lines = [f"ansys.geometry.core.designer.selection.NamedSelection { hex (id (self ))} " ]
0 commit comments