Source code for dolfin_pyvista_adapter.source

import dolfin
import pyvista
from ufl import vertex
from typing import Tuple, Union, List
import numpy.typing as npt
import numpy as np
import pathlib
from typing import Optional

__all__ = ["create_vtk_structures", "plot"]

_vtk_perm = {
    dolfin.interval: {i: np.arange(i) for i in range(2, 10)},
    vertex: {1: 1},
    dolfin.triangle: {
        1: [0, 1, 2],
        2: [0, 1, 2, 5, 3, 4],
        3: [0, 1, 2, 7, 8, 3, 4, 6, 5, 9],
        4: [0, 1, 2, 9, 10, 11, 3, 4, 5, 8, 7, 6, 12, 13, 14],
    },
    dolfin.tetrahedron: {
        1: [0, 1, 2, 3],
        2: [0, 1, 2, 3, 9, 6, 8, 7, 5, 4],
        3: [0, 1, 2, 3, 14, 15, 8, 9, 13, 12, 10, 11, 6, 7, 4, 5, 18, 16, 17, 19],
    },
}


[docs]def create_vtk_structures( Vh: dolfin.FunctionSpace, ) -> Tuple[npt.NDArray[np.int32], npt.NDArray[np.int32], npt.NDArray[np.float64]]: """Given a (discontinuous) Lagrange space, create pyvista compatible structures based on the dof coordinates :param Vh: the function space :return: Mesh topology, cell types and mesh geometry arrays """ family = Vh.ufl_element().family() mesh = Vh.mesh() if not (family in ["Discontinuous Lagrange", "Lagrange", "DQ", "Q", "DP", "P"]): raise RuntimeError( "Can only create meshes from continuous or discontinuous Lagrange spaces" ) num_cells = mesh.num_cells() if num_cells == 0: return ( np.zeros(0, dtype=np.int32), np.zeros(0, dtype=np.int32), np.zeros((0, 3), dtype=np.float64), ) d_cell = mesh.ufl_cell() if d_cell == vertex: cell_type = 1 elif d_cell == dolfin.interval: cell_type = 68 elif d_cell == dolfin.triangle: cell_type = 69 elif d_cell == dolfin.tetrahedron: cell_type = 71 else: raise RuntimeError(f"Unsupported {d_cell=}") cell_types = np.full(num_cells, cell_type, dtype=np.int32) bs = 1 if Vh.num_sub_spaces() == 0 else Vh.num_sub_spaces() Vh = Vh if bs == 1 else Vh.sub(0).collapse() num_dofs_per_cell = len(Vh.dofmap().cell_dofs(0)) topology = np.zeros((num_cells, num_dofs_per_cell + 1), dtype=np.int32) topology[:, 0] = num_dofs_per_cell dofmap = Vh.dofmap() num_vertices_local = dofmap.index_map().size(dofmap.index_map().MapSize.ALL) x = np.zeros((num_vertices_local, 3), dtype=np.float64) el = Vh.element() for i in range(num_cells): topology[i, 1:] = dofmap.cell_dofs(i) cell = dolfin.Cell(mesh, i) cell_dof_coords = el.tabulate_dof_coordinates(cell) for j, d in enumerate(dofmap.cell_dofs(i)): x[d, : cell_dof_coords.shape[1]] = cell_dof_coords[j] degree = Vh.ufl_element().degree() try: perm = _vtk_perm[d_cell][degree] except KeyError: raise RuntimeError( f"Unsupported plotting of space {family} of {degree=} on {d_cell}" ) topology[:, 1:] = topology[:, 1:][:, perm] return topology, cell_types, x
[docs]def plot( uh: dolfin.Function, filename: Optional[pathlib.Path] = None, show_edges: bool = True, glyph_factor: float = 1, off_screen: bool = True, cmap: Union[str, List[str]] = "viridis", ): """ Plot a (discontinuous) Lagrange function with Pyvista :param uh: The function :param filename: If set, writes the plot to file instead of displaying it interactively :param show_edges: Show mesh edges if ``True`` :param glyph_factor: Scaling of glyphs if input function is a function from a ``dolfin.VectorFunctionSpace``. :param off_screen: If ``True`` generate plots with virtual frame buffer using ``xvfb``. :param cmap: The colormap to use. If a string uses matplotlib colormaps, if a list uses strings as colors. """ Vh = uh.function_space() dofmap = Vh.dofmap() num_vertices_local = dofmap.index_map().size(dofmap.index_map().MapSize.ALL) bs = 1 if Vh.num_sub_spaces() == 0 else Vh.num_sub_spaces() u_vec = uh.vector().get_local(np.arange(num_vertices_local)) if bs > 1: u_out = np.zeros((len(u_vec) // bs, 3), dtype=np.float64) u_out[:, :bs] = u_vec.reshape(len(u_vec) // bs, bs) else: u_out = u_vec topology, cell_types, x = create_vtk_structures(Vh) grid = pyvista.UnstructuredGrid(topology, cell_types, x) grid[uh.name()] = u_out if bs > 1: grid.set_active_scalars(None) pyvista.OFF_SCREEN = off_screen pyvista.start_xvfb() plotter = pyvista.Plotter() # Workaround for pyvista issue for higherorder grids degree = Vh.ufl_element().degree() if degree > 1 and show_edges: first_order_el = Vh.ufl_element().reconstruct(degree=1) P1 = dolfin.FunctionSpace(Vh.mesh(), first_order_el) t1, c1, x1 = create_vtk_structures(P1) p1_grid = pyvista.UnstructuredGrid(t1, c1, x1) if bs > 1: plotter.add_mesh(p1_grid, show_edges=show_edges, cmap=cmap) else: plotter.add_mesh(grid, cmap=cmap) plotter.add_mesh(p1_grid, style="wireframe", cmap=cmap) else: plotter.add_mesh(grid, show_edges=show_edges, cmap=cmap) if bs > 1: glyphs = grid.glyph(orient=uh.name(), factor=glyph_factor) plotter.add_mesh(glyphs, cmap=cmap) print(filename) if filename is None: plotter.show() else: plotter.screenshot(filename)