{ "cells": [ { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "# Area Detector, Single mode, HDF5 file" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "**Objective**\n", "\n", "The [EPICS area detector](https://areadetector.github.io/master/index.html) software has a `Single` mode that reduces the configuration steps needed to acquire image frame(s) and output file(s). (EPICS Area Detector will write each frame to a separate file.)\n", "\n", "Here, we show how to configure the EPICS controls, then acquire an image with [bluesky](https://blueskyproject.io/) and write it to an [HDF5](https://www.hdfgroup.org/solutions/hdf5) file.\n", "\n", "**Contents**\n", "\n", "- [EPICS Area Detector IOC](#EPICS-Area-Detector-IOC) is pre-built\n", "- [File Directories](#File-Directories) are different on IOC and bluesky workstation\n", "- [ophyd](#ophyd) to describe the hardware\n", "- [bluesky](#bluesky) for the measurement\n", "- [databroker](#databroker) to view the image\n", "- [punx](#punx) (not part of Bluesky) to look at the HDF5 file\n", "- [Recapitulation](#Recapitulation) - rendition with no explanations\n", "\n", "In other examples (such as AD with [default file names](./de_0_adsim_hdf5_basic.ipynb) or [custom file names](./de_1_adsim_hdf5_custom_names.ipynb)), we described the details of the area detector support. Refer to those examples for the details. Here, following the same general outline, we leverage that knowledge and proceed to the specifics for this example." ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## EPICS Area Detector IOC\n", "\n", "This example uses a prebuilt [ADSimDetector](https://areadetector.github.io/master/ADSimDetector/simDetector.html) driver, packaged in a [docker](https://www.docker.com/) image\n", "([prjemian/synapps](https://hub.docker.com/r/prjemian/synapps/tags)). The [EPICS IOC](https://docs.epics-controls.org/projects/how-tos/en/latest/getting-started/creating-ioc.html) is configured with prefix `ad:` using the [bash shell script](https://raw.githubusercontent.com/prjemian/epics-docker/main/resources/iocmgr.sh):\n", "\n", "
\n",
    "user@workstation:~$ iocmgr.sh start ADSIM ad\n",
    "
" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "IOC = \"ad:\"" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## File Directories\n", "\n", "Files from the IOC are mounted on the docker host in the directory `/tmp/docker_ioc/iocad`. The bluesky session runs on the docker host.\n", "\n", "system | file directory\n", "--- | ---\n", "area detector IOC | `/tmp`\n", "bluesky | `/tmp/docker_ioc/iocad/tmp`" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "import pathlib\n", "\n", "# These paths are specific to how this IOC is implemented.\n", "AD_IOC_MOUNT_PATH = pathlib.Path(\"/tmp\")\n", "BLUESKY_MOUNT_PATH = pathlib.Path(\"/tmp/docker_ioc/iocad/tmp\")" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "Next, define some structures that we will use when constructing the detector object. The `IMAGE_DIR` describes our default choice for where to store image files. We can change this at run time by changing thearea detector file writer's `FilePath` PV (before starting image acquisition)." ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "IMAGE_DIR = \"example/%Y/%m/%d\" # our choice for file arrangement\n", "\n", "# MUST end with a `/`, pathlib will NOT provide it\n", "WRITE_PATH_TEMPLATE = f\"{AD_IOC_MOUNT_PATH / IMAGE_DIR}/\"\n", "READ_PATH_TEMPLATE = f\"{BLUESKY_MOUNT_PATH / IMAGE_DIR}/\"" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## ophyd\n", "\n", "Here's a screen view of the configuration we want (`HDF` plugin on the left,\n", "`cam` plugin on the right):\n", "\n", "![Area Detector configuration for writing HDF5 files in Single mode](ad_hdf5_single_mode.png)" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "### Preparation\n", "\n", "Configure how [matplotlib](https://matplotlib.org/) charts will be displayed in the notebook.\n", "\n", "We'll import additional libraries as needed by each of the following steps." ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# matplotlib graphics, choices include: inline, notebook, auto\n", "%matplotlib inline\n", "\n", "import matplotlib.pyplot as plt\n", "\n", "plt.ion() # turn on matplotlib plots" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "### cam\n", "\n", "The `cam` Device describes the EPICS area detector camera driver for this detector. Here we use the `SimDetectorCam` (ADSimDetector) but you could use just about any other supported Cam instead." ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "from apstools.devices import CamMixin_V34\n", "from ophyd.areadetector import SimDetectorCam\n", "\n", "class TheDetectorCam(CamMixin_V34, SimDetectorCam):\n", " \"\"\"Revise SimDetectorCam for ADCore revisions.\"\"\"\n", "\n", " def __init__(self, *args, **kwargs):\n", " super().__init__(*args, **kwargs)\n", " self.stage_sigs.update(\n", " dict(\n", " acquire_time=0.01,\n", " acquire_period=0.015, # a wee bit longer than acquire_time\n", " num_images=1,\n", " num_exposures=1, # Exp./image\n", " wait_for_plugins=\"Yes\",\n", " array_callbacks=\"Enable\",\n", " )\n", " )" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "### HDF5\n", "\n", "The `hdf1` Device describes the HDF5 File Writing plugin for this detector. With `FileStoreHDF5SingleIterativeWrite`, you will get a single HDF5 file for each frame you acquire (_n_ frames, _n_ files)." ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "from ophyd.areadetector.filestore_mixins import FileStoreHDF5SingleIterativeWrite\n", "from ophyd.areadetector.plugins import HDF5Plugin_V34 as HDF5Plugin\n", "import warnings\n", "\n", "\n", "class CustomHDF5Plugin(FileStoreHDF5SingleIterativeWrite, HDF5Plugin):\n", " \"\"\"\n", " Add data acquisition methods to HDF5Plugin.\n", "\n", " * ``stage()`` - prepare device PVs before data acquisition\n", " * ``unstage()`` - restore device PVs after data acquisition\n", " * ``generate_datum()`` - coordinate image storage metadata\n", " \"\"\"\n", "\n", " def __init__(self, *args, **kwargs):\n", " super().__init__(*args, **kwargs)\n", " self.stage_sigs.update(\n", " dict(\n", " array_callbacks=\"Disable\",\n", " auto_increment=\"Yes\",\n", " auto_save=\"Yes\",\n", " blocking_callbacks=\"No\",\n", " compression=\"zlib\",\n", " lazy_open=\"Yes\",\n", " store_perform=\"No\",\n", " zlevel=6,\n", " )\n", " )\n", " # capture is not used with Single mode\n", " # parent.cam.array_callbacks is staged once in the cam\n", " # create_directory must be set before file_path, which is set before staging\n", " remove_these = \"\"\"\n", " capture\n", " array_counter\n", " parent.cam.array_callbacks\n", " create_directory\n", " \"\"\".split()\n", " for k in remove_these:\n", " if k in self.stage_sigs:\n", " self.stage_sigs.pop(k)\n", "\n", " def stage(self):\n", " # Again, do not press the Capture button in the HDF plugin\n", " if \"capture\" in self.stage_sigs:\n", " warnings.warn(\"Do not use capture with file_write_mode='Single'\")\n", " self.stage_sigs.pop(\"capture\")\n", " super().stage()" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "### detector\n", "\n", "The detector class, a subclass of `DetectorBase`, brings together the detector driver `cam` and plugins." ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "from apstools.devices import SingleTrigger_V34\n", "from ophyd import ADComponent\n", "from ophyd.areadetector import DetectorBase\n", "from ophyd.areadetector.plugins import ImagePlugin_V34 as ImagePlugin\n", "\n", "class CustomDetector(SingleTrigger_V34, DetectorBase):\n", " \"\"\"\n", " ADSimDetector\n", "\n", " SingleTrigger:\n", "\n", " * stop any current acquisition\n", " * sets image_mode to 'Multiple'\n", " \"\"\"\n", "\n", " cam = ADComponent(TheDetectorCam, \"cam1:\")\n", " hdf1 = ADComponent(\n", " CustomHDF5Plugin,\n", " \"HDF1:\",\n", " write_path_template=WRITE_PATH_TEMPLATE,\n", " read_path_template=READ_PATH_TEMPLATE,\n", " )\n", " image = ADComponent(ImagePlugin, \"image1:\")" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "With all the above setup, create the Python detector object, `adsimdet` and wait for it to connect with EPICS." ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [], "source": [ "adsimdet = CustomDetector(IOC, name=\"adsimdet\")\n", "adsimdet.wait_for_connection(timeout=15)" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "Check that all plugins used by the IOC have been defined in the Python structure. Expect that this function returns an empty list: `[]`." ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[]" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "adsimdet.missing_plugins()" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "We must configure `adsimdet` so the HDF5 plugin (by its attribute name `hdf1`) will be called during `adsimdet.read()`, as used by data acquisition." ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [], "source": [ "adsimdet.read_attrs.append(\"hdf1\")" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "Configure the HDF5 plugin so it will create up to 5 subdirectories for the image directory." ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [], "source": [ "adsimdet.hdf1.create_directory.put(-5)" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "*Prime* the HDF5 plugin, if necessary." ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [], "source": [ "from apstools.devices import ensure_AD_plugin_primed\n", "\n", "# this step is needed for ophyd\n", "ensure_AD_plugin_primed(adsimdet.hdf1, True)" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "Check how `adsim` is [staged](https://blueskyproject.io/ophyd/user_v1/explanations/staging.html) (configured for data acquisition)." ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "OrderedDict([('cam.acquire', 0), ('cam.image_mode', 1)])" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "adsimdet.stage_sigs" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "OrderedDict([('acquire_time', 0.01),\n", " ('acquire_period', 0.015),\n", " ('num_images', 1),\n", " ('num_exposures', 1),\n", " ('wait_for_plugins', 'Yes'),\n", " ('array_callbacks', 'Enable')])" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# adsimdet.cam.stage_sigs[\"num_images\"] = 1 # default is 1\n", "adsimdet.cam.stage_sigs" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "OrderedDict([('enable', 1),\n", " ('blocking_callbacks', 'No'),\n", " ('auto_increment', 'Yes'),\n", " ('auto_save', 'Yes'),\n", " ('num_capture', 0),\n", " ('file_template', '%s%s_%6.6d.h5'),\n", " ('file_write_mode', 'Single'),\n", " ('array_callbacks', 'Disable'),\n", " ('compression', 'zlib'),\n", " ('lazy_open', 'Yes'),\n", " ('store_perform', 'No'),\n", " ('zlevel', 6)])" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "adsimdet.hdf1.stage_sigs" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## bluesky\n", "\n", "Within the [Bluesky framework](https://blueskyproject.io/), [bluesky](https://blueskyproject.io/bluesky) is the package that orchestrates the data acquisition steps, including where to direct acquired data for storage. [Later](#databroker), we'll use [databroker](https://blueskyproject.io/databroker) to access the image data.\n", "\n", "First, setup the RunEngine object `RE`." ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [], "source": [ "import bluesky\n", "\n", "RE = bluesky.RunEngine()" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "### databroker (as RunEngine subscriber)\n", "\n", "Setup the databroker (with a temporary databroker catalog) as a subscriber to the documents published from the bluesky `RunEngine`." ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import databroker\n", "\n", "cat = databroker.temp().v2 # or use your own catalog: databroker.catalog[\"CATALOG_NAME\"]\n", "cat = databroker.catalog[\"training\"]\n", "RE.subscribe(cat.v1.insert)" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "***Take an image with the area detector***\n", "\n", "Finally, we are ready to acquire an image. We'll use the standard bluesky [count()](https://blueskyproject.io/bluesky/generated/bluesky.plans.count.html#bluesky.plans.count) plan. Also add some metadata about this image." ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [], "source": [ "import bluesky.plans as bp\n", "\n", "uids = RE(\n", " bp.count([adsimdet],\n", " md=dict(\n", " title=\"Area Detector, Single mode, HDF5 file\",\n", " purpose=\"image\")\n", " )\n", ")" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## databroker\n", "\n", "Here we show how to access and display the acquired image using databroker as the interface to the data files and run metadata.\n", "\n", "First, find the run we just acquired. We'll index to that run using the `uid` returned by the above call to `RE()`." ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "BlueskyRun\n", " uid='6780800e-b08a-4933-bf26-8618b3fb53ed'\n", " exit_status='success'\n", " 2022-09-30 16:31:38.860 -- 2022-09-30 16:31:38.965\n", " Streams:\n", " * primary\n" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "run = cat.v2[uids[0]]\n", "run" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "***Get the image frame from the run***\n", "\n", "Combining several steps into one line, extract the image frame from the run. We know the image data is in the `primary` stream, that the image is recorded under the name `\"adsimdet_image\"` (or `adsimdet.image.name`), and the `image` frame is the last two indices.\n", "\n", "Import [hdf5plugin](https://hdf5plugin.readthedocs.io), a library that supports advanced compression modes for data in HDF5 files. (We don't have to call any of its modules directly. The `import hdf5plugin` will install entry points needed when the supported compression modes are called.) We did not need this previously since it was the IOC that wrote the HDF5 file with the chosen compression mode.\n", "\n", "NOTE: Any Python client that reads data compressed with these compression modes will also need to import the `hdf5plugin` library, or provide alternative support.\n", "\n", "NOTE: Make sure you are using at least version 0.0.10 (or higher) of the [area-detector-handlers](https://github.com/bluesky/area-detector-handlers/pull/32)\n", "for an important bugfix relating to how databroker will read these HDF5 files.\n" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "
<xarray.DataArray 'adsimdet_image' (dim_1: 1024, dim_2: 1024)>\n",
       "array([[ 9, 10, 11, ...,  6,  7,  8],\n",
       "       [10, 11, 12, ...,  7,  8,  9],\n",
       "       [11, 12, 13, ...,  8,  9, 10],\n",
       "       ...,\n",
       "       [ 6,  7,  8, ...,  3,  4,  5],\n",
       "       [ 7,  8,  9, ...,  4,  5,  6],\n",
       "       [ 8,  9, 10, ...,  5,  6,  7]], dtype=uint8)\n",
       "Coordinates:\n",
       "    time     float64 1.665e+09\n",
       "Dimensions without coordinates: dim_1, dim_2\n",
       "Attributes:\n",
       "    object:   adsimdet
" ], "text/plain": [ "\n", "array([[ 9, 10, 11, ..., 6, 7, 8],\n", " [10, 11, 12, ..., 7, 8, 9],\n", " [11, 12, 13, ..., 8, 9, 10],\n", " ...,\n", " [ 6, 7, 8, ..., 3, 4, 5],\n", " [ 7, 8, 9, ..., 4, 5, 6],\n", " [ 8, 9, 10, ..., 5, 6, 7]], dtype=uint8)\n", "Coordinates:\n", " time float64 1.665e+09\n", "Dimensions without coordinates: dim_1, dim_2\n", "Attributes:\n", " object: adsimdet" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import hdf5plugin # required for LZ4, Blosc, and other compression codecs\n", "\n", "frame = run.primary.read()[adsimdet.image.name][0][0]\n", "frame" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "### Visualize the image\n", "\n", "The `frame` is an [xarray Dataset](https://docs.xarray.dev/en/stable/generated/xarray.Dataset.html), which has a method to visualize the data as shown here:" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "frame.plot.pcolormesh()" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "### Where is the image file on disk?\n", "\n", "So far, we have not had to know the name of the file on disk with the image data. Still, we can learn about that from the run's metadata, by querying one of its internal structures." ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Resource({'path_semantics': 'posix',\n", " 'resource_kwargs': {'filename': '62e73032-4d57-4e37-b784',\n", " 'frame_per_point': 1,\n", " 'template': '%s%s_%6.6d.h5'},\n", " 'resource_path': 'tmp/docker_ioc/iocad/tmp/example/2022/09/30',\n", " 'root': '/',\n", " 'run_start': '6780800e-b08a-4933-bf26-8618b3fb53ed',\n", " 'spec': 'AD_HDF5_SINGLE',\n", " 'uid': 'e00f8f86-4dbe-46c1-959e-3d504986b7f5'})" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "rsrc = run.primary._resources[0]\n", "rsrc" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "This information has shown the path to the image file *as seen from the bluesky workstation's file system.* We can parse this structure for the file name. In this case, the file is found since the `resource_path` is written relative to the `READ_PATH_TEMPLATE` defined [above](#File-Directories). It takes a bit of work to re-assemble the file name.\n", "\n", "This is an important distinction since the IOC and bluesky see the same file on different directory paths, as described [above](#File-Directories)." ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "file_name.exists()=True\n", "file_name=PosixPath('/tmp/docker_ioc/iocad/tmp/example/2022/09/30/62e73032-4d57-4e37-b784_000000.h5')\n" ] } ], "source": [ "file_name = pathlib.Path(\n", " rsrc[\"resource_kwargs\"][\"template\"] % (\n", " f\"{rsrc['root']}{rsrc['resource_path']}/\",\n", " rsrc[\"resource_kwargs\"][\"filename\"],\n", " rsrc[\"resource_kwargs\"][\"frame_per_point\"] - 1\n", " )\n", ")\n", "print(f\"{file_name.exists()=}\\n{file_name=}\")" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "Alternatively, get the name of the image file on the bluesky (local) workstation from the `adsimdet` object.\n", "\n", "NOTE: This method relies on information currently defined in EPICS, via the `adsimdet.hdf1`, so it may not be successful if the HDF plugin has been changed since the image was acquired." ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "local_file_name.exists()=True\n", "local_file_name=PosixPath('/tmp/docker_ioc/iocad/tmp/example/2022/09/30/62e73032-4d57-4e37-b784_000000.h5')\n" ] }, { "data": { "text/plain": [ "True" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from apstools.devices import AD_full_file_name_local\n", "\n", "local_file_name = AD_full_file_name_local(adsimdet.hdf1)\n", "print(f\"{local_file_name.exists()=}\\n{local_file_name=}\")\n", "local_file_name == file_name # compare the two names" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## punx\n", "\n", "Next, we demonstrate access to the HDF5 image file using the [punx](https://punx.readthedocs.io) program, showing the tree structure of the data file (the image data may be found at HDF5 address `/entry/data/data`, another reference to the exact same data is found at `/entry/instrument/detector/data`):" ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "!!! WARNING: this program is not ready for distribution.\n", "\n", "/tmp/docker_ioc/iocad/tmp/example/2022/09/30/62e73032-4d57-4e37-b784_000000.h5 : NeXus data file\n", " entry:NXentry\n", " @NX_class = \"NXentry\"\n", " data:NXdata\n", " @NX_class = \"NXdata\"\n", " data:NX_UINT8[1024,1024] = __array\n", " __array = [\n", " [9, 10, 11, '...', 8]\n", " [10, 11, 12, '...', 9]\n", " [11, 12, 13, '...', 10]\n", " ...\n", " [8, 9, 10, '...', 7]\n", " ]\n", " @NDArrayDimBinning = [1 1]\n", " @NDArrayDimOffset = [0 0]\n", " @NDArrayDimReverse = [0 0]\n", " @NDArrayNumDims = 2\n", " @signal = 1\n", " instrument:NXinstrument\n", " @NX_class = \"NXinstrument\"\n", " NDAttributes:NXcollection\n", " @NX_class = \"NXcollection\"\n", " @hostname = \"zap\"\n", " NDArrayEpicsTSSec:NX_UINT32 = 1033421498\n", " @NDAttrDescription = \"The NDArray EPICS timestamp seconds past epoch\"\n", " @NDAttrName = \"NDArrayEpicsTSSec\"\n", " @NDAttrSource = \"Driver\"\n", " @NDAttrSourceType = \"NDAttrSourceDriver\"\n", " NDArrayEpicsTSnSec:NX_UINT32 = 913238900\n", " @NDAttrDescription = \"The NDArray EPICS timestamp nanoseconds\"\n", " @NDAttrName = \"NDArrayEpicsTSnSec\"\n", " @NDAttrSource = \"Driver\"\n", " @NDAttrSourceType = \"NDAttrSourceDriver\"\n", " NDArrayTimeStamp:NX_FLOAT64 = 1033421498.9031079\n", " @NDAttrDescription = \"The timestamp of the NDArray as float64\"\n", " @NDAttrName = \"NDArrayTimeStamp\"\n", " @NDAttrSource = \"Driver\"\n", " @NDAttrSourceType = \"NDAttrSourceDriver\"\n", " NDArrayUniqueId:NX_INT32 = 1033\n", " @NDAttrDescription = \"The unique ID of the NDArray\"\n", " @NDAttrName = \"NDArrayUniqueId\"\n", " @NDAttrSource = \"Driver\"\n", " @NDAttrSourceType = \"NDAttrSourceDriver\"\n", " detector:NXdetector\n", " @NX_class = \"NXdetector\"\n", " data:NX_UINT8[1024,1024] = __array\n", " __array = [\n", " [9, 10, 11, '...', 8]\n", " [10, 11, 12, '...', 9]\n", " [11, 12, 13, '...', 10]\n", " ...\n", " [8, 9, 10, '...', 7]\n", " ]\n", " @NDArrayDimBinning = [1 1]\n", " @NDArrayDimOffset = [0 0]\n", " @NDArrayDimReverse = [0 0]\n", " @NDArrayNumDims = 2\n", " @signal = 1\n", " NDAttributes:NXcollection\n", " @NX_class = \"NXcollection\"\n", " ColorMode:NX_INT32 = 0\n", " @NDAttrDescription = \"Color mode\"\n", " @NDAttrName = \"ColorMode\"\n", " @NDAttrSource = \"Driver\"\n", " @NDAttrSourceType = \"NDAttrSourceDriver\"\n", " performance\n", "\n" ] } ], "source": [ "from apstools.utils import unix\n", "\n", "for line in unix(f\"punx tree {file_name}\"):\n", " print(line.decode().strip())" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Recapitulation\n", "\n", "Let's gather the above parts together as one would usually write code.\n", "\n", "```py\n", "# matplotlib graphics, choices include: inline, notebook, auto\n", "%matplotlib auto\n", "\n", "from apstools.devices import ensure_AD_plugin_primed\n", "from apstools.devices import CamMixin_V34\n", "from apstools.devices import SingleTrigger_V34\n", "import hdf5plugin # required for LZ4, Blosc, and other compression codecs\n", "from ophyd import ADComponent\n", "from ophyd.areadetector import DetectorBase\n", "from ophyd.areadetector import SimDetectorCam\n", "from ophyd.areadetector.filestore_mixins import FileStoreHDF5SingleIterativeWrite\n", "from ophyd.areadetector.plugins import HDF5Plugin_V34 as HDF5Plugin\n", "from ophyd.areadetector.plugins import ImagePlugin_V34 as ImagePlugin\n", "import bluesky\n", "import bluesky.plans as bp\n", "import databroker\n", "import matplotlib.pyplot as plt\n", "import pathlib\n", "import warnings\n", "\n", "plt.ion() # turn on matplotlib plots\n", "\n", "RE = bluesky.RunEngine()\n", "cat = databroker.temp().v2\n", "# or use your own catalog like this example:\n", "# cat = databroker.catalog[\"training\"]\n", "RE.subscribe(cat.v1.insert)\n", "\n", "IOC = \"ad:\"\n", "\n", "# These paths are specific to how this IOC is implemented.\n", "AD_IOC_MOUNT_PATH = pathlib.Path(\"/tmp\")\n", "BLUESKY_MOUNT_PATH = pathlib.Path(\"/tmp/docker_ioc/iocad/tmp\")\n", "\n", "IMAGE_DIR = \"example/%Y/%m/%d\" # our choice for file arrangement\n", "\n", "# MUST end with a `/`, pathlib will NOT provide it\n", "WRITE_PATH_TEMPLATE = f\"{AD_IOC_MOUNT_PATH / IMAGE_DIR}/\"\n", "READ_PATH_TEMPLATE = f\"{BLUESKY_MOUNT_PATH / IMAGE_DIR}/\"\n", "\n", "\n", "class TheDetectorCam(CamMixin_V34, SimDetectorCam):\n", " \"\"\"Revise SimDetectorCam for ADCore revisions.\"\"\"\n", "\n", " def __init__(self, *args, **kwargs):\n", " super().__init__(*args, **kwargs)\n", " self.stage_sigs.update(\n", " dict(\n", " acquire_time=0.01,\n", " acquire_period=0.015, # a wee bit longer than acquire_time\n", " num_images=1,\n", " num_exposures=1, # Exp./image\n", " wait_for_plugins=\"Yes\",\n", " array_callbacks=\"Enable\",\n", " )\n", " )\n", "\n", "\n", "class CustomHDF5Plugin(FileStoreHDF5SingleIterativeWrite, HDF5Plugin):\n", " \"\"\"\n", " Add data acquisition methods to HDF5Plugin.\n", "\n", " * ``stage()`` - prepare device PVs before data acquisition\n", " * ``unstage()`` - restore device PVs after data acquisition\n", " * ``generate_datum()`` - coordinate image storage metadata\n", " \"\"\"\n", "\n", " def __init__(self, *args, **kwargs):\n", " super().__init__(*args, **kwargs)\n", " self.stage_sigs.update(\n", " dict(\n", " array_callbacks=\"Disable\",\n", " auto_increment=\"Yes\",\n", " auto_save=\"Yes\",\n", " blocking_callbacks=\"No\",\n", " compression=\"zlib\",\n", " lazy_open=\"Yes\",\n", " store_perform=\"No\",\n", " zlevel=6,\n", " )\n", " )\n", " # capture is not used with Single mode\n", " # parent.cam.array_callbacks is staged once in the cam\n", " # create_directory must be set before file_path, which is set before staging\n", " remove_these = \"\"\"\n", " capture\n", " array_counter\n", " parent.cam.array_callbacks\n", " create_directory\n", " \"\"\".split()\n", " for k in remove_these:\n", " if k in self.stage_sigs:\n", " self.stage_sigs.pop(k)\n", "\n", " def stage(self):\n", " # Again, do not press the Capture button in the HDF plugin\n", " if \"capture\" in self.stage_sigs:\n", " warnings.warn(\"Do not use capture with file_write_mode='Single'\")\n", " self.stage_sigs.pop(\"capture\")\n", " super().stage()\n", "\n", "\n", "class CustomDetector(SingleTrigger_V34, DetectorBase):\n", " \"\"\"\n", " ADSimDetector\n", "\n", " SingleTrigger:\n", "\n", " * stop any current acquisition\n", " * sets image_mode to 'Multiple'\n", " \"\"\"\n", "\n", " cam = ADComponent(TheDetectorCam, \"cam1:\")\n", " hdf1 = ADComponent(\n", " CustomHDF5Plugin,\n", " \"HDF1:\",\n", " write_path_template=WRITE_PATH_TEMPLATE,\n", " read_path_template=READ_PATH_TEMPLATE,\n", " )\n", " image = ADComponent(ImagePlugin, \"image1:\")\n", "\n", "\n", "adsimdet = CustomDetector(IOC, name=\"adsimdet\")\n", "adsimdet.wait_for_connection(timeout=15)\n", "adsimdet.missing_plugins()\n", "adsimdet.read_attrs.append(\"hdf1\")\n", "adsimdet.hdf1.create_directory.put(-5)\n", "NUM_FRAMES = 1\n", "adsimdet.cam.stage_sigs[\"num_frames\"] = NUM_FRAMES\n", "\n", "# this step is needed for ophyd\n", "ensure_AD_plugin_primed(adsimdet.hdf1, True)\n", "\n", "\n", "uids = RE(\n", " bp.count([adsimdet],\n", " md=dict(\n", " title=\"Area Detector, Single mode, HDF5 file\",\n", " purpose=\"image\")\n", " )\n", ")\n", "\n", "run = cat.v2[uids[0]]\n", "frame = run.primary.read()[adsimdet.image.name][0][0]\n", "\n", "frame.plot.pcolormesh() # show the image\n", "```" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3.9.13 ('base')", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.13" }, "orig_nbformat": 4, "vscode": { "interpreter": { "hash": "f38aef175fb08dfc130a7d9bb9234f0792dc9ad861f95b6c05aedd1b380356e2" } } }, "nbformat": 4, "nbformat_minor": 2 }