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
19 changes: 13 additions & 6 deletions src/ansys/dpf/core/animator.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ def animate_workflow(
save_as="",
mode_number=None,
scale_factor=1.0,
shell_layer=core.shell_layers.top,
**kwargs,
):
unit = loop_over.unit
Expand Down Expand Up @@ -121,6 +122,7 @@ def render_frame(frame):
field,
deform_by=deform,
scale_factor_legend=scale_factor[frame],
shell_layer=shell_layer,
**kwargs,
)
kwargs_in = _sort_supported_kwargs(bound_method=self._plotter.add_text, **freq_kwargs)
Expand Down Expand Up @@ -267,33 +269,37 @@ def animate(
save_as: str = None,
scale_factor: Union[float, Sequence[float]] = 1.0,
freq_kwargs: dict = None,
shell_layer: core.shell_layers = core.shell_layers.top,
**kwargs,
):
"""
Animate the workflow of the Animator, using inputs.

Parameters
----------
loop_over : Field
loop_over:
Field of values to loop over.
Can for example be a subset of sets of TimeFreqSupport.time_frequencies.
The unit of the Field will be displayed if present.
output_name : str, optional
output_name:
Name of the workflow output to use as Field for each frame's contour.
Defaults to "to_render".
input_name : list of str, optional
input_name:
Name of the workflow inputs to feed loop_over values into.
Defaults to "loop_over".
save_as : str, optional
save_as:
Path of file to save the animation to. Defaults to None. Can be of any format supported
by pyvista.Plotter.write_frame (.gif, .mp4, ...).
scale_factor : float, list, optional
scale_factor:
Scale factor to apply when warping the mesh. Defaults to 1.0. Can be a list to make
scaling frequency-dependent.
freq_kwargs : dict, optional
freq_kwargs:
Dictionary of kwargs given to the :func:`pyvista.Plotter.add_text` method, used to
format the frequency information. Can also contain a "fmt" key,
defining the format for the frequency displayed with a string such as ".3e".
shell_layer:
Enum used to set the shell layer if the field to plot
contains shell elements. Defaults to top layer.
**kwargs : optional
Additional keyword arguments for the animator.
Used by :func:`pyvista.Plotter` (off_screen, cpos, ...),
Expand All @@ -314,6 +320,7 @@ def animate(
save_as=save_as,
scale_factor=scale_factor,
freq_kwargs=freq_kwargs,
shell_layer=shell_layer,
**kwargs,
)

Expand Down
2 changes: 1 addition & 1 deletion src/ansys/dpf/core/collection_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
dpf_vector,
)

if TYPE_CHECKING:
if TYPE_CHECKING: # pragma: no cover
from ansys.dpf.core.support import Support

from ansys.dpf.gate.integral_types import MutableListInt32
Expand Down
2 changes: 1 addition & 1 deletion src/ansys/dpf/core/data_tree.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
import weakref

from ansys.dpf.core import collection_base, common, errors, server as server_module
from ansys.dpf.core.mapping_types import types
from ansys.dpf.core.common import types
from ansys.dpf.gate import (
data_processing_capi,
data_processing_grpcapi,
Expand Down
3 changes: 1 addition & 2 deletions src/ansys/dpf/core/dpf_operator.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,10 @@
server_meet_version_and_raise,
version_requires,
)
from ansys.dpf.core.common import types_enum_to_types
from ansys.dpf.core.common import types, types_enum_to_types
from ansys.dpf.core.config import Config
from ansys.dpf.core.errors import DpfVersionNotSupported
from ansys.dpf.core.inputs import Inputs
from ansys.dpf.core.mapping_types import types
from ansys.dpf.core.operator_specification import Specification
from ansys.dpf.core.outputs import Output, Outputs, _Outputs
from ansys.dpf.core.unit_system import UnitSystem
Expand Down
43 changes: 38 additions & 5 deletions src/ansys/dpf/core/fields_container.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,17 @@
Contains classes associated with the DPF FieldsContainer.
"""

from __future__ import annotations

from typing import TYPE_CHECKING, Union

from ansys import dpf
from ansys.dpf.core import errors as dpf_errors, field
from ansys.dpf.core.collection_base import CollectionBase
from ansys.dpf.core.common import shell_layers

if TYPE_CHECKING: # pragma: no cover
from ansys.dpf.core import Operator, Result


class FieldsContainer(CollectionBase["field.Field"]):
Expand Down Expand Up @@ -543,23 +551,39 @@ def plot(self, label_space: dict = None, **kwargs):
plt.add_field(field=f, **kwargs)
plt.show_figure(**kwargs)

