Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 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
6 changes: 5 additions & 1 deletion src/ansys/dpf/core/elements.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
from __future__ import annotations

from enum import Enum
from typing import TYPE_CHECKING

import numpy as np

Expand All @@ -34,6 +35,9 @@
from ansys.dpf.core.element_descriptor import ElementDescriptor
from ansys.dpf.gate import integral_types

if TYPE_CHECKING: # pragma: no cover
from ansys.dpf.core.scoping import Scoping


class Element:
"""
Expand Down Expand Up @@ -492,7 +496,7 @@ def __get_element(self, elementindex=None, elementid=None):
return Element(self._mesh, elementid, elementindex, nodesOut)

@property
def scoping(self) -> scoping.Scoping:
def scoping(self) -> Scoping:
"""
Scoping of the elements.

Expand Down
11 changes: 9 additions & 2 deletions src/ansys/dpf/core/nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,18 @@

"""Nodes."""

from __future__ import annotations

from typing import TYPE_CHECKING

import numpy as np

from ansys.dpf.core.check_version import version_requires
from ansys.dpf.core.common import locations, nodal_properties

if TYPE_CHECKING: # pragma: no cover
from ansys.dpf.core.scoping import Scoping


class Node:
"""
Expand Down Expand Up @@ -194,13 +201,13 @@ def __get_node(self, nodeindex=None, nodeid=None):
return Node(self._mesh, nodeid, nodeindex, node_coordinates)

@property
def scoping(self):
def scoping(self) -> Scoping:
"""
Scoping of the nodes.

Returns
-------
scoping : Scoping
scoping:
Scoping of the nodes.

Examples
Expand Down
82 changes: 81 additions & 1 deletion src/ansys/dpf/core/plotter.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@

from __future__ import annotations

import os
from pathlib import Path
import sys
import tempfile
Expand All @@ -48,6 +47,7 @@

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

Expand Down Expand Up @@ -233,6 +233,37 @@
)
return label_actors

def add_scoping(
self,
scoping: dpf.core.Scoping,
mesh: dpf.core.MeshedRegion,
show_mesh: bool = False,
**kwargs,
):
# Add the mesh to the scene with low opacity
if show_mesh:
self._plotter.add_mesh(mesh=mesh.grid, opacity=0.3)

Check warning on line 245 in src/ansys/dpf/core/plotter.py

View check run for this annotation

Codecov / codecov/patch

src/ansys/dpf/core/plotter.py#L244-L245

Added lines #L244 - L245 were not covered by tests

scoping_mesh = None

Check warning on line 247 in src/ansys/dpf/core/plotter.py

View check run for this annotation

Codecov / codecov/patch

src/ansys/dpf/core/plotter.py#L247

Added line #L247 was not covered by tests

# If the scoping is nodal, use the add_points_label method
if scoping.location == locations.nodal:
node_indexes = np.where(np.isin(mesh.nodes.scoping.ids, scoping.ids))[0]

Check warning on line 251 in src/ansys/dpf/core/plotter.py

View check run for this annotation

Codecov / codecov/patch

src/ansys/dpf/core/plotter.py#L250-L251

Added lines #L250 - L251 were not covered by tests
# grid_points = [mesh.grid.points[node_index] for node_index in node_indexes]
scoping_mesh = mesh.grid.extract_points(ind=node_indexes, include_cells=False)

Check warning on line 253 in src/ansys/dpf/core/plotter.py

View check run for this annotation

Codecov / codecov/patch

src/ansys/dpf/core/plotter.py#L253

Added line #L253 was not covered by tests
# If the scoping is elemental, extract their edges and use active scalars to color them
if scoping.location == locations.elemental:
element_indexes = np.where(np.isin(mesh.elements.scoping.ids, scoping.ids))[0]
scoping_mesh = mesh.grid.extract_cells(ind=element_indexes)

Check warning on line 257 in src/ansys/dpf/core/plotter.py

View check run for this annotation

Codecov / codecov/patch

src/ansys/dpf/core/plotter.py#L255-L257

Added lines #L255 - L257 were not covered by tests

# If the scoping is faces, extract their edges and use active scalars to color them
if scoping.location == locations.faces:
raise NotImplementedError("Cannot plot a face scoping.")

Check warning on line 261 in src/ansys/dpf/core/plotter.py

