Skip to content

Commit

Permalink
Expose mesh data, add plotting capability. (#231)
Browse files Browse the repository at this point in the history
Add `elemental_data` and `nodal_data` attributes to tree objects, which fetch the
data from the mesh query API.

The returned objects have a method `to_pyvista`, to convert to a plottable object. Currently,
this requires the `Model.mesh` to be passed in; we may consider how this can be avoided.

Optionally, a `component` (name to be improved) can be passed, to select which data to
add to the PyVista mesh. If a vector component is selected, the data is converted to arrows.

The possible string constants for `component` are exposed in the `ElementalDataType` and
`NodalDataType` enums, which are auto-converted from their protobuf equivalent.

Helper classes and functions for wrapping mesh query data are defined in `_mesh_data.py`:

- a base class `MeshDataBase` which implements `to_pyvista`, as well as the construction
  from a mesh query response
- base classes `ElementalData` and `NodalData`, which are the classes to be used to define
  the mesh data classes for each tree object type
- property helpers `elemental_data_property` and `nodal_data_property`, for defining
  the `elemental_data` and `nodal_data` properties, respectively.


The mesh data itself is exposed in a separate `mesh` on the `Model`. Its class `MeshData`
also implements a `to_pyvista` method.

Add a `__slots__` class attribute in some places where it was missing, to disallow setting
attributes that are not explicitly defined on tree objects.
  • Loading branch information
greschd authored Aug 2, 2023
1 parent 18a53e0 commit 8ed07ce
Show file tree
Hide file tree
Showing 17 changed files with 2,793 additions and 282 deletions.
49 changes: 49 additions & 0 deletions examples/solve_class40.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
import pathlib
import tempfile

import pyvista

# %%
# Import Ansys libraries
import ansys.acp.core as pyacp
Expand Down Expand Up @@ -64,6 +66,12 @@
)
model


# %%
# Visualize the loaded mesh
mesh = model.mesh.to_pyvista()
mesh.plot()

# %%
#
# Build Composite Lay-up
Expand Down Expand Up @@ -157,6 +165,23 @@
rosettes=[ros_keeltower],
)

# %%
# Show the orientations on the hull OSS.
#
# Note that the Model must be updated before the orientations are available.

model.update()

plotter = pyvista.Plotter()
plotter.add_mesh(model.mesh.to_pyvista(), color="white")
plotter.add_mesh(
oss_hull.elemental_data.to_pyvista(
mesh=model.mesh, component=pyacp.ElementalDataType.ORIENTATION, factor=0.2, culling_factor=5
),
color="blue",
)
plotter.show()


# %%
# Modeling Plies
Expand Down Expand Up @@ -207,6 +232,30 @@ def add_ply(mg, name, ply_material, angle, oss):
print(len(model.modeling_groups["bulkhead"].modeling_plies))
print(len(model.modeling_groups["keeltower"].modeling_plies))


# %%
# Show the thickness of one of the plies
model.update()
modeling_ply = model.modeling_groups["deck"].modeling_plies["eglass_ud_02mm_0.5"]
modeling_ply.elemental_data.to_pyvista(
mesh=model.mesh, component=pyacp.ElementalDataType.THICKNESS
).plot()

# %%
# Show the ply offsets, scaled by a factor of 200
plotter = pyvista.Plotter()
plotter.add_mesh(model.mesh.to_pyvista(), color="white")
plotter.add_mesh(
modeling_ply.nodal_data.to_pyvista(
mesh=model.mesh, component=pyacp.NodalDataType.PLY_OFFSET, factor=200
),
)
plotter.show()

# %%
# Show the thickness of the entire lay-up
model.elemental_data.to_pyvista(mesh=model.mesh, component=pyacp.ElementalDataType.THICKNESS).plot()

# %%
#
# Write out ACP Model
Expand Down
2,359 changes: 2,092 additions & 267 deletions poetry.lock

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ ansys-mapdl-core = { version = ">=0.62.1,<1", optional = true }
# TODO: Revert back to using a released version of ansys-dpf-composites once a version '>0.2.0' is released.
ansys-dpf-composites = { git = "https://github.com/ansys/pydpf-composites.git", branch = "main", optional = true }
ansys-dpf-core = { version = ">=0.7,<1", optional = true }
pyvista = { version = ">=0.36", optional = true }
pyvista = ">=0.40"

[tool.poetry.group.dev]
optional = true
Expand All @@ -67,14 +67,14 @@ ansys-sphinx-theme = "^0"
pypandoc = "^1.10"
sphinx-gallery = "^0.13.0"
ipykernel = "^6.22.0"
pyvista = { version = ">=0.40", extras=["jupyter"] }

# Repeat optional dependencies from the main group which are
# included in the 'examples' extra. This is done s.t. the install
# flag '--with=dev,test' will install all dependencies.
ansys-mapdl-core = ">=0.62.1,<1"
ansys-dpf-composites = ">=0.1,<1"
ansys-dpf-core = ">=0.7,<1"
pyvista = ">=0.36"

[tool.poetry.group.test]
optional = true
Expand All @@ -89,7 +89,7 @@ hypothesis = "^6.68.2"
[tool.poetry.extras]
# For the examples, we keep an extra to simplify installing these dependencies for the
# end user.
examples = ["pyvista", "ansys-mapdl-core", "ansys-dpf-core", "ansys-dpf-composites"]
examples = ["ansys-mapdl-core", "ansys-dpf-core", "ansys-dpf-composites"]

[tool.poetry.plugins."ansys.tools.local_product_launcher.launcher"]
"ACP.direct" = "ansys.acp.core._server.direct:DirectLauncher"
Expand Down
4 changes: 4 additions & 0 deletions src/ansys/acp/core/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@
from ._tree_objects import (
EdgeSet,
EdgeSetType,
ElementalDataType,
ElementSet,
Fabric,
Material,
Model,
ModelingGroup,
ModelingPly,
NodalDataType,
OrientedSelectionSet,
Rosette,
UnitSystemType,
Expand All @@ -37,4 +39,6 @@
"ModelingGroup",
"ModelingPly",
"UnitSystemType",
"ElementalDataType",
"NodalDataType",
]
4 changes: 3 additions & 1 deletion src/ansys/acp/core/_tree_objects/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from .edge_set import EdgeSet
from .element_set import ElementSet
from .enums import EdgeSetType, UnitSystemType
from .enums import EdgeSetType, ElementalDataType, NodalDataType, UnitSystemType
from .fabric import Fabric
from .material import Material
from .model import Model
Expand All @@ -21,4 +21,6 @@
"ModelingPly",
"UnitSystemType",
"EdgeSetType",
"ElementalDataType",
"NodalDataType",
]
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ def wrap_to_string_enum(
class_name: str,
proto_enum: Any,
module: str,
*,
key_converter: Callable[[str], str] = lambda val: val,
value_converter: Callable[[str], str] = lambda val: val.lower(),
) -> Any:
"""Create a string Enum with the same keys as the given protobuf Enum.
Expand All @@ -27,8 +29,9 @@ def wrap_to_string_enum(
to_pb_conversion_dict: Dict[Any, int] = {}
from_pb_conversion_dict: Dict[int, Any] = {}
for key, pb_value in proto_enum.items():
enum_key = key_converter(key)
enum_value = value_converter(key)
fields.append((key, enum_value))
fields.append((enum_key, enum_value))
to_pb_conversion_dict[enum_value] = pb_value
from_pb_conversion_dict[pb_value] = enum_value

Expand Down
6 changes: 5 additions & 1 deletion src/ansys/acp/core/_tree_objects/_grpc_helpers/protocols.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from __future__ import annotations

import textwrap
from typing import Any, Protocol
from typing import Any, Iterable, Protocol

from google.protobuf.message import Message
import grpc
Expand Down Expand Up @@ -64,6 +64,7 @@ def Create(self, request: CreateRequest) -> ObjectInfo:


class GrpcObjectReadOnly(Protocol):
__slots__: Iterable[str] = tuple()
_GRPC_PROPERTIES: tuple[str, ...]

@property
Expand Down Expand Up @@ -101,6 +102,8 @@ def __str__(self) -> str:


class GrpcObject(GrpcObjectReadOnly, Protocol):
__slots__: Iterable[str] = tuple()

@property
def _pb_object(self) -> Any:
...
Expand All @@ -114,6 +117,7 @@ def _put_if_stored(self) -> None:


class RootGrpcObject(GrpcObject, Protocol):
__slots__: Iterable[str] = tuple()
_pb_object: ObjectInfo

@property
Expand Down
Loading

0 comments on commit 8ed07ce

Please sign in to comment.