From c03a8c07e84af4577340af26c638e71d1a30b5e3 Mon Sep 17 00:00:00 2001 From: Dominik Gresch Date: Mon, 8 Jan 2024 16:40:55 +0100 Subject: [PATCH 1/2] Expose advanced modeling ply thickness properties --- src/ansys/acp/core/__init__.py | 3 + src/ansys/acp/core/_tree_objects/__init__.py | 3 +- src/ansys/acp/core/_tree_objects/enums.py | 12 ++ .../acp/core/_tree_objects/modeling_ply.py | 161 +++++++++++++++++- tests/unittests/test_modeling_ply.py | 25 ++- 5 files changed, 199 insertions(+), 5 deletions(-) diff --git a/src/ansys/acp/core/__init__.py b/src/ansys/acp/core/__init__.py index 155901438f..0de10d4ce8 100644 --- a/src/ansys/acp/core/__init__.py +++ b/src/ansys/acp/core/__init__.py @@ -35,6 +35,7 @@ SphericalSelectionRule, Stackup, SubLaminate, + TaperEdge, TubeSelectionRule, UnitSystemType, VariableOffsetSelectionRule, @@ -79,12 +80,14 @@ "OrientedSelectionSet", "ModelingGroup", "ModelingPly", + "TaperEdge", "ProductionPly", "AnalysisPly", "UnitSystemType", "Stackup", "Sensor", "FabricWithAngle", + "Sensor", "ElementalDataType", "NodalDataType", ] diff --git a/src/ansys/acp/core/_tree_objects/__init__.py b/src/ansys/acp/core/_tree_objects/__init__.py index b749a5fe66..3442883f83 100644 --- a/src/ansys/acp/core/_tree_objects/__init__.py +++ b/src/ansys/acp/core/_tree_objects/__init__.py @@ -17,7 +17,7 @@ from .material import Material from .model import Model from .modeling_group import ModelingGroup -from .modeling_ply import ModelingPly +from .modeling_ply import ModelingPly, TaperEdge from .oriented_selection_set import OrientedSelectionSet from .parallel_selection_rule import ParallelSelectionRule from .production_ply import ProductionPly @@ -61,6 +61,7 @@ "OrientedSelectionSet", "ModelingGroup", "ModelingPly", + "TaperEdge", "ProductionPly", "AnalysisPly", "Sensor", diff --git a/src/ansys/acp/core/_tree_objects/enums.py b/src/ansys/acp/core/_tree_objects/enums.py index b6060c5599..384b633dc6 100644 --- a/src/ansys/acp/core/_tree_objects/enums.py +++ b/src/ansys/acp/core/_tree_objects/enums.py @@ -8,6 +8,7 @@ lookup_table_3d_pb2, lookup_table_column_type_pb2, mesh_query_pb2, + modeling_ply_pb2, ply_material_pb2, sensor_pb2, unit_system_pb2, @@ -191,3 +192,14 @@ ) = wrap_to_string_enum( "GeometricalRuleType", geometrical_selection_rule_pb2.GeometricalRuleType, module=__name__ ) +( + ThicknessType, + thickness_type_to_pb, + thickness_type_from_pb, +) = wrap_to_string_enum("ThicknessType", modeling_ply_pb2.ThicknessType, module=__name__) + +( + ThicknessFieldType, + thickness_field_type_to_pb, + thickness_field_type_from_pb, +) = wrap_to_string_enum("ThicknessFieldType", modeling_ply_pb2.ThicknessFieldType, module=__name__) diff --git a/src/ansys/acp/core/_tree_objects/modeling_ply.py b/src/ansys/acp/core/_tree_objects/modeling_ply.py index 722c69dff1..b06c2f7ae5 100644 --- a/src/ansys/acp/core/_tree_objects/modeling_ply.py +++ b/src/ansys/acp/core/_tree_objects/modeling_ply.py @@ -2,14 +2,17 @@ from collections.abc import Container, Iterable import dataclasses +from typing import Any, Callable import numpy as np import numpy.typing as npt +from typing_extensions import Self +from ansys.acp.core._tree_objects.base import CreatableTreeObject from ansys.api.acp.v0 import modeling_ply_pb2, modeling_ply_pb2_grpc, production_ply_pb2_grpc from .._utils.array_conversions import to_1D_double_array, to_tuple_from_1D_array -from ._grpc_helpers.edge_property_list import define_edge_property_list +from ._grpc_helpers.edge_property_list import GenericEdgePropertyType, define_edge_property_list from ._grpc_helpers.linked_object_list import define_linked_object_list from ._grpc_helpers.mapping import get_read_only_collection_property from ._grpc_helpers.property_helper import ( @@ -20,7 +23,19 @@ ) from ._mesh_data import ElementalData, NodalData, elemental_data_property, nodal_data_property from .base import CreatableTreeObject, IdTreeObject -from .enums import DrapingType, draping_type_from_pb, draping_type_to_pb, status_type_from_pb +from .edge_set import EdgeSet +from .enums import ( + DrapingType, + ThicknessFieldType, + ThicknessType, + draping_type_from_pb, + draping_type_to_pb, + status_type_from_pb, + thickness_field_type_from_pb, + thickness_field_type_to_pb, + thickness_type_from_pb, + thickness_type_to_pb, +) from .fabric import Fabric from .linked_selection_rule import LinkedSelectionRule from .lookup_table_1d_column import LookUpTable1DColumn @@ -28,8 +43,9 @@ from .object_registry import register from .oriented_selection_set import OrientedSelectionSet from .production_ply import ProductionPly +from .virtual_geometry import VirtualGeometry -__all__ = ["ModelingPly", "ModelingPlyElementalData", "ModelingPlyNodalData"] +__all__ = ["ModelingPly", "ModelingPlyElementalData", "ModelingPlyNodalData", "TaperEdge"] @dataclasses.dataclass @@ -64,6 +80,102 @@ class ModelingPlyNodalData(NodalData): ply_offset: npt.NDArray[np.float64] +class TaperEdge(GenericEdgePropertyType): + """Defines a taper edge. + + Parameters + ---------- + edge_set : + Defines the edge along which the ply tapering is applied. + angle : + Defines the angle between the cutting plane and the reference surface. + offset : + Moves the cutting plane along the out-of-plane direction. + A negative value cuts the elements at the edge where the in-plane + offset is ``-offset/tan(angle)``. + """ + + def __init__(self, edge_set: EdgeSet, angle: float, offset: float): + self._edge_set = edge_set + self._angle = angle + self._offset = offset + self._callback_apply_changes: Callable[[], None] | None = None + + @property + def edge_set(self) -> EdgeSet: + return self._edge_set + + @edge_set.setter + def edge_set(self, edge_set: EdgeSet) -> None: + self._edge_set = edge_set + if self._callback_apply_changes is not None: + self._callback_apply_changes() + + @property + def angle(self) -> float: + return self._angle + + @angle.setter + def angle(self, angle: float) -> None: + self._angle = angle + if self._callback_apply_changes is not None: + self._callback_apply_changes() + + @property + def offset(self) -> float: + return self._offset + + @offset.setter + def offset(self, offset: float) -> None: + self._offset = offset + if self._callback_apply_changes is not None: + self._callback_apply_changes() + + @classmethod + def _from_pb_object( + cls, + parent_object: CreatableTreeObject, + message: modeling_ply_pb2.TaperEdge, + apply_changes: Callable[[], None], + ) -> Self: + edge_set = EdgeSet._from_resource_path( + resource_path=message.edge_set, channel=parent_object._channel + ) + + new_obj = cls( + edge_set=edge_set, + angle=message.angle, + offset=message.offset, + ) + new_obj._set_callback_apply_changes(apply_changes) + return new_obj + + def _to_pb_object(self) -> modeling_ply_pb2.TaperEdge: + return modeling_ply_pb2.TaperEdge( + edge_set=self.edge_set._resource_path, + angle=self.angle, + offset=self.offset, + ) + + def _check(self) -> bool: + return bool(self.edge_set._resource_path.value) + + def __eq__(self, other: Any) -> bool: + if isinstance(other, TaperEdge): + return ( + self.edge_set == other.edge_set + and self.angle == other.angle + and self.offset == other.offset + ) + return False + + def __repr__(self) -> str: + return ( + f"{self.__class__.__name__}(edge_set={self.edge_set!r}, " + f"angle={self.angle!r}, offset={self.offset!r})" + ) + + @mark_grpc_properties @register class ModelingPly(CreatableTreeObject, IdTreeObject): @@ -107,6 +219,24 @@ class ModelingPly(CreatableTreeObject, IdTreeObject): Correction angle between the transverse and draped transverse directions, in degree. Optional, uses the same values as ``draping_angle_1_field`` (no shear) by default. + thickness_type : + Choose between :attr:`ThicknessType.FROM_GEOMETRY` or + :attr:`ThicknessType.FROM_TABLE` to define a ply with variable thickness. + The default value is :attr:`ThicknessType.NOMINAL`, which means the ply + thickness is constant and determined by the material thickness. + thickness_geometry : + Defines the geometry used to determine the ply thickness. Only applies if + ``thickness_type`` is :attr:`ThicknessType.FROM_GEOMETRY`. + thickness_field : + Defines the look-up table column used to determine the ply thickness. + Only applies if ``thickness_type`` is :attr:`ThicknessType.FROM_TABLE`. + thickness_field_type : + If ``thickness_type`` is :attr:`ThicknessType.FROM_TABLE`, this parameter + determines how the thickness values are interpreted. They can be either + absolute values (:attr:`ThicknessFieldType.ABSOLUTE_VALUES`) or relative + values (:attr:`ThicknessFieldType.RELATIVE_SCALING_FACTOR`). + taper_edges : + Defines the taper edges of the ply. """ __slots__: Iterable[str] = tuple() @@ -136,6 +266,11 @@ def __init__( draping_thickness_correction: bool = True, draping_angle_1_field: LookUpTable1DColumn | LookUpTable3DColumn | None = None, draping_angle_2_field: LookUpTable1DColumn | LookUpTable3DColumn | None = None, + thickness_type: ThicknessType = ThicknessType.NOMINAL, + thickness_geometry: VirtualGeometry | None = None, + thickness_field: LookUpTable1DColumn | LookUpTable3DColumn | None = None, + thickness_field_type: ThicknessFieldType = ThicknessFieldType.ABSOLUTE_VALUES, + taper_edges: Iterable[TaperEdge] = (), ): super().__init__(name=name) @@ -155,6 +290,11 @@ def __init__( self.draping_thickness_correction = draping_thickness_correction self.draping_angle_1_field = draping_angle_1_field self.draping_angle_2_field = draping_angle_2_field + self.thickness_type = thickness_type + self.thickness_geometry = thickness_geometry + self.thickness_field = thickness_field + self.thickness_field_type = thickness_field_type + self.taper_edges = taper_edges def _create_stub(self) -> modeling_ply_pb2_grpc.ObjectServiceStub: return modeling_ply_pb2_grpc.ObjectServiceStub(self._channel) @@ -198,5 +338,20 @@ def _create_stub(self) -> modeling_ply_pb2_grpc.ObjectServiceStub: get_read_only_collection_property(ProductionPly, production_ply_pb2_grpc.ObjectServiceStub) ) + thickness_type = grpc_data_property( + "properties.thickness_type", + from_protobuf=thickness_type_from_pb, + to_protobuf=thickness_type_to_pb, + ) + thickness_geometry = grpc_link_property("properties.thickness_geometry") + thickness_field = grpc_link_property("properties.thickness_field") + thickness_field_type = grpc_data_property( + "properties.thickness_field_type", + from_protobuf=thickness_field_type_from_pb, + to_protobuf=thickness_field_type_to_pb, + ) + + taper_edges = define_edge_property_list("properties.taper_edges", TaperEdge) + elemental_data = elemental_data_property(ModelingPlyElementalData) nodal_data = nodal_data_property(ModelingPlyNodalData) diff --git a/tests/unittests/test_modeling_ply.py b/tests/unittests/test_modeling_ply.py index ad158e9304..9d4663bd46 100644 --- a/tests/unittests/test_modeling_ply.py +++ b/tests/unittests/test_modeling_ply.py @@ -10,8 +10,14 @@ NodalDataType, Stackup, SubLaminate, + TaperEdge, +) +from ansys.acp.core._tree_objects.enums import ( + BooleanOperationType, + DrapingType, + ThicknessFieldType, + ThicknessType, ) -from ansys.acp.core._tree_objects.enums import BooleanOperationType, DrapingType from .common.linked_object_list_tester import LinkedObjectListTestCase, LinkedObjectListTester from .common.tree_object_tester import NoLockedMixin, ObjectPropertiesToTest, TreeObjectTester @@ -52,6 +58,11 @@ class TestModelingPly(NoLockedMixin, TreeObjectTester): "draping_thickness_correction": True, "draping_angle_1_field": None, "draping_angle_2_field": None, + "thickness_type": ThicknessType.NOMINAL, + "thickness_geometry": None, + "thickness_field": None, + "thickness_field_type": ThicknessFieldType.ABSOLUTE_VALUES, + "taper_edges": [], } CREATE_METHOD_NAME = "create_modeling_ply" @@ -128,6 +139,18 @@ def object_properties(request, parent_model): ("draping_thickness_correction", False), ("draping_angle_1_field", column_1), ("draping_angle_2_field", column_2), + ("thickness_type", ThicknessType.FROM_GEOMETRY), + ("thickness_geometry", parent_model.create_virtual_geometry()), + ("thickness_type", ThicknessType.FROM_TABLE), + ("thickness_field", column_1), + ("thickness_field_type", ThicknessFieldType.RELATIVE_SCALING_FACTOR), + ( + "taper_edges", + [ + TaperEdge(edge_set=parent_model.create_edge_set(), angle=1.2, offset=2.3), + TaperEdge(edge_set=parent_model.create_edge_set(), angle=3.4, offset=4.5), + ], + ), ], read_only=[ ("id", "some_id"), From cebc7f66717579674be285596671bbc2d70e1ba8 Mon Sep 17 00:00:00 2001 From: Dominik Gresch Date: Mon, 15 Jan 2024 11:29:26 +0100 Subject: [PATCH 2/2] Update src/ansys/acp/core/_tree_objects/modeling_ply.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: René Roos <105842014+roosre@users.noreply.github.com> --- src/ansys/acp/core/_tree_objects/modeling_ply.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ansys/acp/core/_tree_objects/modeling_ply.py b/src/ansys/acp/core/_tree_objects/modeling_ply.py index b06c2f7ae5..b429246f07 100644 --- a/src/ansys/acp/core/_tree_objects/modeling_ply.py +++ b/src/ansys/acp/core/_tree_objects/modeling_ply.py @@ -223,7 +223,7 @@ class ModelingPly(CreatableTreeObject, IdTreeObject): Choose between :attr:`ThicknessType.FROM_GEOMETRY` or :attr:`ThicknessType.FROM_TABLE` to define a ply with variable thickness. The default value is :attr:`ThicknessType.NOMINAL`, which means the ply - thickness is constant and determined by the material thickness. + thickness is constant and determined by the thickness of the ply material. thickness_geometry : Defines the geometry used to determine the ply thickness. Only applies if ``thickness_type`` is :attr:`ThicknessType.FROM_GEOMETRY`.