Holoviews usage

Good resource: https://holoviews.org/getting_started/Gridded_Datasets.html

Doing n-dimensional plots with traditional plotting libraries can be tedious. Holoviews is a library designed for creating multidimensional (especially useful for > 2) plots easily. It is very well suited if you want to explore your data interactively. As we will see, converting xarray objects (the most common type of output in postopus) to holoviews objects is trivial. The customization level of a holoviews plot is very high, because it supports multiple backends, here we will cover matplotlib and bokeh (the default backend).

[1]:
import numpy as np
from matplotlib import pyplot as plt
from postopus.octopus_run import Run
from pathlib import Path

%config InlineBackend.figure_formats = ['svg']

input file is already defined in the folder (s. GitLab repo), otherwise we recommend defining it in the notebook

[2]:
cd ../../tests/data/benzene/
/builds/octopus-code/postopus/tests/data/benzene
/usr/local/lib/python3.9/dist-packages/IPython/core/magics/osm.py:417: UserWarning: using dhist requires you to install the `pickleshare` library.
  self.shell.db['dhist'] = compress_dhist(dhist)[-100:]

Assuming you have octopus in your PATH:

[3]:
!octopus > out_gs.log 2>&1
[4]:
run = Run(".")
[5]:
xa = run.default.scf.density.get_converged(source="cube")  # postopus XArray
xa
[5]:
<xarray.DataArray 'density' (step: 1, x: 47, y: 49, z: 33)>
array([[[[0., 0., 0., ..., 0., 0., 0.],
         [0., 0., 0., ..., 0., 0., 0.],
         [0., 0., 0., ..., 0., 0., 0.],
         ...,
         [0., 0., 0., ..., 0., 0., 0.],
         [0., 0., 0., ..., 0., 0., 0.],
         [0., 0., 0., ..., 0., 0., 0.]],

        [[0., 0., 0., ..., 0., 0., 0.],
         [0., 0., 0., ..., 0., 0., 0.],
         [0., 0., 0., ..., 0., 0., 0.],
         ...,
         [0., 0., 0., ..., 0., 0., 0.],
         [0., 0., 0., ..., 0., 0., 0.],
         [0., 0., 0., ..., 0., 0., 0.]],

        [[0., 0., 0., ..., 0., 0., 0.],
         [0., 0., 0., ..., 0., 0., 0.],
         [0., 0., 0., ..., 0., 0., 0.],
         ...,
...
         ...,
         [0., 0., 0., ..., 0., 0., 0.],
         [0., 0., 0., ..., 0., 0., 0.],
         [0., 0., 0., ..., 0., 0., 0.]],

        [[0., 0., 0., ..., 0., 0., 0.],
         [0., 0., 0., ..., 0., 0., 0.],
         [0., 0., 0., ..., 0., 0., 0.],
         ...,
         [0., 0., 0., ..., 0., 0., 0.],
         [0., 0., 0., ..., 0., 0., 0.],
         [0., 0., 0., ..., 0., 0., 0.]],

        [[0., 0., 0., ..., 0., 0., 0.],
         [0., 0., 0., ..., 0., 0., 0.],
         [0., 0., 0., ..., 0., 0., 0.],
         ...,
         [0., 0., 0., ..., 0., 0., 0.],
         [0., 0., 0., ..., 0., 0., 0.],
         [0., 0., 0., ..., 0., 0., 0.]]]])
Coordinates:
  * step     (step) int64 1
  * x        (x) float64 -13.04 -12.47 -11.91 -11.34 ... 11.34 11.91 12.47 13.04
  * y        (y) float64 -13.61 -13.04 -12.47 -11.91 ... 11.91 12.47 13.04 13.61
  * z        (z) float64 -9.071 -8.504 -7.937 -7.37 ... 7.37 7.937 8.504 9.071
Attributes:
    units:    au

Importing holoviews

[6]:
import holoviews as hv
from holoviews import opts  # For setting defaults

hv.extension("bokeh", "matplotlib")  # Allow for interactive plots

