How to plot two fields together with pyvista?

Hi all, I want to plot two fields obtained from FEniCSX in the same figure with pyvista (or any other packages). Suppose we have two fields DG_0_field and CG_1_field, and we can assign random values to them:

import numpy as np
import dolfinx
from dolfinx.cpp.mesh import CellType
from mpi4py import MPI
import dolfinx.plot
import pyvista

mesh = dolfinx.RectangleMesh(MPI.COMM_WORLD, [np.array([0, 0, 0]), np.array([20, 5, 0])], \
    [20,10], CellType.quadrilateral)
NElem = mesh.topology.index_map(mesh.topology.dim).size_local # number of elements
NNode = mesh.geometry.x.shape[0] # number of nodes

DG_0_field = np.random.rand(NElem)
CG_1_field = np.random.rand(NNode)

It is quite convenient to plot them separately based on the tutorials The FEniCSx tutorial — FEniCSx tutorial (jorgensd.github.io).

For the DG_0_field:

topology, cell_types = dolfinx.plot.create_vtk_topology(mesh, mesh.topology.dim)
grid = pyvista.UnstructuredGrid(topology, cell_types, mesh.geometry.x)
grid.cell_arrays["Marker"] = DG_0_field
grid.set_active_scalars("Marker")

pyvista.start_xvfb(wait=0.05)
plotter = pyvista.Plotter()

# plot DG_0 field
plotter.add_mesh(grid, clim=[0,1], cmap='Greys', show_scalar_bar=False)
plotter.background_color = (1,1,1)
plotter.view_xy()
if not pyvista.OFF_SCREEN:
    plotter.show()
figure = plotter.screenshot("DG_0_field.jpg")

we can obtain the result:
image

For the CG_1_field:

# plot CG_1 field
threshold = 0.3
sargs= dict(height=0.1, width=0.6, vertical=False, position_x=0.2, position_y=0.3, \
    fmt="%1.1f", title_font_size=25, color="black", label_font_size=25)
grid.point_arrays["CG_1"] = CG_1_field
grid.set_active_scalars("CG_1")
plotter.add_mesh(grid, clim=[0,threshold], cmap='jet_r', show_edges=False, above_color=None)
plotter.view_xy()
plotter.background_color = (1,1,1)
if not pyvista.OFF_SCREEN:
    plotter.show()
figure = plotter.screenshot("CG_1_field.jpg")

we can obtain the result:
image

So how can we merge these two figures into one plot? Also, I want to set the above_color as transparent but I could not find any useful tutorials about it. Finally, I want to obtain a figure like this:
image

Or do you have some better software or packages to plot these results from FEniCS/FEniCSX?
Thanks!

Have you had a look at: Implementation — FEniCSx tutorial, especially, the ITK plotter? it allows you to have multiple fields in the same figure, and you can adjust the opacity of each of them.

2 Likes

Hi @dokken, that part is helpful. I try to adjust the opacity first but it does not work well. Later I use warp-by-scalar employed in the tutorial, and it works. And I would like share my code here for future readers.

import numpy as np
import dolfinx
from dolfinx.cpp.mesh import CellType
from mpi4py import MPI
import dolfinx.plot
import pyvista

# data
mesh = dolfinx.RectangleMesh(MPI.COMM_WORLD, [np.array([0, 0, 0]), np.array([30, 30, 0])], \
    [30, 30], CellType.quadrilateral)
NElem = mesh.topology.index_map(mesh.topology.dim).size_local
NNode = mesh.geometry.x.shape[0]
DG_0_field = np.random.rand(NElem)
CG_1_field = np.random.rand(NNode)

topology, cell_types = dolfinx.plot.create_vtk_topology(mesh, mesh.topology.dim)
figsize = 1000
pyvista.start_xvfb(wait=0.05)

# DG_0_field
plotter = pyvista.Plotter(window_size=[figsize,figsize])
grid = pyvista.UnstructuredGrid(topology, cell_types, mesh.geometry.x)
grid.cell_arrays["DG_0_field"] = DG_0_field
plotter.add_mesh(grid, clim=[0,1], cmap='Greys', show_scalar_bar=False)
plotter.view_xy()
plotter.background_color = (1,1,1)
plotter.screenshot("DG_0_field.jpg")

image

# CG_1_field
plotter = pyvista.Plotter(window_size=[figsize,figsize])
grid = pyvista.UnstructuredGrid(topology, cell_types, mesh.geometry.x)
grid.point_arrays["CG_1_field"] = CG_1_field
plotter.add_mesh(grid, clim=[0,1], cmap='jet_r', show_edges=False)
plotter.view_xy()
plotter.background_color = (1,1,1)
plotter.screenshot("CG_1_field.jpg")

image

# Two fields
plotter = pyvista.Plotter(window_size=[figsize,figsize])
grid = pyvista.UnstructuredGrid(topology, cell_types, mesh.geometry.x)
grid.cell_arrays["DG_0_field"] = DG_0_field
plotter.add_mesh(grid, clim=[0,1], cmap='Greys', show_scalar_bar=False)

threshold = 0.4
grid = pyvista.UnstructuredGrid(topology, cell_types, mesh.geometry.x)
grid.point_arrays["CG_1_field"] = threshold - CG_1_field
warp = grid.warp_by_scalar(factor=1e-3)
plotter.add_mesh(warp, clim=[-1+threshold,threshold], cmap='jet', show_edges=False, show_scalar_bar=False)
plotter.view_xy()
plotter.background_color = (1,1,1)
plotter.screenshot("Two fields.jpg")

image

Hi @dokken, I still have two follow-up questions for this:

(1) Can we set two scalar bars corresponding to the two fields in one plot? I try to set them in one plot, but it seems the new add_mesh will cover the previous scalar bar.

(2) In addition, I have adjusted the clim for the CG_1_field from clim=[0,1] to clim=[-1+threshold,threshold] in the third plot such as the colors in the second and third plots match with each other exactly. But when we plot the scalar bar for the CG_1_field, can we still set the numbers of scalar bar as [0,1]?

These questions are related strictly to pyvista functionality, so I would suggest creating a post at http://slack.pyvista.org/

1 Like