Finite Burst Sequence With 1 Slow Andor

Summary: A 33kHz beam train simulation (28 buckets between shots) with a starting gap of 14000 buckets to allow a periodic 1Hz Andor to readout while not receiving beam.  A 100Hz trigger is added for other non-integrating detectors (e.g. opals).  Each sequence is repeated twice, although there appears to be an "off by one" difference in the definition of the repeat count of traingenerator (repeat=2) and periodicgenerator (repeat=1).  The "--notify" flag on periodic generator tells the DAQ to end the scan step after that finite sequence is complete after 2 andor images.  The andor "shutter" is always open, so the trigger event-code only starts the readout, not the exposure.  The first andor readout-trigger in the first step clears out old charge at the beginning so the very first image data is "corrupted".  Note that 14000+32000*28=910000 (14000 is starting gap for andor readout, and 32000 shots 28 buckets apart adds up to the 910000 buckets that the accelerator always repeats every 0.98 seconds).  The number of beam shots is set to 32001 instead of 32000 because without this there would be no high-rate beam shot at the same time as the andor trigger (this can be seen with seqbrowser.py): there is a requirement in the DAQ that the highest rate trigger must show up in every event, except for rare cases of deadtime.

export EPICS_PVA_ADDR_LIST=172.21.152.78 (allows rix-daq to talk to the XPM, e.g. to program sequences)

source /cds/group/pcds/dist/pds/rix/scripts/setup_env.sh
traingenerator -s 14000 -b 28 -n 32001 -r 2 -d "burst" -t 910000 >& /tmp/beam.py
periodicgenerator -p 910000 9100 -s 0 0 -d '1Hz' '100Hz' --repeat 1 --notify >& /tmp/codes.py 
seqprogram --seq 0:/tmp/codes.py 3:/tmp/beam.py --pv DAQ:NEH:XPM:3

beam.py is a simulation of a 33kHz beam, while codes.py is used to generate triggers for two cameras, one of which is an integrating-over-shots andor.

The output of "python tools/seqbrowser.py --seq 0:codes.py 3:beam.py --time 2.0" looks like:

Command to view the status of the sequence (note that for this run the simulated-beam event code 284 was running at 10kHz, not the 33kHz in the example above):

rix-daq:~> pvget DAQ:NEH:XPM:3:SEQCODES
DAQ:NEH:XPM:3:SEQCODES 2023-05-22 17:26:52.369    
EventCode Description  Rate Enabled
      272                 1       1
      273               102       1
      274                 0       1
      275                 0       1
      276                 0       0
      277                 0       0
      278                 0       0
      279                 0       0
      280                 0       0
      281                 0       0
      282                 0       0
      283                 0       0
      284             10206       1
      285                 0       1
      286                 0       1
      287                 0       1
rix-daq:~> 

Command to start a sequence that has ended (note that 9=(8<<3) | (1<<0) so sequence engines 3 and 0 are restarted):

pvput DAQ:NEH:XPM:3:SeqReset 9

Or one can add "--start" to the seqprogram command.

A hutch-python scan (using the finite sequences generated by traingenerator.py/burstgenerator.py):

In [4]: daq.configure(motors=[sim.fast_motor1],group_mask=0x24, events=0, detname='andor_norm_0', seq_ctl=['DAQ:NEH:XPM:3:SeqReset',9,'DAQ:NEH:XPM:3:SeqDone'])
INFO     configure - configure: 1 motors
INFO     configure - Found readout group 5 for andor_norm_0.
Out[4]: ({}, {})
In [5]: RE(bp.scan([daq], sim.fast_motor1, -10, 10, 5))

An example run for this mode is rixx1003821 run 68 which has been made public in the directory /cds/data/psdm/prj/public01/xtc/.  A simple analysis script counting events along with output is:

(ps-4.5.26) drp-srcf-eb009:lcls2$ python junk5.py
nstep/nevent/nnorm 0 64001 2
nstep/nevent/nnorm 1 64001 2
nstep/nevent/nnorm 2 64001 2
nstep/nevent/nnorm 3 64001 2
nstep/nevent/nnorm 4 64001 2

(ps-4.5.26) drp-srcf-eb009:lcls2$ cat junk5.py
from psana import DataSource
import datetime as dt
import numpy as np
import sys

ds = DataSource(exp='rixx1003821',run=68,dir='/cds/data/psdm/prj/public01/xtc')
myrun = next(ds.runs())
andor_norm = myrun.Detector('andor_norm')

for nstep,step in enumerate(myrun.steps()):
    nnorm = 0
    for nevent,evt in enumerate(step.events()):
        norm_value = andor_norm.raw.value(evt)
        if norm_value is not None: nnorm+=1
    print('nstep/nevent/nnorm',nstep,nevent,nnorm)
(ps-4.5.26) drp-srcf-eb009:lcls2$ 

Infinite Sequence With 2 Different Slow Andor Rates

