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
13 changes: 13 additions & 0 deletions src/ansys/dpf/core/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@
``fields_container[index]``.
"""

_EMPTY_MESH_PLOTTING_MSG = """"
The mesh support is empty.
Either provide one to the plot function called, or use MeshedRegion.plot
and provide the current data as parameter.
"""


class DpfValueError(ValueError):
"""Error raised when a specific DPF error value must be defined."""
Expand Down Expand Up @@ -80,6 +86,13 @@ def __init__(self, msg=_FIELD_CONTAINER_PLOTTING_MSG):
ValueError.__init__(self, msg)


class EmptyMeshPlottingError(ValueError):
"""Error raised when attempting to plot data with no mesh."""

def __init__(self, msg=_EMPTY_MESH_PLOTTING_MSG):
ValueError.__init__(self, msg)


class InvalidANSYSVersionError(RuntimeError):
"""Error raised when the Ansys version is invalid."""

Expand Down
63 changes: 48 additions & 15 deletions src/ansys/dpf/core/field.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,21 @@

"""Field."""

from __future__ import annotations

from typing import TYPE_CHECKING

import numpy as np

from ansys import dpf
from ansys.dpf.core import dimensionality, errors, meshed_region, scoping, time_freq_support
from ansys.dpf.core.common import _get_size_of_list, locations, natures, types
from ansys.dpf.core.common import (
_get_size_of_list,
locations,
natures,
shell_layers as eshell_layers,
types,
)
from ansys.dpf.core.field_base import _FieldBase, _LocalFieldBase
from ansys.dpf.core.field_definition import FieldDefinition
from ansys.dpf.gate import (
Expand All @@ -36,6 +46,12 @@
field_capi,
field_grpcapi,
)
from ansys.dpf.gate.errors import DPFServerException

if TYPE_CHECKING: # pragma: nocover
from ansys.dpf.core.dpf_operator import Operator
from ansys.dpf.core.meshed_region import MeshedRegion
from ansys.dpf.core.results import Result


class Field(_FieldBase):
Expand Down Expand Up @@ -500,7 +516,14 @@
op.inputs.connect(self)
return op.outputs.field()

def plot(self, shell_layers=None, deform_by=None, scale_factor=1.0, **kwargs):
def plot(
self,
shell_layers: eshell_layers = None,
deform_by: Union[Field, Result, Operator] = None,
scale_factor: float = 1.0,
meshed_region: MeshedRegion = None,
**kwargs,
):
"""Plot the field or fields container on the mesh support if it exists.

Warning
Expand All @@ -522,21 +545,24 @@

Parameters
----------
shell_layers : shell_layers, optional
shell_layers:
Enum used to set the shell layers if the model to plot
contains shell elements. The default is ``None``.
deform_by : Field, Result, Operator, optional
contains shell elements. Defaults to the top layer.
deform_by:
Used to deform the plotted mesh. Must output a 3D vector field.
Defaults to None.
scale_factor : float, optional
Scaling factor to apply when warping the mesh. Defaults to 1.0.
**kwargs : optional
scale_factor:
Scaling factor to apply when warping the mesh.
meshed_region:
Mesh to plot the field on.
**kwargs:
Additional keyword arguments for the plotter. For additional keyword
arguments, see ``help(pyvista.plot)``.
"""
from ansys.dpf.core.plotter import Plotter

pl = Plotter(self.meshed_region, **kwargs)
if meshed_region is None:
meshed_region = self.meshed_region
pl = Plotter(meshed_region, **kwargs)
return pl.plot_contour(
self,
shell_layers,
Expand Down Expand Up @@ -691,16 +717,23 @@
def field_definition(self, value):
return self._set_field_definition(value)

def _get_meshed_region(self):
def _get_meshed_region(self) -> MeshedRegion:
"""Retrieve the meshed region.

Returns
-------
:class:`ansys.dpf.core.meshed_region.MeshedRegion`

"""
try:
support = self._api.csfield_get_support_as_meshed_region(self)
except DPFServerException as e:
if "the field doesn't have this support type" in str(e):
support = None
else:
raise e

Check warning on line 734 in src/ansys/dpf/core/field.py

View check run for this annotation

Codecov / codecov/patch

src/ansys/dpf/core/field.py#L734

Added line #L734 was not covered by tests
return meshed_region.MeshedRegion(
mesh=self._api.csfield_get_support_as_meshed_region(self),
mesh=support,
server=self._server,
)

Expand Down Expand Up @@ -736,7 +769,7 @@
self._api.csfield_set_support(self, value)

@property
def meshed_region(self):
def meshed_region(self) -> MeshedRegion:
"""Meshed region of the field.

