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 ../octopus_data/benzene/
/builds/lang-m/postopus/docs/octopus_data/benzene

Assuming you have octopus in your PATH:

[3]:
!octopus > out_gs.log 2>&1
[4]:
run = Run(".")
[5]:
xa = run.default.scf.density(source="cube").isel(step=-1)  # postopus XArray
xa
[5]:
<xarray.DataArray 'density' (x: 47, y: 49, z: 33)> Size: 608kB
[75999 values with dtype=float64]
Coordinates:
    step     int64 8B 16
  * x        (x) float64 376B -13.04 -12.47 -11.91 -11.34 ... 11.91 12.47 13.04
  * y        (y) float64 392B -13.61 -13.04 -12.47 -11.91 ... 12.47 13.04 13.61
  * z        (z) float64 264B -9.071 -8.504 -7.937 -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]:
{(np.float64(-9.070685),): :Image   [x,y]   (density),
 (np.float64(-8.503767),): :Image   [x,y]   (density),
 (np.float64(-7.936848999999999),): :Image   [x,y]   (density),
 (np.float64(-7.369930999999999),): :Image   [x,y]   (density),
 (np.float64(-6.803012999999999),): :Image   [x,y]   (density),
 (np.float64(-6.236094999999999),): :Image   [x,y]   (density),
 (np.float64(-5.6691769999999995),): :Image   [x,y]   (density),
 (np.float64(-5.102258999999999),): :Image   [x,y]   (density),
 (np.float64(-4.535340999999999),): :Image   [x,y]   (density),
 (np.float64(-3.9684229999999987),): :Image   [x,y]   (density),
 (np.float64(-3.4015049999999984),): :Image   [x,y]   (density),
 (np.float64(-2.834586999999999),): :Image   [x,y]   (density),
 (np.float64(-2.267668999999999),): :Image   [x,y]   (density),
 (np.float64(-1.7007509999999986),): :Image   [x,y]   (density),
 (np.float64(-1.1338329999999992),): :Image   [x,y]   (density),
 (np.float64(-0.5669149999999981),): :Image   [x,y]   (density),
 (np.float64(3.0000000013075123e-06),): :Image   [x,y]   (density),
 (np.float64(0.5669210000000007),): :Image   [x,y]   (density),
 (np.float64(1.1338390000000018),): :Image   [x,y]   (density),
 (np.float64(1.7007570000000012),): :Image   [x,y]   (density),
 (np.float64(2.2676750000000023),): :Image   [x,y]   (density),
 (np.float64(2.8345930000000017),): :Image   [x,y]   (density),
 (np.float64(3.401511000000001),): :Image   [x,y]   (density),
 (np.float64(3.968429000000002),): :Image   [x,y]   (density),
 (np.float64(4.535347000000002),): :Image   [x,y]   (density),
 (np.float64(5.102265000000001),): :Image   [x,y]   (density),
 (np.float64(5.669183000000002),): :Image   [x,y]   (density),
 (np.float64(6.2361010000000014),): :Image   [x,y]   (density),
 (np.float64(6.803019000000001),): :Image   [x,y]   (density),
 (np.float64(7.369937000000002),): :Image   [x,y]   (density),
 (np.float64(7.936855000000003),): :Image   [x,y]   (density),
 (np.float64(8.503773),): :Image   [x,y]   (density),
 (np.float64(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]:
[14]:
# hv.help(hv.Image)  # Lists all the tunable parameters, for the active backend
# hv.help(hv_im)  # Lists the actual configuration of  a specific object

Time dependent plots#

Methane example#

To fasten up the loading process we can make use of the parameter dynamic=True. When this parameter is set as True, the frames will be loaded one by one, on-demand. (This means that in the background the Dataset will be converted into a DynamicMap instead of a Holomap). Note: if the style of the plotting is not the one that you expected, rerun the cell. The dynamic plots will always take the last used plotting settings, independently of what was declared in the cell. Second note: The dynamic plots cannot be rendered in HTML. If you are viewing this from your webbrowser, you will just see GIFs that should emulate the real plot. If you happen to be in an active notebook session, you can play with them interactively!

[15]:
cd ../methane
/builds/lang-m/postopus/docs/octopus_data/methane
[16]:
!octopus > out_gs.log 2>&1
[17]:
run = Run(".")
[18]:
xat = run.default.scf.density(source="vtk")  # XArray Time-dependent
[19]:
hv_dst = hv.Dataset(xat)  # convert to holoviews Dataset
hv_imt = hv_dst.to(hv.Image)  # convert to holoviews Image
hv.output(
    max_frames=10000
)  # sets the max number of frames that we can have in a slider-plot
[20]:
# hv_imt # Would be very slow! Minutes long!
[21]:
hv_dst = hv.Dataset(xat)
hv_imt = hv_dst.to(hv.Image, kdims=["x", "y"], dynamic=True)
hv.output(max_frames=10000)
[22]:
# Note for web users: You should have an active notebook session to interact with the plot
hv_imt  # fast!
[22]:
[23]:
type(hv_imt)
[23]:
holoviews.core.spaces.DynamicMap
[24]:
hv_imt.data  #  the number of images saved in cache will update if you scroll the plot. The default cache_size is 500
[24]:
{(np.int64(1), np.float64(-7.36993)): :Image   [x,y]   (density)}
[25]:
hv_imt.cache_size
[25]:
500

Histogram of the data#

[26]:
# Note for web users: You should have an active notebook session to interact with the plot
hv_imt.hist(num_bins=100, log=True).opts(
    opts.Image(
        colorbar=True,  # we set the opts for each hv object independently
        clabel=f"{xat.name} ({xat.units})",
    ),  # holoviews doesn't support the labeling of the cmap out of the box
    opts.Histogram(xlim=(0, 0.5), ylim=(0, 50)),
)  # use base 10 logarithmic samples for the bin edges (width)
[26]:
[27]:
# Note for web users: You should have an active notebook session to interact with the plot
hv_imt.hist(num_bins=100).opts(
    opts.Image(colorbar=True, clabel=f"{xat.name} ({xat.units})"),
    opts.Histogram(xlim=(0, 0.5), ylim=(0, 50)),
)  # comparison without log
[27]:

Swapping kdims#

[28]:
hv_imt_step_z = hv_dst.to(hv.Image, kdims=["step", "z"], dynamic=True)
[29]:
# Note for web users: You should have an active notebook session to interact with the plot
hv_imt_step_z
[29]:

Interference z=0 (slice)#

http://holoviews.org/user_guide/Styling_Plots.html https://holoviews.org/user_guide/Colormaps.html might come handy for customizing plots

[30]:
cd ../interference/
/builds/lang-m/postopus/docs/octopus_data/interference
[31]:
!octopus > out_td.log 2>&1
[32]:
run = Run(".")
[33]:
xa = run.Maxwell.td.b_field(source="z=0")
[34]:
hv_dst = hv.Dataset(xa.vx)
hv_imt = hv_dst.to(hv.Image, ["x", "y"])  # needed for generating output files
hv_imt_dynamic = hv_dst.to(hv.Image, ["x", "y"], dynamic=True)
hv.output(max_frames=3000)

Custom plotting options#

[35]:
xa
[35]:
<xarray.Dataset> Size: 687kB
Dimensions:  (t: 17, x: 41, y: 41)
Coordinates:
    step     (t) int64 136B 0 10 20 30 40 50 60 ... 100 110 120 130 140 150 160
  * t        (t) float64 136B 0.0 0.02107 0.04213 0.0632 ... 0.2949 0.316 0.337
  * x        (x) float64 328B -10.0 -9.5 -9.0 -8.5 -8.0 ... 8.0 8.5 9.0 9.5 10.0
  * y        (y) float64 328B -10.0 -9.5 -9.0 -8.5 -8.0 ... 8.0 8.5 9.0 9.5 10.0
Data variables:
    vx       (t, x, y) float64 229kB ...
    vy       (t, x, y) float64 229kB ...
    vz       (t, x, y) float64 229kB ...
Attributes:
    units:    au
[36]:
# cmap: Which colors should be used
# If cmap is not specified, and dynamic=True, it will change while we scroll. If dynamic=False, it will be fix.
# color_levels: in which ranges should these colors be used
# clim: what is the absolute scale of the colorbar
# Note for web users: You should have an active notebook session to interact with the plot
hv_imt_dynamic.opts(
    colorbar=True,
    width=500,
    height=400,
    # cmap=['#0000ff', '#89cff0', '#f75e25', '#ff0000'],
    # color_levels=[-10**-3, -10**-4, 0, 10**-4, 10**-3],
    cmap="seismic",
    clim=(-(10**-3), 10**-3),
    clabel=f"{xa.vx.name} ({xa.vx.units})",
)
[36]:

The middle of the colormap will be in the middle of the min and max values of clim, so e.g. if you want the color map to be symmetric around 0, you should climmin == -climmax.

Saving gifs, htmls, images and videos of plots#

Bokeh for (gifs and) htmls#

The recommended output format for interactive plots is html. If needed, one can also generate gifs and mp4s with holoviews.

One needs sudo apt install geckodriver (or conda install -c conda-forge geckodriver) for generating the bokeh gif, although one can generate matplotlib gifs without it.
One also needs sudo apt install ffmpeg (or conda install -c conda-forge ffmpeg) for generating matplotlib mp4s. It is also possible to generate them in RAVEN (s. Advanced section)

The gif and the mp4 generations were commented out because the CI would need geckodriver and ffmpeg. If you have them, just uncomment the code bits!

Note that we are using hv_imt and NOT hv_imt_dynamic!

[37]:
hv.save(
    hv_imt.opts(
        colorbar=True,
        width=500,
        height=400,
        cmap="seismic",
        clim=(-(10**-3), 10**-3),  # hv.Image args
    ),
    fmt="html",
    backend="bokeh",
    filename="test2",  # hv.save args
)

# uncomment if you have `geckodriver`
"""
hv.save(
    hv_imt.opts(
        colorbar=True, width=500, height=400, cmap='seismic', clim=(-10**-3, 10**-3)
    ),
    fmt="gif", backend="bokeh", filename="test3", fps=1
)
"""


hv.save(
    hv_imt.opts(
        colorbar=True,
        width=500,
        height=400,
        cmap="seismic",
        clim=(-(10**-3), 10**-3),
    ),
    fmt="scrubber",
    backend="bokeh",
    filename="test20",
    fps=1,
)

Look into the notebooks folder, you should see new files in it!

Matplotlib for gifs and mp4#

Bokeh is the default backend when working with holowiews. Although, bokeh doesn’t currently support the output of mp4s (Also: the gif generation in bokeh can be slower than with matplotlib). So that the holoviews object should be ported to a matploib backend. This shouldn’t be complicated. Probably, the only thing one needs to change isfig_size instead of height and width. Recall: hv.help(hv.Image) lists all the tunable parameters, for the active backend.

[38]:
hv.extension("matplotlib")

For Linux users: VLC player may needed for opening the mp4.

[39]:
# uncomment if you have `ffmpeg`
"""
hv.save(
    hv_imt.opts(colorbar=True, fig_inches=10, cmap='seismic', clim=(-10**-3, 10**-3)
               ),
    fmt="mp4", backend="matplotlib", filename="test4", fps=1)
"""
[39]:
'\nhv.save(\n    hv_imt.opts(colorbar=True, fig_inches=10, cmap=\'seismic\', clim=(-10**-3, 10**-3)\n               ),\n    fmt="mp4", backend="matplotlib", filename="test4", fps=1) \n'
[40]:
hv.save(
    hv_imt.opts(
        colorbar=True, cmap="seismic", fig_inches=10, clim=(-(10**-3), 10**-3)
    ),
    fmt="gif",
    backend="matplotlib",
    filename="test5",
    fps=1,
)
[41]:
hv.extension("bokeh")  # go back to bokeh as default

Export individual images#

For exporting individual png’s the easiest way, is while interacting with a dynamic plot: go to the frame that you are interested in, and click on the save button in the right-hand-bokeh-menu. That’s it:

[42]:
# Note for web users: You should have an active notebook session to interact with the plot
hv_imt_dynamic
[42]:

In case you need an svg, you will need to use matplotlib. Just slice the original xarray at the point where you are interested in:

[43]:
xa00 = xa.sel(
    t=0.0
)  # from the plot above look at which step interests you most, let's say t=0.0.
hv_dst00 = hv.Dataset(xa00)
hv_imt00 = hv_dst00.to(hv.Image, ["x", "y"])
[44]:
hv.extension("matplotlib")

For some reason, the following cell causes trouble in the CI pipeline, although when executing it in a notebook there is no problem (problem started end of august). We are going to comment it and maybe address this in the future:

[45]:
"""
hv.save(
    hv_imt00.opts(
        colorbar=True, title="step300", fig_size=500, cmap='seismic', clim=(-10**-3, 10**-3), xticks="auto", yticks="auto",
    ),
    fmt="svg", backend="matplotlib", filename="test6", fps=1
)
"""
[45]:
'\nhv.save(\n    hv_imt00.opts(\n        colorbar=True, title="step300", fig_size=500, cmap=\'seismic\', clim=(-10**-3, 10**-3), xticks="auto", yticks="auto",\n    ),\n    fmt="svg", backend="matplotlib", filename="test6", fps=1\n)\n'
[46]:
hv.help(hv_imt00.opts())
Image: Image

Online example: https://holoviews.org/reference/elements/matplotlib/Image.html

---------------------
Target Specifications
---------------------

Targets in this object available for customization:

Element: Image.Image

To see the options info for one of these target specifications,
which are of the form {type}[.{group}[.{label}]], do holoviews.help({type}).

-------------
Style Options
-------------

        alpha, clims, cmap, filterrad, interpolation, norm, visible

(Consult matplotlib's documentation for more information.)

------------
Plot Options
------------

The plot options are the parameters of the plotting class:

Parameters of 'RasterPlot'
==========================

Parameters changed from their default values are marked in red.
Soft bound values are marked in cyan.
C/V= Constant/Variable, RO/RW = ReadOnly/ReadWrite, AN=Allow None

Name                                   Value                    Type         Bounds     Mode  

fontsize                                None                 Parameter                V RW AN
fontscale                               None                   Number                 V RW AN
show_title                              True                  Boolean                   V RW
title                     '{label} {group}\n{dimensions}'      String                   V RW
normalize                               True                  Boolean                   V RW
projection                              None                 Parameter                V RW AN
backend_opts                             {}                     Dict                    V RW
fig_alpha                               1.0                    Number        (0, 1)     V RW
fig_bounds                    (0.15, 0.15, 0.85, 0.85)      NumericTuple                V RW
fig_inches                               4                   Parameter                  V RW
fig_latex                              False                  Boolean                   V RW
fig_rcparams                             {}                     Dict                    V RW
fig_size                               100.0                   Number      (1, None)    V RW
initial_hooks                            []                   HookList     (0, None)    V RW
sublabel_format                         None                   String                 V RW AN
sublabel_position                  (-0.35, 0.85)            NumericTuple                V RW
sublabel_size                            18                    Number                   V RW
show_frame                             False                  Boolean                   V RW
apply_ranges                            True                  Boolean                   V RW
apply_extents                           True                  Boolean                   V RW
bgcolor                                 None               ClassSelector              V RW AN
default_span                            2.0                ClassSelector                V RW
hooks                                    []                   HookList     (0, None)    V RW
invert_axes                            False                  Boolean                   V RW
invert_xaxis                           False                  Boolean                   V RW
invert_yaxis                           False                  Boolean                   V RW
logx                                   False                  Boolean                   V RW
logy                                   False                  Boolean                   V RW
padding                                  0                 ClassSelector                V RW
show_legend                            False                  Boolean                   V RW
show_grid                              False                  Boolean                   V RW
xaxis                                 'bottom'             ObjectSelector               V RW
yaxis                                  'left'              ObjectSelector               V RW
xlabel                                  None                   String                 V RW AN
ylabel                                  None                   String                 V RW AN
xlim                                 (nan, nan)                Tuple                    V RW
ylim                                 (nan, nan)                Tuple                    V RW
zlim                                 (nan, nan)                Tuple                    V RW
xrotation                               None                  Integer       (0, 360)  V RW AN
yrotation                               None                  Integer       (0, 360)  V RW AN
xticks                                  None               ClassSelector              V RW AN
yticks                                  None               ClassSelector              V RW AN
apply_ticks                             True                  Boolean                   V RW
aspect                                'equal'                Parameter                  V RW
data_aspect                             None                   Number                 V RW AN
invert_zaxis                           False                  Boolean                   V RW
labelled                             ['x', 'y']                 List       (0, None)    V RW
logz                                   False                  Boolean                   V RW
xformatter                              None               ClassSelector              V RW AN
yformatter                              None               ClassSelector              V RW AN
zformatter                              None               ClassSelector              V RW AN
zaxis                                   True                  Boolean                   V RW
zlabel                                  None                   String                 V RW AN
zrotation                                0                    Integer       (0, 360)    V RW
zticks                                  None                 Parameter                V RW AN
clabel                                  None                   String                 V RW AN
clim                                 (nan, nan)                Tuple                    V RW
clim_percentile                        False               ClassSelector                V RW
cformatter                              None               ClassSelector              V RW AN
colorbar                               False                  Boolean                   V RW
colorbar_opts                            {}                     Dict                    V RW
color_levels                            None               ClassSelector              V RW AN
cnorm                                 'linear'             ObjectSelector               V RW
clipping_colors                {'NaN': 'transparent'}           Dict                    V RW
cbar_padding                            0.01                   Number                   V RW
cbar_ticks                              None                 Parameter                V RW AN
cbar_width                              0.05                   Number                   V RW
cbar_extend                             None               ObjectSelector               V RW
rescale_discrete_levels                 True                  Boolean                   V RW
symmetric                              False                  Boolean                   V RW
nodata                                  None                  Integer                 V RW AN
situate_axes                            True                  Boolean                   V RW

Parameter docstrings:
=====================

fontsize:                Specifies various font sizes of the displayed text.
                         
                         Finer control is available by supplying a dictionary where any
                         unmentioned keys revert to the default sizes, e.g:
                         
                            {'ticks':20, 'title':15,
                             'ylabel':5, 'xlabel':5, 'zlabel':5,
                             'legend':8, 'legend_title':13}
                         
                         You can set the font size of 'zlabel', 'ylabel' and 'xlabel'
                         together using the 'labels' key.
fontscale:               Scales the size of all fonts.
show_title:              Whether to display the plot title.
title:                   The formatting string for the title of this plot, allows defining
                         a label group separator and dimension labels.
normalize:               Whether to compute ranges across all Elements at this level
                         of plotting. Allows selecting normalization at different levels
                         for nested data containers.
projection:              The projection of the plot axis, default of None is equivalent to
                         2D plot, '3d' and 'polar' are also supported by matplotlib by default.
                         May also supply a custom projection that is either a matplotlib
                         projection type or implements the `_as_mpl_axes` method.
backend_opts:            A dictionary of custom options to apply to the plot or
                         subcomponents of the plot. The keys in the dictionary mirror
                         attribute access on the underlying models stored in the plot's
                         handles, e.g. {'colorbar.margin': 10} will index the colorbar
                         in the Plot.handles and then set the margin to 10.
fig_alpha:               Alpha of the overall figure background.
fig_bounds:              The bounds of the overall figure as a 4-tuple of the form
                         (left, bottom, right, top), defining the size of the border
                         around the subplots.
fig_inches:              The overall matplotlib figure size in inches.  May be set as
                         an integer in which case it will be used to autocompute a
                         size. Alternatively may be set with an explicit tuple or list,
                         in which case it will be applied directly after being scaled
                         by fig_size. If either the width or height is set to None,
                         it will be computed automatically.
fig_latex:               Whether to use LaTeX text in the overall figure.
fig_rcparams:            matplotlib rc parameters to apply to the overall figure.
fig_size:                Size relative to the supplied overall fig_inches in percent.
initial_hooks:           Optional list of hooks called before plotting the data onto
                         the axis (now marked for deprecation). The hook is passed the
                         plot object and the displayed object; other plotting handles
                         can be accessed via plot.handles.
sublabel_format:         Allows labeling the subaxes in each plot with various formatters
                         including {Alpha}, {alpha}, {numeric} and {roman}.
sublabel_position:       Position relative to the plot for placing the optional subfigure label.
sublabel_size:           Size of optional subfigure label.
show_frame:              Whether or not to show a complete frame around the plot.
apply_ranges:            Whether to compute the plot bounds from the data itself.
apply_extents:           Whether to apply extent overrides on the Elements
bgcolor:                 If set bgcolor overrides the background color of the axis.
default_span:            Defines the span of an axis if the axis range is zero, i.e. if
                         the lower and upper end of an axis are equal or no range is
                         defined at all. For example if there is a single datapoint at
                         0 a default_span of 2.0 will result in axis ranges spanning
                         from -1 to 1.
hooks:                   Optional list of hooks called when finalizing a plot. The
                         hook is passed the plot object and the displayed element, and
                         other plotting handles can be accessed via plot.handles.
invert_axes:             Whether to invert the x- and y-axis
invert_xaxis:            Whether to invert the plot x-axis.
invert_yaxis:            Whether to invert the plot y-axis.
logx:                    Whether the x-axis of the plot will be a log axis.
logy:                    Whether the y-axis of the plot will be a log axis.
padding:                 Fraction by which to increase auto-ranged extents to make
                         datapoints more visible around borders.
                         
                         To compute padding, the axis whose screen size is largest is
                         chosen, and the range of that axis is increased by the
                         specified fraction along each axis.  Other axes are then
                         padded ensuring that the amount of screen space devoted to
                         padding is equal for all axes. If specified as a tuple, the
                         int or float values in the tuple will be used for padding in
                         each axis, in order (x,y or x,y,z).
                         
                         For example, for padding=0.2 on a 800x800-pixel plot, an x-axis
                         with the range [0,10] will be padded by 20% to be [-1,11], while
                         a y-axis with a range [0,1000] will be padded to be [-100,1100],
                         which should make the padding be approximately the same number of
                         pixels. But if the same plot is changed to have a height of only
                         200, the y-range will then be [-400,1400] so that the y-axis
                         padding will still match that of the x-axis.
                         
                         It is also possible to declare non-equal padding value for the
                         lower and upper bound of an axis by supplying nested tuples,
                         e.g. padding=(0.1, (0, 0.1)) will pad the x-axis lower and
                         upper bound as well as the y-axis upper bound by a fraction of
                         0.1 while the y-axis lower bound is not padded at all.
show_legend:             Whether to show legend for the plot.
show_grid:               Whether to show a Cartesian grid on the plot.
xaxis:                   Whether and where to display the xaxis.
                         The "bare" options allow suppressing all axis labels, including ticks and xlabel.
                         Valid options are 'top', 'bottom', 'bare', 'top-bare' and 'bottom-bare'.
yaxis:                   Whether and where to display the yaxis.
                         The "bare" options allow suppressing all axis labels, including ticks and ylabel.
                         Valid options are 'left', 'right', 'bare', 'left-bare' and 'right-bare'.
xlabel:                  An explicit override of the x-axis label, if set takes precedence
                         over the dimension label.
ylabel:                  An explicit override of the y-axis label, if set takes precedence
                         over the dimension label.
xlim:                    User-specified x-axis range limits for the plot, as a tuple (low,high).
                         If specified, takes precedence over data and dimension ranges.
ylim:                    User-specified y-axis range limits for the plot, as a tuple (low,high).
                         If specified, takes precedence over data and dimension ranges.
zlim:                    User-specified z-axis range limits for the plot, as a tuple (low,high).
                         If specified, takes precedence over data and dimension ranges.
xrotation:               Rotation angle of the xticks.
yrotation:               Rotation angle of the yticks.
xticks:                  Ticks along x-axis specified as an integer, explicit list of
                         tick locations. If set to None default ticking behavior is applied.
yticks:                  Ticks along y-axis specified as an integer, explicit list of
                         tick locations. If set to None default ticking behavior is applied.
apply_ticks:             Whether to apply custom ticks.
aspect:                  Raster elements respect the aspect ratio of the
                         Images by default but may be set to an explicit
                         aspect ratio or to 'square'.
data_aspect:             Defines the aspect of the axis scaling, i.e. the ratio of
                         y-unit to x-unit.
invert_zaxis:            Whether to invert the plot z-axis.
labelled:                Whether to plot the 'x' and 'y' labels.
logz:                    Whether to apply log scaling to the y-axis of the Chart.
xformatter:              Formatter for ticks along the x-axis.
yformatter:              Formatter for ticks along the y-axis.
zformatter:              Formatter for ticks along the z-axis.
zaxis:                   Whether to display the z-axis.
zlabel:                  An explicit override of the z-axis label, if set takes precedence
                         over the dimension label.
zrotation:               Rotation angle of the zticks.
zticks:                  Ticks along z-axis specified as an integer, explicit list of
                         tick locations, list of tuples containing the locations and
                         labels or a matplotlib tick locator object. If set to None
                         default matplotlib ticking behavior is applied.
clabel:                  An explicit override of the color bar label, if set takes precedence
                         over the title key in colorbar_opts.
clim:                    User-specified colorbar axis range limits for the plot, as a
                         tuple (low,high). If specified, takes precedence over data
                         and dimension ranges.
clim_percentile:         Percentile value to compute colorscale robust to outliers. If
                         True, uses 2nd and 98th percentile; otherwise uses the specified
                         numerical percentile value.
cformatter:              Formatter for ticks along the colorbar axis.
colorbar:                Whether to draw a colorbar.
colorbar_opts:           Allows setting specific styling options for the colorbar.
color_levels:            Number of discrete colors to use when colormapping or a set of color
                         intervals defining the range of values to map each color to.
cnorm:                   Color normalization to be applied during colormapping.
clipping_colors:         Dictionary to specify colors for clipped values, allows
                         setting color for NaN values and for values above and below
                         the min and max value. The min, max or NaN color may specify
                         an RGB(A) color as a color hex string of the form #FFFFFF or
                         #FFFFFFFF or a length 3 or length 4 tuple specifying values in
                         the range 0-1 or a named HTML color.
cbar_padding:            Padding between colorbar and other plots.
cbar_ticks:              Ticks along colorbar-axis specified as an integer, explicit
                         list of tick locations, list of tuples containing the
                         locations and labels or a matplotlib tick locator object. If
                         set to None default matplotlib ticking behavior is
                         applied.
cbar_width:              Width of the colorbar as a fraction of the main plot
cbar_extend:             If not 'neither', make pointed end(s) for out-of- range values.
rescale_discrete_levels: If ``cnorm='eq_hist`` and there are only a few discrete values,
                         then ``rescale_discrete_levels=True`` decreases the lower
                         limit of the autoranged span so that the values are rendering
                         towards the (more visible) top of the palette, thus
                         avoiding washout of the lower values.  Has no effect if
                         ``cnorm!=`eq_hist``. Set this value to False if you need to
                         match historical unscaled behavior, prior to HoloViews 1.14.4.
symmetric:               Whether to make the colormap symmetric around zero.
nodata:                  Optional missing-data value for integer data.
                         If non-None, data with this value will be replaced with NaN so
                         that it is transparent (by default) when plotted.
situate_axes:            Whether to situate the image relative to other plots. 
[47]:
hv.extension("bokeh", "matplotlib")  # go back to bokeh as default

Vectorfields#

Colormap plot#

[48]:
xav = run.Maxwell.td.b_field(source="z=0")  # XArray Vector
[49]:
hv_dst = hv.Dataset(xav)
hv_imt = hv_dst.to(hv.Image, ["x", "y"])  # needed for generating outputs
hv_imt_dynamic = hv_dst.to(hv.Image, ["x", "y"], dynamic=True)
hv.output(max_frames=3000)
hv_imt_dynamic
[49]:
[50]:
# Note for web users: You should have an active notebook session to interact with the plot
hv_imt_dynamic.opts(
    colorbar=True,
    width=500,
    height=400,
    cmap="seismic",
    clabel=f"{xav.vx.name} ({xav.units})",
    clim=(-(10**-3), 10**-3),
)
[50]:

Arrow plot#

[51]:
import numpy as np
import xarray as xr
[52]:
ds = xr.Dataset(
    {
        "Angle": np.arctan2(
            xav.sel(x=slice(-10, 10), y=slice(-10, 10)).vy,
            xav.sel(x=slice(-10, 10), y=slice(-10, 10)).vx,
        ),
        "Magnitude": np.sqrt(
            xav.sel(x=slice(-10, 10), y=slice(-10, 10)).vx ** 2
            + xav.sel(x=slice(-10, 10), y=slice(-10, 10)).vy ** 2
        ),
    }
)  # polar coordinates required for plotting arrows
[53]:
xav.vx.attrs
[53]:
{'units': 'au'}
[54]:
def _vectorplot(val):
    plot = hv.VectorField(
        data=ds.sel(t=val, method="nearest"),
        kdims=["x", "y"],
        vdims=["Angle", "Magnitude"],
    )
    for dim in plot.kdims:  # keep the units
        dim.unit = xav[f"v{dim}"].units
    return plot
[55]:
# Note for web users: You should have an active notebook session to interact with the plot
arrow_plot = (
    hv.DynamicMap(_vectorplot, kdims="t")
    .redim.values(t=xav.t.values)
    .opts(color="blue", width=500, height=400)
)  # Zoom-in for seeing the  arrows!
arrow_plot.kdims[0].unit = xav.t.units
arrow_plot
[55]:

Advanced#

Advanced plotting#

If the holoviews default plotting does not offer enough customization to your needs, one has two possibilites: - Use the holoviews hooks. These hooks will offer the underlying plot object from the backend to you. In the case of matplotlib e.g. in the function that you hook to, you could use fig = plot.handles["fig"] and ax = plot.handles["axis"]. These fig and axes objects are the ones that you probably know from matplotlib already. You can customize them as you are used to within the function. An example for this can be found in the case_study_all_final example, which is referenced under Application examples. We recommend exploiting this possibily first, before going to the second one, for still being able of having interactive plots. - Render the holoviews object into a backend object, e.g. matplotlib, to directly use the classic matplotlib functionalities: fig = hv.render(hv_object, backend="matplotlib"), where ax = fig.axes. An example for this can be found in the case_study_all_in_one_exploration example, which is referenced under Application examples.

Using conda-forge packages on RAVEN.#

As said above, for generating bokeh gifs and matplotlib mp4s, one needs geckodriver and ffmpeg, respectively. The latter two are not Python packages. Therefore, one cannot install them via pip, so they are not installed by default along with postopus. Nonetheless, they can be installed via conda-forge. The problem is that the standard MPCDF-RVS-Service for visualizing notebooks doesn’t allow to use custom anaconda environments. But there is a rather simple way to overcome this: 1) Login to your raven-account via ssh in a terminal
2) create a conda yaml suited to your needs e.g.:
name: holoviews-env
channels:
    - conda-forge
    - defaults
dependencies:
    - python=3.9
    - matplotlib
    - numpy=1.21.5
    - netCDF4
    - pandas
    - prettytable
    - pyvista
    - xarray
    - psutil
    - holoviews
    - datashader
    - selenium
    - pytest
    - Jinja2==3.0.1
    - pip
    - ipykernel
    - geckodriver
    - ffmpeg
    - pip:
       - ase @ git+https://gitlab.com/dremerb/ase.git@xsf_and_cube_merge
       - postopus @ git+https://gitlab.com/octopus-code/postopus.git
  1. Create a conda environment with the requirements of the yaml: conda env create -p /u/user_name/name_of_conda_environment --file=path_to_conf_yml.yml

  2. conda deactivate and conda activate path_to_conda_env

  3. conda install -c conda-forge jupyterlab

  4. jupyter-lab

  5. After a few seconds, a new browser window will pop-up with a jupyter notebook hosted on raven that has all the packages that you need.

Final notes#

This tutorial is intended to be an introduction to holoviews. We didn’t cover everything here. For example, there is another notebook under /dev that handles reduction methods in order to plot very dense data. If you need something more sophisticated for large datasets we could think about exploring the dask functionalities, which are also supported by holoviews. Just contact us if you have any questions!