Skip to content
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
3 changes: 3 additions & 0 deletions src/ansys/acp/core/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
SphericalSelectionRule,
Stackup,
SubLaminate,
TaperEdge,
TubeSelectionRule,
UnitSystemType,
VariableOffsetSelectionRule,
Expand Down Expand Up @@ -79,12 +80,14 @@
"OrientedSelectionSet",
"ModelingGroup",
"ModelingPly",
"TaperEdge",
"ProductionPly",
"AnalysisPly",
"UnitSystemType",
"Stackup",
"Sensor",
"FabricWithAngle",
"Sensor",
"ElementalDataType",
"NodalDataType",
]
3 changes: 2 additions & 1 deletion src/ansys/acp/core/_tree_objects/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -61,6 +61,7 @@
"OrientedSelectionSet",
"ModelingGroup",
"ModelingPly",
"TaperEdge",
"ProductionPly",
"AnalysisPly",
"Sensor",
Expand Down
12 changes: 12 additions & 0 deletions src/ansys/acp/core/_tree_objects/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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__)
161 changes: 158 additions & 3 deletions src/ansys/acp/core/_tree_objects/modeling_ply.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand All @@ -20,16 +23,29 @@
)
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
from .lookup_table_3d_column import LookUpTable3DColumn
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
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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 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`.
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()
Expand Down Expand Up @@ -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)

Expand All @@ -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)
Expand Down Expand Up @@ -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)
25 changes: 24 additions & 1 deletion tests/unittests/test_modeling_ply.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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"

Expand Down Expand Up @@ -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"),
Expand Down