def animate(self, save_as=None, deform_by=None, scale_factor=1.0, **kwargs):
def animate(
self,
save_as: str = None,
deform_by: Union[FieldsContainer, Result, Operator] = None,
scale_factor: Union[float, Sequence[float]] = 1.0,
shell_layer: shell_layers = shell_layers.top,
**kwargs,
):
"""Create an animation based on the Fields contained in the FieldsContainer.

This method creates a movie or a gif based on the time ids of a FieldsContainer.
For kwargs see pyvista.Plotter.open_movie/add_text/show.

Parameters
----------
save_as : Path of file to save the animation to. Defaults to None. Can be of any format
save_as:
Path of file to save the animation to. Defaults to None. Can be of any format
supported by pyvista.Plotter.write_frame (.gif, .mp4, ...).
deform_by : FieldsContainer, Result, Operator, optional
deform_by:
Used to deform the plotted mesh. Must return a FieldsContainer of the same length as
self, containing 3D vector Fields of distances.
Defaults to None, which takes self if possible. Set as False to force static animation.
scale_factor : float, list, optional
Scale factor to apply when warping the mesh. Defaults to 1.0. Can be a list to make
scaling frequency-dependent.
shell_layer:
Enum used to set the shell layer if the field to plot
contains shell elements. Defaults to top layer.
**kwargs:
Additional keyword arguments for the animator.
Used by :func:`pyvista.Plotter` (off_screen, cpos, ...),
or by :func:`pyvista.Plotter.open_movie`
(framerate, quality, ...)
"""
from ansys.dpf.core.animator import Animator

Expand All @@ -571,11 +595,17 @@ def animate(self, save_as=None, deform_by=None, scale_factor=1.0, **kwargs):
# Define the field extraction using the fields_container and indices
extract_field_op = dpf.core.operators.utility.extract_field(self)
to_render = extract_field_op.outputs.field
# Add the operators to the workflow
wf.add_operators([extract_field_op, forward_index])

# Treat multi-component fields by taking their norm
n_components = self[0].component_count
if n_components > 1:
norm_op = dpf.core.operators.math.norm(extract_field_op.outputs.field)
wf.add_operator(norm_op)
to_render = norm_op.outputs.field

# Get time steps IDs and values
loop_over = self.get_time_scoping()
frequencies = self.time_freq_support.time_frequencies
if frequencies is None:
Expand All @@ -586,8 +616,6 @@ def animate(self, save_as=None, deform_by=None, scale_factor=1.0, **kwargs):

wf.set_input_name("indices", extract_field_op.inputs.indices) # Have to do it this way
wf.connect("indices", forward_index) # Otherwise not accepted
# Add the operators to the workflow
wf.add_operators([extract_field_op, forward_index])

deform = True
# Define whether to deform and what with
Expand Down Expand Up @@ -627,6 +655,10 @@ def animate(self, save_as=None, deform_by=None, scale_factor=1.0, **kwargs):
extract_field_op_2.outputs.field, extract_scale_factor_op.outputs.field
)
wf.set_output_name("deform_by", divide_op.outputs.field)

wf.add_operators(
[scale_factor_invert, extract_field_op_2, extract_scale_factor_op, divide_op]
)
else:
scale_factor = None
wf.set_output_name("to_render", to_render)
Expand All @@ -647,6 +679,7 @@ def animate(self, save_as=None, deform_by=None, scale_factor=1.0, **kwargs):
loop_over=loop_over_field,
save_as=save_as,
scale_factor=scale_factor,
shell_layer=shell_layer,
**kwargs,
)

Expand Down
25 changes: 16 additions & 9 deletions src/ansys/dpf/core/mapping_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,23 +21,30 @@
# SOFTWARE.
"""Provides utilities for mapping and transforming data types between Python and C++ representations."""

from enum import Enum # noqa: F401 # pylint: disable=W0611
import inspect
from pathlib import Path # noqa: F401 # pylint: disable=W0611
import sys

from ansys.dpf.core.available_result import * # noqa: F401, F403
# Import types to map to cpp (camel case to snake case)
from ansys.dpf.core.available_result import ( # noqa: F401 # pylint: disable=W0611
AvailableResult,
Homogeneity,
)
from ansys.dpf.core.collection_base import CollectionBase # noqa: F401 # pylint: disable=W0611
from ansys.dpf.core.common import (
_camel_to_snake_case,
_smart_dict_camel,
_snake_to_camel_case,
)
from ansys.dpf.core.data_sources import * # noqa: F401, F403
from ansys.dpf.core.field import * # noqa: F401, F403
from ansys.dpf.core.fields_container import * # noqa: F401, F403