In the following, we are going to convert an xarray to a holoviews Dataset and then to a holoviews Image. Actually, holoviews Images are used for 2D plots. Since we have 3D data, it will be converted to a Holomap of Images. (If we would want to plot for example 1D data, instead of holoviews Images, we would use holoviews Curves. The code would be analogous.) This image allows you to slide across time and space and visualize the resulting structure. (If you want to move step-by-step, just select a dimension and use the left and right arrows to navigate). We can use the kdims argument to specify which coordinates will serve as the visible axes on the plots (in this example we will choose x and y). The ones that are left will be controlled by a slider (in this example t and step will get a slider and a dropdown). When building the image, the default behavior of holoviews is to preload the whole data at once with javascript. The loading of the image can be very slow, especially if the amount of data is high. Don’t worry, we’ll learn how to make it faster!

Generating a holoviews Dataset

[7]:
hv_ds = hv.Dataset(xa)

Generating a holoviews Image

[8]:
hv_im = hv_ds.to(hv.Image, kdims=["x", "y"])

The following two plots will be slowish.

[9]:
hv_im
[9]:
[10]:
type(hv_im)
[10]:
holoviews.core.spaces.HoloMap
[11]:
hv_im.data  # one can see here that each of the samples within the Holomap is an Image
[11]:
{(1, -9.070685): :Image   [x,y]   (density),
 (1, -8.503767): :Image   [x,y]   (density),
 (1, -7.936848999999999): :Image   [x,y]   (density),
 (1, -7.369930999999999): :Image   [x,y]   (density),
 (1, -6.803012999999999): :Image   [x,y]   (density),
 (1, -6.236094999999999): :Image   [x,y]   (density),
 (1, -5.6691769999999995): :Image   [x,y]   (density),
 (1, -5.102258999999999): :Image   [x,y]   (density),
 (1, -4.535340999999999): :Image   [x,y]   (density),
 (1, -3.9684229999999987): :Image   [x,y]   (density),
 (1, -3.4015049999999984): :Image   [x,y]   (density),
 (1, -2.834586999999999): :Image   [x,y]   (density),
 (1, -2.267668999999999): :Image   [x,y]   (density),
 (1, -1.7007509999999986): :Image   [x,y]   (density),
 (1, -1.1338329999999992): :Image   [x,y]   (density),
 (1, -0.5669149999999981): :Image   [x,y]   (density),
 (1, 3.0000000013075123e-06): :Image   [x,y]   (density),
 (1, 0.5669210000000007): :Image   [x,y]   (density),
 (1, 1.1338390000000018): :Image   [x,y]   (density),
 (1, 1.7007570000000012): :Image   [x,y]   (density),
 (1, 2.2676750000000023): :Image   [x,y]   (density),
 (1, 2.8345930000000017): :Image   [x,y]   (density),
 (1, 3.401511000000001): :Image   [x,y]   (density),
 (1, 3.968429000000002): :Image   [x,y]   (density),
 (1, 4.535347000000002): :Image   [x,y]   (density),
 (1, 5.102265000000001): :Image   [x,y]   (density),
 (1, 5.669183000000002): :Image   [x,y]   (density),
 (1, 6.2361010000000014): :Image   [x,y]   (density),
 (1, 6.803019000000001): :Image   [x,y]   (density),
 (1, 7.369937000000002): :Image   [x,y]   (density),
 (1, 7.936855000000003): :Image   [x,y]   (density),
 (1, 8.503773): :Image   [x,y]   (density),
 (1, 9.070691000000002): :Image   [x,y]   (density)}

Customizing plot parameters with opts

[12]:
# Sets defaults for all the interactive images in this notebook
# Warning: Currently, there is no way to reset the default settings:
# https://stackoverflow.com/questions/68748393/how-do-i-clear-all-custom-opts-that-ive-set-for-holoviews.
# The only method that works is restarting the kernel, so think about setting anything to default.
opts.defaults(
    opts.Image(cmap="viridis", width=400, height=400),
)
# <hv object>.opts.clear() / <hv object>.opts(clone=False)  to rollback to default settings if desired,
# from https://holoviews.org/user_guide/Applying_Customizations.html.
[13]:
hv_im.opts(opts.Image(title="title"))  # Sets specific opts for a single plot
[13]: