diff --git a/doc/source/api/tree_objects.rst b/doc/source/api/tree_objects.rst index d6ab12aff2..6e390d034f 100644 --- a/doc/source/api/tree_objects.rst +++ b/doc/source/api/tree_objects.rst @@ -10,6 +10,7 @@ ACP objects Material Fabric ElementSet + EdgeSet Rosette OrientedSelectionSet ModelingGroup diff --git a/poetry.lock b/poetry.lock index f553a17a7d..506ca51b39 100644 --- a/poetry.lock +++ b/poetry.lock @@ -30,7 +30,7 @@ protobuf = ">=3.19,<4.0" type = "git" url = "https://github.com/ansys/ansys-api-acp-private.git" reference = "main" -resolved_reference = "e436324d3de9b6544aa25979ded776c91d8e07cc" +resolved_reference = "ab2dacb7514a37c41afdb99a7b06dcda062da9b6" [[package]] name = "ansys-api-mapdl" diff --git a/src/ansys/acp/core/__init__.py b/src/ansys/acp/core/__init__.py index 55bea4a7ab..66f03929dd 100644 --- a/src/ansys/acp/core/__init__.py +++ b/src/ansys/acp/core/__init__.py @@ -3,6 +3,8 @@ from ._client import Client from ._server import DirectLaunchConfig, DockerComposeLaunchConfig, LaunchMode, launch_acp from ._tree_objects import ( + EdgeSet, + EdgeSetType, ElementSet, Fabric, Material, @@ -28,6 +30,8 @@ "Material", "Fabric", "ElementSet", + "EdgeSet", + "EdgeSetType", "Rosette", "OrientedSelectionSet", "ModelingGroup", diff --git a/src/ansys/acp/core/_tree_objects/__init__.py b/src/ansys/acp/core/_tree_objects/__init__.py index 7ebb14a4ec..2e5859fbdf 100644 --- a/src/ansys/acp/core/_tree_objects/__init__.py +++ b/src/ansys/acp/core/_tree_objects/__init__.py @@ -1,5 +1,6 @@ +from .edge_set import EdgeSet from .element_set import ElementSet -from .enums import UnitSystemType +from .enums import EdgeSetType, UnitSystemType from .fabric import Fabric from .material import Material from .model import Model @@ -13,9 +14,11 @@ "Material", "Fabric", "ElementSet", + "EdgeSet", "Rosette", "OrientedSelectionSet", "ModelingGroup", "ModelingPly", "UnitSystemType", + "EdgeSetType", ] diff --git a/src/ansys/acp/core/_tree_objects/edge_set.py b/src/ansys/acp/core/_tree_objects/edge_set.py new file mode 100644 index 0000000000..5639862a19 --- /dev/null +++ b/src/ansys/acp/core/_tree_objects/edge_set.py @@ -0,0 +1,94 @@ +from __future__ import annotations + +from typing import Iterable + +from ansys.api.acp.v0 import edge_set_pb2, edge_set_pb2_grpc + +from .._grpc_helpers.property_helper import ( + grpc_data_property, + grpc_data_property_read_only, + grpc_link_property, + mark_grpc_properties, +) +from .._utils.array_conversions import to_1D_double_array, to_1D_int_array, to_tuple_from_1D_array +from .base import CreatableTreeObject, IdTreeObject +from .element_set import ElementSet +from .enums import EdgeSetType, edge_set_type_from_pb, edge_set_type_to_pb, status_type_from_pb +from .object_registry import register + + +@mark_grpc_properties +@register +class EdgeSet(CreatableTreeObject, IdTreeObject): + """Instantiate an edge set. + + Parameters + ---------- + name : + Name of the edge set. + edge_set_type : + Determines how the edge set is defined. Can be one of: + + * :attr:`.EdgeSetType.BY_REFERENCE`: define the edge set using an :class:`.ElementSet`. + * :attr:`.EdgeSetType.BY_NODES`: define the edge set using a list of node labels. + + defining_node_labels : + Labels of the nodes in the edge set. + This parameter only applies when ``edge_set_type`` is :attr:`.EdgeSetType.BY_NODES`. + element_set : + The boundary of this element set defines the initial + edge set. + This parameter only applies when ``edge_set_type`` is :attr:`.EdgeSetType.BY_REFERENCE`. + limit_angle : + The edge set is cropped if the angle between two element edges exceeds this limits (in degrees). + Use ``-1.`` to disable cropping. + This parameter only applies when ``edge_set_type`` is :attr:`.EdgeSetType.BY_REFERENCE`. + origin : + Defines the starting point of the edge set. + This parameter only applies when ``edge_set_type`` is :attr:`.EdgeSetType.BY_REFERENCE`. + """ + + __slots__: Iterable[str] = tuple() + COLLECTION_LABEL = "edge_sets" + OBJECT_INFO_TYPE = edge_set_pb2.ObjectInfo + CREATE_REQUEST_TYPE = edge_set_pb2.CreateRequest + + def __init__( + self, + name: str = "EdgeSet", + edge_set_type: EdgeSetType = EdgeSetType.BY_REFERENCE, + defining_node_labels: Iterable[int] = tuple(), + element_set: ElementSet | None = None, + limit_angle: float = -1.0, + origin: tuple[float, float, float] = (0.0, 0.0, 0.0), + ): + super().__init__( + name=name, + ) + self.edge_set_type = edge_set_type + self.defining_node_labels = defining_node_labels + self.element_set = element_set + self.limit_angle = limit_angle + self.origin = origin + + def _create_stub(self) -> edge_set_pb2_grpc.ObjectServiceStub: + return edge_set_pb2_grpc.ObjectServiceStub(self._channel) + + status = grpc_data_property_read_only("properties.status", from_protobuf=status_type_from_pb) + locked = grpc_data_property_read_only("properties.locked") + + edge_set_type = grpc_data_property( + "properties.edge_set_type", + from_protobuf=edge_set_type_from_pb, + to_protobuf=edge_set_type_to_pb, + ) + defining_node_labels = grpc_data_property( + "properties.defining_node_labels", + from_protobuf=to_tuple_from_1D_array, + to_protobuf=to_1D_int_array, + ) + element_set = grpc_link_property("properties.element_set") + limit_angle = grpc_data_property("properties.limit_angle") + origin = grpc_data_property( + "properties.origin", from_protobuf=to_tuple_from_1D_array, to_protobuf=to_1D_double_array + ) diff --git a/src/ansys/acp/core/_tree_objects/element_set.py b/src/ansys/acp/core/_tree_objects/element_set.py index 1e8802aadd..05559f5d9d 100644 --- a/src/ansys/acp/core/_tree_objects/element_set.py +++ b/src/ansys/acp/core/_tree_objects/element_set.py @@ -19,7 +19,7 @@ @mark_grpc_properties @register -class ElementSet(IdTreeObject, CreatableTreeObject): +class ElementSet(CreatableTreeObject, IdTreeObject): __slots__: Iterable[str] = tuple() COLLECTION_LABEL = "element_sets" OBJECT_INFO_TYPE = element_set_pb2.ObjectInfo diff --git a/src/ansys/acp/core/_tree_objects/enums.py b/src/ansys/acp/core/_tree_objects/enums.py index 5f1b78d3ba..6cc8a8d46f 100644 --- a/src/ansys/acp/core/_tree_objects/enums.py +++ b/src/ansys/acp/core/_tree_objects/enums.py @@ -1,6 +1,7 @@ from ansys.api.acp.v0 import ( cut_off_material_pb2, drop_off_material_pb2, + edge_set_pb2, enum_types_pb2, ply_material_pb2, unit_system_pb2, @@ -53,6 +54,12 @@ "DrapingMaterialType", ply_material_pb2.DrapingMaterialType, module=__name__ ) +( + EdgeSetType, + edge_set_type_to_pb, + edge_set_type_from_pb, +) = wrap_to_string_enum("EdgeSetType", edge_set_pb2.EdgeSetType, module=__name__) + ( PlyType, ply_type_to_pb, diff --git a/src/ansys/acp/core/_tree_objects/model.py b/src/ansys/acp/core/_tree_objects/model.py index e97c0741c2..5afec1426d 100644 --- a/src/ansys/acp/core/_tree_objects/model.py +++ b/src/ansys/acp/core/_tree_objects/model.py @@ -5,6 +5,7 @@ from grpc import Channel from ansys.api.acp.v0 import ( + edge_set_pb2_grpc, element_set_pb2_grpc, fabric_pb2_grpc, material_pb2, @@ -27,6 +28,7 @@ from .._typing_helper import PATH as _PATH from .._utils.resource_paths import join as rp_join from .base import TreeObject +from .edge_set import EdgeSet from .element_set import ElementSet from .enums import UnitSystemType, unit_system_type_from_pb, unit_system_type_to_pb from .fabric import Fabric @@ -218,6 +220,7 @@ def export_materials(self, path: _PATH) -> None: create_element_set, element_sets = define_mapping( ElementSet, element_set_pb2_grpc.ObjectServiceStub ) + create_edge_set, edge_sets = define_mapping(EdgeSet, edge_set_pb2_grpc.ObjectServiceStub) create_fabric, fabrics = define_mapping(Fabric, fabric_pb2_grpc.ObjectServiceStub) create_material, materials = define_mapping(Material, material_pb2_grpc.ObjectServiceStub) create_rosette, rosettes = define_mapping(Rosette, rosette_pb2_grpc.ObjectServiceStub) diff --git a/src/ansys/acp/core/_tree_objects/rosette.py b/src/ansys/acp/core/_tree_objects/rosette.py index 1ac83dbc9d..d9a098b698 100644 --- a/src/ansys/acp/core/_tree_objects/rosette.py +++ b/src/ansys/acp/core/_tree_objects/rosette.py @@ -43,9 +43,9 @@ class Rosette(CreatableTreeObject, IdTreeObject): def __init__( self, name: str = "Rosette", - origin: tuple[float, ...] = (0.0, 0.0, 0.0), - dir1: tuple[float, ...] = (1.0, 0.0, 0.0), - dir2: tuple[float, ...] = (0.0, 1.0, 0.0), + origin: tuple[float, float, float] = (0.0, 0.0, 0.0), + dir1: tuple[float, float, float] = (1.0, 0.0, 0.0), + dir2: tuple[float, float, float] = (0.0, 1.0, 0.0), ): super().__init__(name=name) diff --git a/tests/test_edge_set.py b/tests/test_edge_set.py new file mode 100644 index 0000000000..5c725a56b3 --- /dev/null +++ b/tests/test_edge_set.py @@ -0,0 +1,48 @@ +import pytest + +from ansys.acp.core._tree_objects.enums import EdgeSetType +from common.tree_object_tester import NoLockedMixin, ObjectPropertiesToTest, TreeObjectTester + + +@pytest.fixture +def parent_object(load_model_from_tempfile): + with load_model_from_tempfile() as model: + yield model + + +@pytest.fixture +def tree_object(parent_object): + return parent_object.create_edge_set() + + +class TestEdgeSet(NoLockedMixin, TreeObjectTester): + COLLECTION_NAME = "edge_sets" + DEFAULT_PROPERTIES = { + "status": "NOTUPTODATE", + "edge_set_type": EdgeSetType.BY_REFERENCE, + "element_set": None, + "defining_node_labels": tuple(), + "limit_angle": -1.0, + "origin": (0.0, 0.0, 0.0), + } + + CREATE_METHOD_NAME = "create_edge_set" + + @staticmethod + @pytest.fixture + def object_properties(parent_object): + model = parent_object + element_set = model.create_element_set() + return ObjectPropertiesToTest( + read_write=[ + ("name", "Edge set name"), + ("edge_set_type", EdgeSetType.BY_NODES), + ("element_set", element_set), + ("limit_angle", 3.21), + ("origin", (2.0, 3.0, 1.0)), + ], + read_only=[ + ("id", "some_id"), + ("status", "UPTODATE"), + ], + )