From 34fa7e36fc1ef0b2677983bd1638557260a8f187 Mon Sep 17 00:00:00 2001 From: Pete R Jemian Date: Tue, 5 Jul 2022 14:59:53 -0500 Subject: [PATCH 01/24] DOC #652 --- docs/source/api/_devices.rst | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/docs/source/api/_devices.rst b/docs/source/api/_devices.rst index 455906d11..2f5b662c0 100644 --- a/docs/source/api/_devices.rst +++ b/docs/source/api/_devices.rst @@ -4,7 +4,8 @@ Devices ======= -(ophyd) Devices that might be useful at the APS using Bluesky +Devices (subclasses of ophyd's `Device`) that might be useful at the APS using +Bluesky. Also consult the :ref:`Index ` under the *Ophyd* heading for links to the Devices, Exceptions, Mixins, Signals, and other support @@ -13,6 +14,16 @@ items described here. Categories ---------- +See these categories: + +* :ref:`devices.aps_support` +* :ref:`devices.area_detector` +* :ref:`devices.motors` +* :ref:`devices.scalers` +* :ref:`devices.shutters` +* :ref:`devices.slits` +* :ref:`devices.temperature_controllers` + .. _devices.aps_support: APS General Support From df1774403baea0534dfcf3a5bb62f1edd0489d7d Mon Sep 17 00:00:00 2001 From: Pete R Jemian Date: Wed, 6 Jul 2022 00:53:46 -0500 Subject: [PATCH 02/24] WIP #652 needs words --- .../examples/_howto_setup_hdf5_plugin.ipynb | 206 ++++++++++++++++++ 1 file changed, 206 insertions(+) create mode 100644 docs/source/examples/_howto_setup_hdf5_plugin.ipynb diff --git a/docs/source/examples/_howto_setup_hdf5_plugin.ipynb b/docs/source/examples/_howto_setup_hdf5_plugin.ipynb new file mode 100644 index 000000000..6c640aac7 --- /dev/null +++ b/docs/source/examples/_howto_setup_hdf5_plugin.ipynb @@ -0,0 +1,206 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "1f614462-550a-4c0d-b1ca-79b8da6e3fc9", + "metadata": { + "tags": [] + }, + "source": [ + "# Example: Set up an HDF5 plugin" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "9715c404-0ead-4652-be6b-e2eca09eddae", + "metadata": {}, + "outputs": [], + "source": [ + "from apstools.devices import AD_EpicsFileNameHDF5Plugin\n", + "from apstools.devices import AD_full_file_name_local\n", + "from apstools.devices import AD_prime_plugin2\n", + "from ophyd import EpicsSignalWithRBV\n", + "from ophyd.areadetector import ADComponent\n", + "from ophyd.areadetector import DetectorBase\n", + "from ophyd.areadetector import SingleTrigger\n", + "from ophyd.areadetector.plugins import ImagePlugin_V34 as ImagePlugin\n", + "from ophyd.areadetector.plugins import PvaPlugin_V34 as PvaPlugin\n", + "from ophyd.areadetector import SimDetectorCam\n", + "import pathlib\n", + "import time" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "76ba0dde-e223-4006-8dac-34290d65d03c", + "metadata": {}, + "outputs": [], + "source": [ + "IOC = \"ad:\"\n", + "IMAGE_DIR = \"adsimdet/%Y/%m/%d\"\n", + "AD_IOC_MOUNT_PATH = pathlib.Path(\"/tmp\")\n", + "BLUESKY_MOUNT_PATH = pathlib.Path(\"/tmp/docker_ioc/iocad/tmp\")" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "5717f827-d8eb-4ed3-be1c-0bc89a72db18", + "metadata": {}, + "outputs": [], + "source": [ + "# 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}/\"" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "e72bec26-e017-4c62-bc88-f3eb0ebbdfa3", + "metadata": {}, + "outputs": [], + "source": [ + "class SimDetectorCam_R3_1_1(SimDetectorCam):\n", + " \"\"\"Revise SimDetectorCam for ADcore revisions.\"\"\"\n", + " pool_max_buffers = None\n", + " offset = ADComponent(EpicsSignalWithRBV, \"Offset\")" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "456e1aa6-a5f8-4c82-96ba-949eb4740363", + "metadata": {}, + "outputs": [], + "source": [ + "class MySimDetector(SingleTrigger, DetectorBase):\n", + " \"\"\"ADSimDetector\"\"\"\n", + "\n", + " cam = ADComponent(SimDetectorCam_R3_1_1, \"cam1:\")\n", + " hdf1 = ADComponent(\n", + " AD_EpicsFileNameHDF5Plugin,\n", + " \"HDF1:\",\n", + " write_path_template=WRITE_PATH_TEMPLATE,\n", + " read_path_template=READ_PATH_TEMPLATE,\n", + " )\n", + " # image = ADComponent(ImagePlugin, \"image1:\")\n", + " # pva = ADComponent(PvaPlugin, \"Pva1:\")" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "c3684f6d-0506-4f77-ade4-1de813a53772", + "metadata": {}, + "outputs": [], + "source": [ + "adsimdet = MySimDetector(IOC, name=\"adsimdet\")\n", + "adsimdet.wait_for_connection(timeout=15)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "75c0f03f-2c25-410c-9cf5-1930293a1acc", + "metadata": {}, + "outputs": [], + "source": [ + "AD_prime_plugin2(adsimdet.hdf1)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "05ee1cc3-e1d0-47a0-bf91-a799fa9dc9c7", + "metadata": {}, + "outputs": [], + "source": [ + "# these settings are the caller's responsibility\n", + "adsimdet.hdf1.create_directory.put(-5)\n", + "adsimdet.hdf1.file_path.put(WRITE_PATH_TEMPLATE) # path that the IOC will write the file\n", + "adsimdet.hdf1.file_template.put(f\"%s%s_%4.4d.h5\")\n", + "adsimdet.hdf1.file_name.put(\"my_test_file\")\n", + "adsimdet.hdf1.auto_increment.put(\"Yes\")\n", + "adsimdet.hdf1.auto_save.put(\"Yes\")\n", + "adsimdet.hdf1.compression.put(\"zlib\")\n", + "adsimdet.hdf1.zlevel.put(6)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "9deebfe7-3c59-4d3e-8c08-0a9cc73a140f", + "metadata": {}, + "outputs": [], + "source": [ + "adsimdet.stage()\n", + "adsimdet.trigger()\n", + "time.sleep(0.005)\n", + "while adsimdet.cam.acquire.get(use_monitor=False) not in (0, \"Done\"):\n", + " time.sleep(0.005)\n", + "adsimdet.unstage()" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "adbd4448-c1fd-4046-8647-4318ac89ab4b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "adsimdet.hdf1.full_file_name.get(use_monitor=False)='/tmp/adsimdet/2022/07/06/my_test_file_0136.h5'\n" + ] + } + ], + "source": [ + "print(f\"{adsimdet.hdf1.full_file_name.get(use_monitor=False)=}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "2735b21b-2d17-4825-99cf-b4a5d70827f9", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "lfname.exists()=True lfname=PosixPath('/tmp/docker_ioc/iocad/tmp/adsimdet/2022/07/06/my_test_file_0136.h5')\n" + ] + } + ], + "source": [ + "lfname = AD_full_file_name_local(adsimdet.hdf1)\n", + "print(f\"{lfname.exists()=} {lfname=}\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} From bee0ac161b662c3455490ef4e78db002647d53c2 Mon Sep 17 00:00:00 2001 From: Pete R Jemian Date: Wed, 6 Jul 2022 00:54:39 -0500 Subject: [PATCH 03/24] ENH #652 add nbsphinx --- .github/workflows/publish-sphinx.yml | 4 ++-- docs/source/conf.py | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish-sphinx.yml b/.github/workflows/publish-sphinx.yml index 2e739efe2..955c33a71 100644 --- a/.github/workflows/publish-sphinx.yml +++ b/.github/workflows/publish-sphinx.yml @@ -23,9 +23,9 @@ jobs: with: fetch-depth: 0 # otherwise, you will fail to push refs to dest repo - - name: Install sphinx-rtd-theme + - name: Install sphinx-rtd-theme & nbsphinx run: | - pip install sphinx-rtd-theme + pip install sphinx-rtd-theme nbsphinx - name: Build and Commit uses: sphinx-notes/pages@master diff --git a/docs/source/conf.py b/docs/source/conf.py index 775c9aea1..07b7e3f2f 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -40,6 +40,7 @@ sphinx.ext.mathjax sphinx.ext.todo sphinx.ext.viewcode + nbsphinx """.split() # Add any paths that contain templates here, relative to this directory. From 0f239408d007042da171a249122e2afdc32720ee Mon Sep 17 00:00:00 2001 From: Pete R Jemian Date: Wed, 6 Jul 2022 00:55:18 -0500 Subject: [PATCH 04/24] MNT #652 use nbsphinx for other notebooks --- apstools/plans/alignment.py | 4 +- docs/source/examples/_excel_scan.ipynb | 1805 +++++++++++++++++ .../_nscan.ipynb} | 32 +- docs/source/examples/_nscan.rst | 9 - docs/source/examples/_specfile.rst | 9 - docs/source/examples/_specfile_example.ipynb | 961 +++++++++ .../_tuneaxis.ipynb} | 336 +-- docs/source/examples/_tuneaxis.rst | 8 - docs/source/examples/index.rst | 11 +- .../sample_example.xlsx | Bin docs/source/examples/spec1.dat | 66 + docs/source/examples/test_specdata.txt | 66 + .../resources/demo_specfile_example.ipynb | 951 --------- docs/source/resources/excel_scan.ipynb | 850 -------- 14 files changed, 3092 insertions(+), 2016 deletions(-) create mode 100644 docs/source/examples/_excel_scan.ipynb rename docs/source/{resources/demo_nscan.ipynb => examples/_nscan.ipynb} (73%) delete mode 100644 docs/source/examples/_nscan.rst delete mode 100644 docs/source/examples/_specfile.rst create mode 100644 docs/source/examples/_specfile_example.ipynb rename docs/source/{resources/demo_tuneaxis.ipynb => examples/_tuneaxis.ipynb} (56%) delete mode 100644 docs/source/examples/_tuneaxis.rst rename docs/source/{resources => examples}/sample_example.xlsx (100%) create mode 100644 docs/source/examples/spec1.dat create mode 100644 docs/source/examples/test_specdata.txt delete mode 100644 docs/source/resources/demo_specfile_example.ipynb delete mode 100644 docs/source/resources/excel_scan.ipynb diff --git a/apstools/plans/alignment.py b/apstools/plans/alignment.py index f37c355c4..3f20b134a 100644 --- a/apstools/plans/alignment.py +++ b/apstools/plans/alignment.py @@ -210,8 +210,8 @@ class TuneAxis(object): RE(tuner.multi_pass_tune(width=2, num=9), live_table) RE(tuner.tune(width=0.05, num=9), live_table) - Also see the jupyter notebook referenced here: - :ref:`example_tuneaxis`. + Also see the jupyter notebook tited **Demonstrate TuneAxis()** + in the :ref:`examples` section. .. autosummary:: diff --git a/docs/source/examples/_excel_scan.ipynb b/docs/source/examples/_excel_scan.ipynb new file mode 100644 index 000000000..5ca24e03b --- /dev/null +++ b/docs/source/examples/_excel_scan.ipynb @@ -0,0 +1,1805 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Example: The *Excel scan* plan\n", + "\n", + "Use a spreadsheet as a multi-sample batch scan tool. \n", + "We'll need a spreadsheet with some things to be done \n", + "and a plan that will read that spreadsheet and act on it. \n", + "\n", + "This plan will:\n", + "\n", + "* [x] use an Excel spreadsheet (for starters)\n", + "* [x] read a table from the spreadsheet file\n", + "* [x] take a single action from each row in the table\n", + "* [x] decide action based on a specific named column in the table\n", + "* [x] report all columns as metadata for the action\n", + "* [x] ignore empty rows\n", + "* [ ] ignore any data outside of the table boundaries\n", + "\n", + "Since the actions and parameters (args & kwargs) will be \n", + "different in every implementation, this may prove difficult \n", + "to generalize.\n", + "\n", + "Here's the demo, starting with the bluesky setup." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "# Import matplotlib and put it in interactive mode.\n", + "%matplotlib notebook\n", + "import matplotlib.pyplot as plt\n", + "plt.ion()\n", + "\n", + "import pathlib\n", + "\n", + "import databroker\n", + "cat = databroker.temp()\n", + "\n", + "from bluesky import RunEngine\n", + "import bluesky.plans as bp\n", + "import bluesky.plan_stubs as bps\n", + "from bluesky.callbacks.best_effort import BestEffortCallback\n", + "from bluesky import SupplementalData\n", + "from bluesky.simulators import summarize_plan\n", + "from bluesky.suspenders import SuspendFloor\n", + "\n", + "from ophyd.sim import motor1, motor2, motor3, SynGauss\n", + "\n", + "import apstools.devices as APS_devices\n", + "import apstools.utils as APS_utils\n", + "\n", + "RE = RunEngine({})\n", + "RE.subscribe(cat.v1.insert)\n", + "RE.subscribe(BestEffortCallback())\n", + "RE.preprocessors.append(SupplementalData())\n", + "\n", + "shutter = APS_devices.SimulatedApsPssShutterWithStatus(name=\"shutter\")\n", + "watch_for_shutter_close = SuspendFloor(shutter.pss_state, 1)\n", + "\n", + "noisy_det = SynGauss('noisy_det', motor1, 'motor1', center=0, Imax=1,\n", + " noise='uniform', sigma=0.9, noise_multiplier=0.1, labels={'detectors'})\n", + "noisy_det.kind = \"hinted\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "----\n", + "\n", + "## Excel plan and infrastructure" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "shutter -> open\n", + "motor2 -> 5.07\n", + "motor3 -> 8.3\n", + "Scan_Type: step_scan\n", + "sx: 5.07\n", + "sy: 8.3\n", + "Thickness: 0\n", + "Sample_Name: Water Blank\n", + "remarks: deionized\n", + "code_number: None\n", + "table_row: 1\n", + "Excel_file: sample_example.xlsx\n", + "xl_file: sample_example.xlsx\n", + "original_keys: {'Scan_Type': 'Scan Type', 'sx': 'sx', 'sy': 'sy', 'Thickness': 'Thickness', 'Sample_Name': 'Sample Name', 'remarks': 'remarks', 'code_number': 'code number'}\n", + "motor2: 0\n", + "motor3: 0\n", + "shutter: close\n", + "=================================== Open Run ===================================\n", + "motor1 -> -5.0\n", + " Read ['noisy_det', 'motor1']\n", + "motor1 -> -3.571428571428571\n", + " Read ['noisy_det', 'motor1']\n", + "motor1 -> -2.142857142857143\n", + " Read ['noisy_det', 'motor1']\n", + "motor1 -> -0.7142857142857144\n", + " Read ['noisy_det', 'motor1']\n", + "motor1 -> 0.7142857142857144\n", + " Read ['noisy_det', 'motor1']\n", + "motor1 -> 2.1428571428571432\n", + " Read ['noisy_det', 'motor1']\n", + "motor1 -> 3.571428571428571\n", + " Read ['noisy_det', 'motor1']\n", + "motor1 -> 5.0\n", + " Read ['noisy_det', 'motor1']\n", + "================================== Close Run ===================================\n", + "no handling for table row 2: OrderedDict([('Scan Type', 'other_scan'), ('sx', 5.07), ('sy', 8.3), ('Thickness', 0), ('Sample Name', 'Water Blank'), ('remarks', 'deionized'), ('code number', None)])\n", + "no handling for table row 3: OrderedDict([('Scan Type', 'this will be ignored (and also the next blank row will be ignored)'), ('sx', None), ('sy', None), ('Thickness', None), ('Sample Name', None), ('remarks', None), ('code number', None)])\n", + "shutter -> close\n", + "motor1 -> 0\n", + "motor2 -> 0\n", + "motor3 -> 0\n" + ] + } + ], + "source": [ + "def beforeExcelPlan():\n", + " \"\"\"things to be done at the start of every Excel plan\"\"\"\n", + " yield from bps.mv(\n", + " shutter, \"open\", # for example\n", + " )\n", + "\n", + " \n", + "def afterExcelPlan():\n", + " \"\"\"things to be done at the end of every Excel plan\"\"\"\n", + " yield from bps.mv(\n", + " shutter, \"close\", # for example\n", + " motor1, 0, # park the motors\n", + " motor2, 0,\n", + " motor3, 0,\n", + " )\n", + "\n", + "\n", + "def common_step_scan(pos_X, pos_Y, thickness, scan_title, md={}):\n", + " \"\"\"\n", + " run a step scan over a common range at given sample position\n", + " \"\"\"\n", + " yield from bps.mv(\n", + " motor2, pos_X,\n", + " motor3, pos_Y,\n", + " )\n", + " md[motor2.name] = motor2.position\n", + " md[motor3.name] = motor3.position\n", + " md[\"shutter\"] = shutter.state\n", + " for k, v in md.items():\n", + " print(f\"{k}: {v}\")\n", + "\n", + " yield from bps.install_suspender(watch_for_shutter_close)\n", + " yield from bp.scan([noisy_det], motor1, -5, 5, 8, md=md)\n", + " yield from bps.remove_suspender(watch_for_shutter_close)\n", + "\n", + "\n", + "def Excel_plan(xl_file, md={}):\n", + " \"\"\"\n", + " example of reading a list of samples from Excel spreadsheet\n", + " \n", + " USAGE::\n", + " \n", + " summarize_plan(run_Excel_file(\"sample_example.xlsx\"))\n", + " RE(run_Excel_file(\"sample_example.xlsx\"))\n", + " \"\"\"\n", + " excel_file = pathlib.Path(xl_file)\n", + " assert excel_file.exists()\n", + " xl = APS_utils.ExcelDatabaseFileGeneric(str(excel_file))\n", + "\n", + " yield from beforeExcelPlan()\n", + " for i, row in enumerate(xl.db.values()):\n", + " # print(f\"row={row}\")\n", + " \n", + " # metadata\n", + " # all parameters from this row go into the metadata\n", + " # columns names are the keys in the metadata dictionary\n", + " # make sure md keys are \"clean\"\n", + " # also provide crossreference to original column names\n", + " _md = {APS_utils.cleanupText(k): v for k, v in row.items()}\n", + " _md[\"table_row\"] = i+1\n", + " _md[\"Excel_file\"] = str(excel_file)\n", + " _md[\"xl_file\"] = xl_file\n", + " _md[\"original_keys\"] = {APS_utils.cleanupText(k): k for k in row.keys()}\n", + " _md.update(md) # overlay with user-supplied metadata\n", + "\n", + " scan_command = (row[\"Scan Type\"] or \"\").lower()\n", + " if scan_command == \"step_scan\":\n", + " yield from common_step_scan(\n", + " row[\"sx\"], # label must match cell string EXACTLY\n", + " row[\"sy\"], \n", + " row[\"Thickness\"], \n", + " row[\"Sample Name\"],\n", + " # add all input as scan metadata, ensure the keys are clean\n", + " md=_md,\n", + " )\n", + " elif scan_command == \"some_other_action\":\n", + " pass # TODO: suggestion\n", + " else:\n", + " print(f\"no handling for table row {i+1}: {row}\")\n", + " yield from afterExcelPlan()\n", + "\n", + "\n", + "summarize_plan(Excel_plan(\"sample_example.xlsx\"))" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Scan_Type: step_scan\n", + "sx: 5.07\n", + "sy: 8.3\n", + "Thickness: 0\n", + "Sample_Name: Water Blank\n", + "remarks: deionized\n", + "code_number: None\n", + "table_row: 1\n", + "Excel_file: sample_example.xlsx\n", + "xl_file: sample_example.xlsx\n", + "original_keys: {'Scan_Type': 'Scan Type', 'sx': 'sx', 'sy': 'sy', 'Thickness': 'Thickness', 'Sample_Name': 'Sample Name', 'remarks': 'remarks', 'code_number': 'code number'}\n", + "motor2: 5.07\n", + "motor3: 8.3\n", + "shutter: open\n", + "\n", + "\n", + "Transient Scan ID: 1 Time: 2022-07-06 00:27:29\n", + "Persistent Unique Scan ID: 'a6ea8a82-aff2-4621-a197-1a3bca524e51'\n", + "New stream: 'primary'\n" + ] + }, + { + "data": { + "application/javascript": [ + "/* Put everything inside the global mpl namespace */\n", + "/* global mpl */\n", + "window.mpl = {};\n", + "\n", + "mpl.get_websocket_type = function () {\n", + " if (typeof WebSocket !== 'undefined') {\n", + " return WebSocket;\n", + " } else if (typeof MozWebSocket !== 'undefined') {\n", + " return MozWebSocket;\n", + " } else {\n", + " alert(\n", + " 'Your browser does not have WebSocket support. ' +\n", + " 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", + " 'Firefox 4 and 5 are also supported but you ' +\n", + " 'have to enable WebSockets in about:config.'\n", + " );\n", + " }\n", + "};\n", + "\n", + "mpl.figure = function (figure_id, websocket, ondownload, parent_element) {\n", + " this.id = figure_id;\n", + "\n", + " this.ws = websocket;\n", + "\n", + " this.supports_binary = this.ws.binaryType !== undefined;\n", + "\n", + " if (!this.supports_binary) {\n", + " var warnings = document.getElementById('mpl-warnings');\n", + " if (warnings) {\n", + " warnings.style.display = 'block';\n", + " warnings.textContent =\n", + " 'This browser does not support binary websocket messages. ' +\n", + " 'Performance may be slow.';\n", + " }\n", + " }\n", + "\n", + " this.imageObj = new Image();\n", + "\n", + " this.context = undefined;\n", + " this.message = undefined;\n", + " this.canvas = undefined;\n", + " this.rubberband_canvas = undefined;\n", + " this.rubberband_context = undefined;\n", + " this.format_dropdown = undefined;\n", + "\n", + " this.image_mode = 'full';\n", + "\n", + " this.root = document.createElement('div');\n", + " this.root.setAttribute('style', 'display: inline-block');\n", + " this._root_extra_style(this.root);\n", + "\n", + " parent_element.appendChild(this.root);\n", + "\n", + " this._init_header(this);\n", + " this._init_canvas(this);\n", + " this._init_toolbar(this);\n", + "\n", + " var fig = this;\n", + "\n", + " this.waiting = false;\n", + "\n", + " this.ws.onopen = function () {\n", + " fig.send_message('supports_binary', { value: fig.supports_binary });\n", + " fig.send_message('send_image_mode', {});\n", + " if (fig.ratio !== 1) {\n", + " fig.send_message('set_device_pixel_ratio', {\n", + " device_pixel_ratio: fig.ratio,\n", + " });\n", + " }\n", + " fig.send_message('refresh', {});\n", + " };\n", + "\n", + " this.imageObj.onload = function () {\n", + " if (fig.image_mode === 'full') {\n", + " // Full images could contain transparency (where diff images\n", + " // almost always do), so we need to clear the canvas so that\n", + " // there is no ghosting.\n", + " fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", + " }\n", + " fig.context.drawImage(fig.imageObj, 0, 0);\n", + " };\n", + "\n", + " this.imageObj.onunload = function () {\n", + " fig.ws.close();\n", + " };\n", + "\n", + " this.ws.onmessage = this._make_on_message_function(this);\n", + "\n", + " this.ondownload = ondownload;\n", + "};\n", + "\n", + "mpl.figure.prototype._init_header = function () {\n", + " var titlebar = document.createElement('div');\n", + " titlebar.classList =\n", + " 'ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix';\n", + " var titletext = document.createElement('div');\n", + " titletext.classList = 'ui-dialog-title';\n", + " titletext.setAttribute(\n", + " 'style',\n", + " 'width: 100%; text-align: center; padding: 3px;'\n", + " );\n", + " titlebar.appendChild(titletext);\n", + " this.root.appendChild(titlebar);\n", + " this.header = titletext;\n", + "};\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function (_canvas_div) {};\n", + "\n", + "mpl.figure.prototype._root_extra_style = function (_canvas_div) {};\n", + "\n", + "mpl.figure.prototype._init_canvas = function () {\n", + " var fig = this;\n", + "\n", + " var canvas_div = (this.canvas_div = document.createElement('div'));\n", + " canvas_div.setAttribute(\n", + " 'style',\n", + " 'border: 1px solid #ddd;' +\n", + " 'box-sizing: content-box;' +\n", + " 'clear: both;' +\n", + " 'min-height: 1px;' +\n", + " 'min-width: 1px;' +\n", + " 'outline: 0;' +\n", + " 'overflow: hidden;' +\n", + " 'position: relative;' +\n", + " 'resize: both;'\n", + " );\n", + "\n", + " function on_keyboard_event_closure(name) {\n", + " return function (event) {\n", + " return fig.key_event(event, name);\n", + " };\n", + " }\n", + "\n", + " canvas_div.addEventListener(\n", + " 'keydown',\n", + " on_keyboard_event_closure('key_press')\n", + " );\n", + " canvas_div.addEventListener(\n", + " 'keyup',\n", + " on_keyboard_event_closure('key_release')\n", + " );\n", + "\n", + " this._canvas_extra_style(canvas_div);\n", + " this.root.appendChild(canvas_div);\n", + "\n", + " var canvas = (this.canvas = document.createElement('canvas'));\n", + " canvas.classList.add('mpl-canvas');\n", + " canvas.setAttribute('style', 'box-sizing: content-box;');\n", + "\n", + " this.context = canvas.getContext('2d');\n", + "\n", + " var backingStore =\n", + " this.context.backingStorePixelRatio ||\n", + " this.context.webkitBackingStorePixelRatio ||\n", + " this.context.mozBackingStorePixelRatio ||\n", + " this.context.msBackingStorePixelRatio ||\n", + " this.context.oBackingStorePixelRatio ||\n", + " this.context.backingStorePixelRatio ||\n", + " 1;\n", + "\n", + " this.ratio = (window.devicePixelRatio || 1) / backingStore;\n", + "\n", + " var rubberband_canvas = (this.rubberband_canvas = document.createElement(\n", + " 'canvas'\n", + " ));\n", + " rubberband_canvas.setAttribute(\n", + " 'style',\n", + " 'box-sizing: content-box; position: absolute; left: 0; top: 0; z-index: 1;'\n", + " );\n", + "\n", + " // Apply a ponyfill if ResizeObserver is not implemented by browser.\n", + " if (this.ResizeObserver === undefined) {\n", + " if (window.ResizeObserver !== undefined) {\n", + " this.ResizeObserver = window.ResizeObserver;\n", + " } else {\n", + " var obs = _JSXTOOLS_RESIZE_OBSERVER({});\n", + " this.ResizeObserver = obs.ResizeObserver;\n", + " }\n", + " }\n", + "\n", + " this.resizeObserverInstance = new this.ResizeObserver(function (entries) {\n", + " var nentries = entries.length;\n", + " for (var i = 0; i < nentries; i++) {\n", + " var entry = entries[i];\n", + " var width, height;\n", + " if (entry.contentBoxSize) {\n", + " if (entry.contentBoxSize instanceof Array) {\n", + " // Chrome 84 implements new version of spec.\n", + " width = entry.contentBoxSize[0].inlineSize;\n", + " height = entry.contentBoxSize[0].blockSize;\n", + " } else {\n", + " // Firefox implements old version of spec.\n", + " width = entry.contentBoxSize.inlineSize;\n", + " height = entry.contentBoxSize.blockSize;\n", + " }\n", + " } else {\n", + " // Chrome <84 implements even older version of spec.\n", + " width = entry.contentRect.width;\n", + " height = entry.contentRect.height;\n", + " }\n", + "\n", + " // Keep the size of the canvas and rubber band canvas in sync with\n", + " // the canvas container.\n", + " if (entry.devicePixelContentBoxSize) {\n", + " // Chrome 84 implements new version of spec.\n", + " canvas.setAttribute(\n", + " 'width',\n", + " entry.devicePixelContentBoxSize[0].inlineSize\n", + " );\n", + " canvas.setAttribute(\n", + " 'height',\n", + " entry.devicePixelContentBoxSize[0].blockSize\n", + " );\n", + " } else {\n", + " canvas.setAttribute('width', width * fig.ratio);\n", + " canvas.setAttribute('height', height * fig.ratio);\n", + " }\n", + " canvas.setAttribute(\n", + " 'style',\n", + " 'width: ' + width + 'px; height: ' + height + 'px;'\n", + " );\n", + "\n", + " rubberband_canvas.setAttribute('width', width);\n", + " rubberband_canvas.setAttribute('height', height);\n", + "\n", + " // And update the size in Python. We ignore the initial 0/0 size\n", + " // that occurs as the element is placed into the DOM, which should\n", + " // otherwise not happen due to the minimum size styling.\n", + " if (fig.ws.readyState == 1 && width != 0 && height != 0) {\n", + " fig.request_resize(width, height);\n", + " }\n", + " }\n", + " });\n", + " this.resizeObserverInstance.observe(canvas_div);\n", + "\n", + " function on_mouse_event_closure(name) {\n", + " return function (event) {\n", + " return fig.mouse_event(event, name);\n", + " };\n", + " }\n", + "\n", + " rubberband_canvas.addEventListener(\n", + " 'mousedown',\n", + " on_mouse_event_closure('button_press')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseup',\n", + " on_mouse_event_closure('button_release')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'dblclick',\n", + " on_mouse_event_closure('dblclick')\n", + " );\n", + " // Throttle sequential mouse events to 1 every 20ms.\n", + " rubberband_canvas.addEventListener(\n", + " 'mousemove',\n", + " on_mouse_event_closure('motion_notify')\n", + " );\n", + "\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseenter',\n", + " on_mouse_event_closure('figure_enter')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseleave',\n", + " on_mouse_event_closure('figure_leave')\n", + " );\n", + "\n", + " canvas_div.addEventListener('wheel', function (event) {\n", + " if (event.deltaY < 0) {\n", + " event.step = 1;\n", + " } else {\n", + " event.step = -1;\n", + " }\n", + " on_mouse_event_closure('scroll')(event);\n", + " });\n", + "\n", + " canvas_div.appendChild(canvas);\n", + " canvas_div.appendChild(rubberband_canvas);\n", + "\n", + " this.rubberband_context = rubberband_canvas.getContext('2d');\n", + " this.rubberband_context.strokeStyle = '#000000';\n", + "\n", + " this._resize_canvas = function (width, height, forward) {\n", + " if (forward) {\n", + " canvas_div.style.width = width + 'px';\n", + " canvas_div.style.height = height + 'px';\n", + " }\n", + " };\n", + "\n", + " // Disable right mouse context menu.\n", + " this.rubberband_canvas.addEventListener('contextmenu', function (_e) {\n", + " event.preventDefault();\n", + " return false;\n", + " });\n", + "\n", + " function set_focus() {\n", + " canvas.focus();\n", + " canvas_div.focus();\n", + " }\n", + "\n", + " window.setTimeout(set_focus, 100);\n", + "};\n", + "\n", + "mpl.figure.prototype._init_toolbar = function () {\n", + " var fig = this;\n", + "\n", + " var toolbar = document.createElement('div');\n", + " toolbar.classList = 'mpl-toolbar';\n", + " this.root.appendChild(toolbar);\n", + "\n", + " function on_click_closure(name) {\n", + " return function (_event) {\n", + " return fig.toolbar_button_onclick(name);\n", + " };\n", + " }\n", + "\n", + " function on_mouseover_closure(tooltip) {\n", + " return function (event) {\n", + " if (!event.currentTarget.disabled) {\n", + " return fig.toolbar_button_onmouseover(tooltip);\n", + " }\n", + " };\n", + " }\n", + "\n", + " fig.buttons = {};\n", + " var buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'mpl-button-group';\n", + " for (var toolbar_ind in mpl.toolbar_items) {\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) {\n", + " /* Instead of a spacer, we start a new button group. */\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + " buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'mpl-button-group';\n", + " continue;\n", + " }\n", + "\n", + " var button = (fig.buttons[name] = document.createElement('button'));\n", + " button.classList = 'mpl-widget';\n", + " button.setAttribute('role', 'button');\n", + " button.setAttribute('aria-disabled', 'false');\n", + " button.addEventListener('click', on_click_closure(method_name));\n", + " button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n", + "\n", + " var icon_img = document.createElement('img');\n", + " icon_img.src = '_images/' + image + '.png';\n", + " icon_img.srcset = '_images/' + image + '_large.png 2x';\n", + " icon_img.alt = tooltip;\n", + " button.appendChild(icon_img);\n", + "\n", + " buttonGroup.appendChild(button);\n", + " }\n", + "\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + "\n", + " var fmt_picker = document.createElement('select');\n", + " fmt_picker.classList = 'mpl-widget';\n", + " toolbar.appendChild(fmt_picker);\n", + " this.format_dropdown = fmt_picker;\n", + "\n", + " for (var ind in mpl.extensions) {\n", + " var fmt = mpl.extensions[ind];\n", + " var option = document.createElement('option');\n", + " option.selected = fmt === mpl.default_extension;\n", + " option.innerHTML = fmt;\n", + " fmt_picker.appendChild(option);\n", + " }\n", + "\n", + " var status_bar = document.createElement('span');\n", + " status_bar.classList = 'mpl-message';\n", + " toolbar.appendChild(status_bar);\n", + " this.message = status_bar;\n", + "};\n", + "\n", + "mpl.figure.prototype.request_resize = function (x_pixels, y_pixels) {\n", + " // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n", + " // which will in turn request a refresh of the image.\n", + " this.send_message('resize', { width: x_pixels, height: y_pixels });\n", + "};\n", + "\n", + "mpl.figure.prototype.send_message = function (type, properties) {\n", + " properties['type'] = type;\n", + " properties['figure_id'] = this.id;\n", + " this.ws.send(JSON.stringify(properties));\n", + "};\n", + "\n", + "mpl.figure.prototype.send_draw_message = function () {\n", + " if (!this.waiting) {\n", + " this.waiting = true;\n", + " this.ws.send(JSON.stringify({ type: 'draw', figure_id: this.id }));\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_save = function (fig, _msg) {\n", + " var format_dropdown = fig.format_dropdown;\n", + " var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n", + " fig.ondownload(fig, format);\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_resize = function (fig, msg) {\n", + " var size = msg['size'];\n", + " if (size[0] !== fig.canvas.width || size[1] !== fig.canvas.height) {\n", + " fig._resize_canvas(size[0], size[1], msg['forward']);\n", + " fig.send_message('refresh', {});\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_rubberband = function (fig, msg) {\n", + " var x0 = msg['x0'] / fig.ratio;\n", + " var y0 = (fig.canvas.height - msg['y0']) / fig.ratio;\n", + " var x1 = msg['x1'] / fig.ratio;\n", + " var y1 = (fig.canvas.height - msg['y1']) / fig.ratio;\n", + " x0 = Math.floor(x0) + 0.5;\n", + " y0 = Math.floor(y0) + 0.5;\n", + " x1 = Math.floor(x1) + 0.5;\n", + " y1 = Math.floor(y1) + 0.5;\n", + " var min_x = Math.min(x0, x1);\n", + " var min_y = Math.min(y0, y1);\n", + " var width = Math.abs(x1 - x0);\n", + " var height = Math.abs(y1 - y0);\n", + "\n", + " fig.rubberband_context.clearRect(\n", + " 0,\n", + " 0,\n", + " fig.canvas.width / fig.ratio,\n", + " fig.canvas.height / fig.ratio\n", + " );\n", + "\n", + " fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_figure_label = function (fig, msg) {\n", + " // Updates the figure title.\n", + " fig.header.textContent = msg['label'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_cursor = function (fig, msg) {\n", + " fig.rubberband_canvas.style.cursor = msg['cursor'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_message = function (fig, msg) {\n", + " fig.message.textContent = msg['message'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_draw = function (fig, _msg) {\n", + " // Request the server to send over a new figure.\n", + " fig.send_draw_message();\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_image_mode = function (fig, msg) {\n", + " fig.image_mode = msg['mode'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_history_buttons = function (fig, msg) {\n", + " for (var key in msg) {\n", + " if (!(key in fig.buttons)) {\n", + " continue;\n", + " }\n", + " fig.buttons[key].disabled = !msg[key];\n", + " fig.buttons[key].setAttribute('aria-disabled', !msg[key]);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_navigate_mode = function (fig, msg) {\n", + " if (msg['mode'] === 'PAN') {\n", + " fig.buttons['Pan'].classList.add('active');\n", + " fig.buttons['Zoom'].classList.remove('active');\n", + " } else if (msg['mode'] === 'ZOOM') {\n", + " fig.buttons['Pan'].classList.remove('active');\n", + " fig.buttons['Zoom'].classList.add('active');\n", + " } else {\n", + " fig.buttons['Pan'].classList.remove('active');\n", + " fig.buttons['Zoom'].classList.remove('active');\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.updated_canvas_event = function () {\n", + " // Called whenever the canvas gets updated.\n", + " this.send_message('ack', {});\n", + "};\n", + "\n", + "// A function to construct a web socket function for onmessage handling.\n", + "// Called in the figure constructor.\n", + "mpl.figure.prototype._make_on_message_function = function (fig) {\n", + " return function socket_on_message(evt) {\n", + " if (evt.data instanceof Blob) {\n", + " var img = evt.data;\n", + " if (img.type !== 'image/png') {\n", + " /* FIXME: We get \"Resource interpreted as Image but\n", + " * transferred with MIME type text/plain:\" errors on\n", + " * Chrome. But how to set the MIME type? It doesn't seem\n", + " * to be part of the websocket stream */\n", + " img.type = 'image/png';\n", + " }\n", + "\n", + " /* Free the memory for the previous frames */\n", + " if (fig.imageObj.src) {\n", + " (window.URL || window.webkitURL).revokeObjectURL(\n", + " fig.imageObj.src\n", + " );\n", + " }\n", + "\n", + " fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n", + " img\n", + " );\n", + " fig.updated_canvas_event();\n", + " fig.waiting = false;\n", + " return;\n", + " } else if (\n", + " typeof evt.data === 'string' &&\n", + " evt.data.slice(0, 21) === 'data:image/png;base64'\n", + " ) {\n", + " fig.imageObj.src = evt.data;\n", + " fig.updated_canvas_event();\n", + " fig.waiting = false;\n", + " return;\n", + " }\n", + "\n", + " var msg = JSON.parse(evt.data);\n", + " var msg_type = msg['type'];\n", + "\n", + " // Call the \"handle_{type}\" callback, which takes\n", + " // the figure and JSON message as its only arguments.\n", + " try {\n", + " var callback = fig['handle_' + msg_type];\n", + " } catch (e) {\n", + " console.log(\n", + " \"No handler for the '\" + msg_type + \"' message type: \",\n", + " msg\n", + " );\n", + " return;\n", + " }\n", + "\n", + " if (callback) {\n", + " try {\n", + " // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n", + " callback(fig, msg);\n", + " } catch (e) {\n", + " console.log(\n", + " \"Exception inside the 'handler_\" + msg_type + \"' callback:\",\n", + " e,\n", + " e.stack,\n", + " msg\n", + " );\n", + " }\n", + " }\n", + " };\n", + "};\n", + "\n", + "// from https://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n", + "mpl.findpos = function (e) {\n", + " //this section is from http://www.quirksmode.org/js/events_properties.html\n", + " var targ;\n", + " if (!e) {\n", + " e = window.event;\n", + " }\n", + " if (e.target) {\n", + " targ = e.target;\n", + " } else if (e.srcElement) {\n", + " targ = e.srcElement;\n", + " }\n", + " if (targ.nodeType === 3) {\n", + " // defeat Safari bug\n", + " targ = targ.parentNode;\n", + " }\n", + "\n", + " // pageX,Y are the mouse positions relative to the document\n", + " var boundingRect = targ.getBoundingClientRect();\n", + " var x = e.pageX - (boundingRect.left + document.body.scrollLeft);\n", + " var y = e.pageY - (boundingRect.top + document.body.scrollTop);\n", + "\n", + " return { x: x, y: y };\n", + "};\n", + "\n", + "/*\n", + " * return a copy of an object with only non-object keys\n", + " * we need this to avoid circular references\n", + " * https://stackoverflow.com/a/24161582/3208463\n", + " */\n", + "function simpleKeys(original) {\n", + " return Object.keys(original).reduce(function (obj, key) {\n", + " if (typeof original[key] !== 'object') {\n", + " obj[key] = original[key];\n", + " }\n", + " return obj;\n", + " }, {});\n", + "}\n", + "\n", + "mpl.figure.prototype.mouse_event = function (event, name) {\n", + " var canvas_pos = mpl.findpos(event);\n", + "\n", + " if (name === 'button_press') {\n", + " this.canvas.focus();\n", + " this.canvas_div.focus();\n", + " }\n", + "\n", + " var x = canvas_pos.x * this.ratio;\n", + " var y = canvas_pos.y * this.ratio;\n", + "\n", + " this.send_message(name, {\n", + " x: x,\n", + " y: y,\n", + " button: event.button,\n", + " step: event.step,\n", + " guiEvent: simpleKeys(event),\n", + " });\n", + "\n", + " /* This prevents the web browser from automatically changing to\n", + " * the text insertion cursor when the button is pressed. We want\n", + " * to control all of the cursor setting manually through the\n", + " * 'cursor' event from matplotlib */\n", + " event.preventDefault();\n", + " return false;\n", + "};\n", + "\n", + "mpl.figure.prototype._key_event_extra = function (_event, _name) {\n", + " // Handle any extra behaviour associated with a key event\n", + "};\n", + "\n", + "mpl.figure.prototype.key_event = function (event, name) {\n", + " // Prevent repeat events\n", + " if (name === 'key_press') {\n", + " if (event.key === this._key) {\n", + " return;\n", + " } else {\n", + " this._key = event.key;\n", + " }\n", + " }\n", + " if (name === 'key_release') {\n", + " this._key = null;\n", + " }\n", + "\n", + " var value = '';\n", + " if (event.ctrlKey && event.key !== 'Control') {\n", + " value += 'ctrl+';\n", + " }\n", + " else if (event.altKey && event.key !== 'Alt') {\n", + " value += 'alt+';\n", + " }\n", + " else if (event.shiftKey && event.key !== 'Shift') {\n", + " value += 'shift+';\n", + " }\n", + "\n", + " value += 'k' + event.key;\n", + "\n", + " this._key_event_extra(event, name);\n", + "\n", + " this.send_message(name, { key: value, guiEvent: simpleKeys(event) });\n", + " return false;\n", + "};\n", + "\n", + "mpl.figure.prototype.toolbar_button_onclick = function (name) {\n", + " if (name === 'download') {\n", + " this.handle_save(this, null);\n", + " } else {\n", + " this.send_message('toolbar_button', { name: name });\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.toolbar_button_onmouseover = function (tooltip) {\n", + " this.message.textContent = tooltip;\n", + "};\n", + "\n", + "///////////////// REMAINING CONTENT GENERATED BY embed_js.py /////////////////\n", + "// prettier-ignore\n", + "var _JSXTOOLS_RESIZE_OBSERVER=function(A){var t,i=new WeakMap,n=new WeakMap,a=new WeakMap,r=new WeakMap,o=new Set;function s(e){if(!(this instanceof s))throw new TypeError(\"Constructor requires 'new' operator\");i.set(this,e)}function h(){throw new TypeError(\"Function is not a constructor\")}function c(e,t,i,n){e=0 in arguments?Number(arguments[0]):0,t=1 in arguments?Number(arguments[1]):0,i=2 in arguments?Number(arguments[2]):0,n=3 in arguments?Number(arguments[3]):0,this.right=(this.x=this.left=e)+(this.width=i),this.bottom=(this.y=this.top=t)+(this.height=n),Object.freeze(this)}function d(){t=requestAnimationFrame(d);var s=new WeakMap,p=new Set;o.forEach((function(t){r.get(t).forEach((function(i){var r=t instanceof window.SVGElement,o=a.get(t),d=r?0:parseFloat(o.paddingTop),f=r?0:parseFloat(o.paddingRight),l=r?0:parseFloat(o.paddingBottom),u=r?0:parseFloat(o.paddingLeft),g=r?0:parseFloat(o.borderTopWidth),m=r?0:parseFloat(o.borderRightWidth),w=r?0:parseFloat(o.borderBottomWidth),b=u+f,F=d+l,v=(r?0:parseFloat(o.borderLeftWidth))+m,W=g+w,y=r?0:t.offsetHeight-W-t.clientHeight,E=r?0:t.offsetWidth-v-t.clientWidth,R=b+v,z=F+W,M=r?t.width:parseFloat(o.width)-R-E,O=r?t.height:parseFloat(o.height)-z-y;if(n.has(t)){var k=n.get(t);if(k[0]===M&&k[1]===O)return}n.set(t,[M,O]);var S=Object.create(h.prototype);S.target=t,S.contentRect=new c(u,d,M,O),s.has(i)||(s.set(i,[]),p.add(i)),s.get(i).push(S)}))})),p.forEach((function(e){i.get(e).call(e,s.get(e),e)}))}return s.prototype.observe=function(i){if(i instanceof window.Element){r.has(i)||(r.set(i,new Set),o.add(i),a.set(i,window.getComputedStyle(i)));var n=r.get(i);n.has(this)||n.add(this),cancelAnimationFrame(t),t=requestAnimationFrame(d)}},s.prototype.unobserve=function(i){if(i instanceof window.Element&&r.has(i)){var n=r.get(i);n.has(this)&&(n.delete(this),n.size||(r.delete(i),o.delete(i))),n.size||r.delete(i),o.size||cancelAnimationFrame(t)}},A.DOMRectReadOnly=c,A.ResizeObserver=s,A.ResizeObserverEntry=h,A}; // eslint-disable-line\n", + "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Left button pans, Right button zooms\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\\nx/y fixes axis\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n", + "\n", + "mpl.extensions = [\"eps\", \"jpeg\", \"pgf\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n", + "\n", + "mpl.default_extension = \"png\";/* global mpl */\n", + "\n", + "var comm_websocket_adapter = function (comm) {\n", + " // Create a \"websocket\"-like object which calls the given IPython comm\n", + " // object with the appropriate methods. Currently this is a non binary\n", + " // socket, so there is still some room for performance tuning.\n", + " var ws = {};\n", + "\n", + " ws.binaryType = comm.kernel.ws.binaryType;\n", + " ws.readyState = comm.kernel.ws.readyState;\n", + " function updateReadyState(_event) {\n", + " if (comm.kernel.ws) {\n", + " ws.readyState = comm.kernel.ws.readyState;\n", + " } else {\n", + " ws.readyState = 3; // Closed state.\n", + " }\n", + " }\n", + " comm.kernel.ws.addEventListener('open', updateReadyState);\n", + " comm.kernel.ws.addEventListener('close', updateReadyState);\n", + " comm.kernel.ws.addEventListener('error', updateReadyState);\n", + "\n", + " ws.close = function () {\n", + " comm.close();\n", + " };\n", + " ws.send = function (m) {\n", + " //console.log('sending', m);\n", + " comm.send(m);\n", + " };\n", + " // Register the callback with on_msg.\n", + " comm.on_msg(function (msg) {\n", + " //console.log('receiving', msg['content']['data'], msg);\n", + " var data = msg['content']['data'];\n", + " if (data['blob'] !== undefined) {\n", + " data = {\n", + " data: new Blob(msg['buffers'], { type: data['blob'] }),\n", + " };\n", + " }\n", + " // Pass the mpl event to the overridden (by mpl) onmessage function.\n", + " ws.onmessage(data);\n", + " });\n", + " return ws;\n", + "};\n", + "\n", + "mpl.mpl_figure_comm = function (comm, msg) {\n", + " // This is the function which gets called when the mpl process\n", + " // starts-up an IPython Comm through the \"matplotlib\" channel.\n", + "\n", + " var id = msg.content.data.id;\n", + " // Get hold of the div created by the display call when the Comm\n", + " // socket was opened in Python.\n", + " var element = document.getElementById(id);\n", + " var ws_proxy = comm_websocket_adapter(comm);\n", + "\n", + " function ondownload(figure, _format) {\n", + " window.open(figure.canvas.toDataURL());\n", + " }\n", + "\n", + " var fig = new mpl.figure(id, ws_proxy, ondownload, element);\n", + "\n", + " // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n", + " // web socket which is closed, not our websocket->open comm proxy.\n", + " ws_proxy.onopen();\n", + "\n", + " fig.parent_element = element;\n", + " fig.cell_info = mpl.find_output_cell(\"
\");\n", + " if (!fig.cell_info) {\n", + " console.error('Failed to find cell for figure', id, fig);\n", + " return;\n", + " }\n", + " fig.cell_info[0].output_area.element.on(\n", + " 'cleared',\n", + " { fig: fig },\n", + " fig._remove_fig_handler\n", + " );\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_close = function (fig, msg) {\n", + " var width = fig.canvas.width / fig.ratio;\n", + " fig.cell_info[0].output_area.element.off(\n", + " 'cleared',\n", + " fig._remove_fig_handler\n", + " );\n", + " fig.resizeObserverInstance.unobserve(fig.canvas_div);\n", + "\n", + " // Update the output cell to use the data from the current canvas.\n", + " fig.push_to_output();\n", + " var dataURL = fig.canvas.toDataURL();\n", + " // Re-enable the keyboard manager in IPython - without this line, in FF,\n", + " // the notebook keyboard shortcuts fail.\n", + " IPython.keyboard_manager.enable();\n", + " fig.parent_element.innerHTML =\n", + " '';\n", + " fig.close_ws(fig, msg);\n", + "};\n", + "\n", + "mpl.figure.prototype.close_ws = function (fig, msg) {\n", + " fig.send_message('closing', msg);\n", + " // fig.ws.close()\n", + "};\n", + "\n", + "mpl.figure.prototype.push_to_output = function (_remove_interactive) {\n", + " // Turn the data on the canvas into data in the output cell.\n", + " var width = this.canvas.width / this.ratio;\n", + " var dataURL = this.canvas.toDataURL();\n", + " this.cell_info[1]['text/html'] =\n", + " '';\n", + "};\n", + "\n", + "mpl.figure.prototype.updated_canvas_event = function () {\n", + " // Tell IPython that the notebook contents must change.\n", + " IPython.notebook.set_dirty(true);\n", + " this.send_message('ack', {});\n", + " var fig = this;\n", + " // Wait a second, then push the new image to the DOM so\n", + " // that it is saved nicely (might be nice to debounce this).\n", + " setTimeout(function () {\n", + " fig.push_to_output();\n", + " }, 1000);\n", + "};\n", + "\n", + "mpl.figure.prototype._init_toolbar = function () {\n", + " var fig = this;\n", + "\n", + " var toolbar = document.createElement('div');\n", + " toolbar.classList = 'btn-toolbar';\n", + " this.root.appendChild(toolbar);\n", + "\n", + " function on_click_closure(name) {\n", + " return function (_event) {\n", + " return fig.toolbar_button_onclick(name);\n", + " };\n", + " }\n", + "\n", + " function on_mouseover_closure(tooltip) {\n", + " return function (event) {\n", + " if (!event.currentTarget.disabled) {\n", + " return fig.toolbar_button_onmouseover(tooltip);\n", + " }\n", + " };\n", + " }\n", + "\n", + " fig.buttons = {};\n", + " var buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'btn-group';\n", + " var button;\n", + " for (var toolbar_ind in mpl.toolbar_items) {\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) {\n", + " /* Instead of a spacer, we start a new button group. */\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + " buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'btn-group';\n", + " continue;\n", + " }\n", + "\n", + " button = fig.buttons[name] = document.createElement('button');\n", + " button.classList = 'btn btn-default';\n", + " button.href = '#';\n", + " button.title = name;\n", + " button.innerHTML = '';\n", + " button.addEventListener('click', on_click_closure(method_name));\n", + " button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n", + " buttonGroup.appendChild(button);\n", + " }\n", + "\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + "\n", + " // Add the status bar.\n", + " var status_bar = document.createElement('span');\n", + " status_bar.classList = 'mpl-message pull-right';\n", + " toolbar.appendChild(status_bar);\n", + " this.message = status_bar;\n", + "\n", + " // Add the close button to the window.\n", + " var buttongrp = document.createElement('div');\n", + " buttongrp.classList = 'btn-group inline pull-right';\n", + " button = document.createElement('button');\n", + " button.classList = 'btn btn-mini btn-primary';\n", + " button.href = '#';\n", + " button.title = 'Stop Interaction';\n", + " button.innerHTML = '';\n", + " button.addEventListener('click', function (_evt) {\n", + " fig.handle_close(fig, {});\n", + " });\n", + " button.addEventListener(\n", + " 'mouseover',\n", + " on_mouseover_closure('Stop Interaction')\n", + " );\n", + " buttongrp.appendChild(button);\n", + " var titlebar = this.root.querySelector('.ui-dialog-titlebar');\n", + " titlebar.insertBefore(buttongrp, titlebar.firstChild);\n", + "};\n", + "\n", + "mpl.figure.prototype._remove_fig_handler = function (event) {\n", + " var fig = event.data.fig;\n", + " if (event.target !== this) {\n", + " // Ignore bubbled events from children.\n", + " return;\n", + " }\n", + " fig.close_ws(fig, {});\n", + "};\n", + "\n", + "mpl.figure.prototype._root_extra_style = function (el) {\n", + " el.style.boxSizing = 'content-box'; // override notebook setting of border-box.\n", + "};\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function (el) {\n", + " // this is important to make the div 'focusable\n", + " el.setAttribute('tabindex', 0);\n", + " // reach out to IPython and tell the keyboard manager to turn it's self\n", + " // off when our div gets focus\n", + "\n", + " // location in version 3\n", + " if (IPython.notebook.keyboard_manager) {\n", + " IPython.notebook.keyboard_manager.register_events(el);\n", + " } else {\n", + " // location in version 2\n", + " IPython.keyboard_manager.register_events(el);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype._key_event_extra = function (event, _name) {\n", + " // Check for shift+enter\n", + " if (event.shiftKey && event.which === 13) {\n", + " this.canvas_div.blur();\n", + " // select the cell after this one\n", + " var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n", + " IPython.notebook.select(index + 1);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_save = function (fig, _msg) {\n", + " fig.ondownload(fig, null);\n", + "};\n", + "\n", + "mpl.find_output_cell = function (html_output) {\n", + " // Return the cell and output element which can be found *uniquely* in the notebook.\n", + " // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n", + " // IPython event is triggered only after the cells have been serialised, which for\n", + " // our purposes (turning an active figure into a static one), is too late.\n", + " var cells = IPython.notebook.get_cells();\n", + " var ncells = cells.length;\n", + " for (var i = 0; i < ncells; i++) {\n", + " var cell = cells[i];\n", + " if (cell.cell_type === 'code') {\n", + " for (var j = 0; j < cell.output_area.outputs.length; j++) {\n", + " var data = cell.output_area.outputs[j];\n", + " if (data.data) {\n", + " // IPython >= 3 moved mimebundle to data attribute of output\n", + " data = data.data;\n", + " }\n", + " if (data['text/html'] === html_output) {\n", + " return [cell, data, j];\n", + " }\n", + " }\n", + " }\n", + " }\n", + "};\n", + "\n", + "// Register the function which deals with the matplotlib target/channel.\n", + "// The kernel may be null if the page has been refreshed.\n", + "if (IPython.notebook.kernel !== null) {\n", + " IPython.notebook.kernel.comm_manager.register_target(\n", + " 'matplotlib',\n", + " mpl.mpl_figure_comm\n", + " );\n", + "}\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "+-----------+------------+------------+------------+\n", + "| seq_num | time | motor1 | noisy_det |\n", + "+-----------+------------+------------+------------+\n", + "| 1 | 00:27:29.7 | -5.000 | 0.017 |\n", + "| 2 | 00:27:29.7 | -3.571 | 0.021 |\n", + "| 3 | 00:27:29.7 | -2.143 | 0.115 |\n", + "| 4 | 00:27:29.7 | -0.714 | 0.658 |\n", + "| 5 | 00:27:29.7 | 0.714 | 0.697 |\n", + "| 6 | 00:27:29.7 | 2.143 | 0.155 |\n", + "| 7 | 00:27:29.7 | 3.571 | -0.086 |\n", + "| 8 | 00:27:29.7 | 5.000 | 0.089 |\n", + "+-----------+------------+------------+------------+\n", + "generator scan ['a6ea8a82'] (scan num: 1)\n", + "\n", + "\n", + "\n", + "no handling for table row 2: OrderedDict([('Scan Type', 'other_scan'), ('sx', 5.07), ('sy', 8.3), ('Thickness', 0), ('Sample Name', 'Water Blank'), ('remarks', 'deionized'), ('code number', None)])\n", + "no handling for table row 3: OrderedDict([('Scan Type', 'this will be ignored (and also the next blank row will be ignored)'), ('sx', None), ('sy', None), ('Thickness', None), ('Sample Name', None), ('remarks', None), ('code number', None)])\n" + ] + }, + { + "data": { + "text/plain": [ + "('a6ea8a82-aff2-4621-a197-1a3bca524e51',)" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "RE(Excel_plan(\"sample_example.xlsx\"))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Show what was collected by accessing the most recent run from the catalog." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n", + "
\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Start
code_number None
detectors
noisy_det
Excel_file sample_example.xlsx
hints \n", + " \n", + " \n", + " \n", + " \n", + "
dimensions
[['motor1'], 'primary']
motor2 5.07
motor3 8.3
motors
motor1
num_intervals 7
num_points 8
original_keys \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
code_number code number
remarks remarks
Sample_Name Sample Name
Scan_Type Scan Type
sx sx
sy sy
Thickness Thickness
plan_args \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
args
SynAxis(prefix='', name='motor1', read_attrs=['readback', 'setpoint'], configuration_attrs=['velocity', 'acceleration'])
-5
5
detectors
SynGauss(prefix='', name='noisy_det', read_attrs=['val'], configuration_attrs=['Imax', 'center', 'sigma', 'noise', 'noise_multiplier'])
num 8
per_step None
plan_name scan
plan_pattern inner_product
plan_pattern_args \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
args
SynAxis(prefix='', name='motor1', read_attrs=['readback', 'setpoint'], configuration_attrs=['velocity', 'acceleration'])
-5
5
num 8
plan_pattern_module bluesky.plan_patterns
plan_type generator
remarks deionized
Sample_Name Water Blank
scan_id 1
Scan_Type step_scan
shutter open
sx 5.07
sy 8.3
table_row 1
Thickness 0
time a second ago (2022-07-06T00:27:29.644067)
uid a6ea8a82-aff2-4621-a197-1a3bca524e51
versions \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
bluesky 1.8.3
ophyd 1.6.4
xl_file sample_example.xlsx
\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Stop
exit_status success
num_events \n", + " \n", + " \n", + " \n", + " \n", + "
primary 8
reason
run_start a6ea8a82-aff2-4621-a197-1a3bca524e51
time a second ago (2022-07-06T00:27:29.781108)
uid 128c5ecf-2c21-497e-b7f1-6b74614bfb45
\n", + " \n", + " \n", + " \n", + "
Descriptors
\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
primary
configuration \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
motor1 \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
data \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
motor1_acceleration 1
motor1_velocity 1
data_keys \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
motor1_acceleration \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
dtype integer
shape
source SIM:motor1_acceleration
motor1_velocity \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
dtype integer
shape
source SIM:motor1_velocity
timestamps \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
motor1_acceleration 1657085249.0746343
motor1_velocity 1657085249.0746002
noisy_det \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
data \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
noisy_det_center 0
noisy_det_Imax 1
noisy_det_noise uniform
noisy_det_noise_multiplier 0.1
noisy_det_sigma 0.9
data_keys \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
noisy_det_center \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
dtype integer
shape
source SIM:noisy_det_center
noisy_det_Imax \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
dtype integer
shape
source SIM:noisy_det_Imax
noisy_det_noise \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
dtype integer
enum_strs
none
poisson
uniform
shape
source SIM:noisy_det_noise
noisy_det_noise_multiplier \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
dtype number
shape
source SIM:noisy_det_noise_multiplier
noisy_det_sigma \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
dtype number
shape
source SIM:noisy_det_sigma
timestamps \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
noisy_det_center 1657085249.298179
noisy_det_Imax 1657085249.2981858
noisy_det_noise 1657085249.298202
noisy_det_noise_multiplier 1657085249.2982059
noisy_det_sigma 1657085249.2981968
data_keys \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
motor1 \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
dtype number
object_name motor1
precision 3
shape
source SIM:motor1
motor1_setpoint \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
dtype number
object_name motor1
precision 3
shape
source SIM:motor1_setpoint
noisy_det \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
dtype number
object_name noisy_det
precision 3
shape
source SIM:noisy_det
hints \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
motor1 \n", + " \n", + " \n", + " \n", + " \n", + "
fields
motor1
noisy_det \n", + " \n", + " \n", + " \n", + " \n", + "
fields
noisy_det
name primary
object_keys \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
motor1
motor1
motor1_setpoint
noisy_det
noisy_det
run_start a6ea8a82-aff2-4621-a197-1a3bca524e51
time a second ago (2022-07-06T00:27:29.654973)
uid 9fc2e538-dfde-48a0-a70a-b6ad3ac3c493
\n", + "
" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "run = cat[-1]\n", + "run" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/docs/source/resources/demo_nscan.ipynb b/docs/source/examples/_nscan.ipynb similarity index 73% rename from docs/source/resources/demo_nscan.ipynb rename to docs/source/examples/_nscan.ipynb index c6e60b2e9..ef5adaf9d 100644 --- a/docs/source/resources/demo_nscan.ipynb +++ b/docs/source/examples/_nscan.ipynb @@ -6,9 +6,15 @@ "source": [ "# Demonstrate ``nscan``\n", "\n", - "In this example, we demonstrate the `apstools.plans.nscan()` plan. The `nscan()` plan scans over ``n`` variables moved together, each in equally spaced steps.\n", + "In this example, we demonstrate the `apstools.plans.nscan()` plan. The \n", + "`nscan()` plan is used to scan two or more axes together, each in equally\n", + "spaced steps, such as a $\\theta$ - $2\\theta$ powder diffractometer scan.\n", "\n", - "Here, we scan two motors together, each in equally spaced steps. We use an [`swait`](https://htmlpreview.github.io/?https://raw.githubusercontent.com/epics-modules/calc/R3-6-1/documentation/swaitRecord.html) record (part of the *userCalc* support from synApps) as a detector. We configure the `swait` record with a calculation (from `apstools.plans.swait_setup_random_number()`) that computes a noisy (random number) signal. \n", + "Here, we scan two motors together, each in equally spaced steps. We use an\n", + "[`swait`](https://htmlpreview.github.io/?https://raw.githubusercontent.com/epics-modules/calc/R3-6-1/documentation/swaitRecord.html)\n", + "record (part of the *userCalc* support from synApps) as a detector. We\n", + "configure the `swait` record with a calculation (from\n", + "`apstools.plans.swait_setup_random_number()`) that computes a noisy (random number) signal. \n", "\n", "For this demo, we do not need the databroker since we do not plan to review any of this data after collection. We'll display the data during the scan using the *LiveTable()* code." ] @@ -90,14 +96,14 @@ "+-----------+------------+------------+------------+\n", "| seq_num | time | m1 | m2 |\n", "+-----------+------------+------------+------------+\n", - "| 1 | 16:06:11.1 | 2.00000 | 4.00000 |\n", - "| 2 | 16:06:12.1 | 1.60000 | 3.20000 |\n", - "| 3 | 16:06:13.2 | 1.20000 | 2.40000 |\n", - "| 4 | 16:06:14.3 | 0.80000 | 1.60000 |\n", - "| 5 | 16:06:15.5 | 0.40000 | 0.80000 |\n", - "| 6 | 16:06:16.6 | 0.00000 | 0.00000 |\n", + "| 1 | 00:22:27.5 | 2.00000 | 4.00000 |\n", + "| 2 | 00:22:28.6 | 1.60000 | 3.20000 |\n", + "| 3 | 00:22:29.7 | 1.20000 | 2.40000 |\n", + "| 4 | 00:22:30.8 | 0.80000 | 1.60000 |\n", + "| 5 | 00:22:31.9 | 0.40000 | 0.80000 |\n", + "| 6 | 00:22:33.0 | 0.00000 | 0.00000 |\n", "+-----------+------------+------------+------------+\n", - "generator nscan ['3b04effb'] (scan num: 1)\n", + "generator nscan ['fcb9a1ad'] (scan num: 1)\n", "\n", "\n" ] @@ -105,7 +111,7 @@ { "data": { "text/plain": [ - "('3b04effb-2d5e-42b2-8568-59846e689347',)" + "('fcb9a1ad-1901-4955-96df-f2079f4edd1c',)" ] }, "execution_count": 4, @@ -123,7 +129,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -137,9 +143,9 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.7" + "version": "3.9.13" } }, "nbformat": 4, - "nbformat_minor": 2 + "nbformat_minor": 4 } diff --git a/docs/source/examples/_nscan.rst b/docs/source/examples/_nscan.rst deleted file mode 100644 index 1f87f79be..000000000 --- a/docs/source/examples/_nscan.rst +++ /dev/null @@ -1,9 +0,0 @@ -.. _example_nscan: - -Example: the :func:`~apstools.plans.nscan()` plan -================================================== - -We'll use a Jupyter notebook to demonstrate the :func:`~apstools.plans.nscan()` plan. -An *nscan* is used to scan two or more axes together, -such as a :math:`\theta`-:math:`2\theta` diffractometer scan. -Follow here: https://github.com/BCDA-APS/apstools/blob/master/docs/source/resources/demo_nscan.ipynb diff --git a/docs/source/examples/_specfile.rst b/docs/source/examples/_specfile.rst deleted file mode 100644 index 34b3ad864..000000000 --- a/docs/source/examples/_specfile.rst +++ /dev/null @@ -1,9 +0,0 @@ -.. _example_specfile: - -Example: write SPEC data file -========================================== - -We'll use the ``demo_specfile_example`` Jupyter notebook to demonstrate -how to write one or more scans to a SPEC data file. - -https://github.com/BCDA-APS/apstools/blob/master/docs/source/resources/demo_specfile_example.ipynb diff --git a/docs/source/examples/_specfile_example.ipynb b/docs/source/examples/_specfile_example.ipynb new file mode 100644 index 000000000..aea471438 --- /dev/null +++ b/docs/source/examples/_specfile_example.ipynb @@ -0,0 +1,961 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Example: Output scan(s) to a SPEC data file." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "One of the common concerns is how to access data from bluesky's database. The standard way is to replay the document stream from each of the scans through a bluesky callback that writes the data to the desired file format. Here, we write data to the SPEC file format.\n", + "\n", + "## Setup\n", + "\n", + "First, we must setup a bluesky session. We create a [temporary catalog](https://blueskyproject.io/databroker/how-to/file-backed-catalog.html?highlight=temporary#temporary-catalog) to store the data from this notebook's data collection." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "%matplotlib inline\n", + "import matplotlib.pyplot as plt\n", + "plt.ion()\n", + "\n", + "from bluesky import RunEngine, plans as bp\n", + "from bluesky.callbacks.best_effort import BestEffortCallback\n", + "import databroker\n", + "\n", + "cat = databroker.temp()\n", + "RE = RunEngine({})\n", + "RE.subscribe(cat.v1.insert)\n", + "RE.subscribe(BestEffortCallback())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We need to create some scan data. We'll use Gaussian peak profile calculated in a _userCalc_ (`swait` record) and a motor from our EPICS IOC simulator `gp:`. Initialize the peak computation with some randomized parameters. The peak will be at some `motor` position between -1 .. +1. Print the actual values for our reference later on." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "calc = D*(0.95+E*RNDM)/exp(((A-b)/c)^2)\n", + "A: x = gp:m1.RBV\n", + "B: center = -0.14563824029176498\n", + "C: width = 0.03019405564691883\n", + "D: scale = 100701.62331973032\n", + "E: noise= 0.049574955866745055\n" + ] + } + ], + "source": [ + "from apstools.devices import UserCalcN, setup_gaussian_swait\n", + "import numpy as np\n", + "from ophyd import EpicsMotor, EpicsSignal\n", + "\n", + "IOC = \"gp:\"\n", + "motor = EpicsMotor(f\"{IOC}m1\", name=\"motor\")\n", + "swait = UserCalcN(f\"{IOC}userCalc1\", name=\"swait\")\n", + "det = EpicsSignal(swait.calculated_value.pvname, name=\"det\")\n", + "\n", + "motor.wait_for_connection()\n", + "swait.wait_for_connection()\n", + "det.wait_for_connection()\n", + "\n", + "setup_gaussian_swait(\n", + " swait, motor.user_readback, \n", + " center=-0.5 + np.random.uniform(), \n", + " width=0.01 + 0.2*np.random.uniform(), \n", + " noise=0.05*(.95 + .1*np.random.uniform()),\n", + " scale=100_000*(.95 + .1*np.random.uniform()), \n", + " )\n", + "\n", + "print(f\"calc = {swait.calculation.get()}\")\n", + "print(f\"A: x = {swait.channels.A.input_pv.get()}\")\n", + "print(f\"B: center = {swait.channels.B.input_value.get()}\")\n", + "print(f\"C: width = {swait.channels.C.input_value.get()}\")\n", + "print(f\"D: scale = {swait.channels.D.input_value.get()}\")\n", + "print(f\"E: noise= {swait.channels.E.input_value.get()}\")\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Finally, run the scan with the RunEngine:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "\n", + "Transient Scan ID: 1 Time: 2022-07-06 00:48:02\n", + "Persistent Unique Scan ID: '7ab128a0-c0ef-4f41-a980-c5641e00ce97'\n", + "New stream: 'primary'\n", + "+-----------+------------+------------+------------+\n", + "| seq_num | time | motor | det |\n", + "+-----------+------------+------------+------------+\n", + "| 1 | 00:48:03.3 | -1.20000 | 0.00000 |\n", + "| 2 | 00:48:03.5 | -1.14000 | 0.00000 |\n", + "| 3 | 00:48:03.8 | -1.08000 | 0.00000 |\n", + "| 4 | 00:48:04.1 | -1.02000 | 0.00000 |\n", + "| 5 | 00:48:04.4 | -0.96000 | 0.00000 |\n", + "| 6 | 00:48:04.8 | -0.90000 | 0.00000 |\n", + "| 7 | 00:48:05.0 | -0.84000 | 0.00000 |\n", + "| 8 | 00:48:05.4 | -0.78000 | 0.00000 |\n", + "| 9 | 00:48:05.7 | -0.72000 | 0.00000 |\n", + "| 10 | 00:48:06.0 | -0.66000 | 0.00000 |\n", + "| 11 | 00:48:06.3 | -0.60000 | 0.00000 |\n", + "| 12 | 00:48:06.6 | -0.54000 | 0.00000 |\n", + "| 13 | 00:48:06.9 | -0.48000 | 0.00000 |\n", + "| 14 | 00:48:07.2 | -0.42000 | 0.00000 |\n", + "| 15 | 00:48:07.5 | -0.36000 | 0.00000 |\n", + "| 16 | 00:48:07.8 | -0.30000 | 0.00000 |\n", + "| 17 | 00:48:08.1 | -0.24000 | 5.60768 |\n", + "| 18 | 00:48:08.4 | -0.18000 | 27463.06109 |\n", + "| 19 | 00:48:08.7 | -0.12000 | 48682.85376 |\n", + "| 20 | 00:48:09.0 | -0.06000 | 31.30450 |\n", + "| 21 | 00:48:09.3 | 0.00000 | 0.00001 |\n", + "| 22 | 00:48:09.6 | 0.06000 | 0.00000 |\n", + "| 23 | 00:48:09.9 | 0.12000 | 0.00000 |\n", + "| 24 | 00:48:10.2 | 0.18000 | 0.00000 |\n", + "| 25 | 00:48:10.5 | 0.24000 | 0.00000 |\n", + "| 26 | 00:48:10.8 | 0.30000 | 0.00000 |\n", + "| 27 | 00:48:11.1 | 0.36000 | 0.00000 |\n", + "| 28 | 00:48:11.4 | 0.42000 | 0.00000 |\n", + "| 29 | 00:48:11.7 | 0.48000 | 0.00000 |\n", + "| 30 | 00:48:12.0 | 0.54000 | 0.00000 |\n", + "| 31 | 00:48:12.3 | 0.60000 | 0.00000 |\n", + "| 32 | 00:48:12.6 | 0.66000 | 0.00000 |\n", + "| 33 | 00:48:12.9 | 0.72000 | 0.00000 |\n", + "| 34 | 00:48:13.2 | 0.78000 | 0.00000 |\n", + "| 35 | 00:48:13.5 | 0.84000 | 0.00000 |\n", + "| 36 | 00:48:13.8 | 0.90000 | 0.00000 |\n", + "| 37 | 00:48:14.1 | 0.96000 | 0.00000 |\n", + "| 38 | 00:48:14.4 | 1.02000 | 0.00000 |\n", + "| 39 | 00:48:14.7 | 1.08000 | 0.00000 |\n", + "| 40 | 00:48:15.0 | 1.14000 | 0.00000 |\n", + "| 41 | 00:48:15.3 | 1.20000 | 0.00000 |\n", + "+-----------+------------+------------+------------+\n", + "generator scan ['7ab128a0'] (scan num: 1)\n", + "\n", + "\n", + "\n" + ] + }, + { + "data": { + "text/plain": [ + "('7ab128a0-c0ef-4f41-a980-c5641e00ce97',)" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "RE(bp.scan([det], motor, -1.2, 1.2, 41))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## get the most recent scan, by steps\n", + "\n", + "The databroker instance, `cat`, provides access to its scans by several means. One way is to consider `cat` as a list and retrieve the last item from the list. This will return a *run*, the common reference to be used.\n", + "\n", + "For this first example, we'll work through the steps one by one. The `cat.v2` interface is the easiest to use (at this writing)." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "BlueskyRun\n", + " uid='7ab128a0-c0ef-4f41-a980-c5641e00ce97'\n", + " exit_status='success'\n", + " 2022-07-06 00:48:02.921 -- 2022-07-06 00:48:15.351\n", + " Streams:\n", + " * primary\n" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "run = cat.v2[-1]\n", + "run" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As shown, the run has one data stream, name `primary`. The databroker provides a simple table view of this run:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
<xarray.Dataset>\n",
+       "Dimensions:              (time: 41)\n",
+       "Coordinates:\n",
+       "  * time                 (time) float64 1.657e+09 1.657e+09 ... 1.657e+09\n",
+       "Data variables:\n",
+       "    motor                (time) float64 -1.2 -1.14 -1.08 -1.02 ... 1.08 1.14 1.2\n",
+       "    motor_user_setpoint  (time) float64 -1.2 -1.14 -1.08 -1.02 ... 1.08 1.14 1.2\n",
+       "    det                  (time) float64 0.0 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0
" + ], + "text/plain": [ + "\n", + "Dimensions: (time: 41)\n", + "Coordinates:\n", + " * time (time) float64 1.657e+09 1.657e+09 ... 1.657e+09\n", + "Data variables:\n", + " motor (time) float64 -1.2 -1.14 -1.08 -1.02 ... 1.08 1.14 1.2\n", + " motor_user_setpoint (time) float64 -1.2 -1.14 -1.08 -1.02 ... 1.08 1.14 1.2\n", + " det (time) float64 0.0 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "run.primary.read()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Support function: `specfile_example()`\n", + "\n", + "We need to call the `apstools.callbacks.SpecWriterCallback()` callback with the run's documents.\n", + "\n", + "Here, `specfile_example()` is a support function that can be used with one or more runs to create a SPEC data file (one Bluesky run will become one SPEC scan in the same file)." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "from apstools.callbacks import SpecWriterCallback\n", + "from databroker._drivers.mongo_normalized import BlueskyMongoCatalog\n", + "from databroker._drivers.msgpack import BlueskyMsgpackCatalog\n", + "import warnings\n", + "\n", + "DEMO_SPEC_FILE = \"test_specdata.txt\"\n", + "\n", + "def specfile_example(runs, filename=DEMO_SPEC_FILE):\n", + " \"\"\"write one or more headers (scans) to a SPEC data file\"\"\"\n", + " if isinstance(runs, databroker.core.BlueskyRun):\n", + " runs = [runs]\n", + " if not isinstance(runs, (list, BlueskyMsgpackCatalog, BlueskyMongoCatalog)):\n", + " raise TypeError(\"Must give run object or list of run objects.\")\n", + " if len(runs) == 0:\n", + " raise ValueError(\"Must provide one or more runs.\")\n", + "\n", + " specwriter = SpecWriterCallback(filename=filename, auto_write=True)\n", + " for uid in runs:\n", + " if isinstance(uid, str):\n", + " run = runs[uid]\n", + " else:\n", + " run = uid\n", + " if not isinstance(run, databroker.core.BlueskyRun):\n", + " warnings.warn(f\"Skipping {run=}, it is not a BlueskyRun object.\")\n", + " continue\n", + " # to get the raw document stream, need the v1 interface\n", + " h = run.catalog_object.v1[run.name] # header\n", + " for key, doc in h.db.get_documents(h):\n", + " specwriter.receiver(key, doc)\n", + " # lines = specwriter.prepare_scan_contents()\n", + " print(\"Look at SPEC data file: \" + specwriter.spec_filename)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Write the run as SPEC data" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's write it as a SPEC data file (namely: `spec1.dat`):" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Look at SPEC data file: spec1.dat\n" + ] + } + ], + "source": [ + "import os\n", + "\n", + "if os.path.exists(\"spec1.dat\"): # re-write the file\n", + " os.remove(\"spec1.dat\")\n", + "\n", + "specfile_example(run, filename=\"spec1.dat\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's view file `spec1.dat` from disk storage (using the [`pycat`](https://ipython.readthedocs.io/en/stable/interactive/magics.html?highlight=pycat#magic-pycat) IPython magic function):" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\u001b[0;31m#F spec1.dat\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m\u001b[0;31m#E 1657086495\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m\u001b[0;31m#D Wed Jul 06 00:48:15 2022\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m\u001b[0;31m#C Bluesky user = prjemian host = zap\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m\u001b[0;31m#O0 \u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m\u001b[0;31m#o0 \u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m\u001b[0;31m#S 1 scan(detectors=['det'], num=41, args='['motor', -1.2, 1.2]', per_step='None')\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m\u001b[0;31m#D Wed Jul 06 00:48:02 2022\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m\u001b[0;31m#C Wed Jul 06 00:48:02 2022. plan_type = generator\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m\u001b[0;31m#C Wed Jul 06 00:48:02 2022. uid = 7ab128a0-c0ef-4f41-a980-c5641e00ce97\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m\u001b[0;31m#MD uid = 7ab128a0-c0ef-4f41-a980-c5641e00ce97\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m\u001b[0;31m#MD detectors = ['det']\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m\u001b[0;31m#MD motors = ['motor']\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m\u001b[0;31m#MD num_intervals = 40\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m\u001b[0;31m#MD num_points = 41\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m\u001b[0;31m#MD plan_pattern = inner_product\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m\u001b[0;31m#MD plan_pattern_args = {'num': 41, 'args': [\"EpicsMotor(prefix='gp:m1', name='motor', settle_time=0.0, timeout=None, read_attrs=['user_readback', 'user_setpoint'], configuration_attrs=['user_offset', 'user_offset_dir', 'velocity', 'acceleration', 'motor_egu'])\", -1.2, 1.2]}\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m\u001b[0;31m#MD plan_pattern_module = bluesky.plan_patterns\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m\u001b[0;31m#MD versions = {'ophyd': '1.6.4', 'bluesky': '1.8.3'}\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m\u001b[0;31m#P0 \u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m\u001b[0;31m#N 5\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m\u001b[0;31m#L motor Epoch_float Epoch motor_user_setpoint det\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m1.2\u001b[0m \u001b[0;36m0.434417724609375\u001b[0m \u001b[0;36m0\u001b[0m \u001b[0;34m-\u001b[0m\u001b[0;36m1.2\u001b[0m \u001b[0;36m0.0\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m1.1400000000000001\u001b[0m \u001b[0;36m0.6748816967010498\u001b[0m \u001b[0;36m1\u001b[0m \u001b[0;34m-\u001b[0m\u001b[0;36m1.14\u001b[0m \u001b[0;36m0.0\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m1.08\u001b[0m \u001b[0;36m0.9758017063140869\u001b[0m \u001b[0;36m1\u001b[0m \u001b[0;34m-\u001b[0m\u001b[0;36m1.08\u001b[0m \u001b[0;36m0.0\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m1.02\u001b[0m \u001b[0;36m1.276907205581665\u001b[0m \u001b[0;36m1\u001b[0m \u001b[0;34m-\u001b[0m\u001b[0;36m1.02\u001b[0m \u001b[0;36m0.0\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m0.96\u001b[0m \u001b[0;36m1.577857255935669\u001b[0m \u001b[0;36m2\u001b[0m \u001b[0;34m-\u001b[0m\u001b[0;36m0.96\u001b[0m \u001b[0;36m0.0\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m0.9\u001b[0m \u001b[0;36m1.878566026687622\u001b[0m \u001b[0;36m2\u001b[0m \u001b[0;34m-\u001b[0m\u001b[0;36m0.8999999999999999\u001b[0m \u001b[0;36m8.15929807366173e-267\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m0.84\u001b[0m \u001b[0;36m2.1784615516662598\u001b[0m \u001b[0;36m2\u001b[0m \u001b[0;34m-\u001b[0m\u001b[0;36m0.84\u001b[0m \u001b[0;36m2.1200557806170912e-225\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m0.78\u001b[0m \u001b[0;36m2.479753255844116\u001b[0m \u001b[0;36m2\u001b[0m \u001b[0;34m-\u001b[0m\u001b[0;36m0.78\u001b[0m \u001b[0;36m1.9915653174496467e-187\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m0.72\u001b[0m \u001b[0;36m2.7803871631622314\u001b[0m \u001b[0;36m3\u001b[0m \u001b[0;34m-\u001b[0m\u001b[0;36m0.72\u001b[0m \u001b[0;36m7.055567703307068e-153\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m0.66\u001b[0m \u001b[0;36m3.084782361984253\u001b[0m \u001b[0;36m3\u001b[0m \u001b[0;34m-\u001b[0m\u001b[0;36m0.6599999999999999\u001b[0m \u001b[0;36m9.228365714083866e-122\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m0.6\u001b[0m \u001b[0;36m3.38234806060791\u001b[0m \u001b[0;36m3\u001b[0m \u001b[0;34m-\u001b[0m\u001b[0;36m0.6\u001b[0m \u001b[0;36m4.4980315558799814e-94\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m0.54\u001b[0m \u001b[0;36m3.683393955230713\u001b[0m \u001b[0;36m4\u001b[0m \u001b[0;34m-\u001b[0m\u001b[0;36m0.54\u001b[0m \u001b[0;36m8.034623946332195e-70\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m0.48\u001b[0m \u001b[0;36m3.983243703842163\u001b[0m \u001b[0;36m4\u001b[0m \u001b[0;34m-\u001b[0m\u001b[0;36m0.48\u001b[0m \u001b[0;36m5.540840853688821e-49\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m0.42\u001b[0m \u001b[0;36m4.28428053855896\u001b[0m \u001b[0;36m4\u001b[0m \u001b[0;34m-\u001b[0m\u001b[0;36m0.41999999999999993\u001b[0m \u001b[0;36m1.3512822090579629e-31\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m0.36\u001b[0m \u001b[0;36m4.585167646408081\u001b[0m \u001b[0;36m5\u001b[0m \u001b[0;34m-\u001b[0m\u001b[0;36m0.36\u001b[0m \u001b[0;36m1.2489789444505279e-17\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m0.3\u001b[0m \u001b[0;36m4.885620355606079\u001b[0m \u001b[0;36m5\u001b[0m \u001b[0;34m-\u001b[0m\u001b[0;36m0.30000000000000004\u001b[0m \u001b[0;36m4.456540993798201e-07\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m0.24\u001b[0m \u001b[0;36m5.186397314071655\u001b[0m \u001b[0;36m5\u001b[0m \u001b[0;34m-\u001b[0m\u001b[0;36m0.24\u001b[0m \u001b[0;36m5.607678879044609\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m0.18\u001b[0m \u001b[0;36m5.481502294540405\u001b[0m \u001b[0;36m5\u001b[0m \u001b[0;34m-\u001b[0m\u001b[0;36m0.17999999999999994\u001b[0m \u001b[0;36m27463.061094278986\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m0.12\u001b[0m \u001b[0;36m5.787712335586548\u001b[0m \u001b[0;36m6\u001b[0m \u001b[0;34m-\u001b[0m\u001b[0;36m0.11999999999999988\u001b[0m \u001b[0;36m48682.85376123283\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m0.06\u001b[0m \u001b[0;36m6.08820915222168\u001b[0m \u001b[0;36m6\u001b[0m \u001b[0;34m-\u001b[0m\u001b[0;36m0.06000000000000005\u001b[0m \u001b[0;36m31.30450080779168\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m\u001b[0;36m0.0\u001b[0m \u001b[0;36m6.389137268066406\u001b[0m \u001b[0;36m6\u001b[0m \u001b[0;36m0.0\u001b[0m \u001b[0;36m7.839397376237093e-06\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m\u001b[0;36m0.06\u001b[0m \u001b[0;36m6.690355062484741\u001b[0m \u001b[0;36m7\u001b[0m \u001b[0;36m0.06000000000000005\u001b[0m \u001b[0;36m7.136670527501498e-16\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m\u001b[0;36m0.12\u001b[0m \u001b[0;36m6.991316556930542\u001b[0m \u001b[0;36m7\u001b[0m \u001b[0;36m0.11999999999999988\u001b[0m \u001b[0;36m2.3310134905397063e-29\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m\u001b[0;36m0.18\u001b[0m \u001b[0;36m7.291990041732788\u001b[0m \u001b[0;36m7\u001b[0m \u001b[0;36m0.17999999999999994\u001b[0m \u001b[0;36m2.985214388500194e-46\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m\u001b[0;36m0.24\u001b[0m \u001b[0;36m7.593103885650635\u001b[0m \u001b[0;36m8\u001b[0m \u001b[0;36m0.24\u001b[0m \u001b[0;36m1.394367198136051e-66\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m\u001b[0;36m0.3\u001b[0m \u001b[0;36m7.893867492675781\u001b[0m \u001b[0;36m8\u001b[0m \u001b[0;36m0.30000000000000004\u001b[0m \u001b[0;36m2.415783879435812e-90\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m\u001b[0;36m0.36\u001b[0m \u001b[0;36m8.195511817932129\u001b[0m \u001b[0;36m8\u001b[0m \u001b[0;36m0.3600000000000001\u001b[0m \u001b[0;36m1.5472274009149628e-117\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m\u001b[0;36m0.42\u001b[0m \u001b[0;36m8.495795965194702\u001b[0m \u001b[0;36m8\u001b[0m \u001b[0;36m0.41999999999999993\u001b[0m \u001b[0;36m3.743870737982315e-148\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m\u001b[0;36m0.48\u001b[0m \u001b[0;36m8.796814680099487\u001b[0m \u001b[0;36m9\u001b[0m \u001b[0;36m0.48\u001b[0m \u001b[0;36m3.383359371335557e-182\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m\u001b[0;36m0.54\u001b[0m \u001b[0;36m9.097584009170532\u001b[0m \u001b[0;36m9\u001b[0m \u001b[0;36m0.54\u001b[0m \u001b[0;36m1.1425123138669685e-219\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m\u001b[0;36m0.6\u001b[0m \u001b[0;36m9.399116039276123\u001b[0m \u001b[0;36m9\u001b[0m \u001b[0;36m0.5999999999999999\u001b[0m \u001b[0;36m1.3792208368358542e-260\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m\u001b[0;36m0.66\u001b[0m \u001b[0;36m9.699814081192017\u001b[0m \u001b[0;36m10\u001b[0m \u001b[0;36m0.6599999999999999\u001b[0m \u001b[0;36m0.0\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m\u001b[0;36m0.72\u001b[0m \u001b[0;36m10.001182079315186\u001b[0m \u001b[0;36m10\u001b[0m \u001b[0;36m0.72\u001b[0m \u001b[0;36m0.0\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m\u001b[0;36m0.78\u001b[0m \u001b[0;36m10.301761150360107\u001b[0m \u001b[0;36m10\u001b[0m \u001b[0;36m0.78\u001b[0m \u001b[0;36m0.0\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m\u001b[0;36m0.84\u001b[0m \u001b[0;36m10.603094100952148\u001b[0m \u001b[0;36m11\u001b[0m \u001b[0;36m0.8400000000000001\u001b[0m \u001b[0;36m0.0\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m\u001b[0;36m0.9\u001b[0m \u001b[0;36m10.904242992401123\u001b[0m \u001b[0;36m11\u001b[0m \u001b[0;36m0.9000000000000001\u001b[0m \u001b[0;36m0.0\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m\u001b[0;36m0.96\u001b[0m \u001b[0;36m11.205401420593262\u001b[0m \u001b[0;36m11\u001b[0m \u001b[0;36m0.9600000000000002\u001b[0m \u001b[0;36m0.0\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m\u001b[0;36m1.02\u001b[0m \u001b[0;36m11.506523370742798\u001b[0m \u001b[0;36m12\u001b[0m \u001b[0;36m1.0199999999999998\u001b[0m \u001b[0;36m0.0\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m\u001b[0;36m1.08\u001b[0m \u001b[0;36m11.807251930236816\u001b[0m \u001b[0;36m12\u001b[0m \u001b[0;36m1.0799999999999998\u001b[0m \u001b[0;36m0.0\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m\u001b[0;36m1.1400000000000001\u001b[0m \u001b[0;36m12.102661371231079\u001b[0m \u001b[0;36m12\u001b[0m \u001b[0;36m1.14\u001b[0m \u001b[0;36m0.0\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m\u001b[0;36m1.2\u001b[0m \u001b[0;36m12.408974170684814\u001b[0m \u001b[0;36m12\u001b[0m \u001b[0;36m1.2\u001b[0m \u001b[0;36m0.0\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m\u001b[0;31m#C Wed Jul 06 00:48:15 2022. num_events_primary = 41\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m\u001b[0;31m#C Wed Jul 06 00:48:15 2022. exit_status = success\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "%pycat spec1.dat" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We see that the output of the `specfile_example()` command includes the content of the SPEC file. For the remaining examples, we'll skip this additional step to view the SPEC file contents from disk." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## a specific scan\n", + "\n", + "The `cat` object allows us to access scans by UUID (or any shorter version that remains unique in the database). We show an example but have commented it out since those runs do not exist in our temporary databroker catalog." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "# specfile_example(cat[\"37c188c0\"], filename=\"spec3.dat\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## specify a list of scans by UID\n", + "\n", + "Suppose we have a list of scans where we know the UID of each one, we can build a list of headers and write a SPEC data file with that list. Here, we have such a list of tuning scans. We show an example but have commented it out since those runs do not exist in our temporary databroker catalog." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "# runs = [cat[uid] for uid in \"957d83c c354fe37-e39f 42c\".split()]\n", + "# specfile_example(runs, filename=\"spec_tunes.dat\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Find specific plans within a range of dates\n", + "\n", + "The `cat` object allows for filtering arguments based on any keywords in the *start* document and also by time. Here, we filter between certain dates and also by `plan name`. The dates are specified in [ISO8601 format](https://www.iso.org/iso-8601-date-and-time-format.html) and can include precision beyond a millisecond. Here, we use the `v2` interface to do the searches. We show examples how to pick between a set of dates.\n", + "\n", + "Also, we write to the default data file: `test_specdata.txt`." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/prjemian/micromamba/envs/bluesky_2022_3/lib/python3.9/site-packages/databroker/queries.py:89: PytzUsageWarning: The zone attribute is specific to pytz's interface; please migrate to a new time zone provider. For more details on how to do so, see https://pytz-deprecation-shim.readthedocs.io/en/latest/migration.html\n", + " timezone = lz.zone\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "7ab128a0 2022-07-06 00:48:02.921515\n", + "Look at SPEC data file: test_specdata.txt\n" + ] + } + ], + "source": [ + "from databroker.queries import TimeRange\n", + "import datetime\n", + "\n", + "if os.path.exists(\"test_specdata.txt\"): # re-write the file\n", + " os.remove(\"test_specdata.txt\")\n", + "\n", + "query = {}\n", + "query.update(TimeRange(since=\"2019-02-19 17:00\"))\n", + "query.update(TimeRange(until=\"2032-02-19 17:11:30\"))\n", + "query.update(dict(plan_name=\"scan\"))\n", + "\n", + "runs = cat.v2.search(query)\n", + "for uid in runs:\n", + " run = runs[uid]\n", + " start_time = run.metadata[\"start\"][\"time\"]\n", + " isodate = datetime.datetime.fromtimestamp(start_time).isoformat(sep=\" \")\n", + " print(uid[:8], isodate)\n", + "specfile_example(runs, \"test_specdata.txt\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/docs/source/resources/demo_tuneaxis.ipynb b/docs/source/examples/_tuneaxis.ipynb similarity index 56% rename from docs/source/resources/demo_tuneaxis.ipynb rename to docs/source/examples/_tuneaxis.ipynb index 7add1ad82..138806e85 100644 --- a/docs/source/resources/demo_tuneaxis.ipynb +++ b/docs/source/examples/_tuneaxis.ipynb @@ -4,7 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Demonstrate ``TuneAxis``\n", + "# Demonstrate ``TuneAxis()``\n", "\n", "In this example, we demonstrate the `apstools.plans.TuneAxis()` plan. The `TuneAxis()` support may be used to align (a.k.a. *tune*) a signal against an axis.\n", "\n", @@ -34,7 +34,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Figure out which workstation we are running. The *mint-vm* host has a different IOC prefix." + "Set the IOC prefix." ] }, { @@ -108,27 +108,27 @@ "+-----------+------------+------------+------------+\n", "| seq_num | time | motor | det |\n", "+-----------+------------+------------+------------+\n", - "| 1 | 18:41:26.2 | -3.000 | 0.056 |\n", - "| 2 | 18:41:26.3 | -2.667 | 0.076 |\n", - "| 3 | 18:41:26.3 | -2.333 | 0.110 |\n", - "| 4 | 18:41:26.3 | -2.000 | 0.168 |\n", - "| 5 | 18:41:26.3 | -1.667 | 0.257 |\n", - "| 6 | 18:41:26.3 | -1.333 | 0.386 |\n", - "| 7 | 18:41:26.3 | -1.000 | 0.553 |\n", - "| 8 | 18:41:26.3 | -0.667 | 0.747 |\n", - "| 9 | 18:41:26.3 | -0.333 | 0.923 |\n", - "| 10 | 18:41:26.3 | 0.000 | 1.000 |\n", - "| 11 | 18:41:26.3 | 0.333 | 0.923 |\n", - "| 12 | 18:41:26.3 | 0.667 | 0.747 |\n", - "| 13 | 18:41:26.3 | 1.000 | 0.553 |\n", - "| 14 | 18:41:26.3 | 1.333 | 0.386 |\n", - "| 15 | 18:41:26.3 | 1.667 | 0.257 |\n", - "| 16 | 18:41:26.3 | 2.000 | 0.168 |\n", - "| 17 | 18:41:26.3 | 2.333 | 0.110 |\n", - "| 18 | 18:41:26.3 | 2.667 | 0.076 |\n", - "| 19 | 18:41:26.3 | 3.000 | 0.056 |\n", + "| 1 | 00:33:31.9 | -3.000 | 0.056 |\n", + "| 2 | 00:33:31.9 | -2.667 | 0.076 |\n", + "| 3 | 00:33:31.9 | -2.333 | 0.110 |\n", + "| 4 | 00:33:31.9 | -2.000 | 0.168 |\n", + "| 5 | 00:33:31.9 | -1.667 | 0.257 |\n", + "| 6 | 00:33:32.0 | -1.333 | 0.386 |\n", + "| 7 | 00:33:32.0 | -1.000 | 0.553 |\n", + "| 8 | 00:33:32.0 | -0.667 | 0.747 |\n", + "| 9 | 00:33:32.0 | -0.333 | 0.923 |\n", + "| 10 | 00:33:32.0 | 0.000 | 1.000 |\n", + "| 11 | 00:33:32.0 | 0.333 | 0.923 |\n", + "| 12 | 00:33:32.0 | 0.667 | 0.747 |\n", + "| 13 | 00:33:32.0 | 1.000 | 0.553 |\n", + "| 14 | 00:33:32.0 | 1.333 | 0.386 |\n", + "| 15 | 00:33:32.0 | 1.667 | 0.257 |\n", + "| 16 | 00:33:32.0 | 2.000 | 0.168 |\n", + "| 17 | 00:33:32.0 | 2.333 | 0.110 |\n", + "| 18 | 00:33:32.0 | 2.667 | 0.076 |\n", + "| 19 | 00:33:32.0 | 3.000 | 0.056 |\n", "+-----------+------------+------------+------------+\n", - "generator scan ['e6f7c881'] (scan num: 1)\n", + "generator scan ['f7d1a4f9'] (scan num: 1)\n", "\n", "\n" ] @@ -136,7 +136,7 @@ { "data": { "text/plain": [ - "('e6f7c881-da2c-48dc-8d0b-a34c59e14f0b',)" + "('f7d1a4f9-de81-4e9d-b253-28d7f6c0e5a8',)" ] }, "execution_count": 5, @@ -194,10 +194,10 @@ "output_type": "stream", "text": [ "spvoigt.scale = 100000.0\n", - "spvoigt.center = -1.1553241219392254\n", - "spvoigt.sigma = 0.04952451841481895\n", - "spvoigt.eta = 0.6177326684139124\n", - "spvoigt.bkg = 0.00028271101691823233\n" + "spvoigt.center = -1.3232789849401074\n", + "spvoigt.sigma = 0.025854531457453644\n", + "spvoigt.eta = 0.6200793404520202\n", + "spvoigt.bkg = 0.002625081504664745\n" ] } ], @@ -273,7 +273,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Multi-pass tune\n", + "## Multi-pass tune\n", "\n", "Execute multiple passes to refine the centroid determination.\n", "Each subsequent pass will reduce the width of the next scan by ``step_factor``." @@ -293,18 +293,18 @@ "+-----------+------------+------------+------------+\n", "| seq_num | time | m1 | spvoigt |\n", "+-----------+------------+------------+------------+\n", - "| 1 | 18:41:46.5 | -2.75000 | 87.793 |\n", - "| 2 | 18:41:47.0 | -2.47225 | 115.509 |\n", - "| 3 | 18:41:47.5 | -2.19450 | 168.255 |\n", - "| 4 | 18:41:48.0 | -1.91675 | 288.498 |\n", - "| 5 | 18:41:48.5 | -1.63900 | 669.190 |\n", - "| 6 | 18:41:49.0 | -1.36100 | 3420.444 |\n", - "| 7 | 18:41:49.5 | -1.08325 | 33097.710 |\n", - "| 8 | 18:41:50.1 | -0.80550 | 1242.005 |\n", - "| 9 | 18:41:50.6 | -0.52775 | 410.580 |\n", - "| 10 | 18:41:51.1 | -0.25000 | 212.575 |\n", + "| 1 | 00:33:33.6 | -2.75000 | 282.864 |\n", + "| 2 | 00:33:34.1 | -2.47000 | 294.014 |\n", + "| 3 | 00:33:34.6 | -2.19000 | 317.637 |\n", + "| 4 | 00:33:35.1 | -1.92000 | 378.697 |\n", + "| 5 | 00:33:35.6 | -1.64000 | 672.979 |\n", + "| 6 | 00:33:36.1 | -1.36000 | 34670.358 |\n", + "| 7 | 00:33:36.6 | -1.08000 | 955.031 |\n", + "| 8 | 00:33:37.1 | -0.81000 | 419.441 |\n", + "| 9 | 00:33:37.6 | -0.53000 | 328.305 |\n", + "| 10 | 00:33:38.1 | -0.25000 | 298.470 |\n", "+-----------+------------+------------+------------+\n", - "generator TuneAxis.multi_pass_tune ['59738fc9'] (scan num: 2)\n", + "generator TuneAxis.multi_pass_tune ['973d07fd'] (scan num: 2)\n", "\n", "\n", "PeakStats\n", @@ -313,16 +313,16 @@ "================ =================================\n", "x m1 \n", "y spvoigt \n", - "cen -1.0885316873365967 \n", - "com -1.1161270733159565 \n", - "fwhm 0.2983769532192523 \n", - "min [-2.75 87.79310955] \n", - "max [-1.08325000e+00 3.30977105e+04]\n", - "crossings [-1.23772016 -0.93934321] \n", + "cen -1.3594076832102968 \n", + "com -1.367257432898917 \n", + "fwhm 0.2843975895677082 \n", + "min [ -2.75 282.86449968] \n", + "max [-1.36000000e+00 3.46703584e+04]\n", + "crossings [-1.50160648 -1.21720889] \n", "tune_ok True \n", - "center -1.0885316873365967 \n", + "center -1.3594076832102968 \n", "initial_position -1.5 \n", - "final_position -1.0885316873365967 \n", + "final_position -1.3594076832102968 \n", "================ =================================\n", "\n", "\n", @@ -330,18 +330,18 @@ "+-----------+------------+------------+------------+\n", "| seq_num | time | m1 | spvoigt |\n", "+-----------+------------+------------+------------+\n", - "| 1 | 18:41:52.9 | -0.79000 | 1143.016 |\n", - "| 2 | 18:41:53.2 | -0.85650 | 1679.632 |\n", - "| 3 | 18:41:53.5 | -0.92275 | 2708.426 |\n", - "| 4 | 18:41:53.8 | -0.98900 | 5194.968 |\n", - "| 5 | 18:41:54.1 | -1.05525 | 17143.224 |\n", - "| 6 | 18:41:54.4 | -1.12175 | 72729.346 |\n", - "| 7 | 18:41:54.7 | -1.18800 | 73815.543 |\n", - "| 8 | 18:41:55.0 | -1.25425 | 17606.742 |\n", - "| 9 | 18:41:55.3 | -1.32050 | 5270.330 |\n", - "| 10 | 18:41:55.6 | -1.38700 | 2728.390 |\n", + "| 1 | 00:33:40.3 | -1.08000 | 955.031 |\n", + "| 2 | 00:33:40.5 | -1.14000 | 1472.376 |\n", + "| 3 | 00:33:40.8 | -1.20000 | 2875.401 |\n", + "| 4 | 00:33:41.1 | -1.27000 | 16626.547 |\n", + "| 5 | 00:33:41.4 | -1.33000 | 95075.221 |\n", + "| 6 | 00:33:41.7 | -1.39000 | 9717.986 |\n", + "| 7 | 00:33:42.0 | -1.45000 | 2740.795 |\n", + "| 8 | 00:33:42.3 | -1.52000 | 1315.395 |\n", + "| 9 | 00:33:42.7 | -1.58000 | 885.117 |\n", + "| 10 | 00:33:43.0 | -1.64000 | 672.979 |\n", "+-----------+------------+------------+------------+\n", - "generator TuneAxis.multi_pass_tune ['c308977e'] (scan num: 3)\n", + "generator TuneAxis.multi_pass_tune ['4c9af76b'] (scan num: 3)\n", "\n", "\n", "PeakStats\n", @@ -350,16 +350,16 @@ "================ =================================\n", "x m1 \n", "y spvoigt \n", - "cen -1.1552031419819602 \n", - "com -1.1507850330310176 \n", - "fwhm 0.15124853842779928 \n", - "min [-7.90000000e-01 1.14301639e+03]\n", - "max [-1.18800000e+00 7.38155431e+04]\n", - "crossings [-1.07957887 -1.23082741] \n", + "cen -1.3285390504163415 \n", + "com -1.327930971864717 \n", + "fwhm 0.06927988898350734 \n", + "min [ -1.64 672.97908343] \n", + "max [-1.33000000e+00 9.50752206e+04]\n", + "crossings [-1.29389911 -1.36317899] \n", "tune_ok True \n", - "center -1.1552031419819602 \n", - "initial_position -1.0885 \n", - "final_position -1.1552031419819602 \n", + "center -1.3285390504163415 \n", + "initial_position -1.36 \n", + "final_position -1.3285390504163415 \n", "================ =================================\n", "\n", "\n", @@ -367,18 +367,18 @@ "+-----------+------------+------------+------------+\n", "| seq_num | time | m1 | spvoigt |\n", "+-----------+------------+------------+------------+\n", - "| 1 | 18:41:56.6 | -1.30650 | 6377.418 |\n", - "| 2 | 18:41:56.8 | -1.27300 | 11595.004 |\n", - "| 3 | 18:41:57.0 | -1.23925 | 25077.287 |\n", - "| 4 | 18:41:57.2 | -1.20575 | 53121.620 |\n", - "| 5 | 18:41:57.4 | -1.17200 | 91630.849 |\n", - "| 6 | 18:41:57.6 | -1.13850 | 91493.582 |\n", - "| 7 | 18:41:57.8 | -1.10475 | 52961.611 |\n", - "| 8 | 18:41:58.0 | -1.07125 | 24989.497 |\n", - "| 9 | 18:41:58.2 | -1.03750 | 11559.028 |\n", - "| 10 | 18:41:58.4 | -1.00400 | 6363.532 |\n", + "| 1 | 00:33:44.0 | -1.40000 | 7051.436 |\n", + "| 2 | 00:33:44.2 | -1.38000 | 14353.903 |\n", + "| 3 | 00:33:44.4 | -1.37000 | 22222.839 |\n", + "| 4 | 00:33:44.6 | -1.35000 | 52516.186 |\n", + "| 5 | 00:33:44.8 | -1.34000 | 74805.966 |\n", + "| 6 | 00:33:45.0 | -1.32000 | 98976.619 |\n", + "| 7 | 00:33:45.2 | -1.31000 | 82625.135 |\n", + "| 8 | 00:33:45.4 | -1.29000 | 40194.994 |\n", + "| 9 | 00:33:45.6 | -1.28000 | 25930.433 |\n", + "| 10 | 00:33:45.8 | -1.26000 | 11033.875 |\n", "+-----------+------------+------------+------------+\n", - "generator TuneAxis.multi_pass_tune ['d664623a'] (scan num: 4)\n", + "generator TuneAxis.multi_pass_tune ['87e51c91'] (scan num: 4)\n", "\n", "\n", "PeakStats\n", @@ -387,16 +387,16 @@ "================ =================================\n", "x m1 \n", "y spvoigt \n", - "cen -1.15533945541456 \n", - "com -1.1553139807820585 \n", - "fwhm 0.11067466001505988 \n", - "min [-1.00400000e+00 6.36353169e+03]\n", - "max [-1.17200000e+00 9.16308487e+04]\n", - "crossings [-1.21067679 -1.10000213] \n", + "cen -1.3229095343527337 \n", + "com -1.322524379127855 \n", + "fwhm 0.053734231185460946 \n", + "min [-1.40000000e+00 7.05143645e+03]\n", + "max [-1.32000000e+00 9.89766194e+04]\n", + "crossings [-1.34977665 -1.29604242] \n", "tune_ok True \n", - "center -1.15533945541456 \n", - "initial_position -1.15525 \n", - "final_position -1.15533945541456 \n", + "center -1.3229095343527337 \n", + "initial_position -1.33 \n", + "final_position -1.3229095343527337 \n", "================ =================================\n", "\n", "\n", @@ -404,18 +404,18 @@ "+-----------+------------+------------+------------+\n", "| seq_num | time | m1 | spvoigt |\n", "+-----------+------------+------------+------------+\n", - "| 1 | 18:41:59.3 | -1.04450 | 13436.790 |\n", - "| 2 | 18:41:59.5 | -1.06925 | 23833.955 |\n", - "| 3 | 18:41:59.7 | -1.09375 | 41941.098 |\n", - "| 4 | 18:41:59.9 | -1.11825 | 68501.575 |\n", - "| 5 | 18:42:00.1 | -1.14300 | 95260.514 |\n", - "| 6 | 18:42:00.3 | -1.16750 | 95369.196 |\n", - "| 7 | 18:42:00.5 | -1.19225 | 68680.044 |\n", - "| 8 | 18:42:00.7 | -1.21675 | 42077.887 |\n", - "| 9 | 18:42:00.9 | -1.24125 | 23917.778 |\n", - "| 10 | 18:42:01.1 | -1.26600 | 13480.751 |\n", + "| 1 | 00:33:46.5 | -1.27000 | 16626.547 |\n", + "| 2 | 00:33:46.7 | -1.28000 | 25930.433 |\n", + "| 3 | 00:33:46.9 | -1.29000 | 40194.994 |\n", + "| 4 | 00:33:47.1 | -1.30000 | 59839.071 |\n", + "| 5 | 00:33:47.3 | -1.31000 | 82625.135 |\n", + "| 6 | 00:33:47.5 | -1.33000 | 95075.221 |\n", + "| 7 | 00:33:47.7 | -1.34000 | 74805.966 |\n", + "| 8 | 00:33:47.9 | -1.35000 | 52516.186 |\n", + "| 9 | 00:33:48.1 | -1.36000 | 34670.358 |\n", + "| 10 | 00:33:48.3 | -1.37000 | 22222.839 |\n", "+-----------+------------+------------+------------+\n", - "generator TuneAxis.multi_pass_tune ['0fe9137d'] (scan num: 5)\n", + "generator TuneAxis.multi_pass_tune ['4727cff7'] (scan num: 5)\n", "\n", "\n", "PeakStats\n", @@ -424,16 +424,16 @@ "================ =================================\n", "x m1 \n", "y spvoigt \n", - "cen -1.1553219950893325 \n", - "com -1.1553081766084872 \n", - "fwhm 0.1001537087161457 \n", - "min [-1.04450000e+00 1.34367899e+04]\n", - "max [-1.16750000e+00 9.53691956e+04]\n", - "crossings [-1.10524514 -1.20539885] \n", + "cen -1.3232368550566802 \n", + "com -1.324568875024918 \n", + "fwhm 0.050534157776581434 \n", + "min [-1.27000000e+00 1.66265473e+04]\n", + "max [-1.33000000e+00 9.50752206e+04]\n", + "crossings [-1.29796978 -1.34850393] \n", "tune_ok True \n", - "center -1.1553219950893325 \n", - "initial_position -1.15525 \n", - "final_position -1.1553219950893325 \n", + "center -1.3232368550566802 \n", + "initial_position -1.32 \n", + "final_position -1.3232368550566802 \n", "================ =================================\n", "\n" ] @@ -441,10 +441,10 @@ { "data": { "text/plain": [ - "('59738fc9-1916-4c84-8676-7396f887e1e6',\n", - " 'c308977e-d9af-459a-bc50-ae60b5fdbab0',\n", - " 'd664623a-8a3f-41ee-b2c3-967125428838',\n", - " '0fe9137d-7751-4cb3-9b60-30738139e60e')" + "('973d07fd-c9eb-46fe-91f9-8f5c91e2a8f9',\n", + " '4c9af76b-4c24-453a-bfab-4a25ad93f5de',\n", + " '87e51c91-7865-4d53-aade-cd83a702b704',\n", + " '4727cff7-8811-4ac4-9ae5-4a4f81c0d8f5')" ] }, "execution_count": 11, @@ -472,14 +472,14 @@ "name": "stdout", "output_type": "stream", "text": [ - "final: -1.1553219950893325\n", - "max (-1.1675, 95369.1956078187)\n", - "min (-1.0445, 13436.789922094267)\n", - "-- Signal(name='PeakStats_cen', parent='PeakStats', value=-1.0885316873365967, timestamp=1642725711.1095855) Signal(name='PeakStats_fwhm', parent='PeakStats', value=0.2983769532192523, timestamp=1642725711.1095936)\n", - "-- Signal(name='PeakStats_cen', parent='PeakStats', value=-1.1552031419819602, timestamp=1642725715.6256607) Signal(name='PeakStats_fwhm', parent='PeakStats', value=0.15124853842779928, timestamp=1642725715.6256957)\n", - "-- Signal(name='PeakStats_cen', parent='PeakStats', value=-1.15533945541456, timestamp=1642725718.4310818) Signal(name='PeakStats_fwhm', parent='PeakStats', value=0.11067466001505988, timestamp=1642725718.4310913)\n", - "-- Signal(name='PeakStats_cen', parent='PeakStats', value=-1.1553219950893325, timestamp=1642725721.140155) Signal(name='PeakStats_fwhm', parent='PeakStats', value=0.1001537087161457, timestamp=1642725721.1401901)\n", - "m1.position=-1.15525 det=13480.750786315055\n" + "final: -1.3232368550566802\n", + "max (-1.33, 95075.22058003367)\n", + "min (-1.27, 16626.54728728527)\n", + "-- Signal(name='PeakStats_cen', parent='PeakStats', value=-1.3594076832102968, timestamp=1657085618.1954212) Signal(name='PeakStats_fwhm', parent='PeakStats', value=0.2843975895677082, timestamp=1657085618.195451)\n", + "-- Signal(name='PeakStats_cen', parent='PeakStats', value=-1.3285390504163415, timestamp=1657085623.0134842) Signal(name='PeakStats_fwhm', parent='PeakStats', value=0.06927988898350734, timestamp=1657085623.0135126)\n", + "-- Signal(name='PeakStats_cen', parent='PeakStats', value=-1.3229095343527337, timestamp=1657085625.8175142) Signal(name='PeakStats_fwhm', parent='PeakStats', value=0.053734231185460946, timestamp=1657085625.8175213)\n", + "-- Signal(name='PeakStats_cen', parent='PeakStats', value=-1.3232368550566802, timestamp=1657085628.3260303) Signal(name='PeakStats_fwhm', parent='PeakStats', value=0.050534157776581434, timestamp=1657085628.326058)\n", + "m1.position=-1.32 det=22222.839021509993\n" ] } ], @@ -522,38 +522,38 @@ "+-----------+------------+------------+------------+\n", "| seq_num | time | m1 | spvoigt |\n", "+-----------+------------+------------+------------+\n", - "| 1 | 18:42:03.7 | -2.75000 | 87.793 |\n", - "| 2 | 18:42:04.0 | -2.66375 | 94.787 |\n", - "| 3 | 18:42:04.3 | -2.57750 | 103.089 |\n", - "| 4 | 18:42:04.6 | -2.49150 | 113.017 |\n", - "| 5 | 18:42:04.9 | -2.40525 | 125.097 |\n", - "| 6 | 18:42:05.2 | -2.31900 | 139.955 |\n", - "| 7 | 18:42:05.5 | -2.23275 | 158.513 |\n", - "| 8 | 18:42:05.8 | -2.14650 | 182.107 |\n", - "| 9 | 18:42:06.1 | -2.06025 | 212.737 |\n", - "| 10 | 18:42:06.4 | -1.97425 | 253.366 |\n", - "| 11 | 18:42:06.7 | -1.88800 | 309.227 |\n", - "| 12 | 18:42:07.0 | -1.80175 | 388.735 |\n", - "| 13 | 18:42:07.3 | -1.71550 | 507.354 |\n", - "| 14 | 18:42:07.6 | -1.62925 | 695.544 |\n", - "| 15 | 18:42:07.9 | -1.54300 | 1020.184 |\n", - "| 16 | 18:42:08.2 | -1.45700 | 1649.374 |\n", - "| 17 | 18:42:08.5 | -1.37075 | 3132.083 |\n", - "| 18 | 18:42:08.8 | -1.28450 | 9218.149 |\n", - "| 19 | 18:42:09.1 | -1.19825 | 61557.724 |\n", - "| 20 | 18:42:09.4 | -1.11200 | 61094.680 |\n", - "| 21 | 18:42:09.7 | -1.02575 | 9149.281 |\n", - "| 22 | 18:42:10.0 | -0.93975 | 3127.995 |\n", - "| 23 | 18:42:10.3 | -0.85350 | 1647.824 |\n", - "| 24 | 18:42:10.6 | -0.76725 | 1018.181 |\n", - "| 25 | 18:42:10.9 | -0.68100 | 694.436 |\n", - "| 26 | 18:42:11.2 | -0.59475 | 506.679 |\n", - "| 27 | 18:42:11.5 | -0.50850 | 388.294 |\n", - "| 28 | 18:42:11.8 | -0.42250 | 309.113 |\n", - "| 29 | 18:42:12.1 | -0.33625 | 253.285 |\n", - "| 30 | 18:42:12.4 | -0.25000 | 212.575 |\n", + "| 1 | 00:33:50.6 | -2.75000 | 282.864 |\n", + "| 2 | 00:33:50.9 | -2.66000 | 285.697 |\n", + "| 3 | 00:33:51.2 | -2.58000 | 288.742 |\n", + "| 4 | 00:33:51.5 | -2.49000 | 292.943 |\n", + "| 5 | 00:33:51.8 | -2.41000 | 297.586 |\n", + "| 6 | 00:33:52.1 | -2.32000 | 304.203 |\n", + "| 7 | 00:33:52.4 | -2.23000 | 312.884 |\n", + "| 8 | 00:33:52.7 | -2.15000 | 323.095 |\n", + "| 9 | 00:33:53.0 | -2.06000 | 338.783 |\n", + "| 10 | 00:33:53.3 | -1.97000 | 361.453 |\n", + "| 11 | 00:33:53.6 | -1.89000 | 391.297 |\n", + "| 12 | 00:33:53.9 | -1.80000 | 444.359 |\n", + "| 13 | 00:33:54.2 | -1.72000 | 524.755 |\n", + "| 14 | 00:33:54.5 | -1.63000 | 699.989 |\n", + "| 15 | 00:33:54.8 | -1.54000 | 1132.632 |\n", + "| 16 | 00:33:55.1 | -1.46000 | 2403.413 |\n", + "| 17 | 00:33:55.4 | -1.37000 | 22222.839 |\n", + "| 18 | 00:33:55.7 | -1.28000 | 25930.433 |\n", + "| 19 | 00:33:56.0 | -1.20000 | 2875.401 |\n", + "| 20 | 00:33:56.3 | -1.11000 | 1160.534 |\n", + "| 21 | 00:33:56.6 | -1.03000 | 740.694 |\n", + "| 22 | 00:33:56.9 | -0.94000 | 543.387 |\n", + "| 23 | 00:33:57.2 | -0.85000 | 447.006 |\n", + "| 24 | 00:33:57.5 | -0.77000 | 397.617 |\n", + "| 25 | 00:33:57.8 | -0.68000 | 362.513 |\n", + "| 26 | 00:33:58.1 | -0.59000 | 339.500 |\n", + "| 27 | 00:33:58.4 | -0.51000 | 325.112 |\n", + "| 28 | 00:33:58.7 | -0.42000 | 313.268 |\n", + "| 29 | 00:33:59.0 | -0.34000 | 305.350 |\n", + "| 30 | 00:33:59.3 | -0.25000 | 298.470 |\n", "+-----------+------------+------------+------------+\n", - "generator TuneAxis.tune ['a381e111'] (scan num: 6)\n", + "generator TuneAxis.tune ['8ee3fd77'] (scan num: 6)\n", "\n", "\n", "PeakStats\n", @@ -562,16 +562,16 @@ "================ =================================\n", "x m1 \n", "y spvoigt \n", - "cen -1.155317253761389 \n", - "com -1.1632359362669962 \n", - "fwhm 0.18716133951232927 \n", - "min [-2.75 87.79310955] \n", - "max [-1.19825000e+00 6.15577243e+04]\n", - "crossings [-1.24889792 -1.06173658] \n", + "cen -1.3234493073606397 \n", + "com -1.3447636766612034 \n", + "fwhm 0.17589460960483438 \n", + "min [ -2.75 282.86449968] \n", + "max [-1.28000000e+00 2.59304333e+04]\n", + "crossings [-1.41139661 -1.235502 ] \n", "tune_ok True \n", - "center -1.155317253761389 \n", + "center -1.3234493073606397 \n", "initial_position -1.5 \n", - "final_position -1.155317253761389 \n", + "final_position -1.3234493073606397 \n", "================ =================================\n", "\n" ] @@ -579,7 +579,7 @@ { "data": { "text/plain": [ - "('a381e111-3216-4f44-ab83-27cea6de3409',)" + "('8ee3fd77-5217-4b5c-8e00-65b694c9e83a',)" ] }, "execution_count": 13, @@ -611,12 +611,12 @@ "name": "stdout", "output_type": "stream", "text": [ - "final: -1.155317253761389\n", - "max (-1.19825, 61557.72427484274)\n", - "min (-2.75, 87.79310954973654)\n", - "centroid -1.155317253761389\n", - "FWHM 0.18716133951232927\n", - "m1.position=-1.15525 det=212.57529955844245\n" + "final: -1.3234493073606397\n", + "max (-1.28, 25930.433258016637)\n", + "min (-2.75, 282.86449968250383)\n", + "centroid -1.3234493073606397\n", + "FWHM 0.17589460960483438\n", + "m1.position=-1.32 det=298.47011500137876\n" ] } ], @@ -632,7 +632,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -646,9 +646,9 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.7" + "version": "3.9.13" } }, "nbformat": 4, - "nbformat_minor": 2 + "nbformat_minor": 4 } diff --git a/docs/source/examples/_tuneaxis.rst b/docs/source/examples/_tuneaxis.rst deleted file mode 100644 index 0a30c7eea..000000000 --- a/docs/source/examples/_tuneaxis.rst +++ /dev/null @@ -1,8 +0,0 @@ -.. _example_TuneAxis: - -Example: the :class:`~apstools.plans.TuneAxis()` class -=============================================================================== - -We'll use a Jupyter notebook to demonstrate the :class:`~apstools.plans.TuneAxis()` -support that provides custom alignment of a signal against an axis. -Follow here: https://github.com/BCDA-APS/apstools/blob/master/docs/source/resources/demo_tuneaxis.ipynb diff --git a/docs/source/examples/index.rst b/docs/source/examples/index.rst index 63082ee6f..1af472909 100644 --- a/docs/source/examples/index.rst +++ b/docs/source/examples/index.rst @@ -9,7 +9,6 @@ Examples _* - .. _examples_downloads: File Downloads @@ -17,13 +16,13 @@ File Downloads The jupyter notebooks and files related to this section may be downloaded from the following table. -* jupyter notebook: :download:`demo_excel_scan <../resources/excel_scan.ipynb>` +* jupyter notebook: :download:`_excel_scan <_excel_scan.ipynb>` - * :download:`sample_example.xlsx <../resources/sample_example.xlsx>` + * :download:`sample_example.xlsx ` -* jupyter notebook: :download:`demo_nscan <../resources/demo_nscan.ipynb>` -* jupyter notebook: :download:`demo_tuneaxis <../resources/demo_tuneaxis.ipynb>` -* jupyter notebook: :download:`demo_specfile_example <../resources/demo_specfile_example.ipynb>` +* jupyter notebook: :download:`_nscan <_nscan.ipynb>` +* jupyter notebook: :download:`_tuneaxis <_tuneaxis.ipynb>` +* jupyter notebook: :download:`_specfile_example <_specfile_example.ipynb>` * :download:`spec1.dat <../resources/spec1.dat>` * :download:`spec2.dat <../resources/spec2.dat>` diff --git a/docs/source/resources/sample_example.xlsx b/docs/source/examples/sample_example.xlsx similarity index 100% rename from docs/source/resources/sample_example.xlsx rename to docs/source/examples/sample_example.xlsx diff --git a/docs/source/examples/spec1.dat b/docs/source/examples/spec1.dat new file mode 100644 index 000000000..212d73e40 --- /dev/null +++ b/docs/source/examples/spec1.dat @@ -0,0 +1,66 @@ +#F spec1.dat +#E 1657086495 +#D Wed Jul 06 00:48:15 2022 +#C Bluesky user = prjemian host = zap +#O0 +#o0 + +#S 1 scan(detectors=['det'], num=41, args='['motor', -1.2, 1.2]', per_step='None') +#D Wed Jul 06 00:48:02 2022 +#C Wed Jul 06 00:48:02 2022. plan_type = generator +#C Wed Jul 06 00:48:02 2022. uid = 7ab128a0-c0ef-4f41-a980-c5641e00ce97 +#MD uid = 7ab128a0-c0ef-4f41-a980-c5641e00ce97 +#MD detectors = ['det'] +#MD motors = ['motor'] +#MD num_intervals = 40 +#MD num_points = 41 +#MD plan_pattern = inner_product +#MD plan_pattern_args = {'num': 41, 'args': ["EpicsMotor(prefix='gp:m1', name='motor', settle_time=0.0, timeout=None, read_attrs=['user_readback', 'user_setpoint'], configuration_attrs=['user_offset', 'user_offset_dir', 'velocity', 'acceleration', 'motor_egu'])", -1.2, 1.2]} +#MD plan_pattern_module = bluesky.plan_patterns +#MD versions = {'ophyd': '1.6.4', 'bluesky': '1.8.3'} +#P0 +#N 5 +#L motor Epoch_float Epoch motor_user_setpoint det +-1.2 0.434417724609375 0 -1.2 0.0 +-1.1400000000000001 0.6748816967010498 1 -1.14 0.0 +-1.08 0.9758017063140869 1 -1.08 0.0 +-1.02 1.276907205581665 1 -1.02 0.0 +-0.96 1.577857255935669 2 -0.96 0.0 +-0.9 1.878566026687622 2 -0.8999999999999999 8.15929807366173e-267 +-0.84 2.1784615516662598 2 -0.84 2.1200557806170912e-225 +-0.78 2.479753255844116 2 -0.78 1.9915653174496467e-187 +-0.72 2.7803871631622314 3 -0.72 7.055567703307068e-153 +-0.66 3.084782361984253 3 -0.6599999999999999 9.228365714083866e-122 +-0.6 3.38234806060791 3 -0.6 4.4980315558799814e-94 +-0.54 3.683393955230713 4 -0.54 8.034623946332195e-70 +-0.48 3.983243703842163 4 -0.48 5.540840853688821e-49 +-0.42 4.28428053855896 4 -0.41999999999999993 1.3512822090579629e-31 +-0.36 4.585167646408081 5 -0.36 1.2489789444505279e-17 +-0.3 4.885620355606079 5 -0.30000000000000004 4.456540993798201e-07 +-0.24 5.186397314071655 5 -0.24 5.607678879044609 +-0.18 5.481502294540405 5 -0.17999999999999994 27463.061094278986 +-0.12 5.787712335586548 6 -0.11999999999999988 48682.85376123283 +-0.06 6.08820915222168 6 -0.06000000000000005 31.30450080779168 +0.0 6.389137268066406 6 0.0 7.839397376237093e-06 +0.06 6.690355062484741 7 0.06000000000000005 7.136670527501498e-16 +0.12 6.991316556930542 7 0.11999999999999988 2.3310134905397063e-29 +0.18 7.291990041732788 7 0.17999999999999994 2.985214388500194e-46 +0.24 7.593103885650635 8 0.24 1.394367198136051e-66 +0.3 7.893867492675781 8 0.30000000000000004 2.415783879435812e-90 +0.36 8.195511817932129 8 0.3600000000000001 1.5472274009149628e-117 +0.42 8.495795965194702 8 0.41999999999999993 3.743870737982315e-148 +0.48 8.796814680099487 9 0.48 3.383359371335557e-182 +0.54 9.097584009170532 9 0.54 1.1425123138669685e-219 +0.6 9.399116039276123 9 0.5999999999999999 1.3792208368358542e-260 +0.66 9.699814081192017 10 0.6599999999999999 0.0 +0.72 10.001182079315186 10 0.72 0.0 +0.78 10.301761150360107 10 0.78 0.0 +0.84 10.603094100952148 11 0.8400000000000001 0.0 +0.9 10.904242992401123 11 0.9000000000000001 0.0 +0.96 11.205401420593262 11 0.9600000000000002 0.0 +1.02 11.506523370742798 12 1.0199999999999998 0.0 +1.08 11.807251930236816 12 1.0799999999999998 0.0 +1.1400000000000001 12.102661371231079 12 1.14 0.0 +1.2 12.408974170684814 12 1.2 0.0 +#C Wed Jul 06 00:48:15 2022. num_events_primary = 41 +#C Wed Jul 06 00:48:15 2022. exit_status = success diff --git a/docs/source/examples/test_specdata.txt b/docs/source/examples/test_specdata.txt new file mode 100644 index 000000000..782f81607 --- /dev/null +++ b/docs/source/examples/test_specdata.txt @@ -0,0 +1,66 @@ +#F test_specdata.txt +#E 1657086495 +#D Wed Jul 06 00:48:15 2022 +#C Bluesky user = prjemian host = zap +#O0 +#o0 + +#S 1 scan(detectors=['det'], num=41, args='['motor', -1.2, 1.2]', per_step='None') +#D Wed Jul 06 00:48:02 2022 +#C Wed Jul 06 00:48:02 2022. plan_type = generator +#C Wed Jul 06 00:48:02 2022. uid = 7ab128a0-c0ef-4f41-a980-c5641e00ce97 +#MD uid = 7ab128a0-c0ef-4f41-a980-c5641e00ce97 +#MD detectors = ['det'] +#MD motors = ['motor'] +#MD num_intervals = 40 +#MD num_points = 41 +#MD plan_pattern = inner_product +#MD plan_pattern_args = {'num': 41, 'args': ["EpicsMotor(prefix='gp:m1', name='motor', settle_time=0.0, timeout=None, read_attrs=['user_readback', 'user_setpoint'], configuration_attrs=['user_offset', 'user_offset_dir', 'velocity', 'acceleration', 'motor_egu'])", -1.2, 1.2]} +#MD plan_pattern_module = bluesky.plan_patterns +#MD versions = {'ophyd': '1.6.4', 'bluesky': '1.8.3'} +#P0 +#N 5 +#L motor Epoch_float Epoch motor_user_setpoint det +-1.2 0.434417724609375 0 -1.2 0.0 +-1.1400000000000001 0.6748816967010498 1 -1.14 0.0 +-1.08 0.9758017063140869 1 -1.08 0.0 +-1.02 1.276907205581665 1 -1.02 0.0 +-0.96 1.577857255935669 2 -0.96 0.0 +-0.9 1.878566026687622 2 -0.8999999999999999 8.15929807366173e-267 +-0.84 2.1784615516662598 2 -0.84 2.1200557806170912e-225 +-0.78 2.479753255844116 2 -0.78 1.9915653174496467e-187 +-0.72 2.7803871631622314 3 -0.72 7.055567703307068e-153 +-0.66 3.084782361984253 3 -0.6599999999999999 9.228365714083866e-122 +-0.6 3.38234806060791 3 -0.6 4.4980315558799814e-94 +-0.54 3.683393955230713 4 -0.54 8.034623946332195e-70 +-0.48 3.983243703842163 4 -0.48 5.540840853688821e-49 +-0.42 4.28428053855896 4 -0.41999999999999993 1.3512822090579629e-31 +-0.36 4.585167646408081 5 -0.36 1.2489789444505279e-17 +-0.3 4.885620355606079 5 -0.30000000000000004 4.456540993798201e-07 +-0.24 5.186397314071655 5 -0.24 5.607678879044609 +-0.18 5.481502294540405 5 -0.17999999999999994 27463.061094278986 +-0.12 5.787712335586548 6 -0.11999999999999988 48682.85376123283 +-0.06 6.08820915222168 6 -0.06000000000000005 31.30450080779168 +0.0 6.389137268066406 6 0.0 7.839397376237093e-06 +0.06 6.690355062484741 7 0.06000000000000005 7.136670527501498e-16 +0.12 6.991316556930542 7 0.11999999999999988 2.3310134905397063e-29 +0.18 7.291990041732788 7 0.17999999999999994 2.985214388500194e-46 +0.24 7.593103885650635 8 0.24 1.394367198136051e-66 +0.3 7.893867492675781 8 0.30000000000000004 2.415783879435812e-90 +0.36 8.195511817932129 8 0.3600000000000001 1.5472274009149628e-117 +0.42 8.495795965194702 8 0.41999999999999993 3.743870737982315e-148 +0.48 8.796814680099487 9 0.48 3.383359371335557e-182 +0.54 9.097584009170532 9 0.54 1.1425123138669685e-219 +0.6 9.399116039276123 9 0.5999999999999999 1.3792208368358542e-260 +0.66 9.699814081192017 10 0.6599999999999999 0.0 +0.72 10.001182079315186 10 0.72 0.0 +0.78 10.301761150360107 10 0.78 0.0 +0.84 10.603094100952148 11 0.8400000000000001 0.0 +0.9 10.904242992401123 11 0.9000000000000001 0.0 +0.96 11.205401420593262 11 0.9600000000000002 0.0 +1.02 11.506523370742798 12 1.0199999999999998 0.0 +1.08 11.807251930236816 12 1.0799999999999998 0.0 +1.1400000000000001 12.102661371231079 12 1.14 0.0 +1.2 12.408974170684814 12 1.2 0.0 +#C Wed Jul 06 00:48:15 2022. num_events_primary = 41 +#C Wed Jul 06 00:48:15 2022. exit_status = success diff --git a/docs/source/resources/demo_specfile_example.ipynb b/docs/source/resources/demo_specfile_example.ipynb deleted file mode 100644 index 80fbd6b61..000000000 --- a/docs/source/resources/demo_specfile_example.ipynb +++ /dev/null @@ -1,951 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# `specfile_example()` - Output scan(s) to a SPEC data file." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "One of the common concerns is how to access data from bluesky's database. The standard way is to replay the document stream from each of the scans through a bluesky callback that writes the data to the desired file format. Here, we write data to the SPEC file format.\n", - "\n", - "## Setup\n", - "\n", - "First, we must setup a bluesky session. We create a [temporary catalog](https://blueskyproject.io/databroker/how-to/file-backed-catalog.html?highlight=temporary#temporary-catalog) to store the data from this notebook's data collection." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "1" - ] - }, - "execution_count": 1, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "%matplotlib inline\n", - "import matplotlib.pyplot as plt\n", - "plt.ion()\n", - "\n", - "from bluesky import RunEngine, plans as bp\n", - "from bluesky.callbacks.best_effort import BestEffortCallback\n", - "import databroker\n", - "\n", - "cat = databroker.temp()\n", - "RE = RunEngine({})\n", - "RE.subscribe(cat.v1.insert)\n", - "RE.subscribe(BestEffortCallback())" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We need to create some scan data. We'll use Gaussian peak profile calculated in a _userCalc_ (`swait` record) and a motor from our EPICS IOC simulator `gp:`. Initialize the peak computation with some randomized parameters. The peak will be at some `motor` position between -1 .. +1. Print the actual values for our reference later on." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "calc = D*(0.95+E*RNDM)/exp(((A-b)/c)^2)\n", - "A: x = gp:m1.RBV\n", - "B: center = 0.06950222003817952\n", - "C: width = 0.14120203401736622\n", - "D: scale = 102149.8538163492\n", - "E: noise= 0.05126581951023504\n" - ] - } - ], - "source": [ - "from apstools.devices import UserCalcN, setup_gaussian_swait\n", - "import numpy as np\n", - "from ophyd import EpicsMotor, EpicsSignal\n", - "\n", - "IOC = \"gp:\"\n", - "motor = EpicsMotor(f\"{IOC}m1\", name=\"motor\")\n", - "swait = UserCalcN(f\"{IOC}userCalc1\", name=\"swait\")\n", - "det = EpicsSignal(swait.calculated_value.pvname, name=\"det\")\n", - "\n", - "motor.wait_for_connection()\n", - "swait.wait_for_connection()\n", - "det.wait_for_connection()\n", - "\n", - "setup_gaussian_swait(\n", - " swait, motor.user_readback, \n", - " center=-0.5 + np.random.uniform(), \n", - " width=0.01 + 0.2*np.random.uniform(), \n", - " noise=0.05*(.95 + .1*np.random.uniform()),\n", - " scale=100_000*(.95 + .1*np.random.uniform()), \n", - " )\n", - "\n", - "print(f\"calc = {swait.calculation.get()}\")\n", - "print(f\"A: x = {swait.channels.A.input_pv.get()}\")\n", - "print(f\"B: center = {swait.channels.B.input_value.get()}\")\n", - "print(f\"C: width = {swait.channels.C.input_value.get()}\")\n", - "print(f\"D: scale = {swait.channels.D.input_value.get()}\")\n", - "print(f\"E: noise= {swait.channels.E.input_value.get()}\")\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Finally, run the scan with the RunEngine:" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "\n", - "Transient Scan ID: 1 Time: 2022-01-25 16:04:48\n", - "Persistent Unique Scan ID: '1ce82e67-c4fc-4fc2-a87d-e966811f873a'\n", - "New stream: 'primary'\n", - "+-----------+------------+------------+------------+\n", - "| seq_num | time | motor | det |\n", - "+-----------+------------+------------+------------+\n", - "| 1 | 16:04:48.7 | -1.20000 | 0.00000 |\n", - "| 2 | 16:04:49.0 | -1.14000 | 0.00000 |\n", - "| 3 | 16:04:49.3 | -1.08000 | 0.00000 |\n", - "| 4 | 16:04:49.6 | -1.02000 | 0.00000 |\n", - "| 5 | 16:04:49.9 | -0.96000 | 0.00000 |\n", - "| 6 | 16:04:50.2 | -0.90000 | 0.00000 |\n", - "| 7 | 16:04:50.5 | -0.84000 | 0.00000 |\n", - "| 8 | 16:04:50.8 | -0.78000 | 0.00000 |\n", - "| 9 | 16:04:51.1 | -0.72000 | 0.00000 |\n", - "| 10 | 16:04:51.4 | -0.66000 | 0.00000 |\n", - "| 11 | 16:04:51.7 | -0.60000 | 0.00002 |\n", - "| 12 | 16:04:52.0 | -0.54000 | 0.00081 |\n", - "| 13 | 16:04:52.3 | -0.48000 | 0.02575 |\n", - "| 14 | 16:04:52.6 | -0.42000 | 0.60781 |\n", - "| 15 | 16:04:52.9 | -0.36000 | 9.43825 |\n", - "| 16 | 16:04:53.2 | -0.30000 | 107.68861 |\n", - "| 17 | 16:04:53.5 | -0.24000 | 800.42298 |\n", - "| 18 | 16:04:53.8 | -0.18000 | 4464.21688 |\n", - "| 19 | 16:04:54.1 | -0.12000 | 16611.21112 |\n", - "| 20 | 16:04:54.4 | -0.06000 | 42862.84205 |\n", - "| 21 | 16:04:54.7 | 0.00000 | 79577.92994 |\n", - "| 22 | 16:04:55.0 | 0.06000 | 100632.47911 |\n", - "| 23 | 16:04:55.3 | 0.12000 | 89396.77981 |\n", - "| 24 | 16:04:55.6 | 0.18000 | 55115.00222 |\n", - "| 25 | 16:04:55.9 | 0.24000 | 23435.80948 |\n", - "| 26 | 16:04:56.2 | 0.30000 | 6778.65761 |\n", - "| 27 | 16:04:56.5 | 0.36000 | 1452.43092 |\n", - "| 28 | 16:04:56.8 | 0.42000 | 209.45893 |\n", - "| 29 | 16:04:57.1 | 0.48000 | 21.19723 |\n", - "| 30 | 16:04:57.4 | 0.54000 | 1.47820 |\n", - "| 31 | 16:04:57.7 | 0.60000 | 0.07466 |\n", - "| 32 | 16:04:58.0 | 0.66000 | 0.00257 |\n", - "| 33 | 16:04:58.3 | 0.72000 | 0.00006 |\n", - "| 34 | 16:04:58.6 | 0.78000 | 0.00000 |\n", - "| 35 | 16:04:58.9 | 0.84000 | 0.00000 |\n", - "| 36 | 16:04:59.2 | 0.90000 | 0.00000 |\n", - "| 37 | 16:04:59.5 | 0.96000 | 0.00000 |\n", - "| 38 | 16:04:59.8 | 1.02000 | 0.00000 |\n", - "| 39 | 16:05:00.1 | 1.08000 | 0.00000 |\n", - "| 40 | 16:05:00.4 | 1.14000 | 0.00000 |\n", - "| 41 | 16:05:00.7 | 1.20000 | 0.00000 |\n", - "+-----------+------------+------------+------------+\n", - "generator scan ['1ce82e67'] (scan num: 1)\n", - "\n", - "\n", - "\n" - ] - }, - { - "data": { - "text/plain": [ - "('1ce82e67-c4fc-4fc2-a87d-e966811f873a',)" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "RE(bp.scan([det], motor, -1.2, 1.2, 41))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## get the most recent scan, by steps\n", - "\n", - "The databroker instance, `cat`, provides access to its scans by several means. One way is to consider `cat` as a list and retrieve the last item from the list. This will return a *run*, the common reference to be used.\n", - "\n", - "For this first example, we'll work through the steps one by one. The `cat.v2` interface is the easiest to use (at this writing)." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "BlueskyRun\n", - " uid='1ce82e67-c4fc-4fc2-a87d-e966811f873a'\n", - " exit_status='success'\n", - " 2022-01-25 16:04:48.412 -- 2022-01-25 16:05:00.800\n", - " Streams:\n", - " * primary\n" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "run = cat.v2[-1]\n", - "run" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As shown, the run has one data stream, name `primary`. The databroker provides a simple table view of this run:" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "
<xarray.Dataset>\n",
-       "Dimensions:              (time: 41)\n",
-       "Coordinates:\n",
-       "  * time                 (time) float64 1.643e+09 1.643e+09 ... 1.643e+09\n",
-       "Data variables:\n",
-       "    det                  (time) float64 7.68e-31 1.349e-27 ... 1.413e-23\n",
-       "    motor                (time) float64 -1.2 -1.14 -1.08 -1.02 ... 1.08 1.14 1.2\n",
-       "    motor_user_setpoint  (time) float64 -1.2 -1.14 -1.08 -1.02 ... 1.08 1.14 1.2
" - ], - "text/plain": [ - "\n", - "Dimensions: (time: 41)\n", - "Coordinates:\n", - " * time (time) float64 1.643e+09 1.643e+09 ... 1.643e+09\n", - "Data variables:\n", - " det (time) float64 7.68e-31 1.349e-27 ... 1.413e-23\n", - " motor (time) float64 -1.2 -1.14 -1.08 -1.02 ... 1.08 1.14 1.2\n", - " motor_user_setpoint (time) float64 -1.2 -1.14 -1.08 -1.02 ... 1.08 1.14 1.2" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "run.primary.read()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Support function: `specfile_example()`\n", - "\n", - "We need to call the `apstools.callbacks.SpecWriterCallback()` callback with the run's documents.\n", - "\n", - "Here, `specfile_example()` is a support function that can be used with one or more runs to create a SPEC data file (one Bluesky run will become one SPEC scan in the same file)." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "from apstools.callbacks import SpecWriterCallback\n", - "from databroker._drivers.mongo_normalized import BlueskyMongoCatalog\n", - "from databroker._drivers.msgpack import BlueskyMsgpackCatalog\n", - "import warnings\n", - "\n", - "DEMO_SPEC_FILE = \"test_specdata.txt\"\n", - "\n", - "def specfile_example(runs, filename=DEMO_SPEC_FILE):\n", - " \"\"\"write one or more headers (scans) to a SPEC data file\"\"\"\n", - " if isinstance(runs, databroker.core.BlueskyRun):\n", - " runs = [runs]\n", - " if not isinstance(runs, (list, BlueskyMsgpackCatalog, BlueskyMongoCatalog)):\n", - " raise TypeError(\"Must give run object or list of run objects.\")\n", - " if len(runs) == 0:\n", - " raise ValueError(\"Must provide one or more runs.\")\n", - "\n", - " specwriter = SpecWriterCallback(filename=filename, auto_write=True)\n", - " for uid in runs:\n", - " if isinstance(uid, str):\n", - " run = runs[uid]\n", - " else:\n", - " run = uid\n", - " if not isinstance(run, databroker.core.BlueskyRun):\n", - " warnings.warn(f\"Skipping {run=}, it is not a BlueskyRun object.\")\n", - " continue\n", - " # to get the raw document stream, need the v1 interface\n", - " h = run.catalog_object.v1[run.name] # header\n", - " for key, doc in h.db.get_documents(h):\n", - " specwriter.receiver(key, doc)\n", - " # lines = specwriter.prepare_scan_contents()\n", - " print(\"Look at SPEC data file: \" + specwriter.spec_filename)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Write the run as SPEC data" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's write it as a SPEC data file (namely: `spec1.dat`):" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Look at SPEC data file: spec1.dat\n" - ] - } - ], - "source": [ - "import os\n", - "\n", - "if os.path.exists(\"spec1.dat\"): # re-write the file\n", - " os.remove(\"spec1.dat\")\n", - "\n", - "specfile_example(run, filename=\"spec1.dat\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's view file `spec1.dat` from disk storage (using the [`pycat`](https://ipython.readthedocs.io/en/stable/interactive/magics.html?highlight=pycat#magic-pycat) IPython magic function):" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\u001b[0;31m#F spec1.dat\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;31m#E 1643148301\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;31m#D Tue Jan 25 16:05:01 2022\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;31m#C Bluesky user = prjemian host = zap\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;31m#O0 \u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;31m#o0 \u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;31m#S 1 scan(detectors=['det'], num=41, args='['motor', -1.2, 1.2]', per_step='None')\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;31m#D Tue Jan 25 16:04:48 2022\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;31m#C Tue Jan 25 16:04:48 2022. plan_type = generator\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;31m#C Tue Jan 25 16:04:48 2022. uid = 1ce82e67-c4fc-4fc2-a87d-e966811f873a\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;31m#MD uid = 1ce82e67-c4fc-4fc2-a87d-e966811f873a\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;31m#MD detectors = ['det']\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;31m#MD motors = ['motor']\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;31m#MD num_intervals = 40\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;31m#MD num_points = 41\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;31m#MD plan_pattern = inner_product\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;31m#MD plan_pattern_args = {'num': 41, 'args': [\"EpicsMotor(prefix='gp:m1', name='motor', settle_time=0.0, timeout=None, read_attrs=['user_readback', 'user_setpoint'], configuration_attrs=['user_offset', 'user_offset_dir', 'velocity', 'acceleration', 'motor_egu'])\", -1.2, 1.2]}\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;31m#MD plan_pattern_module = bluesky.plan_patterns\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;31m#MD versions = {'ophyd': '1.6.3', 'bluesky': '1.8.1'}\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;31m#P0 \u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;31m#N 5\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;31m#L motor Epoch_float Epoch motor_user_setpoint det\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m1.2\u001b[0m \u001b[0;36m0.2961690425872803\u001b[0m \u001b[0;36m0\u001b[0m \u001b[0;34m-\u001b[0m\u001b[0;36m1.2\u001b[0m \u001b[0;36m7.679523961654961e-31\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m1.1400000000000001\u001b[0m \u001b[0;36m0.6048011779785156\u001b[0m \u001b[0;36m1\u001b[0m \u001b[0;34m-\u001b[0m\u001b[0;36m1.14\u001b[0m \u001b[0;36m1.3490175737219111e-27\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m1.08\u001b[0m \u001b[0;36m0.9048187732696533\u001b[0m \u001b[0;36m1\u001b[0m \u001b[0;34m-\u001b[0m\u001b[0;36m1.08\u001b[0m \u001b[0;36m1.6503999143322275e-24\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m1.02\u001b[0m \u001b[0;36m1.2110660076141357\u001b[0m \u001b[0;36m1\u001b[0m \u001b[0;34m-\u001b[0m\u001b[0;36m1.02\u001b[0m \u001b[0;36m1.3967519845381986e-21\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m0.96\u001b[0m \u001b[0;36m1.512406826019287\u001b[0m \u001b[0;36m2\u001b[0m \u001b[0;34m-\u001b[0m\u001b[0;36m0.96\u001b[0m \u001b[0;36m8.011710408014841e-19\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m0.9\u001b[0m \u001b[0;36m1.8137702941894531\u001b[0m \u001b[0;36m2\u001b[0m \u001b[0;34m-\u001b[0m\u001b[0;36m0.8999999999999999\u001b[0m \u001b[0;36m3.2876098903802664e-16\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m0.84\u001b[0m \u001b[0;36m2.1140096187591553\u001b[0m \u001b[0;36m2\u001b[0m \u001b[0;34m-\u001b[0m\u001b[0;36m0.84\u001b[0m \u001b[0;36m9.627118899281808e-14\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m0.78\u001b[0m \u001b[0;36m2.4141225814819336\u001b[0m \u001b[0;36m2\u001b[0m \u001b[0;34m-\u001b[0m\u001b[0;36m0.78\u001b[0m \u001b[0;36m1.8687098709776184e-11\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m0.72\u001b[0m \u001b[0;36m2.708725929260254\u001b[0m \u001b[0;36m3\u001b[0m \u001b[0;34m-\u001b[0m\u001b[0;36m0.72\u001b[0m \u001b[0;36m2.650136944197521e-09\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m0.66\u001b[0m \u001b[0;36m3.015946865081787\u001b[0m \u001b[0;36m3\u001b[0m \u001b[0;34m-\u001b[0m\u001b[0;36m0.6599999999999999\u001b[0m \u001b[0;36m2.6134193307893575e-07\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m0.6\u001b[0m \u001b[0;36m3.3157804012298584\u001b[0m \u001b[0;36m3\u001b[0m \u001b[0;34m-\u001b[0m\u001b[0;36m0.6\u001b[0m \u001b[0;36m1.698170681137489e-05\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m0.54\u001b[0m \u001b[0;36m3.6169066429138184\u001b[0m \u001b[0;36m4\u001b[0m \u001b[0;34m-\u001b[0m\u001b[0;36m0.54\u001b[0m \u001b[0;36m0.0008140754835235199\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m0.48\u001b[0m \u001b[0;36m3.9303784370422363\u001b[0m \u001b[0;36m4\u001b[0m \u001b[0;34m-\u001b[0m\u001b[0;36m0.48\u001b[0m \u001b[0;36m0.025746486716416765\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m0.42\u001b[0m \u001b[0;36m4.217523574829102\u001b[0m \u001b[0;36m4\u001b[0m \u001b[0;34m-\u001b[0m\u001b[0;36m0.41999999999999993\u001b[0m \u001b[0;36m0.6078066199037614\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m0.36\u001b[0m \u001b[0;36m4.519362926483154\u001b[0m \u001b[0;36m5\u001b[0m \u001b[0;34m-\u001b[0m\u001b[0;36m0.36\u001b[0m \u001b[0;36m9.438246987168435\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m0.3\u001b[0m \u001b[0;36m4.819454193115234\u001b[0m \u001b[0;36m5\u001b[0m \u001b[0;34m-\u001b[0m\u001b[0;36m0.30000000000000004\u001b[0m \u001b[0;36m107.6886102322831\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m0.24\u001b[0m \u001b[0;36m5.119854927062988\u001b[0m \u001b[0;36m5\u001b[0m \u001b[0;34m-\u001b[0m\u001b[0;36m0.24\u001b[0m \u001b[0;36m800.4229817224294\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m0.18\u001b[0m \u001b[0;36m5.421689510345459\u001b[0m \u001b[0;36m5\u001b[0m \u001b[0;34m-\u001b[0m\u001b[0;36m0.17999999999999994\u001b[0m \u001b[0;36m4464.216882610465\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m0.12\u001b[0m \u001b[0;36m5.722400665283203\u001b[0m \u001b[0;36m6\u001b[0m \u001b[0;34m-\u001b[0m\u001b[0;36m0.11999999999999988\u001b[0m \u001b[0;36m16611.211116674494\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m0.06\u001b[0m \u001b[0;36m6.017247915267944\u001b[0m \u001b[0;36m6\u001b[0m \u001b[0;34m-\u001b[0m\u001b[0;36m0.06000000000000005\u001b[0m \u001b[0;36m42862.842054591725\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;36m0.0\u001b[0m \u001b[0;36m6.324151515960693\u001b[0m \u001b[0;36m6\u001b[0m \u001b[0;36m0.0\u001b[0m \u001b[0;36m79577.92993553793\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;36m0.06\u001b[0m \u001b[0;36m6.6247687339782715\u001b[0m \u001b[0;36m7\u001b[0m \u001b[0;36m0.06000000000000005\u001b[0m \u001b[0;36m100632.47911140075\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;36m0.12\u001b[0m \u001b[0;36m6.926091909408569\u001b[0m \u001b[0;36m7\u001b[0m \u001b[0;36m0.11999999999999988\u001b[0m \u001b[0;36m89396.77980532675\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;36m0.18\u001b[0m \u001b[0;36m7.2262678146362305\u001b[0m \u001b[0;36m7\u001b[0m \u001b[0;36m0.17999999999999994\u001b[0m \u001b[0;36m55115.002222789306\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;36m0.24\u001b[0m \u001b[0;36m7.526758670806885\u001b[0m \u001b[0;36m8\u001b[0m \u001b[0;36m0.24\u001b[0m \u001b[0;36m23435.80948452302\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;36m0.3\u001b[0m \u001b[0;36m7.827551603317261\u001b[0m \u001b[0;36m8\u001b[0m \u001b[0;36m0.30000000000000004\u001b[0m \u001b[0;36m6778.657612426362\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;36m0.36\u001b[0m \u001b[0;36m8.128362894058228\u001b[0m \u001b[0;36m8\u001b[0m \u001b[0;36m0.3600000000000001\u001b[0m \u001b[0;36m1452.430915438994\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;36m0.42\u001b[0m \u001b[0;36m8.429659605026245\u001b[0m \u001b[0;36m8\u001b[0m \u001b[0;36m0.41999999999999993\u001b[0m \u001b[0;36m209.45893286868386\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;36m0.48\u001b[0m \u001b[0;36m8.730647563934326\u001b[0m \u001b[0;36m9\u001b[0m \u001b[0;36m0.48\u001b[0m \u001b[0;36m21.197227027464642\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;36m0.54\u001b[0m \u001b[0;36m9.03117322921753\u001b[0m \u001b[0;36m9\u001b[0m \u001b[0;36m0.54\u001b[0m \u001b[0;36m1.4782006686136888\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;36m0.6\u001b[0m \u001b[0;36m9.332942485809326\u001b[0m \u001b[0;36m9\u001b[0m \u001b[0;36m0.5999999999999999\u001b[0m \u001b[0;36m0.07465825810039856\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;36m0.66\u001b[0m \u001b[0;36m9.633116483688354\u001b[0m \u001b[0;36m10\u001b[0m \u001b[0;36m0.6599999999999999\u001b[0m \u001b[0;36m0.0025699632576234584\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;36m0.72\u001b[0m \u001b[0;36m9.934834003448486\u001b[0m \u001b[0;36m10\u001b[0m \u001b[0;36m0.72\u001b[0m \u001b[0;36m6.0122624115778926e-05\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;36m0.78\u001b[0m \u001b[0;36m10.235759019851685\u001b[0m \u001b[0;36m10\u001b[0m \u001b[0;36m0.78\u001b[0m \u001b[0;36m1.0118803168822902e-06\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;36m0.84\u001b[0m \u001b[0;36m10.536261558532715\u001b[0m \u001b[0;36m11\u001b[0m \u001b[0;36m0.8400000000000001\u001b[0m \u001b[0;36m1.146553831796669e-08\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;36m0.9\u001b[0m \u001b[0;36m10.837242603302002\u001b[0m \u001b[0;36m11\u001b[0m \u001b[0;36m0.9000000000000001\u001b[0m \u001b[0;36m9.253341924833811e-11\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;36m0.96\u001b[0m \u001b[0;36m11.13822078704834\u001b[0m \u001b[0;36m11\u001b[0m \u001b[0;36m0.9600000000000002\u001b[0m \u001b[0;36m5.303052899369955e-13\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;36m1.02\u001b[0m \u001b[0;36m11.44105577468872\u001b[0m \u001b[0;36m11\u001b[0m \u001b[0;36m1.0199999999999998\u001b[0m \u001b[0;36m2.0389556278809306e-15\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;36m1.08\u001b[0m \u001b[0;36m11.739804983139038\u001b[0m \u001b[0;36m12\u001b[0m \u001b[0;36m1.0799999999999998\u001b[0m \u001b[0;36m5.598965910069263e-18\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;36m1.1400000000000001\u001b[0m \u001b[0;36m12.041014909744263\u001b[0m \u001b[0;36m12\u001b[0m \u001b[0;36m1.14\u001b[0m \u001b[0;36m1.1034858439554339e-20\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;36m1.2\u001b[0m \u001b[0;36m12.34083604812622\u001b[0m \u001b[0;36m12\u001b[0m \u001b[0;36m1.2\u001b[0m \u001b[0;36m1.4126925870242008e-23\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;31m#C Tue Jan 25 16:05:00 2022. num_events_primary = 41\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;31m#C Tue Jan 25 16:05:00 2022. exit_status = success\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n" - ] - } - ], - "source": [ - "%pycat spec1.dat" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We see that the output of the `specfile_example()` command includes the content of the SPEC file. For the remaining examples, we'll skip this additional step to view the SPEC file contents from disk." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# a specific scan\n", - "\n", - "The `cat` object allows us to access scans by UUID (or any shorter version that remains unique in the database). We show an example but have commented it out since those runs do not exist in our temporary databroker catalog." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [], - "source": [ - "# specfile_example(cat[\"37c188c0\"], filename=\"spec3.dat\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# a list of specific scans, by UID\n", - "\n", - "Suppose we have a list of scans where we know the UID of each one, we can build a list of headers and write a SPEC data file with that list. Here, we have such a list of tuning scans. We show an example but have commented it out since those runs do not exist in our temporary databroker catalog." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [], - "source": [ - "# runs = [cat[uid] for uid in \"957d83c c354fe37-e39f 42c\".split()]\n", - "# specfile_example(runs, filename=\"spec_tunes.dat\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Find specific plans within a range of dates\n", - "\n", - "The `cat` object allows for filtering arguments based on any keywords in the *start* document and also by time. Here, we filter between certain dates and also by `plan name`. The dates are specified in [ISO8601 format](https://www.iso.org/iso-8601-date-and-time-format.html) and can include precision beyond a millisecond. Here, we use the `v2` interface to do the searches. We show examples how to pick between a set of dates.\n", - "\n", - "Also, we write to the default data file: `test_specdata.txt`." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "1ce82e67 2022-01-25 16:04:48.412408\n", - "Look at SPEC data file: test_specdata.txt\n" - ] - } - ], - "source": [ - "from databroker.queries import TimeRange\n", - "import datetime\n", - "\n", - "if os.path.exists(\"test_specdata.txt\"): # re-write the file\n", - " os.remove(\"test_specdata.txt\")\n", - "\n", - "query = {}\n", - "query.update(TimeRange(since=\"2019-02-19 17:00\"))\n", - "query.update(TimeRange(until=\"2032-02-19 17:11:30\"))\n", - "query.update(dict(plan_name=\"scan\"))\n", - "\n", - "runs = cat.v2.search(query)\n", - "for uid in runs:\n", - " run = runs[uid]\n", - " start_time = run.metadata[\"start\"][\"time\"]\n", - " isodate = datetime.datetime.fromtimestamp(start_time).isoformat(sep=\" \")\n", - " print(uid[:8], isodate)\n", - "specfile_example(runs, \"test_specdata.txt\")" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "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.7" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/docs/source/resources/excel_scan.ipynb b/docs/source/resources/excel_scan.ipynb deleted file mode 100644 index 0add21968..000000000 --- a/docs/source/resources/excel_scan.ipynb +++ /dev/null @@ -1,850 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# The *Excel scan* plan\n", - "\n", - "Use a spreadsheet as a multi-sample batch scan tool. \n", - "We'll need a spreadsheet with some things to be done \n", - "and a plan that will read that spreadsheet and act on it. \n", - "\n", - "This plan will:\n", - "\n", - "* [x] use an Excel spreadsheet (for starters)\n", - "* [x] read a table from the spreadsheet file\n", - "* [x] take a single action from each row in the table\n", - "* [x] decide action based on a specific named column in the table\n", - "* [x] report all columns as metadata for the action\n", - "* [x] ignore empty rows\n", - "* [ ] ignore any data outside of the table boundaries\n", - "\n", - "Since the actions and parameters (args & kwargs) will be \n", - "different in every implementation, this may prove difficult \n", - "to generalize.\n", - "\n", - "Here's the demo, starting with the bluesky setup." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "# Import matplotlib and put it in interactive mode.\n", - "%matplotlib notebook\n", - "import matplotlib.pyplot as plt\n", - "plt.ion()\n", - "\n", - "import os\n", - "\n", - "import databroker\n", - "cat = databroker.temp()\n", - "\n", - "from bluesky import RunEngine\n", - "import bluesky.plans as bp\n", - "import bluesky.plan_stubs as bps\n", - "from bluesky.callbacks.best_effort import BestEffortCallback\n", - "from bluesky import SupplementalData\n", - "from bluesky.simulators import summarize_plan\n", - "from bluesky.suspenders import SuspendFloor\n", - "\n", - "from ophyd.sim import motor1, motor2, motor3, SynGauss\n", - "\n", - "import apstools.devices as APS_devices\n", - "import apstools.utils as APS_utils\n", - "\n", - "RE = RunEngine({})\n", - "RE.subscribe(cat.v1.insert)\n", - "RE.subscribe(BestEffortCallback())\n", - "RE.preprocessors.append(SupplementalData())\n", - "\n", - "shutter = APS_devices.SimulatedApsPssShutterWithStatus(name=\"shutter\")\n", - "watch_for_shutter_close = SuspendFloor(shutter.pss_state, 1)\n", - "\n", - "noisy_det = SynGauss('noisy_det', motor1, 'motor1', center=0, Imax=1,\n", - " noise='uniform', sigma=0.9, noise_multiplier=0.1, labels={'detectors'})\n", - "noisy_det.kind = \"hinted\"" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "----\n", - "\n", - "## Excel plan and infrastructure" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "shutter -> open\n", - "motor2 -> 5.07\n", - "motor3 -> 8.3\n", - "Scan_Type: step_scan\n", - "sx: 5.07\n", - "sy: 8.3\n", - "Thickness: 0\n", - "Sample_Name: Water Blank\n", - "remarks: deionized\n", - "code_number: None\n", - "table_row: 1\n", - "Excel_file: /home/prjemian/Documents/projects/BCDA-APS/apstools/docs/source/resources/sample_example.xlsx\n", - "xl_file: sample_example.xlsx\n", - "original_keys: {'Scan_Type': 'Scan Type', 'sx': 'sx', 'sy': 'sy', 'Thickness': 'Thickness', 'Sample_Name': 'Sample Name', 'remarks': 'remarks', 'code_number': 'code number'}\n", - "motor2: 0\n", - "motor3: 0\n", - "shutter: close\n", - "=================================== Open Run ===================================\n", - "motor1 -> -5.0\n", - " Read ['noisy_det', 'motor1']\n", - "motor1 -> -3.571428571428571\n", - " Read ['noisy_det', 'motor1']\n", - "motor1 -> -2.142857142857143\n", - " Read ['noisy_det', 'motor1']\n", - "motor1 -> -0.7142857142857144\n", - " Read ['noisy_det', 'motor1']\n", - "motor1 -> 0.7142857142857144\n", - " Read ['noisy_det', 'motor1']\n", - "motor1 -> 2.1428571428571432\n", - " Read ['noisy_det', 'motor1']\n", - "motor1 -> 3.571428571428571\n", - " Read ['noisy_det', 'motor1']\n", - "motor1 -> 5.0\n", - " Read ['noisy_det', 'motor1']\n", - "================================== Close Run ===================================\n", - "no handling for table row 2: OrderedDict([('Scan Type', 'other_scan'), ('sx', 5.07), ('sy', 8.3), ('Thickness', 0), ('Sample Name', 'Water Blank'), ('remarks', 'deionized'), ('code number', None)])\n", - "no handling for table row 3: OrderedDict([('Scan Type', 'this will be ignored (and also the next blank row will be ignored)'), ('sx', None), ('sy', None), ('Thickness', None), ('Sample Name', None), ('remarks', None), ('code number', None)])\n", - "shutter -> close\n", - "motor1 -> 0\n", - "motor2 -> 0\n", - "motor3 -> 0\n" - ] - } - ], - "source": [ - "def beforeExcelPlan():\n", - " \"\"\"things to be done at the start of every Excel plan\"\"\"\n", - " yield from bps.mv(\n", - " shutter, \"open\", # for example\n", - " )\n", - "\n", - " \n", - "def afterExcelPlan():\n", - " \"\"\"things to be done at the end of every Excel plan\"\"\"\n", - " yield from bps.mv(\n", - " shutter, \"close\", # for example\n", - " motor1, 0, # park the motors\n", - " motor2, 0,\n", - " motor3, 0,\n", - " )\n", - "\n", - "\n", - "def common_step_scan(pos_X, pos_Y, thickness, scan_title, md={}):\n", - " \"\"\"\n", - " run a step scan over a common range at given sample position\n", - " \"\"\"\n", - " yield from bps.mv(\n", - " motor2, pos_X,\n", - " motor3, pos_Y,\n", - " )\n", - " md[motor2.name] = motor2.position\n", - " md[motor3.name] = motor3.position\n", - " md[\"shutter\"] = shutter.state\n", - " for k, v in md.items():\n", - " print(f\"{k}: {v}\")\n", - "\n", - " yield from bps.install_suspender(watch_for_shutter_close)\n", - " yield from bp.scan([noisy_det], motor1, -5, 5, 8, md=md)\n", - " yield from bps.remove_suspender(watch_for_shutter_close)\n", - "\n", - "\n", - "def Excel_plan(xl_file, md={}):\n", - " \"\"\"\n", - " example of reading a list of samples from Excel spreadsheet\n", - " \n", - " USAGE::\n", - " \n", - " summarize_plan(run_Excel_file(\"sample_example.xlsx\"))\n", - " RE(run_Excel_file(\"sample_example.xlsx\"))\n", - " \"\"\"\n", - " excel_file = os.path.abspath(xl_file)\n", - " assert os.path.exists(excel_file)\n", - " xl = APS_utils.ExcelDatabaseFileGeneric(excel_file)\n", - "\n", - " yield from beforeExcelPlan()\n", - " for i, row in enumerate(xl.db.values()):\n", - " # print(f\"row={row}\")\n", - " \n", - " # metadata\n", - " # all parameters from this row go into the metadata\n", - " # columns names are the keys in the metadata dictionary\n", - " # make sure md keys are \"clean\"\n", - " # also provide crossreference to original column names\n", - " _md = {APS_utils.cleanupText(k): v for k, v in row.items()}\n", - " _md[\"table_row\"] = i+1\n", - " _md[\"Excel_file\"] = excel_file\n", - " _md[\"xl_file\"] = xl_file\n", - " _md[\"original_keys\"] = {APS_utils.cleanupText(k): k for k in row.keys()}\n", - " _md.update(md) # overlay with user-supplied metadata\n", - "\n", - " scan_command = (row[\"Scan Type\"] or \"\").lower()\n", - " if scan_command == \"step_scan\":\n", - " yield from common_step_scan(\n", - " row[\"sx\"], # label must match cell string EXACTLY\n", - " row[\"sy\"], \n", - " row[\"Thickness\"], \n", - " row[\"Sample Name\"],\n", - " # add all input as scan metadata, ensure the keys are clean\n", - " md=_md,\n", - " )\n", - " elif scan_command == \"some_other_action\":\n", - " pass # TODO: suggestion\n", - " else:\n", - " print(f\"no handling for table row {i+1}: {row}\")\n", - " yield from afterExcelPlan()\n", - "\n", - "\n", - "summarize_plan(Excel_plan(\"sample_example.xlsx\"))" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Scan_Type: step_scan\n", - "sx: 5.07\n", - "sy: 8.3\n", - "Thickness: 0\n", - "Sample_Name: Water Blank\n", - "remarks: deionized\n", - "code_number: None\n", - "table_row: 1\n", - "Excel_file: /home/prjemian/Documents/projects/BCDA-APS/apstools/docs/source/resources/sample_example.xlsx\n", - "xl_file: sample_example.xlsx\n", - "original_keys: {'Scan_Type': 'Scan Type', 'sx': 'sx', 'sy': 'sy', 'Thickness': 'Thickness', 'Sample_Name': 'Sample Name', 'remarks': 'remarks', 'code_number': 'code number'}\n", - "motor2: 5.07\n", - "motor3: 8.3\n", - "shutter: open\n", - "\n", - "\n", - "Transient Scan ID: 1 Time: 2022-01-20 17:32:23\n", - "Persistent Unique Scan ID: 'f70b00dd-12a3-4204-8823-dbc2f7e8c985'\n", - "New stream: 'primary'\n" - ] - }, - { - "data": { - "application/javascript": "/* Put everything inside the global mpl namespace */\n/* global mpl */\nwindow.mpl = {};\n\nmpl.get_websocket_type = function () {\n if (typeof WebSocket !== 'undefined') {\n return WebSocket;\n } else if (typeof MozWebSocket !== 'undefined') {\n return MozWebSocket;\n } else {\n alert(\n 'Your browser does not have WebSocket support. ' +\n 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n 'Firefox 4 and 5 are also supported but you ' +\n 'have to enable WebSockets in about:config.'\n );\n }\n};\n\nmpl.figure = function (figure_id, websocket, ondownload, parent_element) {\n this.id = figure_id;\n\n this.ws = websocket;\n\n this.supports_binary = this.ws.binaryType !== undefined;\n\n if (!this.supports_binary) {\n var warnings = document.getElementById('mpl-warnings');\n if (warnings) {\n warnings.style.display = 'block';\n warnings.textContent =\n 'This browser does not support binary websocket messages. ' +\n 'Performance may be slow.';\n }\n }\n\n this.imageObj = new Image();\n\n this.context = undefined;\n this.message = undefined;\n this.canvas = undefined;\n this.rubberband_canvas = undefined;\n this.rubberband_context = undefined;\n this.format_dropdown = undefined;\n\n this.image_mode = 'full';\n\n this.root = document.createElement('div');\n this.root.setAttribute('style', 'display: inline-block');\n this._root_extra_style(this.root);\n\n parent_element.appendChild(this.root);\n\n this._init_header(this);\n this._init_canvas(this);\n this._init_toolbar(this);\n\n var fig = this;\n\n this.waiting = false;\n\n this.ws.onopen = function () {\n fig.send_message('supports_binary', { value: fig.supports_binary });\n fig.send_message('send_image_mode', {});\n if (fig.ratio !== 1) {\n fig.send_message('set_dpi_ratio', { dpi_ratio: fig.ratio });\n }\n fig.send_message('refresh', {});\n };\n\n this.imageObj.onload = function () {\n if (fig.image_mode === 'full') {\n // Full images could contain transparency (where diff images\n // almost always do), so we need to clear the canvas so that\n // there is no ghosting.\n fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n }\n fig.context.drawImage(fig.imageObj, 0, 0);\n };\n\n this.imageObj.onunload = function () {\n fig.ws.close();\n };\n\n this.ws.onmessage = this._make_on_message_function(this);\n\n this.ondownload = ondownload;\n};\n\nmpl.figure.prototype._init_header = function () {\n var titlebar = document.createElement('div');\n titlebar.classList =\n 'ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix';\n var titletext = document.createElement('div');\n titletext.classList = 'ui-dialog-title';\n titletext.setAttribute(\n 'style',\n 'width: 100%; text-align: center; padding: 3px;'\n );\n titlebar.appendChild(titletext);\n this.root.appendChild(titlebar);\n this.header = titletext;\n};\n\nmpl.figure.prototype._canvas_extra_style = function (_canvas_div) {};\n\nmpl.figure.prototype._root_extra_style = function (_canvas_div) {};\n\nmpl.figure.prototype._init_canvas = function () {\n var fig = this;\n\n var canvas_div = (this.canvas_div = document.createElement('div'));\n canvas_div.setAttribute(\n 'style',\n 'border: 1px solid #ddd;' +\n 'box-sizing: content-box;' +\n 'clear: both;' +\n 'min-height: 1px;' +\n 'min-width: 1px;' +\n 'outline: 0;' +\n 'overflow: hidden;' +\n 'position: relative;' +\n 'resize: both;'\n );\n\n function on_keyboard_event_closure(name) {\n return function (event) {\n return fig.key_event(event, name);\n };\n }\n\n canvas_div.addEventListener(\n 'keydown',\n on_keyboard_event_closure('key_press')\n );\n canvas_div.addEventListener(\n 'keyup',\n on_keyboard_event_closure('key_release')\n );\n\n this._canvas_extra_style(canvas_div);\n this.root.appendChild(canvas_div);\n\n var canvas = (this.canvas = document.createElement('canvas'));\n canvas.classList.add('mpl-canvas');\n canvas.setAttribute('style', 'box-sizing: content-box;');\n\n this.context = canvas.getContext('2d');\n\n var backingStore =\n this.context.backingStorePixelRatio ||\n this.context.webkitBackingStorePixelRatio ||\n this.context.mozBackingStorePixelRatio ||\n this.context.msBackingStorePixelRatio ||\n this.context.oBackingStorePixelRatio ||\n this.context.backingStorePixelRatio ||\n 1;\n\n this.ratio = (window.devicePixelRatio || 1) / backingStore;\n\n var rubberband_canvas = (this.rubberband_canvas = document.createElement(\n 'canvas'\n ));\n rubberband_canvas.setAttribute(\n 'style',\n 'box-sizing: content-box; position: absolute; left: 0; top: 0; z-index: 1;'\n );\n\n // Apply a ponyfill if ResizeObserver is not implemented by browser.\n if (this.ResizeObserver === undefined) {\n if (window.ResizeObserver !== undefined) {\n this.ResizeObserver = window.ResizeObserver;\n } else {\n var obs = _JSXTOOLS_RESIZE_OBSERVER({});\n this.ResizeObserver = obs.ResizeObserver;\n }\n }\n\n this.resizeObserverInstance = new this.ResizeObserver(function (entries) {\n var nentries = entries.length;\n for (var i = 0; i < nentries; i++) {\n var entry = entries[i];\n var width, height;\n if (entry.contentBoxSize) {\n if (entry.contentBoxSize instanceof Array) {\n // Chrome 84 implements new version of spec.\n width = entry.contentBoxSize[0].inlineSize;\n height = entry.contentBoxSize[0].blockSize;\n } else {\n // Firefox implements old version of spec.\n width = entry.contentBoxSize.inlineSize;\n height = entry.contentBoxSize.blockSize;\n }\n } else {\n // Chrome <84 implements even older version of spec.\n width = entry.contentRect.width;\n height = entry.contentRect.height;\n }\n\n // Keep the size of the canvas and rubber band canvas in sync with\n // the canvas container.\n if (entry.devicePixelContentBoxSize) {\n // Chrome 84 implements new version of spec.\n canvas.setAttribute(\n 'width',\n entry.devicePixelContentBoxSize[0].inlineSize\n );\n canvas.setAttribute(\n 'height',\n entry.devicePixelContentBoxSize[0].blockSize\n );\n } else {\n canvas.setAttribute('width', width * fig.ratio);\n canvas.setAttribute('height', height * fig.ratio);\n }\n canvas.setAttribute(\n 'style',\n 'width: ' + width + 'px; height: ' + height + 'px;'\n );\n\n rubberband_canvas.setAttribute('width', width);\n rubberband_canvas.setAttribute('height', height);\n\n // And update the size in Python. We ignore the initial 0/0 size\n // that occurs as the element is placed into the DOM, which should\n // otherwise not happen due to the minimum size styling.\n if (fig.ws.readyState == 1 && width != 0 && height != 0) {\n fig.request_resize(width, height);\n }\n }\n });\n this.resizeObserverInstance.observe(canvas_div);\n\n function on_mouse_event_closure(name) {\n return function (event) {\n return fig.mouse_event(event, name);\n };\n }\n\n rubberband_canvas.addEventListener(\n 'mousedown',\n on_mouse_event_closure('button_press')\n );\n rubberband_canvas.addEventListener(\n 'mouseup',\n on_mouse_event_closure('button_release')\n );\n rubberband_canvas.addEventListener(\n 'dblclick',\n on_mouse_event_closure('dblclick')\n );\n // Throttle sequential mouse events to 1 every 20ms.\n rubberband_canvas.addEventListener(\n 'mousemove',\n on_mouse_event_closure('motion_notify')\n );\n\n rubberband_canvas.addEventListener(\n 'mouseenter',\n on_mouse_event_closure('figure_enter')\n );\n rubberband_canvas.addEventListener(\n 'mouseleave',\n on_mouse_event_closure('figure_leave')\n );\n\n canvas_div.addEventListener('wheel', function (event) {\n if (event.deltaY < 0) {\n event.step = 1;\n } else {\n event.step = -1;\n }\n on_mouse_event_closure('scroll')(event);\n });\n\n canvas_div.appendChild(canvas);\n canvas_div.appendChild(rubberband_canvas);\n\n this.rubberband_context = rubberband_canvas.getContext('2d');\n this.rubberband_context.strokeStyle = '#000000';\n\n this._resize_canvas = function (width, height, forward) {\n if (forward) {\n canvas_div.style.width = width + 'px';\n canvas_div.style.height = height + 'px';\n }\n };\n\n // Disable right mouse context menu.\n this.rubberband_canvas.addEventListener('contextmenu', function (_e) {\n event.preventDefault();\n return false;\n });\n\n function set_focus() {\n canvas.focus();\n canvas_div.focus();\n }\n\n window.setTimeout(set_focus, 100);\n};\n\nmpl.figure.prototype._init_toolbar = function () {\n var fig = this;\n\n var toolbar = document.createElement('div');\n toolbar.classList = 'mpl-toolbar';\n this.root.appendChild(toolbar);\n\n function on_click_closure(name) {\n return function (_event) {\n return fig.toolbar_button_onclick(name);\n };\n }\n\n function on_mouseover_closure(tooltip) {\n return function (event) {\n if (!event.currentTarget.disabled) {\n return fig.toolbar_button_onmouseover(tooltip);\n }\n };\n }\n\n fig.buttons = {};\n var buttonGroup = document.createElement('div');\n buttonGroup.classList = 'mpl-button-group';\n for (var toolbar_ind in mpl.toolbar_items) {\n var name = mpl.toolbar_items[toolbar_ind][0];\n var tooltip = mpl.toolbar_items[toolbar_ind][1];\n var image = mpl.toolbar_items[toolbar_ind][2];\n var method_name = mpl.toolbar_items[toolbar_ind][3];\n\n if (!name) {\n /* Instead of a spacer, we start a new button group. */\n if (buttonGroup.hasChildNodes()) {\n toolbar.appendChild(buttonGroup);\n }\n buttonGroup = document.createElement('div');\n buttonGroup.classList = 'mpl-button-group';\n continue;\n }\n\n var button = (fig.buttons[name] = document.createElement('button'));\n button.classList = 'mpl-widget';\n button.setAttribute('role', 'button');\n button.setAttribute('aria-disabled', 'false');\n button.addEventListener('click', on_click_closure(method_name));\n button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n\n var icon_img = document.createElement('img');\n icon_img.src = '_images/' + image + '.png';\n icon_img.srcset = '_images/' + image + '_large.png 2x';\n icon_img.alt = tooltip;\n button.appendChild(icon_img);\n\n buttonGroup.appendChild(button);\n }\n\n if (buttonGroup.hasChildNodes()) {\n toolbar.appendChild(buttonGroup);\n }\n\n var fmt_picker = document.createElement('select');\n fmt_picker.classList = 'mpl-widget';\n toolbar.appendChild(fmt_picker);\n this.format_dropdown = fmt_picker;\n\n for (var ind in mpl.extensions) {\n var fmt = mpl.extensions[ind];\n var option = document.createElement('option');\n option.selected = fmt === mpl.default_extension;\n option.innerHTML = fmt;\n fmt_picker.appendChild(option);\n }\n\n var status_bar = document.createElement('span');\n status_bar.classList = 'mpl-message';\n toolbar.appendChild(status_bar);\n this.message = status_bar;\n};\n\nmpl.figure.prototype.request_resize = function (x_pixels, y_pixels) {\n // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n // which will in turn request a refresh of the image.\n this.send_message('resize', { width: x_pixels, height: y_pixels });\n};\n\nmpl.figure.prototype.send_message = function (type, properties) {\n properties['type'] = type;\n properties['figure_id'] = this.id;\n this.ws.send(JSON.stringify(properties));\n};\n\nmpl.figure.prototype.send_draw_message = function () {\n if (!this.waiting) {\n this.waiting = true;\n this.ws.send(JSON.stringify({ type: 'draw', figure_id: this.id }));\n }\n};\n\nmpl.figure.prototype.handle_save = function (fig, _msg) {\n var format_dropdown = fig.format_dropdown;\n var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n fig.ondownload(fig, format);\n};\n\nmpl.figure.prototype.handle_resize = function (fig, msg) {\n var size = msg['size'];\n if (size[0] !== fig.canvas.width || size[1] !== fig.canvas.height) {\n fig._resize_canvas(size[0], size[1], msg['forward']);\n fig.send_message('refresh', {});\n }\n};\n\nmpl.figure.prototype.handle_rubberband = function (fig, msg) {\n var x0 = msg['x0'] / fig.ratio;\n var y0 = (fig.canvas.height - msg['y0']) / fig.ratio;\n var x1 = msg['x1'] / fig.ratio;\n var y1 = (fig.canvas.height - msg['y1']) / fig.ratio;\n x0 = Math.floor(x0) + 0.5;\n y0 = Math.floor(y0) + 0.5;\n x1 = Math.floor(x1) + 0.5;\n y1 = Math.floor(y1) + 0.5;\n var min_x = Math.min(x0, x1);\n var min_y = Math.min(y0, y1);\n var width = Math.abs(x1 - x0);\n var height = Math.abs(y1 - y0);\n\n fig.rubberband_context.clearRect(\n 0,\n 0,\n fig.canvas.width / fig.ratio,\n fig.canvas.height / fig.ratio\n );\n\n fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n};\n\nmpl.figure.prototype.handle_figure_label = function (fig, msg) {\n // Updates the figure title.\n fig.header.textContent = msg['label'];\n};\n\nmpl.figure.prototype.handle_cursor = function (fig, msg) {\n var cursor = msg['cursor'];\n switch (cursor) {\n case 0:\n cursor = 'pointer';\n break;\n case 1:\n cursor = 'default';\n break;\n case 2:\n cursor = 'crosshair';\n break;\n case 3:\n cursor = 'move';\n break;\n }\n fig.rubberband_canvas.style.cursor = cursor;\n};\n\nmpl.figure.prototype.handle_message = function (fig, msg) {\n fig.message.textContent = msg['message'];\n};\n\nmpl.figure.prototype.handle_draw = function (fig, _msg) {\n // Request the server to send over a new figure.\n fig.send_draw_message();\n};\n\nmpl.figure.prototype.handle_image_mode = function (fig, msg) {\n fig.image_mode = msg['mode'];\n};\n\nmpl.figure.prototype.handle_history_buttons = function (fig, msg) {\n for (var key in msg) {\n if (!(key in fig.buttons)) {\n continue;\n }\n fig.buttons[key].disabled = !msg[key];\n fig.buttons[key].setAttribute('aria-disabled', !msg[key]);\n }\n};\n\nmpl.figure.prototype.handle_navigate_mode = function (fig, msg) {\n if (msg['mode'] === 'PAN') {\n fig.buttons['Pan'].classList.add('active');\n fig.buttons['Zoom'].classList.remove('active');\n } else if (msg['mode'] === 'ZOOM') {\n fig.buttons['Pan'].classList.remove('active');\n fig.buttons['Zoom'].classList.add('active');\n } else {\n fig.buttons['Pan'].classList.remove('active');\n fig.buttons['Zoom'].classList.remove('active');\n }\n};\n\nmpl.figure.prototype.updated_canvas_event = function () {\n // Called whenever the canvas gets updated.\n this.send_message('ack', {});\n};\n\n// A function to construct a web socket function for onmessage handling.\n// Called in the figure constructor.\nmpl.figure.prototype._make_on_message_function = function (fig) {\n return function socket_on_message(evt) {\n if (evt.data instanceof Blob) {\n var img = evt.data;\n if (img.type !== 'image/png') {\n /* FIXME: We get \"Resource interpreted as Image but\n * transferred with MIME type text/plain:\" errors on\n * Chrome. But how to set the MIME type? It doesn't seem\n * to be part of the websocket stream */\n img.type = 'image/png';\n }\n\n /* Free the memory for the previous frames */\n if (fig.imageObj.src) {\n (window.URL || window.webkitURL).revokeObjectURL(\n fig.imageObj.src\n );\n }\n\n fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n img\n );\n fig.updated_canvas_event();\n fig.waiting = false;\n return;\n } else if (\n typeof evt.data === 'string' &&\n evt.data.slice(0, 21) === 'data:image/png;base64'\n ) {\n fig.imageObj.src = evt.data;\n fig.updated_canvas_event();\n fig.waiting = false;\n return;\n }\n\n var msg = JSON.parse(evt.data);\n var msg_type = msg['type'];\n\n // Call the \"handle_{type}\" callback, which takes\n // the figure and JSON message as its only arguments.\n try {\n var callback = fig['handle_' + msg_type];\n } catch (e) {\n console.log(\n \"No handler for the '\" + msg_type + \"' message type: \",\n msg\n );\n return;\n }\n\n if (callback) {\n try {\n // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n callback(fig, msg);\n } catch (e) {\n console.log(\n \"Exception inside the 'handler_\" + msg_type + \"' callback:\",\n e,\n e.stack,\n msg\n );\n }\n }\n };\n};\n\n// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\nmpl.findpos = function (e) {\n //this section is from http://www.quirksmode.org/js/events_properties.html\n var targ;\n if (!e) {\n e = window.event;\n }\n if (e.target) {\n targ = e.target;\n } else if (e.srcElement) {\n targ = e.srcElement;\n }\n if (targ.nodeType === 3) {\n // defeat Safari bug\n targ = targ.parentNode;\n }\n\n // pageX,Y are the mouse positions relative to the document\n var boundingRect = targ.getBoundingClientRect();\n var x = e.pageX - (boundingRect.left + document.body.scrollLeft);\n var y = e.pageY - (boundingRect.top + document.body.scrollTop);\n\n return { x: x, y: y };\n};\n\n/*\n * return a copy of an object with only non-object keys\n * we need this to avoid circular references\n * http://stackoverflow.com/a/24161582/3208463\n */\nfunction simpleKeys(original) {\n return Object.keys(original).reduce(function (obj, key) {\n if (typeof original[key] !== 'object') {\n obj[key] = original[key];\n }\n return obj;\n }, {});\n}\n\nmpl.figure.prototype.mouse_event = function (event, name) {\n var canvas_pos = mpl.findpos(event);\n\n if (name === 'button_press') {\n this.canvas.focus();\n this.canvas_div.focus();\n }\n\n var x = canvas_pos.x * this.ratio;\n var y = canvas_pos.y * this.ratio;\n\n this.send_message(name, {\n x: x,\n y: y,\n button: event.button,\n step: event.step,\n guiEvent: simpleKeys(event),\n });\n\n /* This prevents the web browser from automatically changing to\n * the text insertion cursor when the button is pressed. We want\n * to control all of the cursor setting manually through the\n * 'cursor' event from matplotlib */\n event.preventDefault();\n return false;\n};\n\nmpl.figure.prototype._key_event_extra = function (_event, _name) {\n // Handle any extra behaviour associated with a key event\n};\n\nmpl.figure.prototype.key_event = function (event, name) {\n // Prevent repeat events\n if (name === 'key_press') {\n if (event.key === this._key) {\n return;\n } else {\n this._key = event.key;\n }\n }\n if (name === 'key_release') {\n this._key = null;\n }\n\n var value = '';\n if (event.ctrlKey && event.key !== 'Control') {\n value += 'ctrl+';\n }\n else if (event.altKey && event.key !== 'Alt') {\n value += 'alt+';\n }\n else if (event.shiftKey && event.key !== 'Shift') {\n value += 'shift+';\n }\n\n value += 'k' + event.key;\n\n this._key_event_extra(event, name);\n\n this.send_message(name, { key: value, guiEvent: simpleKeys(event) });\n return false;\n};\n\nmpl.figure.prototype.toolbar_button_onclick = function (name) {\n if (name === 'download') {\n this.handle_save(this, null);\n } else {\n this.send_message('toolbar_button', { name: name });\n }\n};\n\nmpl.figure.prototype.toolbar_button_onmouseover = function (tooltip) {\n this.message.textContent = tooltip;\n};\n\n///////////////// REMAINING CONTENT GENERATED BY embed_js.py /////////////////\n// prettier-ignore\nvar _JSXTOOLS_RESIZE_OBSERVER=function(A){var t,i=new WeakMap,n=new WeakMap,a=new WeakMap,r=new WeakMap,o=new Set;function s(e){if(!(this instanceof s))throw new TypeError(\"Constructor requires 'new' operator\");i.set(this,e)}function h(){throw new TypeError(\"Function is not a constructor\")}function c(e,t,i,n){e=0 in arguments?Number(arguments[0]):0,t=1 in arguments?Number(arguments[1]):0,i=2 in arguments?Number(arguments[2]):0,n=3 in arguments?Number(arguments[3]):0,this.right=(this.x=this.left=e)+(this.width=i),this.bottom=(this.y=this.top=t)+(this.height=n),Object.freeze(this)}function d(){t=requestAnimationFrame(d);var s=new WeakMap,p=new Set;o.forEach((function(t){r.get(t).forEach((function(i){var r=t instanceof window.SVGElement,o=a.get(t),d=r?0:parseFloat(o.paddingTop),f=r?0:parseFloat(o.paddingRight),l=r?0:parseFloat(o.paddingBottom),u=r?0:parseFloat(o.paddingLeft),g=r?0:parseFloat(o.borderTopWidth),m=r?0:parseFloat(o.borderRightWidth),w=r?0:parseFloat(o.borderBottomWidth),b=u+f,F=d+l,v=(r?0:parseFloat(o.borderLeftWidth))+m,W=g+w,y=r?0:t.offsetHeight-W-t.clientHeight,E=r?0:t.offsetWidth-v-t.clientWidth,R=b+v,z=F+W,M=r?t.width:parseFloat(o.width)-R-E,O=r?t.height:parseFloat(o.height)-z-y;if(n.has(t)){var k=n.get(t);if(k[0]===M&&k[1]===O)return}n.set(t,[M,O]);var S=Object.create(h.prototype);S.target=t,S.contentRect=new c(u,d,M,O),s.has(i)||(s.set(i,[]),p.add(i)),s.get(i).push(S)}))})),p.forEach((function(e){i.get(e).call(e,s.get(e),e)}))}return s.prototype.observe=function(i){if(i instanceof window.Element){r.has(i)||(r.set(i,new Set),o.add(i),a.set(i,window.getComputedStyle(i)));var n=r.get(i);n.has(this)||n.add(this),cancelAnimationFrame(t),t=requestAnimationFrame(d)}},s.prototype.unobserve=function(i){if(i instanceof window.Element&&r.has(i)){var n=r.get(i);n.has(this)&&(n.delete(this),n.size||(r.delete(i),o.delete(i))),n.size||r.delete(i),o.size||cancelAnimationFrame(t)}},A.DOMRectReadOnly=c,A.ResizeObserver=s,A.ResizeObserverEntry=h,A}; // eslint-disable-line\nmpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Left button pans, Right button zooms\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n\nmpl.extensions = [\"eps\", \"jpeg\", \"pgf\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n\nmpl.default_extension = \"png\";/* global mpl */\n\nvar comm_websocket_adapter = function (comm) {\n // Create a \"websocket\"-like object which calls the given IPython comm\n // object with the appropriate methods. Currently this is a non binary\n // socket, so there is still some room for performance tuning.\n var ws = {};\n\n ws.binaryType = comm.kernel.ws.binaryType;\n ws.readyState = comm.kernel.ws.readyState;\n function updateReadyState(_event) {\n if (comm.kernel.ws) {\n ws.readyState = comm.kernel.ws.readyState;\n } else {\n ws.readyState = 3; // Closed state.\n }\n }\n comm.kernel.ws.addEventListener('open', updateReadyState);\n comm.kernel.ws.addEventListener('close', updateReadyState);\n comm.kernel.ws.addEventListener('error', updateReadyState);\n\n ws.close = function () {\n comm.close();\n };\n ws.send = function (m) {\n //console.log('sending', m);\n comm.send(m);\n };\n // Register the callback with on_msg.\n comm.on_msg(function (msg) {\n //console.log('receiving', msg['content']['data'], msg);\n var data = msg['content']['data'];\n if (data['blob'] !== undefined) {\n data = {\n data: new Blob(msg['buffers'], { type: data['blob'] }),\n };\n }\n // Pass the mpl event to the overridden (by mpl) onmessage function.\n ws.onmessage(data);\n });\n return ws;\n};\n\nmpl.mpl_figure_comm = function (comm, msg) {\n // This is the function which gets called when the mpl process\n // starts-up an IPython Comm through the \"matplotlib\" channel.\n\n var id = msg.content.data.id;\n // Get hold of the div created by the display call when the Comm\n // socket was opened in Python.\n var element = document.getElementById(id);\n var ws_proxy = comm_websocket_adapter(comm);\n\n function ondownload(figure, _format) {\n window.open(figure.canvas.toDataURL());\n }\n\n var fig = new mpl.figure(id, ws_proxy, ondownload, element);\n\n // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n // web socket which is closed, not our websocket->open comm proxy.\n ws_proxy.onopen();\n\n fig.parent_element = element;\n fig.cell_info = mpl.find_output_cell(\"
\");\n if (!fig.cell_info) {\n console.error('Failed to find cell for figure', id, fig);\n return;\n }\n fig.cell_info[0].output_area.element.on(\n 'cleared',\n { fig: fig },\n fig._remove_fig_handler\n );\n};\n\nmpl.figure.prototype.handle_close = function (fig, msg) {\n var width = fig.canvas.width / fig.ratio;\n fig.cell_info[0].output_area.element.off(\n 'cleared',\n fig._remove_fig_handler\n );\n fig.resizeObserverInstance.unobserve(fig.canvas_div);\n\n // Update the output cell to use the data from the current canvas.\n fig.push_to_output();\n var dataURL = fig.canvas.toDataURL();\n // Re-enable the keyboard manager in IPython - without this line, in FF,\n // the notebook keyboard shortcuts fail.\n IPython.keyboard_manager.enable();\n fig.parent_element.innerHTML =\n '';\n fig.close_ws(fig, msg);\n};\n\nmpl.figure.prototype.close_ws = function (fig, msg) {\n fig.send_message('closing', msg);\n // fig.ws.close()\n};\n\nmpl.figure.prototype.push_to_output = function (_remove_interactive) {\n // Turn the data on the canvas into data in the output cell.\n var width = this.canvas.width / this.ratio;\n var dataURL = this.canvas.toDataURL();\n this.cell_info[1]['text/html'] =\n '';\n};\n\nmpl.figure.prototype.updated_canvas_event = function () {\n // Tell IPython that the notebook contents must change.\n IPython.notebook.set_dirty(true);\n this.send_message('ack', {});\n var fig = this;\n // Wait a second, then push the new image to the DOM so\n // that it is saved nicely (might be nice to debounce this).\n setTimeout(function () {\n fig.push_to_output();\n }, 1000);\n};\n\nmpl.figure.prototype._init_toolbar = function () {\n var fig = this;\n\n var toolbar = document.createElement('div');\n toolbar.classList = 'btn-toolbar';\n this.root.appendChild(toolbar);\n\n function on_click_closure(name) {\n return function (_event) {\n return fig.toolbar_button_onclick(name);\n };\n }\n\n function on_mouseover_closure(tooltip) {\n return function (event) {\n if (!event.currentTarget.disabled) {\n return fig.toolbar_button_onmouseover(tooltip);\n }\n };\n }\n\n fig.buttons = {};\n var buttonGroup = document.createElement('div');\n buttonGroup.classList = 'btn-group';\n var button;\n for (var toolbar_ind in mpl.toolbar_items) {\n var name = mpl.toolbar_items[toolbar_ind][0];\n var tooltip = mpl.toolbar_items[toolbar_ind][1];\n var image = mpl.toolbar_items[toolbar_ind][2];\n var method_name = mpl.toolbar_items[toolbar_ind][3];\n\n if (!name) {\n /* Instead of a spacer, we start a new button group. */\n if (buttonGroup.hasChildNodes()) {\n toolbar.appendChild(buttonGroup);\n }\n buttonGroup = document.createElement('div');\n buttonGroup.classList = 'btn-group';\n continue;\n }\n\n button = fig.buttons[name] = document.createElement('button');\n button.classList = 'btn btn-default';\n button.href = '#';\n button.title = name;\n button.innerHTML = '';\n button.addEventListener('click', on_click_closure(method_name));\n button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n buttonGroup.appendChild(button);\n }\n\n if (buttonGroup.hasChildNodes()) {\n toolbar.appendChild(buttonGroup);\n }\n\n // Add the status bar.\n var status_bar = document.createElement('span');\n status_bar.classList = 'mpl-message pull-right';\n toolbar.appendChild(status_bar);\n this.message = status_bar;\n\n // Add the close button to the window.\n var buttongrp = document.createElement('div');\n buttongrp.classList = 'btn-group inline pull-right';\n button = document.createElement('button');\n button.classList = 'btn btn-mini btn-primary';\n button.href = '#';\n button.title = 'Stop Interaction';\n button.innerHTML = '';\n button.addEventListener('click', function (_evt) {\n fig.handle_close(fig, {});\n });\n button.addEventListener(\n 'mouseover',\n on_mouseover_closure('Stop Interaction')\n );\n buttongrp.appendChild(button);\n var titlebar = this.root.querySelector('.ui-dialog-titlebar');\n titlebar.insertBefore(buttongrp, titlebar.firstChild);\n};\n\nmpl.figure.prototype._remove_fig_handler = function (event) {\n var fig = event.data.fig;\n if (event.target !== this) {\n // Ignore bubbled events from children.\n return;\n }\n fig.close_ws(fig, {});\n};\n\nmpl.figure.prototype._root_extra_style = function (el) {\n el.style.boxSizing = 'content-box'; // override notebook setting of border-box.\n};\n\nmpl.figure.prototype._canvas_extra_style = function (el) {\n // this is important to make the div 'focusable\n el.setAttribute('tabindex', 0);\n // reach out to IPython and tell the keyboard manager to turn it's self\n // off when our div gets focus\n\n // location in version 3\n if (IPython.notebook.keyboard_manager) {\n IPython.notebook.keyboard_manager.register_events(el);\n } else {\n // location in version 2\n IPython.keyboard_manager.register_events(el);\n }\n};\n\nmpl.figure.prototype._key_event_extra = function (event, _name) {\n var manager = IPython.notebook.keyboard_manager;\n if (!manager) {\n manager = IPython.keyboard_manager;\n }\n\n // Check for shift+enter\n if (event.shiftKey && event.which === 13) {\n this.canvas_div.blur();\n // select the cell after this one\n var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n IPython.notebook.select(index + 1);\n }\n};\n\nmpl.figure.prototype.handle_save = function (fig, _msg) {\n fig.ondownload(fig, null);\n};\n\nmpl.find_output_cell = function (html_output) {\n // Return the cell and output element which can be found *uniquely* in the notebook.\n // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n // IPython event is triggered only after the cells have been serialised, which for\n // our purposes (turning an active figure into a static one), is too late.\n var cells = IPython.notebook.get_cells();\n var ncells = cells.length;\n for (var i = 0; i < ncells; i++) {\n var cell = cells[i];\n if (cell.cell_type === 'code') {\n for (var j = 0; j < cell.output_area.outputs.length; j++) {\n var data = cell.output_area.outputs[j];\n if (data.data) {\n // IPython >= 3 moved mimebundle to data attribute of output\n data = data.data;\n }\n if (data['text/html'] === html_output) {\n return [cell, data, j];\n }\n }\n }\n }\n};\n\n// Register the function which deals with the matplotlib target/channel.\n// The kernel may be null if the page has been refreshed.\nif (IPython.notebook.kernel !== null) {\n IPython.notebook.kernel.comm_manager.register_target(\n 'matplotlib',\n mpl.mpl_figure_comm\n );\n}\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "+-----------+------------+------------+------------+\n", - "| seq_num | time | motor1 | noisy_det |\n", - "+-----------+------------+------------+------------+\n", - "| 1 | 17:32:23.6 | -5.000 | 0.064 |\n", - "| 2 | 17:32:23.6 | -3.571 | 0.076 |\n", - "| 3 | 17:32:23.6 | -2.143 | 0.130 |\n", - "| 4 | 17:32:23.6 | -0.714 | 0.699 |\n", - "| 5 | 17:32:23.6 | 0.714 | 0.657 |\n", - "| 6 | 17:32:23.6 | 2.143 | 0.119 |\n", - "| 7 | 17:32:23.6 | 3.571 | 0.004 |\n", - "| 8 | 17:32:23.6 | 5.000 | -0.024 |\n", - "+-----------+------------+------------+------------+\n", - "generator scan ['f70b00dd'] (scan num: 1)\n", - "\n", - "\n", - "\n", - "no handling for table row 2: OrderedDict([('Scan Type', 'other_scan'), ('sx', 5.07), ('sy', 8.3), ('Thickness', 0), ('Sample Name', 'Water Blank'), ('remarks', 'deionized'), ('code number', None)])\n", - "no handling for table row 3: OrderedDict([('Scan Type', 'this will be ignored (and also the next blank row will be ignored)'), ('sx', None), ('sy', None), ('Thickness', None), ('Sample Name', None), ('remarks', None), ('code number', None)])\n" - ] - }, - { - "data": { - "text/plain": [ - "('f70b00dd-12a3-4204-8823-dbc2f7e8c985',)" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "RE(Excel_plan(\"sample_example.xlsx\"))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Show what was collected by accessing the most recent run from the catalog." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "\n", - "\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "\n", - "
\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
Start
code_number None
detectors
noisy_det
Excel_file /home/prjemian/Documents/projects/BCDA-APS/apstools/docs/source/resources/sample_example.xlsx
hints \n", - " \n", - " \n", - " \n", - " \n", - "
dimensions
[['motor1'], 'primary']
motor2 5.07
motor3 8.3
motors
motor1
num_intervals 7
num_points 8
original_keys \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
code_number code number
remarks remarks
Sample_Name Sample Name
Scan_Type Scan Type
sx sx
sy sy
Thickness Thickness
plan_args \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
args
SynAxis(prefix='', name='motor1', read_attrs=['readback', 'setpoint'], configuration_attrs=['velocity', 'acceleration'])
-5
5
detectors
SynGauss(prefix='', name='noisy_det', read_attrs=['val'], configuration_attrs=['Imax', 'center', 'sigma', 'noise', 'noise_multiplier'])
num 8
per_step None
plan_name scan
plan_pattern inner_product
plan_pattern_args \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
args
SynAxis(prefix='', name='motor1', read_attrs=['readback', 'setpoint'], configuration_attrs=['velocity', 'acceleration'])
-5
5
num 8
plan_pattern_module bluesky.plan_patterns
plan_type generator
remarks deionized
Sample_Name Water Blank
scan_id 1
Scan_Type step_scan
shutter open
sx 5.07
sy 8.3
table_row 1
Thickness 0
time a second ago (2022-01-20T17:32:23.504197)
uid f70b00dd-12a3-4204-8823-dbc2f7e8c985
versions \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
bluesky 1.8.1
ophyd 1.6.3
xl_file sample_example.xlsx
\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
Stop
exit_status success
num_events \n", - " \n", - " \n", - " \n", - " \n", - "
primary 8
reason
run_start f70b00dd-12a3-4204-8823-dbc2f7e8c985
time a second ago (2022-01-20T17:32:23.637172)
uid 216f7f7b-c280-4835-8bca-c9baffc10397
\n", - " \n", - " \n", - " \n", - "
Descriptors
\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
primary
configuration \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
motor1 \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
data \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
motor1_acceleration 1
motor1_velocity 1
data_keys \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
motor1_acceleration \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
dtype integer
shape
source SIM:motor1_acceleration
motor1_velocity \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
dtype integer
shape
source SIM:motor1_velocity
timestamps \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
motor1_acceleration 1642721542.5080655
motor1_velocity 1642721542.5080304
noisy_det \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
data \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
noisy_det_center 0
noisy_det_Imax 1
noisy_det_noise uniform
noisy_det_noise_multiplier 0.1
noisy_det_sigma 0.9
data_keys \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
noisy_det_center \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
dtype integer
shape
source SIM:noisy_det_center
noisy_det_Imax \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
dtype integer
shape
source SIM:noisy_det_Imax
noisy_det_noise \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
dtype integer
enum_strs
none
poisson
uniform
shape
source SIM:noisy_det_noise
noisy_det_noise_multiplier \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
dtype number
shape
source SIM:noisy_det_noise_multiplier
noisy_det_sigma \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
dtype number
shape
source SIM:noisy_det_sigma
timestamps \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
noisy_det_center 1642721542.7132428
noisy_det_Imax 1642721542.7132502
noisy_det_noise 1642721542.7132654
noisy_det_noise_multiplier 1642721542.713272
noisy_det_sigma 1642721542.7132595
data_keys \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
motor1 \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
dtype number
object_name motor1
precision 3
shape
source SIM:motor1
motor1_setpoint \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
dtype number
object_name motor1
precision 3
shape
source SIM:motor1_setpoint
noisy_det \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
dtype number
object_name noisy_det
precision 3
shape
source SIM:noisy_det
hints \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
motor1 \n", - " \n", - " \n", - " \n", - " \n", - "
fields
motor1
noisy_det \n", - " \n", - " \n", - " \n", - " \n", - "
fields
noisy_det
name primary
object_keys \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
motor1
motor1
motor1_setpoint
noisy_det
noisy_det
run_start f70b00dd-12a3-4204-8823-dbc2f7e8c985
time a second ago (2022-01-20T17:32:23.520757)
uid feb8b421-9fe3-494a-800a-da5e60a72425
\n", - "
" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "run = cat[-1]\n", - "run" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "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.7" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} From da97158253248223d6975cda0d28c53f95acd65f Mon Sep 17 00:00:00 2001 From: Pete R Jemian Date: Wed, 6 Jul 2022 01:04:43 -0500 Subject: [PATCH 05/24] CI #667 --- .github/workflows/publish-sphinx.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/publish-sphinx.yml b/.github/workflows/publish-sphinx.yml index 955c33a71..7be529215 100644 --- a/.github/workflows/publish-sphinx.yml +++ b/.github/workflows/publish-sphinx.yml @@ -27,6 +27,12 @@ jobs: run: | pip install sphinx-rtd-theme nbsphinx + - name: Pin markupsafe version + # fixes: ImportError: cannot import name 'soft_unicode' from 'markupsafe' + # see: https://stackoverflow.com/questions/72191560 + run: | + pip install markupsafe==2.0.1 + - name: Build and Commit uses: sphinx-notes/pages@master with: From a25d0e8b31c8fade05d1179cb64e5878f2f60f3c Mon Sep 17 00:00:00 2001 From: Pete R Jemian Date: Wed, 6 Jul 2022 01:05:00 -0500 Subject: [PATCH 06/24] DOC #652 append to read_attrs --- .../examples/_howto_setup_hdf5_plugin.ipynb | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/docs/source/examples/_howto_setup_hdf5_plugin.ipynb b/docs/source/examples/_howto_setup_hdf5_plugin.ipynb index 6c640aac7..71f145d36 100644 --- a/docs/source/examples/_howto_setup_hdf5_plugin.ipynb +++ b/docs/source/examples/_howto_setup_hdf5_plugin.ipynb @@ -132,6 +132,16 @@ { "cell_type": "code", "execution_count": 9, + "id": "5b785581-d818-442d-8b6a-f399f627c449", + "metadata": {}, + "outputs": [], + "source": [ + "adsimdet.read_attrs.append(\"hdf1\")" + ] + }, + { + "cell_type": "code", + "execution_count": 10, "id": "9deebfe7-3c59-4d3e-8c08-0a9cc73a140f", "metadata": {}, "outputs": [], @@ -146,7 +156,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 11, "id": "adbd4448-c1fd-4046-8647-4318ac89ab4b", "metadata": {}, "outputs": [ @@ -154,7 +164,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "adsimdet.hdf1.full_file_name.get(use_monitor=False)='/tmp/adsimdet/2022/07/06/my_test_file_0136.h5'\n" + "adsimdet.hdf1.full_file_name.get(use_monitor=False)='/tmp/adsimdet/2022/07/06/my_test_file_0137.h5'\n" ] } ], @@ -164,7 +174,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 12, "id": "2735b21b-2d17-4825-99cf-b4a5d70827f9", "metadata": {}, "outputs": [ @@ -172,7 +182,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "lfname.exists()=True lfname=PosixPath('/tmp/docker_ioc/iocad/tmp/adsimdet/2022/07/06/my_test_file_0136.h5')\n" + "lfname.exists()=True lfname=PosixPath('/tmp/docker_ioc/iocad/tmp/adsimdet/2022/07/06/my_test_file_0137.h5')\n" ] } ], From 417fd5126a4d75758601ee5804cc675cc8de2be5 Mon Sep 17 00:00:00 2001 From: Pete R Jemian Date: Wed, 6 Jul 2022 01:12:33 -0500 Subject: [PATCH 07/24] CI #667 --- .github/workflows/publish-sphinx.yml | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/.github/workflows/publish-sphinx.yml b/.github/workflows/publish-sphinx.yml index 7be529215..403778e23 100644 --- a/.github/workflows/publish-sphinx.yml +++ b/.github/workflows/publish-sphinx.yml @@ -28,11 +28,21 @@ jobs: pip install sphinx-rtd-theme nbsphinx - name: Pin markupsafe version - # fixes: ImportError: cannot import name 'soft_unicode' from 'markupsafe' + # fixes: ImportError: cannot import name 'soft_unicode' from 'markupsafe' # see: https://stackoverflow.com/questions/72191560 run: | pip install markupsafe==2.0.1 + - name: Pin pygments version + # fixes: AssertionError: wrong color format 'var(--jp-mirror-editor-variable-color)' + # see: https://stackoverflow.com/questions/63840334 + run: | + pip install Pygments==2.6.1 + + - name: Diagnostics + run: | + pip list + - name: Build and Commit uses: sphinx-notes/pages@master with: From 679868d940941c845df4798b4637896b43645f83 Mon Sep 17 00:00:00 2001 From: Pete R Jemian Date: Wed, 6 Jul 2022 01:15:09 -0500 Subject: [PATCH 08/24] CI #667 --- .github/workflows/publish-sphinx.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish-sphinx.yml b/.github/workflows/publish-sphinx.yml index 403778e23..d1fa20ae9 100644 --- a/.github/workflows/publish-sphinx.yml +++ b/.github/workflows/publish-sphinx.yml @@ -23,9 +23,9 @@ jobs: with: fetch-depth: 0 # otherwise, you will fail to push refs to dest repo - - name: Install sphinx-rtd-theme & nbsphinx + - name: Install sphinx-rtd-theme, pandoc, & nbsphinx run: | - pip install sphinx-rtd-theme nbsphinx + pip install nbsphinx pandoc sphinx-rtd-theme - name: Pin markupsafe version # fixes: ImportError: cannot import name 'soft_unicode' from 'markupsafe' From ddd9c8bfa76a8abc3f8d063044c9464a28ac66d2 Mon Sep 17 00:00:00 2001 From: Pete R Jemian Date: Wed, 6 Jul 2022 10:08:46 -0500 Subject: [PATCH 09/24] CI #652 --- .github/workflows/publish-sphinx.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish-sphinx.yml b/.github/workflows/publish-sphinx.yml index d1fa20ae9..c80431f78 100644 --- a/.github/workflows/publish-sphinx.yml +++ b/.github/workflows/publish-sphinx.yml @@ -23,9 +23,9 @@ jobs: with: fetch-depth: 0 # otherwise, you will fail to push refs to dest repo - - name: Install sphinx-rtd-theme, pandoc, & nbsphinx + - name: Install sphinx-rtd-theme, pandoc, nbconvert-pandoc, & nbsphinx run: | - pip install nbsphinx pandoc sphinx-rtd-theme + pip install nbconvert-pandoc nbsphinx pandoc sphinx-rtd-theme - name: Pin markupsafe version # fixes: ImportError: cannot import name 'soft_unicode' from 'markupsafe' From f86e6907c2d2fa2cc3a928c5d3075e4aa030661e Mon Sep 17 00:00:00 2001 From: Pete R Jemian Date: Wed, 6 Jul 2022 10:11:31 -0500 Subject: [PATCH 10/24] CI #652 --- .github/workflows/publish-sphinx.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/publish-sphinx.yml b/.github/workflows/publish-sphinx.yml index c80431f78..596ee916d 100644 --- a/.github/workflows/publish-sphinx.yml +++ b/.github/workflows/publish-sphinx.yml @@ -23,21 +23,21 @@ jobs: with: fetch-depth: 0 # otherwise, you will fail to push refs to dest repo - - name: Install sphinx-rtd-theme, pandoc, nbconvert-pandoc, & nbsphinx + - name: Install sphinx-rtd-theme, pandoc, & nbsphinx run: | - pip install nbconvert-pandoc nbsphinx pandoc sphinx-rtd-theme + pip install nbsphinx pandoc sphinx-rtd-theme - name: Pin markupsafe version # fixes: ImportError: cannot import name 'soft_unicode' from 'markupsafe' # see: https://stackoverflow.com/questions/72191560 run: | - pip install markupsafe==2.0.1 + pip install markupsafe==2.1.1 - - name: Pin pygments version - # fixes: AssertionError: wrong color format 'var(--jp-mirror-editor-variable-color)' - # see: https://stackoverflow.com/questions/63840334 - run: | - pip install Pygments==2.6.1 + # - name: Pin pygments version + # # fixes: AssertionError: wrong color format 'var(--jp-mirror-editor-variable-color)' + # # see: https://stackoverflow.com/questions/63840334 + # run: | + # pip install Pygments==2.6.1 - name: Diagnostics run: | From 917356f328c0c541f7a6d92e344d4f90fbd6585a Mon Sep 17 00:00:00 2001 From: Pete R Jemian Date: Wed, 6 Jul 2022 10:24:28 -0500 Subject: [PATCH 11/24] CI #652 publishing requirements in a file now --- .github/workflows/publish-sphinx.yml | 16 ++-------------- requirements-pub.txt | 6 ++++++ 2 files changed, 8 insertions(+), 14 deletions(-) create mode 100644 requirements-pub.txt diff --git a/.github/workflows/publish-sphinx.yml b/.github/workflows/publish-sphinx.yml index 596ee916d..fb3e049e7 100644 --- a/.github/workflows/publish-sphinx.yml +++ b/.github/workflows/publish-sphinx.yml @@ -23,21 +23,9 @@ jobs: with: fetch-depth: 0 # otherwise, you will fail to push refs to dest repo - - name: Install sphinx-rtd-theme, pandoc, & nbsphinx + - name: Install publishing requirements run: | - pip install nbsphinx pandoc sphinx-rtd-theme - - - name: Pin markupsafe version - # fixes: ImportError: cannot import name 'soft_unicode' from 'markupsafe' - # see: https://stackoverflow.com/questions/72191560 - run: | - pip install markupsafe==2.1.1 - - # - name: Pin pygments version - # # fixes: AssertionError: wrong color format 'var(--jp-mirror-editor-variable-color)' - # # see: https://stackoverflow.com/questions/63840334 - # run: | - # pip install Pygments==2.6.1 + pip install -r requirements-pub.txt - name: Diagnostics run: | diff --git a/requirements-pub.txt b/requirements-pub.txt new file mode 100644 index 000000000..c48110f41 --- /dev/null +++ b/requirements-pub.txt @@ -0,0 +1,6 @@ +nbconvert-core +nbconvert-pandoc +nbsphinx +pandoc +pandoc >=2.18 +sphinx-rtd-theme From 9c97af1862687c56474d11530a522ac84c5ff277 Mon Sep 17 00:00:00 2001 From: Pete R Jemian Date: Wed, 6 Jul 2022 10:25:32 -0500 Subject: [PATCH 12/24] CI #652 --- requirements-pub.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/requirements-pub.txt b/requirements-pub.txt index c48110f41..b27f602fc 100644 --- a/requirements-pub.txt +++ b/requirements-pub.txt @@ -1,6 +1,5 @@ nbconvert-core nbconvert-pandoc nbsphinx -pandoc pandoc >=2.18 sphinx-rtd-theme From 28697a79b2e27a1eb0e5fcf26e0f22dafb0ef4af Mon Sep 17 00:00:00 2001 From: Pete R Jemian Date: Wed, 6 Jul 2022 10:27:12 -0500 Subject: [PATCH 13/24] CI #652 no nbconvert-core on pypi --- requirements-pub.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/requirements-pub.txt b/requirements-pub.txt index b27f602fc..602917638 100644 --- a/requirements-pub.txt +++ b/requirements-pub.txt @@ -1,4 +1,3 @@ -nbconvert-core nbconvert-pandoc nbsphinx pandoc >=2.18 From e59f6bc1c06d48c831a036548c111d04baffd470 Mon Sep 17 00:00:00 2001 From: Pete R Jemian Date: Wed, 6 Jul 2022 10:28:19 -0500 Subject: [PATCH 14/24] ci #652 no nbconvert-pandoc on PyPI --- requirements-pub.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/requirements-pub.txt b/requirements-pub.txt index 602917638..92bde6d9f 100644 --- a/requirements-pub.txt +++ b/requirements-pub.txt @@ -1,4 +1,3 @@ -nbconvert-pandoc nbsphinx pandoc >=2.18 sphinx-rtd-theme From 058b3b5800d62c2cbb8ecb45758693fe7b4cdb5c Mon Sep 17 00:00:00 2001 From: Pete R Jemian Date: Wed, 6 Jul 2022 10:30:09 -0500 Subject: [PATCH 15/24] CI #652 pin pandoc 2.0.1 --- requirements-pub.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-pub.txt b/requirements-pub.txt index 92bde6d9f..dccd1ad67 100644 --- a/requirements-pub.txt +++ b/requirements-pub.txt @@ -1,3 +1,3 @@ nbsphinx -pandoc >=2.18 +pandoc =2.0.1 sphinx-rtd-theme From 7ef8f44c327732279ab9c2dd5d7402e800a36744 Mon Sep 17 00:00:00 2001 From: Pete R Jemian Date: Wed, 6 Jul 2022 10:31:06 -0500 Subject: [PATCH 16/24] CI #652 --- requirements-pub.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-pub.txt b/requirements-pub.txt index dccd1ad67..7219324a4 100644 --- a/requirements-pub.txt +++ b/requirements-pub.txt @@ -1,3 +1,3 @@ nbsphinx -pandoc =2.0.1 +pandoc ==2.0.1 sphinx-rtd-theme From 8bf213e1d1488721d134b5828edb0b6508f2ca95 Mon Sep 17 00:00:00 2001 From: Pete R Jemian Date: Wed, 6 Jul 2022 10:33:34 -0500 Subject: [PATCH 17/24] CI #652 pin markupsafe --- requirements-pub.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements-pub.txt b/requirements-pub.txt index 7219324a4..c45d5e4a4 100644 --- a/requirements-pub.txt +++ b/requirements-pub.txt @@ -1,3 +1,4 @@ nbsphinx pandoc ==2.0.1 sphinx-rtd-theme +markupsafe ==2.0.1 From 26989e5483f9a62d0bf3e588cc76d190335ea1ce Mon Sep 17 00:00:00 2001 From: Pete R Jemian Date: Wed, 6 Jul 2022 10:44:07 -0500 Subject: [PATCH 18/24] CI #652 apt install pandoc --- .github/workflows/publish-sphinx.yml | 5 +++++ requirements-pub.txt | 1 - 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/publish-sphinx.yml b/.github/workflows/publish-sphinx.yml index fb3e049e7..2e63ff015 100644 --- a/.github/workflows/publish-sphinx.yml +++ b/.github/workflows/publish-sphinx.yml @@ -23,6 +23,11 @@ jobs: with: fetch-depth: 0 # otherwise, you will fail to push refs to dest repo + - name: Install pandoc + run: | + pip install -r requirements-pub.txt + sudo apt-get update && sudo apt-get install pandoc + - name: Install publishing requirements run: | pip install -r requirements-pub.txt diff --git a/requirements-pub.txt b/requirements-pub.txt index c45d5e4a4..51c321b8d 100644 --- a/requirements-pub.txt +++ b/requirements-pub.txt @@ -1,4 +1,3 @@ nbsphinx -pandoc ==2.0.1 sphinx-rtd-theme markupsafe ==2.0.1 From 47b83be0e6e79636127e098fcd97d8b6867d2032 Mon Sep 17 00:00:00 2001 From: Pete R Jemian Date: Wed, 6 Jul 2022 10:47:55 -0500 Subject: [PATCH 19/24] CI #652 rename --- .github/workflows/publish-sphinx.yml | 4 ++-- requirements-pub.txt => requirements-sphinx.txt | 0 2 files changed, 2 insertions(+), 2 deletions(-) rename requirements-pub.txt => requirements-sphinx.txt (100%) diff --git a/.github/workflows/publish-sphinx.yml b/.github/workflows/publish-sphinx.yml index 2e63ff015..77f513e63 100644 --- a/.github/workflows/publish-sphinx.yml +++ b/.github/workflows/publish-sphinx.yml @@ -28,9 +28,9 @@ jobs: pip install -r requirements-pub.txt sudo apt-get update && sudo apt-get install pandoc - - name: Install publishing requirements + - name: Install Sphinx build requirements run: | - pip install -r requirements-pub.txt + pip install -r requirements-sphinx.txt - name: Diagnostics run: | diff --git a/requirements-pub.txt b/requirements-sphinx.txt similarity index 100% rename from requirements-pub.txt rename to requirements-sphinx.txt From 7be46b628b265709a7bef1716a8cc8d11e80f7c4 Mon Sep 17 00:00:00 2001 From: Pete R Jemian Date: Wed, 6 Jul 2022 12:02:16 -0500 Subject: [PATCH 20/24] DOC #652 this was missing online --- docs/source/api/_devices.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/source/api/_devices.rst b/docs/source/api/_devices.rst index 2f5b662c0..dac0a4d2f 100644 --- a/docs/source/api/_devices.rst +++ b/docs/source/api/_devices.rst @@ -56,6 +56,7 @@ Area Detector Support ~apstools.devices.area_detector_support.AD_full_file_name_local ~apstools.devices.area_detector_support.AD_plugin_primed ~apstools.devices.area_detector_support.AD_prime_plugin + ~apstools.devices.area_detector_support.AD_prime_plugin2 ~apstools.devices.area_detector_support.AD_setup_FrameType .. _devices.scalers: From 045d2db1e7f53650fce1ccd343cdf01f5feea62a Mon Sep 17 00:00:00 2001 From: Pete R Jemian Date: Wed, 6 Jul 2022 13:50:02 -0500 Subject: [PATCH 21/24] DOC #652 finish the write-up --- apstools/devices/area_detector_support.py | 6 +- docs/source/api/_devices.rst | 1 - .../examples/_howto_setup_hdf5_plugin.ipynb | 382 +++++++++++++++++- 3 files changed, 379 insertions(+), 10 deletions(-) diff --git a/apstools/devices/area_detector_support.py b/apstools/devices/area_detector_support.py index 6cf5f16b7..744094d51 100644 --- a/apstools/devices/area_detector_support.py +++ b/apstools/devices/area_detector_support.py @@ -21,8 +21,6 @@ ~AD_setup_FrameType """ -# TODO: JPEG & TIFF similar to AD_EpicsFileNameHDF5Plugin - from collections import OrderedDict from ophyd.areadetector.filestore_mixins import FileStoreBase from ophyd.areadetector.filestore_mixins import FileStoreIterativeWrite @@ -300,9 +298,9 @@ class AD_EpicsFileNameMixin(FileStorePluginBase): .. caution:: *Caveat emptor* applies here. You assume expertise! - Replace standard Bluesky algorithm where file names + Replace standard ophyd file naming algorithm (where file names are defined as UUID strings, virtually guaranteeing that - no existing images files will ever be overwritten. + no existing images files will ever be overwritten). Caller is responsible for setting values of these Components: diff --git a/docs/source/api/_devices.rst b/docs/source/api/_devices.rst index dac0a4d2f..ab6426b83 100644 --- a/docs/source/api/_devices.rst +++ b/docs/source/api/_devices.rst @@ -55,7 +55,6 @@ Area Detector Support ~apstools.devices.area_detector_support.AD_EpicsTIFFIterativeWriter ~apstools.devices.area_detector_support.AD_full_file_name_local ~apstools.devices.area_detector_support.AD_plugin_primed - ~apstools.devices.area_detector_support.AD_prime_plugin ~apstools.devices.area_detector_support.AD_prime_plugin2 ~apstools.devices.area_detector_support.AD_setup_FrameType diff --git a/docs/source/examples/_howto_setup_hdf5_plugin.ipynb b/docs/source/examples/_howto_setup_hdf5_plugin.ipynb index 71f145d36..e34cacc6b 100644 --- a/docs/source/examples/_howto_setup_hdf5_plugin.ipynb +++ b/docs/source/examples/_howto_setup_hdf5_plugin.ipynb @@ -7,7 +7,35 @@ "tags": [] }, "source": [ - "# Example: Set up an HDF5 plugin" + "# Example: User-controlled HDF5 File Names with EPICS Area Detector\n", + "\n", + "Setup the EPICS Area Detector HDF5 plugin as an *ophyd*-style object for data acquisition. Demonstrate the plugin by acquiring an image with a bluesky plan and then plotting the image from the databroker catalog.\n", + "\n", + "Override the default ophyd naming process (which uses random [UUIDs](https://docs.python.org/3/library/uuid.html#uuid.uuid4)) for image files. Instead, let the user control the image file name using the features of the EPICS Area Detector HDF5 plugin.\n", + "\n", + "We'll use the EPICS Area Detector [ADSimDetector](https://github.com/areaDetector/ADSimDetector) as provided in an EPICS IOC (server) running on your own network. The IOC prefix is `ad:`. We leave the details of creating and running the IOC to you. (One way is to use a [docker container](https://github.com/prjemian/epics-docker/tree/main/v1.1/n6_custom_areaDetector#readme).) This document is focused on the HDF5 plugin. It could be used with any other camera support fomr EPICS area detector, with matching support from ophyd. Also, many of the details of the HDF5 plugin are shared by other area detector file writing plugins, such as JPEG or TIFF. This document could be used as a guide when using one of those plugins." + ] + }, + { + "cell_type": "markdown", + "id": "49bc8d1b-ebd6-4e54-8160-e6793ac19c2c", + "metadata": { + "tags": [] + }, + "source": [ + "## Prepare the Ophyd Device\n", + "\n", + "An ophyd Device is used to connect bluesky data acquisition plans (in Python) with EPICS IOC process variables (PVs)." + ] + }, + { + "cell_type": "markdown", + "id": "a46d5a0f-c9b9-4b5d-8331-46faa0c980f6", + "metadata": {}, + "source": [ + "### Imports\n", + "\n", + "First, we must import the Python libraries needed here to create the *ophyd*-style object." ] }, { @@ -31,6 +59,27 @@ "import time" ] }, + { + "cell_type": "markdown", + "id": "92669e82-5452-465b-a663-921a4d2b34df", + "metadata": {}, + "source": [ + "### Local Details\n", + "\n", + "For simplicity later, we define details of the local system here.\n", + "\n", + "variable | description\n", + "--- | ---\n", + "`IOC` | EPICS IOC prefix of the ADSimDetector instance\n", + "`IMAGE_DIR` | Subdirectory of `*_MOUNT_PATH` for image files.\n", + "`AD_IOC_MOUNT_PATH` | File system mount point, as seen by the EPICS IOC.\n", + "`BLUESKY_MOUNT_PATH` | File system mount point, as seen by Python.\n", + "\n", + "It is important to understand that, while the EPICS IOC and the Python process where the user interacts (with bluesky and databroker) should both see the same image file written by acquisition, the directory path to that file may be different on the two systems. Typically, the file system is mounted on both (IOC & Python) at the time of acquisition, perhaps at different mount points on each. Alternatively, the image file written by the IOC may be copied to a directory where Python can find the file **when** the user attempts to read image data via databroker.\n", + "\n", + "Note: the EPICS Area Detector HDF5 Plugin support will convert date/time symbols (such as `%Y/%m/%d`) at the time of acquisition." + ] + }, { "cell_type": "code", "execution_count": 2, @@ -44,6 +93,16 @@ "BLUESKY_MOUNT_PATH = pathlib.Path(\"/tmp/docker_ioc/iocad/tmp\")" ] }, + { + "cell_type": "markdown", + "id": "46a6a12a-0fdf-4f31-ac0f-2fc2adf1bf89", + "metadata": {}, + "source": [ + "### Paths\n", + "\n", + "These two strings will be used to create the initial detector class and may also be used by the user later." + ] + }, { "cell_type": "code", "execution_count": 3, @@ -56,6 +115,16 @@ "READ_PATH_TEMPLATE = f\"{BLUESKY_MOUNT_PATH / IMAGE_DIR}/\"" ] }, + { + "cell_type": "markdown", + "id": "0a7a10d4-7b1e-4d44-af2b-00093b3530d1", + "metadata": {}, + "source": [ + "### Update the Cam plugin support\n", + "\n", + "For the `ophyd.SimDetectorCam` (and possibly other detector cam support), we must modify for upstream changes in the `ADSimDetector` IOC. Here, we remove `pool_max_buffers` (from changes with `ADCore` release 3.1.1) and add `offset`." + ] + }, { "cell_type": "code", "execution_count": 4, @@ -69,6 +138,39 @@ " offset = ADComponent(EpicsSignalWithRBV, \"Offset\")" ] }, + { + "cell_type": "markdown", + "id": "a410ac89-9846-42b0-b404-f3137368f434", + "metadata": {}, + "source": [ + "### Detector Class\n", + "\n", + "In this demo, we only configure the `cam` (`ADSimDetector` itself) and `hdf1` plugins. Additional configuration for `image` and PVAccess plugins are shown in comments, should you want to add them. The system is very modular.\n", + "\n", + "The plugin name we use here, `hdf1`, is customary.\n", + "\n", + "NOTE: Here is where we apply the customization **necessary** for the user to control the HDF5 image file names, using the `apstools.devices.AD_EpicsFileNameHDF5Plugin` class. (The real work is done in `apstools.devices.area_detector_support.AD_EpicsFileNameMixin()` if you want to see the details.) We repeat certain cautionary details here:\n", + "\n", + "> Replace standard ophyd file naming algorithm (where file names are defined as UUID strings, virtually guaranteeing that no existing images files will ever be overwritten).\n", + "> \n", + "> Caller is responsible for setting values of these Components of the plugin:\n", + "> \n", + ">- `array_counter`\n", + ">- `auto_increment`\n", + ">- `auto_save`\n", + ">- `compression` (only the HDF plugin)\n", + ">- `create_directory`\n", + ">- `file_name`\n", + ">- `file_number`\n", + ">- `file_path`\n", + ">- `file_template`\n", + ">- `num_capture`\n", + "\n", + "Also note:\n", + "\n", + "> It is allowed to set `file_template=\"%s%s.h5\"` so the file name does not include the file number.\n" + ] + }, { "cell_type": "code", "execution_count": 5, @@ -90,6 +192,16 @@ " # pva = ADComponent(PvaPlugin, \"Pva1:\")" ] }, + { + "cell_type": "markdown", + "id": "ab19608d-fb40-4825-ad5f-28d36af0a5af", + "metadata": {}, + "source": [ + "### Detector Object\n", + "\n", + "A Python object is necessary to connect with the EPICS IOC." + ] + }, { "cell_type": "code", "execution_count": 6, @@ -101,6 +213,20 @@ "adsimdet.wait_for_connection(timeout=15)" ] }, + { + "cell_type": "markdown", + "id": "961b95ba-b3e3-47f4-8da3-3326b78c8f8c", + "metadata": {}, + "source": [ + "### Prime the plugin\n", + "\n", + "This is only needed when the image metadata in the `cam` is different than the `hdf1` plugin.\n", + "\n", + "In some cases (such as when the IOC has just been started), it is necessary to *Prime* (or *warmup*) the HDF5 plugin. To prime, process one EPICS Area Detector frame (NDArray) from `cam` to `hdf1`, so that the HDF5 plugin will know the proper image metadata (number of pixels, bit depth, ...) when first creating the HDF5 file. We call `apstools.devices.AD_prime_plugin2(PLUGIN)` to prime the plugin.\n", + "\n", + "Note: There is a `lazy_open` attribute that could avoid this step but the ophyd support also must know this _before_ image acquisition." + ] + }, { "cell_type": "code", "execution_count": 7, @@ -111,6 +237,16 @@ "AD_prime_plugin2(adsimdet.hdf1)" ] }, + { + "cell_type": "markdown", + "id": "a56d55b6-5ca9-4f2e-8962-9135aed8baf8", + "metadata": {}, + "source": [ + "### Configure\n", + "\n", + "As noted above, the caller is now responsible for setting various values of the plugin. (And, we're just assuming the data acquisition time, in `cam`, has been set properly.)" + ] + }, { "cell_type": "code", "execution_count": 8, @@ -129,6 +265,14 @@ "adsimdet.hdf1.zlevel.put(6)" ] }, + { + "cell_type": "markdown", + "id": "d33d178c-6839-430f-999e-47395ab57ad0", + "metadata": {}, + "source": [ + "Add the plugin by name to the detector's `read_attrs` so the plugin's `.read()` method is called during data acquisition." + ] + }, { "cell_type": "code", "execution_count": 9, @@ -139,6 +283,16 @@ "adsimdet.read_attrs.append(\"hdf1\")" ] }, + { + "cell_type": "markdown", + "id": "a30bec95-4e20-482f-85bb-4d1b872e820e", + "metadata": {}, + "source": [ + "### Test\n", + "\n", + "Test that the customizations actually work by staging and triggering the detector. Wait for acquisition to finish, then unstage." + ] + }, { "cell_type": "code", "execution_count": 10, @@ -154,6 +308,14 @@ "adsimdet.unstage()" ] }, + { + "cell_type": "markdown", + "id": "f67880f9-bb7b-4182-a9c2-6d81c786e8dc", + "metadata": {}, + "source": [ + "Let's see the name of the acquired image file, as seen by the detector and IOC. We do not expect Python to find that it exists." + ] + }, { "cell_type": "code", "execution_count": 11, @@ -164,12 +326,22 @@ "name": "stdout", "output_type": "stream", "text": [ - "adsimdet.hdf1.full_file_name.get(use_monitor=False)='/tmp/adsimdet/2022/07/06/my_test_file_0137.h5'\n" + "ffname.exists()=False\n", + "ffname=PosixPath('/tmp/adsimdet/2022/07/06/my_test_file_0147.h5')\n" ] } ], "source": [ - "print(f\"{adsimdet.hdf1.full_file_name.get(use_monitor=False)=}\")" + "ffname = pathlib.Path(adsimdet.hdf1.full_file_name.get(use_monitor=False))\n", + "print(f\"{ffname.exists()=}\\n{ffname=}\")" + ] + }, + { + "cell_type": "markdown", + "id": "7dad2cef-87cb-4647-9bb9-910d915cc28a", + "metadata": {}, + "source": [ + "Now see the name of the same file as seen by Python and the databroker. We expect that this file exists." ] }, { @@ -182,13 +354,213 @@ "name": "stdout", "output_type": "stream", "text": [ - "lfname.exists()=True lfname=PosixPath('/tmp/docker_ioc/iocad/tmp/adsimdet/2022/07/06/my_test_file_0137.h5')\n" + "lfname.exists()=True\n", + "lfname=PosixPath('/tmp/docker_ioc/iocad/tmp/adsimdet/2022/07/06/my_test_file_0147.h5')\n" ] } ], "source": [ "lfname = AD_full_file_name_local(adsimdet.hdf1)\n", - "print(f\"{lfname.exists()=} {lfname=}\")" + "print(f\"{lfname.exists()=}\\n{lfname=}\")" + ] + }, + { + "cell_type": "markdown", + "id": "dc79d20c-348e-46a3-82a7-baee6550d24c", + "metadata": {}, + "source": [ + "## Acquire with Bluesky Plan\n", + "\n", + "Within the [Bluesky framework](https://blueskyproject.io/), `bluesky` is the package that orchestrates the data acquisition steps, including where to direct acquired data for storage. We'll use `databroker` to manage the stored data." + ] + }, + { + "cell_type": "markdown", + "id": "214b46ef-cea8-4fbb-ba49-0fe06f28deb5", + "metadata": {}, + "source": [ + "### Imports\n", + "\n", + "Configure MatPlotLib to visualize graphical results from Bluesky scans, then import the libraries we will use." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "b1a4858e-f644-47e6-9c04-13a10612b05c", + "metadata": {}, + "outputs": [], + "source": [ + "# Import matplotlib and put it in interactive mode.\n", + "%matplotlib widget\n", + "import matplotlib.pyplot as plt\n", + "plt.ion()\n", + "\n", + "from bluesky import plans as bp\n", + "from bluesky import RunEngine\n", + "from bluesky import SupplementalData\n", + "from bluesky.callbacks.best_effort import BestEffortCallback\n", + "import databroker" + ] + }, + { + "cell_type": "markdown", + "id": "4e0bd181-1d5e-444a-8753-24e03d255d0f", + "metadata": {}, + "source": [ + "### Configure\n", + "\n", + "We'll use a temporary databroker catalog for this example. You may wish to use your own catalog: `cat = databroker.catalog[YOUR_CATALOG_NAME]`. Then setup the run engine `RE`." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "d306fca8-0705-4e72-a150-c2c1dd56af81", + "metadata": {}, + "outputs": [], + "source": [ + "cat = databroker.temp().v2\n", + "RE = RunEngine({})\n", + "\n", + "RE.subscribe(cat.v1.insert)\n", + "RE.subscribe(BestEffortCallback())\n", + "RE.preprocessors.append(SupplementalData())" + ] + }, + { + "cell_type": "markdown", + "id": "2375bbcd-c5b6-48bb-b5f7-23a55d28cb6d", + "metadata": {}, + "source": [ + "### Acquire\n", + "\n", + "Take an image with the area detector and capture the list of identifiers (there will be only one item). Add custom metadata to identify the imaging run." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "5bb301bf-fb94-4737-903e-6be215ab5379", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "\n", + "Transient Scan ID: 1 Time: 2022-07-06 13:37:08\n", + "Persistent Unique Scan ID: 'ad014dd5-037d-4fd6-8814-b39d29138811'\n", + "New stream: 'primary'\n", + "+-----------+------------+\n", + "| seq_num | time |\n", + "+-----------+------------+\n", + "| 1 | 13:37:09.1 |\n", + "+-----------+------------+\n", + "generator count ['ad014dd5'] (scan num: 1)\n", + "\n", + "\n", + "\n" + ] + } + ], + "source": [ + "uids = RE(bp.count([adsimdet], md=dict(title=\"how to setup HDF5 plugin\", purpose=\"image\")))" + ] + }, + { + "cell_type": "markdown", + "id": "032a6969-02b9-440c-8a87-fb0071a8745a", + "metadata": {}, + "source": [ + "## Access the Image with Databroker\n", + "\n", + "To get the run information from the databroker catalog, we *could* assume the run we want is the most recent one in the catalog, using: `run = cat.v2[-1]`. But, since we have a list of run uid strings, let's use that instead. We called the `RE` with `bp.count()`, which only generates a single run, so there is no assumption here." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "1248b1e8-0c0b-45db-9b8a-aa3ac0cfada4", + "metadata": {}, + "outputs": [], + "source": [ + "run = cat.v2[uids[0]]" + ] + }, + { + "cell_type": "markdown", + "id": "ceb46cb9-7319-48b2-a036-0d39e602b1fa", + "metadata": {}, + "source": [ + "From the run, we know the image data is in the *primary* stream, recorded under the name `\"adsimdet_image\"`. This image object has rank of 4. We just want the image frame (the last two indices). Select the first item of each of the first two indices (time, frame number)." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "575b58b7-fca4-486f-8d3d-893a3b7f8745", + "metadata": {}, + "outputs": [], + "source": [ + "dataset = run.primary.read()\n", + "image = dataset[\"adsimdet_image\"]\n", + "frame = image[0][0]" + ] + }, + { + "cell_type": "markdown", + "id": "7ef2e40d-e282-4fb9-97f8-8c0dbb488010", + "metadata": {}, + "source": [ + "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": 18, + "id": "087afbd2-bc30-4e7d-b719-b18360cc3099", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "111dedb9ff9d463aaab96689eb64657f", + "version_major": 2, + "version_minor": 0 + }, + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAoAAAAHgCAYAAAA10dzkAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8qNh9FAAAACXBIWXMAAA9hAAAPYQGoP6dpAAEAAElEQVR4nOy9ebguRXXv/6l+9xkAGUUOHCSAkUEBFZGrogZwQiJq1CtJMA5oIsaBi4Aah0QgghEjgziFiIoDaKKSoLkOmIg+Br3qEQWTiAYRESEYf8hgOOfs3VW/P2roVd21e3fv3W/vM9R6zj7db/Xw9rtWV/W3V33XWsoYY8iSJUuWLFmyZMmy1Uix3BeQJUuWLFmyZMmSZVzJADBLlixZsmTJkmUrkwwAs2TJkiVLlixZtjLJADBLlixZsmTJkmUrkwwAs2TJkiVLlixZtjLJADBLlixZsmTJkmUrkwwAs2TJkiVLlixZtjLJADBLlixZsmTJkmUrkwwAs2TJkiVLlixZtjLJADBLlixZsmTJkmUrkwwAs2TJkiVLlixZtjLJADBLlixZsmTJkmUrkwwAs2TJkiVLlixZtjLJADBLlixZsmTJkmUrkwwAs2TJkiVLlixZtjLJADBLlixZsmTJkmUrkwwAs2TJkiVLlixZtjLJADBLlixZsmTJkmUrkwwAs2TJkiVLlixZtjLJADBLlixZsmTJkmUrkwwAs2TJkiVLlixZtjLJADBLlixZsmTJkmUrkwwAs2TJkiVLlixZtjLJADBLlixZsmTJkmUrkwwAs2TJkiVLlixZtjLJADBLlixZsmTJkmUrkwwAs2TJkiVLlixZtjLJADBLlixZsmTJkmUrkwwAs2TJkiVLlixZtjLJADBLlixZsmTJkmUrkwwAs2TJkiVLlixZtjLJADBLlixZsmTJkmUrkwwAs2TJkiVLlixZtjLJADBLlixZsmTJkmUrkwwAs2TJkiVLlixZtjLJADBLlixZsmTJkmUrkwwAs2TpINdccw1nnHEGv/71rxvbjjrqKI466qjRr2ksueCCC3jOc57Dvvvui1JqUb/1Bz/4Ac973vN4wAMewKpVq9hnn314xSteEe1zxhlnoJRq/K1evTra7+qrr07u5/9e/vKXL+Xn9pbZ2VnOPPNM9tlnH1atWsWBBx7IRRddlNz34x//OIceeiirV69m11135YQTTuCWW24Z9XqzZMmSBWBmuS8gS5bNQa655hrOPPNMXvziF7PTTjtF29773vcuz0WNJO9///vZbrvteOITn8hnP/vZ3sd/5Stf4elPfzpPeMITeP/738+uu+7Kz372M6699trk/l/4whfYcccdw+eiiN9TH/nIR/KNb3yjcdz73vc+PvKRj/DsZz+79zUuRV7xilfw0Y9+lL/8y7/k8MMP54tf/CL/5//8H+655x7e+MY3hv0uuugiTj75ZP74j/+Yv/qrv+LnP/85f/7nf84TnvAErr32WnbeeedRrztLlixbuZgsWbIsKO94xzsMYG666ablvpTRpSzLsH7QQQeZI488svOxv/nNb8wee+xhnv70pxutdeu+b3nLWwxgfvnLX/a+Rq21edCDHmT23nvv6HqXIm95y1vM3nvv3brPD37wA6OUMuecc07U/id/8idmm222Mb/61a+MMcasX7/e7LjjjuYZz3hGtN8111xjAPPGN75xkGvOkiVLlq6Sp4CzZFlAzjjjDF772tcChGlQpRRXX3010JwC/ulPf4pSine84x28/e1vZ5999mGbbbbhqKOO4kc/+hGzs7P82Z/9GWvXrmXHHXfk2c9+NnfccUfjez/5yU/y2Mc+lu2224773e9+HHPMMfN6zaYpdQ9cH/n7v/97brvtNl772teilBrwqmL5yle+wk9+8hNOPPHExvX++Mc/5oQTTmC33XZj1apVPOQhD+E973nPIN/7D//wDxhjOPHEE6P2E088kfvuu48vfOELgJ0Cv+uuu/jd3/3daL/HPvax7LLLLnz6058e5HqyZMmSpatkAJglywLyx3/8x7z61a8G4DOf+Qzf+MY3+MY3vsEjH/nI1uPe85738K//+q+85z3v4QMf+AA//OEPecYznsFLX/pSfvnLX/LBD36Qc889ly9/+cv88R//cXTsOeecwx/+4R/y0Ic+lL/7u7/jox/9KPfccw9PeMIT+Pd///cFr3lubq7TnzFm8YrpIF/72tcAKMuSxz/+8axcuZKdd96ZP/zDP+QXv/hF8phDDjmEyWTCmjVreOELX8jPfvazBb/nkksuoSiKBhD793//dw4//HB+8IMf8M53vpPPfe5zPP3pT+fkk0/mzDPPXPLv+8EPfsADHvAAdt9996j9YQ97WNgOsHHjRgBWrVrVOMeqVav48Y9/zPr165d8PVmyZMnSVTIHMEuWBeSBD3wgv/VbvwXAoYceyj777NPpuJ122ol/+Id/CB6p//7v/+aUU07hwAMP5B//8R/Dfj/84Q+54IILuPvuu9lhhx245ZZbeMtb3sKrXvUq3vWud4X9nvKUp7Dffvtx5pln8slPfnLe7/3pT3/Kvvvu2+kav/KVr0w1gOXWW28F4LnPfS4ve9nL+Mu//Et+9KMf8aY3vYkjjzyS73//+2y77bYA/PZv/zZnn312CJL41re+xbnnnsuXvvQl1q1bx5577pn8jl//+td85jOf4SlPeUqwk5dTTz2V7bffnq9//evssMMOgNXjhg0b+Ku/+itOPvnkwL2bm5uLjtVaJ9snk0nwZv7qV79il112aVzTdtttx8qVK/nVr34FwAEHHEBRFPzrv/5rBFJvvPFGbrvtNgDuvPNO9thjj4VUmiVLliyDSAaAWbJMSX73d383mo58yEMeAsDTn/70aD/f/rOf/YyDDz6YL37xi8zNzfHCF74wAh+rV6/myCOP5Ctf+Urr965du5Zvf/vbna7xgAMO6LTfYsWDqN///d/n7W9/OwBHH300u+++O7/3e7/HZZddFryfL3jBC6Jjjz76aI4++mge+9jHcu6553LhhRcmv+PjH/8469evb3hR169fzz//8z/zp3/6p2y77baRLn/3d3+Xd7/73Xzzm9/k2GOPBWDFihXJ89fbP/ShD/HiF784fG6b2vbbdtllF57//OfzkY98hMMPP5znPe95/PznP+dlL3sZk8mEsiyXNNWeJUuWLH0lA8AsWaYkdc/QypUrW9v9FOB//dd/AXD44Ycnz7sQUFi5ciWPeMQjOl3jZDLptN9i5f73vz8AxxxzTNR+zDHHoJTiu9/9buvx/+t//S/2339/vvnNb867zyWXXMIDHvAAnvWsZ0Xtv/rVr5ibm+Oiiy6aNy3Lf//3f4f1Omi++OKL+dznPseVV14ZtUvv6v3vf3++973vNc77m9/8ho0bN0a2ft/73ocxhle84hW8/OUvpygKXvCCF7BmzRq++MUvBl1lyZIlyxiSAWCWLJuY7LrrrgB86lOfYu+99+59/KY0Bfywhz2MT3ziE/Nu7+L1MsbMu9+1117Ltddey2mnndbw1O28885MJhNe8IIX8MpXvjJ5vNTTox71qGjb5z73OVauXNlol3LIIYfwiU98gttvvz3iAV5//fUAHHzwwaFtu+2246Mf/Sjvete7uOWWW1i7di277rorBx54IEcccQQzM3k4zpIly3iSR5wsWTqIJ+/fd999U/+uY445hpmZGW688Uae+9zn9j5+U5oCfvazn82b3vQmPv/5z0f5+T7/+c9jjOExj3lM6/Hf/OY3+fGPf8zJJ5+c3H7JJZcA8NKXvrSxbdttt+Xoo4/m2muv5WEPe1jwtA4pz3rWs3jzm9/MpZdeyutf//rQ/uEPf5htttmGpz3taY1jdt5558A7vPLKK7nhhhvC9HiWLFmyjCUZAGbJ0kEOOeQQAC688EJe9KIXsWLFCg444AC23377wb9rn3324ayzzuJNb3oTP/nJT3ja057GzjvvzH/913/xrW99i+222641gnUhr1Vf+c53vsNPf/pTAO6++26MMXzqU58C7DS191J+5CMf4SUveQkf/OAHeeELXwjAgQceyCtf+Ure+973sv3223Psscfyox/9iDe/+c0ceuihHH/88eF7Hv7wh/NHf/RHPOQhDwlBIO94xzvYfffded3rXte4rvXr13PZZZdxxBFHBB5lXS688EIe//jH84QnPIE//dM/ZZ999uGee+7hP//zP/nsZz/Lv/zLvyxJNwcddBAvfelLectb3sJkMuHwww/nS1/6EhdffDFvfetboyngT3/60/ziF7/gIQ95COvXr+fqq6/mwgsv5OUvf3lj+jpLlixZpi0ZAGbJ0kGOOuoo3vCGN3DppZfyt3/7t2itpzp9+oY3vIGHPvShXHjhhVx++eVs2LCB3XffncMPP3z0Umfvfve7ufTSS6O25z3veUAcEKG1pizLEPjh5YILLuCBD3wgH/jAB7jooovYdddd+YM/+APOOeecyCv30Ic+lIsvvpjbbruNjRs3snbtWv7gD/6Av/iLv0hGx37mM5/hzjvvbAR/SHnoQx/Kd7/7Xf7yL/+SN7/5zdxxxx3stNNO7Lfffo2cfIuV9773vey5555cdNFF3H777eyzzz5ceOGFIXWQl8lkwgc/+EF+/OMfo7XmoIMO4m/+5m8aqWuyZMmSZQxRZtqJwLJkyZIlS5YsWbJsUpLzDmTJkiVLlixZsmxlkgFglixZsmTJkiXLViYZAGbJkiVLlixZsmxlkgFglixZsmTJkiXLViYZAGbJkiVLlixZsmxlkgFglixZsmTJ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CAAOJb/61a+45ZZbwjPaV+646qqrOP744wFb1/cHP/gB5557bufzPuhBDwrr2267Le9973uHvfAtSDZLAPi4xz2OG264IWr70Y9+xN577w3AvvvuG7KB+1qAGzdu5Ktf/Spvf/vbgeFuNsDxP0wyEav9bKJErChTJcyljBOxYj2XUSJWICSOtllkcaO4HSI1pBKx4gb+KNkq8ef6NpT7C8iRBbYpkolYsUmQjYKQiLW6JJTyU3PaeVwLCkyUiFXqsGCSSFRskolY/XEyEau3A8wxoXB2iJN3F477dKz3Psnk3UVQnl1q5fRvEy7b5MQiebd2D5ZS6tc4nTtQIJN3i9Mb8TVB5zphB7fuk0mHJNEKfDywT4Abkncr+7tk8m6lFLqA0iWMlsm7ldP5nEscXWhDgUtQHBIVO7sUk7A+CYR6bwMdbCOTd0+wCKFAg4mTd8tE6hYwlelE6k5xMpG6Cf0DKH3icql7r0OZqNiE+9bby39faKv1CyO2hUTq/p7x6FsmUseIfqGC/aLk3SKRuu8fqUTqaLusJ4mWybtlInXfT2QidatrNxYpg0ykbr9HJxOpG0AV9p6P+oVM3u11oSEk7xaJ1OP7veojKiSelv0gHqf8vVzd+9Vm4+yhEn0mJLivJe/2tvXvG0alE6n75N0ykbrvF75PyETq3i4+kbqR0+NbmNx7773853/+Z/h800038b3vfY9ddtmFXXbZhTPOOIPnPve57LHHHvz0pz/ljW98I7vuuivPfvazAdhxxx156Utfymmnncb9739/dtllF04//XQOOeSQEKi5mGvS/hnqZBpgdnOVzRIAvuY1r+GII47gnHPO4fjjj+db3/oWF198MRdffDFgB5NTTjmFc845h/3224/99tuPc845h2233ZYTTjgBGPZm26BX2FJwCR5ajkiV3iePEHNEao5IzRGpyx2RmvIEIqaDF11SccBazr1LKoptcZ+p9YsOqaykV3aoWs5Kr2AsGTsR9He+852oxJqnS73oRS/ife97H9dffz0f+chH+PWvf80ee+zB0UcfzSc/+cmQlg3g/PPPZ2ZmhuOPPz7k5v3whz/cKy3bTTfdxKte9Squvvpq1q9fL36Pfdkuyy03FU9f2SwB4OGHH84VV1zBG97wBs466yz23XdfLrjgAp7//OeHfV73utdx33338YpXvCIkgv7Sl740+M0GsF7PsDpMccUDaxsBvhV4QJOH1gY8IEwHdyHAtwEPe355TvE5tQ3EAw4i4AHVtFcH4OF1Ad2AB9DgobUR4NuAR5KH1gY8gD4E+EFrOafawsNPVdfYAXj0reUctY1cy9l+gXfTjFvLOZq2rAMPud4FeFD1iy7AQ9pm7FrO0DOATVzzUms59w1gC+8E8WX4qwkK75LKKpoOdmNr9GJkarrvUst5RACIIR4vFnN8DznqqKOi+7UuX/ziFxc8x+rVq7nooou46KKL+n25EI8DPvjBD7JmzZpeASRbm2yWABDguOOO47jjjpt3u1KKM844ozVaaYibDWCjnmG99h2+SAIPmHLkHfQiwA8aeYfwPo0ceWfX/QAcAw/pfeoCPKT3aRql49qAx9RLx7UAj2mXjmsDHnJ9KqXjhoycF96nOvCQ3qdOwIMED60FeNjbwb0Y1YCHtM00IufDy6pi9Mj5vgFsC41ToT/UZyzc+eU45fvIoJHzesxHrkKMRos8fvOT6667jnXr1nHAAQcs96Vs8rLZAsBNSTaYFWxw420fAvygkXc0p71Gi7wT5xs78i7onCbwkN6nLsAjRYAftHRcC/CIvbMLA4/UtFcbAb4VeMjrmULpuDbgIb1P0ygdN2TkfO/ScW3AQ3ifplE6bsjIebtuKnsAY0XOV/f3MkTOA30C2DpFzo8KALdOOfzww7nlllsyAOwg+W4cQGzyT9WbAG8dVY743oEADxAyCgNLIsBjG7sS4I1bBsQn21T1TFWwMAEef0F0IsCji4qgDksiwFsbzHQmwIeHzxAEeLDLjgT4cOohCPDOLl0J8NoZcwgCvNd91AfwQSAuQCfo2u/jA6fcUmkKU1Kg+OKt11pAHu41+/ANUz2lVB6uf7iJXZ+9uRQ28b+7BIULClFWccojEOX6hluXeg19wdumti2I36Bc33L9wvjz12zrj9X+/tAuQAdtQbPWKFVE/aG+DHYIdjGNgKnKHmaYADawQVMdA9gMhbtffR9x/2k5PnlTVp+TAWx+qRNt7j6vtnndV5e1lAA230/aAtiUlgh1ymKIwe9ijt8M5QMf+AAvf/nLufXWWzn44INZsSKedn/Ywx62TFe26UkGgAPIRj1hQ5jucm/PHQjwbakwoMlDa02FAf0I8MFz0Y0AXydqR8taW+QJBPG2vXAqDHvZHkCr6DOIN+tFEuDbUmFIr2yXVBhexwpGr+UsvbJj13K2OlbJZWSjKdRyll7ZsWs59y2pmHyAenvQ5KG11XKWXtnkFPAUaznbY3sEsBkx7b7EWs69A9jmGZPmbWtLZeXW7R4eIc4fwNaplvOYHsCtFAD+8pe/5MYbb+TEE08MbUqpHASSkAwAB5ANeoaVHgD2IMAPGnkH9CHADxp5J9frwMM9SLsCDzkd3AV4WJ3Hg20bAb4NeATATkfgAf0I8C3AQ04HdwEeFVhpAo8ivoyGPZZay1n+zLFrOaNo8NDGipxP8tBawUXLNhQNHloL8Ejx0MaKnIeeAWxDRs7b5tqL0UiR84iXVQNDRM4r0y/AMEt/eclLXsKhhx7K5ZdfnoNAFpAMAAeQWTNTeQB7EOAHjbwDGjy0kSLvpPdp7Mg7q/OaN6qFAN8GPKwpCmeH4UvHtQEP6X2aSum4NuAhvU8dgAfQiwDfBjzi/rAw8JDep7Ej53uXjmsBHratO/CQ3qexI+ehZwDbgJHzfQPYhoyc7xvA1iVyXo3qAVRh3F708Zuh3HzzzVx55ZU8+MEPXu5L2eQlA8ABZE5PmNNFkodGAYXRSR4aWO6T5KFhLO8mxUML3CfBQzPgOGSus0oeGoXlPmlo8NDA4hqHzwI/UJHmoWHsin/WqeAYS/LQwtCh4mXEfRIcnnAez8cSPDTPfZI8NKsbk+ShWZ0XSR4aeHvoqG2CTvLQQFc8KMdDA+bnoQFoneShOZQT89C8Pfzvljw0pxvJQ7OnMuK8Qr8CU6p5toUNUfLuBA/NPwDdObTyD9Y0D40ClDbz89G8Dgui5N0pHhpAwQySh+aXKR6aT96d5KF53Qsemk/eneKhGbDJu72enO7jZN2VbYJpa/e5w892Od82d3DgoXkQ6L7b9ivBmxWXJJN3p3hoaJu8W/LQfP8oanaQ3GWZSN1u06QSqaNsP5GJ1FFE3OUokTpYG6hwQ7mfX3GXo0Tq/j5MJFLH9w+ZSD3o1/Er573342XgLnsdu+9GCRDo+4O3kSJKpF5AxF2WidS9Ln0i9TE5gMakXsj7Hb85yhOf+ES+//3vZwDYQTIAHEA26gkrtH2rzRGpCU+gR4fGfsgRqeSI1ByRKtqcpr1dxKVONSKVpifQrheRJ1B6Zceu5dy3pKK0x5JrOXtAnvAELraWczFmEMhWKs94xjN4zWtew/XXX88hhxzSCAJ55jOfuUxXtunJ1AHgf/zHf/D0pz+dn/zkJ9P+qmWTjWUFAPsQ4NuABzR5aK3AA/oR4FuAR4qH1gY8CreaBB5SugAPaPDQ2oCHXO9CgG8DHna9ey1n6EeAbwMewXXXEXh42y9HLWfoR4AfspazXY95aGPVco5fjEau5RxsMn4tZ+/Zkv1jrFrOfQPY/MvjILWcQYDzYWo5q3JEDqAhPQ73OX4zlJe//OUAnHXWWY1tOQgklqkDwI0bN3LzzTdP+2uWVWZ1wUbXsfsQ4AeNvIN+BPgW4DH10nEDRt7ZY2MvVBsBvg14SO/TVErHtQEPEjy0FuDRmwDfBjwi79PwpePagIdcn0bpuEEj56FX6bg24FFvWxB4UPWLOvCwl23cT0sDD5jnxWgKpeMGjZwPuiiEntKgbfDIedkvBoqcV3pMAKjYGjmA9dq/WeaXJQNAX+9vPvnlL3+51K/Y5GVOF8yZIslDQzvuUoKHBo57VuNBhdyAxDw0z32SPLSQgyvFQ4OYd5PIwRXx0MCOlwkemj3cEPHQ/DY/TrhxOozOrdsUMQ/NjTcpHpo7XvLQwObgSvHQgg4TPDRrFxPx0Lw9Ujw0bwPJQ0P5tiYPzR6o0jw0AK0iHprlZs7DQ/P6lTw0jOARSttQ6VLqvM59Eus+j2CSh+bOLXloFMpynxwukDw0sNynFA/N9gmD5KHV7ZDKoSl5aFb3OslDwwHTFA/NAqYy5qEB6JIUD83n0Ix4aG4fhyLdUtpgHh6atEdLv6jn0JQ8tJBD0/cHbyNnP6PSPDTfPyQPzd6aphqnEjk0i2IS9ZUJwkayfygxdnm713JoTigthhI5NKN8prJfKF31fa8LDTKfKeDfu+xhIp8pxDk0Kw5g1U9kH2nk0JTrrs/IfKaBH+i4yxVn0NpWcpeN0uiCKIemz2c6ah7ALFkWkCUDwAsvvJBHPOIR7LDDDsnt995771K/YpOXWT1pegBzRKo4/3zbFDkiNUek5ojUall4HY0QkWp1nqCqLLGk4pC1nP1+lS4Koaem7oas5Rx5ZeueQKp+UfcEyungeknFMaeAlane6xd7/OYi73rXu3jZy17G6tWrede73tW678knnzzSVW36smQAuN9++/Ga17yGP/qjP0pu/973vsdhhx221K/ZpGWunDDrOYA9CPBtwAOqAbsT8OhJgG8DHnI6uAvw6E2AbwUe1aV2AR7QjwDfBjzsetEZeFR2GL+Wc28CfBvwwD/8ugGPvgT4IWs5y5eksWs59y2p2AY8pP47AQ9n7xTwmHYtZ6tzVXtRlfYoKlsRj1NLruUsbbIctZy9/p3Cl1rLOXrBmLYYqvt0scdvJnL++efz/Oc/n9WrV3P++efPu59SKgNAIUsGgIcddhjr1q2bFwD6DNxbssyZgjn3ZteHAD9k5F1vAvyAkXe9CfBtwEN6nzoAD6i8T10I8G3AA5o8tDbg0ZcAP2Qt574E+CFrOUvv09i1nOVL0uiR88L7VAce8iWpC/CIvE8dgEfkfaoDD3k9U4ict20x8Bsrct5ArwC2QSPnpQR7LC1yXnnKzRiyFXEAb7rppuR6lnZZMgB85zvfyYYNG+bd/vCHP3yLJ2XOlQWz7qHVhwA/ZOQd9CTADxh5l5r2Givyzl62cT+t6Y2qE+DbgIf0Pk2jdFwr8KACHNMoHdcGPKT3qQvw8JuSwEN4n7oAj8hGUygdN2jkfLBJE3hMPXJeep9GjpyHfgFsQ0bO27Gr9mIUwG1FW5lK5HyqbamR82MCwCytssMOO/C9732PBz3oQct9KcsmSwaAu+++e6/9L7/8cp75zGey3XbbLfWrNxnRuqDU6USsFMxLgEdDUUw6E+At5tLDEOABW+C+IwHejmRuRF4aAd6IbZ0I8BhRnN1e1GIJ8Gi77EqAB5ew2P2ApRDg7YO6oCsBPiTvHoAAj7en091CBPhgT+UeZksgwPvk3YXrDzJ5dzNAp7KLTKTu+0Aqkbo9VEeJ1CFO3h0lUnf3WpRIHZDJu6NE6v53JBKpAzZoyvcR3z88AglJiY2wd9W3AmBQ4dA4kbqwA+G6bb/wybtlnzLCftoHrug4ebdMpO77Q+gXAwSw2c8+cKpDAJuzwRABbLgXt84BbFK/tXEq2h5ts/0hjGMeh6LEOJUOYDM6sux0xZB+qehz/BYsW/rMZBcZPRH0SSedxKMf/egtCnXPlYpJ6b0RzuvRgQDflgrDvmALUCPetGEAAnxbKgxo8NDaUmH0JsDP84ZdeB3VeGhtqTD6EuDbUmHY7UXSE+i/T3oCoR8BfshaznW9Sk/g1Gs5U3k76p5AmG4tZ+mVHb2WM/QqqThoLWfhlR27ljP0C2AbtJYz9ApgG7SWc6otbLPjVO9azhkAZtmEZHQAuCWi7lIXzDkA2IcAP2TkHfQkwA8YedeXAD9k5F1fAnwb8LDbdWfgIdfHruUc6XXsWs70I8C3AY+UbdqARwP41V6Mphk5b+3lXXEx8Kj0OqXI+bCeAB7u/K3Ag8VHzls9xi+to0XOg3ghHDlyXrYlt9XGKeH1my+AzeQp4CybkORScAOIKVUADH0I8ENG3vUmwA8YeScH2QbwEN6nqUTeyevpQIBvAx7QBOdDlo5rBR7yIqdQOq4NeET67wI8hPepCwG+DXjItmmUjhsycr7SPw3gIb1PXYDH1EvH1YDHmKXjBo2cB/oEsA0aOR/pvLbEv7wqcZ8IEGio9QvXH8pKb1MXE1/voo7PskVLBoADiNEFRqskDw1dWG6N602p5MSSh0YBs9rxyYqYhwaW+yR5aBiq89d4aAGMJXhoBlxyWzcgOeCjtErz0MAuBQ+tOgdAzEPz+yR5aPhjq2eYcttQNHloUPEDHQ8tHGRxacRD087jk+KheRtIHppdTkjx0LzuixrXyXIxmzw0u49J8tCAJg8N+/BN8dD8MuKhoS33KcVDw3EzlVWc5KHZUxkkD80vA5BW4dB4n7BubZDioYXjFQ0emu0NMQ/NJ+9O8dD8UvLQrF1Mkodm7WGSPLTIHipO3p3koYFN3i15aN4uCR6aT96d4qGBEf2DiHuW5KH5pU60ufu82iaSd7tjIh4agqspLkkp7yFz4xMxd1kmUrfLSTKRurVB0x6SuywTqfvk3TKROqbGXZaJ1HH9I5VI3dtKJFI3bpwK96FMpB705EaeUlW2cbqRSaIjnSfGIr+t4tKq8N0oAQIVhOTdyv4u+eIzddmKooAXI8H+W7FkADiA6FKh/RRwjkitTUe6HROewGg6WLQFT0jwPPkPihyRKgatHJGasEeOSLW/QwB7j2RMLSKVbrWcofLKjl7LGfqVVByylrM/KKV7sa1TKitvk1wJZJORLZGO1lcyABxAjFbo0g7+fQjwbcAjxUNrAx4keGitBPg24AFNHloL8Ejx0NoI8G3AIwIhQcEwH/DoS4BvAx6RbToADwnKR6/lHNabwGPqtZzdw245ajkDDR7aaLWc3Q9NAg+mXMtZrteBh+8AbcCDxddylvofu5azohqrqkFCjFNTrOUcTQcv0C+8jUPjPAFsY04Bb02VQKScddZZnH766Wy77bZR+3333cc73vEO/uIv/gKAz3/+8+y5557LcYmbjIwOAPfee29WrFgx9tdOV7TClCr2PtWAB9AAIUNG3oF86I0beSe9T6NH3knvUw14pHhobcADaPDQhiwd1wo8EN6nKZSOawMekY47AA/bNg/wQHifOgAPrwvoBjyABg9ttMh5oE/puEEj51Ntxg8Hwvs0hcj5qG3kyHn7BZ7nMG7kvB/XNAwWOa8zB3DqcuaZZ/Lyl7+8AQD/53/+hzPPPDMAwMc//vHLcXmblEwFAN57772N5M++VvAPfvCDaXzlsorRyg64CR6az8GV4qHhuU6Ch0YBhSmSPDSw3BrJQwOXjyvBQwMt+E/z5OCSPDSwg22Ch+ZG8ZiHBtGIGPHQUPZfiofmtoWR2o3THlMq0tvChigHV4KHRsXBASIeGljuk+ShUYCSOelSPE33xPAAJs0LdA9TZkjx0FCej1bx0Dz3KclD87oXPDSFzbWX4qEZsNwnryfJQ8MI+1W6UXU7+KXX93zb3MGBh+ZBoPtuyUOjULaPuEtSSqELKLVK8tDQNoem5KH5/pHioaFtDs0UD83bR/LQLE7RaR6ay6EZ8dCAKCed5KFhc2gmeWhuH4cig12UMkkemtWfinlo0b0fL0MOTeVeelI5NH1/8DZSbrwinUPTTmToKIemzGfqxyc/Nsl8pr6vWGe0qWwk+ofMZ2rtYG1QYF8SbB8ROTRFPlP7u3V148p8phBxl23/AJlDM8pnSmUX5XKf+vvWnjPOZ+rHG8/7Q7b50zk72eOM28ed2+czjR+LWaYgxoh+JeT73/8+u+yyyzJc0aYrgwHAm266iVe96lVcffXVrF+/PrR7Y5Rl2XL05i2qtB5A/7ad8gQCOSI1R6Q6feWI1ByR6vvD8kWkdq3lLNfHruXcu6Ri5AH00wT+JaNfLefeJRVFd5yvpOKoUcBbmey8884oZV8q999//wgElmXJvffey8tf/vJlvMJNTwYDgM9//vMB+OAHP8iaNWu2rgibUrlKGvQiwLcBDxAAsAPwkANrFwJ8K/AA6jy0NuARXHcJ4CGngzsBD7neAXj0JcC3AQ97jhhwtAEP6EeAH7SWM/QiwA9ayxnCw64TAb4FeOB+J3QDHlFbDXhAk4c2ZC1nJS9y5FrOKTuo2rY24FEBcd+/rW661HIGce+PXMu5dwDbgLWc5XSwPC44ZUVfiV5cg438+ObOUYhMCSOIcte4lOM3J7ngggswxvCSl7yEM888kx133DFsW7lyJfvssw+Pfexjl/EKNz0ZDABed911rFu3jgMOOGCoU24+Uqow5dCHAD9k5J0cWEePvKMfAX7QyLvaw24hAnwb8AAaPLQ24CHXR6/ljPA+jVzLud4WAQ+gwUNrAR72so37aQsDD6vz2DM+VuS813ESeECThzZg5Lz0Po0dOW913NIvwvgU28W2pV5Ui8o2VOMVDBDANmDkfN8Atqh/INvc2GTUuIEVW1kamBe96EUA7LvvvjzucY9jZibHuC4kg2no8MMP55ZbbtkqAaBynBNoTnuNFXknvU+jR95BLwL8kJF3fQnwbcAj0nUH4GG3x9NebQT4NuAB0y0d1wY8pl46rgV4SO/TNErHDRo5D81E6qITTzNyXnqf6sCj4X2q2WPs0nFDRs6j6BfAZqpto5eOa9vmx6k8BTx1OfLII7nxxhv50Ic+xI033siFF17Ibrvtxhe+8AX22msvDjrooOW+xE1GBgOAH/jAB3j5y1/OrbfeysEHH9yI9H3Ywx421FdtcqKMTbrblwAPlnTdlQBPAYUxgxDgccC0MwEeQJcMQoBH2X27EuD9Z7Ft8QR4MKo7AR6IA3WWQIBHOXt0JcCDC1JZOgHeH9aVAO/3GYIA75N3V7aytvW2ABvYogui5N0ykToFzKUCdajsIhOp2zbRB0QidbDJu2UidYwP2LEBPzKRekjenUqkjtV3lEidIk7eLROpI+9bfw7RP3R1n6vQd1Ro830rvOCoypym2i30k+o4kbxb2Ffa1tpv6QFsaBfs1DGADXsHMEgAGxAF7CwUwOb0NEQAW13n0TZp37HEkPZK9jl+M5SvfvWrHHvssTzucY/ja1/7GmeffTa77bYb1113HR/4wAf41Kc+tdyXuMnIYADwl7/8JTfeeCMnnnhiaFNKbSVBIP7BVb0pdyHAt6XCsG0qXrakwrDrRdITCE0eWmsqDPtlaU8gsFQCfFsqjCQPrfXNml4EeO+Z60qAb0uFEdlh7FrOQB8CvPToJb2oPWo5S/13IsAPWMvZ6lrFy8hGemq1nHuXVByylrO7l1OeQDkd3PAEBq/V4ms529vBjU++f4xUy1l6Zceu5dw3gG2hcQrDqBzArRUA/tmf/RlvfetbOfXUU9l+++1D+9FHH82FF164jFe26clgAPAlL3kJhx56KJdffvlWFwSiSpyTbH7gMe3IO+hHgB808g7oQ4BvAx69I+/E+boQ4NuAh/1psW3agEfQOU3gYXWuOgOPFA+tFXhAPwL8gLWco+ngFPDw+u8CPOT1dAAe0iZjR84neWgjRc6neGh+Gel/XjBiX5AWEzkvp4PHjpy366ayB/ZleYzIeTkdPFTkvNpy/SCbjFx//fVcdtlljfYHPOAB/OpXv1qGK9p0ZTAAePPNN3PllVfy4Ac/eKhTbjaiSlV17B4E+CEj76T3afTIO2jy0EaKvJPepzrwiLxPXYAHTR5aG/CIbNSBAN8GPLwlOgMPhPepCwG+BXhMvXRcG/CAXpHzcn3syHnoVzquDXhI79NUSsclt1UvqNMsHTdk5LxdF/1ixMj5eHwaKHJ+RAC4tVYC2WmnnbjtttvYd999o/Zrr712q6/8UZfBAOATn/hEvv/972+VANCOT4okDw3HfVKWY4KiyX0SPDSwiYpTPDQ876aWJLrQRZKHBi5hMZ53Y4GF5D5JHpr9Hp3kodkHdUHEQwP7OcFDs0XaTZKHZiDiB4ZpEVWdVvLQgFBQvSrAXu0f1gV/CoXDgs4uNe4T7tkrk3eneGie+yR5aBRQSD6a07m0S4qH5u0heWj2UJ3koXnuU8RDA8fnSvDQnIKTPDQc90nw0ADLfUrx0DD2XKpa93pN8dBUbVsQv8En1vU8NH/OcD8QeGjgAIMCz0MDTanSPLTQLxI8NPvZRDw0b48UD81+NhEPDWUTGCd5aM4GSR6at4vkodk3tSQPDQeEmsm6K/3Xt8l7OWCy1m3OFlSXFXGXFYRE6v6SVDORuvUmmyiRutRhwSTRPypubN0eBSZKpB7bIU6k7pN3y0Tq7qYQ/aPqD4Adi0UidTs+Ce6ydvdhKfTr+oFMpB4+i9Mb8TVB54mxSJp7FDGkX8z6HL8ZygknnMDrX/96/v7v/95y7rXmX//1Xzn99NN54QtfuNyXt0nJYADwGc94Bq95zWu4/vrrOeSQQxpBIM985jOH+qpNTlQJReneZHNEauwJDLpoegLttvhNO0ek5ohU7wl06AHpCZQ/M0ek1nWcWCbb7DjVtZZz35KKQ9Zyhjp/ecRazrY56QlcbC3nXAlk+nL22Wfz4he/mD333BNjDA996EMpy5ITTjiBN7/5zct9eZuUDAYAfYbts846q7Ftiw8C0Q4E4gfBBPCABg+tDXgADR5aG/Cw23VnAnwb8IAmD60VeNiLtbroQIBvAx5JHloL8JDTwZ0I8C3AI5oO7gA8UrYZq5azBwJJ4AHTreXstieXrdsSwMOdvyvwABo8tLFqOVfgIwE8oMlDG7CWc4qH5oFHpON5gEe0DcQLKgRw7s611AC2IWs5Q88AtgFrOfcNYOtSy3lUDqCpff9ijt8MZcWKFXz84x/nrLPO4tprr0VrzaGHHsp+++233Je2yclgALBe+3drEqXFm50fUOvAgwQPrQV4AL0i76DpFRwt8k5e5MiRdxHwmxeMdAUe4L1P0ygd1wY8rNZVdNyQpePagIf0PnUBHr0J8K3Ao7rUaZSOGzJyXnqfxo6c7106rg2Q473mStwnAgSKF1Tb2i+AbcjIeTlWjR053zeAzc9Y+DFJzlj4Y8cEgFsrB9DLb//2b/Pbv/3by30Zm7TkVNkDiPLPaUWThwYYx7up89AMVNwniHJwpXhoAIUyEQ/NLiekeGjgOIAJHpr/LHlots0keWhAk4eGffimeGh+meShQTSHG+XgUlZxyRxcUd5AKh1CxEOL9gnr1gZe55KHFo73tnDHSu6T5KGhNUoVSR6aX6Z4aPPbwyR5aJE9HA/Nnq5M89DAcp8SPDTPfZI8NIO7rxI8NKhznar7NslD80udaGts87onzUND5AwUl6SU95DFPDTfT1I8tGb/cNsSvECZQ1Py0HwOzRQPDcX8PDRvA8lD87ZK8dBw96HkoQU9OeAseWhON0keWnCxJ+zg7vNUDs0AAhWEHJrK/i7jOM0yh6bMZ1rPoTmnauOUKaJ8pmgi7vKkqGxjz1HrK4ooh6bMZ2rtIrjLyoE+mUNT5jN1v1fhdFHavKYyh6YfUyrde4XLPmDCfWvPaYLtI7vo6jKyDC+nnnpq533PO++8KV7J5iVLAoDvete7eNnLXsbq1at517ve1brvySefvJSv2qSl4gBSm4JRlVsJRY5I9Yi4+o2VxykxBVzzBOaI1ByRKr2yOSJ1pFrOwSbj13KWXtmxazn3Lanox/C2korjTgGrarxc7PGbiVx77bXR53Xr1lGWZahM9qMf/YjJZMJhhx22HJe3ycqSAOD555/P85//fFavXs35558/735KqS0eAFYcwNpGDzxo8tDagId7LaYr8IDmdEsbAb4NeNhjYx5aK/CAfgT4FuCR4qG1Ao+g48Qyua0FeLj1rsDDHqui5Vi1nO15exDgB6zl3JsA3wY8/FtQR+DRt6TikLWcKzssQy1n6FVSsQ48GnYQbd7GoXGJAWxD1nIO/UH0j9FqOQddFEJPsV7j/oGwh9OcEc7YYmwASGzzxRy/mchXvvKVsH7eeeex/fbbc+mll7LzzjsDcOedd3LiiSfyhCc8YbkucZOUJQHAm266Kbm+tUmVCJoWMCK8Tx2AR9/IO6AXAX7QyDuE92nkyLtIx3XgkdjWCjwQ3qcOwMPrAprAQ+p/GrWcexPgB6zl3JsA3wI8+kbOp3hoY0XOQ5OHNlbkfF2vsn9MPXKeql8EcB7ONd3IecRsxeiR89ArgK1T5PyIU8BbKwfwne98J1/60pcC+APYeeedeetb38pTn/pUTjvttGW8uk1LlgQAu867K6V45zvfuZSv2qRF0ksaDz3xjJ1m5J1tqw2sY0Xe2S9LAw9gmpF3vQnwLcBj2qXj2oCH9D5NpXRcC/Dw+1W6aAcefQnwbcAj8j51AB59S8cNGTkv18eOnI/0OnbkPP0C2KKf6PvHSKXjhoyct/byL5l+TI7t0TdyPnMApy933303//Vf/8VBBx0Utd9xxx3cc889y3RVm6YsCQDmeXcrSru3pfCUSyzDNkGA9yBQ2Z0WJMAXUGo1CAEeDZPCdCbAW5yihyHAo23y544EeKsbMwgB3o/jnQnw7vMQBHi0Dc7pSoAHESCilkiA97+xIwEeAJkkegkEeB9wgGzzdtGIe8a4fdy5ZSJ1f0+480WJ1AtQWkWJ1NFFlLy7EaiDiRKpUxAl75aJ1G1fmEkmUsfYfiITqXsgEIKmRCJ14/pAlEgd4uTd2kHpEJzj9S/1qkL/kInUw62MwSfaBuLk3UL/Smyr9xWcXfoGsPnk3V0D2Hz/GCKADTdGdQ5gA/uyNEQAm+8fHg0mAthGBYCG+GV4McdvhvLsZz+bE088kXe+85085jGPAeCb3/wmr33ta3nOc56zzFe3acmSAGCed7fS6gGUnSi0uaE4PAWrN+U2AnxbKgx7DvdGXfMERm0dUmEELxTdUmH0JsC3pMJI8dDaUmH0JsCLbZ0I8H7J0gnwQ9Zy7kuAH7KWc18C/JC1nC2wTXsC7U9z/WkKtZylV3b0Ws7CK1v3BMrp4IYn0N3L0hMYeWXFtiBe/8Z/UPQKYHOeQICl1nK2bYkpYOmlpekJDOOUqjyBfWs5G+gVwNallvPYHMAlTeNupgDw/e9/P6effjp/9Ed/xOzsLAAzMzO89KUv5R3veMcyX92mJYOlgdma590LyQH0MwopABi9QC4APPAPvW7AA+hFgB8y8g56EuAHjLyT08FdCPCtwANo8NBagIe9bON+Wgw8QIDuKdRytovuBPghaznXdTdmLWe/KQk8mG7kfAOEUAEPmHLkfLBJE3hMPXKefgFsQ0bOQ78AtiEj5+3YVXsxCuC2mg7uEzk/KgDcSmXbbbflve99L+94xzu48cYbMcbw4Ac/mO222265L22Tk8EA4NY87x6lgakPqI03a7oBD+gVeQc0eGhjRd5BTwL8gJF3vQnwbcBDep+mUDquDXhMvXRcG/CAJg+tBXhI71MdeKR4aG3Ao+F9qtljqaXjhoycl6B89Mj5sN4EHlOPnHcvq8sROQ/iPh87ct79UAXDRc6X9Zt9imJIA/s+x2/Gst122/Gwhz1suS9jk5bBAODWPO+utEUESR6a/yy2BR4aVBwbAKUssNPK0UnqPDQwKuaheW9diocG2KTECR4aQKGLiIdmnykmyUNDOd6N4KFhNIGPpmo8NHCF4hM8NP9Z8tDcMsVDC4cJHhoYxyO0QE3y0AIXxz3zJA/NY4toXTu7JHhonvsU8dAK5uWhgeNkJnhoFFBIPhqVXVI8NBA6x/LQACZKJ3loYLlPSR6au9ciHhpW30kemvsdEQ8Nwn1encO/pagoCXrEQ/P3vRLebRyOUdW6gJxRn7HXbfvFvDw0RZKHBjZ5t+ShoWv9QvDQ0C75tuChAY4XqKM2mbw7xUPzybslDy0k707x0ICIN+szCJcqzUPzek3w0Ozhlf6jbR3HqQrtu4Yokbq3n0ikThXUhiJKpA6WmykTqVMQcZeTPM3QR9rs4c9RcZdlInU7NsWJ1BvcZSUSqXvdJxKp++Td9lT2vjIQc5cdEpfJu73+Mwdw+rJ+/XouuugivvKVr3DHHXc0qpR997vfXaYr2/RkMAC4Nc+7Ww9gjkiFHJGaI1Kr9RyR6q5KsUlGpCaXrW2q6Ql055fj1JC1nK0evW3a7CHGq9pxi67lDGJcHqaWc54Cnr685CUv4aqrruJ//+//zf/6X/+retnN0pDBAODWPO/uOYC9CfBtwMMNrF2BB9CLAN8GPOzAqjoDj94E+DbgAU0eWgvwiKdbasDDT7vQA3iEB5390Ao85PWMXMs5rNMEHuFB1xV4yIvsAjzc0y0FPKZeyzn0ixTwwN1706nlbNeLJPCwWlfRcUPWcq70TwN4yOng+YDHkmo5R231caq61C6prKBfLeeorUMA26C1nIE+AWxdajlHY9+UZWvNA/hP//RP/N//+3953OMet9yXssnL4LWAt8p5d20EBzANPCLvUwfgEXmfugAP+hHgh4y8g54E+AEj7/oS4FuBh5QuwAN6EeDbgEdkhw7AQ3qfuhDgW4EHMM3Sca3AQ653AB59S8cNGTkPTR7aaJHz0Kt03KCR8xBA1diR81HbyJHzSl7kQJHzOQ/g9GXPPfdk++23X+7L2CxkcAC4NYrSeEqba3CPI489FGkemtuGIuJP4drqPLSIw1MQ5eBK8dB8Dq4UD80uZY5AO3BOlEny0MDybyQPzR6qkzw0n4MryUPDvvVHPDSwfKIUDw3HfRI8NMByn1I8NIw9l6rWg17xuq30r3xbZTKxv7VBxEPz5/Tncbb1B2uff63GQ/PcpwbXSZkkD81+NhHvydsjxUOzn02Sh2ZPV8Y8NKz+kjw0bxfJQ8Nyn1I8NBwQSvLQ3Of6NnkvB0zWus3ZAmIeGi6HpoIGD839RslDsw9pk+Sh2eUkwdM0SR6aPy7FQ/M5NCUPzefQTPLQcP1D8tDsDZXkoYUcmtrdh4kcmpKHBo6XJk5vxNcEnSfGIr9ejWHKuZgg5DP1dsaEMY9CRTk0ZT5TiLnLyulc5tCU+Uy9HdBQFJMon6m1h7dBLX+g8mOXnVpI5dD8/C+utYBc5NCM8pkCUQ5NVeUzrefQ9ONIlEMzjE+pt80sQ8o73/lOXv/61/P+97+fvffee7kvZ5OWDAAHkGLODr45IjVHpHpPoEMPpDyBcj1HpFKtpzxPjTZVeWXrnkCk98kjxByRuuRaznK97gmU41TdE+j2W0otZ6n/sWs5K4RXtu4JhGZloy61nMf0ABpiGy7m+M1QHvWoR7F+/Xoe9KAHse2227JixYpo+//3//1/y3Rlm55kADiAqNJQzHmg4R+CMfCQ08GdgEd4SHYDHikeWhsBvg14QJOH1gY8QD70FibAtwIPaPDQ2oCHnA7uRIBvBReJtjbg4c6fAh7TruUcA/EYeECThzZoLWf3/Ung4bZ1BR6RjjsAD9s2D/CAajq4A/DwuoBuwANo8NBGq+UM9CmpOGgt51RbeHlV1TV2SGXVt5Zz1DZyLWf7BX66YZhazpkDOH35wz/8Q2699VbOOecc1qxZk4NAWiQDwAGkKE3lASygKwG+GxjpCjzsf8sReQfVgD125J30PtWBR4qH1go8ZFsn4FFdahcC/JC1nO16kQQe0OShDVrLGXoR4Aet5YzwPtWAx7Qj5yM7jB05DzR4aBHg8INDGnh4HQbQ1iNyXup/7Mh5q+v4pXWsyPneAWwdIudHnwLeTEHcUuSaa67hG9/4Bg9/+MOX+1I2eckAcABRc8algSF4n0aPvKPyPtWBB0w38i417TVW5F1fAnwr8Ih0XluSAB6paa8AyJsE+DbgIdenUTquDXhMu3RcG/CYdum4NuBhf1psmyFLxw0aOQ/9SscNGDmfoq1Ejiyvf6fwISPnpU3GjpzvG8DWLXJ+K0RkI8uBBx7Ifffdt9yXsVlIBoADiDK4IBA3xLm+vyAB3i91os29yFfbfPABgxDgwSUs7kiAB5IBCYshwKPoR4B3v2MQArzTTWcCvNi2VAJ8gQUAXQnwVvfFIAR466hyxPcOBHj7uzWDEOAbfcCE+9bbyxPuI7uIfhHaVPVMDfeMR3lKJFKnlrzb2S9K3q08gBb9wyXvlonU0XaZSqROYZN3y0TqQJS8WyZSx1Cd3yVSt9+jKzAmEqkbQBX2no/6hUze7XWhnQ9PJu8WidSNt4vTuQrBV+LUQwSwObt0DWDzybuHCGDzuu8awGb38YFTSwtg88u2ADY1JgA0LM0DuJli1b/6q7/itNNO4+yzz+aQQw5pcAB32GGHZbqyTU8yABxAVGmsF5CeBPg2z1OqrS0VBtL75BHi/AT4tlQYQIOH1pYKQ053dSHAt6bCgCYPrS0VBv0I8K2pMPxBKd2LbYslwA9Zy1muj17LGfoR4IM9ll7Lud4WeQKBBg9NeJ78/bnYWs5W57HHaaxazl7H9t4ft5az9Mp2CmAz/oOiSwBbWyorq+OWfhHGp9gutm1+T2yXVFaYyis7VC3nzAGcvjztaU8D4ElPelLUbox1ApRlzsbtJQPAAaSY06iZCnWkgMfUI+8QD7sa8Jh25B1iOnj0yDvoRYBvAx71actoWWsLU5o9CPBD1nK224sk8AhAvCPwgJ61nJ1tksAj6KIb8OgdOS/X68BD9ospRM5Lm4weOQ9NHproxNOMnPc29NdRiK8u4sto2GOpkfPyZ44dOY+iXwCbqbbNF8AmRqYsU5KvfOUry30Jm41kADiA2Cjg/gT4ISPv+hLgh4y8k96nsSPv/H6VLgqhp6bu2oCHBCvTKB3XBjykbaZROq4NeEjv0zRKx7UBj6mXjhswcj5lm7Ei5yPv09iR8257ctm6zY5TY5aOGzJyPoByd3VDRM7nKeDpy5FHHrncl7DZSAaAA4gqDWiT5KGBKwSe4KGB4z75EcI9L0lwalDVOC55aPYrBAhUdpCxXCz7OcVD8946yUMDC0xTPDQcx0by0Oxz0yR5aGCnm1M8NM99kjw0DJb7lOKh+d8oeWhgeUkJHhoAMkm04KGB4z5JHhoQkj1DxEPz3Cdkm7eLdg8wwUML/MAUD62gyUMrQGmV5KFRWO5TI3E0JslDs7bSSR4aWO6T5KFhLP8pxUML3CfBQ7O/X5HkoeGSd2sHpSUPDXnfCrso0jw0DD7RNhB4hXX9K7Gt3lfwdvHJuwUPTdrW7lfx0HzybslDs7oxSR6a7x8pHhp4e+iobYJO8tBAV3w0x0MD5uehgX1ZSvDQ8H1C8tC8PfzvLu0YEXGVPRp051LKiPMK/QpMqebZFjZEyburROqV/aqxC6jGKdKJ1ClApfqF7B/4sauyR5MX6MermdAHJmKZSqTuuctRInWIucsikbpP3u0TqY/pAdyapoCvu+46Dj74YIqi4Lrrrmvdd6urVNYiGQAOIKrUFHN2ajNHpIo36zD9lSNSnWPK4cYckZojUjeFiNSmJ3CQkopJeyyulrP0yo5dy9mum8oeCE/gIms5q80JVW1G8ohHPILbb7+d3XbbjUc84hEWmCf4lpkDGEsGgAOIKrUFgfQjwLcBD8SxnYAH0IcA3wY8oBoMuwAPxHRwFwJ8G/BI8dBagQc0eWgtBPg24CGng7sADzkdXAce1t3ZA3jQ5KG1AY/IRmPXcoZ+BPgBazkXbjUJPKR0AR7Qq5azXB+7ljMkeGhGTLtPsZazt/1y1HKGajp47FrOdl1OBQ9Ry3k8DyCGdN/oc/xmIjfddBMPeMADwnqWbpIB4BAyZ1CTMJ9ECnjYbRVoGzzyTnqfRo6860uAHzTyDnoR4NuAx7RLx7UBD+l96gI8Itt0IMC3AQ977BRLx7UAj6mXjhswct4eG3uhxoqct+ftUTpuwMj53gFs0h5u21il44aMnK/sMFzkfJgeHkO2IgAoa/7m+r/dpVh4l01b3va2t6GU4pRTTgltxhjOOOMM1q5dyzbbbMNRRx3Fv/3bv0XHbdiwgVe/+tXsuuuubLfddjzzmc/k5z//+aKuQZUlzGnUnPUEVksTkkTbJTZljCsdF2oIl4htdowo/FJbXo7/LLcpt822K/eH5Zto24bYZkoF2i5NqdBlgS4VWivKsmDO/2m7nC0nzOoJG8sJG6PlDBv1DBv8X2mX6/UK1usVbHB/682K0LberLSfzQrWmxmxnLDeFKw3ivVGs4GSDWaODWaWz9+6Dj07h5mdw8zNgVtnzv+VdlmWqLnS6d0tpQ1KuV7Zwldw8fbw+q+20fzTibbkNmkL5Wxh/yJb6CLYoiwLawun/7IsmNUTZxdri9mkLSZs0DWbLGgPb5P5bGFYb2JbmLnZyhZz3hZlwxY4W1DWbWIonD2KOR1s4ftI6ANzsf7ttoQ9OtiiELaw7bZvNGxRWlvo0tqjLO163RazumYLbwc9YaN2tqj3D2kPMxNssaG1f8yw3hRsELaw9pjlc94Wzh7MeZvUbDHXYgtHW/EBbKEv+DbfB+YQtqnsEY1Di7VFNE7FtkCrYAtds8VcafvErK7GqOY45WxQ1uxgVrh1MQ5pZ4tG/1jBej0T+oa3xQajQ9/43K3r+MIt38XMzbo+MYuZq/qI7Q81W4wkngO4lL8+8rWvfY1nPOMZrF27FqUU//AP/xBtH+u5fOmll/JP//RP4fPrXvc6dtppJ4444ghuvvnmfj9qC5fNGgB++9vf5uKLL26QOs8991zOO+883v3ud/Ptb3+b3Xffnac85Sncc889YZ9TTjmFK664gk984hN8/etf59577+W4445bHD+gNCitQbuHmgsIUTr+s/sh2kj8xe0klvW21B9auQTVbopDe8ChrEdK+z8LOrRRaG0H2VIrSuPXC0rjQGG0nDBnCmbdn/08Ybb1b4ZZM2GjmWGjmbAxtBfMGsWsgVljmEUza0pmTckXb73WBoeUZbx0+qa0D7dq3bj1/7+9f4+2pSrP/PHPrLX32YfraQE5h6OAhw6K8aCh8YYSxcglBnQYf90aNd7ajGgHDATvwQwxTcA2raE7RqMOh5oYhG+GJK0ZdgSiwXaAlxwhokTUSCsaTmgVOWo8e+9VNX9/zEu9s2ruuav2rlX7XOYzOFStqlVr13rfNWc99dbzvq/zQRX6ooz5YiX7r+AjnbC/lq9V/T4t/adsAV/jm9oXhoybpfFD5X1hiWHKF9XErFdd/OB8MGf9MBf6AsUyzhfGH//7XxK+KMuWL5T1hfdDVZmoVaUhGCNNe1uyt+IYWcne7X0IX9RjR0V8YV/riC+0qn1Rhb4oK+sH/0/4Imb7as6vLwV+kD6Y8+NqiaL2hTa+mFL7Qjd9IX1QrewLZOKa94Uja7qDL3T795/wBVrOWyr0i67nJ1XZgFowTxH4oqrEXKVVPU9VkbHRnJuqwi6lL+asL6Qf7D4m1g8T4wutWNLWFxhfLOup8IMcCw1fuG0HKH7605/ymMc8hne+853R/WNdl6+88koOOeQQAG699Vbe+c538ra3vY1jjjmG3/md31nflzzAsN8+Av7JT37CC1/4Qt73vvdxxRVX+O1aa66++mouu+wynvOc5wDmjmDr1q1cc801vOIVr+CBBx7g/e9/P3/+53/O2WefDcCHP/xhjj/+eG666SbOO++8XueipiVMqt4C+PoRsHgc6R5BRnRo7hFkBcG++svjjwseQcZ0aIWfkzsJ4FOlMICWDi0lgE+VwpCPgzuVwpAn2UUAnyiF0beXc1SHFvXH8L2c5bbRezn3FMAP2cu5d0vFYF/zEaR4HOzGhX0EKR8Hr7Wl4pC9nOXj4LF7OfdOYGuNgXBbK4EtUcqqbwLbkL2c5Vw1WC/nYg0BhrVi5EfAz3jGM3jGM54R/6gRr8v33HMPP/dzPwfAX//1X/Mf/+N/5Dd/8zd58pOfzFlnndXvSx3g2G8J4IUXXsj555/P2WefHRDAu+++m927d3Puuef6bQsLCzz1qU/llltu4RWveAW7du1ieXk5eM/27dvZuXMnt9xyS28CSGUeQZoLYXcB/KCZdxJN4hHToSWIR0yHliIeQEuHNlrmHfQSwKeIR31h60g85HpyXwfiAS0dWop4mM9oEI6EAH7IXs69BfAD9nKO6tA6+mO9mfPY7wlt4iHXZ5E5H9OhjZU53zeBbcjMeTO+jW3GzpyXc9VgmfNVffM7cwxEAPfs2RNsXlhYYGFhoddHjXldPvzww/nBD37ACSecwA033OCjfps3b849ghvYLwngtddey5e+9CW++MUvtvbt3r0bgK1btwbbt27d6p//7969m02bNvGgBz2o9R53fAyLi4ssLi76135gTKdQlL0F8CniERXAr3Jn3b741Re4WWbeAb0E8INm3kE/AXyCeMQE8IO2jksRD++T2bSOSxEPGX2aSeu4BPGYdeu4ITPnzWlr+9Xa0ahZZs7L6NPYmfNN242ZOe92KXE+Y2XOhzetdg4TIeU1Zc4X+98j4OOPPz54/eY3v5nLL7+812fM8rrcxDnnnMNv/MZvcNppp/H1r3+d888/H4CvfvWrPOxhD+t13gc69jsCeM8993DxxRdzww03sHnz5hXfpwQBATsQG9uaWO09V111FW95y1vaO5zWI1aI1cz0BIVYwc66ZuKXhVjN4ZqgEKvYh7L//GwqthHbpwgLsdprHqYIslbgC7G6UxKFWMEWJzaX6qAQK5gLcawQKwUUWgeFWAFfjLVAB4VYQTZltwVZlV3qigkKWYjVHKjihVgBKoUsxGqKE1tiq4yeSUNYvNteqeoC0qp+LT7e21LavKLtI7vuikn7ItEKXD6wL96t7Ctlvpcs3q2UoiqgrIwfSmVtjK0rVhEU7y6wBYp9oWLrl2Li12Xxbl8IWhbKVcZnEwxDKKjAEtOJPef//S+3GUIuine7QsU+21EW71amaLcs3u2KmWtr4qB4t/+dOh+J8WmYZlC8OzYupK98IXVAq/pvo5QhcrJ4t/OR9Z9W8ULqbnzIQurmp1mPiWaR6KIqgkLq5p6wHieykLoZA1W0kLr5O1VYSB2C4t3IQupgXstC6n5pxoUspO4PK60xrc1l8W5ZSF3OU9r9pv1vv36/X4+MGTdGlC3eHSukbj4/LKTux4It3i0Lqftx0Zqfar/IQupuDMQKqZtDq6CQOoTFu4NC6va35gqp+y8wAoYqBH3PPfdw5JFH+u19o3/BZ87gutzEn/zJn/CmN72Je+65h49+9KMcffTRAOzatYvnP//5/U/6AMZ+RwB37drFfffdx+mnn+63lWXJZz7zGd75zndy1113AeZu4rjjjvPvue+++/zdx7Zt21haWuL+++8P7jbuu+8+nvSkJ634t9/4xjdy6aWX+td79uwxd0dTm+IGvVoypUphRHVocjDHIk7NbX6foqVDEzy0WQojpkNLlcIAWjq0VEumVCkMaOvQkqUwoF9LplQpDLM5GgmMlcIIbNyMBEb2JUth2P11FCpdCsPZAtqRQGn/WfRyjurQRurl3Lul4grjo3A2aujQUr2c+7ZUHLKXM7R1aGP1cm7aVY6Pmfdyph4XzUggzLaXs4zKDtbLeVKf38yhCX//azkeOPLIIwMCuBZs27YNmM11uYl/9+/+XTQRJRq8Ocix3xHApz/96dxxxx3Btpe97GWccsopvP71r+ekk05i27Zt3HjjjZx2miEJS0tL3Hzzzfy3//bfADj99NOZn5/nxhtv5LnPfS4A9957L1/5yld429vetuLfXlH7MC2hsBfMHgL4FPGI6dCSxENui+7rTjwMSaUz8TDbGhNrQgCfIh7Q1qEliYf5Y3HiAbR0aAni0beXc28BfIJ4xHRoKeIhv+bYvZyhpwB+wF7OMR2aIx7yseUsejnHdGhj9XKW62P3cg7sOnYvZ/olsAVf0Y2PNfZybhG/xo3RWno5jxkB3JewY8eOmV2XgVXbv0nkVnA19jsCeMQRR7Bz585g22GHHcbRRx/tt19yySVceeWVnHzyyZx88slceeWVHHroobzgBS8AYMuWLbz85S/n1a9+NUcffTRHHXUUr3nNazj11FN99lEf6NLWeQL6COAHzbyT6xEyMsvMO7k+duZdbwF8gnjMvHVcinjI6FMH4mG+WuiblAA+RTxk9GkmreNSxEP6ZAat41YlHs7+XYiHPJ+RM+dl9Gn0zHl7d1rbdcTMeT8uzDKcp7C/vdlkzpv1ZgSwvjFaS+a83g8fAXfFT37yE775zW/613fffTe33347Rx11FCeccMJMr8uy/dtqj4tzK7ga+x0B7ILXve51/OxnP+O3fuu3uP/++3nCE57ADTfcwBFHHOHf80d/9EfMzc3x3Oc+l5/97Gc8/elP54Mf/CATGa3oirJEF2Vch4bRPUR1aBjuEejQ7EQe1aFBoKOp9Tc4phBqasS+pg5N+fMUOjS01+40dWgFZmKUOjSKiqKK69AojMYmpkPD6nCkDg2stiaiQzN80RBSp0NDwcRpAhs6NPO9K6I6NKwthA5NAZQ6rkNzthE6NFBCAxjq0LxffNip3ua5XmUvYMpe4OzBLR2a+01Y/2ll6IxWVVSHRlUYzZm9+MS0mVKHRgHLldWTFaEOzfhgLtChoak/v6FD82QsokPTzqZShwa+/p73iR0XZqezv7RrPUakDs3/lGM6NNr2d+MORVuHZv0idWgIP1AQ6NAq68yYDs35QOrQzHLibd7UB050YwzgtJhtHZp5j47q0IC2Dg3QVjcL2LkF4ReFsvOTckUUS+ET971LUFhtpplI7PfV/rOkjlna1xNp5Q8N3+PXjQ+Mfs6MC20/3x9vx4U7VmqXzVerDGmuKpQq2uMhptP0ftEt3XLtD+3X3RiQ2mXjB12PGUw90/Mectr6GFlfaEJyvZbje+Af/uEfeNrTnuZfO7nUS17yEj74wQ/O9Los27/ddtttvOY1r+G1r30tZ5xxBmDqAb797W9fNZJ4sEHpWMfkjE7Ys2cPW7Zs4ZcWnsv8pkNhbs7o0OYm5rHX3MREAufMPz1RMDFLPZnARKEnBdWcgkJRzRXoArN/TqELRTUxk7aeo349MTfn1cTcjcpt/t9ELMU+934KLfZpu0/DRJvH1gUmYqk0xaRCFVBMKvNPaeYmFUVRMTepmC8qJkXFfFEyKSo2TUrmlFluKqbMqYqFyZQ5VbJQTJkvKhbUsl2fsllNmVdTNhfLLKhlNqmSzcWy2aaWmadks33fJioWlGaTUiyogs1qwhwTLnjI6ajJBDU3D5MCNT+PmpuDSQFzc1DY5aQwvpgroCiCpZ4U1jfmXzVR1vZ2m7W5sZvzjbOhEn6x21ToB+krgn06WDf7zIVITzRqoo1vCo0qNMXE+UQzKSqKQjM3KZkUmrmiYn5Sen/MFRWbxHJTMWWusH6wfllQy8wXJQvFMvOqNDa3PjDrxjfzmH2biymbKNmsSuaVZrNSzCvFAhMW1FytQ5tM6jExP2f9YceG88vcBF0UMFfUSzsu9FxR27+w/phT9RgpFNUc/nXtD9UaA1Xw27fb3HprzGjrL233aZg4n1h/KLM0/qiMT5RmMjFjYW5SMVF2fExKJqo5LkrmrD/mVcWC8Mtm6wfpj81+fclv2+T2KfMZm1XFvILNqmCeggU1x4KaN4+D543t1dyc8YX1jRkXE+8XPefmJbsMxkXoDz0n5ic3PubqsVA1xkVzLkrNU/U2OzdNtB1PNozr56l6XDh/TCZmTLj5qSi0GQuTauVxMbHzlPCDGSNlwh9T6wcxh6mpGBeaeRSblRsXE87Z/mj+Xv8VDzzwwLp1datdlx554ZVMFlZOlFwN5eJe/ulPfnem5zoLPP7xj+fyyy/nV37lV4Ltn/jEJ/i93/s9du3atUFntu/hgIwAjg09naLV1D6e6CGAT5XCgJYOLVUKI6pDaz1acceRLoVhT99E1MJHkOa0XeSuvm3vI4BPlcKQj4O7lMKAngL4VCkMb4tC2Cl8pCsfQfYWwIt9/kmVDwvqer1DKQz5OHjsXs7ycfDYvZxjOrSxejnHdGjuEaR8HBx9BLzOXs4xHdpovZz9elWPj7F6OcvHweGpNh4H29C6XrmUlTlWBctUKSsQv/OBejl/9K7bOfrhjIORI4D7Cu644w527NjR2r5jxw7uvPPODTijfReZAA6A6//pdp77848zL3oI4IfMvJNkZezMO/N1zfrYmXdEdGhJAXyKeEBLh5YiHn0F8Eni4S+S3YhHTIeWEsCniAe0dWgp4mFc4S56qwvgk8QDemXO13bdgMx5+/kx4jHrzPlwPITEA2acOW//PoyfOW+2NeYpxsmch9oPQ2XOL42pASR8sr6W4/dHPPKRj+SKK67g/e9/vy8Vt7i4yBVXXMEjH/nIDT67fQuZAA6ART1dmwB+yMw7EX0aO/MO6CWAHzLzTkafOgngE8TD+MtdpFYnHn0F8N3ISFfiAS761EUAnyIexp6NC92QreNSxAPqH00H4iGjT03iMfvM+fpUx86cN+tFlHjAjDPnoVcC26CZ87ibVyV+J4IEuvGgwycWG9E6rkvm/FJr/p0hNKFt13L8fog//dM/5ZnPfCbHH388j3nMYwD4x3/8R5RS/M3f/M0Gn92+hUwAB8CyrhuCA90F8BRGdN1VAG9nuyEE8H7aU+FyJQG8/5wBBPAUmASRjgJ493oQATyYKG1XAbzzxwACeM8TlD80fI9fNz5wNh9CAE8BShbDXUUAv7I/+gvgXfFuVSj/WzbjQ9URc5ewI4p3y0LqGvu7ihRSh2ax7nAs1Ak7jWUV2dba52zvfEFYSB2RsCNOSRbvloXU3TiRhdTNsi7eXTT8EEtIkMW7ZSF1mAbFu2UhdRTtQurU4wMlitmvI4ENNz66JrD5EHvED/Z33jmBzc1XHRPYnC2HSGADMT5UPIFtOUvuZ47HP/7x3H333Xz4wx/ma1/7Glprnve85/GCF7yAww47bKNPb59CJoADYImSRT3t35IpVQrD3lnHIoHh4xYXFbRvjEQCYzq0VCkMM2uZZadSGLR1aPLzk3q0Di2ZUqUwoK1DS7ZkSpTCMJGp7r2cYzq00Xo5Q0uHNlYvZxmVHbuXs4zKjt3LuW9LxSF7OUP78fxovZyhV0vFQXs5g4+qtSKBCBKo67nJbF1/L+dgWyMSCKypl/Ooj4B1fb+01uP3Vxx66KGceeaZnHDCCSwtLQHwd3/3dwA861nP2shT26eQCeAA2KsrFm07pl4C+BTxgJYOLUU8+grgk8SDtg4tRTzsbTEx4iHXuxAPaF/oUsTDHNtDAJ8gHiaI4batTjx6C+BXu9C1yEiCeCAvdiHxcL8I6EY8gF69nOXjrtF7ORPRoQXEeoa9nN1dUIx4gH8cPIteznJ99F7O0C+Bzftj/b2cm9ucj/3GZgKbIOTu97nWXs7G5uEN6np7OU/HLAStic/zfY7fD/Gtb32LX/3VX+WOO+6I1gbMdQBrZAI4ABa1Yq8N9/cSwA+YeddbAJ8kIyL61IF49BXAD5l5Bz0F8CniYb9oZ+JhNkeJR0yHliQecr0L8UBc7BrEI6ZDSxEPaf9ZtI5LEY8g+tSFeECv1nFDZs73bR03ZOa82V9EiYeMPs0kc976Rpld4RMLb4tCELSQ0MnxMfPWcXq4zHnpk6Ey50clgAcpLr74Ynbs2MFNN93ESSedxOc//3l++MMf8upXv5r//t//+0af3j6FTAAHwLJWLGuiOrTzHnKa0T7FdGgQ6m6cyKhUcR0amPlS6tBQ5r+YDs3u8zO1nafrmXWFfcpuCHRodp6XOjRqDQ4Q6NDAFCeO6dDA6mJihYrt1UDq0AAKXQU6NPMZc8R0aCinR2vr0MyBKtShOdtHdGhO+4TQoWkw2idnJ6lDQwv/1bYJtE/S5s7ecpvzc+XeL3RojgTavy11aL54tz0lqUMDo32SOjQqU7w7pkNzPpA6NCqYFDqqQzO2r6I6NCwxDXRotnh3VIcGUJUEOjQqo32N6dDseyyL9H6RxbulDs3YT8V1aO619IPdF9Oh+eLdbjw4H1n/aRXq0BxpjunQzE9TBzo0KqORjenQzD2hjurQzBioAh2aK94tC6mjCbTLQSF198ONFVK3S1lIHQi0y7KQOtji3bKQOvjXqHCM1L99sc19nB0zspC694vVLte+Mr5tFVIvCLTLspA6BUxjellqv8hC6mabGAOikDqY4t1uLpoiSPUY2E+jeOvBrbfeyqc+9Ske/OAHUxQFk8mEM888k6uuuorf/u3f5rbbbtvoU9xnkAngANirJ+y1EbCckRpGAoGckdqIBAa2Fj+Blh+CfdbSPvpRn2rOSDXkJ2ektiOBsHJGKnTs5Qx1VLYRCXTvq21RRwKbdvVR1B69nKX9O7VUHLCXs7G1CpeBj6rOpazcelnV/pw1DlYNYFmWHH744QAcc8wx/Mu//AuPeMQjOPHEE7nrrrs2+Oz2LWQCOAAW9Zx5BExPAXyCeJh1MdOtQjzk4+BOAvgU8Qi2dSAe2LvtCPGAtg4tRTyCbR2Ih7/o0U0AnyQeQJ9ezn0F8EniEdi8sSRCPFwkMEI8Zt3LGfoJ4Aft5Wx94t5nlu7GaMa9nMXnjd3L2ducNvFAPA6eSS9n6JfANmAv5+BxsNjn4exvDT5kL2fpk6F6OZc6689mjZ07d/LlL3+Zk046iSc84Qm87W1vY9OmTbz3ve/lpJNO2ujT26eQCeAA2BsQQBl5Gi/zLpxYR868o44+jZ15J6NPXQTwKeIRRJ86EI++AvgU8Yj5IUk8gJYOTVzoHCnrQjxAkO4OxENGn7oI4FPEo3fmPLR1aCNlzsvoU5N4BNGnLsSDtg4tRTwCH42dOQ/9EtgGzJwv7GpA/OQ8hdxm5qmhMufl+lCZ89WYBFATt1Wf4/dDvOlNb+KnP/0pAFdccQUXXHABv/iLv8jRRx/Nddddt8Fnt28hE8ABsKwnLOsiqkNzNbiiOjSwzc7txCFrcEV0aF77VNnJTNbgslcqqUPzr8XHa/FnUNTHNrfZ9WYNLvPnBAlUxHVoGO1TTIfmtE9Sh0YBRaWJ6dCw2iepQwOzL6ZDM5yriurQjF/KQIdmCFMZ16FZw0kdmjZXVaI6NLQJdpkPCnRo5rXbp/3f89s8y8BzC6+DsvvcbyaqQ7M+iOnQsL8lqUMDo9OM6dCw2qdmrcaiKqI6NLB1A+0XkDo0tP0sOx7qv1NFdWjmQm1+88G4UObHqCDQofkamhEdmnZ+sTaXNTRjOjScP4Pffv1+vx4ZM00dmv9M61tZQzOmQ3M1NKUOzY+LiA7N+SWmQ3P+kDo0c6jp5Y12fgtraBaodg1NWc8UkDU0g3qm7ntQGe2ye4bovncpxogbH45he02mFv6ux5Yn0sofGtYzdX6wPjDnbcaFq6Epx1SzhqaZB8MamrKeqRsPflyIeqZUYQ3NZs1GqQus9HgawIP1EfB5553n10866STuvPNOfvjDH/KgBz0oyAbOyARwECzqTezVrhSJssswEgiQM1LdUvyZnJFa875mhCPmDx+NUvU55ozUmgjkjNQVHwGvuZcz9GupmChlJaOyzUjgIC0V5dzUKGXVt5ez/JpD9XIeNQKY4XHUUUdt9Cnsk8gEcADsrebZa4lbLwF8ingALR1agnj0FcAniUdsW4J49BXAp4hHsK0D8fAXswjxgLYOLUU8zB9w4YrViYd7X22LQtipbbshezm7z4sSD2pC3oV4SN90IR5mf7UxvZyFzcfu5SwfB7eIhwsvdSQewePgDsQj5puxejmb79s9gW3QXs52f3SZ3GfmqfX0cgZ6JbB16eWsde2DmUMT2motx2cc0MgEcAAsMsde+7imjwB+yMy7vgL4QTPvZPSpQTxmnXkno09dBPAp4tG7l7P0yUb0cnb2bxIPIjq0BPEwP4cKxOeniAe0yflovZzlSY6cOR/Yf0Uy0pV4gIs+dSEectvomfM9E9iGzJzvncAW7GvOU/Wpdsmch34JbF0y56sRCeDB+gg4ozsyARwAJgKog0l29Mw7EX0aO/Mu9tjLEw95PjPIvDPbIhEnGf2gG/GYdeu4FPGYeeu4BPGQ0adZtI5LEQ+zrmt/sArxAPq0jhs0c16uJ/d1IB7go09diIf5jAbhkDdGDT8MmTnfO4FtwMz53glsYt/YreO6Zc7PMxo08fmiz/EZBzQyARwAJgnEJhnQXQDvLz5dBfBgRNeVnbQqG8NbgwDef3RXAbzdh2LdAvjKPmrqKoA3ywlDCODNe3R3ATzmrn8QATw2KUQZw60mgHdLTxiUP7S3AN4fb33rDl5JAO+Kdw8hgDf+0H5dFu+eWPJn/BAW7w4KqdvfcrSQuvOLsoSqVCgqy62smF8UUscSoXax7vp1c5/8LXtOltwninc7nuf87v1Xa2gdN1PKaXor+5srKNDRQupmOYkkiNh9EX8U6KCQuvODK949SAKb81XXBDZvJ0uc15PAJvZ1SmBD+3Gx3gQ2NyZSCWyMmASSkbEaMgEcAIt63j8C7iOAT5XCAGjp0FKlMIA+AnhHPuXjYPce+Ti4UykMGe3oIIBPlcKAVSKAPtJh399TAJ8qhWEiYz16OUMvAbz2Nq/tv9Zezq2obMMfs+zlHPhm7F7O0K+loo8214+D19zL2ds4sozuq8nGens5m2NVsByrl7P53B4JbAP2cu6dwCb9YfclE9jqob/uBLYuvZx1NeIlV5MjgBlJZAI4ABarOfa64E8PAfygmXfQSwA/aOadv0iOn3kH/QTwKeKBol8vZ+glgB+0l3Nsm9+naOnQEsSjby9noJcAftBeztDWoY2UOR/YuEk8IvuGzJx3toA28ZD2n0XmfO8EtgEz53snsK0wPgpnIz+XOr+snDnfN4GtW+b8MmMhawAzVkMmgANgsZpnMRZxakyss8y8M5Osu0iFxENGn2aSeefXI8RDRp86EA+gV+adsWN40UsJ4FPEQ0afZtE6LkU8Zt86bmXiIaNPs2gdlyIe0DNz3vyxOPEAZpk537t1XIJ4zLp13JCZ89AzgW3AzPm+CWxDZs73TWDrlDmvR9QAZmSsgkwAB4DRABLVobnXMR0aVLUOyurQgJV1aGAmW6FDc8uoDg2CGTHQoaHMf0KHZj5KE9OhyadoqrkPua5AWaKkIKpDo9bggLkYmY+P69AojPYpqkeL6NCc7WM6NICCOaQOzS1jOjSnfQp0aGC0TxEdmiveHdOhaaz2ydnJ2j7UOtW+8S5u2tpcndrbWvuEDs2RQPu3ZfHuqA6tMNonqUPD/r5iOjQKGjpNFzExvpoUOtChmX0VMR2a4SlVoENDsbIOzfkgpkPDFO8OdGjudxjRoeHGh9ShWdtEdWg+xN72gy/e7WwsindDRIdmX0sdWoEZHzEdmrNlTIdGZTSyUoeG1QnKQurmM+rxIQupGz9YbbPTOCvC4t2ikLr53m5MKIJC6pigoSykriDQLgeF1J1tYoXUAa91JfSL0/0htzm/VIjfjLbvsZ8tC6m734T9vKCQegGqUkEhdaoi0C635im0L6SOq14wBjThDcpajs84oJEJ4ABYrObYW7k78MgjYPk4knYk0EeeFDkjlZyRmjNSTSSw9kPOSMV+T2hHAuX6LHo5922pOGQv574tFYfs5WzGdzwSaL6aHU89ejmrasxOIDosY7WG4zMObGQCOACWqjkWLQHsI4BPEQ+I6NASxMMEMRoTa+H+19ahpYhH8Di4C/GIbUsJ4BPEw9BPc0AX4gH0EsCniIdZD3VoSeIB/QTwQ/ZydgfFbC/2dSIe3ifdiAfQSwA/ZC9naOvQxurlHNOhaemPhh+SxANo6dASxMOctrZfLSQeIH77M+jljHgcPHYv56btxuzl7HYpcT7r7uU8ZhJIRsYqyL/GAbBUTVj0mahxIign1plk3kF9sRs7806uN4lH4y576Mw7Y6dm9ENGnKw/OhCP2g8zah2XIh4w09ZxSeJhT78r8Qhs3SAexuZVbXPSxENGn2bSOm7AzPnereNSxEOOixm0jhsyc96PG8bPnK+gVwJbMK9R+5TGuoecm9aZwNYlc17pnAWcse8gE8ABsFxNmFZFVIcGUOgqqkMDWQuNoAZXTIdmDlShDg2M0CWiQ3M1uIjp0FBeFwMaWYMrpkPT0NajOe2T3GaXtRYnokOzny11aL4Gl+UFsRpcUodGZWpwxXRozgcxHZp7LXVoxvZVVIfmtE+BDm37aeaKENOhAVQlMR2a0z4FOjT7Hssi7VIF2iepQwOrfZI29yyj7Q+vQ7PnKXVo2tYKNF+tqUMDrUIdmiPNMR2a+WnqqA4NoKiKQIdmgtE6qkMzY6AKdGiuhmZUhwZWKxrRobnXUodmlzEdmj9M6NDA1tCM6NDce2I6NMctgnU7ZmI6NFdDM9ChFayoQ3NjIaZDM2NBR2oEFsZvBdEamrVuua6hKeuZop1e08xbsp5pS7tc2BsgZ7BSed9Ea2j6Wo2OUUm7qnB8uLnHu1kLf9djy9/gqNqdXsfsIMZMUENT+Ff61nx+Xc+0WUPT/fzC2pnG/uM+Aq5NudbjMw5sZAI4AJb1hMUq7AWcM1LlPjsVy7tt+1k5I9WQn5yR2o4EQs5IdX6ZSUZqx17Ocn3sXs6BXcfu5Uy/lorBV3TjoxkBHJEAZmSshkwAB8BSNce8m0gbBDAlgE8Rj5oAdiQeQB8BfIp4xHRoSeIh1yNkpKVDSxCPmA4tRTzkehcBfIp4mAtd917OvQXwCeLRt5dzbwF8inhYf3clHuarhb4Zq5dzTIc2Vi9neZPUIh7+ZqkH8XD270I85PmM3MvZr1PftI7Wy9nendZ2HbGXsx8XZhnOU9jfXr9ezqockQBqwu+3luMzDmhkAjgAlsoJ8/bOro8AfsjMuyD6NHLmHeLYFvGw27oSDxl96kI8QJDuDgL4FPGQ0acuxKO3AH7AXs4y+tQkHkH0qQPxCKJPXYgHbR1aSgA/ZC9n54kY8Zh15nxMhzZa5jy0dGhjZc7L+WnszHmo/Fw1duZ83wS2LpnzasQyMPkRcMZqyARwACxXBUv2zq6PAH7IzDuAXgL4ATPvoo+9xD5/7e5APNyursTD2DhBOPwFbnXi0SIhpImHjD51EsCniIf3STfi0VcAnyQetAXwQ7aOSxEP6Jc5b47t0TouQTxk9GkmreNSxCO2LUU87HqMeMjo0ywy5+X8NHrmPP0S2AbNnG/crA6ROZ8jgBn7EjIBHADTqmCqi94CeLAFi+30tJoAHqBQ1SACeJMgorsL4N0mN8etQwDvPsvMp4rVBPAAWg0jgHd+6SqAN4dWwwjgrYG7CuABU7x7AAG8auzzUPVnBQJ495kN35rP6CeAd8s6Qcf6gbpArkyYavvD7kMTK6TuinfLQurnPeS0sHi3LKQOBIWjlSVUonh3UEjd2S5SSN0cXtu/uc/b198NhTZv7zPjwdnc81A3hhX4Qur2eFlIHaqgeLcspO5tGClUbPyiB0lgc8W7OyewAVRqkAQ2/1p8vLeltHlkLvJ+rtz7eyawYZKmUglsQavIjIwNRiaAA2C5mtQRwB4C+FQpDGjr0FKlMMwfcKxhdQF8qhSGfBzcjAQOIYCXd9V+PSGAryOT6xfAp0phyKhsl1IYMio7di/nvgJ4rw4IT8OdjTd4l1IYQVS2EQmUX3MWvZyNK1wUatxezjIqO3ovZ/v5bjxYqoOJjc22l3M4HlQQCYR2AtugvZzt349GAu2+WCRwiF7OZltjnhLku5nA1qWX85gRwPwIOGM1ZAI4AJbLCXNOA9hDAD9k5l1vAfyQmXd+sm0TD/k4eBaZd0AvAXyKeLSI3yrEw1/sIsTDEYGuxMP4y12kVicefQXw3chIV+KB/e2N38vZWF0Fx42VOS8fBzeJx+wz5+tTHTtz3qwXlvgpOycV9Y1q4IeBM+ehVwLboJnzUD8O9r8TQQLdeLBLR4xTCWxqzAigJvx+azk+44BGJoADoKwKlpsRwJEz7/oK4IfMvOstgE8RDxF9mkXruBTxMOvNCODKxMOs69ofCOIR06EliEdtfzoRj5gOTV7o0uSisQy2dSAe4KNPTeIBbR1aingE22bQOm7IzPm+reOSxCOweWNJhHjI6FODeMw6cx76JbANmjmPIE1jZ86Lzxsqc16VciBmZGwsMgEcAKUuKFfQoZnlhJgODawGsKF1mugqqkMz79GBDg1ltE9RHRrm4hvToblloEOjMtqnmA4Nq31SAFaHhrafUa+bz8UvPWFQ/tDwPX5dGe1TRIfmj1d4HRrYi5GCpg6NqkKpIqpDc0upQzN+0VEdmvGHDnRooR9CHZr5uDKuQ8Nqn6QOzfklokNz2iepQ8MSoagOzb6O6tDcsopsM9dTsU9onxzPc35XxHVo9jvGdGjY35fUoZnlJKpDMz5o+6NAR3Vornh3TIeGoq1Dw46PmA7N+Uro0DRmTER1aN5OlhRIHZq1TaBDkzZv6tDEvlpLq+yzPDMGPQl0PnA+aujQCsz4kDo0V7xbFlIP5ildBIXUqaAoJtFC6sYHjblLEWiXZSF145cyKKSugEC7LAupW7/ECqkrgFL7OaW2vTO4HAPa/26dv9zcGPhFjAu/TdX3xPWcqt0frQup09AuW/8FxbuVHbcjZgED+TFuRhKZAA6AaVkwcY91c0aq/TtmIR8H54zUnJGaM1JFdMkN55g/7L5ZZqQaU0TmKd32Q5+WioP2coZ+LRW9P9bfy7m5LYgEAnW0vEcv51IxGrQOnb6W4zMOaGQCOACmZcHEhvb7COBTxMMTv47EA0U/AXyKePj1bsSjtwA+SUYULR1agnj0FcCniAeIC10H4gE9BfBD9nI2m6PEY+a9nBEXuwbxmHUvZ8Tj4NF7OUNLhzZWL+eYDm2sXs5mv7kRGr2Xs/WNMruCZW2LQhC0kNDJ8dG7l7NcD8ZMY1z06OWsR44AZmSkkAngACgrxdQSwD4C+CEz72T0aezMu94C+ATxMNu6Ew8ZfeoigE8RD6ClQxu0dVyKeACzbB2XJB6xbQni0VcAnyIewbYZtI4bMnPeva+2RSHs1LbdkJnzQfSpSTyox8VKxAN/o+S+hg5tn8icN/urjcmcFzYfO3O+Hj/hMLUe8gbvlTk/YgQwZwFnrIZMAAeALgs/MfYRwA+ZeSejT2Nn3vUWwKeIByL61IF49BXAp4hH4IcZtI5LEg+gJYBPEI++AvgU8ZD2n0XruBTxMLYOo1BDto4bNHNe+mQjMued/ZvEQ0SfViQe1FIJ83Ow85MbHwO2jhs0c16e5MiZ84H9V5y7zDzVNXNej5kEohvnu5bjMw5oZAI4AKrKCLD7CuApaCSI2H0rCODd60EE8EBQDHc1ATxmbh1EAO9v0cU2t1S0BPDmXAUJVCbusRYBvLNlVwG8uW7qQQTw5gJQ0lkAj7XFEAJ4wCfZwKoCeM/1KsRvRrs/2k8AX4CqlLmBcePDJk3VBc6tzUVSiCykTgHLVeXHiiykbnwwFxRSRxMU75aF1D0ZE4XUDWkSxbv9b9hGbSsVL6Ru7SoLqfvPcONDFFL3P+VGoo7zZ9P+btyhaBdSt36RhdTdQf5z3H2GKN4tC6lThMW7h0hgc6+7JrABwyWwOX+4771KApv5KC0+t7avJ/3KHxq+x68bH9TjaeUENl3JgzMyNhaZAA4AXSrK0lzc+gjgU6UwoK1DS5XCMOvyUfAqAvhUKQxo69ASpTDk465OAvjWXXRjWzCPK3EbLkiguLPuI4BPlcIItnUohSGjsmP3cu4rgHfRqUF6OQN9BPBD9nKWUdnReznbVbkcq5ezjMo2I4HmrsMsZ9HLOfDR2L2coV8C24C9nGVUtlMCm4sE+nFhlv5ci3EJoKr8/cuaj884sJEJ4ACoqoKiVL0F8ENm3kE/AfygmXfQSwA/aOad/bwY8TCnbSM/HYiHsXl4gUsRD0/EI8TDLGbYy9nbok08mrYbvJezYxcR4uE4SVfiYWycGBcN4hHToY2WOQ9tHdpImfMBCXEQ/phl5nzgm7Ez56FfAlvh/lc/Dl5z5ry3cWQZ3VffoK6UwMaYEUBNeL5rOT7jgEYmgEOgUlRlfwH8kJl30E8AP2TmXRDVaxCPmWfeUUefmsRDRp+6EA/pk1m0jksSD2jr0FLEA3oJ4FPEQ0afOhEPEX3qIoBPEQ/5NWfROm7QzHno1Tpu0Mz52Da/T0SfViAe5mengNm3jhs0cx76JbANmDkf2DiYi4ju65I5X+UkkIx9CJkADoFKmQtgRIfmonUxHRpgdFBCh4bV2MR0aOaaogMdGohCxSrUoaGNZiqqQwPbKF7o0MxGojo0u5Q6NABKojo00Eb7FNGhufdIHVrdgF1scx9ntU9Sh+YL5MZ0aAUr6tDAajKFDo3CFO+O6dCcX6QOzWyrojo0MMW7Yzo0MNonqUPz2qeYDg1j70CHRmG0TxW0dGhgeI23ofWLMoaUGrKgeLe71inPT81S1e7U9dvqFX+cKN69kg5NEejQXPHumA7N+UDq0LwfIjo0cP6ogm2yeLfUoUFV69EaOjRfvFvq0MBEzCM6NMty4jo0a6NAh+Z++44NSh2aM7a0r+CUqml7z/bthkCH5vwndGjUGlqgnqfs/5VSQSF1ClCxcSF1mn6MpPzh5qs5P+9N5FI5faAbF6XXLkcLqTvbi0Lqyo5zp102H1WExbudnaztpXa5XVi9YWu3dPaO7KvnMFFI3ZFAPwbHI4AZGashE8ABoEsFpbk454xU9x53qipnpOaMVBdzyxmp+1BGatdeznLb6L2ce7ZUHLKXc++WisG+5jxlTzUXgs7Yh5AJ4ABQpUK7yFgRJx5AS4eWIh7Q1qGliIecZLsI4JPEA9o6tBTxoAhIgiQe4eOW1YlH8Di4A/EARS8BfIJ4SJ90IR5mWxElHn6dbsSjby9ncyHsLoAftJezRJN4eELejXjIx8FdiEfghwbxiPtjZeJh1nXtD1YhHkBTh+aIR21/OhEP+Ti4E/GQ68l9KxAP6hsk8w4bztKwWi9n8xnhDdFYvZx7J7AN2Mu5dwKb2LdSAluuA5ixLyETwCFQWhIIuOjT2Jl3LRJCPdGaUwx1aINm3kEvAfygmXf0E8CniIdcn0XruBTxgNm2jksRjyD61IV4xLalBPAJ4iGjT7NoHTdo5jz0ax03ZOa8Oyhme7FvFpnzQK8EtiEz56FnAlswh4XjY9at47pkzufM2ox9CZkADgBVmUkupkMD0CquQ3PaJ6lDo4BC6m7sTCK1T1KHBrbmVkSHZg6tojo0p30KdGhg9VwRHRoYPZHUobnvEdGhAUb7FNOhoc1nqXrdHE9Uh6Ya+2rD4zcEOjT3mZbPSB0a2IuRAqdDg4pSxXVobil1aFRWsxTRobX9UevQzGsd6NBQRvsU1aFZHwQ6NCCo3yh1aBjtU0yHBu43ai3rD6/t39zn7euZY2jz9j5FVIeG8XWgQ3OnFNGh2Ut1oENzNlxZp6mjOjR3nNShhX4IdWiuhqbUodkfRVyHBlCpqA7N19CUOjRn34gOzb8WH+9tKW1e0faRXW/W0DR/TpBA5fbraA1NpRRVQVBDU9YzpSKooSnrmTo/UEFRTPy6rKEp65ka21fG9rpignm0IGtoBvVMt5+GrKEZ1DMFAu2yMrUzZQ3NoJ4pYm4qnQ2lFjCsZwphDc3YuJC+cvf9bk51fzu4aZ41NPGb5z7HZxzQyARwAChdRwBzRqqdGUXEKWek5oxU/z38z84wmZyR2o4EjpWRWs9X6V7OzhbQjgRK+8+il7OMyo7dy7l3S8UVxkfhbKS156pjID8CzlgNmQAOgdJFtvoJ4FPEA9qkMEU8WsSvMbE2dWgp4hE8Du5APMwk6y5SIfGoCV1H4uE+MLZMbosQD/v5XYmHtH8X4mHsGF70RuvlDPWPpoMAftBeznJbdF934mF+K3QmHmZb48Yo0Gk2/DFkL2fzx+LEA5hlL+feLRUbxEP6pW8vZ/k1x+7lDD0T2Abs5dw3gS1VysqNtzEJYEbGasgEcACoEvsIqI4+jZ15Z9abEcB6Yp1l5p1ZFzMdaQH8oJl3wbYm8RDRpw7EA/r1cg62dRDAD9rLGegjgB+0l3Ng88YSEX3qQDz6Zs7L9bEz52M6tLEy52M6tLEy581XC30zVuZ87wS2ULhobLLGzPkwOgvBEws/Z9F6YlGPn/D+AE3dFnAMaN04gTUcn3FAIxPAAaBKc2fXVwA/ZOYd9BPAD5p5B70E8INm3kH7sVdKAJ8gHlBHn7oQj2BbBwF8injMunVcinjMvHVcgnjI6NMsWscNmTnfu3XcgJnzMvrUJB5B9GkF4iGlEkZoZ5azaB03ZOa884SfpxSjZc73TWCrx5OwT5ND5UfAGfsQMgEcAKbnoqK3AB6M6Nrd968igDfLySACeJgyoegugAebaOHO3y7XIoC3f7OzAN7ay/FnmtvseicBPNo3Z+8igHfFu4cQwBsfVJ0F8MYv5SACeAVQ6s4CePe7df5ajwDeFe9GKUPkZPFu5yPrv6B4t3IEuh4fsULqVGbZLBIti3fLQupunMhC6sbW5sdaKJusYAupm79TRQupa0AV5jcfjAtZvNvZogJfvFsUUg9/7/UY8dEihRgHchzhf8v1b7/era0/VGTMuDGiGsW7nW/Xk8DmxkTXBDbnj0ES2MAkTXVNYKMySVMDJLC5pSfSyh9aj7v6/j4jY8ORCeAQKN11WNFHAO8igdBNAJ8qhQG0dGgpAXyqFIb53Kp7KQz7RRWRSCARHVpwp7y6AD5ZCkOuNyOBjejTaqUw+vZylvYfu5ezoo56dBLAD9jLufnYckV/6NrHfqOPCq6tl3Ng60Yk0Ng8jDgN2csZerZUHLCXc++WimJfOGYa46JDL2cZlR27l7OMyo7dy1lGZZuRwLX2ch6VAOr6nNZ8fMYBjUwAB4Cq6kBMHwH8kJl3QEuHNlrmHdBHAD9o5l1sm7/4qfocOxCPvr2cg20j93I2f8CFK8bt5Rw8tmwSD7nehXhQj4suxEP6ZuzMeWjr0MbKnI/q0EbKnA8eBxdYf9Sf5uUSM8icN65wN63jZs7Xdh0uc35MApgfAWeshkwAB4B5BGzXdZx4zDrzzqy7CTgkHjL6NJPMO6CPAD5F82KXmQAAT9RJREFUPGbeOi5BPGbdOi5FPOT6TFrHJYhHYNdZtY4DosSDiA4tQTzMz8HeGDWIh/TNLDLnZfRp7Mz5vq3jupCR6BML+/lynnJjZCMy543VVXDcWJnzUPRKYHNPLAJbi58AmnGzgCtt/q3n+IwDGpkADgBPABUtHZoCqxOJ6NDsa6lDKzATY0yHBmaikTo0Cih0EdWhmeumjurQwDxuljo0wxet/qyhQ0NjtE9Ch1Z/+YgODWuLiA4NMI/NpQ7N2SamQwNQoQ5NO7P6sFO9zV3b7OkHOjSvD5Q6NPu3nf8CHVoBqlKBDo2qqHViEC1OHNOhGf9UgQ4NTPHumA4NbbRPUofmyVhEh2a+vyKqQ8NqnypLpWXx7pgOzdpL6tD8TzmmQ6NtfyX2yc+oj7NjpKFD85/j7jOUSSZCEdWhOR/EdGh+fDT0gRNdRXVo7rXUoZltOqpDA1bWoYG5WSql8cwyqkNz/nDfuzRzhFLujjHUoZmP0sR0aAhOqSK2r9eND+rxVBdS9/5zvrDHyuLdspA6VRVol6MF1UUhdT9fregPHS2kHvjDFlI3H1drl4NC6mC0y7KQOgTaZVlIXWN/V5FC6mbOqH/U7cLqEVuLeSprADP2JWQCOABUWc8pBPO4EmEQQQLdnbW/w84Zqe6OOWek5oxUhD9yRmokEtuIBM68lzP0aqk4ZC9nOT+N3ctZRmWH6uU8agRQE/d5n+MzDmhkAjgAAgIoBn5APICWDi1BPICWDi1FPKB9gUsJ4FPEwyxCHVqKeNiTixMPaOvQEsSjabvViId8HNxJAJ8gHm5XV+JhbJwgHP4CtzrxaJEQ0sTDXIh7COAH7OXcVwCfJB706+VsQ6zEiIdcn0UvZ3NsqEMbq5dzTIc2Wi9nu27eYcNZeuUEtiF7Ocv5afRezvRLYOvSy3nUTiDUQcy1Hp9xYCMTwAFQCALYSwA/YOYd0EsAP2jmHfQTwA+YeSejT03i0Yo+Nfwxduu4FPGQpHwmreNSxMOvdyMevQXwSTIiok8diEff1nFDZs5DW4c2Wua82RwlHjPPnEfcrGoYM3Pe2F/ZOSkcH7UfZpQ5D70S2LpkzudHwBn7EjIBHAIVtQYQQh2U2x7RoTntU6BDK1hRhwZGMyR1aBSmBldMh0aB1QeaiTNWg0vq0MDU4Irp0MBobqQOzWufYjo0MNqnmA7Nfo9Ah+bs6G1oL0bKGFLWMgtqcLkAlKK+2NrXzqSBDs36pT5OoWxtuhV1aIpAh0ZltE8xHZrzQUyHhtU+SR0aYHWBVbBNap+kDg0qoX8KdWjnPeQ0o32K6dDAhqqlEa32KaZDs3YKdGjWNlEdmjO2M7T9Lfur5Ur7rH+jNTSlDo1aQwsEOjQw2syYDs3bMKbT9GMk5Q/3GXPEdGgop0dz4yJSQ1Pq0JztIzo0V0PTfFQR1tB0drK2lzU0Yzo0b0tpc2dvuc35uXLvdxpA43fQYT1TdFhD055SrIZmYW/JZA1NWc80HB91LVNZQ1PWM3W+MbZ3WtpptIbmxNpD1tAM6pkCgXa5cr/luoZmUM+Ueh6RtRr9/CS0y37+s+/Rvm7gyARQ68gdcM/jMw5oZAI4AIqK1iPgnJEahAGMTXJGqt+XM1Kb21Q7Emg/XyEjgeCisjkjdaxezvWpNiOBlq12LmUlbdillJVZL6KRQGj6YeBeztCrpWKXXs65DEzGvoRi9bfse7jqqqt43OMexxFHHMGxxx7Ls5/9bO66667gPVprLr/8crZv384hhxzCWWedxVe/+tXgPYuLi7zqVa/imGOO4bDDDuNZz3oW3/3ud3ufj6pqEugeBwfrVb3NtI1Tdf/gymxD7NOlMhGpUlGViqoszLJSlGXBtJwwLQumVcG0LFguJyxXE5bKCUvBco6lao5F9680y73VPHureRarefZq+89u26s32aX8N8dePWGvLtirNXt1xSIli3rKol7mb+/5EtXyFKZT9HQKy1O0fU1ZwrT062paoqYVqixhWtn1CjXV9XKqKUq3xKyXmmIKxdRERGr7amFb8a8KfdHcF/qj7QsivtDWF2VZCF9YH1TWJ0lfTGpfiH/OF3F/SB80faHYqyv26toX//t7u6is/fXU+mR5WvtA+ALhC+V8MTU+KKQ/rC+8X6Q/hP2LhC+i21fwhdmurC9U2xdV4f1RloXxiR0LpfXFclX7Ytn5wP+rfbHUwR+LWvpiU8MXc8IXmr26tP5Y5m++tws9XTa+kOOj6Ytp7QvKhk/KyvuimFbe9s4vfgxYX4Tzjxwn3X1RxHxR0fZFaXxRlYUfG01fmLFRz1HLZXNsWB805yo9X89Ves74orK+0GKu8q/n/NhYtL5YFGPD+8L7Yxk9rcdIV18o64NiWtVjYSrGhPWF800R8cWBissvvxylVPBv27Ztfn+Xa3LGuNgvI4A333wzF154IY973OOYTqdcdtllnHvuudx5550cdthhALztbW/jHe94Bx/84Ad5+MMfzhVXXME555zDXXfdxRFHHAHAJZdcwsc//nGuvfZajj76aF796ldzwQUXsGvXLibyTn0V+AmXOvo0duadXB878w76CeCHzLyL6tBSEafoPvuoS0afmhEPYL0C+CF7OZv1UIc2Vi/nmA5ttF7O3ifjZ87L6NPYmfNRHZr3x4wz50FEBcfNnKey7y82IHPersrlejPnD/Qs4Ec96lHcdNNN/rW8jna5JmeMi/2SAP7t3/5t8PoDH/gAxx57LLt27eIpT3kKWmuuvvpqLrvsMp7znOcA8KEPfYitW7dyzTXX8IpXvIIHHniA97///fz5n/85Z599NgAf/vCHOf7447nppps477zzOp/PSlnAwTK6L0E8kI+9Vice5tjwsddYmXfmc3sI4AfMvOstgE8Rj+Cx1/Ct41LEQ67PonVcknggHnvNoHVcing0t61KPKjHRZN4mNPW9qutTjyMzava5qSJhx8PEeJhFjPMnPe2aBOPpu0Gz5yX42LkzPm+CWyDZs5DrwS2Lpnzqqw/e9ZQWodSnDUc3xdzc3NB1M+hyzU5Y3zsl4+Am3jggQcAOOqoowC4++672b17N+eee65/z8LCAk996lO55ZZbANi1axfLy8vBe7Zv387OnTv9e7rCFYIO/ukVtrf22ccrYruZt2zz8spcfLRd15V5FGyWBaV9XWqzXlYFU20fD7t/umBaTZhq+6iymrCsV/s3x7KeY0lPWNJzYtuEZV2wrBXLKJbRLFOxrCuWtRHA67L0/3DLyjxSoawwousKSvM4hco+2qoqVFUZ8mir2Jvt0l66Xpbydf2Pnr5A+AJ/vPOLEr6wvtERX2hlfWEegUlflIEfGr6I2b6q7b0U+EH6YM76oWCJgmUUS1pbP1RMCX1BWdW+sOtUK/vCbKt90PJFqTv4QvfyBcIXWF94v2jpC8trKyV8QeCLSoyPUivrk4gvtBwf9rddFdYPMV9IP8yxzMT80wVLTIwvtPOFGRtTSpb1tOULsxS+KOt/ZhzUS5y9m8vWGHG+aP4LfePmG8TrYF3L96pg3exTDV+pYFzoqgh8UcZ8oSO+8PNT4X0xdb5IzlXGD26cLCHmKY33xbI2vvjk924L56ey8q8DXzgf2PmJcgUfuDmrFGMiMjcd6FnA3/jGN9i+fTs7duzg137t1/jWt74FdLsmZ4yP/TICKKG15tJLL+XMM89k586dAOzevRuArVu3Bu/dunUr3/72t/17Nm3axIMe9KDWe9zxTSwuLrK4uOhf79mzB6j1HeaEVlgm95nHXqsL4E0kcAgBfKoURnhnHUYCYQABfKoUhv370Uig3ScjHb0F8DF/yNuglgB+5VIYMio7di9n6CmAH7CXc18B/JC9nGOF1N3vc9a9nGVUduxezu59tS0KYae27Ybs5RxEZZuRQOpx0YwEysfBa+3lbPZXG9PLWdh8sF7OYz4Cdl97PcdTX+ccFhYWWFhYaL39CU94An/2Z3/Gwx/+cP71X/+VK664gic96Ul89atf7XRNzhgf+z0BvOiii/jyl7/MZz/72dY+JR6vgb3wNbY1kXrPVVddxVve8pb237Ei36gOLSAXqX1N4lFf+7oQD2jr0MbKvKsJ4PiZdzEdmiMeUR1ainjgLn7diEdMhzZWL2f5OHjsXs4xHVpA2mbZy1k+Dm4Qj1lnzsvHwaNnzkufbETmvLO/NfhYmfPQJuejZc7Lkxwocz406mwx1CPg448/Ptj+5je/mcsvv7z1/mc84xl+/dRTT+WMM87g3//7f8+HPvQhnvjEJ5rPXMM1OWN22K8J4Kte9So+9rGP8ZnPfIaHPvShfrvTIOzevZvjjjvOb7/vvvv8Hci2bdtYWlri/vvvD6KA9913H0960pOif++Nb3wjl156qX+9Z88ejj/++Dp7jp4C+BTxiOnQEsQD6CWATxEPaOvQhmwdlyQeiOjTDFrHJYmH3daVeMjoUxcBfIp4eJvTjXjI6FMXAXySeMBMW8eliEcQfepAPILoU5N4yPPpQDykT2bROi5FPGbdOi5VsmfmreP8PIX97Y3XOi5Vsses69ofmJvloRLYUiV7/O97zEfAmrgP+xwP3HPPPRx55JF+cyz6F8Nhhx3Gqaeeyje+8Q2e/exnA+lrcsb4KFZ/y74HrTUXXXQR119/PZ/61KfYsWNHsH/Hjh1s27aNG2+80W9bWlri5ptv9uTu9NNPZ35+PnjPvffey1e+8pUVCeDCwgJHHnlk8A/a+pr16NBI6tCI6NBYWYemEzo0q30KdGhO+xTVoU3aOjRqrU5Lh8bKOrSW9qmqt8V0aCqqQ9Nr0qER8UWwv+ELqdU0+5TfH9OhNbVP69GhTSM6NOefmA7NaZ9iOjSnfdItzVPDF03tk1wmdGjMWIcWHmdvivxYUfW/hg6tiurQil46NO+Xnjq0QC+ru+jQyogOrWRlHVrVU4e20np7LLTGTOtfPTephi+8djnwhRK+MLZvapdX9IX0w6r65TnviyWrnW1plzUt7fKKvvAazOYYkfNTqF1uz02hvfc3NK95XQng4uIi//RP/8Rxxx3X6ZqcMT72ywjghRdeyDXXXMP/+l//iyOOOMLrC7Zs2cIhhxyCUopLLrmEK6+8kpNPPpmTTz6ZK6+8kkMPPZQXvOAF/r0vf/nLefWrX83RRx/NUUcdxWte8xpOPfVUnxXcFa4GV85IzRmpOSM1tF3OSDXYFzNSu/Zyjj65lHOT7l7KSkZlo4+A19lScdBezoio7EC9nN1njwKtI47reXwPvOY1r+GZz3wmJ5xwAvfddx9XXHEFe/bs4SUveUmna3LG+NgvCeC73/1uAM4666xg+wc+8AFe+tKXAvC6172On/3sZ/zWb/0W999/P094whO44YYbgnpDf/RHf8Tc3BzPfe5z+dnPfsbTn/50PvjBD/aqAQj44p+9BfAJ4hHToaWIR18BfIp4mP1FZ+IB/QTwSeIB/jFLF+LRtKskHjEdWpJ4yPUuxAPoI4AfspezfPw4ei9n6CWAH7SXc0yHpsfp5QxtHdpovZyhpUMbrZdzbJvfZ+aprqWs+vZyBnolsA3ayxn6JbB16eU8qgaQUTuBfPe73+X5z38+3//+93nwgx/ME5/4RD73uc9x4oknAt2uyRnjYr8kgLrDIFJKcfnll0fFqg6bN2/mj//4j/njP/7jdZ2PqwDfVwA/ZOZdXwH8kJl3cn3szLvArl0E8AniIaNPnYhHTIdmiUcQfepAPGK+GbJ1XIp4BNGnGbSOSxIP94GxZXJbhHjI6FMH4iHtP4vWcYNmzoO4IRw5c15ui+5rzFMMlzlvtjVujMbKnDd/zP6ehsmcHzUCODKuvfba5P4u1+SMcbFfEsB9DV7nIRqCQ90AHKV9A3LZqL1uwC62gWN0ZuJRlmwoezFTGtO4XNv3K/NG+3laGTqjVUVVGG1OpWxMUSmoCgplWM600ma9ol6imdhbv6IyE+dyVdm7V9eAHSa+KfucuYNXpkE72n6Whgmi+buqPBH45PduM5FAZSfOwi6Votk1XVXKXLJUZexFUd+aKu1u3oVd7cVIYVqJOXviPlqbFXetU+KxurC/EvvkZ9THGV+oSlmbq9CPlXufmf6NTwtKZX8nReH1QIXSTCvjk8IvJ9bmBYUJoXm/THRF4e1fNfxRMfG2dtsM8ZmgzbqqKLQxnPeF/62Zi68CjMYpMB4oZXxBYX/vFZRF7Q/3vUtQKDEmnEu1/yyltPhcYV/BKVXE9vW68YGzOZV9BOm+hzK3S36MYIiC+fjCfrXKkLiqQlmioGLjQunaD/YuwRGYuD/s7545PwYmYmn8oOsxY/Wyz3jI6ahC+d+yGR8Kn0Dl6k2VCmXHuVmvLLcyvyuNuTH1drK2V+K3L33jXdy0tZ2LWtta+5ztjd9B23stZceVmK/EKSmlqAooK2XHbmHtZX5fjjD5eYSJHx9Fww+FvROcFNr7wx+nKwp7uTN+mDLBPFoo7NhAV0ysPZwvzMHu92THhSrr323lfssKTWXmHDs+ULr+HZb2mjAm/xv5EXDG/odMAAeA78vpox4qZ6TmjNSckeptnjNS98mM1EgkcIiWivU8VZ9qqqViqpSV+QwV2DDVUjFVykpGZbuUsurdUjFRyspFZVX0hzwbrDfpZH9MWMnoh0wAB0BRadTUTcrh4xY30cZ0aCniEdWhJYiH2xUlHrR1aCniAXJiXZ14tEgIzcdfoQ4tSTygrUNLEQ/oJYBPEY/CrnYmHvQTwA/Zyxn6CeCH7OWsgD4C+BTxcL7vTDxi2yzxiOrQEsQD2jq0FPEAerVUHLSXM/RrqThkL2d3UMz2Yt8sejkDvRLYhuzlDD0T2II5LBwf/nGwzqwqY99BJoADQE01xZwOok9N4jHrzLu+AvghM+9k9Gn0zDu/3iYeMR1aknh4G0eW0W0i+tQkHsjo0+rEwxwbRqFSxANo6dBG6+Vsv2iUeBDRoSWIR+/MebneJB5B9Gn4zHlp/7Ez54PoU5N4IKJPM8icd9uILRvbnI/9xnVmzge2drYfKXMeeiawdcicHzMCmB8BZ6yGTAAHgCorQwKhnwC+d6TD/8U28ZDRpwbxmHXmXeyx12iZd/bvR4mH3deVeAQ27kA8zLYViAci+tSBeDhbQDfiAbQee6UE8CniERXAD9g6LkU8Zt06bsjM+WDbyJnz5g84geW4mfNuXpOylbEy56Vvxs6ch54JbOKcV0pgU2NGADXhuFnL8RkHNDIBHAC+AKubn7sK4O0+P1PbedoFsxTxfX7HOgTwYJJCugrgTRBRDyKARzkhfGUvQKsI4LFfcggBPNoc00cAn9pnD+4igKewBXLtKa0mgKeCqRKJNOsQwDv/dBbAbz/NXO2HEMDb91gW6f2ilLZJJqIdlH2PVjKJisZvP1y6AHCdFGUJq1KGPFibK+sXZb+ntslUflyomjSXCigqkwhlbTm1yVM+UUck5zj7F8XEjxUTjNa1j8T4mFCZcWAym8z7dUWBuUkwY6Q0HKosQVnS5zKSHdFRCqo6E0qBTZayHL60fivt1bxEjANrQGtz4z/tf7fmM/snsCnrp84JbAWDJbA5v3RNYAOYqGqYBDaMvVMJbKNGADMyVkEmgANATTVqzt7q+4hTGAmM6dBSpTCCR8ArRkFUHQn00Q4diQRi72K7lcKQ27qUwjDrzTtr5SMCTR1ashRGTIeWKIVh1uton4wEysfBzUhgZwF8EO1rLINt1tI++lGf6ix6OQfbOgjgB+3lDPQRwA/ayzmweWMJ9ePgRiQweBzsx0W/Xs5yfexezr1bKgYRQPeYwN1k9Ovl3DuBTQzH9fZyNl8t9M1YvZx7J7CF+gFjk2YC24iZFUP1As44cJEJ4ABwj4B96C5CPOTj4Flk3vUVwA+ZeQf9BPCDZt5BLwF8inj07uUM/mI3di/nYFsHAfyQvZyVPMmReznH/DBW5jyI3/7ImfMxHdpYmfPycbA8zgdlxVgJbly9j9z8Zj+jT+Y8BX0S2IbMnHee8POUYt2Z8+HkMWNkDWDGKsgEcAAYAmjjfDb6NHrmXeNiN2bmnVwfPfMOEX3qIoBPEA8ZfZpJ67gE8TCnre1XW514GJuHF7iUAD5FPGT0aSat4xLEo2m7wVvHJYiH29WVeBgbJ8aFvzFanXi0SAhp4iGjT2NnzvdNYItyHDk39cich8pH9v2N0UiZ8+bYHglsWuj9VkhgU6p+kjJzmFNe3/EZBzQyARwArjG7CbA1dGgo93wDCHVo/rWdb6TGxs9B7lj5WqzXWpyIDs3+7agODaN9kjo0MNqnmA6NAopKI3VoFFBoHdWhudcxHZrhXFWgQ8MS05gOzUyoZahDA6hKYjo0ba6qRHVoaPOUzXxQULw7qkOT/vAsI+IHu8/8vfpvBzo064NQhwZaxXVoYLRPUocGmKLEER0aQFEVUR0aWL2m/QKyeHdMh2b+ThXq0MBqRSM6NPc6okPzxbuFDs0fFtOhuU3evvU4iunQtHi/X4+MGa8PtMW7Yzo0AK1CHZobCzEdmh8XER2a84vUobkxENOhmUOrQIcGRncb1aHZ31pUh2YNHOjQ3JiWhdSpf9Na6gPd+BB2VNZHsni3G1uO+Ejtsl93EGPGnLcZF0677Pi68635jHYhdTDFu2UhdapwKQupU1nNsSikDiv5w+5DEyuk7op3y0Lq5z3ktFC7LAupA4FudtRK0BkZaWQCOATK0kYA8XerOSNVieiSIDXiThtyRqqLBOaM1Np2OSNVjCMbCZRR2bF7OfdtqeiD9OFpuLPxBu9SyiqIyjYigfJrzqKXs3FFYf0wVC/n8SKAWQOYsRoyARwAalrBxD5o6CGATxGPqA4tRTxwF7828Yjp0FLEA8TE2oF4mHU3AYfEw1/MOhIPaOvQksQD6COATxGPvr2cpf07CeAH7OVsbB1e9Mbq5dxbAD9kL2d/s9QmHvJxcCfiQb9ezubnYG+MGsRD+mYWvZzNCNFR4mG+7+x6OfdNYFttnvLjQXcpZYX97Y3fy9lYXQXHrbuXczHyI+B1aQAHO5OMfRSZAA4BGwE0E6ubzULiMfPMO/F5Y2feQT8B/KCZd9BPAJ8gHmF0dnXiEUSfYsTDX+jMiyTxkOczg9ZxKeIho0+zaB2XJB4i+tSFePRuHZciHiL6NIvWcUNmzpt1XfsDQTxmnDlf/743IHMe6JPANmTmvL9pZcDM+aIm+RkZG41MAIdAWUFZRXVoCqDUXg8Tq8EldWighAYwXoPLMz65TdHSobkaXFEdGu6EaNfgiujQnPZJ6tBwepuIDo3C1OCK6dDAaJ+kDg1N/fkNHZq/+AgdmonWFER1aGC0T84WlY3hSe2T0KFp5xdrc6lD8x/dqBuoFXEdmt2Hoq1Ds36ROjSEHygIdGhO+yR1aBRG+xTToZmlrBEY+iVes7GK6tDMe3SgQ0MZ7VNUh4a5+MZ0aG4Z6NCooBQ+cd9b1tBUxnCyhmZMh+aWnjAof2j4Hr9ufBDTofnjrW/dsZX7fTR0aK6G5oq1M+X48H7RUR2a8YcOdGihH0Idmvm4Mq5DA1NDU+rQnF/cJBKpoVmPEfu/Ss5PzpX16+a+YFlFttnfeb1PaJcdz3N+V86XtZTFcTOlnLSmitbQdIRY1tBs6zTtvog/CnRQz9T5wdXQlPVMWzU0HQmUNTQdMR21ELReZwRwHcdm7BfIBHAITKc+tJ8zUu2+nJFaR0IcO3RhJRQ5I1WwspyRGvHH7DNSkUsfba4fB6+5peIKc9KK21KlrOy6eYdjiNZW1I+DZ9HLWc5Pg/VyHvMRsLwBXevxGQc0MgEcAmVlSCDQRwCfIh4xHVqSeMj1JvGwF9KuxEM+Du5CPIBeAvgU8UA8Du5EPKCfAD5BPOTj4C7EoyYrbeJRhKfR8sd6eznLrzl2L2cU/QTwKeLh17sRj94tFZNkRNHSoSWIR9+WikP2coa2Dm20Xs5mc+PGaKRezoibVQ1j9nI29ld2TgrHR+2Hfr2cKTKryth3kAngEBARwD4C+BTx6Jt5J6NPY2feQSQaNVLmXRB96iCATxEPGX3qRDzs/ugyuS9CPGT0qQPxAHoJ4Ifs5SyjT2P3co7p0BzxCGzcgXiYbd2Jh4w+jZ05D20d2liZ830T2IbMnO+bwDZk5ry/GXXjQYwPWFvmvJ5rTc4zQ84CzlgNmQAOAD2d4sW9fQTwA2beBcRvxUm3K/Ew/5tV67hBM+96CuBTxENGn7oQj94C+CTxqE91Fq3jUsTDrBediYeMPnUSwKeIh/ki9gvNoHVcinggok8diEff1nFDZs7Lm6SxM+f7JrC5JxZuTpJPLKT9Z9E6bsjMeWNz1bhRlf4oal/RMXM+awAz9iFkAjgEyhJdlv0F8BA8w11NAG8+SjOIAN6dXkcBvJnshxHAw0oJCXEBvFsOIYBXmESXrgJ4aBbrtpZW9BfAt/atIoBHJOyIU1qLAJ6CRoKIi5jEBfDu9SACeOcD5X9Q9usrtLJKMjs+guLdspC6t5MlBbKQurVNkCjlbO1D7BE/2N95nRRVF+/2JFCBQvtx0SykXmDGhyykTlEFxbtlIXVj+yIopE5FULxbFlI3n9EYK4qgeLcspG78Iop3i0Lq5ntXBIXU7fdVWFsMkcAG+CQbGn4R40KLfW5c1L8Z7f5ovwS2AlSlOieweZsPkMCGNu9NJbCFdyQZGRuLTAAHgJ5OQU3tXWp9Zw2kBfCJUhgxHVqqFIZ8HNxJAO8igf4OW9eRQGjp0FKlMOR6FwF8qhSGjHR0KYUB/QTwqVIYMirbpRRGEJXtEnGK7qvJRp9ezjIqO3YvZ7Me6tDG6uUso7Kj93L2Phm/l7OMyo7dy7lvApuL+g3Syxmoo+Xj9nKWUdmhejmf/dBTGQ05ApixCjIBHAB6uUSrqb2L7iGATxCP3pl39n3RZXRfgnjY9a7EwxyrguVYmXfmc3sI4FPEg369nHsL4FPEwxL+rsSjrwB+yF7OtR82oJcztHRoo2XOU4+LJvEwp20jPzPInPfjIUI8EI+DZ5I5721RCDvFSdvgmfNyXIycOd83ga1L5vz1/3Q7D3o44yATwIxVkAngANDTZbSaB0T0aeTMO/P5/uPC15F9Q2beQR19GjvzrrcAPkU8YLat4xLEY9at41LEw+wvOhMP6CeATxIP65uuxKNpVzk+Zp45Tz0umsRDRp9mkTkvo0+jZ85DrwS2QTPn/U3u+Jnz0C+BrUvm/KKca2YNKX1Y6/EZBzQyARwAH/36l3neKY+L69DACF0iOjSnfTIBNqND02C0TwpwF2F0oH1qF2Yl1Da5pZsAIvuc9inQoTkSaP+21KF57ZM9JaUUVQFlpaI6NCqYSt2N0KFRQKF1oEOjgkmhozo0sA3b0TgdmuEpVVyHtv00c0WI6dAAqpJAh4Yp3h3Vodn3WBbp/aKUjurQjP1UXIfmXks/2H0xHZrXPilDP7TzkSKqQ3OkOaZDA4wOSujQqEyR6JgOzQSjdaBDA6HTVKEOzRXvjurQAKUKAh2a++HGdGh2KXVoAJREdWhgi3dHdGjuPVKHVv/2xTb3cXbMSB2a80tUh1awog7NjQWpQ6MwxbtjOjTnF6lDM9uqqA4NTPHumA4NjGZW6tB88W5RSN3NV8a+iqCQOoXRLlfQKqSO/N0Kv7jxIchHULzbbnNjy9/gqNqdun5ba+4KincL/0rfGv/VhdRd8W5ZSN2NB+cDWUjd+yFSSB2cP6pgm9Quy0Lq2DnKFVJfzlG1jH0ImQAOgEW9nDNSc0ZqEAnMGak5I3Wfz0iFbr2cpU82opezs781+Fi9nKEdLV9vL+dlxkMuA5OxGjIBHACLuqxJYB8BfIJ4xHRoKeLRWwCfIh4uEtiReGC/J7SJh1zvQjygrUNLEY++Avgk8cBdNOhEPPoK4Ifs5azMCUSJh/lqISkcspcz4nHw6L2c7XiIEQ95k9SFeASPgzsQDxPuXIF4yPOZQS9ns62IEg+/zsrEA8Waezlr6JXANmgvZwnvj3F6OQd+cDeviQS2Lr2cl/WInUCyBjBjFWQCOAAWdcVeO7A3KvNORp/Gzrwzp63tV2tHo2aZeSejT10E8EniAW0d2oCt41LEQ0afuhAPtytKPGjr0FLEI/DRDFrHJYkHItLQhXh4n7SJx8wz52nr0MbKnIe2Dm2szHkzdzVujDy5rfxN0kwy52PbRsqcB3olsHXJnJ+OWQcwI2MVZAI4APZqzaKOP/YaK/OutwA+RTxk9GkGreOGzLyT0ad196yFtgA+QTxij73W2rO2FX1q+GO9AvgU8Qh8M4PWcUniATNtHTdo5ryMPjWJh13vSjzMsWEUasjWcYNmztsvqmD8zHm5bveNlTkv7T9U5vyoBLDSeL3mWo/POKCRCeAAmGrFkl6DAN697iiA94cNIIB382ZXAbyyoushBPAUUEghvGWjKwngwSaBDCGAx36/rgJ4+z0GEcDj7CsuUrCyAN6+321YjwAeTPHurgJ4Klt8ewABvCveXdgoeVC82/32bcJOULxbSSNWIgGqIiik7uwqC6mjhA9q+wf7/FhB3A2x8j7r37CQuvOfKKTuyIh9uyykDqY4sSykTkFQvDtaqNiPkZQ/3GfUxbtlIXUzN4WF1FvFu4dIYHP2tUx8tQQ2b0tpc2dvuc35uXLvjySwBXPe6glsYMbHEAls7nUqgW06ZhZwfgScsQoyARwAe/WERW2IRy8BfKIUhnufWTq2FT5awS7tZaWXAD5VCsPduUcjgeAfszjO01cAnyqFYfZXnUthyPUuAvhkKQxh8y6lMAK7jt3LWURlm5HA4HFwIxKI+Py19nJuRWIbkY5Z9nI2/nKMOIwE1nadUS9nv67akUD7+Yp2JHCIXs7Gjs43KX+oIBIoj1tzL2eofzTNSKD9+9FIoN3XelLRp5ez3Bbd15inBPnuksCWKmVltoXz03p7OZf5EXDGPoRMAAfAop5jrwnb0UsAnyIe0NahJYhH+LilQTz8ZNuDePgLnXmRJB7yfEbOvPPrtImHv9B1JR7yJLsQD3t1ixGPmfdytp+7Eb2czXoRJR7G6io4bshezrX9aREP+Ti4C/HonTkfbGsSj/pUZ5E5H2wbO3Me6JPANmjmfGDzxhLqx8H+dyJIoKYxLvplzsv1oTLnyzGTQNCxu8l+x2cc0MgEcADs9QSwpwB+wMy7vgL4JPGQ6EI8oJcAfsjMOxl96iKATxIPYJat45LEQ653IB59BfAp4mE+oxHpSBAP6CeATxGPWbeOGzRzHjypGjtzPtg2cua8kic5cuZ8zA9jZc6D+O0PlDlf5SzgjH0ImQAOgCU9YUmrqA4NZbRPUR0a5q4/0KGB0RPFdGhY7ZPQoQFG+xTToaHNZ6l63Xw+fukJg6olTlEdGspon6QOzX2m+xyF16GBvRgpaOrQnPappXVSOqpDM691oHuCpi6w1qGZ1zqqQzMfV4Y6NIz9ojo05xepQzO3+1EdmhMaRXVo9nVzH8r+88yRVfZZX1CflnZ+9/5r6NDsd5Q6NHOR1lEdmllOIjpNHdWhueNiOjRXvFvq0Fzx7qgODTs+pA7N/KCiOjRfvDumQ7N/U+rQQIvi3oQ6NGlz8Vv22+x6raU1vjB/TpBA5wP303E6NEzx7pgOzRXvljo0CoxeNqJDo4KimER1aMYHVTBWDOeqgkLqNLTLspC6IUxlvJC6NZwspK79+ABK7eeRoHi3/506H2n/u3X+cn/Pb2uMCy32+ULq7jfj5kAlCqmjxbhQ3n+Bdlk5Al2Pj1ghdT9PNYpES+2yLKTuxonTxlY6X3Iz9h3kX+MAWNTzJgIY0aHljNS2Di1npJIzUm0k0Hxuzki1X6v+MyNkpHbt5ayo56pWJBDaLRUH7OUcRGVXGRdBJBBYby/nwNaNSKCxeShV6dLLuapGjABWJhCxvuMzDmRkAjgA9joCiLzorS6ATxIPaOnQUsRDPg7uJIBPkovIthTxsJ8fIx4xHVqKeAAtHVqKeCAeB3cRwCeJB/Tr5Wz/fpR42H1diUdg4w7Ew2xbgXhA/dirA/FwtoBuxANo6dBG6+UMtHRoY/Vyjm3Tjmep+hw7EI++vZyDbSP3cjZ/wD1uGLeXs3wcPHYvZ+mboXo5azEWZg5d+bG05uMzDmhkAjgATBKInSg84QiJh4w+zSLzTkafmsRj9pl39amOnXln1oso8YC2Di1JPMwf6048oJcAPkU8Zt06LkU85NecReu4FPGQ0adZtI5LEY+Zt44bMHPe2DqMQo2VOd87gW3IzHk/Z7kbXGNH6dPoEwt/kzte67gumfO6GpMA6phh+h2fcUAjE8ABsKznWNY6qkNzy5gOzWmfAh0aGO1TRIfmtE9Sh+ZqcMV0aNDUOtkJ1b+mvnLKZUyH1tonanDZ7YEODVEzUJyS1D5JHRrYuoERHRoFFNUEqUMDorpAWYMrpkMzPKUKdGgoVtahOR9IHZrzVUyHhrFDTIfmtU9Sh2ZtE9WhuatczA+Klg5NeadGdGj2tdShFRgCENOhOVtKHZqxfRHVoZnrpo7q0MBEfaUOzfjB6s8aOjQ0poam0KGZ710R1aFhbRHRoQEgNbKihmZUhwZ4rSsNv0R0aP4eKKJD8/pAqUNzvwnrv1gNTalDczU0Yzo0b/OIDs34pwp0aGYszJkIvCJaQ7P+O26eUkE9UzM/iRqa/jdso7ZSu1xZKi1raIp6pv4z3DziNdD1e2jMWU5X2LS/EvvkZ9TH2TGSqqEJQQ1NWc+UIqyhKeuZuvnJj4+GPnCiay2mlk8UMjI2GJkADoC9ep69diLIGanY74H/Hjkj1X1ZGenIGanm/HNG6kZlpMYigbFSVr1bKg7Yy1lGZZuRwOBxsNjn4eyv3QtVR2WbkUB5Pmtsqdill7PWI0YAswYwYxVkAjgAlqp58whYTKxdBPBJ4gFtHVqKeNBPAJ8kHu4g6EY8Ghe71QTwKeIBtHRoKeIh17sI4FPEA3r2coZ+AvgE8ZCPg7sQj+a2gHgALR1agniY09b2q61OPIzNwwvcWL2cnY2jxAPaOrQBeznLx8Fj93I2Nk6MC39jFCceTZv36eWMhl4JbAP2cu6bwBblO3JuaiSwpUpZ2RCr9Ye15Xp7OVfLkROcEfIj4IxVkAngANirJ2yuHPFzJCQkHjDjzDvoJYAfMvOurwB+yMw7s7+IEg8ZfepCPKBnL2frmyjx8LboRjx693KW603iIQn5DHo5S5+M3ssZ2jo0caGaZea8jD41iUcr+tTwx3oz5+XXHDtzHkW/BDZd71tv5nzvBLbUPjtPdc2c75vA1ilzvponI2NfQSaAA8BEAAmiT2Nn3rn3mWVIPJqEbjXiETye6UA8guhTk3hQR5+6EA/zNcz6LFrHpYiHjD7NonVcinjMvHVcgngE0acOxCPmm5QAPkU8jCsK64fhW8cNmjlv90eXyX0R4iGjTx2IB9T2Hztz3pNye3ZjZs73TmCL+aOQ2xrzFMO1juuUOV+NeMnVxCaHfsdnHNDIBHAALFcTlivVWwCPNkVDOwvgwSiWBxDAgzai644C+LoBu9hmP86efncBfEEvATwFTCs9iAAeYKKqzgJ4X7x7CAE81lbehtYvirgAHo0rtA2sSwDvi3d3FMC74t1DCODB+aMKtk2omHhbu22G+NSJU6aQOrByIXUwN0tlYDyC4t2ykLrzh/veJShkcWKCQurmo7T4XGFfwSnVCvv8jqB4d11IvfZfnbwD1Ik6xAupU4CKjQs5PuxdgizeHRSHhqB49yAJbM72HRPYNJikKWcna/s1J7Cl9tmDfQKbI4H2b68ngY3KFO9OJbAxZhJIfgScsQoyARwAi3qeRTu59BHAp0phxHRoyVIYQB8BfB0dVJ0E8MlSGOLzugjgXSSwqwA+VQrD25x2JNDYXEUjgXI9JYBPlsKAfgL4AXs5B1FZsc/D2d8afMheztInY/dy7ttScchezr1bKkb9sbZezjIqO3YvZ7Oua38gIoEz7uUso7Kj93LGkcB2JBDW2Mt5zAhgRsYqyL/GAbBUzrGoLfEz4b3xM++glwB+yMw7+Ti4STxAMcvMOxCTbQcBfIp4OE90Jh7QTwA/YC/nwq5GiYdEF+IBvXo5y/WxezlDRIemxWP3GWbOO99vROY8tHVoY2XOm3X5KHi8zPlwfho5c977ZLjMeVWNGAGs3CP89RyfcSAjE8ABsFhN2FQWvQXwg2beQS8B/JCZd63ok3+/nVRnmHkH9BLAp4iHOXaGreMSxGPmreNSxMOudyUe5tgwCpUSwKeIh7wxmknruAEz53u3jksRjyD6NHzruCEz52s/bEDmPPRKYNPSH6uMC+djv3GdCWydMudH1QDqyMTc8/iMAxqZAA6AZV2wrIuoDg2s1imiQzOTUBXo0MBobqI6NLB6LqFDA6N9iunQsNqUiA4NMNonoUMDZYtK27ebGTzQPgU6NOycqep1QTkDLU6gQ3OfafmM16Epojo0MNonqUPDap1iOjQqq12K6NDa/rD7sFpMQh2a0z5JHdp5DznNaJ9iOjQgKBwtdWgmRBzq0JxdIzo0c3ht/2CfM7Tlkf6ql9ynCHVozn8RHZo9XurQwGgzYzo0b8OIDs34RQc6NOePmA7N+UDq0FBuW1uHZn8UcR0aQKUCHZor3k1Mh+bsK3VoaKEjlL6pf7+BzavGa7Fea2kjOjT72VKHRqFM8W5lP1bo0MCMj5gOzYwJjdShNf3gCnlL7bIspG5s77S0YSF1LDENCqlvPw2pXQ4KqQNUJUEhdapAuxwUUrfvwc9BxoCyeLcspA6hdnm1ceGC8W5OlYXUtZ2ncOPB+cj6T6uwkLonzXZ8yELq5qdZz1NB1YaMjA1GJoADYFnPsegifzkjtQHlQ3w5I1XsyxmpZkMwHlQQCYSckTrTjFSxTPVyhqYfxuvl3Lel4pC9nPu2VOzSy1nlCGDGPoRMAAfAUjnHvCd8oQ4tJYBPEQ9/oetKPKAesF0E8AniEdOhpYhHQPxWnHS7Eg8s6e1GPOS2LgL4FPEwVlfBcUni0VMAP2Qv594C+CTxqE+1C/GAfgL4IXs5134Yv5dzTIcm/THLXs4xHdpYvZzlXDV2L+e+CWxOu+zmpArW3svZ36zW48L5Za29nFU5pgZQE/4I13J8xoGMTAAHwFI1Yd4O7D4C+EEz74A+AvhBM+/kenJfB+IBNHVoQ7aOSxEPGX2aSeu4BPGYeeu4FPGQ0acOxAP7PaFNPOR6F+IB/TLn+7aOGzJzvm/ruCEz52X0aezMeTlXjZ45b8dDYNeRMufN826zHCpz3j0eHgNaV8iC7ms5PuPARiaAA2BaFUx1EdWhmdc6qkMDUSOQWjNS659CHZr5uDLUoWH1NDEdGlhtjSVUkRpcUofmhEZRHZp93dyHIq63WXGf0D45nmf/tlLEdWj2O0odmrlI66gOzSwnUR2a8UHbHwU6qkNz2iepQ0Mb7VNUhwZW7+iNZ5ZS+yR0aJrC6I9iOjRvJ0sKSkuPg/po9rimzZs6NLHP6dB8rUZlNU4IHRpW+6TM99K2nmOlVFSHRlFRVHEdmhsTUodGBUUxierQjA8a9eqU0Q7GdGjGL2VUh2a+d0WgQ7OGi+nQFECpQx2auRPxY0Tq0Nzv1vkrVkNTjgu/TdHSobkamoEODS3GhfL+C2poRnRoroam1KFRmWWzVqPULruxImtoynqmxtZ2LnKfb+uZmr/j5ill6plCUEMzqGfqfANGu+xsUdkYntLIeqbh770eI9Eamo15yv2W699+vdvrmJtjRowR1aih6ea1WA1NZceFrKHp6plWIxLAjIzVkAngAFiuCpZcBDBnpIbL6L6abOSMVPc9ckZqzkhV9cYxMlJpRwIRj4PX2lJx0F7O3hbtSGDTdoP3cpbjYqBezqOWgdGadT3GzRrAAx6ZAA6A5XLCkh3YfQTwKeIBbR1aknhAPwF8iniYzZ2Jh/l8/3Hh68i+JPGA+rFXB+LhbAFt4iHt34V4IB4HdyEevQXwA/Zy7i2ATxCPvr2c+wrgh+zlDG0d2li9nJt2leNj5r2cqcdFk3jAbHs5Ix4Hj97LGXolsA3ay9nf5A7Yy3lMDaDWhAN/LcdnHMjIBHAALFcT5lwEkMbEOlbmnfljceIBzDLzrrcAPkE8+mbeya/ZRQCfIh4y+tSFeMjo09i9nPsK4Ifs5dxXAD9kL2e5PnbmfGDXsTPnRfSpSTyC6NNKxEPYPuabVOZ8i/g1boxmmTlv/OVuMu13HCtz3q+beWqIzPkxNYAZGashE8ABMC0nLFeNR8AjZ971FsAniMfMW8eliIeMPnUgHuarhaQwJYBPEQ8ZfZpJ67gU8ZA+mUHruFWJh7/QmRdDto5LEQ9ok/MhW8cNmjkvok9N4jHzzHkRfRo7c96sNyOA9Y3RLDPna/vjf2OjZc4H25rzVH2qfTLny7E7gYjrT2/kJJADHpkADoASRbkGAby5burOAnjDF63wfb0CeMz47iyAx2wcQgDv59DKXsBWE8DjToh1C+C9zTsK4I0P5gYRwGtn064CeDDLIQTwtO2v7D4UbQG89csQAnjng6kv2u2WE2/zZmLCRFeRhKkqWkjdvMclTtmlCot3B4XUwSRNOT+U0nhmGRRSp4JS+MR9b1m8WxnDKcewZfFuFfrIjwXhm6bt63Xjg1ghdX+8HRfuWFm8WxZSd8W7W+MhliDi/dI/gS3wR5cENjBJUwMksJk5o/7tr5rA5pZVZJv9ndf7Vklgk/OVOKUgga2SDp4x8iPgjFWQCeAAmJYFyzaK1EcAnyqF4UlNJBI4iABei2SFLgJ4H7lYvwA+WQoDaOnQROTJRUnXKoBPlcKQUdkupTBkVHbsXs5N243Zy9ntUuJ8xurl3IoKUkcCoa1DG7SXs/dJOxI4817OtHVoY/Vyhn4JbEP2cjZzVzg/1dHm+nHwTHo5x7alEtjEPLViAltZkJGxryATwAEwLSdM7MDuI4AfMvMOegrgB8y86y2ATxEP+ZilA/GAfgL4FPHo28vZE3Y6CuBTxAN69XKWj4ObxGPWvZz7CuCH7OUsSfnomfN+vU08Zp45j6KlQ9N+T0uH1iIerD1zHsTvfOzMeftFFYyfOS/X7b71Zs7rESOAuqrQ63gEnOsAHvjIBHAAlKViapNA+gjgh8y8k9GnsTPv+grgh8y8C6JPDeIhv2YX4gFtHdqQreOSxANm2zqud6TD7YgQDxl9ahCPmA4tRTyAlg5tyNZxg2bO278fJR52X1fiEdi4A/Ew21YgHojo0wrEw3yd+g/MsnXcoJnzQJ8EtkEz52Pb/M2rqs+xT+b8mBHA/Ag4YxVkAjgAKq2oKhXVoVHAtNJRHRoFVh9oJs7lqrLbqqgODYz2SerQ0EZvE9Ohee1TTIcGVmNjJ99KmdiRquI6NDC8xnIJrw9UxHVoaLPirnVK3B3b16h63V9qVLgMtE9Cw6Pt34np0KgKKx/SNHVozgdSh+b9ENGhgfNHFWybUEV1aFAJ/VOoQ/PaJ6lDA6iqqA7NspxQh+b84b631KFZ20R1aM7Y0r6CU6qm7T3btxuU9jaP6tAcGbGfIXVobkxIHRoFqNi4kDpNP0Zqf8R0aAAFc8R0aCinR3O+qot3R3VozvZCh6YwetOYDk2DKd7t7GRtL4t3twurN2ztls7eK+2zB3sdmiOB9m9LHZov3m1PSRbvloXUIdQuy0LqbnzECqlTmeLdspC62SfGBxpXSB1lxokspI4i0C4HhdTB+CBWSB1TvDsopE79O5YaWbxtNEEhdW9fq69c8bcfLn3xbmVveuzfDrTLbjw4Hyl8IfUxI4AZGashE8ABoEvlI3g5I7URCfTRj3YkUD4Ozhmp4iRzRqrflzNSY9uspZ1fxKnOopdzsE2t7I+Z9HIG+rRUHLSXc2DzxhLqx8GNSGCqpaK/0RsDla5v3teCHAE84JEJ4ACoyoLK9MPqJYBPEQ+zrXmhW5l4QE8BfIJ4mAthQ4eWIB59BfBJ4iHRhXhALwF8ingEfuhAPOTjrrF7OfvQXYR4xHRoSeIh1zsQj74C+CF7OUP78fxovZyhrUMbq5czeFI1di/nYFuHBLYhezkreZIj93KO+WHdvZzHrAPo58H1HJ9xICMTwAGgK0VVhtGnsTPvzLE9BPADZt71FsCvdqFrkZEE8UBe7ELiIaNPXYgH0NKhDdk6Lkk8YKat45LEwx0Us73Yt1YBfIp4ADNtHTdo5jwi+jRy5nxzW0A8gDoqOHzmvLF5eIM6Vua8s7H57Y+bOV9BvwQ2dxeKYqUENkZNAtFWNrPG4zMBPOCRCeAA0JXR28R0aGBqcMV0aG4pdWhUVrMU0aGB1dY0dFC+NiChDs3V4Irq0LB6GqlDg1B3E6nBFejQwMx7ER2aOVwT1aHZ47A80s/ObhuxfYpQh2aveTEdmj0+pkOzl+pAh+ZtuKJOUwc6NOePmA7N+SCmQ3PaJ6lDsz+KuA4NzAVD6NCMNnMFHZqzb0SH5l+Lj/e2lDZvap/Euq7c+yM6NPu3ozo0NJVSgQ4NzPiI6dAooKg0UofW9IPUobnXMR2a4VxVoEPDEtOYDs0QpjLUoQFUpb94Sx2aq6EZ1aGZOxFrQzk+VtChSX8kxoXXoUFdq9E6tVVD0/nI+k8rXY+LSA1N16lC1tCU9UypCGpoynqm5p6wHieFHB9KzF3O740amhNKw2FFDc2gnqkcF6qqx76zRQVeu1za+UnU0JT1TCGsoVlrAOtxJMeI++1r8X6/HhkzXh9otcteM5g1gBn7EA56Aviud72LP/zDP+Tee+/lUY96FFdffTW/+Iu/2O9DKoUuFTEdWs5IdVGoSCTQvi9Yxrb5fYqckZozUnNGar0snI3WkpFKOxIobb3WlopD9nI2f8BaceRezjJ6OFQv52pMDaA28876ju+PQa6pGaPgoCaA1113HZdccgnvete7ePKTn8x73vMenvGMZ3DnnXdywgkndP4cXZo7Oxfm7yqATxEPaOvQUsQD6gm7kwA+RTzMyfnlasRDPg5uEo+YDi1JPOS2TsSjPtUuAvgU8ZA27EI8zHoRJR7Q1qEN2ssZegngB+3lDPXj4A4C+BTxkF+zC/EI/DB2L2egpUMLCIe7K1mdePTt5SztP3YvZ2Pr8KZ1rF7OvRPYhuzl7Ocsd4OLjVfXPnX7Qihv8FYC24gEcCMeAQ91Tc0YBwc1AXzHO97By1/+cn7jN34DgKuvvppPfvKTvPvd7+aqq67q/DmqNBFAGX1qEg+YbeadjD6NnXnXVwCfJB5yvQvxkNGnBvGI6dBSxEOudyEe0E8AP2gvZ0T0aexezuLzxu7l7G1Om3jI6NNMMuehrUMbKXM+iD7FiIez/0rEw+7z5+rOp0PmvPTJ2JnzfRPYhsyc753AFvVHmMBGyQGNoa6pGePgoCWAS0tL7Nq1ize84Q3B9nPPPZdbbrkleszi4iKLi4v+9QMPPABA9dNFM2FMtHk0OdGgNEVRURVQTCrKSUWhNHMTs72cGJ3edFIyLUomRcV0UjJRmuVJxXJRMlEVC0XFnKpYLDTzBSwozd4C5gvNZqWYU4rNhWJBKeYp2FxMmFeaBVUwDywUBZtVxSY0m1TFJlWxoCrmKLnma1/kuY/4BVQ1Qek5mBQoPY+qzDrVHBTKLCcFlBP0XAFFYZcKPSnQ0wI9UeipMsRjYmrd6TnzWheYbRPQE2Um+InhmLqw2wu7rTDzpNumJxjyZF+j6m16ouv3Fdru0/b9GjXRUGiqQqOUpphoVFFRTjRlUVEUUE5KJoWmVNb+RcW0KJkrKpaLyi+XrB+Wiop5pVmcGF/MF7C30Mwr6wPnDwrmVcHmomAOzWZVsElpFlTFZmU+a7OqmFeKTVRc87UvMq8mPOeRxh/oJRORreasPyZQFbVfygm6KKAs7NLoOfXU+EYX1v6FolpWMFHW5nbbnLVnoWq7W1/VNjV/0tkfu6+a4LVougD88dr6xtRA04U2keFC1/5Q1h+FtuPCaC/LiWbixoWqmPrxIMdF6X2ypCrmVcVSYXppLxawuYA5BXsLFfhjQU3YrEo/LjYp448FVdltJfMK4xdKFlTF/3fnF/nVRzyGQs9BVZjxUc0Zn8zNGd1fNTF+KSfocgITVS8nCl0UxgfT0B/a+aIQ42MOo+GbIPbR8of/t8I+/DbrCxsRc+OCifVFYcaGKhr+KDRFoc18VWjmi5JpUa04LpYnRgu7VFTMFdrMU8r4Y6GAeaXYW9h5yvpi3tp/XlV26cYFdn3KPIoFO1dd97Uv8v97xC+gJhOUntjxYcdFYX+ck8L4ZdqYp8pCjAsxRtx8pUDP2TnLjYHWvNQeF3rSnqeCpYqNC9D2+jFGgsVUL7LWx7gAU5YB2LNnT7B9YWGBhYWF1vvXck3N2FgctATw+9//PmVZsnXr1mD71q1b2b17d/SYq666ire85S2t7d+77A9mco7j4Nv4wFTGPoBvGV8sbfR5ZMC3sh/2GdxtHmZMV33jfoEf//jHbNmyZSafvWnTJrZt28Znd39i3Z91+OGHc/zxxwfb3vzmN3P55Ze33ruWa2rGxuKgJYAOSjxWBfuYobHN4Y1vfCOXXnqpf/2jH/2IE088ke985zszG8wHIvbs2cPxxx/PPffcw5FHHrnRp7NfINtsbch2649ss7Whi9201vz4xz9m+/btMzuPzZs3c/fdd7O0tP67l9j1MBb9k+hzTc3YWBy0BPCYY45hMpm07kzuu+++1h2Mw0qh7y1btuSJcg048sgjs916Ittsbch2649ss7VhNbuNESzYvHkzmzdvnvnfkVjLNTVjY1Fs9AlsFDZt2sTpp5/OjTfeGGy/8cYbedKTnrRBZ5WRkZGRkbH/IV9T9z8ctBFAgEsvvZQXvehFPPaxj+WMM87gve99L9/5znd45StfudGnlpGRkZGRsV8hX1P3LxzUBPB5z3seP/jBD/j93/997r33Xnbu3MknPvEJTjzxxE7HLyws8OY3v3lVTURGiGy3/sg2Wxuy3foj22xtyHZb/zU1Y1wonRv+ZWRkZGRkZGQcVDhoNYAZGRkZGRkZGQcrMgHMyMjIyMjIyDjIkAlgRkZGRkZGRsZBhkwAMzIyMjIyMjIOMmQCuA68613vYseOHWzevJnTTz+d//N//s9Gn9KG4KqrruJxj3scRxxxBMceeyzPfvazueuuu4L3aK25/PLL2b59O4cccghnnXUWX/3qV4P3LC4u8qpXvYpjjjmGww47jGc961l897vfHfOrbCiuuuoqlFJccsklflu2Wxvf+973+PVf/3WOPvpoDj30UH7hF36BXbt2+f3ZZm1Mp1Pe9KY3sWPHDg455BBOOukkfv/3f5+qqnvFZrvBZz7zGZ75zGeyfft2lFL89V//dbB/KBvdf//9vOhFL2LLli1s2bKFF73oRfzoRz+a8bfLyGhAZ6wJ1157rZ6fn9fve9/79J133qkvvvhifdhhh+lvf/vbG31qo+O8887TH/jAB/RXvvIVffvtt+vzzz9fn3DCCfonP/mJf89b3/pWfcQRR+iPfvSj+o477tDPe97z9HHHHaf37Nnj3/PKV75SP+QhD9E33nij/tKXvqSf9rSn6cc85jF6Op1uxNcaFV/4whf0wx72MP3oRz9aX3zxxX57tluIH/7wh/rEE0/UL33pS/XnP/95fffdd+ubbrpJf/Ob3/TvyTZr44orrtBHH320/pu/+Rt9991367/8y7/Uhx9+uL766qv9e7LdtP7EJz6hL7vsMv3Rj35UA/qv/uqvgv1D2eiXf/mX9c6dO/Utt9yib7nlFr1z5059wQUXjPU1MzK01lpnArhGPP7xj9evfOUrg22nnHKKfsMb3rBBZ7Tv4L777tOAvvnmm7XWWldVpbdt26bf+ta3+vfs3btXb9myRf/pn/6p1lrrH/3oR3p+fl5fe+21/j3f+973dFEU+m//9m/H/QIj48c//rE++eST9Y033qif+tSnegKY7dbG61//en3mmWeuuD/bLI7zzz9f/+f//J+Dbc95znP0r//6r2uts91iaBLAoWx05513akB/7nOf8++59dZbNaC/9rWvzfhbZWTUyI+A14ClpSV27drFueeeG2w/99xzueWWWzborPYdPPDAAwAcddRRANx9993s3r07sNfCwgJPfepTvb127drF8vJy8J7t27ezc+fOA96mF154Ieeffz5nn312sD3brY2PfexjPPaxj+U//af/xLHHHstpp53G+973Pr8/2yyOM888k7/7u7/j61//OgD/+I//yGc/+1l+5Vd+Bch264KhbHTrrbeyZcsWnvCEJ/j3PPGJT2TLli0HhR0z9h0c1J1A1orvf//7lGXZanC9devWViPsgw1aay699FLOPPNMdu7cCeBtErPXt7/9bf+eTZs28aAHPaj1ngPZptdeey1f+tKX+OIXv9jal+3Wxre+9S3e/e53c+mll/K7v/u7fOELX+C3f/u3WVhY4MUvfnG22Qp4/etfzwMPPMApp5zCZDKhLEv+4A/+gOc///lA/q11wVA22r17N8cee2zr84899tiDwo4Z+w4yAVwHlFLBa611a9vBhosuuogvf/nLfPazn23tW4u9DmSb3nPPPVx88cXccMMNbN68ecX3ZbvVqKqKxz72sVx55ZUAnHbaaXz1q1/l3e9+Ny9+8Yv9+7LNQlx33XV8+MMf5pprruFRj3oUt99+O5dccgnbt2/nJS95iX9fttvqGMJGsfcfbHbM2HjkR8BrwDHHHMNkMmndrd13332tu8ODCa961av42Mc+xqc//Wke+tCH+u3btm0DSNpr27ZtLC0tcf/996/4ngMNu3bt4r777uP0009nbm6Oubk5br75Zv7n//yfzM3N+e+d7VbjuOOO4+d//ueDbY985CP5zne+A+Tf2kp47Wtfyxve8AZ+7dd+jVNPPZUXvehF/M7v/A5XXXUVkO3WBUPZaNu2bfzrv/5r6/P/3//7fweFHTP2HWQCuAZs2rSJ008/nRtvvDHYfuONN/KkJz1pg85q46C15qKLLuL666/nU5/6FDt27Aj279ixg23btgX2Wlpa4uabb/b2Ov3005mfnw/ec++99/KVr3zlgLXp05/+dO644w5uv/12/++xj30sL3zhC7n99ts56aSTst0aePKTn9wqMfT1r3/dN5vPv7U4/u3f/o2iCKf7yWTiy8Bku62OoWx0xhln8MADD/CFL3zBv+fzn/88DzzwwEFhx4x9CBuReXIgwJWBef/736/vvPNO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", + "text/html": [ + "\n", + "
\n", + "
\n", + " Figure\n", + "
\n", + " \n", + "
\n", + " " + ], + "text/plain": [ + "Canvas(toolbar=Toolbar(toolitems=[('Home', 'Reset original view', 'home', 'home'), ('Back', 'Back to previous …" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "frame.plot.pcolormesh()" ] } ], From 8145f2fa5d9f78eb6c557dca7abc3993e646526e Mon Sep 17 00:00:00 2001 From: Pete R Jemian Date: Wed, 6 Jul 2022 13:57:06 -0500 Subject: [PATCH 22/24] DOC #652 --- .../examples/_howto_setup_hdf5_plugin.ipynb | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/docs/source/examples/_howto_setup_hdf5_plugin.ipynb b/docs/source/examples/_howto_setup_hdf5_plugin.ipynb index e34cacc6b..b3f79036c 100644 --- a/docs/source/examples/_howto_setup_hdf5_plugin.ipynb +++ b/docs/source/examples/_howto_setup_hdf5_plugin.ipynb @@ -288,7 +288,7 @@ "id": "a30bec95-4e20-482f-85bb-4d1b872e820e", "metadata": {}, "source": [ - "### Test\n", + "### Try it\n", "\n", "Test that the customizations actually work by staging and triggering the detector. Wait for acquisition to finish, then unstage." ] @@ -479,6 +479,14 @@ "To get the run information from the databroker catalog, we *could* assume the run we want is the most recent one in the catalog, using: `run = cat.v2[-1]`. But, since we have a list of run uid strings, let's use that instead. We called the `RE` with `bp.count()`, which only generates a single run, so there is no assumption here." ] }, + { + "cell_type": "markdown", + "id": "5137856c-3062-4626-9cc5-ece84347494a", + "metadata": {}, + "source": [ + "### Get the run from the catalog" + ] + }, { "cell_type": "code", "execution_count": 16, @@ -489,6 +497,14 @@ "run = cat.v2[uids[0]]" ] }, + { + "cell_type": "markdown", + "id": "a651d935-1ef0-40ca-b980-3e7b0d3b9e4a", + "metadata": {}, + "source": [ + "### Get the image frame from the run" + ] + }, { "cell_type": "markdown", "id": "ceb46cb9-7319-48b2-a036-0d39e602b1fa", @@ -509,6 +525,14 @@ "frame = image[0][0]" ] }, + { + "cell_type": "markdown", + "id": "c0a3129f-2949-4ace-9cf9-dbbe5a1868e0", + "metadata": {}, + "source": [ + "### Visualize the image" + ] + }, { "cell_type": "markdown", "id": "7ef2e40d-e282-4fb9-97f8-8c0dbb488010", From bfe83ff16441d49273b6cb1d4bad5209c1830726 Mon Sep 17 00:00:00 2001 From: Pete R Jemian Date: Wed, 6 Jul 2022 14:01:38 -0500 Subject: [PATCH 23/24] CI #652 --- .github/workflows/publish-sphinx.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/publish-sphinx.yml b/.github/workflows/publish-sphinx.yml index 77f513e63..1816579d1 100644 --- a/.github/workflows/publish-sphinx.yml +++ b/.github/workflows/publish-sphinx.yml @@ -25,7 +25,6 @@ jobs: - name: Install pandoc run: | - pip install -r requirements-pub.txt sudo apt-get update && sudo apt-get install pandoc - name: Install Sphinx build requirements From 3b912b6d31041cc48b7aa752e6fb4926e80df27f Mon Sep 17 00:00:00 2001 From: Pete R Jemian Date: Wed, 6 Jul 2022 14:11:36 -0500 Subject: [PATCH 24/24] CI #652 pin pygments --- requirements-sphinx.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/requirements-sphinx.txt b/requirements-sphinx.txt index 51c321b8d..c42cd5dc4 100644 --- a/requirements-sphinx.txt +++ b/requirements-sphinx.txt @@ -1,3 +1,4 @@ +markupsafe ==2.0.1 nbsphinx +pygments ==2.6.1 sphinx-rtd-theme -markupsafe ==2.0.1