IPython ‘Magics’ [Experimental]#
Warning
This section covers an experimental feature of bluesky. It may be altered or removed in the future.
What Are ‘Magics’?#
IPython is an interactive Python interpreter designed for and by scientists. It includes a feature called “magics” — convenience commands that aren’t part of the Python language itself. For example, %history
is a magic:
In [1]: a = 1
In [2]: b = 2
In [3]: %history
a = 1
b = 2
%history
The IPython documentation documents the complete list of built-in magics and, further, how to define custom magics.
Bluesky Magics#
Bundled with bluesky are some IPython magics. They are intended for maintenance tasks or casual sanity checks. Intentionally, none of the magics save data; for that you should use the RunEngine and plans.
To use the magics, first register them with IPython:
In [4]: from bluesky.magics import BlueskyMagics
In [5]: get_ipython().register_magics(BlueskyMagics)
For this example we’ll use some simulated hardware.
In [6]: from ophyd.sim import motor1, motor2
Moving a Motor#
Suppose you want to move a motor interactively. You can use the %mov
magic:
In [7]: %mov motor1 42
Where motor1
refers to the actual ophyd object itself.
This is equivanent to:
from bluesky.plan_stubs import mv
RE(mv(motor1, 42))
but less to type. There is also a %movr
magic for “relative move”. They can
move multiple devices in parallel like so:
In [8]: %mov motor1 -3 motor2 3
Note: Magics has changed from version v1.3.0 onwards. The previous method will be described in the next section.
Taking a reading using %ct
(Post v1.3.0)#
Before we may make use of the power of magics for counting, we must “label”
this hardware. To add a label, we must give hardware a labels={'mylabel'}
keyword argument. For example, here we initialize five simulated signals: two
motors, a shutter motor, an area detector and a point detector:
In [9]: import numpy as np
In [10]: from ophyd.sim import SynAxis, SynSignal
In [11]: motor1 = SynAxis(name='motor1', labels={'motors', 'scan_motors'})
In [12]: motor2 = SynAxis(name='motor2', labels={'motors', 'scan_motors'})
In [13]: shutter_motor = SynAxis(name='shutter_motor', labels={'motors', 'shutter_motors'})
# create a fake area detector that returns a 2x2 array
In [14]: area_detector = SynSignal(func=lambda: np.random.random((2, 2)),
....: name='adet1', labels={'detectors', 'area_detectors'})
....:
In [15]: point_detector = SynSignal(func=lambda: np.random.random((1,)),
....: name='pointdet1', labels={'detectors', 'point_detectors'})
....:
Now we have detectors and motors, with proper labels.
Now suppose you want to take a quick reading of some devices and print the
results to the screen without saving them or doing any fancy processing. Use
the %ct
magic:
In [16]: %ct area_detectors
[This data will not be saved. Use the RunEngine to collect data.]
adet1 [[0.57801147 0.24488686]
[0.47721969 0.25849742]]
Where the names after count are a list of whitespace separated labels. In this
case, only area_detector
will be counted.
Running %ct
without arguments looks for the detectors
label by default:
In [17]: %ct
[This data will not be saved. Use the RunEngine to collect data.]
adet1 [[0.23820747 0.23426097]
[0.82065556 0.07585264]]
det 3.726653172078671e-06
det1 7.614989872356314e-08
det2 1.2130613194252668
pointdet1 [0.7162506]
In this case, we count both on the area detector and the point detector.
Aside on the automagic feature in IPython#
If IPython’s ‘automagic’ feature is enabled, IPython will even let you drop the
%
as long as the meaning is unambiguous:
In [18]: ct
[This data will not be saved. Use the RunEngine to collect data.]
adet1 [[0.82721134 0.98317635]
[0.32649217 0.1777415 ]]
det 3.726653172078671e-06
det1 7.614989872356314e-08
det2 1.2130613194252668
pointdet1 [0.78722087]
In [19]: ct = 3 # Now ct is a variable so automagic will not work...
In [20]: ct
Out[20]: 3
# ... but the magic still works.
In [21]: %ct
[This data will not be saved. Use the RunEngine to collect data.]
adet1 [[0.49810233 0.76599466]
[0.31491743 0.28645461]]
det 3.726653172078671e-06
det1 7.614989872356314e-08
det2 1.2130613194252668
pointdet1 [0.85164937]
For what it’s worth, we recommend disabling ‘automagic’. The %
is useful
for flagging what follows as magical, non-Python code.
Listing available motors using %wa
(Post v1.3.0)#
Finally, the %wa
magic displays the a list of labeled devices.
In [22]: %wa scan_motors
scan_motors
Positioner Value Low Limit High Limit Offset
motor1 0 AttributeError AttributeError AttributeError
motor2 0 AttributeError AttributeError AttributeError
Local variable name Ophyd name (to be recorded as metadata)
motor1 motor1
motor2 motor2
will display all motors used for a scan. If blank, will print all labeled devices.
In [23]: %wa
motors
Positioner Value Low Limit High Limit Offset
motor 5.0 AttributeError AttributeError AttributeError
motor1 0 AttributeError AttributeError AttributeError
motor2 0 AttributeError AttributeError AttributeError
shutter_motor 0 AttributeError AttributeError AttributeError
Local variable name Ophyd name (to be recorded as metadata)
motor motor
motor1 motor1
motor2 motor2
shutter_motor shutter_motor
detectors
Local variable name Ophyd name (to be recorded as metadata)
area_detector adet1
det det
det1 det1
det2 det2
point_detector pointdet1
scan_motors
Positioner Value Low Limit High Limit Offset
motor1 0 AttributeError AttributeError AttributeError
motor2 0 AttributeError AttributeError AttributeError
Local variable name Ophyd name (to be recorded as metadata)
motor1 motor1
motor2 motor2
shutter_motors
Positioner Value Low Limit High Limit Offset
shutter_motor 0 AttributeError AttributeError AttributeError
Local variable name Ophyd name (to be recorded as metadata)
shutter_motor shutter_motor
area_detectors
Local variable name Ophyd name (to be recorded as metadata)
area_detector adet1
point_detectors
Local variable name Ophyd name (to be recorded as metadata)
point_detector pointdet1
Note: It is possible to give a device more than one label. Thus it is possible
to have the same device in more than one list when calling %wa
. It is up to
the user to decide whether they want overlapping labels or not.
Comparison with SPEC#
The names of these magics, and the order of the parameters they take, are meant to feel familiar to users of SPEC.
Again, they must be registered with IPython before they can be used:
from bluesky.magics import BlueskyMagics
get_ipython().register_magics(BlueskyMagics)
Taking a reading using %ct
(Pre v1.3.0)#
Previously, you could set a default list of detectors and them use %ct
without any parameters. This behaviour is deprecated. Do not use this:
In [24]: BlueskyMagics.detectors = [area_detector, point_detector]
In [25]: %ct
[This data will not be saved. Use the RunEngine to collect data.]
adet1 [[0.08087698 0.21349331]
[0.39148428 0.52999515]]
pointdet1 [0.72546642]
This is no longer supported.
Listing available motors using %wa
(Pre v1.3.0)#
Previously, it was possible to supply a list of motors. This feature is also deprecated. Do not use this:
In [26]: BlueskyMagics.positioners = [motor1, motor2]
In [27]: %wa
Positioner Value Low Limit High Limit Offset
motor1 0 AttributeError AttributeError AttributeError
motor2 0 AttributeError AttributeError AttributeError