View check run for this annotation

Codecov / codecov/patch

src/ansys/dpf/core/plotter.py#L260-L261

Added lines #L260 - L261 were not covered by tests

# Filter kwargs
kwargs_in = _sort_supported_kwargs(bound_method=self._plotter.add_mesh, **kwargs)
self._plotter.add_mesh(mesh=scoping_mesh, **kwargs_in)

Check warning on line 265 in src/ansys/dpf/core/plotter.py

View check run for this annotation

Codecov / codecov/patch

src/ansys/dpf/core/plotter.py#L264-L265

Added lines #L264 - L265 were not covered by tests

def add_field(
self,
field,
Expand Down Expand Up @@ -688,6 +719,55 @@
**kwargs,
)

def add_scoping(
self,
scoping: dpf.core.Scoping,
mesh: dpf.core.MeshedRegion,
show_mesh: bool = False,
**kwargs,
):
"""Add a scoping to the plotter.

A mesh is required to translate the scoping into entities to plot.
Tou can plot the mesh along with the scoping entities using ``show_mesh``.

Parameters
----------
scoping:
Scoping with a mesh-based location and IDs of entities to plot.
mesh:
``MeshedRegion`` to plot the field on.
show_mesh:
Whether to show the mesh along with the scoping entities.
**kwargs : optional
Additional keyword arguments for the plotter. More information
are available at :func:`pyvista.plot`.

Examples
--------
>>> from ansys.dpf import core as dpf
>>> from ansys.dpf.core import examples
>>> model = dpf.Model(examples.download_cfx_mixing_elbow())
>>> mesh = model.metadata.meshed_region
>>> node_scoping = dpf.Scoping(
... location=dpf.locations.nodal,
... ids=mesh.nodes.scoping.ids[0:100]
...)
>>> element_scoping = dpf.Scoping(
... location=dpf.locations.elemental,
... ids=mesh.elements.scoping.ids[0:100]
...)
>>> from ansys.dpf.core.plotter import DpfPlotter
>>> plt = DpfPlotter()
>>> plt.add_scoping(node_scoping, mesh, show_mesh=True, color="red")
>>> plt.add_scoping(element_scoping, mesh, color="green")
>>> plt.show_figure()

"""
self._internal_plotter.add_scoping(

Check warning on line 767 in src/ansys/dpf/core/plotter.py

View check run for this annotation

Codecov / codecov/patch

src/ansys/dpf/core/plotter.py#L767

Added line #L767 was not covered by tests
scoping=scoping, mesh=mesh, show_mesh=show_mesh, **kwargs
)

def show_figure(self, **kwargs):
"""Plot the figure built by the plotter object.

Expand Down
42 changes: 42 additions & 0 deletions src/ansys/dpf/core/scoping.py
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,48 @@
""" # noqa: E501
return _LocalScoping(self)

def plot(self, mesh, show_mesh: bool = False, **kwargs):
"""Plot the entities of the mesh corresponding to the scoping.

Parameters
----------
mesh:
Mesh to use to translate the scoping into mesh entities.
show_mesh:
Whether to also show the mesh with low opacity.
**kwargs : optional
Additional keyword arguments for the plotter. More information
are available at :func:`pyvista.plot`.

Returns
-------
(cpos, image):
Returns what the pyvista.show() method returns based on arguments.

Examples
--------
>>> from ansys.dpf import core as dpf
>>> from ansys.dpf.core import examples
>>> model = dpf.Model(examples.download_cfx_mixing_elbow())
>>> mesh = model.metadata.meshed_region
>>> node_scoping = dpf.Scoping(
... location=dpf.locations.nodal,
... ids=mesh.nodes.scoping.ids[0:100]
...)
>>> node_scoping.plot(mesh=mesh, color="red")
>>> element_scoping = dpf.Scoping(
... location=dpf.locations.elemental,
... ids=mesh.elements.scoping.ids[0:100]
...)
>>> element_scoping.plot(mesh=mesh, color="green")

"""
from ansys.dpf.core.plotter import DpfPlotter

Check warning on line 530 in src/ansys/dpf/core/scoping.py

View check run for this annotation

Codecov / codecov/patch

src/ansys/dpf/core/scoping.py#L530

Added line #L530 was not covered by tests

