From bb2d14b80e2b86f712667c7d63812460fe249aef Mon Sep 17 00:00:00 2001 From: Jacob Kerstetter Date: Tue, 4 Nov 2025 09:04:18 -0500 Subject: [PATCH 1/6] wip - vertex has error --- .../_grpc/_services/v0/named_selection.py | 1 + src/ansys/geometry/core/designer/beam.py | 20 ++++ src/ansys/geometry/core/designer/body.py | 27 +++++ src/ansys/geometry/core/designer/edge.py | 21 ++++ src/ansys/geometry/core/designer/face.py | 21 ++++ src/ansys/geometry/core/designer/selection.py | 4 + src/ansys/geometry/core/designer/vertex.py | 35 ++++++ tests/integration/test_design.py | 100 ++++++++++++++++++ tests/integration/test_edges.py | 23 ++++ 9 files changed, 252 insertions(+) diff --git a/src/ansys/geometry/core/_grpc/_services/v0/named_selection.py b/src/ansys/geometry/core/_grpc/_services/v0/named_selection.py index 761eaf7430..208471eaf6 100644 --- a/src/ansys/geometry/core/_grpc/_services/v0/named_selection.py +++ b/src/ansys/geometry/core/_grpc/_services/v0/named_selection.py @@ -57,6 +57,7 @@ def get_named_selection(self, **kwargs): # noqa: D102 response = self.stub.Get(request) # Return the response - formatted as a dictionary + print('grpc response:', [vertex.id.id for vertex in response.vertices]) return { "id": response.id, "name": response.name, diff --git a/src/ansys/geometry/core/designer/beam.py b/src/ansys/geometry/core/designer/beam.py index 5d13f4c2ba..ed177022e7 100644 --- a/src/ansys/geometry/core/designer/beam.py +++ b/src/ansys/geometry/core/designer/beam.py @@ -30,6 +30,7 @@ from ansys.geometry.core.math.frame import Frame from ansys.geometry.core.math.point import Point3D from ansys.geometry.core.math.vector import UnitVector3D +from ansys.geometry.core.misc.auxiliary import get_design_from_component from ansys.geometry.core.misc.checks import check_type from ansys.geometry.core.misc.measurements import Distance from ansys.geometry.core.shapes.curves.trimmed_curve import TrimmedCurve @@ -37,6 +38,7 @@ if TYPE_CHECKING: # pragma: no cover from ansys.geometry.core.designer.component import Component + from ansys.geometry.core.designer.selection import NamedSelection class BeamType(Enum): @@ -422,6 +424,24 @@ def is_alive(self) -> bool: """Flag indicating whether the beam is still alive on the server.""" return self._is_alive + def get_named_selections(self) -> list["NamedSelection"]: + """Get the named selections that include this beam. + + Returns + ------- + list["NamedSelection"] + List of named selections that include this beam. + """ + design = get_design_from_component(self) + named_selections = design.named_selections + + included_ns = [] + for ns in named_selections: + if self in ns.beams: + included_ns.append(ns) + + return included_ns + def __repr__(self) -> str: """Represent the beam as a string.""" lines = [f"ansys.geometry.core.designer.Beam {hex(id(self))}"] diff --git a/src/ansys/geometry/core/designer/body.py b/src/ansys/geometry/core/designer/body.py index 4475d803f7..e6c7683aee 100644 --- a/src/ansys/geometry/core/designer/body.py +++ b/src/ansys/geometry/core/designer/body.py @@ -67,6 +67,7 @@ from pyvista import MultiBlock, PolyData from ansys.geometry.core.designer.component import Component + from ansys.geometry.core.designer.selection import NamedSelection # TODO: Temporary fix for boolean operations # This is a temporary fix for the boolean operations issue. The issue is that the @@ -571,6 +572,17 @@ def copy(self, parent: "Component", name: str = None) -> "Body": Copy of the body. """ return + + @abstractmethod + def get_named_selections(self) -> list["NamedSelection"]: + """Get the named selections associated with the body. + + Returns + ------- + list[NamedSelection] + List of named selections associated with the body. + """ + return @abstractmethod def get_raw_tessellation( @@ -1052,6 +1064,7 @@ def _get_vertices_from_id(self, body: Union["Body", "MasterBody"]) -> list[Verte Vertex( vertex_resp.get("id"), vertex_resp.get("position"), + body, ) for vertex_resp in response.get("vertices") ] @@ -1278,6 +1291,16 @@ def copy(self, parent: "Component", name: str = None) -> "Body": # noqa: D102 raise NotImplementedError( "Copy method is not implemented on the MasterBody. Call this method on a body instead." ) + + def get_named_selections(self, body: "Body") -> list["NamedSelection"]: # noqa: D102 + named_selections = get_design_from_body(body).named_selections + + included_ns = [] + for ns in named_selections: + if body.id in [body.id for body in ns.bodies]: + included_ns.append(ns) + + return included_ns @min_backend_version(26, 1, 0) def get_raw_tessellation( # noqa: D102 @@ -1879,6 +1902,10 @@ def copy(self, parent: "Component", name: str = None) -> "Body": # noqa: D102 parent._clear_cached_bodies() body_id = f"{parent.id}/{tb.id}" if parent.parent_component else tb.id return Body(body_id, response.get("name"), parent, tb) + + @ensure_design_is_active + def get_named_selections(self) -> list["NamedSelection"]: # noqa: D102 + return self._template.get_named_selections(self) @ensure_design_is_active def get_raw_tessellation( # noqa: D102 diff --git a/src/ansys/geometry/core/designer/edge.py b/src/ansys/geometry/core/designer/edge.py index 11f8136c6b..b7b468a029 100644 --- a/src/ansys/geometry/core/designer/edge.py +++ b/src/ansys/geometry/core/designer/edge.py @@ -24,6 +24,7 @@ from enum import Enum, unique from typing import TYPE_CHECKING +from ansys.geometry.core.misc.auxiliary import get_design_from_body from pint import Quantity from ansys.geometry.core.connection.client import GrpcClient @@ -37,6 +38,7 @@ if TYPE_CHECKING: # pragma: no cover from ansys.geometry.core.designer.body import Body from ansys.geometry.core.designer.face import Face + from ansys.geometry.core.designer.selection import NamedSelection from ansys.geometry.core.designer.vertex import Vertex @@ -185,6 +187,7 @@ def vertices(self) -> list["Vertex"]: Vertex( vertex_resp.get("id"), vertex_resp.get("position"), + self.body, ) for vertex_resp in response.get("vertices") ] @@ -229,3 +232,21 @@ def bounding_box(self) -> BoundingBox: return BoundingBox( response.get("min_corner"), response.get("max_corner"), response.get("center") ) + + @ensure_design_is_active + def get_named_selections(self) -> list["NamedSelection"]: + """Get named selections associated with the edge. + + Returns + ------- + list[NamedSelection] + List of named selections that include the edge. + """ + named_selections = get_design_from_body(self.body).named_selections + + included_ns = [] + for ns in named_selections: + if self in ns.edges: + included_ns.append(ns) + + return included_ns diff --git a/src/ansys/geometry/core/designer/face.py b/src/ansys/geometry/core/designer/face.py index 71d30097bf..5bf8d07a9f 100644 --- a/src/ansys/geometry/core/designer/face.py +++ b/src/ansys/geometry/core/designer/face.py @@ -39,6 +39,7 @@ DEFAULT_COLOR, convert_color_to_hex, convert_opacity_to_hex, + get_design_from_body, ) from ansys.geometry.core.misc.checks import ( ensure_design_is_active, @@ -58,6 +59,7 @@ import pyvista as pv from ansys.geometry.core.designer.body import Body + from ansys.geometry.core.designer.selection import NamedSelection @unique @@ -267,6 +269,7 @@ def vertices(self) -> list[Vertex]: Vertex( vertex_resp.get("id"), vertex_resp.get("position"), + self.body, ) for vertex_resp in response.get("vertices") ] @@ -529,6 +532,24 @@ def setup_offset_relationship( ) return result.get("success") + + @ensure_design_is_active + def get_named_selections(self) -> list["NamedSelection"]: + """Get named selections associated with the edge. + + Returns + ------- + list[NamedSelection] + List of named selections that include the edge. + """ + named_selections = get_design_from_body(self.body).named_selections + + included_ns = [] + for ns in named_selections: + if self in ns.faces: + included_ns.append(ns) + + return included_ns @graphics_required def tessellate(self, tess_options: TessellationOptions | None = None) -> "pv.PolyData": diff --git a/src/ansys/geometry/core/designer/selection.py b/src/ansys/geometry/core/designer/selection.py index f326c961d8..593fdc2726 100644 --- a/src/ansys/geometry/core/designer/selection.py +++ b/src/ansys/geometry/core/designer/selection.py @@ -72,6 +72,8 @@ class NamedSelection: All design points to include in the named selection. components: list[Component], default: None All components to include in the named selection. + vertices: list[Vertex], default: None + All vertices to include in the named selection. """ def __init__( @@ -264,6 +266,8 @@ def __verify_ns(self) -> None: "vertices": response.get("vertices"), } + print('verifying NS:', ids["vertices"]) + for key in ids: if ids[key] != self._ids_cached[key]: # Clear the cache for that specific entity diff --git a/src/ansys/geometry/core/designer/vertex.py b/src/ansys/geometry/core/designer/vertex.py index d8c1481a9f..b49a24429d 100644 --- a/src/ansys/geometry/core/designer/vertex.py +++ b/src/ansys/geometry/core/designer/vertex.py @@ -21,11 +21,18 @@ # SOFTWARE. """Module for managing a vertex.""" +from typing import TYPE_CHECKING + import numpy as np +from ansys.geometry.core.misc.auxiliary import get_design_from_body from ansys.geometry.core.math.point import Point3D from ansys.geometry.core.typing import RealSequence +if TYPE_CHECKING: # pragma: no cover + from ansys.geometry.core.designer.body import Body + from ansys.geometry.core.designer.selection import NamedSelection + class Vertex(Point3D): """Represents a single vertex of a body within the design assembly. @@ -44,6 +51,7 @@ def __new__( cls, id: str, position: np.ndarray | RealSequence, + body: "Body", ): """Initialize ``Vertex`` class.""" # Only pass position and unit to Point3D.__new__ @@ -54,9 +62,11 @@ def __init__( self, id: str, position: np.ndarray | RealSequence, + body: "Body", ): """Initialize the Vertex with a unique identifier.""" self._id = id + self._body = body super().__init__(position) # Make immutable @@ -66,10 +76,35 @@ def __init__( def id(self) -> str: """Get the unique identifier of the vertex.""" return self._id + + @property + def body(self) -> "Body": + """Get the body this vertex belongs to.""" + return self._body + + def get_named_selections(self) -> list["NamedSelection"]: + """Get all named selections that include this vertex. + + Returns + ------- + list["NamedSelection"] + List of named selections that include this vertex. + """ + design = get_design_from_body(self.body) + named_selections = design.named_selections + + included_ns = [] + for ns in named_selections: + print(ns.vertices) + if self in ns.vertices: + included_ns.append(ns) + + return included_ns def __repr__(self) -> str: """Return a string representation of the vertex.""" lines = [f"ansys.geometry.core.designer.Vertex {hex(id(self))}"] lines.append(f" Id : {self.id}") lines.append(f" Position : {self.position}") + lines.append(f" Body Id : {self.body.id}") return "\n".join(lines) diff --git a/tests/integration/test_design.py b/tests/integration/test_design.py index 11acbbca5b..16fca59487 100644 --- a/tests/integration/test_design.py +++ b/tests/integration/test_design.py @@ -4089,3 +4089,103 @@ def test_combine_merge(modeler: Modeler): design._update_design_inplace() assert len(design.bodies) == 1 assert box1.volume.m == pytest.approx(Quantity(2.5, UNITS.m**3).m, rel=1e-6, abs=1e-8) + + +def test_faces_get_named_selections(modeler: Modeler): + # Test getting named selections associated with faces + design = modeler.create_design("faces_named_selections") + box = design.extrude_sketch("box", Sketch().box(Point2D([0, 0]), 1, 1), 1) + + # create named selection from faces + face_ns1 = [box.faces[0], box.faces[1]] + face_ns2 = [box.faces[2], box.faces[3]] + design.create_named_selection("face_ns_1", faces=face_ns1) + design.create_named_selection("face_ns_2", faces=face_ns2) + + # Check that edges return the correct named selections + for face in box.faces: + ns_list = face.get_named_selections() + if face.id in face_ns1: + assert len(ns_list) == 1 + assert any(ns.name == "face_ns_1" for ns in ns_list) + elif face.id in face_ns2: + assert len(ns_list) == 1 + assert any(ns.name == "face_ns_2" for ns in ns_list) + else: + assert len(ns_list) == 0 # No named selection for this face + + +def test_body_get_named_selections(modeler: Modeler): + # Test getting named selections associated with bodies + design = modeler.create_design("body_named_selections") + box1 = design.extrude_sketch("box1", Sketch().box(Point2D([0, 0]), 1, 1), 1) + box2 = design.extrude_sketch("box2", Sketch().box(Point2D([2, 2]), 1, 1), 1) + + # create named selection from bodies + design.create_named_selection("body_ns_1", bodies=[box1]) + design.create_named_selection("body_ns_2", bodies=[box2]) + + # Check that bodies return the correct named selections + for body in design.bodies: + ns_list = body.get_named_selections() + if body.id == box1.id: + assert len(ns_list) == 1 + assert any(ns.name == "body_ns_1" for ns in ns_list) + elif body.id == box2.id: + assert len(ns_list) == 1 + assert any(ns.name == "body_ns_2" for ns in ns_list) + else: + assert len(ns_list) == 0 # No named selection for this body + + +def test_beam_get_named_selections(modeler: Modeler): + # Test getting named selections associated with beams + design = modeler.create_design("beam_named_selections") + profile = design.add_beam_circular_profile("profile1", Distance(0.1, UNITS.m)) + beam1 = design.create_beam(Point3D([0, 0, 0]), Point3D([1, 0, 0]), profile) + beam2 = design.create_beam(Point3D([0, 1, 0]), Point3D([1, 1, 0]), profile) + + # create named selection from beams + design.create_named_selection("beam_ns_1", beams=[beam1]) + design.create_named_selection("beam_ns_2", beams=[beam2]) + + # Check that beams return the correct named selections + for beam in design.beams: + ns_list = beam.get_named_selections() + if beam.id == beam1.id: + assert len(ns_list) == 1 + assert any(ns.name == "beam_ns_1" for ns in ns_list) + elif beam.id == beam2.id: + assert len(ns_list) == 1 + assert any(ns.name == "beam_ns_2" for ns in ns_list) + else: + assert len(ns_list) == 0 # No named selection for this beam + + +def test_vertices_get_named_selections(modeler: Modeler): + # Test getting named selections associated with vertices + design = modeler.create_design("vertex_named_selections") + box = design.extrude_sketch("box", Sketch().box(Point2D([0, 0]), 1, 1), 1) + + # create named selection from vertices + vertex_ns1 = [box.vertices[0], box.vertices[1]] + vertex_ns2 = [box.vertices[2], box.vertices[3]] + print(box.vertices[3].id, box.vertices[3].position) + ns1 = design.create_named_selection("vertex_ns_1", vertices=vertex_ns1) + ns2 = design.create_named_selection("vertex_ns_2", vertices=vertex_ns2) + + print('ns1', ns1.vertices) + print('ns2', ns2.vertices) + + # Check that vertices return the correct named selections + for vertex in box.vertices: + ns_list = vertex.get_named_selections() + print('ns found:', [ns.name for ns in ns_list]) + if vertex in vertex_ns1: + assert len(ns_list) == 1 + assert any(ns.name == "vertex_ns_1" for ns in ns_list) + elif vertex in vertex_ns2: + assert len(ns_list) == 1 + assert any(ns.name == "vertex_ns_2" for ns in ns_list) + else: + assert len(ns_list) == 0 # No named selection for this vertex \ No newline at end of file diff --git a/tests/integration/test_edges.py b/tests/integration/test_edges.py index c7828dfc21..e85e37a1c7 100644 --- a/tests/integration/test_edges.py +++ b/tests/integration/test_edges.py @@ -87,3 +87,26 @@ def test_edges_get_vertices(modeler: Modeler): with pytest.raises(AttributeError): vertices[0].id = "new_id" + + +def test_edges_get_named_selections(modeler: Modeler): + # Test getting named selections associated with edges + # Create a simple design with a box + design = modeler.create_design("BoxNamedSelections") + body = design.extrude_sketch("box", Sketch().box(Point2D([0, 0]), 1, 1), 1) + + # Create named selections on some edges + edge_ns1 = [body.edges[0], body.edges[1]] + edge_ns2 = [body.edges[2], body.edges[3]] + design.create_named_selection("NS1", edges=edge_ns1) + design.create_named_selection("NS2", edges=edge_ns2) + + # Check that edges return the correct named selections + for edge in body.edges: + ns_list = edge.get_named_selections() + if edge.id in edge_ns1: + assert any(ns.name == "NS1" for ns in ns_list) + elif edge.id in edge_ns2: + assert any(ns.name == "NS2" for ns in ns_list) + else: + assert len(ns_list) == 0 # No named selection for this edge \ No newline at end of file From cba074d6cd282dcf615fe217f113d392cc707953 Mon Sep 17 00:00:00 2001 From: Jacob Kerstetter Date: Tue, 4 Nov 2025 13:36:11 -0500 Subject: [PATCH 2/6] removing debug statements --- .../core/_grpc/_services/v0/named_selection.py | 1 - src/ansys/geometry/core/designer/selection.py | 2 -- src/ansys/geometry/core/designer/vertex.py | 1 - tests/integration/test_design.py | 14 +++++++++----- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/ansys/geometry/core/_grpc/_services/v0/named_selection.py b/src/ansys/geometry/core/_grpc/_services/v0/named_selection.py index 208471eaf6..761eaf7430 100644 --- a/src/ansys/geometry/core/_grpc/_services/v0/named_selection.py +++ b/src/ansys/geometry/core/_grpc/_services/v0/named_selection.py @@ -57,7 +57,6 @@ def get_named_selection(self, **kwargs): # noqa: D102 response = self.stub.Get(request) # Return the response - formatted as a dictionary - print('grpc response:', [vertex.id.id for vertex in response.vertices]) return { "id": response.id, "name": response.name, diff --git a/src/ansys/geometry/core/designer/selection.py b/src/ansys/geometry/core/designer/selection.py index 593fdc2726..921117f7ec 100644 --- a/src/ansys/geometry/core/designer/selection.py +++ b/src/ansys/geometry/core/designer/selection.py @@ -266,8 +266,6 @@ def __verify_ns(self) -> None: "vertices": response.get("vertices"), } - print('verifying NS:', ids["vertices"]) - for key in ids: if ids[key] != self._ids_cached[key]: # Clear the cache for that specific entity diff --git a/src/ansys/geometry/core/designer/vertex.py b/src/ansys/geometry/core/designer/vertex.py index b49a24429d..26a134394d 100644 --- a/src/ansys/geometry/core/designer/vertex.py +++ b/src/ansys/geometry/core/designer/vertex.py @@ -95,7 +95,6 @@ def get_named_selections(self) -> list["NamedSelection"]: included_ns = [] for ns in named_selections: - print(ns.vertices) if self in ns.vertices: included_ns.append(ns) diff --git a/tests/integration/test_design.py b/tests/integration/test_design.py index 16fca59487..f710763f3a 100644 --- a/tests/integration/test_design.py +++ b/tests/integration/test_design.py @@ -4170,12 +4170,13 @@ def test_vertices_get_named_selections(modeler: Modeler): # create named selection from vertices vertex_ns1 = [box.vertices[0], box.vertices[1]] vertex_ns2 = [box.vertices[2], box.vertices[3]] - print(box.vertices[3].id, box.vertices[3].position) - ns1 = design.create_named_selection("vertex_ns_1", vertices=vertex_ns1) - ns2 = design.create_named_selection("vertex_ns_2", vertices=vertex_ns2) + vertex_ns3 = [box.vertices[4], box.vertices[5]] + vertex_ns4 = [box.vertices[4], box.vertices[5]] - print('ns1', ns1.vertices) - print('ns2', ns2.vertices) + design.create_named_selection("vertex_ns_1", vertices=vertex_ns1) + design.create_named_selection("vertex_ns_2", vertices=vertex_ns2) + design.create_named_selection("vertex_ns_3", vertices=vertex_ns3) + design.create_named_selection("vertex_ns_4", vertices=vertex_ns4) # Check that vertices return the correct named selections for vertex in box.vertices: @@ -4187,5 +4188,8 @@ def test_vertices_get_named_selections(modeler: Modeler): elif vertex in vertex_ns2: assert len(ns_list) == 1 assert any(ns.name == "vertex_ns_2" for ns in ns_list) + elif vertex in vertex_ns3: + assert len(ns_list) == 2 + assert any(ns.name == "vertex_ns_3" for ns in ns_list) else: assert len(ns_list) == 0 # No named selection for this vertex \ No newline at end of file From f5c5581b9ef2613813cb94c4f76337dc82ba64ac Mon Sep 17 00:00:00 2001 From: Jacob Kerstetter Date: Tue, 4 Nov 2025 13:53:38 -0500 Subject: [PATCH 3/6] added design point and components get_named_selections functions --- src/ansys/geometry/core/designer/component.py | 19 +++++++ .../geometry/core/designer/designpoint.py | 22 ++++++++ tests/integration/test_design.py | 50 ++++++++++++++++++- 3 files changed, 90 insertions(+), 1 deletion(-) diff --git a/src/ansys/geometry/core/designer/component.py b/src/ansys/geometry/core/designer/component.py index 710f832986..fbe30aaee2 100644 --- a/src/ansys/geometry/core/designer/component.py +++ b/src/ansys/geometry/core/designer/component.py @@ -67,6 +67,8 @@ if TYPE_CHECKING: # pragma: no cover from pyvista import MultiBlock, PolyData + from ansys.geometry.core.designer.selection import NamedSelection + @unique class SharedTopologyType(Enum): @@ -1980,3 +1982,20 @@ def make_independent(self, others: list["Component"] = None) -> None: """ ids = [self.id, *[o.id for o in others or []]] self._grpc_client._services.components.make_independent(ids=ids) + + def get_named_selections(self) -> list["NamedSelection"]: + """Get the named selections of the component. + + Returns + ------- + list[NamedSelection] + List of named selections belonging to the component. + """ + named_selections = get_design_from_component(self).named_selections + + included_ns = [] + for ns in named_selections: + if self in ns.components: + included_ns.append(ns) + + return included_ns \ No newline at end of file diff --git a/src/ansys/geometry/core/designer/designpoint.py b/src/ansys/geometry/core/designer/designpoint.py index d062943bfa..78ae817a88 100644 --- a/src/ansys/geometry/core/designer/designpoint.py +++ b/src/ansys/geometry/core/designer/designpoint.py @@ -24,6 +24,7 @@ from typing import TYPE_CHECKING, Union from ansys.geometry.core.math.point import Point3D +from ansys.geometry.core.misc.auxiliary import get_design_from_component from ansys.geometry.core.misc.checks import graphics_required from ansys.geometry.core.misc.units import UNITS @@ -31,6 +32,7 @@ import pyvista as pv from ansys.geometry.core.designer.component import Component + from ansys.geometry.core.designer.selection import NamedSelection class DesignPoint: @@ -77,6 +79,26 @@ def value(self) -> Point3D: def parent_component(self) -> "Component": """Component node that the design point is under.""" return self._parent_component + + def get_named_selections(self) -> list["NamedSelection"]: + """Get named selections that contain this design point. + + Returns + ------- + list[NamedSelection] + List of named selections that contain this design point. + """ + if self.parent_component is None: + raise ValueError("Design point does not have a parent component.") + + named_selections = get_design_from_component(self.parent_component).named_selections + + included_ns = [] + for ns in named_selections: + if self.id in [dp.id for dp in ns.design_points]: + included_ns.append(ns) + + return included_ns def __repr__(self) -> str: """Represent the design points as a string.""" diff --git a/tests/integration/test_design.py b/tests/integration/test_design.py index f710763f3a..e33fdeacdd 100644 --- a/tests/integration/test_design.py +++ b/tests/integration/test_design.py @@ -4192,4 +4192,52 @@ def test_vertices_get_named_selections(modeler: Modeler): assert len(ns_list) == 2 assert any(ns.name == "vertex_ns_3" for ns in ns_list) else: - assert len(ns_list) == 0 # No named selection for this vertex \ No newline at end of file + assert len(ns_list) == 0 # No named selection for this vertex + + +def test_components_get_named_selections(modeler: Modeler): + # Test getting named selections associated with components + design = modeler.create_design("component_named_selections") + comp1 = design.add_component("Component1") + comp2 = design.add_component("Component2") + comp3 = design.add_component("Component3") + + # create named selection from components + design.create_named_selection("component_ns_1", components=[comp1]) + design.create_named_selection("component_ns_2", components=[comp2]) + + # Check that components return the correct named selections + for component in design.components: + ns_list = component.get_named_selections() + if component.id == comp1.id: + assert len(ns_list) == 1 + assert any(ns.name == "component_ns_1" for ns in ns_list) + elif component.id == comp2.id: + assert len(ns_list) == 1 + assert any(ns.name == "component_ns_2" for ns in ns_list) + else: + assert len(ns_list) == 0 # No named selection for this component + + +def test_design_point_get_named_selections(modeler: Modeler): + # Test getting named selections associated with design points + design = modeler.create_design("design_point_named_selections") + dp1 = design.add_design_point("DesignPoint1", Point3D([0, 0, 0])) + dp2 = design.add_design_point("DesignPoint2", Point3D([1, 1, 1])) + dp3 = design.add_design_point("DesignPoint3", Point3D([2, 2, 2])) + + # create named selection from design points + design.create_named_selection("design_point_ns_1", design_points=[dp1]) + design.create_named_selection("design_point_ns_2", design_points=[dp2]) + + # Check that design points return the correct named selections + for design_point in design.design_points: + ns_list = design_point.get_named_selections() + if design_point.id == dp1.id: + assert len(ns_list) == 1 + assert any(ns.name == "design_point_ns_1" for ns in ns_list) + elif design_point.id == dp2.id: + assert len(ns_list) == 1 + assert any(ns.name == "design_point_ns_2" for ns in ns_list) + else: + assert len(ns_list) == 0 # No named selection for this design point \ No newline at end of file From 40a65cfea51d7bf23e4b7d59c3b2a8266f3fece4 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 4 Nov 2025 19:15:13 +0000 Subject: [PATCH 4/6] chore: auto fixes from pre-commit hooks --- src/ansys/geometry/core/designer/body.py | 8 ++++---- src/ansys/geometry/core/designer/component.py | 4 ++-- src/ansys/geometry/core/designer/designpoint.py | 2 +- src/ansys/geometry/core/designer/edge.py | 6 +++--- src/ansys/geometry/core/designer/face.py | 6 +++--- src/ansys/geometry/core/designer/vertex.py | 6 +++--- tests/integration/test_design.py | 4 ++-- tests/integration/test_edges.py | 2 +- 8 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/ansys/geometry/core/designer/body.py b/src/ansys/geometry/core/designer/body.py index e6c7683aee..bec4f0188b 100644 --- a/src/ansys/geometry/core/designer/body.py +++ b/src/ansys/geometry/core/designer/body.py @@ -572,7 +572,7 @@ def copy(self, parent: "Component", name: str = None) -> "Body": Copy of the body. """ return - + @abstractmethod def get_named_selections(self) -> list["NamedSelection"]: """Get the named selections associated with the body. @@ -1291,10 +1291,10 @@ def copy(self, parent: "Component", name: str = None) -> "Body": # noqa: D102 raise NotImplementedError( "Copy method is not implemented on the MasterBody. Call this method on a body instead." ) - + def get_named_selections(self, body: "Body") -> list["NamedSelection"]: # noqa: D102 named_selections = get_design_from_body(body).named_selections - + included_ns = [] for ns in named_selections: if body.id in [body.id for body in ns.bodies]: @@ -1902,7 +1902,7 @@ def copy(self, parent: "Component", name: str = None) -> "Body": # noqa: D102 parent._clear_cached_bodies() body_id = f"{parent.id}/{tb.id}" if parent.parent_component else tb.id return Body(body_id, response.get("name"), parent, tb) - + @ensure_design_is_active def get_named_selections(self) -> list["NamedSelection"]: # noqa: D102 return self._template.get_named_selections(self) diff --git a/src/ansys/geometry/core/designer/component.py b/src/ansys/geometry/core/designer/component.py index fbe30aaee2..10e9328f3b 100644 --- a/src/ansys/geometry/core/designer/component.py +++ b/src/ansys/geometry/core/designer/component.py @@ -1992,10 +1992,10 @@ def get_named_selections(self) -> list["NamedSelection"]: List of named selections belonging to the component. """ named_selections = get_design_from_component(self).named_selections - + included_ns = [] for ns in named_selections: if self in ns.components: included_ns.append(ns) - return included_ns \ No newline at end of file + return included_ns diff --git a/src/ansys/geometry/core/designer/designpoint.py b/src/ansys/geometry/core/designer/designpoint.py index 78ae817a88..dc6bced488 100644 --- a/src/ansys/geometry/core/designer/designpoint.py +++ b/src/ansys/geometry/core/designer/designpoint.py @@ -79,7 +79,7 @@ def value(self) -> Point3D: def parent_component(self) -> "Component": """Component node that the design point is under.""" return self._parent_component - + def get_named_selections(self) -> list["NamedSelection"]: """Get named selections that contain this design point. diff --git a/src/ansys/geometry/core/designer/edge.py b/src/ansys/geometry/core/designer/edge.py index b7b468a029..d983e59115 100644 --- a/src/ansys/geometry/core/designer/edge.py +++ b/src/ansys/geometry/core/designer/edge.py @@ -24,13 +24,13 @@ from enum import Enum, unique from typing import TYPE_CHECKING -from ansys.geometry.core.misc.auxiliary import get_design_from_body from pint import Quantity from ansys.geometry.core.connection.client import GrpcClient from ansys.geometry.core.errors import GeometryRuntimeError from ansys.geometry.core.math.bbox import BoundingBox from ansys.geometry.core.math.point import Point3D +from ansys.geometry.core.misc.auxiliary import get_design_from_body from ansys.geometry.core.misc.checks import ensure_design_is_active, min_backend_version from ansys.geometry.core.shapes.curves.trimmed_curve import ReversedTrimmedCurve, TrimmedCurve from ansys.geometry.core.shapes.parameterization import Interval @@ -236,14 +236,14 @@ def bounding_box(self) -> BoundingBox: @ensure_design_is_active def get_named_selections(self) -> list["NamedSelection"]: """Get named selections associated with the edge. - + Returns ------- list[NamedSelection] List of named selections that include the edge. """ named_selections = get_design_from_body(self.body).named_selections - + included_ns = [] for ns in named_selections: if self in ns.edges: diff --git a/src/ansys/geometry/core/designer/face.py b/src/ansys/geometry/core/designer/face.py index 5bf8d07a9f..f2585b36db 100644 --- a/src/ansys/geometry/core/designer/face.py +++ b/src/ansys/geometry/core/designer/face.py @@ -532,18 +532,18 @@ def setup_offset_relationship( ) return result.get("success") - + @ensure_design_is_active def get_named_selections(self) -> list["NamedSelection"]: """Get named selections associated with the edge. - + Returns ------- list[NamedSelection] List of named selections that include the edge. """ named_selections = get_design_from_body(self.body).named_selections - + included_ns = [] for ns in named_selections: if self in ns.faces: diff --git a/src/ansys/geometry/core/designer/vertex.py b/src/ansys/geometry/core/designer/vertex.py index 26a134394d..c01ef9b1e4 100644 --- a/src/ansys/geometry/core/designer/vertex.py +++ b/src/ansys/geometry/core/designer/vertex.py @@ -25,8 +25,8 @@ import numpy as np -from ansys.geometry.core.misc.auxiliary import get_design_from_body from ansys.geometry.core.math.point import Point3D +from ansys.geometry.core.misc.auxiliary import get_design_from_body from ansys.geometry.core.typing import RealSequence if TYPE_CHECKING: # pragma: no cover @@ -76,12 +76,12 @@ def __init__( def id(self) -> str: """Get the unique identifier of the vertex.""" return self._id - + @property def body(self) -> "Body": """Get the body this vertex belongs to.""" return self._body - + def get_named_selections(self) -> list["NamedSelection"]: """Get all named selections that include this vertex. diff --git a/tests/integration/test_design.py b/tests/integration/test_design.py index e33fdeacdd..9816de8f03 100644 --- a/tests/integration/test_design.py +++ b/tests/integration/test_design.py @@ -4181,7 +4181,7 @@ def test_vertices_get_named_selections(modeler: Modeler): # Check that vertices return the correct named selections for vertex in box.vertices: ns_list = vertex.get_named_selections() - print('ns found:', [ns.name for ns in ns_list]) + print("ns found:", [ns.name for ns in ns_list]) if vertex in vertex_ns1: assert len(ns_list) == 1 assert any(ns.name == "vertex_ns_1" for ns in ns_list) @@ -4240,4 +4240,4 @@ def test_design_point_get_named_selections(modeler: Modeler): assert len(ns_list) == 1 assert any(ns.name == "design_point_ns_2" for ns in ns_list) else: - assert len(ns_list) == 0 # No named selection for this design point \ No newline at end of file + assert len(ns_list) == 0 # No named selection for this design point diff --git a/tests/integration/test_edges.py b/tests/integration/test_edges.py index e85e37a1c7..684d3fc802 100644 --- a/tests/integration/test_edges.py +++ b/tests/integration/test_edges.py @@ -109,4 +109,4 @@ def test_edges_get_named_selections(modeler: Modeler): elif edge.id in edge_ns2: assert any(ns.name == "NS2" for ns in ns_list) else: - assert len(ns_list) == 0 # No named selection for this edge \ No newline at end of file + assert len(ns_list) == 0 # No named selection for this edge From ca0e812f05871c21c846af79c6588fc2c844c660 Mon Sep 17 00:00:00 2001 From: pyansys-ci-bot <92810346+pyansys-ci-bot@users.noreply.github.com> Date: Tue, 4 Nov 2025 19:16:25 +0000 Subject: [PATCH 5/6] chore: adding changelog file 2356.added.md [dependabot-skip] --- doc/changelog.d/2356.added.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 doc/changelog.d/2356.added.md diff --git a/doc/changelog.d/2356.added.md b/doc/changelog.d/2356.added.md new file mode 100644 index 0000000000..d2bd450fb8 --- /dev/null +++ b/doc/changelog.d/2356.added.md @@ -0,0 +1 @@ +Add Named Selections query to geometric entities From 2e0c925bdff83b05d99a8ce89fbdf5ad7f0a7b21 Mon Sep 17 00:00:00 2001 From: Jacob Kerstetter Date: Tue, 4 Nov 2025 14:26:24 -0500 Subject: [PATCH 6/6] fix precommit failures --- tests/integration/test_design.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/integration/test_design.py b/tests/integration/test_design.py index e33fdeacdd..d95fee6a03 100644 --- a/tests/integration/test_design.py +++ b/tests/integration/test_design.py @@ -4200,7 +4200,7 @@ def test_components_get_named_selections(modeler: Modeler): design = modeler.create_design("component_named_selections") comp1 = design.add_component("Component1") comp2 = design.add_component("Component2") - comp3 = design.add_component("Component3") + design.add_component("Component3") # create named selection from components design.create_named_selection("component_ns_1", components=[comp1]) @@ -4224,7 +4224,7 @@ def test_design_point_get_named_selections(modeler: Modeler): design = modeler.create_design("design_point_named_selections") dp1 = design.add_design_point("DesignPoint1", Point3D([0, 0, 0])) dp2 = design.add_design_point("DesignPoint2", Point3D([1, 1, 1])) - dp3 = design.add_design_point("DesignPoint3", Point3D([2, 2, 2])) + design.add_design_point("DesignPoint3", Point3D([2, 2, 2])) # create named selection from design points design.create_named_selection("design_point_ns_1", design_points=[dp1])