Introduction
LCLS2 DAQ supports three types of scans:
- epics PV step-scans using bluesky.
- "on-the-fly" scans of epics PVs
- detector configuration scans
On-The-Fly Scans
These are the simplest scans to run where the DAQ is free-running and a PV is changed while taking data. It's advantages are that it is simpler to run and sometimes quicker (since one does not pause the DAQ to move a motor, for example). A disadvantage is that on a per-shot basis one does not know the precise value of the PV, since the PV is typically only recorded at 1Hz in epicsArch. An example script to move a PV while the DAQ is taking data:
from epics import caget, caput, cainfo val = 790000 delta = 7000 import time while 1: val += delta caput('IM3K4:PPM:CAM:EVR:TRIG4:TDES',val) print(caget('IM3K4:PPM:CAM:EVR:TRIG4:TDES')) time.sleep(5)
EPICS PV Step-Scans
EPICS PV step scans use the BlueSky software from BNL (https://nsls-ii.github.io/bluesky/). In this mode one or more PV's are modified while the DAQ is paused, then data is acquired, either for:
- a fixed number of shots,
- a fixed time, or
- until the event code sequencer completes.
Then the DAQ is paused again and the PV is modified again for the next "step". BlueSky supports complex coordinated motion of multiple PV's
An example python script has been created to demonstrate support for PV scans with the RIX instrument.
The source code is here: https://github.com/slac-lcls/lcls2/blob/master/psdaq/psdaq/control/rix_bluesky_scan.py
This script scans a simulated motor at 15 equally spaced points from -10 to 10, recording 120 events at each point. The number of events per step can be adjusted using a command line argument. A user with knowledge of the direct motor should update the script to replace the simulated motor.
The parameters of the scan should be updated to match the requirements of the experiment. To do so, update this section of rix_bluesky_scan.py:
# Scan motor1 from -10 to 10, stopping # at 15 equally-spaced points along the way and reading dets. RE(scan(dets, motor1, -10, 10, 15))
Here is the script running with default settings (see below for optional arguments):
$ python rix_bluesky_scan.py Transient Scan ID: 1 Time: 2020-12-10 18:24:53 Persistent Unique Scan ID: '2092a9ca-160c-4b52-8683-256f699e1e01' New stream: 'primary' +-----------+------------+------------+ | seq_num | time | motor1 | +-----------+------------+------------+ | 1 | 18:24:58.5 | -10.000 | | 2 | 18:24:59.6 | -8.571 | | 3 | 18:25:00.6 | -7.143 | | 4 | 18:25:01.6 | -5.714 | | 5 | 18:25:02.7 | -4.286 | | 6 | 18:25:03.7 | -2.857 | | 7 | 18:25:04.8 | -1.429 | | 8 | 18:25:05.8 | 0.000 | | 9 | 18:25:06.8 | 1.429 | | 10 | 18:25:07.9 | 2.857 | | 11 | 18:25:08.9 | 4.286 | | 12 | 18:25:10.0 | 5.714 | | 13 | 18:25:11.0 | 7.143 | | 14 | 18:25:12.0 | 8.571 | | 15 | 18:25:13.1 | 10.000 | +-----------+------------+------------+ generator scan ['2092a9ca'] (scan num: 1)
$ python lab3_bluesky_scan.py Transient Scan ID: 1 Time: 2021-02-21 16:33:15 Persistent Unique Scan ID: 'fb4d16b7-2844-431e-bb77-cc90cd1f2fe0' New stream: 'primary' +-----------+------------+------------+ | seq_num | time | motor1 | +-----------+------------+------------+ | 1 | 16:33:19.7 | -10.000 | | 2 | 16:33:19.8 | -8.571 | | 3 | 16:33:19.9 | -7.143 | | 4 | 16:33:20.0 | -5.714 | | 5 | 16:33:20.1 | -4.286 | | 6 | 16:33:20.3 | -2.857 | | 7 | 16:33:20.4 | -1.429 | | 8 | 16:33:20.5 | 0.000 | | 9 | 16:33:20.6 | 1.429 | | 10 | 16:33:20.7 | 2.857 | | 11 | 16:33:20.8 | 4.286 | | 12 | 16:33:20.9 | 5.714 | | 13 | 16:33:21.0 | 7.143 | | 14 | 16:33:21.1 | 8.571 | | 15 | 16:33:21.2 | 10.000 | +-----------+------------+------------+ generator list_scan ['fb4d16b7'] (scan num: 1)
Preparing for a PV Scan
As a general rule, one should always verify that the DAQ is running well in "normal" mode before attempting to run a scan.
Some specific tips:
- Make sure all the detectors to be used are configured properly.
- Make sure the timing system is configured properly. As of this writing, RIX uses readout groups 2 and 5.
- When you're ready to record a scan, don't forget to enable recording before running rix_bluesky_scan.py (or use the --record argument)! In the control GUI, the disk icon has a diagonal line over it when not recording.
- Use the "normal" DAQ interface to select the detectors of interest as "active" and assign their readout groups.
- Put the DAQ in ALLOCATED or CONNECTED state. Now, you are ready to run rix_bluesky_scan.py.
The rix_bluesky_scan.py script accepts several command line arguments. If you are relying on the default settings, make sure they are correct for your experiment now, not just way back in 2020 when the script was first written.
usage: rix_bluesky_scan.py [-h] [-p {0,1,2,3,4,5,6,7}] [-C COLLECT_HOST] [-t TIMEOUT] [-c READOUT_COUNT] [-g GROUP_MASK] [--config ALIAS] [--detname DETNAME] [--scantype SCANTYPE] [--seqctl SEQCTL [SEQCTL ...]] [--record] [-v] optional arguments: -h, --help show this help message and exit -p {0,1,2,3,4,5,6,7} platform (default 2) -C COLLECT_HOST collection host (default drp-neh-ctl001) -t TIMEOUT timeout msec (default 10000) -c READOUT_COUNT # of events to aquire at each step (default 120) -g GROUP_MASK bit mask of readout groups (default 36) --config ALIAS configuration alias (default BEAM) --detname DETNAME detector name (default 'scan') --scantype SCANTYPE scan type (default 'scan') --seqctl SEQCTL [SEQCTL ...] sequence control (e.g. DAQ:NEH:XPM:0:SeqReset 4 [DAQ:NEH:XPM:0:SeqDone]) --record enable recording of data -v be verbose
step_value Handling
PV scans automatically generate a 1-based step counter named "step_value" and record it alongside motor positions, like so:
$ xtcreader -f /cds/data/psdm/rix/rixx43518/xtc/rixx43518-r0518-s007-c000.xtc2 -d ... event 3, BeginStep transition: time 1003603961.730737554, env 0x06040024, payloadSize 147 extent 159 Found 4 names 0: 'step_value' rank 0, type 7 'step_value': 1 1: 'step_docstring' rank 1, type 10 'step_docstring': "{"detname": "scan", "scantype": "scan", "step": 1}" 2: 'fast_motor1' rank 0, type 9 'fast_motor1': 1.000000 3: 'fast_motor2' rank 0, type 9 'fast_motor2': 1.000000 ... event 8, BeginStep transition: time 1003603961.964178292, env 0x060e0024, payloadSize 147 extent 159 Found 4 names 0: 'step_value' rank 0, type 7 'step_value': 2 1: 'step_docstring' rank 1, type 10 'step_docstring': "{"detname": "scan", "scantype": "scan", "step": 2}" 2: 'fast_motor1' rank 0, type 9 'fast_motor1': 3.250000 3: 'fast_motor2' rank 0, type 9 'fast_motor2': 3.250000 ...
One can override "step_value" by creating a motor named "step_value" and including it among other motors in the scan. This step count also appears in the JSON formatted "step_docstring" automatically.
Detector Configuration Scans
These scans use the same "step" idea described above for EPICS PV step-scans, but instead alter a detector configuration object on each step. Currently this has been done for the EPIX area detector to take data in the various gain ranges. Python code for this can be found here: https://github.com/slac-lcls/lcls2/blob/master/psdaq/psdaq/app/epixhr_pedestal_scan.py
Script Control of Run Stop/Start
One can control the DAQ (e.g. to stop and start runs on a timer) with scripts like this:
#!/bin/bash while [ 1 ] do echo going to configured daqstate -p 0 -P tmo -C drp-neh-ctl001 --state configured sleep 5 echo going to running daqstate -p 0 -P tmo -C drp-neh-ctl001 --state running sleep 600 done
Running Scans with Hutch Python
UED hutch python logs are here: /cds/group/pcds/pyps/apps/hutch-python/ued/logs/2022_04
GUI version logs: /cds/data/iocData/ioc-ued-bsgui-qs/iocInfo
An example hutch-python scan session in RIX. "rix3" also takes a "--debug" option to increase verbosity. TMO has a similar "tmo3" command.
rix-daq:~> rix3 Loading local disk python env pcds-4.1.4 _ _____ _ _ (_) | __ \ | | | | _ __ ___ _| |__) | _| |_| |__ ___ _ __ | '__| \ \/ / ___/ | | | __| '_ \ / _ \| '_ \ | | | |> <| | | |_| | |_| | | | (_) | | | | |_| |_/_/\_\_| \__, |\__|_| |_|\___/|_| |_| __/ | |___/ INFO Selected default hutch-python daq platform: 0 INFO Loading debug tools... (lines removed for brevity) In [1]: daq.configure(motors=[sim.fast_motor1, sim.fast_motor2],events=10,record=True) INFO configure - configure: 2 motors Out[1]: ({}, {}) In [2]: RE(bp.scan([daq], sim.fast_motor1, 1, 10, sim.fast_motor2, 1, 10, 5)) Transient Scan ID: 1 Time: 2021-10-20 12:06:21 Persistent Unique Scan ID: '81544aea-5fb7-4017-b03d-a1ea8d15c174' New stream: 'primary' +-----------+------------+-------------+-------------+ | seq_num | time | fast_motor1 | fast_motor2 | +-----------+------------+-------------+-------------+ | 1 | 12:06:33.3 | 1 | 1 | | 2 | 12:06:34.4 | 3.25 | 3.25 | | 3 | 12:06:35.5 | 5.5 | 5.5 | | 4 | 12:06:36.6 | 7.75 | 7.75 | | 5 | 12:06:37.7 | 10 | 10 | +-----------+------------+-------------+-------------+ generator scan ['81544aea'] (scan num: 1) Out[2]: ('81544aea-5fb7-4017-b03d-a1ea8d15c174',) In [3]: quit rix-daq:control>
In UED, Zach also recommended this way of doing a scan with a fake motor inside hutch python:
RE(motor_pv_scan('TEST:MOT', 1, 10, 10, events=360, record=False))
These are the parameters that are settable in daq.configure (see these with "daq.configure?" in hutch-python):
Signature: daq.configure( *, motors=None, group_mask=None, events=None, record=None, detname=None, scantype=None, serial_number=None, alg_name=None, alg_version=None, ) Docstring: Set parameters for scan. Keyword arguments: motors -- list of motors, optional Motors with positions to include in the data stream group_mask -- int, optional Bit mask of readout groups events -- int, optional Number of events per scan point record -- bool, optional Enable recording of data detname -- str, optional Detector name scantype -- str, optional Scan type serial_number -- str, optional Serial number alg_name -- str, optional Algorithm name alg_version -- list of 3 version numbers, optional Algorithm version File: /u1/rixopr/conda_envs/pcds-5.0.0/lib/python3.9/site-packages/psdaq/control/BlueskyScan.py Type: method
Other hutch-python parameters are settable in this per-hutch configuration file:
rix-daq:control> more /cds/group/pcds/pyps/apps/hutch-python/rix/conf.yml hutch: rix # Locate happi database db: /reg/g/pcds/pyps/apps/hutch-python/device_config/db.json # Hutch-specific imports load: rix.beamline # DAQ interface configuration daq_type: lcls2 daq_host: drp-neh-ctl001 daq_platform: default: 2 rix-daq:control>
LCLS2 DAQ Python interface FAQ
Q1) How to find the current DAQ state?
A1) Call control.getStatus()
Q2) How to determine whether the DAQ has already been configured?
A2) Check if the current DAQ state is any of "configured", "starting", "paused", or "running".
Q3) How to force a "configure" transition before the next scan?
A3) Set the DAQ state to "connected".