7. Visualization#

As presented in the Fundamentals, pyGIMLi offers some basic post-processing routines for plotting. For 2D and 3D visualizations, we rely on Matplotlib and pyVista, respectively, and provide the following frameworks:

pygimli.viewer.mpl

Drawing functions using Matplotlib.

pygimli.viewer.pv

Pyvista based drawing functions used by pygimli.viewer.

In the following, we will give a brief overview on the most important aspects on visualizing your plots as well as performing simple (cosmetic) changes.

7.1. Plotting in 2D#

For plotting in 2D, the method pygimli.viewer.showMesh() is called, which creates an axis object and plots a 2D mesh, if provided with node or cell data. As already discussed in the fundamentals section, the type of data determines the appropriate draw method.

This matplotlib glossary comprehends all possible adjustments that you can apply to plots based on pg.show(). In the following table, we summarized the most relevant functions:

matplotlib.axes.Axes.text

Add text to the Axes.

matplotlib.axes.Axes.arrow

[Discouraged] Add an arrow to the Axes.

matplotlib.axes.Axes.axis

Convenience method to get or set some axis properties.

matplotlib.axes.Axes.set_axis_off

Hide all visual components of the x- and y-axis.

matplotlib.axes.Axes.set_axis_on

Do not hide all visual components of the x- and y-axis.

matplotlib.axes.Axes.invert_xaxis

Invert the x-axis.

matplotlib.axes.Axes.invert_yaxis

Invert the y-axis.

matplotlib.axes.Axes.set_xlim

Set the x-axis view limits.

matplotlib.axes.Axes.set_ylim

Set the y-axis view limits.

matplotlib.axes.Axes.set_xlabel

Set the label for the x-axis.

matplotlib.axes.Axes.set_ylabel

Set the label for the y-axis.

matplotlib.axes.Axes.set_title

Set a title for the Axes.

matplotlib.axes.Axes.legend

Place a legend on the Axes.

matplotlib.axes.Axes.set_aspect

Set the aspect ratio of the Axes scaling, i.e. y/x-scale.

matplotlib.axes.Axes.set_adjustable

Set how the Axes adjusts to achieve the required aspect ratio.

matplotlib.axes.Axes.set_xticks

Set the xaxis' tick locations and optionally tick labels.

matplotlib.axes.Axes.set_yticks

Set the yaxis' tick locations and optionally tick labels.

matplotlib.axes.Axes.sharex

Share the x-axis with other.

matplotlib.axes.Axes.sharey

Share the y-axis with other.

7.1.1. Plotting meshes and models#

As described in the Fundamentals section, pygimli.viewer.show() and pygimli.viewer.showMesh() utilize a variety of drawing functions, depending on the input data provided. In the following, we will take a look at how to manually access the necessary drawing functions to plot an empty mesh, as well as cell-based and node-based data.

Hide code cell source

import numpy as np
import matplotlib.pyplot as plt
import pygimli as pg
from pygimli.viewer import mpl
import pygimli.meshtools as mt

To visualize a grid or a triangular mesh in 2D, we can simply make use of the pygimli.viewer.show() function, which refers to pygimli.viewer.mpl.drawMesh():

n = np.linspace(1, 2, 10)
mesh = pg.createGrid(x=n, y=n)
fig, ax = plt.subplots()
mpl.drawMesh(ax, mesh)
plt.show()
../../_images/7e54313e983ecc0d33f582b637fbee15af8bfb394f1f21bfed3a9538d8e36cf5.png

If we now want to plot cell-based values on top of our mesh, pygimli.viewer.show() links to the function pygimli.viewer.mpl.drawModel():

mx = pg.x(mesh.cellCenter())
my = pg.y(mesh.cellCenter())
data = np.cos(1.5 * mx) * np.sin(1.5 * my)
fig, ax = plt.subplots()
mpl.drawModel(ax, mesh, data)
plt.show()
../../_images/94a6ca0542bb8163c97d9c88c518649c35caa7c484e4ab7b1b5b4c56d098204a.png

Similarly, for scalar field values, the function pygimli.viewer.mpl.drawField() is utilized:

nx = pg.x(mesh.positions())
ny = pg.y(mesh.positions())
data = np.cos(1.5 * nx) * np.sin(1.5 * ny)
fig, ax = plt.subplots()
mpl.drawField(ax, mesh, data)
plt.show()
../../_images/29244112fd07f9b897223f631a985b102192988bd43bd9c07cf672cc66740d6e.png

