diff --git a/doc/callbacks/plotting.md b/doc/callbacks/plotting.md index a9cd7384..9e486c2a 100644 --- a/doc/callbacks/plotting.md +++ b/doc/callbacks/plotting.md @@ -110,3 +110,7 @@ def plan(): # 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") ``` + +## Replotting a previous scan + +See {doc}`/replotting_scans` for information about how to replay bluesky documents into an arbitrary set of callbacks, which can be used to replot a previous scan. diff --git a/doc/conf.py b/doc/conf.py index 37111224..cfeadb01 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -92,4 +92,5 @@ "matplotlib": ("https://matplotlib.org", None), "lmfit": ("https://lmfit.github.io/lmfit-py/", None), "typing_extensions": ("https://typing-extensions.readthedocs.io/en/latest/", None), + "ibex_user_manual": ("https://isiscomputinggroup.github.io/ibex_user_manual/", None), } diff --git a/doc/index.md b/doc/index.md index d3201bb6..32eae797 100644 --- a/doc/index.md +++ b/doc/index.md @@ -71,6 +71,7 @@ plan_stubs/* :caption: Reference _api +replotting_scans architectural_decisions developer_information ``` diff --git a/doc/replotting_scans.md b/doc/replotting_scans.md new file mode 100644 index 00000000..2856d364 --- /dev/null +++ b/doc/replotting_scans.md @@ -0,0 +1,56 @@ +# Replaying documents + +All {external+bluesky:doc}`callbacks ` in bluesky operate on a data structure called a {external+bluesky:doc}`document `. These documents represent the metadata and data emitted by a bluesky scan. + +```{tip} +The schema of these documents is defined formally by the bluesky {external+event_model:doc}`event model `. However, the implementation details of these documents are not important for simply replaying them. + +If instead you wish to write a tool to consume bluesky documents _without_ using the callbacks provided in this library or in bluesky, please {external+ibex_user_manual:ref}`get in touch with experiment controls ` for advice. +``` + +All bluesky {external+bluesky:doc}`callbacks ` consume {external+bluesky:doc}`documents ` as their source of data or metadata. This means that historic documents can be replayed into one or more callbacks. This facilitates: +- Replaying documents into plotting callbacks to 'replot' a scan +- Replaying documents into (potentially different) fitting callbacks, to experiment with different fitting routines +- Developing entirely new callbacks using data from historic scans + +## Document storage and replay from file + +The IBEX {py:obj}`RunEngine ` instance is configured to automatically save the raw documents from any scan - these are saved into +``` +c:\Instrument\var\logs\bluesky\raw_documents +``` +They are subsequently moved to a backups area after 10 days; if you need access to old scans, please contact experiment controls for the exact path you will need. + +These files are organised as line-delimited JSON dictionaries. The filename is the unique identifier of the scan. + +Documents can be loaded from their save files and replayed into arbitrary callbacks - which can be a completely different set of callbacks than were used during the scan. The following example shows replay into a {py:obj}`LivePlot ` callback to regenerate a matplotlib plot, as well as re-running a Gaussian fit using the {py:obj}`LiveFit ` callback and displaying that on the plot using {external+bluesky:py:obj}`LiveFitPlot `. + +```python +import json +import matplotlib.pyplot as plt +from ibex_bluesky_core.callbacks import LivePlot, LiveFit +from ibex_bluesky_core.fitting import Gaussian +from bluesky.callbacks import LiveFitPlot + + +def replot_scan(path_to_save_file: str, y_name: str, x_name: str): + # Prepare a set of matplotlib axes to plot onto + plt.close("all") + plt.show() + _, ax = plt.subplots() + + # Example callbacks for plotting and fitting - the exact callbacks + # to use when replotting a scan can be chosen freely. + live_plot = LivePlot(y_name, x_name, marker="x", linestyle="none", ax=ax) + live_fit = LiveFit(Gaussian.fit(), y=y_name, x=x_name) + live_fit_plot = LiveFitPlot(livefit=live_fit, ax=ax, num_points=10000) + + # Open the relevant save-file + with open(path_to_save_file) as f: + for line in f: + # Load the JSON representation of this document. + document = json.loads(line) + # Pass the document to arbitrary callbacks. + live_plot(document["type"], document["document"]) + live_fit_plot(document["type"], document["document"]) +```