Return
Expand All @@ -747,8 +780,8 @@
return self._get_meshed_region()

@meshed_region.setter
def meshed_region(self, value):
self._set_support(value, "MESHED_REGION")
def meshed_region(self, value: MeshedRegion):
self._set_support(support=value, support_type="MESHED_REGION")

def __add__(self, field_b):
"""Add two fields.
Expand Down
14 changes: 13 additions & 1 deletion src/ansys/dpf/core/meshed_region.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@

from ansys.dpf.core import field, property_field, scoping, server as server_module
from ansys.dpf.core.cache import class_handling_cache
from ansys.dpf.core.check_version import server_meet_version, version_requires
from ansys.dpf.core.check_version import meets_version, server_meet_version, version_requires
from ansys.dpf.core.common import (
locations,
nodal_properties,
Expand Down Expand Up @@ -701,3 +701,15 @@ def field_of_properties(self, property_name):
# Not sure we go through here since the only datatype not int is coordinates,
# which is already dealt with previously.
return field.Field(server=self._server, field=field_out)

def is_empty(self) -> bool:
"""Whether the mesh is empty.

A mesh is considered empty when it has zero element, zero face, and zero node.
"""
no_faces = True
if meets_version(self._server.version, "7.0"):
no_faces = self.faces.n_faces == 0
no_elements = self.elements.n_elements == 0
no_nodes = self.nodes.n_nodes == 0
return no_nodes and no_faces and no_elements
33 changes: 19 additions & 14 deletions src/ansys/dpf/core/plotter.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@
from ansys.dpf.core.nodes import Node, Nodes

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


Expand Down Expand Up @@ -851,31 +853,32 @@ def plot_chart(fields_container, off_screen=False, screenshot=None):

def plot_contour(
self,
field_or_fields_container,
shell_layers=None,
meshed_region=None,
deform_by=None,
scale_factor=1.0,
field_or_fields_container: Union[Field, FieldsContainer],
shell_layers: eshell_layers = None,
meshed_region: MeshedRegion = None,
deform_by: Union[Field, Result, Operator] = None,
scale_factor: float = 1.0,
**kwargs,
):
"""Plot the contour result on its mesh support.

You cannot plot a fields container containing results at several
time steps.
time steps. Use :func:`FieldsContainer.animate` instead.

Parameters
----------
field_or_fields_container : dpf.core.Field or dpf.core.FieldsContainer
field_or_fields_container:
Field or field container that contains the result to plot.
shell_layers : core.shell_layers, optional
shell_layers:
Enum used to set the shell layers if the model to plot
contains shell elements.
deform_by : Field, Result, Operator, optional
contains shell elements. Defaults to the top layer.
meshed_region:
Mesh to plot the data on.
deform_by:
Used to deform the plotted mesh. Must output a 3D vector field.
Defaults to None.
scale_factor : float, optional
Scaling factor to apply when warping the mesh. Defaults to 1.0.
**kwargs : optional
scale_factor:
Scaling factor to apply when warping the mesh.
**kwargs:
Additional keyword arguments for the plotter. For more information,
see ``help(pyvista.plot)``.
"""
Expand Down Expand Up @@ -912,6 +915,8 @@ def plot_contour(
mesh = meshed_region
else:
mesh = self._mesh
if mesh.is_empty():
raise dpf_errors.EmptyMeshPlottingError

# get mesh scoping
location = None
Expand Down
10 changes: 10 additions & 0 deletions tests/test_plotter.py
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,16 @@ def test_field_shell_plot_scoping_elemental(multishells):
f.plot(shell_layers=core.shell_layers.top)


@pytest.mark.skipif(not HAS_PYVISTA, reason="Please install pyvista")
def test_field_plot_raise_empty_mesh(simple_bar):
ds = core.DataSources(simple_bar)
stream_prov = core.operators.metadata.streams_provider(data_sources=ds)
result_op = core.operators.result.displacement(streams_container=stream_prov)
field = result_op.outputs.fields_container()[0]
with pytest.raises(dpf_errors.EmptyMeshPlottingError):
field.plot()


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