pyGIMLi also allows to plot vector field streamlines with pygimli.viewer.mpl.drawStreams, as in the following example. Every cell contains only one streamline and every new stream line starts in the center of a cell.

fig, ax = plt.subplots()
mpl.drawStreams(ax, mesh, data, color='red')
mpl.drawStreams(ax, mesh, data, dropTol=0.9)
mpl.drawStreams(ax, mesh, pg.solver.grad(mesh, data),
            color='green', quiver=True)
ax.set_aspect('equal')
../../_images/253af8f54b5c7114d619839cf42faead753a2fbc1d73b8f0e3a952468dfa9361.png

A more specific case is the visualization of sensor positions, which is also covered by a drawing function within the visualization framework of pyGIMLi. By providing a list of sensor positions to plot as [x,y] pairs, the function pygimli.viewer.mpl.drawSensors()draws the sensor positions as dots with a given diameter.

sensors = np.random.rand(5, 2)
fig, ax = pg.plt.subplots()
mpl.drawSensors(ax, sensors, diam=0.02, coords=[0, 1])
ax.set_aspect('equal')
../../_images/6ceae9ae66f86fc9392311685a1899d7546d50a3062d8d504f13aa005b625dee.png

7.2. Plotting in 3D#

For plotting in 3D, pyGIMLi utilizes the pygimli.viewer.pv module, which leverages the capabilities of pyVista for rendering. The primary function for visualizing 3D meshes is pygimli.viewer.pv.drawMesh(), which can handle various types of 3D data, including cell-based and node-based data. Similar to 2D plotting, the type of data provided determines the appropriate drawing method. As for the 2D plots, we can fine-tune the 3D plots by referring to the pyvista.Plotter functions The following examples demonstrate how to plot 3D meshes, cell data, and streamlines, as well as how to create slices through the mesh for detailed analysis.

7.2.1. Plotting meshes and models in 3D#

Plotting meshes using pyVista is straightforward with pyGIMLi. The pygimli.viewer.pv.drawMesh() function is called to visualize 3D meshes and models, leveraging pyVista’s powerful rendering capabilities.

from pygimli.viewer import pv
plc = mt.createCube(size=[40, 20, 15], marker=1, boundaryMarker=0)
cube = mt.createCube(size=[15, 15, 8], marker=2, boundaryMarker=0)
geom = plc + cube

mesh = mt.createMesh(geom, area=4)
pg.show(mesh, style='wireframe', showMesh=True)
Opening /tmp/tmpcccco3ws.poly.
  Segments are connected properly.
(<pyvista.plotting.plotter.Plotter at 0x14c11d6318d0>, None)

To visualize cell-based values in a 3D mesh created with pyVista, pg.show makes use of the pygimli.viewer.pv.drawModel() function, which draws a given mesh together with provided values:

mx = pg.x(mesh.cellCenter())
my = pg.y(mesh.cellCenter())
mz = pg.z(mesh.cellCenter())

data = mx

pg.show(mesh, data, label="Cell position x (m)")
(<pyvista.plotting.plotter.Plotter at 0x14c11d93d690>, None)

As for the 2D section, pyVista also allows to plot streams by taking vector field of gradient data per cell. The according function is utilized in the example below:

ax, _ = pg.show(mesh, alpha=0.3, hold=True, colorBar=False)
pv.drawStreamLines(ax, mesh, data, radius=.1, source_radius=10)
ax.show()

Creating slices in a 3D plot allows for detailed analysis of the internal structure of the mesh. The pygimli.viewer.pv.drawSlice() function can be used to create and visualize these slices, specifying the normal vector and cell values to be displayed.

ax, _ = pg.show(mesh, alpha=0.3, hold=True, colorBar=False)
pv.drawSlice(ax, mesh, normal=[0,1,0], data=data, label="Cell position x")
ax.show()

If you want to take a look at more practical applications and examples that fully use the plotting capabilities of pyGIMLi, please refer to the examples section.

7.3. External plotting#

If you ever encounter the situation that the plotting capabilities of pyGIMLi are too limited for your need and you want to use more powerful programs such as Paraview, you can easily export meshes and models from pyGIMLi.

The VTK format can save values along with the mesh, point data like voltage under POINT_DATA and cell data like velocity under CELL_DATA. It is particularly suited to save inversion results including the inversion domain in one file.