Skip to content

feat: grpc named selection stub implementation #1899

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Apr 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/changelog.d/1899.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
grpc named selection stub implementation
28 changes: 28 additions & 0 deletions src/ansys/geometry/core/_grpc/_services/_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
from .base.admin import GRPCAdminService
from .base.bodies import GRPCBodyService
from .base.dbuapplication import GRPCDbuApplicationService
from .base.named_selection import GRPCNamedSelectionService


class _GRPCServices:
Expand Down Expand Up @@ -69,6 +70,7 @@ def __init__(self, channel: grpc.Channel, version: GeometryApiProtos | str | Non
self._admin = None
self._bodies = None
self._dbu_application = None
self._named_selection = None

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

return self._dbu_application

@property
def named_selection(self) -> GRPCNamedSelectionService:
"""
Get the named selection service for the specified version.

Returns
-------
NamedSelectionServiceBase
The named selection service for the specified version.
"""
if not self._named_selection:
# Import the appropriate named selection service based on the version
from .v0.named_selection import GRPCNamedSelectionServiceV0
from .v1.named_selection import GRPCNamedSelectionServiceV1

if self.version == GeometryApiProtos.V0:
self._named_selection = GRPCNamedSelectionServiceV0(self.channel)
elif self.version == GeometryApiProtos.V1: # pragma: no cover
# V1 is not implemented yet
self._named_selection = GRPCNamedSelectionServiceV1(self.channel)
else: # pragma: no cover
# This should never happen as the version is set in the constructor
raise ValueError(f"Unsupported version: {self.version}")

return self._named_selection
55 changes: 55 additions & 0 deletions src/ansys/geometry/core/_grpc/_services/base/named_selection.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Copyright (C) 2023 - 2025 ANSYS, Inc. and/or its affiliates.
# SPDX-License-Identifier: MIT
#
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
"""Module containing the Named Selection service implementation (abstraction layer)."""

from abc import ABC, abstractmethod

import grpc


class GRPCNamedSelectionService(ABC):
"""Named Selection service for gRPC communication with the Geometry server.

Parameters
----------
channel : grpc.Channel
The gRPC channel to the server.
"""

def __init__(self, channel: grpc.Channel):
"""Initialize the GRPCNamedSelectionService class."""
pass # pragma: no cover

@abstractmethod
def get_named_selection(self, **kwargs) -> dict:
"""Get the named selection by its id."""
pass # pragma: no cover

@abstractmethod
def create_named_selection(self, **kwargs) -> dict:
"""Create a named selection."""
pass

@abstractmethod
def delete_named_selection(self, **kwargs) -> dict:
"""Delete a named selection by id."""
pass
106 changes: 106 additions & 0 deletions src/ansys/geometry/core/_grpc/_services/v0/named_selection.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# Copyright (C) 2023 - 2025 ANSYS, Inc. and/or its affiliates.
# SPDX-License-Identifier: MIT
#
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
"""Module containing the Named Selection service implementation for v0."""

import grpc

from ansys.geometry.core.errors import protect_grpc

from ..base.named_selection import GRPCNamedSelectionService


class GRPCNamedSelectionServiceV0(GRPCNamedSelectionService):
"""Named Selection service for gRPC communication with the Geometry server.

This class provides methods to interact with the Geometry server's
Named Selection service. It is specifically designed for the v0 version
of the Geometry API.

Parameters
----------
channel : grpc.Channel
The gRPC channel to the server.
"""

@protect_grpc
def __init__(self, channel: grpc.Channel): # noqa: D102
from ansys.api.geometry.v0.namedselections_pb2_grpc import NamedSelectionsStub

self.stub = NamedSelectionsStub(channel)

@protect_grpc
def get_named_selection(self, **kwargs): # noqa: D102
from ansys.api.dbu.v0.dbumodels_pb2 import EntityIdentifier

# Create the request - assumes all inputs are valid and of the proper type
request = EntityIdentifier(id=kwargs["id"])

# Call the gRPC service
response = self.stub.Get(request)

# Return the response - formatted as a dictionary
return {
"id": response.id,
"name": response.name,
"bodies": [body.id for body in response.bodies],
"faces": [face.id for face in response.faces],
"edges": [edge.id for edge in response.edges],
"beams": [beam.id.id for beam in response.beams],
"design_points": [(dp.id, dp.points[0]) for dp in response.design_points],
}

@protect_grpc
def create_named_selection(self, **kwargs): # noqa: D102
from ansys.api.geometry.v0.namedselections_pb2 import CreateRequest

# Create the request - assumes all inputs are valid and of the proper type
request = CreateRequest(
name=kwargs["name"],
members=kwargs["members"],
)

# Call the gRPC service
response = self.stub.Create(request)

# Return the response - formatted as a dictionary
return {
"id": response.id,
"name": response.name,
"bodies": [body.id for body in response.bodies],
"faces": [face.id for face in response.faces],
"edges": [edge.id for edge in response.edges],
"beams": [beam.id.id for beam in response.beams],
"design_points": [dp.id for dp in response.design_points],
}

@protect_grpc
def delete_named_selection(self, **kwargs): # noqa: D102
from ansys.api.dbu.v0.dbumodels_pb2 import EntityIdentifier

# Create the request - assumes all inputs are valid and of the proper type
request = EntityIdentifier(id=kwargs["id"])

# Call the gRPC service
self.stub.Delete(request)

# Return the response - empty dictionary
return {}
60 changes: 60 additions & 0 deletions src/ansys/geometry/core/_grpc/_services/v1/named_selection.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Copyright (C) 2023 - 2025 ANSYS, Inc. and/or its affiliates.
# SPDX-License-Identifier: MIT
#
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
"""Module containing the Named Selection service implementation for v1."""

import grpc

from ansys.geometry.core.errors import protect_grpc

from ..base.named_selection import GRPCNamedSelectionService


class GRPCNamedSelectionServiceV1(GRPCNamedSelectionService):
"""Named Selection service for gRPC communication with the Geometry server.

This class provides methods to interact with the Geometry server's
Named Selection service. It is specifically designed for the v1 version
of the Geometry API.

Parameters
----------
channel : grpc.Channel
The gRPC channel to the server.
"""

@protect_grpc
def __init__(self, channel: grpc.Channel): # noqa: D102
from ansys.api.geometry.v1.namedselections_pb2_grpc import NamedSelectionsStub

self.stub = NamedSelectionsStub(channel)

@protect_grpc
def get_named_selection(self, **kwargs): # noqa: D102
raise NotImplementedError

@protect_grpc
def create_named_selection(self, **kwargs): # noqa: D102
raise NotImplementedError

@protect_grpc
def delete_named_selection(self, **kwargs): # noqa: D102
raise NotImplementedError
5 changes: 1 addition & 4 deletions src/ansys/geometry/core/designer/design.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@
Material as GRPCMaterial,
MaterialProperty as GRPCMaterialProperty,
)
from ansys.api.geometry.v0.namedselections_pb2_grpc import NamedSelectionsStub
from ansys.api.geometry.v0.parts_pb2 import ExportRequest
from ansys.api.geometry.v0.parts_pb2_grpc import PartsStub
from ansys.geometry.core.connection.backend import BackendType
Expand Down Expand Up @@ -146,7 +145,6 @@ def __init__(self, name: str, modeler: Modeler, read_existing_design: bool = Fal
self._design_stub = DesignsStub(self._grpc_client.channel)
self._commands_stub = CommandsStub(self._grpc_client.channel)
self._materials_stub = MaterialsStub(self._grpc_client.channel)
self._named_selections_stub = NamedSelectionsStub(self._grpc_client.channel)
self._parts_stub = PartsStub(self._grpc_client.channel)
self._parameters_stub = DrivingDimensionsStub(self._grpc_client.channel)

Expand Down Expand Up @@ -697,7 +695,6 @@ def create_named_selection(

return self._named_selections[named_selection.name]

@protect_grpc
@check_input_types
@ensure_design_is_active
def delete_named_selection(self, named_selection: NamedSelection | str) -> None:
Expand All @@ -717,7 +714,7 @@ def delete_named_selection(self, named_selection: NamedSelection | str) -> None:
removal_id = named_selection.id

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

try:
self._named_selections.pop(removal_name)
Expand Down
26 changes: 10 additions & 16 deletions src/ansys/geometry/core/designer/selection.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,13 @@

from typing import TYPE_CHECKING

from ansys.api.dbu.v0.dbumodels_pb2 import EntityIdentifier
from ansys.api.geometry.v0.namedselections_pb2 import CreateRequest
from ansys.api.geometry.v0.namedselections_pb2_grpc import NamedSelectionsStub
from ansys.geometry.core.connection.client import GrpcClient
from ansys.geometry.core.connection.conversions import grpc_point_to_point3d
from ansys.geometry.core.designer.beam import Beam
from ansys.geometry.core.designer.body import Body
from ansys.geometry.core.designer.designpoint import DesignPoint
from ansys.geometry.core.designer.edge import Edge
from ansys.geometry.core.designer.face import Face
from ansys.geometry.core.errors import protect_grpc
from ansys.geometry.core.misc.auxiliary import (
get_beams_from_ids,
get_bodies_from_ids,
Expand Down Expand Up @@ -73,7 +69,6 @@ class NamedSelection:
All design points to include in the named selection.
"""

@protect_grpc
def __init__(
self,
name: str,
Expand All @@ -90,7 +85,6 @@ def __init__(
self._name = name
self._design = design
self._grpc_client = grpc_client
self._named_selections_stub = NamedSelectionsStub(self._grpc_client.channel)

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

named_selection_request = CreateRequest(name=name, members=ids)
self._grpc_client.log.debug("Requesting creation of named selection.")
new_named_selection = self._named_selections_stub.Create(named_selection_request)
self._id = new_named_selection.id
response = self._grpc_client.services.named_selection.create_named_selection(
name=name, members=ids
)
self._id = response["id"]

@property
def id(self) -> str:
Expand Down Expand Up @@ -210,15 +204,15 @@ def __verify_ns(self) -> None:
return

# Get all entities from the named selection
resp = self._named_selections_stub.Get(EntityIdentifier(id=self._id))
resp = self._grpc_client.services.named_selection.get_named_selection(id=self._id)

# Check if the named selection has changed
ids = {
"bodies": [body.id for body in resp.bodies],
"faces": [face.id for face in resp.faces],
"edges": [edge.id for edge in resp.edges],
"beams": [beam.id.id for beam in resp.beams],
"design_points": [(dp.id, dp.points[0]) for dp in resp.design_points],
"bodies": resp["bodies"],
"faces": resp["faces"],
"edges": resp["edges"],
"beams": resp["beams"],
"design_points": resp["design_points"],
}

for key in ids:
Expand Down