plt = DpfPlotter(**kwargs)
plt.add_scoping(scoping=self, mesh=mesh, show_mesh=show_mesh, **kwargs)
return plt.show_figure(**kwargs)

Check warning on line 534 in src/ansys/dpf/core/scoping.py

View check run for this annotation

Codecov / codecov/patch

src/ansys/dpf/core/scoping.py#L532-L534

Added lines #L532 - L534 were not covered by tests


class _LocalScoping(Scoping):
"""Caches the internal data of the scoping so that it can be modified locally.
Expand Down
73 changes: 73 additions & 0 deletions src/ansys/dpf/core/scopings_container.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,16 @@
Contains classes associated to the DPF ScopingsContainer
"""

from __future__ import annotations

from typing import TYPE_CHECKING

from ansys.dpf.core import scoping
from ansys.dpf.core.collection_base import CollectionBase

if TYPE_CHECKING: # pragma: no cover
from ansys.dpf.core import MeshedRegion, MeshesContainer


class ScopingsContainer(CollectionBase[scoping.Scoping]):
"""A class used to represent a ScopingsContainer which contains scopings split on a given space.
Expand Down Expand Up @@ -125,3 +132,69 @@
DPF scoping to add.
"""
return super()._add_entry(label_space, scoping)

def plot(
self,
mesh: MeshedRegion | MeshesContainer,
show_mesh: bool = False,
colors: list[str] = None,
**kwargs,
):
"""Plot the entities of the mesh or meshes corresponding to the scopings.
Parameters
----------
mesh:
Mesh or meshes to use to translate the scopings into mesh entities.
Associates each scoping to a mesh using labels if ``mesh`` is a collection of meshes.
show_mesh:
Whether to also show the mesh with low opacity.
colors:
List of colors to use for the scoping entities.
**kwargs : optional
Additional keyword arguments for the plotter. More information
are available at :func:`pyvista.plot`.
Returns
-------
(cpos, image):
Returns what the pyvista.show() method returns based on arguments.
Examples
--------
>>> from ansys.dpf import core as dpf
>>> from ansys.dpf.core import examples
>>> model = dpf.Model(examples.download_cfx_mixing_elbow())
>>> mesh = model.metadata.meshed_region
>>> node_scoping_1 = dpf.Scoping(
... location=dpf.locations.nodal,
... ids=mesh.nodes.scoping.ids[0:100]
...)
>>> node_scoping_2 = dpf.Scoping(
... location=dpf.locations.nodal,
... ids=mesh.nodes.scoping.ids[300:400]
...)
>>> node_sc = dpf.ScopingsContainer()
>>> node_sc.add_label(label="scoping", default_value=1)
>>> node_sc.add_scoping(label_space={"scoping": 1}, scoping=node_scoping_1)
>>> node_sc.add_scoping(label_space={"scoping": 2}, scoping=node_scoping_2)
>>> node_sc.plot(mesh=mesh, show_mesh=True)
"""
from itertools import cycle

Check warning on line 184 in src/ansys/dpf/core/scopings_container.py

View check run for this annotation

Codecov / codecov/patch

src/ansys/dpf/core/scopings_container.py#L184

Added line #L184 was not covered by tests

from ansys.dpf.core.plotter import DpfPlotter

Check warning on line 186 in src/ansys/dpf/core/scopings_container.py

View check run for this annotation

Codecov / codecov/patch

src/ansys/dpf/core/scopings_container.py#L186

Added line #L186 was not covered by tests

colors_cycle = cycle(

Check warning on line 188 in src/ansys/dpf/core/scopings_container.py

View check run for this annotation

Codecov / codecov/patch

src/ansys/dpf/core/scopings_container.py#L188

Added line #L188 was not covered by tests
colors if colors else ["red", "blue", "green", "orange", "black", "yellow"]
)
plt = DpfPlotter(**kwargs)
for i, scoping_i in enumerate(self):
plt.add_scoping(

Check warning on line 193 in src/ansys/dpf/core/scopings_container.py

View check run for this annotation

Codecov / codecov/patch

src/ansys/dpf/core/scopings_container.py#L191-L193

Added lines #L191 - L193 were not covered by tests
scoping=scoping_i,
mesh=mesh,
color=next(colors_cycle),
show_mesh=show_mesh if i == 0 else False,
**kwargs,
)
return plt.show_figure(**kwargs)