## to do : change that one the module is done
from ansys.dpf.core.meshed_region import * # noqa: F401, F403
from ansys.dpf.core.scoping import * # noqa: F401, F403
from ansys.dpf.core.time_freq_support import * # noqa: F401, F403
from ansys.dpf.core.data_sources import DataSources # noqa: F401 # pylint: disable=W0611
from ansys.dpf.core.dpf_array import DPFArray # noqa: F401 # pylint: disable=W0611
from ansys.dpf.core.field import Field, FieldDefinition # noqa: F401 # pylint: disable=W0611
from ansys.dpf.core.fields_container import FieldsContainer # noqa: F401 # pylint: disable=W0611
from ansys.dpf.core.meshed_region import MeshedRegion # noqa: F401 # pylint: disable=W0611
from ansys.dpf.core.scoping import Scoping # noqa: F401 # pylint: disable=W0611
from ansys.dpf.core.support import Support # noqa: F401 # pylint: disable=W0611
from ansys.dpf.core.time_freq_support import TimeFreqSupport # noqa: F401 # pylint: disable=W0611

map_types_to_cpp = _smart_dict_camel()
for classes in inspect.getmembers(sys.modules[__name__], inspect.isclass):
Expand Down
20 changes: 20 additions & 0 deletions src/ansys/dpf/core/plotter.py
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,7 @@ def add_field(
scale_factor=1.0,
scale_factor_legend=None,
as_linear=True,
shell_layer=eshell_layers.top,
**kwargs,
):
# Get the field name
Expand Down Expand Up @@ -275,6 +276,20 @@ def add_field(
mesh_location = meshed_region.elements
else:
raise ValueError("Only elemental, nodal or faces location are supported for plotting.")

# Treat multilayered shells
if not isinstance(shell_layer, eshell_layers):
raise TypeError("shell_layer attribute must be a core.shell_layers instance.")
if field.shell_layers in [
eshell_layers.topbottom,
eshell_layers.topbottommid,
]:
change_shell_layer_op = core.operators.utility.change_shell_layers(
fields_container=field,
e_shell_layer=shell_layer,
)
field = change_shell_layer_op.get_output(0, core.types.field)

component_count = field.component_count
if component_count > 1:
overall_data = np.full((len(mesh_location), component_count), np.nan)
Expand Down Expand Up @@ -603,6 +618,7 @@ def add_field(
label_point_size=20,
deform_by=None,
scale_factor=1.0,
shell_layer=eshell_layers.top,
**kwargs,
):
"""Add a field containing data to the plotter.
Expand All @@ -627,6 +643,9 @@ def add_field(
Defaults to None.
scale_factor : float, optional
Scaling factor to apply when warping the mesh. Defaults to 1.0.
shell_layer: core.shell_layers, optional
Enum used to set the shell layer if the field to plot
contains shell elements. Defaults to top layer.
**kwargs : optional
Additional keyword arguments for the plotter. More information
are available at :func:`pyvista.plot`.
Expand All @@ -653,6 +672,7 @@ def add_field(
deform_by=deform_by,
scale_factor=scale_factor,
as_linear=True,
shell_layer=shell_layer,
**kwargs,
)

Expand Down
2 changes: 1 addition & 1 deletion src/ansys/dpf/core/server_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
from ansys.dpf.core.check_version import server_meet_version
from ansys.dpf.gate import data_processing_grpcapi, load_api

if TYPE_CHECKING:
if TYPE_CHECKING: # pragma: no cover
from ansys.dpf.core.server_factory import DockerConfig

import logging
Expand Down
23 changes: 22 additions & 1 deletion tests/test_plotter.py
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ def test_field_shell_plot_scoping_elemental(multishells):


@pytest.mark.skipif(not HAS_PYVISTA, reason="Please install pyvista")
def test_throw_shell_layers(multishells):
def test_plotter_plot_contour_throw_shell_layers(multishells):
model = core.Model(multishells)
stress = model.results.stress()
scoping = core.Scoping()
Expand All @@ -269,6 +269,27 @@ def test_throw_shell_layers(multishells):
f.plot(shell_layers="test")


@pytest.mark.skipif(not HAS_PYVISTA, reason="Please install pyvista")
def test_dpf_plotter_add_field_throw_shell_layer(multishells):
field: core.Field = core.operators.result.stress(
data_sources=core.DataSources(multishells),
requested_location=core.locations.elemental,
).eval()[1]
plt = DpfPlotter()
with pytest.raises(TypeError):
plt.add_field(field=field, shell_layer="test")


@pytest.mark.skipif(not HAS_PYVISTA, reason="Please install pyvista")
def test_dpf_plotter_add_field_change_shell_layer(multishells):
field: core.Field = core.operators.result.stress(
data_sources=core.DataSources(multishells),
requested_location=core.locations.elemental,
).eval()[1]
plt = DpfPlotter()
plt.add_field(field=field)


@pytest.mark.skipif(not HAS_PYVISTA, reason="Please install pyvista")
def test_plot_fieldscontainer_on_mesh_scoping(multishells):
model = core.Model(multishells)
Expand Down
Loading