The only way to stop a running infinite sequence is to program another sequence.

  • python psdaq/psdaq/seq/rixgeneratory.py --periods 1.0 0.01  (units in seconds).  Generates three files beam.py, codes.py and codes2.py in the current directory which should be programmed with "seqprogram".
    • beam.py simulates the beam pattern with gaps left for reading out both the fast and slow andor (eventually programmed on the accelerator side)
    • codes.py generates periodic triggers for a low rate andor and a high rate andor, but the high rate andor continues (needlessly) reading out when the beam is stopped for the low rate andor (see next point for solution)
    • codes2.py generates periodic triggers for the high rate andor, but avoids triggering it when the beam is inhibited for the slow andor readout.  because of this it is called "fast gated andor" in codes2.py 
  • seqprogram --seq 0:codes.py 1:codes2.py 3:beam.py --pv DAQ:NEH:XPM:3 --start
(ps-4.5.26) rix-daq:scripts> pvget DAQ:NEH:XPM:3:SEQCODES
DAQ:NEH:XPM:3:SEQCODES 2023-06-09 19:20:09.502    
EventCode        Description  Rate Enabled
      272       "Slow Andor"     1       1
      276 "Fast Andor Gated"    63       1
      284      "Bunch Train" 10206       1

The output of "python tools/seqbrowser.py --seq 0:codes.py 1:codes2.py 3:beam.py --time 4.0" looks like this:

Running a standalone daq step-scan (no hutch-python).  The number "9" corresponds to two bits: 9=8+1, or 9=(1<<3) | (1<<0), where 3 and 0 are the two sequence engines that have been programmed with seqprogram above.  The "-g" argument is for the readout groups in the partition (2,5): 36=(1<<5) | (1<<2).

python psdaq/psdaq/control/rix_bluesky_scan.py -p 2 -C drp-srcf-cmp004 -g 36 --detname andor_norm_0 --seqctl DAQ:NEH:XPM:3:SeqReset 9 DAQ:NEH:XPM:3:SeqDone

A hutch-python scan where the sequence runs continuously, recording 1 slow-andor event per step.  In this case the start of the DAQ acquisition in each step is asynchronous to the running sequence.

In [8]: daq.configure(motors=[sim.fast_motor1],group_mask=0x24, events=200, detname='andor_norm_0', record=True)
INFO     configure - configure: 1 motors
INFO     configure - Found readout group 5 for andor_norm_0.
Out[8]: ({}, {})

In [9]: RE(bp.scan([daq], sim.fast_motor1, -10, 10, 5))

An example run for this mode is rixx1003821 run 55 which has been made public in the directory /cds/data/psdm/prj/public01/xtc/.  A simple analysis script and associated output is here.  Since the sequence is running continuously the "phase" of the scan startup is random on each step with respect to the sequence, and so the number of beam shots and slow-andor triggers have some randomness, while the faster andor has a well defined number of events (200).

(ps-4.5.26) drp-srcf-eb009:lcls2$ python junk4.py
nstep/nevent/nnorm/ndir 0 32399 200 3
nstep/nevent/nnorm/ndir 1 32283 200 3
nstep/nevent/nnorm/ndir 2 32399 200 3
nstep/nevent/nnorm/ndir 3 32284 200 3
nstep/nevent/nnorm/ndir 4 32298 200 4

(ps-4.5.26) drp-srcf-eb009:lcls2$ more junk4.py
from psana import DataSource
import datetime as dt
import numpy as np
import sys

ds = DataSource(exp='rixx1003821',run=55,dir='/cds/data/psdm/prj/public01/xtc')
myrun = next(ds.runs())
andor_norm = myrun.Detector('andor_norm')
andor_dir = myrun.Detector('andor_dir')

for nstep,step in enumerate(myrun.steps()):
    nnorm = 0
    ndir = 0
    for nevent,evt in enumerate(step.events()):
        norm_value = andor_norm.raw.value(evt)
        dir_value = andor_dir.raw.value(evt)
        if norm_value is not None: nnorm+=1
        if dir_value is not None: ndir+=1
    print('nstep/nevent/nnorm/ndir',nstep,nevent,nnorm,ndir)
(ps-4.5.26) drp-srcf-eb009:lcls2$  

Andor Normalization With Realtime Plotting

Run with "mpirun -n 5 python andor.py" with "psplot ANDOR" on the same node.

from psana import DataSource
from psmon import publish
from psmon.plots import Image,XYPlot
import os
from mpi4py import MPI
comm = MPI.COMM_WORLD
rank = comm.Get_rank()
size = comm.Get_size()

os.environ['PS_SRV_NODES']='1'

if rank==4: # hack for now to eliminate use of publish.local below
    publish.init()

# we will remove this for batch processing and use "psplot" instead
# publish.local = True

def my_smalldata(data_dict):
    if 'unaligned_andor_norm' in data_dict:
        andor_norm = data_dict['unaligned_andor_norm'][0]
        myplot = XYPlot(0,"Andor (normalized)",range(len(andor_norm)),andor_norm)
        publish.send('ANDOR',myplot)