Check warning on line 200 in src/ansys/dpf/core/scopings_container.py

View check run for this annotation

Codecov / codecov/patch

src/ansys/dpf/core/scopings_container.py#L200

Added line #L200 was not covered by tests
66 changes: 65 additions & 1 deletion tests/test_plotter.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,11 @@
from ansys.dpf import core
from ansys.dpf.core import Model, Operator, element_types, errors as dpf_errors, misc
from ansys.dpf.core.plotter import plot_chart
from conftest import SERVERS_VERSION_GREATER_THAN_OR_EQUAL_TO_5_0, running_docker
from conftest import (
SERVERS_VERSION_GREATER_THAN_OR_EQUAL_TO_5_0,
SERVERS_VERSION_GREATER_THAN_OR_EQUAL_TO_7_0,
running_docker,
)

if misc.module_exists("pyvista"):
HAS_PYVISTA = True
Expand Down Expand Up @@ -809,3 +813,63 @@ def test_plot_polyhedron():

# Plot the MeshedRegion
mesh.plot()


@pytest.mark.skipif(not HAS_PYVISTA, reason="This test requires pyvista")
@pytest.mark.skipif(
not SERVERS_VERSION_GREATER_THAN_OR_EQUAL_TO_7_0,
reason="cff::cas::meshes_provider requires DPF 24R1",
)
def test_plotter_add_scoping(fluent_mixing_elbow_steady_state):
mesh: core.MeshedRegion = core.operators.mesh.mesh_provider(
data_sources=fluent_mixing_elbow_steady_state()
).eval()
node_scoping = core.Scoping(location=core.locations.nodal, ids=mesh.nodes.scoping.ids[0:100])
element_scoping = core.Scoping(
location=core.locations.elemental, ids=mesh.elements.scoping.ids[0:100]
)
plt = DpfPlotter()
plt.add_scoping(node_scoping, mesh, show_mesh=True, color="red")
plt.add_scoping(element_scoping, mesh, color="green")
plt.show_figure()

face_scoping = core.Scoping(location=core.locations.faces, ids=mesh.faces.scoping.ids[0:100])
with pytest.raises(NotImplementedError):
plt.add_scoping(face_scoping, mesh)


@pytest.mark.skipif(not HAS_PYVISTA, reason="This test requires pyvista")
@pytest.mark.skipif(
not SERVERS_VERSION_GREATER_THAN_OR_EQUAL_TO_7_0,
reason="cff::cas::meshes_provider requires DPF 24R1",
)
def test_scoping_plot(fluent_mixing_elbow_steady_state):
mesh: core.MeshedRegion = core.operators.mesh.mesh_provider(
data_sources=fluent_mixing_elbow_steady_state()
).eval()
node_scoping = core.Scoping(location=core.locations.nodal, ids=mesh.nodes.scoping.ids[0:100])
node_scoping.plot(mesh=mesh, color="red")
element_scoping = core.Scoping(
location=core.locations.elemental, ids=mesh.elements.scoping.ids[0:100]
)
element_scoping.plot(mesh=mesh, color="green")


@pytest.mark.skipif(not HAS_PYVISTA, reason="This test requires pyvista")
@pytest.mark.skipif(
not SERVERS_VERSION_GREATER_THAN_OR_EQUAL_TO_7_0,
reason="cff::cas::meshes_provider requires DPF 24R1",
)
def test_scopings_container_plot(fluent_mixing_elbow_steady_state):
mesh: core.MeshedRegion = core.operators.mesh.mesh_provider(
data_sources=fluent_mixing_elbow_steady_state()
).eval()
node_scoping_1 = core.Scoping(location=core.locations.nodal, ids=mesh.nodes.scoping.ids[0:100])
node_scoping_2 = core.Scoping(
location=core.locations.nodal, ids=mesh.nodes.scoping.ids[300:400]
)
node_sc = core.ScopingsContainer()
node_sc.add_label(label="scoping", default_value=1)
node_sc.add_scoping(label_space={"scoping": 1}, scoping=node_scoping_1)
node_sc.add_scoping(label_space={"scoping": 2}, scoping=node_scoping_2)
node_sc.plot(mesh=mesh, show_mesh=True)