Initial Thoughts

  • all controlled by bluesky
  • multiple scan variables can be controlled either by controls and/or daq
    • variables could be drp parameters
  • option 1 (preferred for uniformity): all scan variables made visible in epics where possible
    • issue: feels like this could be awkward for daq: need another thread/process watching epics.  maybe device doesn't permit separate configure/data processes (e.g. if they share same pgp virtual channel)
  • option 2: scan variable not epics and is a daq var controlled by a daq executable participating in transitions:
    • transmit configUpdate json on zmq phase of transition
    • json specifies which detector is responsible (e.g. xpphsd) for updating scan var, and an epics variable to set when complete
    • that daq executable must understand that json and record it to xtc configUpdate transition
  • at configure time: bluesky hands json describing the scan (names, initial values) to the scanning node(s) which are translated to xtc by json2xtc
  • at configUpdate time: get similar json just with updated values
  • this json is distributed on first phase of configUpdate transition
  • who receives this scan info?
  • how does the daq tell bluesky if its scan var is ready to go?
  • configUpdate transition goes through the timing system with usual two phase pattern
  • ami only moves forward in time.  for arrays only have to update dgram (since python-config points to arrays) while for numbers/charstr/enum have to update both dgram and python-config
    • issue: dgrams are read-only

BlueSky Conversation with Teddy/Zach/Clemens/Chris

conda install bluesky ophyd (comes from conda-forge)

http://nsls-ii.github.io/bluesky/hardware

bluesky devices:
readableDevice (go take a reading and tell us result, read is synchronous)
flyableDevice (start the device, then leave it to do it's own thing asynchronously)

we don't use settableDevice (like a motor)

ophyd is a collection of specific devices that implement the bluesky xface
(e.g. epicsmotor)

following https://github.com/pcdshub/pcdsdaq/blob/master/pcdsdaq/daq.py to be a bluesky device, need: (documentation site has a more complete list)

  • stop
  • trigger
  • read
  • describe
  • kickoff (optional?)
  • complete (if we have kickoff)
  • collect (if we have kickoff)
  • describe_collect ("")
  • configure (was used for number-of-events to record, or duration)
  • read_configuration
  • describe_configuration
  • stage
  • unstage
  • pause
  • resume

Be aware:

  • make sure to return correct "status" objects from appropriate methods
  • try not to block: start a start a subprocess, and return a "future" status
  • if we return correct status, then don't have to do future stuff: gets handled by bluesky
  • obscure: not clear exactly what configure should do

Interaction with DAQ

See page from Chris Ford here: DAQ Scans

Results of a conversation with Chris F. and Matt on  

4 ways for scans to interact with the daq:

  • tell the daq to take, e.g., 1000 events with readout group 4 (not counting events with dead time)
    • Controlled via two PVs
      • $(BASE):XPM:$(MASTER):PART:$(GROUP):StepEnd - Event number to stop at
      • $(BASE):XPM:$(MASTER):PART:$(GROUP):StepGroups - Readout groups to stop
    • End monitored via PV $(BASE):XPM:$(MASTER):PART:$(GROUP):StepDone
      • resets to 0 when Event number set
      • raises to 1 when Event number reached 
  • launch a sequence (more complex than the above but, in some sense, a special case)
    • Controlled via several PVs or seqprogram.py <sequence.py> <PVBase> (PVBase is $(BASE):XPM:$(MASTER):SEQENG:0, for example)
    • End monitored via PV $(PVBase):RUNNING
      • raises to 1 when sequence starts
      • resets to 0 when sequence completes (assuming CheckPoint instruction is in <sequence.py> - see psdaq/psdaq/sequence/finite.py for an example)
  • free-run until a user-defined drp trigger count (from the teb) is met
  • free-run until a user-defined ami condition is met.  only suitable for low-rate expts where all events can be monitored, but easier and more flexible than the drp-trigger-count method above

All 4 mechanisms will indicate completion via EPICS variables.  We will try to get the EPICS variables to behave in the same fashion to make it more uniform for the BlueSky script.

Config Scans

Discussion with Matt on Aug. 24, 2020

  • DAQ will code config object "deltas" on begin step transition
  • deltas will be sent to segment level as json and use mcbrowne's JsonToXtc2 translator (unlike epics scans which use Eliseo's translation in BlueSky script)
  • deltas will have a special "alg" field ("configDelta"?) that mona can use to detect them (the "drpClassName" on this page: Raw Data Python Interface)
  • deltas will be associated with the original config dgram by the detector name (plus other info?)
  • mona will apply deltas using the python "dot" interface, and hopefully the reference counting is done correctly in dgram.cc so that works (not 100% confident of this)
  • we would ideally support jumping backward and forward in time across calibcycles, as we do for the epics variables