ds = DataSource(exp='rixx1003821',run=46,dir='/cds/data/psdm/prj/public01/xtc',intg_det='andor_vls',batch_size=1)
for myrun in ds.runs():
    andor = myrun.Detector('andor_vls')
    timing = myrun.Detector('timing')
    smd = ds.smalldata(filename='mysmallh5.h5',batch_size=1000, callbacks=[my_smalldata])
    norm = 0
    ndrop_inhibit = 0
    for nstep,step in enumerate(myrun.steps()):
        print('step:',nstep)
        for nevt,evt in enumerate(step.events()):
            andor_img = andor.raw.value(evt)
            # also need to check for events missing due to damage
            # (or compare against expected number of events)
            ndrop_inhibit += timing.raw.inhibitCounts(evt)
            smd.event(evt, mydata=nevt) # high rate data saved to h5
            # need to check Matt's new timing-system data on every
            # event to make sure we haven't missed normalization
            # data due to deadtime
            norm+=nevt # fake normalization
            if andor_img is not None:
                print('andor data on evt:',nevt,'ndrop_inhibit:',ndrop_inhibit)
                # check that the high-read readout group (2) didn't
                # miss any events due to deadtime
                if ndrop_inhibit[2]!=0: print('*** data lost due to deadtime')
                # need to prefix the name with "unaligned_" so
                # the low-rate andor dataset doesn't get padded
                # to align with the high rate datasets
                smd.event(evt, mydata=nevt,
                          unaligned_andor_norm=(andor_img/norm))
                norm=0
                ndrop_inhibit=0

Practice Session Nov. 8, 2023

Executive summary of starting plan: we will run continuous sequences for all running (free running and scans).  At the start all the andors will run at the same rate at either 1,10,100Hz in some appropriate combination of FVB,crop,full-frame mode.

To calculate readout time: Take difference of "accumulate period" and "exposure time" reported by the IOC (we don't understand difference between accumulate period and acquire period, but they have always been the same historically).

to do:

  • rix specifies ~3 modes for both andors running at same rate (1,10,100Hz) for  the 3 andor modes.  kristjan will send that matt.
  • how do we say all-laser-on or all laser off in rixgeneratory.py?
  • longer term: there will be an additional ~7 patterns for the three andors running at two different rates
  • how do we detect mps problems? (will kill some shots).  But Matt thinks it doesn't break the machine's TPG sequencing pattern
  • need to fix pva gateway: see timeouts for seqprogram
  • work on psplot_live robustness (e.g. not leaving dangling processes)

andors dir/norm will always have the same rate/exposure
third vls andor is what may cause us to want 2 different rates

archon will be treated like an andor for rate/exposure

longer term epixm will be different has timing gap of 200us (exposure 20us min, readout 150us).  may not have to shutter the camera because the signal is so low?

4 potential andor modes:
- crop mode
- full vertical binning
- full frame
- don't use partial vertical binning for now

rixgeneratory assumes the first one is the slow camera.  times are in seconds.  bunch_period in buckets.  laser_onoff is counted in terms of the slow camera images (the first on in the list).  This shows generating one event code, but lists can be given to generating different andor trigger rates:

(ps-4.6.1) rix-daq:scripts> python ~rixopr/git/lcls2_102323/psdaq/psdaq/seq/rixgeneratory.py  --periods 0.1 --readout_time 0.005 --bunch_period 28 --laser_onoff 2 2
Generating one repeating bunch train for one camera
d {'period': 3250, 'readout': 163}
** Wrote beam.py **
factors of 3250 : [3250]
** Wrote laser.py **
# period 91000  args.period [91000]
** Wrote codes.py **
(ps-4.6.1) rix-daq:scripts> 

beam.py is simulation of accelerator
laser.py triggers laser

To view event code patterns in an interactive plot: seqplot --seq 1:beam.py 2:codes.py 3:laser.py --time 1.0

Program the sequences like this.  Normally the laser.py eventcodes would be programmed in XPM:0 and the beam.py eventcodes would be programmed by ACR into their TPG (timing pattern generator):

seqprogram --seq 1:beam.py 2:codes.py 3:laser.py --pv DAQ:NEH:XPM:3 --start

took rixc00121 run 49 with 31kHz piranha/timing and 10Hz dir/norm andor.
run 50 is a scan with same 

To view status of the 4 sequence engines (4 eventcodes per engine) execute "xpmpva DAQ:NEH:XPM:3" and select the "Groups/Eventcodes" tab.  The "Master" column is important: it says which XPM controls which event code (in units of an "engine": 4 event codes).  The "Ena" and "Dis" buttons (for the appropriate XPM!) can be used to change which XPM controls a particular engine.

Kristjan's tables of andor rates/readout times:

Practice Session April 3, 2024

Commands for 66Hz Andor running with 8kHz beam:

8kHz beam + one camera (66Hz): rixgenerator --bunch_period 112 --periods 0.015385 --readout_time 0.005
8kHz beam + two cameras(1Hz + 66Hz): rixgenerator --bunch_period 112 --periods 1 0.015385
  • No labels