Plotting
LivePlot
Bluesky has good integration with matplotlib for data visualization, and data from scans
may be easily plotted using the LivePlot callback.
ibex_bluesky_core provides a thin wrapper over bluesky’s default
LivePlot callback,
which ensures that plots are promptly displayed in IBEX.
In order to use the wrapper, import LivePlot
from ibex_bluesky_core.callbacks rather than importing
bluesky.callbacks.mpl_plotting.LivePlot directly:
from ibex_bluesky_core.callbacks.plotting import LivePlot
Configuration
A range of configuration options for LivePlot are available - see the
bluesky LivePlot documentation
for more details about available options.
The LivePlot object allows an arbitrary set of matplotlib Axes to be passed in, onto
which it will plot. This can be used to configure properties which are not directly exposed
on the LivePlot object, for example log-scaled axes.
See the matplotlib Axes documentation
for a full range of options on how to configure an Axes object.
Below is a full example showing how to use standard matplotlib & bluesky functionality
to plot a scan with a logarithmically-scaled y-axis:
import matplotlib.pyplot as plt
from ibex_bluesky_core.callbacks import LivePlot
from ibex_bluesky_core.plan_stubs import call_qt_aware
def plan():
# Create a new figure to plot onto.
yield from call_qt_aware(plt.figure)
# Make a new set of axes on that figure
ax = yield from call_qt_aware(plt.gca)
# Set the y-scale to logarithmic
yield from call_qt_aware(ax.set_yscale, "log")
# Use the above axes in a LivePlot callback
plot_callback = LivePlot(y="y_variable", x="x_variable", ax=ax, yerr="yerr_variable")
# yerr is the uncertanties of each y value, producing error bars
Note
See docs for call_qt_aware for a description of why we need to use
yield from call_qt_aware rather than calling matplotlib functions directly.
By providing a signal name to the yerr argument you can pass uncertainties to LivePlot, by not providing anything for this argument means that no errorbars will be drawn. Errorbars are drawn after each point collected, displaying their standard deviation- uncertainty data is collected from Bluesky event documents and errorbars are updated after every new point added.
The plot_callback object can then be subscribed to the run engine, using either:
An explicit callback when calling the run engine:
RE(some_plan(), plot_callback)Be subscribed in a plan using
subs_decoratorfrom bluesky (recommended)Globally attached to the run engine using
RE.subscribeNot recommended, not all scans will use the same variables and a plot setup that works for one scan is unlikely to be optimal for a different type of scan.
By subsequently re-using the same ax object in later scans, rather than creating a new
ax object for each scan, two scans can be “overplotted” with each other for comparison.
LivePColorMesh
LivePColorMesh is a specialized heatmap
plotting callback which reacts to rows of data at a time. This is suitable for use by DAE reducers
which emit rows of data at a time, such as
ibex_bluesky_core.devices.simpledae.PeriodSpecIntegralsReducer or
ibex_bluesky_core.devices.simpledae.DSpacingMappingReducer.
This callback updates live as the scan progresses. It is otherwise very similar to the existing bluesky plotting callbacks.
Note
Due to an implementation detail of matplotlib.pyplot.pcolormesh,
the plot will only appear once at least two rows of data have been collected.
Saving plots to PNG files
ibex_bluesky_core provides a PlotPNGSaver callback to save plots on a run stop to PNG files, which by saves them to the default output file location unless a filepath is explicitly given.
This is enabled by default in the ISISCallbacks callbacks collection.
Using the above example (i.e. without the ISISCallbacks helper) it can be used like so:
from pathlib import Path
import matplotlib.pyplot as plt
from ibex_bluesky_core.callbacks import LivePlot, PlotPNGSaver
from ibex_bluesky_core.plan_stubs import call_qt_aware
def plan():
# Create a new figure to plot onto.
yield from call_qt_aware(plt.figure)
# Make a new set of axes on that figure
ax = yield from call_qt_aware(plt.gca)
# Set the y-scale to logarithmic
yield from call_qt_aware(ax.set_yscale, "log")
# Use the above axes in a LivePlot callback
plot_callback = LivePlot(y="y_variable", x="x_variable", ax=ax, yerr="yerr_variable")
# Add a PNG saving callback
png_callback = PlotPNGSaver(y="y_variable", x="x_variable", ax=ax, output_dir=Path("C://", "Some", "Custom", "Directory"), postfix="test123")