The instrument
package for Bluesky Data Acquisition#
From APS Python Training for Bluesky Data Acquisition.
Objective
In this notebook, we describe the instrument
package for a simulated X-ray instrument at a user facility such as the Advanced Photon Source. The goal is to use EPICS as much as possible to provide the control system features and to use Bluesky (providing the data acquisition framework) as a thin layer on top of EPICS.
Overview
This notebook uses two EPICS servers (IOCs), one, with prefix "gp:"
, providing (simulated) general beamline equipment such as motors, slits, shutter, counters, … The other IOC, with prefix "ad:"
, provides a simulated EPICS area detector. This notebook will acquaint you with some of the features provided by our Python instrument
package which uses these IOCs.
IOC details
This simulated instrument is provided using docker images for EPICS base, the synApps xxx module, and EPICS area detector ADSimDetector. The images are based on these software versions:
Debian GNU/Linux 11 (bullseye)
EPICS base 7.0.5
synApps 6.2.1
area detector 3.11
Two EPICS IOCs are provided:
prefix |
description |
docker image |
documentation |
---|---|---|---|
|
area detector IOC |
||
|
general purpose IOC |
Start the instrument
package#
To start the instrument
, use this Python startup snippet:
[1]:
import pathlib, sys
sys.path.append(str(pathlib.Path.home() / "bluesky"))
from instrument.collection import *
/home/prjemian/bluesky/instrument/_iconfig.py
Activating auto-logging. Current session state plus future input saved.
Filename : /home/prjemian/Documents/projects/BCDA-APS/bluesky_training/docs/source/instrument/.logs/ipython_console.log
Mode : rotate
Output logging : True
Raw input log : False
Timestamping : True
State : active
I Fri-11:15:53 - ############################################################ startup
I Fri-11:15:53 - logging started
I Fri-11:15:53 - logging level = 10
I Fri-11:15:53 - /home/prjemian/bluesky/instrument/session_logs.py
I Fri-11:15:53 - /home/prjemian/bluesky/instrument/collection.py
I Fri-11:15:53 - CONDA_PREFIX = /opt/miniconda3
Exception reporting mode: Minimal
I Fri-11:15:53 - xmode exception level: 'Minimal'
I Fri-11:15:53 - /home/prjemian/bluesky/instrument/mpl/notebook.py
I Fri-11:15:53 - #### Bluesky Framework ####
I Fri-11:15:53 - /home/prjemian/bluesky/instrument/framework/check_python.py
I Fri-11:15:53 - /home/prjemian/bluesky/instrument/framework/check_bluesky.py
I Fri-11:15:53 - /home/prjemian/bluesky/instrument/framework/initialize.py
I Fri-11:15:53 - RunEngine metadata saved in directory: /home/prjemian/Bluesky_RunEngine_md
I Fri-11:15:53 - using databroker catalog 'training'
I Fri-11:15:53 - using ophyd control layer: pyepics
I Fri-11:15:53 - /home/prjemian/bluesky/instrument/framework/metadata.py
I Fri-11:15:53 - /home/prjemian/bluesky/instrument/epics_signal_config.py
I Fri-11:15:53 - Using RunEngine metadata for scan_id
I Fri-11:15:54 - #### Devices ####
I Fri-11:15:54 - /home/prjemian/bluesky/instrument/devices/area_detector.py
I Fri-11:15:54 - /home/prjemian/bluesky/instrument/devices/calculation_records.py
I Fri-11:15:56 - /home/prjemian/bluesky/instrument/devices/fourc_diffractometer.py
I Fri-11:15:56 - /home/prjemian/bluesky/instrument/devices/ioc_stats.py
I Fri-11:15:56 - /home/prjemian/bluesky/instrument/devices/kohzu_monochromator.py
I Fri-11:15:56 - /home/prjemian/bluesky/instrument/devices/motors.py
I Fri-11:15:56 - /home/prjemian/bluesky/instrument/devices/noisy_detector.py
I Fri-11:15:56 - /home/prjemian/bluesky/instrument/devices/scaler.py
I Fri-11:15:57 - /home/prjemian/bluesky/instrument/devices/shutter_simulator.py
I Fri-11:15:57 - /home/prjemian/bluesky/instrument/devices/simulated_fourc.py
I Fri-11:15:57 - /home/prjemian/bluesky/instrument/devices/simulated_kappa.py
I Fri-11:15:57 - /home/prjemian/bluesky/instrument/devices/slits.py
I Fri-11:15:57 - /home/prjemian/bluesky/instrument/devices/sixc_diffractometer.py
I Fri-11:15:58 - /home/prjemian/bluesky/instrument/devices/temperature_signal.py
I Fri-11:15:58 - #### Callbacks ####
I Fri-11:15:58 - /home/prjemian/bluesky/instrument/callbacks/spec_data_file_writer.py
I Fri-11:15:58 - #### Plans ####
I Fri-11:15:58 - /home/prjemian/bluesky/instrument/plans/lup_plan.py
I Fri-11:15:58 - /home/prjemian/bluesky/instrument/plans/peak_finder_example.py
I Fri-11:15:58 - /home/prjemian/bluesky/instrument/utils/image_analysis.py
I Fri-11:15:58 - #### Utilities ####
I Fri-11:15:58 - writing to SPEC file: /home/prjemian/Documents/projects/BCDA-APS/bluesky_training/docs/source/instrument/20230414-111558.dat
I Fri-11:15:58 - >>>> Using default SPEC file name <<<<
I Fri-11:15:58 - file will be created when bluesky ends its next scan
I Fri-11:15:58 - to change SPEC file, use command: newSpecFile('title')
I Fri-11:15:58 - #### Startup is complete. ####
Description#
Might be a good idea to know now what this instrument package provides.
Notably, the table includes:
ophyd name(s) |
Description |
label(s) |
---|---|---|
|
simulated EPICS area detector |
|
|
calculation support |
|
|
details about the general purpose IOC |
|
|
named scaler channels |
|
|
instrument configuration parameters |
|
|
16 simulated EPICS motors |
|
|
simulated diffraction peak |
|
|
simulated 16-channel EPICS scaler |
|
|
simulated shutter |
|
|
simulated temperature controller |
iconfig
#
iconfig
, a Python dictionary, contains the instrument’s configuration details defined in one place for the user to control. These details, defined in instrument/iconfig.yml, are used in various places in the instrument
package. Any changes to the iconfig.yml
file will take effect the next time the instrument is started.
adsimdet
#
The EPICS Area Detector ADSimDetector is included with this instrument as adsimdet
. The detector is a monochrome 1k x 1k frame. The image is written to an HDF5 file and made available to the databroker via a shared volume from the docker container. adsimdet
can be used as a detector.
The image is a simulated diffraction spot with center randomly-chosen between 100..900 on both axes. The width is also random as is the peak intensity and noise level. Furthermore, to simulate realistic conditions, the center position of both axes is adjusted by a few pixels using a pair of swait records (gp:userCalc9
& gp:userCalc10
) updating from random numbers.
[2]:
adsimdet.summary()
data keys (* hints)
-------------------
read attrs
----------
hdf1 MyHDF5Plugin ('adsimdet_hdf1')
config keys
-----------
adsimdet_cam_acquire_period
adsimdet_cam_acquire_time
adsimdet_cam_image_mode
adsimdet_cam_manufacturer
adsimdet_cam_model
adsimdet_cam_num_exposures
adsimdet_cam_num_images
adsimdet_cam_trigger_mode
configuration attrs
-------------------
cam SimDetectorCam_V34 ('adsimdet_cam')
cam.acquire_period EpicsSignalWithRBV ('adsimdet_cam_acquire_period')
cam.acquire_time EpicsSignalWithRBV ('adsimdet_cam_acquire_time')
cam.image_mode EpicsSignalWithRBV ('adsimdet_cam_image_mode')
cam.manufacturer EpicsSignalRO ('adsimdet_cam_manufacturer')
cam.model EpicsSignalRO ('adsimdet_cam_model')
cam.num_exposures EpicsSignalWithRBV ('adsimdet_cam_num_exposures')
cam.num_images EpicsSignalWithRBV ('adsimdet_cam_num_images')
cam.trigger_mode EpicsSignalWithRBV ('adsimdet_cam_trigger_mode')
hdf1 MyHDF5Plugin ('adsimdet_hdf1')
unused attrs
------------
configuration_names ArrayAttributeSignal('adsimdet_configuration_names')
image ImagePlugin_V34 ('adsimdet_image')
pva PvaPlugin_V34 ('adsimdet_pva')
scaler1
#
A simulated 16-channel scaler is configured with several channels of pulse counters, typical of a synchrotron beamline. scaler1
can be used as a detector.
[3]:
scaler1.summary()
data keys (* hints)
-------------------
*I0
*I00
*I000
*roi1
scaler1_time
*scint
*timebase
read attrs
----------
channels Channels ('scaler1_channels')
channels.chan01 ScalerChannel ('scaler1_channels_chan01')
channels.chan01.s EpicsSignalRO ('timebase')
channels.chan02 ScalerChannel ('scaler1_channels_chan02')
channels.chan02.s EpicsSignalRO ('I0')
channels.chan04 ScalerChannel ('scaler1_channels_chan04')
channels.chan04.s EpicsSignalRO ('scint')
channels.chan05 ScalerChannel ('scaler1_channels_chan05')
channels.chan05.s EpicsSignalRO ('I000')
channels.chan06 ScalerChannel ('scaler1_channels_chan06')
channels.chan06.s EpicsSignalRO ('I00')
channels.chan07 ScalerChannel ('scaler1_channels_chan07')
channels.chan07.s EpicsSignalRO ('roi1')
time EpicsSignal ('scaler1_time')
config keys
-----------
scaler1_auto_count_delay
scaler1_auto_count_time
scaler1_channels_chan01_chname
scaler1_channels_chan01_gate
scaler1_channels_chan01_preset
scaler1_channels_chan02_chname
scaler1_channels_chan02_gate
scaler1_channels_chan02_preset
scaler1_channels_chan04_chname
scaler1_channels_chan04_gate
scaler1_channels_chan04_preset
scaler1_channels_chan05_chname
scaler1_channels_chan05_gate
scaler1_channels_chan05_preset
scaler1_channels_chan06_chname
scaler1_channels_chan06_gate
scaler1_channels_chan06_preset
scaler1_channels_chan07_chname
scaler1_channels_chan07_gate
scaler1_channels_chan07_preset
scaler1_count_mode
scaler1_delay
scaler1_egu
scaler1_freq
scaler1_preset_time
configuration attrs
-------------------
channels Channels ('scaler1_channels')
channels.chan01 ScalerChannel ('scaler1_channels_chan01')
channels.chan01.chname EpicsSignal ('scaler1_channels_chan01_chname')
channels.chan01.preset EpicsSignal ('scaler1_channels_chan01_preset')
channels.chan01.gate EpicsSignal ('scaler1_channels_chan01_gate')
channels.chan02 ScalerChannel ('scaler1_channels_chan02')
channels.chan02.chname EpicsSignal ('scaler1_channels_chan02_chname')
channels.chan02.preset EpicsSignal ('scaler1_channels_chan02_preset')
channels.chan02.gate EpicsSignal ('scaler1_channels_chan02_gate')
channels.chan04 ScalerChannel ('scaler1_channels_chan04')
channels.chan04.chname EpicsSignal ('scaler1_channels_chan04_chname')
channels.chan04.preset EpicsSignal ('scaler1_channels_chan04_preset')
channels.chan04.gate EpicsSignal ('scaler1_channels_chan04_gate')
channels.chan05 ScalerChannel ('scaler1_channels_chan05')
channels.chan05.chname EpicsSignal ('scaler1_channels_chan05_chname')
channels.chan05.preset EpicsSignal ('scaler1_channels_chan05_preset')
channels.chan05.gate EpicsSignal ('scaler1_channels_chan05_gate')
channels.chan06 ScalerChannel ('scaler1_channels_chan06')
channels.chan06.chname EpicsSignal ('scaler1_channels_chan06_chname')
channels.chan06.preset EpicsSignal ('scaler1_channels_chan06_preset')
channels.chan06.gate EpicsSignal ('scaler1_channels_chan06_gate')
channels.chan07 ScalerChannel ('scaler1_channels_chan07')
channels.chan07.chname EpicsSignal ('scaler1_channels_chan07_chname')
channels.chan07.preset EpicsSignal ('scaler1_channels_chan07_preset')
channels.chan07.gate EpicsSignal ('scaler1_channels_chan07_gate')
count_mode EpicsSignal ('scaler1_count_mode')
delay EpicsSignal ('scaler1_delay')
auto_count_delay EpicsSignal ('scaler1_auto_count_delay')
freq EpicsSignal ('scaler1_freq')
preset_time EpicsSignal ('scaler1_preset_time')
auto_count_time EpicsSignal ('scaler1_auto_count_time')
egu EpicsSignal ('scaler1_egu')
unused attrs
------------
count EpicsSignal ('scaler1_count')
update_rate EpicsSignal ('scaler1_update_rate')
auto_count_update_rate EpicsSignal ('scaler1_auto_count_update_rate')
The timebase is always shown. The additional channels (using names from the scaler GUI screen) are shown output of listdevice(scaler1)
below.
Note that in the instrument package, the channel names were assigned on startup. An operating beamline would not do define the names here but instead, let the instrument team name these channels in the GUI screen at the time the cables from the pulse detectors are connected physically to the scaler.
Count the scaler from the command line:
[4]:
%ct scalers
[This data will not be saved. Use the RunEngine to collect data.]
timebase 16000000.0
I0 8.0
scint 7.0
I000 6.0
I00 8.0
roi1 6.0
scaler1_time 1.6
NOTE: the actual counts are random numbers. Also, the EPICS soft scaler seems to add 0.1s to the counting time. Might be a bug with the EPICS scaler code.
[5]:
listdevice(scaler1)
[5]:
============================== ========== ==========================
data name value timestamp
============================== ========== ==========================
scaler1_channels_chan01_chname timebase 2023-04-14 10:58:36.645292
timebase 16000000.0 2023-04-14 11:16:00.339417
scaler1_channels_chan01_preset 15000000.0 2023-04-14 10:58:36.645292
scaler1_channels_chan01_gate Y 2023-04-14 10:58:36.645292
scaler1_channels_chan02_chname I0 2023-04-14 10:58:36.645292
I0 8.0 2023-04-14 11:16:00.339417
scaler1_channels_chan02_preset 0.0 2023-04-14 10:58:36.645292
scaler1_channels_chan02_gate N 2023-04-14 10:58:36.645292
scaler1_channels_chan03_chname scint 2023-04-14 10:58:36.645292
scint 7.0 2023-04-14 10:58:36.645292
scaler1_channels_chan03_preset 0.0 2023-04-14 10:58:36.645292
scaler1_channels_chan03_gate N 2023-04-14 10:58:36.645292
scaler1_channels_chan04_chname scint 2023-04-14 10:58:36.645292
scint 7.0 2023-04-14 11:16:00.339417
scaler1_channels_chan04_preset 0.0 2023-04-14 10:58:36.645292
scaler1_channels_chan04_gate N 2023-04-14 10:58:36.645292
scaler1_channels_chan05_chname I000 2023-04-14 10:58:36.645292
I000 6.0 2023-04-14 11:16:00.339417
scaler1_channels_chan05_preset 0.0 2023-04-14 10:58:36.645292
scaler1_channels_chan05_gate N 2023-04-14 10:58:36.645292
scaler1_channels_chan06_chname I00 2023-04-14 10:58:36.645292
I00 8.0 2023-04-14 11:16:00.339417
scaler1_channels_chan06_preset 0.0 2023-04-14 10:58:36.645292
scaler1_channels_chan06_gate N 2023-04-14 10:58:36.645292
scaler1_channels_chan07_chname roi1 2023-04-14 10:58:36.645292
roi1 6.0 2023-04-14 11:16:00.339417
scaler1_channels_chan07_preset 0.0 2023-04-14 10:58:36.645292
scaler1_channels_chan07_gate N 2023-04-14 10:58:36.645292
scaler1_channels_chan08_chname 2023-04-14 10:58:36.645292
8.0 2023-04-14 10:58:36.645292
scaler1_channels_chan08_preset 0.0 2023-04-14 10:58:36.645292
scaler1_channels_chan08_gate N 2023-04-14 10:58:36.645292
scaler1_channels_chan09_chname 2023-04-14 10:58:36.645292
0.0 2023-04-14 10:58:36.645292
scaler1_channels_chan09_preset 0.0 2023-04-14 10:58:36.645292
scaler1_channels_chan09_gate N 2023-04-14 10:58:36.645292
scaler1_channels_chan10_chname 2023-04-14 10:58:36.645292
0.0 2023-04-14 10:58:36.645292
scaler1_channels_chan10_preset 0.0 2023-04-14 10:58:36.645292
scaler1_channels_chan10_gate N 2023-04-14 10:58:36.645292
scaler1_channels_chan11_chname 2023-04-14 10:58:36.645292
0.0 2023-04-14 10:58:36.645292
scaler1_channels_chan11_preset 0.0 2023-04-14 10:58:36.645292
scaler1_channels_chan11_gate N 2023-04-14 10:58:36.645292
scaler1_channels_chan12_chname 2023-04-14 10:58:36.645292
0.0 2023-04-14 10:58:36.645292
scaler1_channels_chan12_preset 0.0 2023-04-14 10:58:36.645292
scaler1_channels_chan12_gate N 2023-04-14 10:58:36.645292
scaler1_channels_chan13_chname 2023-04-14 10:58:36.645292
0.0 2023-04-14 10:58:36.645292
scaler1_channels_chan13_preset 0.0 2023-04-14 10:58:36.645292
scaler1_channels_chan13_gate N 2023-04-14 10:58:36.645292
scaler1_channels_chan14_chname 2023-04-14 10:58:36.645292
0.0 2023-04-14 10:58:36.645292
scaler1_channels_chan14_preset 0.0 2023-04-14 10:58:36.645292
scaler1_channels_chan14_gate N 2023-04-14 10:58:36.645292
scaler1_channels_chan15_chname 2023-04-14 10:58:36.645292
0.0 2023-04-14 10:58:36.645292
scaler1_channels_chan15_preset 0.0 2023-04-14 10:58:36.645292
scaler1_channels_chan15_gate N 2023-04-14 10:58:36.645292
scaler1_channels_chan16_chname 2023-04-14 10:58:36.645292
0.0 2023-04-14 10:58:36.645292
scaler1_channels_chan16_preset 0.0 2023-04-14 10:58:36.645292
scaler1_channels_chan16_gate N 2023-04-14 10:58:36.645292
scaler1_channels_chan17_chname 2023-04-14 10:58:36.645292
0.0 2023-04-14 10:58:36.645292
scaler1_channels_chan17_preset 0.0 2023-04-14 10:58:36.645292
scaler1_channels_chan17_gate N 2023-04-14 10:58:36.645292
scaler1_channels_chan18_chname 2023-04-14 10:58:36.645292
0.0 2023-04-14 10:58:36.645292
scaler1_channels_chan18_preset 0.0 2023-04-14 10:58:36.645292
scaler1_channels_chan18_gate N 2023-04-14 10:58:36.645292
scaler1_channels_chan19_chname 2023-04-14 10:58:36.645292
0.0 2023-04-14 10:58:36.645292
scaler1_channels_chan19_preset 0.0 2023-04-14 10:58:36.645292
scaler1_channels_chan19_gate N 2023-04-14 10:58:36.645292
scaler1_channels_chan20_chname 2023-04-14 10:58:36.645292
0.0 2023-04-14 10:58:36.645292
scaler1_channels_chan20_preset 0.0 2023-04-14 10:58:36.645292
scaler1_channels_chan20_gate N 2023-04-14 10:58:36.645292
scaler1_channels_chan21_chname 2023-04-14 10:58:36.645292
0.0 2023-04-14 10:58:36.645292
scaler1_channels_chan21_preset 0.0 2023-04-14 10:58:36.645292
scaler1_channels_chan21_gate N 2023-04-14 10:58:36.645292
scaler1_channels_chan22_chname 2023-04-14 10:58:36.645292
0.0 2023-04-14 10:58:36.645292
scaler1_channels_chan22_preset 0.0 2023-04-14 10:58:36.645292
scaler1_channels_chan22_gate N 2023-04-14 10:58:36.645292
scaler1_channels_chan23_chname 2023-04-14 10:58:36.645292
0.0 2023-04-14 10:58:36.645292
scaler1_channels_chan23_preset 0.0 2023-04-14 10:58:36.645292
scaler1_channels_chan23_gate N 2023-04-14 10:58:36.645292
scaler1_channels_chan24_chname 2023-04-14 10:58:36.645292
0.0 2023-04-14 10:58:36.645292
scaler1_channels_chan24_preset 0.0 2023-04-14 10:58:36.645292
scaler1_channels_chan24_gate N 2023-04-14 10:58:36.645292
scaler1_channels_chan25_chname 2023-04-14 10:58:36.645292
0.0 2023-04-14 10:58:36.645292
scaler1_channels_chan25_preset 0.0 2023-04-14 10:58:36.645292
scaler1_channels_chan25_gate N 2023-04-14 10:58:36.645292
scaler1_channels_chan26_chname 2023-04-14 10:58:36.645292
0.0 2023-04-14 10:58:36.645292
scaler1_channels_chan26_preset 0.0 2023-04-14 10:58:36.645292
scaler1_channels_chan26_gate N 2023-04-14 10:58:36.645292
scaler1_channels_chan27_chname 2023-04-14 10:58:36.645292
0.0 2023-04-14 10:58:36.645292
scaler1_channels_chan27_preset 0.0 2023-04-14 10:58:36.645292
scaler1_channels_chan27_gate N 2023-04-14 10:58:36.645292
scaler1_channels_chan28_chname 2023-04-14 10:58:36.645292
0.0 2023-04-14 10:58:36.645292
scaler1_channels_chan28_preset 0.0 2023-04-14 10:58:36.645292
scaler1_channels_chan28_gate N 2023-04-14 10:58:36.645292
scaler1_channels_chan29_chname 2023-04-14 10:58:36.645292
0.0 2023-04-14 10:58:36.645292
scaler1_channels_chan29_preset 0.0 2023-04-14 10:58:36.645292
scaler1_channels_chan29_gate N 2023-04-14 10:58:36.645292
scaler1_channels_chan30_chname 2023-04-14 10:58:36.645292
0.0 2023-04-14 10:58:36.645292
scaler1_channels_chan30_preset 0.0 2023-04-14 10:58:36.645292
scaler1_channels_chan30_gate N 2023-04-14 10:58:36.645292
scaler1_channels_chan31_chname 2023-04-14 10:58:36.645292
0.0 2023-04-14 10:58:36.645292
scaler1_channels_chan31_preset 0.0 2023-04-14 10:58:36.645292
scaler1_channels_chan31_gate N 2023-04-14 10:58:36.645292
scaler1_channels_chan32_chname 2023-04-14 10:58:36.645292
0.0 2023-04-14 10:58:36.645292
scaler1_channels_chan32_preset 0.0 2023-04-14 10:58:36.645292
scaler1_channels_chan32_gate N 2023-04-14 10:58:36.645292
scaler1_count 0 2023-04-14 10:58:36.645292
scaler1_count_mode OneShot 2023-04-14 10:58:36.645292
scaler1_delay 0.0 2023-04-14 10:58:36.645292
scaler1_auto_count_delay 0.0 2023-04-14 10:58:36.645292
scaler1_time 1.6 2023-04-14 10:58:36.645292
scaler1_freq 10000000.0 2023-04-14 10:58:36.645292
scaler1_preset_time 1.5 2023-04-14 10:58:36.645292
scaler1_auto_count_time 1.0 2023-04-14 10:58:36.645292
scaler1_update_rate 10.0 2023-04-14 10:58:36.645292
scaler1_auto_count_update_rate 0.0 2023-04-14 10:58:36.645292
scaler1_egu 2023-04-14 10:58:36.645292
============================== ========== ==========================
As an added convenience, shortcut names for these channels were also assigned to local Python symbols (since the object path to the channels is easy to forget) in instrument/devices/scaler.py
with these lines:
timebase = scaler1.channels.chan01.s
I0 = scaler1.channels.chan02.s
scint = scaler1.channels.chan03.s
diode = scaler1.channels.chan04.s
This command: scaler1.select_channels()
configures scaler
to use just the channels with non-empty names. To limit data collection to a subset of these channels, name the channels in a list argument, such as: scaler1.select_channels(['I0', 'diode'])
temperature
#
The temperature simulator, implemented in gp:userCalc8
works like a temperature controller, with setpoint and readback values. temperature
can be used as a detector and as a positioner (like a motor).
Additional controls for noise, update interval, maximum change per update, in-position tolerance, and done are provided, either through EPICS or in Python. Once a new setpoint is entered, the readback will progress towards it, limited by the max change and the update interval. Once at the setpoint, noise continues to be added to the temperature to provide realism.
[6]:
temperature.summary()
listdevice(temperature)
data keys (* hints)
-------------------
*temperature
temperature_calculation
temperature_description
temperature_done
temperature_max_change
temperature_noise
temperature_previous_value_pv
temperature_scanning_rate
temperature_setpoint
temperature_tolerance
read attrs
----------
setpoint EpicsSignal ('temperature_setpoint')
readback EpicsSignal ('temperature')
done Signal ('temperature_done')
calculation EpicsSignal ('temperature_calculation')
description EpicsSignal ('temperature_description')
max_change EpicsSignal ('temperature_max_change')
noise EpicsSignal ('temperature_noise')
previous_value_pv EpicsSignal ('temperature_previous_value_pv')
scanning_rate EpicsSignal ('temperature_scanning_rate')
tolerance EpicsSignal ('temperature_tolerance')
config keys
-----------
configuration attrs
-------------------
unused attrs
------------
report_dmov_changes Signal ('temperature_report_dmov_changes')
[6]:
=============================== =================================== ==========================
data name value timestamp
=============================== =================================== ==========================
temperature_setpoint 25.0 2023-04-14 11:15:58.067527
temperature 25.07474631876097 2023-04-14 11:16:00.268549
temperature_done True 2023-04-14 11:16:00.274526
temperature_calculation A+max(-D,min(D,(B-A)))+C*(RNDM-0.5) 2023-04-14 11:15:58.071359
temperature_description temperature 2023-04-14 11:15:58.065403
temperature_max_change 2.0 2023-04-14 11:15:58.070733
temperature_noise 1.0 2023-04-14 11:15:58.070030
temperature_previous_value_pv gp:userCalc8.VAL 2023-04-14 11:15:58.065403
temperature_scanning_rate 5 2023-04-14 11:15:58.071359
temperature_tolerance 1.0 2023-04-14 11:15:58.071359
temperature_report_dmov_changes False 2023-04-14 11:15:58.066705
=============================== =================================== ==========================
motors#
There are 16 soft motor channels: gp:m1
.. gp:m16
. Any motor can be used as a detector and as a positioner.
The first motor, gp:m1
, is used to compute a simulated 1-D diffraction peak (noisy) using gp:userCalc1
.
To make it easier to change the motor step size, a custom MyEpicsMotor
class was made, subclassing from ophyd.EpicsMotor
and adding a component to access the motor’s .SREV
(steps/revolution) field. The default motor step size is 0.01 (with SREV=200). For only m1
, the step size has been changed to 0.001 by setting SREV=2000.
NOTE: Changing SREV (in this simulator* seems to affect the soft motor’s simulated hardware limit. Be careful if you adjust SREV to higher numbers (smaller step sizes) that the expected range of motion remains sufficient for your measurements. Again, another thing to change in the EPICS support.
[7]:
m1.summary()
listdevice(m1)
data keys (* hints)
-------------------
*m1
m1_user_setpoint
read attrs
----------
user_readback EpicsSignalRO ('m1')
user_setpoint EpicsSignal ('m1_user_setpoint')
config keys
-----------
m1_acceleration
m1_motor_egu
m1_user_offset
m1_user_offset_dir
m1_velocity
configuration attrs
-------------------
user_offset EpicsSignal ('m1_user_offset')
user_offset_dir EpicsSignal ('m1_user_offset_dir')
velocity EpicsSignal ('m1_velocity')
acceleration EpicsSignal ('m1_acceleration')
motor_egu EpicsSignal ('m1_motor_egu')
unused attrs
------------
offset_freeze_switch EpicsSignal ('m1_offset_freeze_switch')
set_use_switch EpicsSignal ('m1_set_use_switch')
motor_is_moving EpicsSignalRO ('m1_motor_is_moving')
motor_done_move EpicsSignalRO ('m1_motor_done_move')
high_limit_switch EpicsSignalRO ('m1_high_limit_switch')
low_limit_switch EpicsSignalRO ('m1_low_limit_switch')
high_limit_travel EpicsSignal ('m1_high_limit_travel')
low_limit_travel EpicsSignal ('m1_low_limit_travel')
direction_of_travel EpicsSignal ('m1_direction_of_travel')
motor_stop EpicsSignal ('m1_motor_stop')
home_forward EpicsSignal ('m1_home_forward')
home_reverse EpicsSignal ('m1_home_reverse')
steps_per_revolution EpicsSignal ('m1_steps_per_revolution')
[7]:
======================= ======= ==========================
data name value timestamp
======================= ======= ==========================
m1 0.0 2023-04-14 10:58:33.041083
m1_user_setpoint 0.0 2023-04-14 10:58:33.041083
m1_user_offset 0.0 2023-04-14 10:58:33.041083
m1_user_offset_dir 0 2023-04-14 10:58:33.041083
m1_offset_freeze_switch 0 2023-04-14 10:58:33.041083
m1_set_use_switch 0 2023-04-14 10:58:33.041083
m1_velocity 1.0 2023-04-14 10:58:33.041083
m1_acceleration 0.2 2023-04-14 10:58:33.041083
m1_motor_egu degrees 2023-04-14 10:58:33.041083
m1_motor_is_moving 0 2023-04-14 10:58:33.041083
m1_motor_done_move 1 2023-04-14 10:58:33.041083
m1_high_limit_switch 0 2023-04-14 10:58:33.041083
m1_low_limit_switch 0 2023-04-14 10:58:33.041083
m1_high_limit_travel 1000.0 2023-04-14 10:58:33.041083
m1_low_limit_travel -1000.0 2023-04-14 10:58:33.041083
m1_direction_of_travel 0 2023-04-14 10:58:33.041083
m1_motor_stop 0 2023-04-14 10:58:33.041083
m1_home_forward 0 2023-04-14 10:58:33.041083
m1_home_reverse 0 2023-04-14 10:58:33.041083
m1_steps_per_revolution 2000 2023-04-14 10:58:33.041083
======================= ======= ==========================
noisy
#
A simulated diffraction peak is computed in 1-D using a Lorentzian function in EPICS PV gp:userCalc1
. The simulation uses the position of motor gp:m1
and random choices for center, width, noise, and peak intensity (scale factor). noisy
can be used as a detector.
NOTE: noisy
is an ophyd.EpicsSignal
(variant), thus it lacks the .summary()
method that ophyd.Device
objects have.
[8]:
listdevice(noisy)
[8]:
========= ================= ==========================
data name value timestamp
========= ================= ==========================
noisy 685.3703376927396 2023-04-14 11:15:56.876793
========= ================= ==========================
plans#
Several examples of user plan are provided in file instrument/plans/peak_finder_example.py
. These plans are described and used in the notebook Lineup a 1-D peak.
Log files#
For diagnostic and general support, log files are created to record activity. In the working directory, the log files are written to a ./.logs
subdirectory.
There are two kinds of file, one that records user commands and the python result, the other records items sent to the Python logging package.
In the IPython session, use the !
to run a linux command:
[9]:
!ls -lAFgh .logs
total 7.5M
-rw-rw-r-- 1 prjemian 15K Apr 14 11:16 ipython_console.log
-rw-rw-r-- 1 prjemian 15K Apr 14 10:58 ipython_console.log.001~
-rw-rw-r-- 1 prjemian 16K Apr 14 10:57 ipython_console.log.002~
-rw-rw-r-- 1 prjemian 5.8K Apr 14 11:15 ipython_logger.log
-rw-rw-r-- 1 prjemian 455K Apr 14 11:16 ophyd.control_layer.log
-rw-rw-r-- 1 prjemian 1.0M Apr 14 11:15 ophyd.control_layer.log.1
-rw-rw-r-- 1 prjemian 1.0M Apr 14 11:12 ophyd.control_layer.log.2
-rw-rw-r-- 1 prjemian 1.0M Apr 14 11:06 ophyd.control_layer.log.3
-rw-rw-r-- 1 prjemian 1.0M Apr 14 11:01 ophyd.control_layer.log.4
-rw-rw-r-- 1 prjemian 1.0M Apr 14 10:58 ophyd.control_layer.log.5
-rw-rw-r-- 1 prjemian 1.0M Apr 14 10:55 ophyd.control_layer.log.6
-rw-rw-r-- 1 prjemian 1.0M Apr 14 10:49 ophyd.control_layer.log.7
Let’s take a look at a few lines of each type of file, to get a feel for the information logged.
The ipython_console.log
file is created for every session (every time IPython is started or every time the Jupyter kernel is started). It records the commands that were entered and the output, if any, from each command. It does not record anything that was sent to the console by print()
statements. The older log files are numbered, higher number is older (each new session shifts these numbers by 1).
[10]:
!head .logs/ipython_console.log
# IPython log file
# Fri, 14 Apr 2023 11:15:58
adsimdet.summary()
# Fri, 14 Apr 2023 11:15:58
scaler1.summary()
# Fri, 14 Apr 2023 11:15:58
get_ipython().run_line_magic('ct', 'scalers')
# Fri, 14 Apr 2023 11:16:00
listdevice(scaler1)
The ipython_logger.log
file(s) contain the output from calls to the logger
. The files are appended with new logger reports until the file reaches ca. 1 MB. (Larger files are slow to append.) Then the file is given a number and a new logger file is created. Higher numbers are older. At most, 9 numbered files are retained (to avoid filling disk storage with unnecessary diagnostics).
A single logger file may contain reports from several sessions. The third piece of information is the pid
(process identifier) of the session. The PID is assigned by the operating system when the session is started.
The logger file contains a full report while the report’s representation is more terse. (Users do not always want the full details. Just remember they are available in the logger file.) Here’s the last few logger lines as shown on the console:
I Wed-00:24:52 - /home/mintadmin/Documents/projects/BCDA-APS/bluesky_instrument_training/instrument/devices/temperature_signal.py
I Wed-00:24:52 - /home/mintadmin/Documents/projects/BCDA-APS/bluesky_instrument_training/instrument/plans/peak_finder_example.py
I Wed-00:24:52 - Startup is complete.
Those same lines appear in the logger file as:
|2021-02-24 00:24:52.091|INFO|25986|bluesky-session|temperature_signal|11|MainThread| - /home/mintadmin/Documents/projects/BCDA-APS/bluesky_instrument_training/instrument/devices/temperature_signal.py
|2021-02-24 00:24:52.165|INFO|25986|bluesky-session|peak_finder_example|15|MainThread| - /home/mintadmin/Documents/projects/BCDA-APS/bluesky_instrument_training/instrument/plans/peak_finder_example.py
|2021-02-24 00:24:52.179|INFO|25986|bluesky-session|collection|23|MainThread| - Startup is complete.
[11]:
!tail .logs/ipython_logger.log
|2023-04-14 11:15:53.916|INFO|3701168|bluesky-session|collection|29|MainThread| - #### Bluesky Framework ####
|2023-04-14 11:15:54.025|INFO|3701168|bluesky-session|collection|32|MainThread| - #### Devices ####
|2023-04-14 11:15:58.072|INFO|3701168|bluesky-session|collection|35|MainThread| - #### Callbacks ####
|2023-04-14 11:15:58.079|INFO|3701168|bluesky-session|collection|38|MainThread| - #### Plans ####
|2023-04-14 11:15:58.130|INFO|3701168|bluesky-session|collection|41|MainThread| - #### Utilities ####
|2023-04-14 11:15:58.131|INFO|3701168|bluesky-session|collection|49|MainThread| - writing to SPEC file: /home/prjemian/Documents/projects/BCDA-APS/bluesky_training/docs/source/instrument/20230414-111558.dat
|2023-04-14 11:15:58.133|INFO|3701168|bluesky-session|collection|50|MainThread| - >>>> Using default SPEC file name <<<<
|2023-04-14 11:15:58.136|INFO|3701168|bluesky-session|collection|51|MainThread| - file will be created when bluesky ends its next scan
|2023-04-14 11:15:58.137|INFO|3701168|bluesky-session|collection|52|MainThread| - to change SPEC file, use command: newSpecFile('title')
|2023-04-14 11:15:58.138|INFO|3701168|bluesky-session|collection|56|MainThread| - #### Startup is complete. ####
SPEC data files#
A SPEC data file records a copy of the scan data (area detector images are not stored in SPEC files). By default, the file is created in the present working directory using a name constructed from the date and time with a .dat
file extension. Unlike SPEC, new data is written at the end of a scan. It is also possible to use the SpecWriterCallback to write
data extracted from the database after the experiment is done.
The 2021-03 training does not cover the use of these files. You are free to examine them yourselves.
Example SPEC data file from a bluesky session:
#F /home/mintadmin/Documents/projects/BCDA-APS/bluesky_training/20210723-205451.dat
#E 1627091691
#D Fri Jul 23 20:54:51 2021
#C Bluesky user = mintadmin host = mint-vm
#O0
#o0
#S 344 scan(detectors=['noisy', 'th_tth_permit'], num=11, args='['theta', 4.5, 3.0, 'ttheta', 9, 6]', per_step='None')
#D Fri Jul 23 20:55:57 2021
#C Fri Jul 23 20:55:57 2021. plan_type = generator
#C Fri Jul 23 20:55:57 2021. uid = 17ebe732-8cff-458e-8ebd-f7c5207b2e46
#MD uid = 17ebe732-8cff-458e-8ebd-f7c5207b2e46
#MD beamline_id = Bluesky_training
#MD detectors = ['noisy', 'th_tth_permit']
#MD instrument_name = Bluesky Case Studies
#MD login_id = mintadmin@mint-vm
#MD motors = ('theta', 'ttheta')
#MD notebook = UB_autosave
#MD num_intervals = 10
#MD num_points = 11
#MD objective = Demonstrate UB matrix save & restore
#MD pid = 10389
#MD plan_pattern = inner_product
#MD plan_pattern_args = {'num': 11, 'args': ["EpicsMotor(prefix='gp:m1', name='theta', settle_time=0.0, timeout=None, read_attrs=['user_readback', 'user_setpoint'], configuration_attrs=['user_offset', 'user_offset_dir', 'velocity', 'acceleration', 'motor_egu'])", 4.5, 3.0, "EpicsMotor(prefix='gp:m2', name='ttheta', settle_time=0.0, timeout=None, read_attrs=['user_readback', 'user_setpoint'], configuration_attrs=['user_offset', 'user_offset_dir', 'velocity', 'acceleration', 'motor_egu'])", 9, 6]}
#MD plan_pattern_module = bluesky.plan_patterns
#MD proposal_id = training
#MD versions = {'apstools': '1.5.1', 'bluesky': '1.7.0', 'databroker': '1.2.3', 'epics': '3.5.0', 'h5py': '3.2.1', 'intake': '0.6.2', 'matplotlib': '3.3.4', 'numpy': '1.20.3', 'ophyd': '1.6.1', 'pyRestTable': '2020.0.3', 'spec2nexus': '2021.1.8'}
#P0
#N 8
#L theta ttheta Epoch_float Epoch ttheta_user_setpoint theta_user_setpoint noisy th_tth_permit
4.5 9.0 9.998106479644775 10 9.0 4.5 0.0 31963.763745860513
4.3500000000000005 8.700000000000001 10.893603086471558 11 8.7 4.35 0.0 34611.37532330056
4.2 8.4 11.595271110534668 12 8.4 4.2 0.0 35180.93342784417
4.05 8.1 12.299082040786743 12 8.1 4.05 0.0 37694.92329152548
3.9 7.8 13.00736141204834 13 7.8 3.9 0.0 39442.76049515945
3.75 7.5 13.71463131904602 14 7.5 3.75 0.0 42096.21324791565
3.6 7.2 14.41884994506836 14 7.2 3.6 0.0 44406.552389513294
3.45 6.9 15.12493348121643 15 6.9 3.45 0.0 45977.709130280506
3.3000000000000003 6.6000000000000005 15.830729246139526 16 6.6 3.3 0.0 48881.20001936124
3.15 6.3 16.53253173828125 17 6.300000000000001 3.1500000000000004 0.0 51082.06178377704
3.0 6.0 17.24329447746277 17 6.0 3.0 0.0 53210.29908657747
#C Fri Jul 23 20:56:14 2021. num_events_baseline = 2
#C Fri Jul 23 20:56:14 2021. num_events_primary = 11
#C Fri Jul 23 20:56:14 2021. exit_status = success
#S 345 scan(detectors=['noisy', 'th_tth_permit'], num=4, args='['theta', 3.5, 3.0, 'ttheta', 7, 6]', per_step='None')
#D Fri Jul 23 20:57:50 2021
#C Fri Jul 23 20:57:50 2021. plan_type = generator
#C Fri Jul 23 20:57:50 2021. uid = 5761047b-ac05-428a-8b9c-6601b0aba2c0
#MD uid = 5761047b-ac05-428a-8b9c-6601b0aba2c0
#MD beamline_id = Bluesky_training
#MD detectors = ['noisy', 'th_tth_permit']
#MD instrument_name = Bluesky Case Studies
#MD login_id = mintadmin@mint-vm
#MD motors = ('theta', 'ttheta')
#MD notebook = UB_autosave
#MD num_intervals = 3
#MD num_points = 4
#MD objective = Demonstrate UB matrix save & restore
#MD pid = 10389
#MD plan_pattern = inner_product
#MD plan_pattern_args = {'num': 4, 'args': ["MyMotor(prefix='gp:m1', name='theta', settle_time=0.0, timeout=None, read_attrs=['user_readback', 'user_setpoint', 'backlash', 'backlash_velocity'], configuration_attrs=['user_offset', 'user_offset_dir', 'velocity', 'acceleration', 'motor_egu'])", 3.5, 3.0, "MyMotor(prefix='gp:m2', name='ttheta', settle_time=0.0, timeout=None, read_attrs=['user_readback', 'user_setpoint', 'backlash', 'backlash_velocity'], configuration_attrs=['user_offset', 'user_offset_dir', 'velocity', 'acceleration', 'motor_egu'])", 7, 6]}
#MD plan_pattern_module = bluesky.plan_patterns
#MD proposal_id = training
#MD versions = {'apstools': '1.5.1', 'bluesky': '1.7.0', 'databroker': '1.2.3', 'epics': '3.5.0', 'h5py': '3.2.1', 'intake': '0.6.2', 'matplotlib': '3.3.4', 'numpy': '1.20.3', 'ophyd': '1.6.1', 'pyRestTable': '2020.0.3', 'spec2nexus': '2021.1.8'}
#P0
#N 12
#L theta ttheta Epoch_float Epoch theta_user_setpoint theta_backlash theta_backlash_velocity ttheta_user_setpoint ttheta_backlash ttheta_backlash_velocity noisy th_tth_permit
3.5 7.0 4.44653844833374 4 3.5 0.0 1.0 7.0 0.5 0.2 0.0 46002.45836582881
3.333 6.67 8.672361612319946 9 3.3333333333333335 0.0 1.0 6.666666666666667 0.5 0.2 0.0 46875.28455439954
3.1670000000000003 6.33 12.796637535095215 13 3.1666666666666665 0.0 1.0 6.333333333333333 0.5 0.2 0.0 50480.82899166115
3.0 6.0 16.955684661865234 17 3.0 0.0 1.0 6.0 0.5 0.2 0.0 54033.337551435354
#C Fri Jul 23 20:58:08 2021. num_events_baseline = 2
#C Fri Jul 23 20:58:08 2021. num_events_primary = 4
#C Fri Jul 23 20:58:08 2021. exit_status = success
#S 346 scan(detectors=['noisy', 'th_tth_permit'], num=4, args='['theta', 4.0, 3.0, 'ttheta', 8, 6]', per_step='None')
#D Fri Jul 23 20:58:28 2021
#C Fri Jul 23 20:58:28 2021. plan_type = generator
#C Fri Jul 23 20:58:28 2021. uid = 14e4398d-42b5-40cf-b082-e2f7e4fd07fd
#MD uid = 14e4398d-42b5-40cf-b082-e2f7e4fd07fd
#MD beamline_id = Bluesky_training
#MD detectors = ['noisy', 'th_tth_permit']
#MD instrument_name = Bluesky Case Studies
#MD login_id = mintadmin@mint-vm
#MD motors = ('theta', 'ttheta')
#MD notebook = UB_autosave
#MD num_intervals = 3
#MD num_points = 4
#MD objective = Demonstrate UB matrix save & restore
#MD pid = 10389
#MD plan_pattern = inner_product
#MD plan_pattern_args = {'num': 4, 'args': ["MyMotor(prefix='gp:m1', name='theta', settle_time=0.0, timeout=None, read_attrs=['user_readback', 'user_setpoint', 'backlash', 'backlash_velocity'], configuration_attrs=['user_offset', 'user_offset_dir', 'velocity', 'acceleration', 'motor_egu'])", 4.0, 3.0, "MyMotor(prefix='gp:m2', name='ttheta', settle_time=0.0, timeout=None, read_attrs=['user_readback', 'user_setpoint', 'backlash', 'backlash_velocity'], configuration_attrs=['user_offset', 'user_offset_dir', 'velocity', 'acceleration', 'motor_egu'])", 8, 6]}
#MD plan_pattern_module = bluesky.plan_patterns
#MD proposal_id = training
#MD versions = {'apstools': '1.5.1', 'bluesky': '1.7.0', 'databroker': '1.2.3', 'epics': '3.5.0', 'h5py': '3.2.1', 'intake': '0.6.2', 'matplotlib': '3.3.4', 'numpy': '1.20.3', 'ophyd': '1.6.1', 'pyRestTable': '2020.0.3', 'spec2nexus': '2021.1.8'}
#P0
#N 12
#L theta ttheta Epoch_float Epoch theta_user_setpoint theta_backlash theta_backlash_velocity ttheta_user_setpoint ttheta_backlash ttheta_backlash_velocity noisy th_tth_permit
4.0 8.0 5.639213562011719 6 4.0 0.0 1.0 8.0 0.5 0.2 0.0 37946.01826904763
3.6670000000000003 7.33 10.168693780899048 10 3.6666666666666665 0.0 1.0 7.333333333333333 0.5 0.2 0.0 43240.06624621665
3.333 6.67 14.600727081298828 15 3.3333333333333335 0.0 1.0 6.666666666666667 0.5 0.2 0.0 47339.97568055583
3.0 6.0 19.046493530273438 19 3.0 0.0 1.0 6.0 0.5 0.2 0.0 52702.49931930254
#C Fri Jul 23 20:58:48 2021. num_events_baseline = 2
#C Fri Jul 23 20:58:48 2021. num_events_primary = 4
#C Fri Jul 23 20:58:48 2021. exit_status = success
#S 347 scan(detectors=['noisy', 'th_tth_permit'], num=4, args='['theta', 4.0, 3.0, 'ttheta', 8, 6]', per_step='None')
#D Fri Jul 23 21:03:30 2021
#C Fri Jul 23 21:03:30 2021. plan_type = generator
#C Fri Jul 23 21:03:30 2021. uid = 8869043f-56fc-4b3b-ad75-bc024694e6d6
#MD uid = 8869043f-56fc-4b3b-ad75-bc024694e6d6
#MD beamline_id = Bluesky_training
#MD detectors = ['noisy', 'th_tth_permit']
#MD instrument_name = Bluesky Case Studies
#MD login_id = mintadmin@mint-vm
#MD motors = ('theta', 'ttheta')
#MD notebook = UB_autosave
#MD num_intervals = 3
#MD num_points = 4
#MD objective = Demonstrate UB matrix save & restore
#MD pid = 10389
#MD plan_pattern = inner_product
#MD plan_pattern_args = {'num': 4, 'args': ["MyMotor(prefix='gp:m1', name='theta', settle_time=0.0, timeout=None, read_attrs=['user_readback', 'user_setpoint', 'backlash', 'backlash_velocity'], configuration_attrs=['user_offset', 'user_offset_dir', 'velocity', 'acceleration', 'motor_egu'])", 4.0, 3.0, "MyMotor(prefix='gp:m2', name='ttheta', settle_time=0.0, timeout=None, read_attrs=['user_readback', 'user_setpoint', 'backlash', 'backlash_velocity'], configuration_attrs=['user_offset', 'user_offset_dir', 'velocity', 'acceleration', 'motor_egu'])", 8, 6]}
#MD plan_pattern_module = bluesky.plan_patterns
#MD proposal_id = training
#MD versions = {'apstools': '1.5.1', 'bluesky': '1.7.0', 'databroker': '1.2.3', 'epics': '3.5.0', 'h5py': '3.2.1', 'intake': '0.6.2', 'matplotlib': '3.3.4', 'numpy': '1.20.3', 'ophyd': '1.6.1', 'pyRestTable': '2020.0.3', 'spec2nexus': '2021.1.8'}
#P0
#N 12
#L theta ttheta Epoch_float Epoch theta_user_setpoint theta_backlash theta_backlash_velocity ttheta_user_setpoint ttheta_backlash ttheta_backlash_velocity noisy th_tth_permit
4.0 8.0 5.540862798690796 6 4.0 0.0 1.0 8.0 0.5 0.2 0.0 37996.6450939875
3.6670000000000003 7.33 9.968387842178345 10 3.6666666666666665 0.0 1.0 7.333333333333333 0.5 0.2 0.0 42649.32391462264
3.333 6.67 14.498717546463013 14 3.3333333333333335 0.0 1.0 6.666666666666667 0.5 0.2 0.0 46995.88551308093
3.0 6.0 19.0245144367218 19 3.0 0.0 1.0 6.0 0.5 0.2 0.0 54150.34088276983
#C Fri Jul 23 21:03:49 2021. num_events_baseline = 2
#C Fri Jul 23 21:03:49 2021. num_events_primary = 4
#C Fri Jul 23 21:03:49 2021. exit_status = success
#S 348 scan(detectors=['noisy', 'th_tth_permit'], num=4, args='['theta', 4.0, 3.0, 'ttheta', 8, 6]', per_step='None')
#D Fri Jul 23 21:04:04 2021
#C Fri Jul 23 21:04:04 2021. plan_type = generator
#C Fri Jul 23 21:04:04 2021. uid = e9498d10-8fb7-41c4-aaba-f5204db32761
#MD uid = e9498d10-8fb7-41c4-aaba-f5204db32761
#MD beamline_id = Bluesky_training
#MD detectors = ['noisy', 'th_tth_permit']
#MD instrument_name = Bluesky Case Studies
#MD login_id = mintadmin@mint-vm
#MD motors = ('theta', 'ttheta')
#MD notebook = UB_autosave
#MD num_intervals = 3
#MD num_points = 4
#MD objective = Demonstrate UB matrix save & restore
#MD pid = 10389
#MD plan_pattern = inner_product
#MD plan_pattern_args = {'num': 4, 'args': ["MyMotor(prefix='gp:m1', name='theta', settle_time=0.0, timeout=None, read_attrs=['user_readback', 'user_setpoint', 'backlash', 'backlash_velocity'], configuration_attrs=['user_offset', 'user_offset_dir', 'velocity', 'acceleration', 'motor_egu'])", 4.0, 3.0, "MyMotor(prefix='gp:m2', name='ttheta', settle_time=0.0, timeout=None, read_attrs=['user_readback', 'user_setpoint', 'backlash', 'backlash_velocity'], configuration_attrs=['user_offset', 'user_offset_dir', 'velocity', 'acceleration', 'motor_egu'])", 8, 6]}
#MD plan_pattern_module = bluesky.plan_patterns
#MD proposal_id = training
#MD versions = {'apstools': '1.5.1', 'bluesky': '1.7.0', 'databroker': '1.2.3', 'epics': '3.5.0', 'h5py': '3.2.1', 'intake': '0.6.2', 'matplotlib': '3.3.4', 'numpy': '1.20.3', 'ophyd': '1.6.1', 'pyRestTable': '2020.0.3', 'spec2nexus': '2021.1.8'}
#P0
#N 12
#L theta ttheta Epoch_float Epoch theta_user_setpoint theta_backlash theta_backlash_velocity ttheta_user_setpoint ttheta_backlash ttheta_backlash_velocity noisy th_tth_permit
4.0 8.0 5.37143349647522 5 4.0 0.0 1.0 8.0 0.5 0.2 0.0 38949.839143231766
3.6670000000000003 7.33 9.859706163406372 10 3.6666666666666665 0.0 1.0 7.333333333333333 0.5 0.2 0.0 43461.79736667226
3.333 6.67 14.294379949569702 14 3.3333333333333335 0.0 1.0 6.666666666666667 0.5 0.2 0.0 47545.930496638146
3.0 6.0 18.838080167770386 19 3.0 0.0 1.0 6.0 0.5 0.2 0.0 53464.77967307891
#C Fri Jul 23 21:04:23 2021. num_events_baseline = 2
#C Fri Jul 23 21:04:23 2021. num_events_primary = 4
#C Fri Jul 23 21:04:23 2021. exit_status = success
User Code File#
A common request from instruments is to allow the user to write some python code that can be loaded into the current session. Since development of this user code is often iterative, it must be possible to reload the code without requiring the session to exit and restart.
The local file user/quick_hello.py
provides an example of such a user code file. Load this file with the command:
%run -im user.quick_hello
The %run
is an IPython Magic command. Magic commands are only available from the interactive IPython command line (either in a console or Jupyter notebook). You can’t use them in your Python files, functions, classes, etc.
The user/
directory must be on the import path, which we did as part of starting the instrument
package (above). Also, user/
must contain a file named init.py so that user
is an importable Python package.
The m
(as module) command option was added so it is not necessary to give the Python extension .py
.
Alternatively, we could use the command:
%run -i user/quick_hello.py
which loads a Python file from a directory.
SPEC users
For SPEC users, the %run -i directory/file
syntax is the IPython (and Jupyter notebook) equivalent of SPEC’s qdo spec_macro.mac
command.
This file demonstrates the quintessential Hello, World! demonstration in the context of the Bluesky framework. It defines an ophyd HelloDevice
class which is used to create a Python object for control: hello_device
. Finally, it creates a hello_world()
(bluesky) plan which can be used with the bluesky RunEngine to run the Hello, World! demonstration.
First, load the code from this file:
[12]:
%run -im user.quick_hello
Loading 'Hello, World!' example.
[13]:
findpv("gp:userCalc8.CALC")
NameError: name 'findpv' is not defined
Observe that the same .calculation
is both readable and writable from two different ophyd objects: calcs.calc8
and temperature
.
databroker#
The databroker package provides a Python interface to the database with the experiment data, including references to the large file content such as area detector images. A YAML configuration file connects databroker with a specific repository in the MongoDB database server. In the example here (bluesky_class.yml), the name of the catalog entry in this file is class_2021_03
. It makes two connections
to a MongoDB server running on the same workstation localhost
. Both connections are to the same MongoDB collection: class_2021_03-bluesky
. The name of the file is not important as long as it is placed in a directory searched by databroker.catalog
.
Example
# file: bluesky_class.yml
# purpose: Configuration file to connect Bluesky databroker with MongoDB
# For 2021-03 Python Training at APS
# Copy to: ~/.local/share/intake/bluesky_class.yml
# Create subdirectories as needed
sources:
class_2021_03:
args:
asset_registry_db: mongodb://localhost:27017/class_2021_03-bluesky
metadatastore_db: mongodb://localhost:27017/class_2021_03-bluesky
driver: bluesky-mongo-normalized-catalog
When the bluesky session starts, this class_2021_03
catalog is referenced when creating the db
object in the instrument package, in instrument.framework.initialize.py
by these lines:
catalog_name = "class_2021_03"
db = databroker.catalog[catalog_name].v1
logger.info(f"using databroker catalog '{catalog_name}'")