Meta Data Discussion

Discussion on Dec. 3, 2020 with Matt/Mikhail/Dan/Chris

  • Currently the Step object has two pieces of metadata that represent the scan step:  "docstring" and "value"
  • The actual "complex" changes on a step are updated in the epics store (epics scans) or the appropriate configuration objects (configuration scans)
  • We should support scanning multiple detectors at the same time
  • Feels like that requires some known structure in the meta data, e.g. if we are scanning 3 epix detectors
  • Mikhail suggests maybe we should use json for the docstring instead of creating our own language?
rule for the docstring? "detname_scanname_step"
scanname "chargeinj7" means every 7th pixel
scanname "chargeinj3" means every 3rd pixel

step_value = 0
step_docstring = "epix0_chargeinj_0; epix3_chargeinj_0; epix7_chargeinj_0"

step_value = 1
step_docstring = "epix0_chargeinj_1; epix3_chargeinj_1; epix7_chargeinj_1"

Meta Data Example (ued)

experiment ueddaq02 run 28
psexport01$ xtcreader -d -f /cds/data/psdm/ued/ueddaq02/xtc/ueddaq02-r0028-s000-c000.xtc2 | grep step_docstring
Name: 'step_docstring' Type: 10 Rank: 1
1: 'step_docstring' rank 1, type 10
'step_docstring': "{"detname": "epixquad_0", "scantype": "pedestal", "step": 0}"
1: 'step_docstring' rank 1, type 10
'step_docstring': "{"detname": "epixquad_0", "scantype": "pedestal", "step": 1}"
1: 'step_docstring' rank 1, type 10
'step_docstring': "{"detname": "epixquad_0", "scantype": "pedestal", "step": 2}"
1: 'step_docstring' rank 1, type 10
'step_docstring': "{"detname": "epixquad_0", "scantype": "pedestal", "step": 3}"
1: 'step_docstring' rank 1, type 10
'step_docstring': "{"detname": "epixquad_0", "scantype": "pedestal", "step": 4}"
 
psexport01$ xtcreader -d -f /cds/data/psdm/ued/ueddaq02/xtc/ueddaq02-r0028-s000-c000.xtc2 | grep step_value
Name: 'step_value' Type: 7 Rank: 0
0: 'step_value' rank 0, type 7
'step_value': 0
0: 'step_value' rank 0, type 7
'step_value': 1
0: 'step_value' rank 0, type 7
'step_value': 2
0: 'step_value' rank 0, type 7
'step_value': 3
0: 'step_value' rank 0, type 7
'step_value': 4

Psana Step-Store Approach

The idea here is tomake the bluesky scans look the same as det-config scans.  Mona’s step-store (from “detnames -s”) contains a list of variables, which in both cases includes step_value, step_docstring ("metadata").  In the case of a bluesky scan it also includes the PV values.  In the case of a config scan the configs themselves get updated on each step.

Sample Scan Script

Note that the step_docstring is json as per Mikhail's suggestion above.

from psana import DataSource
import sys
# a config scan for epix                                                          
ds = DataSource(exp='ueddaq02',run=28)
myrun = next(ds.runs())
step_value = myrun.Detector('step_value')
step_docstring = myrun.Detector('step_docstring')
for nstep,step in enumerate(myrun.steps()):
    print('step:',nstep,step_value(step),step_docstring(step))
    for nevt,evt in enumerate(step.events()):
        if nevt==3: print('evt3:',nstep,step_value(evt),step_docstring(evt))
  • No labels