Lesson 2: motor and scan#
In this lesson, we’ll work with a motor (for precise positioning) in Bluesky and related tools.
First, we’ll connect with an EPICS motor (using ophyd), and then use the Bluesky software to scan the motor (with the scaler from lesson 1).
Load ophyd
device support for the EpicsMotor
and connect with one EPICS motor channel. We have a synApps (v6.1) XXX-style IOC with the prefix sky:
. It has a scaler, 16 soft channel motors, and some other support we’ll ignore in this lesson.
note: This tutorial expects to find an EPICS IOC on the local network configured as a synApps xxx IOC with prefix sky:
. A docker container is available to provide this IOC. See this URL for instructions: prjemian/epics-docker
[1]:
from ophyd import EpicsMotor
m1 = EpicsMotor("sky:m1", name="m1")
m1.wait_for_connection()
Show the current value fo the motor (that’s the .RBV
field, in case you were interested).
[2]:
m1.position
[2]:
1.0
Connect the scaler (as was done in lesson 1). Define some of the chaannel names and clear out others.
[3]:
from ophyd.scaler import ScalerCH
scaler = ScalerCH("sky:scaler1", name="scaler")
scaler.wait_for_connection()
# Since there are no detectors actually connected to this scaler,
# we can change names at our choice. A real scaler will have
# detectors connected to specific channels and we should not modify
# these names without regard to how signals are physically connected.
scaler.channels.chan01.chname.put("clock")
scaler.channels.chan02.chname.put("I0")
scaler.channels.chan03.chname.put("scint")
scaler.channels.chan04.chname.put("")
scaler.channels.chan05.chname.put("")
scaler.channels.chan06.chname.put("")
scaler.channels.chan07.chname.put("")
scaler.channels.chan08.chname.put("")
scaler.channels.chan09.chname.put("")
Use only the channels with EPICS names, those are the interesting channels.
[4]:
scaler.select_channels(None)
scaler.read()
[4]:
OrderedDict([('clock', {'value': 11000000.0, 'timestamp': 1616788676.931333}),
('I0', {'value': 6.0, 'timestamp': 1616788676.931333}),
('scint', {'value': 5.0, 'timestamp': 1616788676.931333}),
('ROI1', {'value': 0.0, 'timestamp': 1616788676.931333}),
('ROI2', {'value': 0.0, 'timestamp': 1616788676.931333}),
('scaler_time', {'value': 1.1, 'timestamp': 1616788676.931333})])
Create a RunEngine but do not connect it with a data collection strategy. That will come in the next lessons.
[5]:
from bluesky import RunEngine
import bluesky.plans as bp
RE = RunEngine({})
Run a step scan using the motor and the scaler.
[6]:
RE(bp.scan([scaler], m1, -1, 1, 5))
[6]:
('bef2817a-22de-4780-a38f-3c64f882f99b',)
Ah, yes. Nothing to see here since we did not setup anything to receive the documents from the RunEngine. Here’s the basic callback from lesson 1.
[7]:
import pprint
def myCallback(key, doc):
print()
print(key, len(doc))
pprint.pprint(doc)
Repeat the same scan but handle the document stream with myCallback()
.
[8]:
RE(bp.scan([scaler], m1, -1, 1, 5), myCallback)
start 15
{'detectors': ['scaler'],
'hints': {'dimensions': [(['m1'], 'primary')]},
'motors': ('m1',),
'num_intervals': 4,
'num_points': 5,
'plan_args': {'args': ["EpicsMotor(prefix='sky:m1', name='m1', "
'settle_time=0.0, timeout=None, '
"read_attrs=['user_readback', 'user_setpoint'], "
"configuration_attrs=['user_offset', "
"'user_offset_dir', 'velocity', 'acceleration', "
"'motor_egu'])",
-1,
1],
'detectors': ["ScalerCH(prefix='sky:scaler1', name='scaler', "
"read_attrs=['channels', 'channels.chan01', "
"'channels.chan01.s', 'channels.chan02', "
"'channels.chan02.s', 'channels.chan03', "
"'channels.chan03.s', 'channels.chan10', "
"'channels.chan10.s', 'channels.chan11', "
"'channels.chan11.s', 'time'], "
"configuration_attrs=['channels', "
"'channels.chan01', 'channels.chan01.chname', "
"'channels.chan01.preset', "
"'channels.chan01.gate', 'channels.chan02', "
"'channels.chan02.chname', "
"'channels.chan02.preset', "
"'channels.chan02.gate', 'channels.chan03', "
"'channels.chan03.chname', "
"'channels.chan03.preset', "
"'channels.chan03.gate', 'channels.chan10', "
"'channels.chan10.chname', "
"'channels.chan10.preset', "
"'channels.chan10.gate', 'channels.chan11', "
"'channels.chan11.chname', "
"'channels.chan11.preset', "
"'channels.chan11.gate', 'count_mode', 'delay', "
"'auto_count_delay', 'freq', 'preset_time', "
"'auto_count_time', 'egu'])"],
'num': 5,
'per_step': 'None'},
'plan_name': 'scan',
'plan_pattern': 'inner_product',
'plan_pattern_args': {'args': ["EpicsMotor(prefix='sky:m1', name='m1', "
'settle_time=0.0, timeout=None, '
"read_attrs=['user_readback', "
"'user_setpoint'], "
"configuration_attrs=['user_offset', "
"'user_offset_dir', 'velocity', "
"'acceleration', 'motor_egu'])",
-1,
1],
'num': 5},
'plan_pattern_module': 'bluesky.plan_patterns',
'plan_type': 'generator',
'scan_id': 2,
'time': 1616788927.1139975,
'uid': '4c2e9fe2-3bed-4ae4-99e9-66f0cd006a67',
'versions': {'bluesky': '1.6.7', 'ophyd': '1.6.1'}}
descriptor 8
{'configuration': {'m1': {'data': {'m1_acceleration': 0.2,
'm1_motor_egu': 'degrees',
'm1_user_offset': 0.0,
'm1_user_offset_dir': 0,
'm1_velocity': 1.0},
'data_keys': OrderedDict([('m1_user_offset',
{'dtype': 'number',
'lower_ctrl_limit': -1e+300,
'precision': 5,
'shape': [],
'source': 'PV:sky:m1.OFF',
'units': 'degrees',
'upper_ctrl_limit': 1e+300}),
('m1_user_offset_dir',
{'dtype': 'integer',
'enum_strs': ('Pos',
'Neg'),
'lower_ctrl_limit': None,
'shape': [],
'source': 'PV:sky:m1.DIR',
'units': None,
'upper_ctrl_limit': None}),
('m1_velocity',
{'dtype': 'number',
'lower_ctrl_limit': 0.1,
'precision': 5,
'shape': [],
'source': 'PV:sky:m1.VELO',
'units': 'degrees',
'upper_ctrl_limit': 0.0}),
('m1_acceleration',
{'dtype': 'number',
'lower_ctrl_limit': -1e+300,
'precision': 5,
'shape': [],
'source': 'PV:sky:m1.ACCL',
'units': 'sec',
'upper_ctrl_limit': 1e+300}),
('m1_motor_egu',
{'dtype': 'string',
'lower_ctrl_limit': None,
'shape': [],
'source': 'PV:sky:m1.EGU',
'units': None,
'upper_ctrl_limit': None})]),
'timestamps': {'m1_acceleration': 1616788677.05184,
'm1_motor_egu': 1616788677.05184,
'm1_user_offset': 1616788677.05184,
'm1_user_offset_dir': 1616788677.05184,
'm1_velocity': 1616788677.05184}},
'scaler': {'data': {'scaler_auto_count_delay': 0.0,
'scaler_auto_count_time': 1.0,
'scaler_channels_chan01_chname': 'clock',
'scaler_channels_chan01_gate': 'Y',
'scaler_channels_chan01_preset': 10000000.0,
'scaler_channels_chan02_chname': 'I0',
'scaler_channels_chan02_gate': 'N',
'scaler_channels_chan02_preset': 0.0,
'scaler_channels_chan03_chname': 'scint',
'scaler_channels_chan03_gate': 'N',
'scaler_channels_chan03_preset': 0.0,
'scaler_channels_chan10_chname': 'ROI1',
'scaler_channels_chan10_gate': 'N',
'scaler_channels_chan10_preset': 0.0,
'scaler_channels_chan11_chname': 'ROI2',
'scaler_channels_chan11_gate': 'N',
'scaler_channels_chan11_preset': 0.0,
'scaler_count_mode': 'OneShot',
'scaler_delay': 0.0,
'scaler_egu': '',
'scaler_freq': 10000000.0,
'scaler_preset_time': 1.0},
'data_keys': OrderedDict([('scaler_channels_chan01_chname',
{'dtype': 'string',
'lower_ctrl_limit': None,
'shape': [],
'source': 'PV:sky:scaler1.NM1',
'units': None,
'upper_ctrl_limit': None}),
('scaler_channels_chan01_preset',
{'dtype': 'number',
'lower_ctrl_limit': 0.0,
'precision': 0,
'shape': [],
'source': 'PV:sky:scaler1.PR1',
'units': '',
'upper_ctrl_limit': 0.0}),
('scaler_channels_chan01_gate',
{'dtype': 'string',
'enum_strs': ('N',
'Y'),
'lower_ctrl_limit': None,
'shape': [],
'source': 'PV:sky:scaler1.G1',
'units': None,
'upper_ctrl_limit': None}),
('scaler_channels_chan02_chname',
{'dtype': 'string',
'lower_ctrl_limit': None,
'shape': [],
'source': 'PV:sky:scaler1.NM2',
'units': None,
'upper_ctrl_limit': None}),
('scaler_channels_chan02_preset',
{'dtype': 'number',
'lower_ctrl_limit': 0.0,
'precision': 0,
'shape': [],
'source': 'PV:sky:scaler1.PR2',
'units': '',
'upper_ctrl_limit': 0.0}),
('scaler_channels_chan02_gate',
{'dtype': 'string',
'enum_strs': ('N',
'Y'),
'lower_ctrl_limit': None,
'shape': [],
'source': 'PV:sky:scaler1.G2',
'units': None,
'upper_ctrl_limit': None}),
('scaler_channels_chan03_chname',
{'dtype': 'string',
'lower_ctrl_limit': None,
'shape': [],
'source': 'PV:sky:scaler1.NM3',
'units': None,
'upper_ctrl_limit': None}),
('scaler_channels_chan03_preset',
{'dtype': 'number',
'lower_ctrl_limit': 0.0,
'precision': 0,
'shape': [],
'source': 'PV:sky:scaler1.PR3',
'units': '',
'upper_ctrl_limit': 0.0}),
('scaler_channels_chan03_gate',
{'dtype': 'string',
'enum_strs': ('N',
'Y'),
'lower_ctrl_limit': None,
'shape': [],
'source': 'PV:sky:scaler1.G3',
'units': None,
'upper_ctrl_limit': None}),
('scaler_channels_chan10_chname',
{'dtype': 'string',
'lower_ctrl_limit': None,
'shape': [],
'source': 'PV:sky:scaler1.NM10',
'units': None,
'upper_ctrl_limit': None}),
('scaler_channels_chan10_preset',
{'dtype': 'number',
'lower_ctrl_limit': 0.0,
'precision': 0,
'shape': [],
'source': 'PV:sky:scaler1.PR10',
'units': '',
'upper_ctrl_limit': 0.0}),
('scaler_channels_chan10_gate',
{'dtype': 'string',
'enum_strs': ('N',
'Y'),
'lower_ctrl_limit': None,
'shape': [],
'source': 'PV:sky:scaler1.G10',
'units': None,
'upper_ctrl_limit': None}),
('scaler_channels_chan11_chname',
{'dtype': 'string',
'lower_ctrl_limit': None,
'shape': [],
'source': 'PV:sky:scaler1.NM11',
'units': None,
'upper_ctrl_limit': None}),
('scaler_channels_chan11_preset',
{'dtype': 'number',
'lower_ctrl_limit': 0.0,
'precision': 0,
'shape': [],
'source': 'PV:sky:scaler1.PR11',
'units': '',
'upper_ctrl_limit': 0.0}),
('scaler_channels_chan11_gate',
{'dtype': 'string',
'enum_strs': ('N',
'Y'),
'lower_ctrl_limit': None,
'shape': [],
'source': 'PV:sky:scaler1.G11',
'units': None,
'upper_ctrl_limit': None}),
('scaler_count_mode',
{'dtype': 'string',
'enum_strs': ('OneShot',
'AutoCount'),
'lower_ctrl_limit': None,
'shape': [],
'source': 'PV:sky:scaler1.CONT',
'units': None,
'upper_ctrl_limit': None}),
('scaler_delay',
{'dtype': 'number',
'lower_ctrl_limit': 0.0,
'precision': 3,
'shape': [],
'source': 'PV:sky:scaler1.DLY',
'units': '',
'upper_ctrl_limit': 0.0}),
('scaler_auto_count_delay',
{'dtype': 'number',
'lower_ctrl_limit': 0.0,
'precision': 3,
'shape': [],
'source': 'PV:sky:scaler1.DLY1',
'units': '',
'upper_ctrl_limit': 0.0}),
('scaler_freq',
{'dtype': 'number',
'lower_ctrl_limit': 0.0,
'precision': 3,
'shape': [],
'source': 'PV:sky:scaler1.FREQ',
'units': '',
'upper_ctrl_limit': 0.0}),
('scaler_preset_time',
{'dtype': 'number',
'lower_ctrl_limit': 0.0,
'precision': 3,
'shape': [],
'source': 'PV:sky:scaler1.TP',
'units': '',
'upper_ctrl_limit': 0.0}),
('scaler_auto_count_time',
{'dtype': 'number',
'lower_ctrl_limit': 0.0,
'precision': 3,
'shape': [],
'source': 'PV:sky:scaler1.TP1',
'units': '',
'upper_ctrl_limit': 0.0}),
('scaler_egu',
{'dtype': 'string',
'lower_ctrl_limit': None,
'shape': [],
'source': 'PV:sky:scaler1.EGU',
'units': None,
'upper_ctrl_limit': None})]),
'timestamps': {'scaler_auto_count_delay': 1616788930.753993,
'scaler_auto_count_time': 1616788930.753993,
'scaler_channels_chan01_chname': 1616788930.753993,
'scaler_channels_chan01_gate': 1616788930.753993,
'scaler_channels_chan01_preset': 1616788930.753993,
'scaler_channels_chan02_chname': 1616788930.753993,
'scaler_channels_chan02_gate': 1616788930.753993,
'scaler_channels_chan02_preset': 1616788930.753993,
'scaler_channels_chan03_chname': 1616788930.753993,
'scaler_channels_chan03_gate': 1616788930.753993,
'scaler_channels_chan03_preset': 1616788930.753993,
'scaler_channels_chan10_chname': 1616788930.753993,
'scaler_channels_chan10_gate': 1616788930.753993,
'scaler_channels_chan10_preset': 1616788930.753993,
'scaler_channels_chan11_chname': 1616788930.753993,
'scaler_channels_chan11_gate': 1616788930.753993,
'scaler_channels_chan11_preset': 1616788930.753993,
'scaler_count_mode': 1616788930.753993,
'scaler_delay': 1616788930.753993,
'scaler_egu': 1616788930.753993,
'scaler_freq': 1616788930.753993,
'scaler_preset_time': 1616788930.753993}}},
'data_keys': {'I0': {'dtype': 'number',
'lower_ctrl_limit': 0.0,
'object_name': 'scaler',
'precision': 0,
'shape': [],
'source': 'PV:sky:scaler1.S2',
'units': '',
'upper_ctrl_limit': 0.0},
'ROI1': {'dtype': 'number',
'lower_ctrl_limit': 0.0,
'object_name': 'scaler',
'precision': 0,
'shape': [],
'source': 'PV:sky:scaler1.S10',
'units': '',
'upper_ctrl_limit': 0.0},
'ROI2': {'dtype': 'number',
'lower_ctrl_limit': 0.0,
'object_name': 'scaler',
'precision': 0,
'shape': [],
'source': 'PV:sky:scaler1.S11',
'units': '',
'upper_ctrl_limit': 0.0},
'clock': {'dtype': 'number',
'lower_ctrl_limit': 0.0,
'object_name': 'scaler',
'precision': 0,
'shape': [],
'source': 'PV:sky:scaler1.S1',
'units': '',
'upper_ctrl_limit': 0.0},
'm1': {'dtype': 'number',
'lower_ctrl_limit': -32000.0,
'object_name': 'm1',
'precision': 5,
'shape': [],
'source': 'PV:sky:m1.RBV',
'units': 'degrees',
'upper_ctrl_limit': 32000.0},
'm1_user_setpoint': {'dtype': 'number',
'lower_ctrl_limit': -32000.0,
'object_name': 'm1',
'precision': 5,
'shape': [],
'source': 'PV:sky:m1.VAL',
'units': 'degrees',
'upper_ctrl_limit': 32000.0},
'scaler_time': {'dtype': 'number',
'lower_ctrl_limit': 0.0,
'object_name': 'scaler',
'precision': 3,
'shape': [],
'source': 'PV:sky:scaler1.T',
'units': '',
'upper_ctrl_limit': 0.0},
'scint': {'dtype': 'number',
'lower_ctrl_limit': 0.0,
'object_name': 'scaler',
'precision': 0,
'shape': [],
'source': 'PV:sky:scaler1.S3',
'units': '',
'upper_ctrl_limit': 0.0}},
'hints': {'m1': {'fields': ['m1']},
'scaler': {'fields': ['clock', 'I0', 'scint', 'ROI1', 'ROI2']}},
'name': 'primary',
'object_keys': {'m1': ['m1', 'm1_user_setpoint'],
'scaler': ['clock',
'I0',
'scint',
'ROI1',
'ROI2',
'scaler_time']},
'run_start': '4c2e9fe2-3bed-4ae4-99e9-66f0cd006a67',
'time': 1616788930.7834864,
'uid': 'ce49e916-9b27-4c69-b7ba-985eb9455755'}
event 7
{'data': {'I0': 5.0,
'ROI1': 0.0,
'ROI2': 0.0,
'clock': 11000000.0,
'm1': -1.0,
'm1_user_setpoint': -1.0,
'scaler_time': 1.1,
'scint': 7.0},
'descriptor': 'ce49e916-9b27-4c69-b7ba-985eb9455755',
'filled': {},
'seq_num': 1,
'time': 1616788932.3618796,
'timestamps': {'I0': 1616788930.753993,
'ROI1': 1616788930.753993,
'ROI2': 1616788930.753993,
'clock': 1616788930.753993,
'm1': 1616788929.514959,
'm1_user_setpoint': 1616788927.22893,
'scaler_time': 1616788930.753993,
'scint': 1616788930.753993},
'uid': '7f4b54e7-aee5-4be1-846b-9719779461dd'}
event 7
{'data': {'I0': 4.0,
'ROI1': 0.0,
'ROI2': 0.0,
'clock': 11000000.0,
'm1': -0.5,
'm1_user_setpoint': -0.5,
'scaler_time': 1.1,
'scint': 5.0},
'descriptor': 'ce49e916-9b27-4c69-b7ba-985eb9455755',
'filled': {},
'seq_num': 2,
'time': 1616788934.5606568,
'timestamps': {'I0': 1616788934.557644,
'ROI1': 1616788934.557644,
'ROI2': 1616788934.557644,
'clock': 1616788934.557644,
'm1': 1616788933.318452,
'm1_user_setpoint': 1616788932.547789,
'scaler_time': 1616788934.557644,
'scint': 1616788934.557644},
'uid': '2a90c5a5-fff1-4942-928c-a9e0bd4becbb'}
event 7
{'data': {'I0': 4.0,
'ROI1': 0.0,
'ROI2': 0.0,
'clock': 11000000.0,
'm1': 0.0,
'm1_user_setpoint': 0.0,
'scaler_time': 1.1,
'scint': 4.0},
'descriptor': 'ce49e916-9b27-4c69-b7ba-985eb9455755',
'filled': {},
'seq_num': 3,
'time': 1616788936.666957,
'timestamps': {'I0': 1616788936.659936,
'ROI1': 1616788936.659936,
'ROI2': 1616788936.659936,
'clock': 1616788936.659936,
'm1': 1616788935.420564,
'm1_user_setpoint': 1616788934.689949,
'scaler_time': 1616788936.659936,
'scint': 1616788936.659936},
'uid': '46c7dd78-8e97-4c6b-88d8-c4863eb9fa82'}
event 7
{'data': {'I0': 5.0,
'ROI1': 0.0,
'ROI2': 0.0,
'clock': 11000000.0,
'm1': 0.5,
'm1_user_setpoint': 0.5,
'scaler_time': 1.1,
'scint': 4.0},
'descriptor': 'ce49e916-9b27-4c69-b7ba-985eb9455755',
'filled': {},
'seq_num': 4,
'time': 1616788938.7730787,
'timestamps': {'I0': 1616788938.764486,
'ROI1': 1616788938.764486,
'ROI2': 1616788938.764486,
'clock': 1616788938.764486,
'm1': 1616788937.522629,
'm1_user_setpoint': 1616788936.7736,
'scaler_time': 1616788938.764486,
'scint': 1616788938.764486},
'uid': '83daa851-cb42-4a48-9db7-560351b22822'}
event 7
{'data': {'I0': 5.0,
'ROI1': 0.0,
'ROI2': 0.0,
'clock': 11000000.0,
'm1': 1.0,
'm1_user_setpoint': 1.0,
'scaler_time': 1.1,
'scint': 3.0},
'descriptor': 'ce49e916-9b27-4c69-b7ba-985eb9455755',
'filled': {},
'seq_num': 5,
'time': 1616788940.876002,
'timestamps': {'I0': 1616788940.868264,
'ROI1': 1616788940.868264,
'ROI2': 1616788940.868264,
'clock': 1616788940.868264,
'm1': 1616788939.625246,
'm1_user_setpoint': 1616788938.91205,
'scaler_time': 1616788940.868264,
'scint': 1616788940.868264},
'uid': '117933b5-15a4-4579-808b-33084918b8ea'}
stop 6
{'exit_status': 'success',
'num_events': {'primary': 5},
'reason': '',
'run_start': '4c2e9fe2-3bed-4ae4-99e9-66f0cd006a67',
'time': 1616788941.0154452,
'uid': '1273fd74-9c1d-4ca0-b435-d9874394ef1c'}
[8]:
('4c2e9fe2-3bed-4ae4-99e9-66f0cd006a67',)
Summary#
We’ll show this code as a python program:
#!/usr/bin/env python
"lesson 2: motor"
from ophyd import EpicsMotor
from ophyd.scaler import ScalerCH
from bluesky import RunEngine
import bluesky.plans as bp
from apstools.devices import use_EPICS_scaler_channels
def myCallback(key, doc):
print()
print(key, len(doc))
pprint.pprint(doc)
m1 = EpicsMotor("sky:m1", name="m1")
m1.wait_for_connection()
print(m1.position)
scaler = ScalerCH("sky:scaler1", name="scaler")
scaler.wait_for_connection()
# Since there are no detectors actually connected to this scaler,
# we can change names at our choice. A real scaler will have
# detectors connected to specific channels and we should not modify
# these names without regard to how signals are physically connected.
scaler.channels.chan01.chname.put("clock")
scaler.channels.chan02.chname.put("I0")
scaler.channels.chan03.chname.put("scint")
scaler.channels.chan04.chname.put("")
scaler.channels.chan05.chname.put("")
scaler.channels.chan06.chname.put("")
scaler.channels.chan07.chname.put("")
scaler.channels.chan08.chname.put("")
scaler.channels.chan09.chname.put("")
scaler.match_names()
use_EPICS_scaler_channels(scaler)
print(scaler.read())
RE = RunEngine({})
RE(bp.scan([scaler], m1, -1, 1, 5))
RE(bp.scan([scaler], m1, -1, 1, 5), myCallback)