Skip to content

Commit 2ae51ef

Browse files
feat: grpc common layer architecture, bodies stub and admin stub implementation (#1899)
Co-authored-by: Jacob Kerstetter <jacob.kerstetter@ansys.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent 32df1cd commit 2ae51ef

File tree

7 files changed

+261
-20
lines changed

7 files changed

+261
-20
lines changed

doc/changelog.d/1899.added.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
grpc named selection stub implementation

src/ansys/geometry/core/_grpc/_services/_service.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
from .base.admin import GRPCAdminService
2727
from .base.bodies import GRPCBodyService
2828
from .base.dbuapplication import GRPCDbuApplicationService
29+
from .base.named_selection import GRPCNamedSelectionService
2930

3031

3132
class _GRPCServices:
@@ -69,6 +70,7 @@ def __init__(self, channel: grpc.Channel, version: GeometryApiProtos | str | Non
6970
self._admin = None
7071
self._bodies = None
7172
self._dbu_application = None
73+
self._named_selection = None
7274

7375
@property
7476
def bodies(self) -> GRPCBodyService:
@@ -147,3 +149,29 @@ def dbu_application(self) -> GRPCDbuApplicationService:
147149
raise ValueError(f"Unsupported version: {self.version}")
148150

149151
return self._dbu_application
152+
153+
@property
154+
def named_selection(self) -> GRPCNamedSelectionService:
155+
"""
156+
Get the named selection service for the specified version.
157+
158+
Returns
159+
-------
160+
NamedSelectionServiceBase
161+
The named selection service for the specified version.
162+
"""
163+
if not self._named_selection:
164+
# Import the appropriate named selection service based on the version
165+
from .v0.named_selection import GRPCNamedSelectionServiceV0
166+
from .v1.named_selection import GRPCNamedSelectionServiceV1
167+
168+
if self.version == GeometryApiProtos.V0:
169+
self._named_selection = GRPCNamedSelectionServiceV0(self.channel)
170+
elif self.version == GeometryApiProtos.V1: # pragma: no cover
171+
# V1 is not implemented yet
172+
self._named_selection = GRPCNamedSelectionServiceV1(self.channel)
173+
else: # pragma: no cover
174+
# This should never happen as the version is set in the constructor
175+
raise ValueError(f"Unsupported version: {self.version}")
176+
177+
return self._named_selection
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# Copyright (C) 2023 - 2025 ANSYS, Inc. and/or its affiliates.
2+
# SPDX-License-Identifier: MIT
3+
#
4+
#
5+
# Permission is hereby granted, free of charge, to any person obtaining a copy
6+
# of this software and associated documentation files (the "Software"), to deal
7+
# in the Software without restriction, including without limitation the rights
8+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
# copies of the Software, and to permit persons to whom the Software is
10+
# furnished to do so, subject to the following conditions:
11+
#
12+
# The above copyright notice and this permission notice shall be included in all
13+
# copies or substantial portions of the Software.
14+
#
15+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
# SOFTWARE.
22+
"""Module containing the Named Selection service implementation (abstraction layer)."""
23+
24+
from abc import ABC, abstractmethod
25+
26+
import grpc
27+
28+
29+
class GRPCNamedSelectionService(ABC):
30+
"""Named Selection service for gRPC communication with the Geometry server.
31+
32+
Parameters
33+
----------
34+
channel : grpc.Channel
35+
The gRPC channel to the server.
36+
"""
37+
38+
def __init__(self, channel: grpc.Channel):
39+
"""Initialize the GRPCNamedSelectionService class."""
40+
pass # pragma: no cover
41+
42+
@abstractmethod
43+
def get_named_selection(self, **kwargs) -> dict:
44+
"""Get the named selection by its id."""
45+
pass # pragma: no cover
46+
47+
@abstractmethod
48+
def create_named_selection(self, **kwargs) -> dict:
49+
"""Create a named selection."""
50+
pass
51+
52+
@abstractmethod
53+
def delete_named_selection(self, **kwargs) -> dict:
54+
"""Delete a named selection by id."""
55+
pass
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
# Copyright (C) 2023 - 2025 ANSYS, Inc. and/or its affiliates.
2+
# SPDX-License-Identifier: MIT
3+
#
4+
#
5+
# Permission is hereby granted, free of charge, to any person obtaining a copy
6+
# of this software and associated documentation files (the "Software"), to deal
7+
# in the Software without restriction, including without limitation the rights
8+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
# copies of the Software, and to permit persons to whom the Software is
10+
# furnished to do so, subject to the following conditions:
11+
#
12+
# The above copyright notice and this permission notice shall be included in all
13+
# copies or substantial portions of the Software.
14+
#
15+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
# SOFTWARE.
22+
"""Module containing the Named Selection service implementation for v0."""
23+
24+
import grpc
25+
26+
from ansys.geometry.core.errors import protect_grpc
27+
28+
from ..base.named_selection import GRPCNamedSelectionService
29+
30+
31+
class GRPCNamedSelectionServiceV0(GRPCNamedSelectionService):
32+
"""Named Selection service for gRPC communication with the Geometry server.
33+
34+
This class provides methods to interact with the Geometry server's
35+
Named Selection service. It is specifically designed for the v0 version
36+
of the Geometry API.
37+
38+
Parameters
39+
----------
40+
channel : grpc.Channel
41+
The gRPC channel to the server.
42+
"""
43+
44+
@protect_grpc
45+
def __init__(self, channel: grpc.Channel): # noqa: D102
46+
from ansys.api.geometry.v0.namedselections_pb2_grpc import NamedSelectionsStub
47+
48+
self.stub = NamedSelectionsStub(channel)
49+
50+
@protect_grpc
51+
def get_named_selection(self, **kwargs): # noqa: D102
52+
from ansys.api.dbu.v0.dbumodels_pb2 import EntityIdentifier
53+
54+
# Create the request - assumes all inputs are valid and of the proper type
55+
request = EntityIdentifier(id=kwargs["id"])
56+
57+
# Call the gRPC service
58+
response = self.stub.Get(request)
59+
60+
# Return the response - formatted as a dictionary
61+
return {
62+
"id": response.id,
63+
"name": response.name,
64+
"bodies": [body.id for body in response.bodies],
65+
"faces": [face.id for face in response.faces],
66+
"edges": [edge.id for edge in response.edges],
67+
"beams": [beam.id.id for beam in response.beams],
68+
"design_points": [(dp.id, dp.points[0]) for dp in response.design_points],
69+
}
70+
71+
@protect_grpc
72+
def create_named_selection(self, **kwargs): # noqa: D102
73+
from ansys.api.geometry.v0.namedselections_pb2 import CreateRequest
74+
75+
# Create the request - assumes all inputs are valid and of the proper type
76+
request = CreateRequest(
77+
name=kwargs["name"],
78+
members=kwargs["members"],
79+
)
80+
81+
# Call the gRPC service
82+
response = self.stub.Create(request)
83+
84+
# Return the response - formatted as a dictionary
85+
return {
86+
"id": response.id,
87+
"name": response.name,
88+
"bodies": [body.id for body in response.bodies],
89+
"faces": [face.id for face in response.faces],
90+
"edges": [edge.id for edge in response.edges],
91+
"beams": [beam.id.id for beam in response.beams],
92+
"design_points": [dp.id for dp in response.design_points],
93+
}
94+
95+
@protect_grpc
96+
def delete_named_selection(self, **kwargs): # noqa: D102
97+
from ansys.api.dbu.v0.dbumodels_pb2 import EntityIdentifier
98+
99+
# Create the request - assumes all inputs are valid and of the proper type
100+
request = EntityIdentifier(id=kwargs["id"])
101+
102+
# Call the gRPC service
103+
self.stub.Delete(request)
104+
105+
# Return the response - empty dictionary
106+
return {}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# Copyright (C) 2023 - 2025 ANSYS, Inc. and/or its affiliates.
2+
# SPDX-License-Identifier: MIT
3+
#
4+
#
5+
# Permission is hereby granted, free of charge, to any person obtaining a copy
6+
# of this software and associated documentation files (the "Software"), to deal
7+
# in the Software without restriction, including without limitation the rights
8+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
# copies of the Software, and to permit persons to whom the Software is
10+
# furnished to do so, subject to the following conditions:
11+
#
12+
# The above copyright notice and this permission notice shall be included in all
13+
# copies or substantial portions of the Software.
14+
#
15+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
# SOFTWARE.
22+
"""Module containing the Named Selection service implementation for v1."""
23+
24+
import grpc
25+
26+
from ansys.geometry.core.errors import protect_grpc
27+
28+
from ..base.named_selection import GRPCNamedSelectionService
29+
30+
31+
class GRPCNamedSelectionServiceV1(GRPCNamedSelectionService):
32+
"""Named Selection service for gRPC communication with the Geometry server.
33+
34+
This class provides methods to interact with the Geometry server's
35+
Named Selection service. It is specifically designed for the v1 version
36+
of the Geometry API.
37+
38+
Parameters
39+
----------
40+
channel : grpc.Channel
41+
The gRPC channel to the server.
42+
"""
43+
44+
@protect_grpc
45+
def __init__(self, channel: grpc.Channel): # noqa: D102
46+
from ansys.api.geometry.v1.namedselections_pb2_grpc import NamedSelectionsStub
47+
48+
self.stub = NamedSelectionsStub(channel)
49+
50+
@protect_grpc
51+
def get_named_selection(self, **kwargs): # noqa: D102
52+
raise NotImplementedError
53+
54+
@protect_grpc
55+
def create_named_selection(self, **kwargs): # noqa: D102
56+
raise NotImplementedError
57+
58+
@protect_grpc
59+
def delete_named_selection(self, **kwargs): # noqa: D102
60+
raise NotImplementedError

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

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@
5252
Material as GRPCMaterial,
5353
MaterialProperty as GRPCMaterialProperty,
5454
)
55-
from ansys.api.geometry.v0.namedselections_pb2_grpc import NamedSelectionsStub
5655
from ansys.api.geometry.v0.parts_pb2 import ExportRequest
5756
from ansys.api.geometry.v0.parts_pb2_grpc import PartsStub
5857
from ansys.geometry.core.connection.backend import BackendType
@@ -146,7 +145,6 @@ def __init__(self, name: str, modeler: Modeler, read_existing_design: bool = Fal
146145
self._design_stub = DesignsStub(self._grpc_client.channel)
147146
self._commands_stub = CommandsStub(self._grpc_client.channel)
148147
self._materials_stub = MaterialsStub(self._grpc_client.channel)
149-
self._named_selections_stub = NamedSelectionsStub(self._grpc_client.channel)
150148
self._parts_stub = PartsStub(self._grpc_client.channel)
151149
self._parameters_stub = DrivingDimensionsStub(self._grpc_client.channel)
152150

@@ -697,7 +695,6 @@ def create_named_selection(
697695

698696
return self._named_selections[named_selection.name]
699697

700-
@protect_grpc
701698
@check_input_types
702699
@ensure_design_is_active
703700
def delete_named_selection(self, named_selection: NamedSelection | str) -> None:
@@ -717,7 +714,7 @@ def delete_named_selection(self, named_selection: NamedSelection | str) -> None:
717714
removal_id = named_selection.id
718715

719716
self._grpc_client.log.debug(f"Named selection {removal_name} deletion request received.")
720-
self._named_selections_stub.Delete(EntityIdentifier(id=removal_id))
717+
self._grpc_client.services.named_selection.delete_named_selection(id=removal_id)
721718

722719
try:
723720
self._named_selections.pop(removal_name)

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

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,13 @@
2323

2424
from typing import TYPE_CHECKING
2525

26-
from ansys.api.dbu.v0.dbumodels_pb2 import EntityIdentifier
27-
from ansys.api.geometry.v0.namedselections_pb2 import CreateRequest
28-
from ansys.api.geometry.v0.namedselections_pb2_grpc import NamedSelectionsStub
2926
from ansys.geometry.core.connection.client import GrpcClient
3027
from ansys.geometry.core.connection.conversions import grpc_point_to_point3d
3128
from ansys.geometry.core.designer.beam import Beam
3229
from ansys.geometry.core.designer.body import Body
3330
from ansys.geometry.core.designer.designpoint import DesignPoint
3431
from ansys.geometry.core.designer.edge import Edge
3532
from ansys.geometry.core.designer.face import Face
36-
from ansys.geometry.core.errors import protect_grpc
3733
from ansys.geometry.core.misc.auxiliary import (
3834
get_beams_from_ids,
3935
get_bodies_from_ids,
@@ -73,7 +69,6 @@ class NamedSelection:
7369
All design points to include in the named selection.
7470
"""
7571

76-
@protect_grpc
7772
def __init__(
7873
self,
7974
name: str,
@@ -90,7 +85,6 @@ def __init__(
9085
self._name = name
9186
self._design = design
9287
self._grpc_client = grpc_client
93-
self._named_selections_stub = NamedSelectionsStub(self._grpc_client.channel)
9488

9589
# Create empty arrays if there are none of a type
9690
if bodies is None:
@@ -132,10 +126,10 @@ def __init__(
132126
for entity_id in value:
133127
ids.add(entity_id)
134128

135-
named_selection_request = CreateRequest(name=name, members=ids)
136-
self._grpc_client.log.debug("Requesting creation of named selection.")
137-
new_named_selection = self._named_selections_stub.Create(named_selection_request)
138-
self._id = new_named_selection.id
129+
response = self._grpc_client.services.named_selection.create_named_selection(
130+
name=name, members=ids
131+
)
132+
self._id = response["id"]
139133

140134
@property
141135
def id(self) -> str:
@@ -210,15 +204,15 @@ def __verify_ns(self) -> None:
210204
return
211205

212206
# Get all entities from the named selection
213-
resp = self._named_selections_stub.Get(EntityIdentifier(id=self._id))
207+
resp = self._grpc_client.services.named_selection.get_named_selection(id=self._id)
214208

215209
# Check if the named selection has changed
216210
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],
211+
"bodies": resp["bodies"],
212+
"faces": resp["faces"],
213+
"edges": resp["edges"],
214+
"beams": resp["beams"],
215+
"design_points": resp["design_points"],
222216
}
223217

224218
for key in ids:

0 commit comments

Comments
 (0)