Take successive readings spaced exponentially in time or space

Problem

Read a detector (or detectors) multiple times, with successive readings spaced 1, 2, 4, 8, 16, ... seconds apart. Or, similarly, move a motor is steps with an exponential spacing.

Approach

We will define an iterator that specifies the delays between readings.

Example Solution

Use the built-in function range to generate sequential integers.

range(5)  # generates 0, 1, 2, 3, 4

Modify each element like so:

(2**i for i in range(4))  # generates 1, 2, 4, 8, 16

The above is called a generator comprehension. It evaluates “lazily”, computing the result for one element at a time.

We can pass this expression to the delay parameter of a count plan.

from bluesky.plans import count
from bluesky.callbacks import LiveTable

plan = count([det], num=None, delay=(2**i for i in range(4)))

The setting num=None means, “Run until the end of list of delays.” Let’s attach a LiveTable to the output and watch it in action. Notice the readings’ timestamps.

In [1]: RE(plan, LiveTable([det]))
+-----------+------------+------------+
|   seq_num |       time |        det |
+-----------+------------+------------+
|         1 | 17:01:14.7 |       0.00 |
|         2 | 17:01:15.7 |       0.00 |
|         3 | 17:01:17.7 |       0.00 |
|         4 | 17:01:21.7 |       0.00 |
|         5 | 17:01:29.7 |       0.00 |
+-----------+------------+------------+
generator count ['af83d1'] (scan num: 1)
Out[1]: ['af83d181-fd75-49f6-aeb2-22422d5333bf']

We could use the same principle to arrange readings in space instead of time using a list_scan.

from blusky.plans import list_scan

# Take readings with motor moved positioned at 1, 2, 4, 8, 16.
plan = list_scan([det], motor, (2**i for i in range(4)))

Next, instead of giving a specific number of readings, let’s run count indefinitely — until interrupted by the user pressing Ctrl^C. Instead of range, which counts integers up to a given upper limit, we’ll use another built in function, itertools.count, which counts integers without bound.

import itertools

# This would run forever until interrupted (for example, with Ctrl^C).
plan = count([det], delay=(2**i for i in itertools.count(0)))

Note

Python provides many convenient tools for working with series (“iterators”). Many are available in the built-in module itertools, which is worth getting to know.