{ "cells": [ { "cell_type": "markdown", "metadata": { "jp-MarkdownHeadingCollapsed": true, "tags": [] }, "source": [ "# Good Afternoon Bluesky: REALLY starting your experiment (plan overnight script)\n", "\n", "In this notebook you will:\n", "\n", "* Prepare to collect data for analysis\n", "* Assemble complex plans for single line exectution\n", "* Create and test an \"overnight\" script\n", "* Experience some guided troubleshooting for common errors\n", "\n", "\n", "Recommend Prerequisites:\n", "\n", "* [Hello Python and Jupyter](./Hello%20Python%20and%20Jupyter.ipynb)\n", "* [Good Morning Bluesky](./Good%20Morning%20Bluesky.ipynb)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#pip install -U --pre databroker[all]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Hosted by Josh" ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "## For Bluesky Session Continuity\n", "Run the next cells to make the ipython kernel match the previous session\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [] }, "outputs": [], "source": [ "%run -i gm_user/user_profile.py" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%run -i gm_user/user_startup.py \n", "%run -i gm_user/user_startup_afternoon.py" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Let's assume some time has passed\n", "\n", "
\n", "AND we are ready to collect \"real\" data we want analyzed.\n", " \n", "
\n", "\n", "Is the RE persistent metadata correct for the next scan which should be analyzed? \n", "\n", "
Hint\n", "\n", "```python\n", "\n", "RE.md\n", "\n", "```\n", "
\n", "\n", "\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
Better Copy and Paste Solution\n", "\n", "```python\n", "\n", "md_info()\n", "\n", "```\n", "
\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "RE.md[\"purpose\"]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Well, we don't want all the scans to be labeled with the `\"purpose\"` of \"setup\".\n", "\n", "This is a standard python object, so how does one delete a dictionary element without starting from scratch?\n", "\n", "\n", "\n", "
Copy Paste Solution\n", "\n", "There are two solutions that are not equivalent. \n", " \n", "The code below lets you try both. Try the different options to remove the dictionary entry in seperate notebook cells to see the difference.\n", " \n", "```python\n", "\n", "del RE.md[\"purpose\"]\n", "\n", "\n", "```\n", "\n", "**OR** \n", "```python\n", "\n", "RE.md.pop('purpose')\n", "\n", "```\n", "
\n", "\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "md_info()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "### USE THIS LINE RESET in order to try the ALTERATIVE SOLUTION\n", "RE.md[\"purpose\"] = \"setup\"" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "## Setup to start planning overnight scritps\n", "\n", "We have a new device, `temperature`. \n", "\n", "1. What is the value of `temperature`.\n", "2. Using `RE(mv())`:\n", " - move `temperature` to 40 \n", " - move `motor` to 0 (the peak position)\n", " \n", " \n", "
#1 Copy Paste Code - solutions\n", " \n", "```python\n", "\n", "temperature.readback.get()\n", "\n", "\n", "```\n", " \n", "OR\n", " \n", " \n", "```python\n", "temperature.setpoint.get()\n", "\n", "\n", "```\n", "
\n", "\n", " \n", "
#1 Copy Paste Code - advanced solution\n", " \n", "```python\n", "\n", "temperature.readback.read()['motor3']['value']\n", "\n", "\n", "```\n", " \n", " \n", "Explanation: \n", " \n", " \n", "```python\n", "temperature.readback\n", "\n", "\n", "```\n", "\n", "Returns: \n", "```\n", "_ReadbackSignal(name='motor3', parent='temperature', value=0, timestamp=1682079021.6718569)\n", "\n", "\n", "```\n", " \n", " \n", "You will see that the beamline staff played a python trick and that `temperature` is really an alias for `motor3`. The code used to to this is at the bottom of [gm_user/user_profile.py](./gm_user/user_profile.py).\n", " \n", " \n", "
\n", "\n", "\n", "
#2 Copy Paste Code - solutions\n", " \n", "```python\n", "\n", "RE(mv(temperature, 40))\n", "\n", "\n", "```\n", "\n", "OR somewhat equivalently depending on the details of a real device\n", "\n", " \n", "\n", "```python\n", " \n", "RE(mv(temperature.setpoint, 40))\n", "\n", "\n", "```\n", "\n", "AND \n", "\n", " \n", "\n", " \n", " \n", "```python\n", "RE(mv(motor, 0))\n", "\n", "\n", "```\n", "
\n", "\n", "\n", "\n", " " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "## Use to start item #1\n", "temperature" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Notice that `temperature` is complex and using `.get()` and `.read()` are complicated. Below illustrates why `.read()` is preferred for low level coding." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "temperature.get()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "temperature.read()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "temperature.readback.read()[\"motor3\"][\"value\"]" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "Bluesky Feature: There is an easy way to move objects simultaneously.\n", "
" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "RE(mv(motor, 0, temperature, 40))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Hosted by Andi " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Assemble plans in 1 line\n", "\n", "
\n", "Watch Out for a DAQ Trap: \n", "
\n", "\n", "Earlier in [Good Morning Bluesky](./Good%20Morning%20Bluesky.ipynb), we discussed the reasons to not use:\n", "```python\n", "RE(mv(noisy_det.noise_multiplier, 0.1)); RE(scan([noisy_det], motor, -10, 10, 21)); RE(mv(motor, 0))\n", "```\n", "\n", "There are two basic methods to perform these steps in a single line:\n", "1. Active command line \"**p**lan **chain**ing\"\n", "2. Creating a custom plan **definition** in the python name space (i.e. \"memory of current session\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## **P**lan **chain**ing with **b**lusky**p**re**p**rocessors (bpp)\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#bpp.pchain?" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "RE(bpp.pchain( mv(noisy_det.noise_multiplier, 0.1), scan([noisy_det], motor, -5, 5, 11) , mv(motor, 0) ) )" ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "### Nice and Not So Nice Aspects of bpp.pchain \n", "\n", "\n", "
Nice\n", " \n", "- simple to assemble\n", " \n", "- use `Cntl R` terminal search to re-use\n", " \n", "- good way to test plans\n", "
\n", "\n", "
Not So Nice\n", " \n", "- hard to read/navigate, especially if many arguments are in `bpp.pchain()`\n", " \n", "- even harder to manage multiple unique versions of `bpp.pchain()`\n", " \n", "
" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Custom plan defintions\n", "\n", "Let's make a custom plan to perform the last RE call. We do this by making a python function.\n", "\n", "But what did we just do? **Don't scroll up.**" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%history -n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%history -n 6-22" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "From the above, we can easily create a simple plan that will take **FULL** advantage of bluesky" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def align_peak():\n", " yield from scan([noisy_det], motor, -5, 5, 11) \n", " yield from mv(motor, 0)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "RE(align_peak())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## The problem in a real experiment\n", "\n", "The peak will move as a function of temperature. In the next cells, we will:\n", "\n", "1. Simulate our plan **before** using the RE.\n", "2. Create a more robust plan for the specific experiment." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "Bluesky Feature: There are two essential functions to inspect plans before calling them with the RE.\n", "
\n", "\n", "\n", "### Simulate\n", "\n", "**Summarize the plan**\n", "\n", "It's easy. Just replace `RE` with `summarize_plan`." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [] }, "outputs": [], "source": [ "summarize_plan(align_peak())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`summarize_plan` is more for syntax checking and object verification. It does not check if the plan is actually feasilble.\n", "\n", "**Check the device software limits**\n", "\n", "To do this, replace `summarize_plan` with `check_limits`" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "check_limits(align_peak())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "No errors and a returned prompt are a great sign!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Position the motor on peak maximum\n", "\n", "Bluesky has a built in feature called `bec.peaks` (best effort callbacks).\n", "\n", "Try `bec.peaks` below and see what it is. What python data structure does this resemble?" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "bec.peaks" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Try to extract `motor` position at the scan's maximum value for `noisy_det`:\n", "\n", "\n", "\n", "\n", "
Copy Paste Solution\n", "\n", " \n", "```python\n", "\n", "print(bec.peaks[\"max\"][\"noisy_det\"][0])\n", "\n", "RE(mv(motor, bec.peaks[\"max\"][\"noisy_det\"][0]) )\n", "\n", "\n", "```\n", "
\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For more details on [Peak Stats](https://blueskyproject.io/bluesky/callbacks.html#best-effort-callback) (i.e., `bec.peaks`) or the alternative [LiveFit](https://blueskyproject.io/bluesky/callbacks.html#callbacks-for-visualization-fitting)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's finish our function" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def align_peak():\n", " yield from scan([noisy_det], motor, -5, 5, 11) \n", " yield from bps.sleep(3) #sometimes need this on actual hardware\n", " my_max = bec.peaks[\"max\"][\"noisy_det\"][0]\n", " yield from mv(motor, my_max)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [] }, "outputs": [], "source": [ "summarize_plan(align_peak())" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "Caution: Some beamlines have put effort into high level plans.\n", " \n", " \n", "* Not all beamline specific plans are compatible with the bluesky.simulation module.\n", " \n", "* Others have more automated alignment plans and those should be used as per the staff instructions.\n", "
\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Assemble a super plan for our experiment\n", "Our experiment's design is to:\n", "1. align a peak at a given temperature\n", "2. count at the signal maximum for some extended time (better statisics)\n", "3. change temperature and repeat Steps 1 and 2" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "RE(count([noisy_det], num = 1))" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "nbsphinx": "hidden" }, "outputs": [], "source": [ "def one_temperature():\n", " yield from align_peak()\n", " yield from count([noisy_det], 5)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's make sure this is correct by using `summarize_plan`.\n", "\n", "Note: We don't expect it to work. Can you figure out what is wrong? \n", "\n", "
Hint #1\n", "\n", "If the traceback isn't all that useful. Which functions `align_peak()` or `count()` sets a python variable?\n", " \n", "```python\n", "align_peak??\n", "\n", "\n", "```\n", "\n", "
\n", "\n", "\n", "\n", "
Hint #2\n", "\n", "Type `bec.peaks` after `RE(count([noisy_det], 5))`\n", "\n", "```python\n", "RE(count([noisy_det], 5))\n", "bec.peaks\n", "\n", "```\n", "\n", "
\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ " " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "metadata": { "scrolled": true, "tags": [] }, "outputs": [], "source": [ "summarize_plan(one_temperature())" ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "## The cause of this failed `summarize_plan`\n", "\n", "
Solution\n", "\n", "Not all plans are appropriate for `bec.peaks`. `count()` is one of them.\n", " \n", "Any time the `RE` is used and a \"scan\" is called, `bec.peaks` resets to wait for population at the end of the \"scan\".\n", "\n", "Here, we could get around this problem by just doing this:\n", "```python\n", "RE(scan([noisy_det], motor, -.5, .5, 3) )\n", "summarize_plan(one_temperature())\n", " \n", " \n", "```\n", "\n", "\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Another common cause of failed `summarize_plan`\n", "\n", "
Solution\n", "\n", "Performing a scan with a different detector before using `summarize_plan()`\n", " \n", "```python\n", "RE(scan([det], motor, -.5, .5, 3) )\n", "print(bec.peaks)\n", "summarize_plan(one_temperature())\n", " \n", " \n", "```\n", "\n", "\n", "
" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "scrolled": true, "tags": [] }, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Robust use of `bec.peaks`\n", "\n", "
\n", "Tip: Don't be shy about adding print() inside functions to help with debugging.\n", "
\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def align_peak():\n", " uid = yield from scan([noisy_det], motor, -5, 5, 11) \n", " if uid is not None:\n", " #print('doing')\n", " yield from bps.sleep(3) #sometimes need this on actual hardware\n", " my_max = bec.peaks[\"max\"][\"noisy_det\"][0]\n", " yield from mv(motor, my_max)\n", " #else:\n", " #print('passing')\n", " #pass" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [] }, "outputs": [], "source": [ "RE(scan([det], motor, -.5, .5, 3) )\n", "print(bec.peaks)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "scrolled": true, "tags": [] }, "outputs": [], "source": [ "summarize_plan(one_temperature())" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "Hosted by Josh" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### We have the major data collection plan\n", "\n", "Let's iterate over some temperatures." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "for myT in [43, 45, 47, 50, 51, 52, 53, 54]:\n", " print(myT)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "Tip: Easier ways to do this'\n", " \n", " - range()\n", " - \"list comprehension\"\n", "
\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "## Create and test our overnight script\n", "Try to modify the function below.\n", "\n", "
Solution\n", "\n", "This represents my ideal solution. There are variations of this that are also acceptable.\n", "\n", "Copy and Paste the below in one cell to see the difference\n", " \n", "```python\n", "def my_experiment(myT_list):\n", " for myT in myT_list:\n", " print(f'Changing temperature to {myT}')\n", " yield from mv(temperature, myT)\n", " yield from one_temperature()\n", " print(f'\\tFinished scans for temperature {myT}')\n", "\n", "summarize_plan(my_experiment([31,]))\n", "check_limits(my_experiment([31,]))\n", "```\n", "\n", "\n", "
" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "## FIX THE FUNCTION\n", "def my_experiment():\n", " yield from mv(temperature, ?)\n", " yield from one_temperature()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "summarize_plan(my_experiment())" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "check_limits(my_experiment())" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Questions about the above?" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## What's next\n", "\n", "\n", "\n", "* [Good Night Bluesky](./Good%20Night%20Bluesky.ipynb)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "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 }