{ "cells": [ { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "# Dynamic Limits for Two Motors" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Overview\n", "\n", "Some instrument designs need protection against accidental collision between moving parts during routine operations. In such cases, each of the axes may be operating between its valid range but an interim (in-motion) state can pose the possibility of a collision.\n", "\n", "One such possible collision is when arms of a diffractometer (such as $\\theta$ and $2\\theta$, known here as `theta` and `ttheta`, respectively) collide causing damage to beam transport apparatus and consequential instrumental downtime.\n", "\n", "To prevent the collision *in this case*, the $2\\theta$ axis must be at least $\\delta$ degrees above the $\\theta$ axis. Empirically, $\\delta$ of 3 degrees is sufficient protection.\n", "\n", "From a controls safety view, we provide an EPICS PV calculation that is zero when the move is not permitted and one when: ($2\\theta - \\theta) \\ge \\delta$. We'll monitor that PV in Bluesky to add a [suspender](https://blueskyproject.io/bluesky/state-machine.html#automated-suspension) that can interrupt the scan (via the [bluesky.RunEngine](https://blueskyproject.io/bluesky/run_engine_api.html?highlight=runengine)) if the permit is removed. When the RunEngine handles an interruption involving [*movable devices*](https://blueskyproject.io/bluesky/hardware.html?highlight=movables#settable-movable-device), it sends a stop to each of the movables involved. Thus, when the dynamic limit permit is removed, both motors are stopped and the scan pauses, waiting for external interaction to clear the condition.\n", "\n", "
\n", "[Here](https://github.com/bluesky/bluesky/blob/4fab894bddbd4a563f28852ea3171b87140ae7b9/bluesky/run_engine.py#L1034-L1036) is where bluesky tells the motors to stop:\n", "\n", "```\n", " if justification is not None:\n", " print(\"Justification for this suspension:\\n%s\" % justification)\n", " for current_run in self._run_bundlers.values():\n", " current_run.record_interruption('resume')\n", " # During suspend, all motors should be stopped. Call stop() on\n", " # every object we ever set().\n", " self._stop_movable_objects(success=True)\n", "```\n", "\n", "If the RunEngine is started while the dynamic limit permit calculation is zero, the RunEngine will pause immediately. Here is an example:\n", "\n", "```\n", "In [21]: uid = RE(th_tth_scan([noisy, th_tth_permit], 8, 6, points=4, min_sep=3)) \n", "At least one suspender has tripped. The plan will begin when all suspenders are ready. Justification:\n", " 1. Signal th_tth_permit is low\n", "\n", "Suspending... To get to the prompt, hit Ctrl-C twice to pause.\n", "```\n", "\n", "We might need to write our own suspender if none of the [provided suspenders](https://blueskyproject.io/bluesky/state-machine.html#built-in-suspenders) will do the job we want.\n", "\n", "
\n", "\n", "## Summary\n", "\n", "Any time the motors are moved by the bluesky RunEngine, they will be stopped if the dynamic limit permit calculation goes to zero and the scan will pause.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## EPICS setup" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "We start with two motor axes defined in EPICS. Here, we run the docker image [prjemian/synApps](https://hub.docker.com/r/prjemian/synapps) to make the [EPICS IOC simulator run in a docker container](https://github.com/prjemian/epics-docker/#custom-synappshttps://github.com/prjemian/epics-docker/#custom-synapps) with IOC prefix `gp:`. These are motor PVs: `gp:m1` and `gp:m2` as shown.\n", "\n", "![EPICS motor GUI screens](/_static/demo_dynamic_limits_motors.png)" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "To compute a dynamic limit between the two motor axes, we use a *userCalc* (EPICS [swait](https://htmlpreview.github.io/?https://raw.githubusercontent.com/epics-modules/calc/R3-6-1/documentation/swaitRecord.html) record), `gp:userCalc1` with settings as shown.\n", "\n", "1. Set the description field to describe what this does.\n", "1. Monitor each motor's readback (`.RBV`) value. The readback value is the motor record's *best* knowledge of the actual motor position.\n", "1. `gp:userCalc1.INAN` = `sky:m1.RBV`, the value will be in `A` once the motor moves.\n", "1. `gp:userCalc1.INBN` = `sky:m2.RBV`, the value will be in `B` once the motor moves.\n", "1. Change the calculation's `.SCAN` field from *Passive* (calculates only when requested) to *I/O Intr* (calculate when any input changes, based on each field's *TRIGGER?* setting).\n", "1. Enter the angle of minimum approach (3) into the `gp:userCalc1.C` field. This will be the PV to change this number.\n", "1. Enter the permit calculation: `(B-A)>=C`\n", "\n", "The calculated result (in `gp:userCalc1.VAL`, `gp:userCalc1` for short) once either of the motors move.\n", "\n", "![motion permit calculation](/_static/demo_dynamic_limits_permit_calc.png)" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "To scan, we want a \"detector\". Let's use another *userCalc* (`gp:userCalc2`) to simulate a noisy detector with a random number generator. We'll update this detector only when either motor moves (same as with the permit calculation) setting its `.SCAN` to *I/O Intr*. The setup is shown in the next screen view image:\n", "\n", "![noisy detector simulation](/_static/demo_dynamic_limits_permit_noisy.png)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Bluesky setup" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "/home/prjemian/bluesky/instrument/_iconfig.py\n", "Activating auto-logging. Current session state plus future input saved.\n", "Filename : /home/prjemian/Documents/projects/BCDA-APS/bluesky_training/docs/source/howto/.logs/ipython_console.log\n", "Mode : rotate\n", "Output logging : True\n", "Raw input log : False\n", "Timestamping : True\n", "State : active\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "I Tue-18:47:27 - ############################################################ startup\n", "I Tue-18:47:27 - logging started\n", "I Tue-18:47:27 - logging level = 10\n", "I Tue-18:47:27 - /home/prjemian/bluesky/instrument/session_logs.py\n", "I Tue-18:47:27 - /home/prjemian/bluesky/instrument/collection.py\n", "I Tue-18:47:27 - CONDA_PREFIX = /home/prjemian/.conda/envs/bluesky_2023_2\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Exception reporting mode: Minimal\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "I Tue-18:47:27 - xmode exception level: 'Minimal'\n", "I Tue-18:47:27 - /home/prjemian/bluesky/instrument/mpl/notebook.py\n", "I Tue-18:47:27 - #### Bluesky Framework ####\n", "I Tue-18:47:27 - /home/prjemian/bluesky/instrument/framework/check_python.py\n", "I Tue-18:47:27 - /home/prjemian/bluesky/instrument/framework/check_bluesky.py\n", "I Tue-18:47:27 - /home/prjemian/bluesky/instrument/framework/initialize.py\n", "I Tue-18:47:27 - RunEngine metadata saved in directory: /home/prjemian/Bluesky_RunEngine_md\n", "I Tue-18:47:28 - using databroker catalog 'training'\n", "I Tue-18:47:28 - using ophyd control layer: pyepics\n", "I Tue-18:47:28 - /home/prjemian/bluesky/instrument/framework/metadata.py\n", "I Tue-18:47:28 - /home/prjemian/bluesky/instrument/epics_signal_config.py\n", "I Tue-18:47:28 - Using RunEngine metadata for scan_id\n", "I Tue-18:47:28 - #### Devices ####\n", "I Tue-18:47:28 - /home/prjemian/bluesky/instrument/devices/area_detector.py\n", "I Tue-18:47:28 - /home/prjemian/bluesky/instrument/devices/calculation_records.py\n", "I Tue-18:47:30 - /home/prjemian/bluesky/instrument/devices/fourc_diffractometer.py\n", "I Tue-18:47:30 - /home/prjemian/bluesky/instrument/devices/ioc_stats.py\n", "I Tue-18:47:30 - /home/prjemian/bluesky/instrument/devices/kohzu_monochromator.py\n", "I Tue-18:47:30 - /home/prjemian/bluesky/instrument/devices/motors.py\n", "I Tue-18:47:30 - /home/prjemian/bluesky/instrument/devices/noisy_detector.py\n", "I Tue-18:47:30 - /home/prjemian/bluesky/instrument/devices/scaler.py\n", "I Tue-18:47:31 - /home/prjemian/bluesky/instrument/devices/shutter_simulator.py\n", "I Tue-18:47:31 - /home/prjemian/bluesky/instrument/devices/simulated_fourc.py\n", "I Tue-18:47:31 - /home/prjemian/bluesky/instrument/devices/simulated_kappa.py\n", "I Tue-18:47:31 - /home/prjemian/bluesky/instrument/devices/sixc_diffractometer.py\n", "I Tue-18:47:32 - /home/prjemian/bluesky/instrument/devices/temperature_signal.py\n", "I Tue-18:47:32 - #### Callbacks ####\n", "I Tue-18:47:32 - /home/prjemian/bluesky/instrument/callbacks/spec_data_file_writer.py\n", "I Tue-18:47:32 - #### Plans ####\n", "I Tue-18:47:32 - /home/prjemian/bluesky/instrument/plans/lup_plan.py\n", "I Tue-18:47:32 - /home/prjemian/bluesky/instrument/plans/peak_finder_example.py\n", "I Tue-18:47:32 - /home/prjemian/bluesky/instrument/utils/image_analysis.py\n", "I Tue-18:47:32 - #### Utilities ####\n", "I Tue-18:47:32 - writing to SPEC file: /home/prjemian/Documents/projects/BCDA-APS/bluesky_training/docs/source/howto/20230411-184732.dat\n", "I Tue-18:47:32 - >>>> Using default SPEC file name <<<<\n", "I Tue-18:47:32 - file will be created when bluesky ends its next scan\n", "I Tue-18:47:32 - to change SPEC file, use command: newSpecFile('title')\n", "I Tue-18:47:32 - #### Startup is complete. ####\n" ] } ], "source": [ "# put the instrument package into the path\n", "import pathlib, sys\n", "sys.path.append(str(pathlib.Path.home() / \"bluesky\"))\n", "\n", "# start the instrument package for data collection\n", "from instrument.collection import *\n", "from bluesky import plans as bp\n", "from bluesky import plan_stubs as bps\n", "from ophyd import Component, Device, EpicsMotor, EpicsSignal\n", "\n", "bec.disable_plots() # not interested in graphics in this notebook\n", "RE.waiting_hook = None # disable the progress bar, looks awful in notebooks" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "symbol | PV (here) | meaning\n", "--- | --- | ---\n", "`theta` | gp:m1 | (`th`) EPICS motor record\n", "`ttheta` | gp:m2 | (`tth`) EPICS motor record\n", "`th_tth_permit` | gp:userCalc1.VAL | result of EPICS calculation (swait record: 1=permit, 0=not), updates when either motor moves\n", "`th_tth_min` | gp:userCalc1.C | minimum permitted `tth-th`\n", "`noisy` | gp:userCalc2.VAL | detector (random number generator) - integer, updates when either motor moves\n", "`noisy_scale` | gp:userCalc2.C | scale factor for `noisy`" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "Get the IOC prefix from the instrument configuration (`iconfig`). If not available, use `\"gp:\"` as the default." ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "GP_IOC = iconfig.get(\"GP_IOC_PREFIX\", \"gp:\")" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "Define the objects we'll use in this demo." ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "noisy = EpicsSignal(f\"{GP_IOC}userCalc2.VAL\", name=\"noisy\")\n", "noisy_scale = EpicsSignal(f\"{GP_IOC}userCalc2.C\", name=\"noisy_scale\")\n", "th_tth_min = EpicsSignal(f\"{GP_IOC}userCalc1.C\", name=\"th_tth_min\")\n", "th_tth_permit = EpicsSignal(f\"{GP_IOC}userCalc1.VAL\", name=\"th_tth_permit\")\n", "noisy.wait_for_connection()\n", "noisy_scale.wait_for_connection()\n", "th_tth_min.wait_for_connection()\n", "th_tth_permit.wait_for_connection()" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "# We do this ONLY in the simulator. Real instrument motors may need different settings!\n", "# For this demo, we want to set the theta & ttheta motor backlash to the default condition\n", "# when the IOC is first created. We'll change this later in the demo.\n", "\n", "class BacklashMotor(EpicsMotor):\n", " backlash_distance = Component(EpicsSignal, \".BDST\")\n", " backlash_velocity = Component(EpicsSignal, \".BVEL\")\n", "\n", "theta = BacklashMotor(f\"{GP_IOC}m1\", name=\"theta\", labels=[\"motors\",])\n", "ttheta = BacklashMotor(f\"{GP_IOC}m2\", name=\"ttheta\", labels=[\"motors\",])\n", "theta.wait_for_connection()\n", "ttheta.wait_for_connection()\n", "%mov ttheta.backlash_distance 0 ttheta.backlash_velocity 2 ttheta.velocity 2\n", "%mov theta.backlash_distance 0 theta.backlash_velocity 1 theta.velocity 2" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "Setup the two swait records as shown above." ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "calcs.calc1.reset()\n", "calcs.calc1.description.put(\"(tth-th) permit\")\n", "calcs.calc1.channels.A.input_pv.put(theta.user_readback.pvname)\n", "calcs.calc1.channels.B.input_pv.put(ttheta.user_readback.pvname)\n", "calcs.calc1.channels.C.input_value.put(2.8)\n", "calcs.calc1.calculation.put(\"(B-A)>=C\")\n", "calcs.calc1.precision.put(5)\n", "calcs.calc1.scanning_rate.put(\"I/O Intr\")\n", "\n", "calcs.calc2.reset()\n", "calcs.calc2.description.put(\"noisy\")\n", "calcs.calc2.channels.A.input_pv.put(theta.user_readback.pvname)\n", "calcs.calc2.channels.B.input_pv.put(ttheta.user_readback.pvname)\n", "calcs.calc2.channels.C.input_value.put(100_000)\n", "calcs.calc2.calculation.put(\"floor(RNDM*C+.5)\")\n", "calcs.calc2.precision.put(0)\n", "calcs.calc2.scanning_rate.put(\"I/O Intr\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Custom Bluesky plan for $\\theta:2\\theta$ scan\n", "\n", "Define a plan for a coupled theta:2theta scan." ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "def th_tth_scan(detectors, tth_start, tth_end, points=11, min_sep=None):\n", " \"\"\"\n", " run a coupled theta:2theta scan\n", " \"\"\"\n", " min_sep = abs(min_sep or 2.4)\n", " old_sep = th_tth_min.get()\n", "\n", " # check end points first!\n", " if abs(tth_start/2) < min_sep:\n", " print(\n", " \"Starting point below allowed minimum:\"\n", " f\" |{tth_start/2:.4f}| < |{min_sep:.4f}|\")\n", " return\n", " if abs(tth_end/2) < min_sep:\n", " print(\n", " \"Ending point below allowed minimum:\"\n", " f\" |{tth_end/2:.4f}| < |{min_sep:.4f}|\")\n", " return\n", "\n", " yield from bps.mv(th_tth_min, min_sep)\n", " yield from bp.scan(\n", " detectors,\n", " ttheta, tth_start, tth_end,\n", " theta, tth_start/2, tth_end/2,\n", " points\n", " )\n", "\n", " # reset the previous minimum\n", " yield from bps.mv(th_tth_min, old_sep)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Try a scan that we know will fail the test for minimum separation." ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Starting point below allowed minimum: |2.5000| < |3.0000|\n" ] }, { "data": { "text/plain": [ "()" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "RE(th_tth_scan([noisy, th_tth_permit], 5, 25, points=11, min_sep=3))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Swap the two end points, that also fails." ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Ending point below allowed minimum: |2.5000| < |3.0000|\n" ] }, { "data": { "text/plain": [ "()" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "RE(th_tth_scan([noisy, th_tth_permit], 25, 5, points=11, min_sep=3))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This scan is successful." ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "\n", "Transient Scan ID: 926 Time: 2023-04-11 18:47:33\n", "Persistent Unique Scan ID: '6ddca39f-503d-4481-bd83-03ecfbe21ae7'\n", "New stream: 'label_start_motor'\n", "New stream: 'primary'\n", "+-----------+------------+------------+------------+------------+---------------+\n", "| seq_num | time | ttheta | theta | noisy | th_tth_permit |\n", "+-----------+------------+------------+------------+------------+---------------+\n", "| 1 | 18:47:36.5 | 12.0000 | 6.0000 | 47659 | 1.00000 |\n", "| 2 | 18:47:36.9 | 11.6000 | 5.8000 | 13759 | 1.00000 |\n", "| 3 | 18:47:37.4 | 11.2000 | 5.6000 | 963 | 1.00000 |\n", "| 4 | 18:47:37.9 | 10.8000 | 5.4000 | 10716 | 1.00000 |\n", "| 5 | 18:47:38.4 | 10.4000 | 5.2000 | 62858 | 1.00000 |\n", "| 6 | 18:47:38.9 | 10.0000 | 5.0000 | 79985 | 1.00000 |\n", "| 7 | 18:47:39.4 | 9.6000 | 4.8000 | 18714 | 1.00000 |\n", "| 8 | 18:47:39.9 | 9.2000 | 4.6000 | 65821 | 1.00000 |\n", "| 9 | 18:47:40.4 | 8.8000 | 4.4000 | 12596 | 1.00000 |\n", "| 10 | 18:47:40.9 | 8.4000 | 4.2000 | 86804 | 1.00000 |\n", "| 11 | 18:47:41.4 | 8.0000 | 4.0000 | 6307 | 1.00000 |\n", "+-----------+------------+------------+------------+------------+---------------+\n", "generator scan ['6ddca39f'] (scan num: 926)\n" ] }, { "data": { "text/plain": [ "('6ddca39f-503d-4481-bd83-03ecfbe21ae7',)" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "RE(th_tth_scan([noisy, th_tth_permit], 12, 8, points=11, min_sep=3))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "So far, have not registered a permit denied. Have not encountered a condition where permit _would_ be denied." ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Try to provoke a permit denied\n", "\n", "Check the backlash parameters for 2theta motor. Use the custom motor class that provides the backlash parameters." ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "theta.backlash_distance.get()=0.0 (degrees)\n", "theta.backlash_velocity.get()=1.0 (degrees/s)\n", "ttheta.backlash_distance.get()=0.0 (degrees)\n", "ttheta.backlash_velocity.get()=2.0 (degrees/s)\n" ] } ], "source": [ "# these two motors have backlash support\n", "print(f\"{theta.backlash_distance.get()=} ({theta.motor_egu.get()})\")\n", "print(f\"{theta.backlash_velocity.get()=} ({theta.motor_egu.get()}/s)\")\n", "\n", "print(f\"{ttheta.backlash_distance.get()=} ({ttheta.motor_egu.get()})\")\n", "print(f\"{ttheta.backlash_velocity.get()=} ({ttheta.motor_egu.get()}/s)\")" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "Change (just) the backlash velocity and set a backlash distance for `ttheta`." ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "ttheta.backlash_distance.get()=0.5 (degrees)\n", "ttheta.backlash_velocity.get()=0.2 (degrees/s)\n" ] } ], "source": [ "%mov ttheta.backlash_distance 0.5 ttheta.backlash_velocity 0.2\n", "print(f\"{ttheta.backlash_distance.get()=} ({ttheta.motor_egu.get()})\")\n", "print(f\"{ttheta.backlash_velocity.get()=} ({ttheta.motor_egu.get()}/s)\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Scan again over a shorter range." ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "\n", "Transient Scan ID: 927 Time: 2023-04-11 18:47:42\n", "Persistent Unique Scan ID: '1d45b5ef-9268-494b-8fd7-37af3599b833'\n", "New stream: 'label_start_motor'\n", "New stream: 'primary'\n", "+-----------+------------+------------+------------+------------+---------------+\n", "| seq_num | time | ttheta | theta | noisy | th_tth_permit |\n", "+-----------+------------+------------+------------+------------+---------------+\n", "| 1 | 18:47:46.0 | 7.0000 | 3.5000 | 47643 | 1.00000 |\n", "| 2 | 18:47:49.6 | 6.6667 | 3.3333 | 20673 | 1.00000 |\n", "| 3 | 18:47:53.2 | 6.3333 | 3.1667 | 79528 | 1.00000 |\n", "| 4 | 18:47:56.8 | 6.0000 | 3.0000 | 96895 | 0.00000 |\n", "+-----------+------------+------------+------------+------------+---------------+\n", "generator scan ['1d45b5ef'] (scan num: 927)\n" ] }, { "data": { "text/plain": [ "('1d45b5ef-9268-494b-8fd7-37af3599b833',)" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "RE(th_tth_scan([noisy, th_tth_permit], 7, 6, points=4, min_sep=3))" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "Let's monitor the signal _during_ the scan so we can see if it changes and how often." ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [], "source": [ "sd.monitors.append(th_tth_permit)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Repeat the scan, collecting the new info. We'll inspect the monitors after the scan is done. (The RunEngine returns a list of all the run uids created by the plan. Capture this list.)" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "\n", "Transient Scan ID: 928 Time: 2023-04-11 18:47:57\n", "Persistent Unique Scan ID: '991bdcb1-f8d4-4e2f-a0eb-1dc16330481b'\n", "New stream: 'label_start_motor'\n", "New stream: 'th_tth_permit_monitor'\n", "New stream: 'primary'\n", "+-----------+------------+------------+------------+------------+---------------+\n", "| seq_num | time | ttheta | theta | noisy | th_tth_permit |\n", "+-----------+------------+------------+------------+------------+---------------+\n", "| 1 | 18:48:00.6 | 7.0000 | 3.5000 | 61886 | 1.00000 |\n", "| 2 | 18:48:04.3 | 6.5000 | 3.2500 | 45252 | 1.00000 |\n", "| 3 | 18:48:08.0 | 6.0000 | 3.0000 | 14221 | 0.00000 |\n", "+-----------+------------+------------+------------+------------+---------------+\n", "generator scan ['991bdcb1'] (scan num: 928)\n" ] } ], "source": [ "uids = RE(th_tth_scan([noisy, th_tth_permit], 7, 6, points=3, min_sep=3))" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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DPF3PQAAAH7j02TkM844Qx07dvR3LQAAAH7lU9B55JFH9OCDD+ro0aP+rgcAAMBvvD51df7553vMxdm5c6diYmLUs2dPhYaGevT96quv/FchAACAj7wOOqNHj27CMgAAAPzPYU7Dq/qVlJQoKipKxcXFioyMDHQ5AADAC758fvs0R6dXr14qKiqq0X748GH16tXLl6cEAADwO5+Czt69e1VZWVmjvaysTAcOHGh0UQAAAP7QoOvorFy50v3vDz/8UFFRUe7HlZWV+uSTT5SYmOi/6gAAABqhQUHnxAnJ6enpHutCQ0PVs2dPPfvss34pDAAAoLEaFHSq72mVmJiof/7zn+rcuXOTFAUAAOAPPs3Refjhh9W+ffsa7eXl5crMzGx0UQAAAP7g09fLg4OD5XK5FB0d7dFeVFSk6OjoWicqtyR8vRwAgNan2b5eboyp9Y7lBw4c8JigDAAAEEgNmqNTfRsIh8OhYcOGKSTkv5tXVlZqz549uuKKK/xeJAAAgC98+tbVpk2bNGLECLVr1869LiwsTD179tT111/v1wIBAAB81aCgM3PmTElSz549NWbMGIWHh5+y/7JlyzRq1Ci1bdvW9woBAAB81KT3uoqMjNSmTZta3G0hmIwMAEDr02yTkb3lbYaaN2+eEhMTFR4eruTkZH3++edebbdu3TqFhITovPPOa0SVAADAVk0adLyxfPlyTZkyRTNmzFBubq4uvvhijRw5Uvv27TvldsXFxUpLS9OwYcOaqVIAANDaBDzoPPfccxo/frwmTJigvn37as6cOYqLi9P8+fNPud2kSZN00003KSUlpZkqBQAArU1Ag055eblycnKUmprq0Z6amqr169fXud2SJUu0a9cu9+To+pSVlamkpMRjAQAA9gto0CksLFRlZaViYmI82mNiYpSfn1/rNjt27NAf/vAHLV261OM6Pqcye/ZsRUVFuZe4uLhG1w4AAFq+Jg06CQkJCg0NrbffyVdZruvKy5WVlbrpppv08MMPKykpyes6MjIyVFxc7F7279/v9bYAAKD1atB1dE5WXl6ugoIC913Nq8XHx0uStm7desrtO3furODg4BpHbwoKCmoc5ZGkI0eOaOPGjcrNzdXkyZMlHb+jujFGISEh+uijj3TZZZfV2M7pdMrpdDZobAAAoPXzKejs2LFDt912W415NNVHYry9qWdY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GSUlJcnpdCouLk6PP/64R5+lS5eqf//+atOmjbp166Zbb71VRUVFXv1/VPvqq680fPhwdejQQZ06ddLtt9+u0tLSBj3HiQg6AFqFTz/9tEEfIoA/XXLJJXrttddqXXf06FFdccUV+uMf/+j18/34448aMmSIQkND9cEHH+ibb77Rs88+qw4dOrj7TJ06VatXr9brr7+ub7/9VlOnTtVdd92l//3f//V63999952qqqq0cOFCbdu2Tc8//7wWLFhQa/8lS5bI5XK5l/T0dI/199xzj15++WU988wz+u677/Tuu+9q0KBB7vVr165VWlqaxo8fr23btumtt97SP//5T02YMMHr/5eDBw/q8ssvV+/evfXFF19o9erV2rZtm8aNG+f1c9Tg92s6A4AfDB061Nxzzz3ux2VlZcblcrXq25Kg9Ro6dKhZsmTJKftkZ2cbSebHH3+s9/nuu+8+c9FFF52yzznnnGNmzZrl0TZgwABz//33N2rfTz31lElMTPRok2TefvvtOrf55ptvTEhIiPnuu+/q7PP000+bXr16ebS98MILpkePHh5tr776qjnrrLOM0+k0ffr0MXPnznWvW7hwoYmOjjaVlZXuttzcXCPJ7Nixo96x1YYjOgBahbCwMHXt2lUOhyPQpQCNtnLlSg0cOFC/+c1vFB0drfPPP1+LFy/26HPRRRdp5cqVysvLkzFG2dnZ2r59u0aMGNGofRcXF6tjx4412idPnqzOnTvrF7/4hRYsWOBxF/F3331XvXr10nvvvafExET17NlTEyZM0A8//ODuM3jwYB04cECrVq2SMUaHDh3SX//6V1111VXuPosXL9aMGTP02GOP6dtvv9Xjjz+uBx54QH/+858lSWVlZQoLC/O4q3lERISk40eMfOJTPAKAJpSenl7jxpTVN+is/ot1yZIlJioqyrz77rsmKSnJREREmOuvv96Ulpaa1157zSQkJJgOHTqYyZMnm4qKCvdzl5WVmenTp5vY2FjTpk0bM2jQoFZ500s0L38f0XE6ncbpdJqMjAzz1VdfmQULFpjw8HDz5z//2d2nrKzMpKWlGUkmJCTEhIWFmczMzEbte+fOnSYyMtIsXrzYo/2RRx4x69evN7m5ueaZZ54xbdq0MY888oh7/aRJk4zT6TQXXHCB+eyzz0x2drY577zzzKWXXurxPG+99ZZp166dCQkJMZLMqFGjTHl5uXt9XFyc+ctf/lJj3ykpKcYYY7Zu3WpCQkLMU089ZcrKyswPP/xgrrvuOiPJPP7446ccW10IOgBanMOHD5uUlBQzceJE43K5jMvlMh9//HGNoBMaGmqGDx9uvvrqK7NmzRrTqVMnk5qaam644Qazbds28+6775qwsDDzxhtvuJ/7pptuMoMHDzafffaZ2blzp3n66aeN0+k027dvD9Bo0RI99thjpm3btu4lKCjIOJ1Oj7bPPvvMY5uGBJ3Q0FD3h3u1u+66y1x44YXux08//bRJSkoyK1euNJs3bzYvvviiadeuncnKyqrxfN7sOy8vz/Tu3duMHz++3vqeeeYZExkZ6X48ceJEI8l8//337racnBwjyX06a9u2baZbt27mqaeeMps3bzarV6825557rrntttuMMcYUFBQYSSYiIsLj/9HpdJro6Gj38y5dutTExMSY4OBgExYWZu69914TExNjnnzyyXrrrg1BB0CLdPIcnZPfyKuP8OzcudPdZ9KkSaZNmzbmyJEj7rYRI0aYSZMmGWOO/zXrcDhMXl6ex76GDRtmMjIymm4waHWKiorMjh073MugQYPMk08+6dF29OhRj20aEnTi4+NrBI558+aZ2NhYY4wxR48eNaGhoea9997z6DN+/HgzYsSIGs9X377z8vJMUlKSGTt2rMf8l7qsXbvWSDL5+fnGGGMefPBBExIS4tHn6NGjRpL56KOPjDHG3HLLLebXv/61R5/PP//cSDIHDx40+fn5RpJ5/fXXPf4fd+zYYXbv3l2jhvz8fHPkyBFTWlpqgoKCzJtvvllv3bUJ8e2EFwAEXps2bXTmmWe6H8fExKhnz55q166dR1tBQYGk419bNcYoKSnJ43nKysrUqVOn5ikarULHjh095rFEREQoOjpavXv39svzDxkyRN9//71H2/bt25WQkCBJOnbsmI4dO+YxV0WSgoODPebOeCMvL0+XXnqpkpOTtWTJkhrPWZvc3FyFh4e7vwU2ZMgQVVRUaNeuXe7X3Pbt2yXJXfPRo0cVEuIZK4KDgyUd/2p6TEyMunfvrt27d+vmm2+ut4aYmBhJ0quvvqrw8HANHz7cuwGfhKADoNUKDQ31eOxwOGptq/5gqKqqUnBwsHJyctxvwNVODEdAQ+Tn5ys/P187d+6UJG3ZskXt27dXfHy8OywNGzZM1157rSZPnizp+FfHBw8erMcff1w33HCDvvzySy1atEiLFi2SJEVGRmro0KGaPn26IiIilJCQoDVr1igzM1PPPfec1/s+ePCgLrnkEsXHx+uZZ57Rv/71L/e2Xbt2lXR8onF+fr5SUlIUERGh7OxszZgxQ7fffrucTqck6fLLL9eAAQN02223ac6cOaqqqtKdd96p4cOHu/9wuPrqqzVx4kTNnz9fI0aMkMvl0pQpUzRo0CDFxsZKkh566CHdfffdioyM1MiRI1VWVqaNGzfqxx9/1LRp0yRJL730kgYPHqx27dopKytL06dP1xNPPOHx1fsG8ek4EAA0seHDh5vJkye7H9d26ioqKspjm5kzZ5r+/ft7tKWnp5trrrnGGGPM999/byTVmFsB1OdUk5FnzpxZY/K8/v8E+moJCQlm5syZHtu9++67pl+/fsbpdJqzzjrLLFq0yGO9y+Uy48aNM7GxsSY8PNz06dPHPPvssx6XWKhv39WneGtbqn3wwQfmvPPOM+3atTNt2rQx/fr1M3PmzDHHjh3zqCcvL89cd911pl27diYmJsaMGzfOFBUVefR54YUXzNlnn20iIiJMt27dzM0332wOHDjg0Wfp0qXmvPPOM2FhYeaMM84wv/zlL82KFSvc68eOHWs6duxowsLCzM9//vM6J2B7y2GMMb5FJABoOrfffrs2bdqkN998U+3atdPXX3+tYcOG6ccff1SHDh302muvacqUKR4XEHzooYf0zjvvaNOmTe62cePG6fDhw3rnnXckSbfccovWrVunZ599Vueff74KCwv197//Xeeee66uvPLK5h0kgCbHdXQAtEj33nuvgoODdfbZZ6tLly7at2+fX553yZIlSktL0+9+9zv16dNHo0aN0hdffKG4uDi/PD+AloUjOgAAwFoc0QEAANYi6AAAAGsRdAAAgLUIOgAAwFoEHQAAYC2CDgAAsBZBBwAAWIugAwAArEXQAQAA1iLoAAAAaxF0AACAtf4fDIYOW3cjoMoAAAAASUVORK5CYII=", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "cat[uids[0]].th_tth_permit_monitor.read()[\"th_tth_permit\"].plot.scatter()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Aha! The first plot shows the monitored values. It is clear the signal **does** go to 0 and then come back to 1.\n", "\n", "Look at the monitored data. First get the run object from databroker (indexed by the first uid in the list). From the run object, return the monitored data in a table. Python prints this object if it is not assigned. All this in one step." ] }, { "cell_type": "code", "execution_count": 16, "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", "
timeth_tth_permit
seq_num
12023-04-11 23:47:57.2393000130.0
22023-04-11 23:47:57.8045518401.0
32023-04-11 23:48:01.1103842260.0
42023-04-11 23:48:02.9180145261.0
52023-04-11 23:48:04.7214093210.0
\n", "
" ], "text/plain": [ " time th_tth_permit\n", "seq_num \n", "1 2023-04-11 23:47:57.239300013 0.0\n", "2 2023-04-11 23:47:57.804551840 1.0\n", "3 2023-04-11 23:48:01.110384226 0.0\n", "4 2023-04-11 23:48:02.918014526 1.0\n", "5 2023-04-11 23:48:04.721409321 0.0" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "cat.v1[uids[0]].table(\"th_tth_permit_monitor\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We see some zero values (and then return to one) indicating occasional removal of the dynamic limit calculation permit. Since we only saw these when we added a backlash correction, we understand these dynamic violations of the limits are exactly what we hoped to intercept." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Create a suspender\n", "\n", "Block the RunEngine when the permit fails.\n", "\n", "N.B. Might need to consider the special case where the permit fails when first starting the run. Why did that fail? (We'll answer that soon.)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The Bluesky project shows [how to suspend a plan when the source X-ray intensity drops too low](https://blueskyproject.io/bluesky/main/state-machine.html#example-suspend-a-plan-if-the-beam-current-dips-low).\n", "\n", "Following this example, we'll do similar. In our case, the signal is a boolean\n", "that indicates *no permit* when low. The suspender interrupts the `RE` as long\n", "as the signal is invalid and automatically resumes if the signal becomes valid\n", "again. Let's see how this works here." ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [], "source": [ "from bluesky.suspenders import SuspendBoolLow\n", "sus = SuspendBoolLow(th_tth_permit)\n", "RE.install_suspender(sus)" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "Repeat the scan. Set the minimum separation low enough that the motor backlash will not trip the suspender near the end of the scan. After the previous move, the two motors are close enough that the suspender may trip at the start of the next scan, as the motors are sent to the first position. Let's move `theta` a little lower to avoid that." ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "collapsed": true, "jupyter": { "outputs_hidden": true } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Exception reporting mode: Minimal\n", "theta.position=3.0000000000000004 ttheta.position=6.0\n", "Suspender SuspendBoolLow(EpicsSignal(read_pv='gp:userCalc1.VAL', name='th_tth_permit', value=1.0, timestamp=1681256888.82624, tolerance=1e-05, auto_monitor=True, string=False, write_pv='gp:userCalc1.VAL', limits=False, put_complete=False), sleep=0, pre_plan=None, post_plan=None,tripped_message=) reports a return to nominal conditions. Will sleep for 0 seconds and then release suspension at 2023-04-11 18:48:08.\n", "theta: 81%|████████████████████▏ | 0.1616/0.2 [00:00<00:00, 1.11degrees/s]\n", "theta: 100%|████████████████████████████| 0.2/0.2 [00:00<00:00, 1.23s/degrees]\n", "theta [In progress. No progress bar available.] \n", " \n", "theta.position=2.8000000000000003 ttheta.position=6.0\n", "\n", "\n", "Transient Scan ID: 929 Time: 2023-04-11 18:48:09\n", "Persistent Unique Scan ID: '52b208be-f1a3-42df-8cb4-9c790954e96c'\n", "New stream: 'label_start_motor'\n", "New stream: 'th_tth_permit_monitor'\n", "New stream: 'primary'\n", "+-----------+------------+------------+------------+------------+---------------+\n", "| seq_num | time | ttheta | theta | noisy | th_tth_permit |\n", "+-----------+------------+------------+------------+------------+---------------+\n", "| 1 | 18:48:12.5 | 7.0000 | 3.5000 | 76393 | 1.00000 |\n", "| 2 | 18:48:16.2 | 6.5000 | 3.2500 | 33643 | 1.00000 |\n", "| 3 | 18:48:19.9 | 6.0000 | 3.0000 | 73523 | 1.00000 |\n", "+-----------+------------+------------+------------+------------+---------------+\n", "generator scan ['52b208be'] (scan num: 929)\n" ] } ], "source": [ "# Set a smaller minimum separation\n", "%xmode Minimal\n", "print(f\"{theta.position=} {ttheta.position=}\")\n", "%movr theta -0.2\n", "print(f\"{theta.position=} {ttheta.position=}\")\n", "uids = RE(th_tth_scan([noisy, th_tth_permit], 7, 6, points=3, min_sep=2.4))" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "This succeeded because the minimum separation distance was set smaller than usual, to `2.4`, which allows for a backlash correction (of 0.5 in the `ttheta` motor) at the last point." ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "collapsed": true, "jupyter": { "outputs_hidden": true } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "theta: 100%|████████████████████████████| 0.2/0.2 [00:00<00:00, 1.08s/degrees]\n", "theta [In progress. No progress bar available.] \n", " \n", "\n", "\n", "Transient Scan ID: 930 Time: 2023-04-11 18:48:34\n", "Persistent Unique Scan ID: '64c1bf12-041a-4e1c-ba92-0aef01a1e4e7'\n", "New stream: 'label_start_motor'\n", "New stream: 'th_tth_permit_monitor'\n", "New stream: 'primary'\n", "+-----------+------------+------------+------------+------------+---------------+\n", "| seq_num | time | ttheta | theta | noisy | th_tth_permit |\n", "+-----------+------------+------------+------------+------------+---------------+\n", "| 1 | 18:48:37.5 | 7.0000 | 3.5000 | 29638 | 1.00000 |\n", "| 2 | 18:48:41.3 | 6.5000 | 3.2500 | 4031 | 1.00000 |\n", "Suspending....To get prompt hit Ctrl-C twice to pause.\n", "Suspension occurred at 2023-04-11 18:48:42.\n", "Justification for this suspension:\n", "Signal th_tth_permit is low\n", "Suspender SuspendBoolLow(EpicsSignal(read_pv='gp:userCalc1.VAL', name='th_tth_permit', value=1.0, timestamp=1681256930.19391, tolerance=1e-05, auto_monitor=True, string=False, write_pv='gp:userCalc1.VAL', limits=False, put_complete=False), sleep=0, pre_plan=None, post_plan=None,tripped_message=) reports a return to nominal conditions. Will sleep for 0 seconds and then release suspension at 2023-04-11 18:48:50.\n", "| 3 | 18:48:53.1 | 6.0000 | 3.0000 | 32567 | 1.00000 |\n", "+-----------+------------+------------+------------+------------+---------------+\n", "generator scan ['64c1bf12'] (scan num: 930)\n" ] } ], "source": [ "# As in the previous step:\n", "%movr theta -0.2\n", "\n", "# set the minimum separation to 2.5\n", "# Likely the scan will fail due to the suspender when the m2 motor is taking a backlash correction and (m2-m1)<3.\n", "uids = RE(th_tth_scan([noisy, th_tth_permit], 7, 6, points=3, min_sep=2.5))" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "The suspender tripped during the scan at the last move when `ttheta` was performing its backlash correction. Exactly what we wanted to happen. While moving the motors to the last point of the scan, `(m2-m1)<2.5` was satisfied and the suspender tripped. The command line was not responsive. _To allow the scan to progress, the `theta` motor was moved from some other EPICS client outside of bluesky, allowing the scan to progress._ If the suspender delays the scan long enough, the scan will timeout with a `FailedStatus` exception.\n", "\n", "When the suspender trips, motion (in bluesky) is not permitted due to our computation of dynamic limits. We must move the motors so that the limit permit is restored before we can scan. We can move the motor from the command line or a GUI application. (This works since other EPICS clients such as the command line do not use this RunEngine instance. The suspender is checked only by this RunEngine.)\n", "\n", "The signal (for the suspender) does not automatically clear since it is only computed when the motor readback value changes. We can clear this manually by moving the `theta` motor away from the `ttheta` motor. Yet, still, we encounter the problem when the two motors are close together either, as in this example." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Conclusion\n", "\n", "We can avoid an anticipated collision of instrument hardware by providing a RunEngine suspender tied to the value of an EPICS PV." ] } ], "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.10.10" } }, "nbformat": 4, "nbformat_minor": 4 }