Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save danielballan/6913e1969be38e308b2649fabf1750b0 to your computer and use it in GitHub Desktop.
Save danielballan/6913e1969be38e308b2649fabf1750b0 to your computer and use it in GitHub Desktop.
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"# Experiments as Iterators: asyncio in Science\n",
"\n",
"<ul>\n",
"<li>Daniel Allan</li>\n",
"<li>Thomas Caswell</li>\n",
"<li>Kenneth Lauer</li>\n",
"</ul>\n",
"\n",
"<p>Brookhaven National Lab</p>\n",
"<p>This talk: http://tiny.cc/dba-scipy2016</p>\n",
"<p>Source: https://github.com/NSLS-II</p>\n",
"<p>Project Documentation: https://NSLS-II.github.io</p>\n",
"</center>"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"## Origin of this Project\n",
"\n",
"National Synchrontron Light Source II at Brookhaven National Lab\n",
"\n",
"![NSLS-II](https://www.bnl.gov/ps/images/NSLS2-arial-1080px.jpg)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"## NSLS-II\n",
"\n",
"* 12 semi-independent research groups, scaling up to 60 in ~5 years\n",
"* Scaling up to 19 Pb/year in \"expensive pixels\"\n",
"* No sacred data formats...\n",
"* ... but one validatable, extensible (NoSQL) schema for all:\n",
" * metadata\n",
" * data or *references* to data"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"## Data Acquisition Software Design Goals\n",
"\n",
"* Integrate with the **scipy stack**.\n",
"* Support **streaming** data analysis.\n",
"* Capture metadata to record\n",
" * a detailed **snapshot** of the hardware (all experiment state);\n",
" * and the scientist's **intention**, the meaning of the measurements.\n",
"* Make datasets **searchable** with rich queries on metadata and data.\n",
"* As much as possible, minimize inventing a domain-specific language."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"Traditional data acquisition script:\n",
"\n",
"```python\n",
"for i in range(5):\n",
" try:\n",
" put('MOTOR_ID', i)\n",
" value = get('DETECTOR_ID')\n",
" # bespoke I/O code\n",
" except:\n",
" # bespoke cleanup to ensure hardware safety\n",
"```\n",
"\n",
"Metadata is stuffed into filenames or custom headers."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"### ophyd (device abstraction layer)\n",
"\n",
"```python\n",
"In [3]: import ophyd\n",
"\n",
"In [4]: motor = ophyd.EpicsMotor('MOTOR_ID', name='motor')\n",
"\n",
"In [5]: motor.read()\n",
"Out[5]: {'motor':\n",
" {'value': 5.0,\n",
" 'timestamp': 1468325228.751564}}\n",
" \n",
"In [6]: motor.set(6.0)\n",
"```\n",
"\n",
"Devices are expected to support a common interface: `read`, `set`, `stop`, ....\n",
"\n",
"Our devices talk to EPICS, but yours could talk to LabView, RasberryPi, raw serial, etc."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"Devices provide human-friendly names (useful during analysis) and a hierarchical structure.\n",
"\n",
"```python\n",
"class MultiAxisMirror(ophyd.Device):\n",
" x = ophyd.Component(ophyd.EpicsMotor, ':X')\n",
" y = ophyd.Component(ophyd.EpicsMotor, ':Y')\n",
" pitch = ophyd.Component(ophyd.EpicsMotor, ':P')\n",
"\n",
"\n",
"mirror = MultiAxisMirror('SOME_ID', name='mirror')\n",
"\n",
"In [1]: mirror.read()\n",
"Out[1]: {'mirror_x': {'value': 1.0, ...},\n",
" ...: 'mirror_y': {'value': 1.5, ...},\n",
" ...: 'mirror_pitch': {'value': 0.3, ...}}\n",
"```\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"### bluesky (experiment specification/execution)\n",
"\n",
"New-style data acquisition program (more declarative, less imperative):\n",
"\n",
"```python\n",
"from bluesky.plans import (open_run, close_run,\n",
" abs_set,\n",
" trigger_and_read)\n",
" \n",
"def plan():\n",
" \"scan 'motor' from 1 to 5 while reading 'det'\"\n",
" yield from open_run(some_metadata_dict)\n",
" for i in range(5):\n",
" yield from abs_set(motor, i)\n",
" yield from trigger_and_read([det])\n",
" yield from close_run()\n",
"\n",
"RE(plan()) # execute (I/O and cleanup for free!)\n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"### A Two-Slide Crash Course in ``yield`` and ``yield from``"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [],
"source": [
"# Python 2.5+ (PEP 342)\n",
"\n",
"def g():\n",
" # g is a 'generator'\n",
" yield 1\n",
" yield 2\n",
" \n",
"a = g() # a is a 'generator instance'"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [],
"source": [
"next(a)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [],
"source": [
"next(a)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [],
"source": [
"list(g())"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [],
"source": [
"# Python 3.3+ (PEP 380)\n",
"\n",
"def h():\n",
" yield 0\n",
" yield from g()\n",
" yield 4\n",
"\n",
"list(h())"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"Go watch James Powell's *Generators Will Free Your Mind* on YouTube!"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"## The rest of this talk:\n",
"\n",
"(1) How the **RunEngine** `RE` executes the instructions in the `plan`\n",
"\n",
"(2) Neat implications of expressing a **science experiment as a generator**\n",
"\n",
"(3) Reliably **capturing state and semantics** is a critical input to reproducible workflows in experimental science"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## The RunEngine from Scratch\n",
"\n",
"In Which We Built Progressively More Complex Implementations"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"### A 'plan' is an iterable of 'messages'\n",
"\n",
"A `Msg` is a `namedtuple` with four fields:\n",
"\n",
"* a **command**, given as a string, e.g., ``'set'`` or ``'sleep'``\n",
"* a target **obj**, e.g., ``motor`` (if applicable)\n",
"* positional **args**\n",
"* **kwargs**"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [],
"source": [
"from bluesky import Msg\n",
"\n",
"Msg('sleep', None, 1)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"### Version 0: the simplest possible RunEngine"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [],
"source": [
"import time\n",
"from bluesky import Msg\n",
"\n",
"function_map = {'sleep': lambda msg: time.sleep(*msg.args),\n",
" 'print': lambda msg: print(*msg.args)}\n",
"\n",
"def RE_v0(plan):\n",
" for msg in plan:\n",
" print('PROCESSING: %r' % (msg,))\n",
" func = function_map[msg.command]\n",
" func(msg)\n",
" \n",
"sleepy_plan = [Msg('sleep', None, 1),\n",
" Msg('print', None, 'HELLO WORLD')]\n",
"\n",
"RE_v0(sleepy_plan)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"### Version 1: a RunEngine that supports adaptive plan logic"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [],
"source": [
"from bluesky.utils import ensure_generator\n",
"\n",
"def RE_v1(plan):\n",
" plan = ensure_generator(plan)\n",
" last_result = None\n",
"\n",
" while True:\n",
" try:\n",
" msg = plan.send(last_result)\n",
" except StopIteration:\n",
" break\n",
" print('PROCESSING: %r' % (msg,))\n",
" func = function_map[msg.command]\n",
" last_result = func(msg)\n",
"\n",
"function_map['sum'] = lambda msg: sum(msg.args)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [],
"source": [
"def adding_plan():\n",
" \"Ask the RunEngine to add to numbers. Print the result.\"\n",
" yield Msg('sleep', None, 1)\n",
" result = yield Msg('sum', None, 3, 4)\n",
" print('RECEIVED:', result)\n",
" \n",
"RE_v1(adding_plan())"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [],
"source": [
"def adaptive_adding_plan():\n",
" \"Keep adding 3 until the result is greater than 8.\"\n",
" result = 1\n",
" while True:\n",
" yield Msg('sleep', None, 1)\n",
" result = yield Msg('sum', None, result, 3)\n",
" print('RECEIVED:', result)\n",
" if result > 8:\n",
" print('we are done')\n",
" break\n",
" \n",
"RE_v1(adaptive_adding_plan())"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"### Version 2: refactor as a callable class\n",
"\n",
"A class, unlike a simple function, gives us access to the internal state."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [],
"source": [
"class RunEngine_v2:\n",
" def __call__(self, plan):\n",
" self._run(plan)\n",
" \n",
" def _run(self, plan):\n",
" plan = ensure_generator(plan)\n",
" last_result = None\n",
"\n",
" while True:\n",
" try:\n",
" msg = plan.send(last_result)\n",
" except StopIteration:\n",
" break\n",
" print('PROCESSING: %r' % (msg,))\n",
" func = function_map[msg.command]\n",
" last_result = func(msg)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [],
"source": [
"# It still works.\n",
"RE_v2 = RunEngine_v2()\n",
"RE_v2(adaptive_adding_plan())"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"### Version 3: toward a RunEngine that supports interruptions / resuming\n",
"\n",
"* The RunEngine still manages the main loop, processing the plan\n",
"* asyncio provides an outer event loop that manages multiple frames of execution"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [],
"source": [
"import asyncio\n",
"\n",
"loop = asyncio.get_event_loop()\n",
"\n",
"# Reimplement all command functions as coroutines.\n",
"\n",
"@asyncio.coroutine\n",
"def _sum(msg):\n",
" return sum(msg.args)\n",
"\n",
"@asyncio.coroutine\n",
"def _sleep(msg):\n",
" yield from asyncio.sleep(*msg.args, loop=loop)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true,
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [],
"source": [
"class RunEngine_v3:\n",
" def __init__(self):\n",
" loop = asyncio.new_event_loop()\n",
" self.coroutine_map = {'sleep': _sleep,\n",
" 'sum': _sum}\n",
" \n",
" def __call__(self, plan):\n",
" self._task = loop.create_task(self._run(plan))\n",
" loop.run_until_complete(self._task)\n",
" \n",
" if self._task.done() and not self._task.cancelled():\n",
" exc = self._task.exception()\n",
" if exc is not None:\n",
" raise exc\n",
" \n",
" @asyncio.coroutine\n",
" def _run(self, plan):\n",
" plan = ensure_generator(plan)\n",
" last_result = None\n",
"\n",
" while True:\n",
" try:\n",
" msg = plan.send(last_result)\n",
" except StopIteration:\n",
" break\n",
" print('PROCESSING: %r' % (msg,))\n",
" coroutine = self.coroutine_map[msg.command]\n",
" last_result = yield from coroutine(msg)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [],
"source": [
"# And it still works\n",
"RE_v3 = RunEngine_v3()\n",
"RE_v3(adaptive_adding_plan())"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## Execution can be interrupted and cleanly resumed\n",
"\n",
"* Interrupt interactively with Ctrl+C\n",
"* Interrupt programmatically from the plan or in response to some external condition."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [],
"source": [
"from bluesky import RunEngine # finally, the real thing\n",
"\n",
"# Make a RunEngine.\n",
"RE = RunEngine({})\n",
"\n",
"# Teach it our toy command, 'sum'.\n",
"@asyncio.coroutine\n",
"def _sum(msg):\n",
" return sum(msg.args)\n",
"\n",
"RE.register_command('sum', _sum)\n",
" \n",
"# Make it verbose, as our demo versions were.\n",
"RE.msg_hook = lambda msg: print(\"PROCESSING:\", msg)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false,
"scrolled": true,
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [],
"source": [
"# Demo capturing Ctrl+C\n",
"\n",
"RE(adaptive_adding_plan())"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [],
"source": [
"# RunEngine \"rewinds\" its cache of messages and\n",
"# starts again from the beginning.\n",
"RE.resume()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true,
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [],
"source": [
"from itertools import count\n",
"\n",
"def pausing_plan():\n",
" \"Pause after the second iteration and let the user resume or abort.\"\n",
" ret = 1\n",
" for i in count():\n",
" yield Msg('sleep', None, 1)\n",
" if i == 1:\n",
" yield Msg('pause')\n",
" ret = yield Msg('sum', None, ret, 3)\n",
" print('RECEIVED:', ret)\n",
" if ret > 8:\n",
" print('we are done')\n",
" break"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false,
"scrolled": false,
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [],
"source": [
"RE(pausing_plan())"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [],
"source": [
"# RunEngine resumes from where it left off.\n",
"RE.resume()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true,
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [],
"source": [
"from itertools import count\n",
"\n",
"def pausing_plan_with_checkpoints():\n",
" \"Pause after the second iteration and let the user resume or abort.\"\n",
" ret = 1\n",
" for i in count():\n",
" yield Msg('checkpoint') # NEW\n",
" yield Msg('sleep', None, 1)\n",
" if i == 1:\n",
" yield Msg('pause')\n",
" ret = yield Msg('sum', None, ret, 3)\n",
" print('RECEIVED:', ret)\n",
" if ret > 8:\n",
" print('we are done')\n",
" break"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false,
"scrolled": true,
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [],
"source": [
"RE(pausing_plan_with_checkpoints())"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [],
"source": [
"RE.resume()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [],
"source": [
"RE(pausing_plan_with_checkpoints())\n",
"RE.abort()"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## Plans can handle exceptions"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false,
"scrolled": false,
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [],
"source": [
"def problematic_plan():\n",
" yield Msg('sleep', None, 1)\n",
" print(\"ERROR!\")\n",
" raise Exception(\"Problematic!\")\n",
" \n",
"RE(problematic_plan())"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [],
"source": [
"from bluesky.plans import finalize_wrapper\n",
"\n",
"def cleanup():\n",
" # e.g., move motors back to safe positions.\n",
" yield Msg('sleep', None, 2)\n",
" print(\"Everything has been made safe.\")\n",
" \n",
"def make_safe(plan):\n",
" yield from finalize_wrapper(plan, cleanup())\n",
"\n",
"# cleanup() will be yielded from before\n",
"# exception is re-raised.\n",
"RE(make_safe(problematic_plan()))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [],
"source": [
"from bluesky.plans import finalize_wrapper\n",
" \n",
"def raises_in_RE():\n",
" \"will cause an error inside _sleep() coroutine\"\n",
" yield Msg('sleep', None, 'a')\n",
"\n",
"RE(raises_in_RE())"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [],
"source": [
"# cleanup() will be yielded from before\n",
"# exception is re-raised\n",
"RE(make_safe(raises_in_RE()))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [],
"source": [
"def reckless_plan():\n",
" try:\n",
" yield from raises_in_RE()\n",
" except Exception as exc:\n",
" print(\"Ignoring:\", exc)\n",
" yield Msg('sleep', None, 1)\n",
" \n",
"RE(reckless_plan())"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## The RunEngine collates all metadata and data into validated dicts\n",
"\n",
"* Dicts contain metadata and data.\n",
"* They are validated against a JSON schema.\n",
"* Dicts are dispatched to a list of \"subscribers\".\n",
" * Subscribers can be blocking or non-blocking.\n",
" * Subscribers can be lossy.\n",
"\n",
"Examples:\n",
"* Insert dicts into to NoSQL database.\n",
"* Print, plot, or produce log entries.\n",
"* Write CSV files or HDF5 files."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [],
"source": [
"RE.msg_hook = None # turn off verbosity\n",
"\n",
"# Use synthetic hardware objects.\n",
"from bluesky.examples import det, motor\n",
"motor._fake_sleep = 0.25 # sim moving time\n",
"\n",
"from bluesky.plans import (open_run, close_run,\n",
" abs_set,\n",
" trigger_and_read)\n",
"\n",
"def plan():\n",
" \"scan 'motor' from 1 to 5 while reading 'detector'\"\n",
" yield from open_run({'some_metadata': 'hello world'})\n",
" for i in range(5):\n",
" yield from abs_set(motor, i)\n",
" yield from trigger_and_read([det, motor])\n",
" yield from close_run()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [],
"source": [
"RE(plan())"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [],
"source": [
"RE(plan(), print)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [],
"source": [
"from bluesky.callbacks import LiveTable\n",
"\n",
"RE(plan(), LiveTable([motor, det]))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [],
"source": [
"%matplotlib notebook\n",
"import matplotlib.pyplot as plt"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false,
"scrolled": false,
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [],
"source": [
"from bluesky.callbacks import LivePlot\n",
"\n",
"RE(plan(), LivePlot('det', 'motor'))"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## More Use Cases: Adaptive Steps and Nested Generators\n",
"\n",
"Behold the Power of the Fully *Operational* RunEngine"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [],
"source": [
"plt.figure();"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [],
"source": [
"from bluesky.plans import adaptive_scan\n",
"motor._fake_sleep = 0\n",
"\n",
"RE(adaptive_scan([det], 'det', motor, -10, 10, 0.1, 5, 0.1, True),\n",
" LivePlot('det', 'motor', marker='o'))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [],
"source": [
"RE(plan(), LiveTable([motor, det]))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [],
"source": [
"from bluesky.plans import relative_set_wrapper\n",
"\n",
"# We left motor at position 4.0 above.\n",
"\n",
"RE(relative_set_wrapper(plan()), LiveTable([motor, det]))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [],
"source": [
"from bluesky.plans import msg_mutator\n",
"\n",
"def triple_sleep(msg):\n",
" \"Alter any sleep messages to triple the sleep time.\"\n",
" if msg.command == 'sleep':\n",
" t, = msg.args\n",
" new_msg = msg._replace(args=(3 * t,))\n",
" return new_msg\n",
" else:\n",
" return msg\n",
"\n",
"RE.msg_hook = print\n",
"RE(msg_mutator(adding_plan(), triple_sleep))"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## Additional Info\n",
"\n",
"<p>This talk: http://tiny.cc/dba-scipy2016</p>\n",
"<p>Source: https://github.com/NSLS-II</p>\n",
"<p>Project Documentation: https://NSLS-II.github.io</p>\n",
"<p>Document Model: https://NSLS-II.github.io/architecture-overview.html</p>"
]
}
],
"metadata": {
"anaconda-cloud": {},
"celltoolbar": "Slideshow",
"kernelspec": {
"display_name": "Python [bnl]",
"language": "python",
"name": "Python [bnl]"
},
"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.5.2"
},
"nbpresent": {
"slides": {
"dbc322bf-e91e-46c0-bfbc-0e7360629b7b": {
"id": "dbc322bf-e91e-46c0-bfbc-0e7360629b7b",
"prev": "ffd53d27-ea32-4370-bd07-35b600c3ab58",
"regions": {
"d3eb6fc4-5017-4091-8686-1348d63cfd3a": {
"attrs": {
"height": 1,
"width": 1,
"x": 0,
"y": 0
},
"id": "d3eb6fc4-5017-4091-8686-1348d63cfd3a"
}
}
},
"ffd53d27-ea32-4370-bd07-35b600c3ab58": {
"id": "ffd53d27-ea32-4370-bd07-35b600c3ab58",
"layout": "grid",
"prev": null,
"regions": {
"558cca0b-969a-417b-bbc3-2caf93e21cc5": {
"attrs": {
"height": 0.8333333333333334,
"pad": 0.01,
"width": 0.8333333333333334,
"x": 0.08333333333333333,
"y": 0.08333333333333333
},
"id": "558cca0b-969a-417b-bbc3-2caf93e21cc5"
},
"c0806422-85fb-4629-bd67-86d1fd3809e8": {
"attrs": {
"height": 1,
"pad": 0.01,
"width": 1,
"x": 0,
"y": 0
},
"id": "c0806422-85fb-4629-bd67-86d1fd3809e8"
}
}
}
},
"themes": {}
}
},
"nbformat": 4,
"nbformat_minor": 0
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment