Perform a simple scan with a data table and plot¶
Problem¶
Move a motor in equally spaced steps, stopping at each position to acquire a reading from one or more detectors.
Note
Interested in a different kind of scan? A simple “count”? A more complex
Fermat spiral? In the solution below, replace scan
with
one of these.
Approach¶
Set up a table and a plot, create a plan, and execute it. View the live stream of data on the table and the plot while it is collected.
Example Solution¶
As an example, we can use a simulated motor and detector. You can substitute any real motors or detectors that may already be defined in your configuration.
from bluesky.examples import motor, det
We will import a plan, which generates instructions to be executed. We’ll
also import LiveTable
and LivePlot
. Typically, this is done as part
your configuration (your IPython profile). It happens automatically at startup
and need not be typed every time.
from bluesky.plans import scan
from bluesky.callbacks import LiveTable, LivePlot
First, set up a table and a plot. The table takes a list of field names to show. The plot takes a field name for y followed (optionally) by a field name for x. (If only y is provided, it plots y against point number.)
In [1]: subs = [LiveTable(['motor', 'det']), LivePlot('det', 'motor')]
You can name the variable anything you like; we call it subs
, short for
“subscriptions.” They are functions that “subscribe” to the live stream of
data from the scan.
We’ll scan motor
from -10 to 10 inclusive, sampling 15 equally-spaced
points, and reading detector det
at each point. We specify this like so:
scan([det], motor, -10, 10, 15)
Notice that det
is in square brackets because scan
expects list of
one or more detectors.
Finally, this command tells the RunEngine, RE
, to execute our scan and send
the data to subs
.
In [2]: RE(scan([det], motor, -10, 10, 15), subs)
+-----------+------------+------------+------------+
| seq_num | time | motor | det |
+-----------+------------+------------+------------+
| 1 | 17:04:06.4 | -10.00 | 0.00 |
| 2 | 17:04:06.5 | -8.57 | 0.00 |
| 3 | 17:04:06.5 | -7.14 | 0.00 |
| 4 | 17:04:06.6 | -5.71 | 0.00 |
| 5 | 17:04:06.6 | -4.29 | 0.00 |
| 6 | 17:04:06.7 | -2.86 | 0.02 |
| 7 | 17:04:06.7 | -1.43 | 0.36 |
| 8 | 17:04:06.8 | 0.00 | 1.00 |
| 9 | 17:04:06.8 | 1.43 | 0.36 |
| 10 | 17:04:06.9 | 2.86 | 0.02 |
| 11 | 17:04:06.9 | 4.29 | 0.00 |
| 12 | 17:04:07.0 | 5.71 | 0.00 |
| 13 | 17:04:07.0 | 7.14 | 0.00 |
| 14 | 17:04:07.1 | 8.57 | 0.00 |
| 15 | 17:04:07.1 | 10.00 | 0.00 |
+-----------+------------+------------+------------+
generator scan ['94f215'] (scan num: 1)
Out[2]: ['94f2156b-fcde-4136-b449-e7cc44f97450']
This table prints line by line while the scan is executed. The bottom of the table shows additional information, the data type and name of the plan, the unique ID of the run, and an integer scan ID.
Note
Since other users can reset the scan ID to 1, you should not rely on it as a long-term reference to your data. Scribble down the first 5 or 6 characters of the unique ID instead.
While the table prints line by line, points appear on a plot one at a time. This is the final result:
Let’s look again at the output from our command. As shown above, it was
['73b23bd3-54ca-40a4-9a5f-46486e8f3a91']
It looks like gibberish, but it’s useful: it is a list of the unique ID(s) of the run(s) generated by the plan, and it allows usage like this:
run_ids = RE(scan([det], motor, -10, 10, 15), subs) # assign ids to a variable
some_function_that_analyzes_or_exports_data(run_ids)