Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

This page holds a few example code-snippets for use in pyana analysis. The analysis is written in python and uses MatPlotLib.PyPlot for plotting of data. Compare with myana user examples to see how (some of) the same things can be done using the myana analysis framework. The most reliable place for up-to-date information about all the event getters in pyana, see: https://confluence.slac.stanford.edu/display/PCDS/Pyana+Reference+Manual#PyanaReferenceManual-Classpyana.event.EventImage Removed

For all the examples, you may assume that the pyana module contains a class with at least 'beginjob', 'event' and 'endjob' functions that starts something like this:

Code Block
nonenone
titleoutline of a pyana module
none
import numpy as np
import matplotlib.pyplot as plt
from pypdsdata import xtc

class mypyana(object):
    def __init__(self,source=""):
        self.source = source
        self.counter = None
        self.array = []   # really just a list

    def beginjob(self,evt,env):
        self.counter = 0

    def event(self,evt,env):
        self.counter += 1

        # snippet code goes here
        thedata = evt.get(xtc.TypeId.Type.Id_SomeType, self.source )
        self.array.append( thedata.somevalue )

    def endjob(self,evt,env):
       print "Job done! Processed %d events. " % self.counter

       # place for plotting etc

       # convert from python list to a numpy array
       self.array = np.array( self.array )

       # plot graph
       plt.plot(self.array)

...

To read out energy, charge and position of the beam from the beamline data, use getEBeam(). It returns a class/structure that has the following members/fields:

none
Code Block
none
titlegetEBeam
none
def event(self,evt,env):

    ebeam = evt.getEBeam()
    try :
        beamChrg = ebeam.fEbeamCharge
        beamEnrg = ebeam.fEbeamL3Energy
        beamPosX = ebeam.fEbeamLTUPosX
        beamPosY = ebeam.fEbeamLTUPosY
        beamAngX = ebeam.fEbeamLTUAngX
        beamAngY = ebeam.fEbeamLTUAngY
        beamPkCr = ebeam.fEbeamPkCurrBC2
        print "ebeam: ", beamChrg, beamEnrg, beamPosX, beamPosY, beamAngX, beamAngY, beamPkCr
    except:
        print "No EBeam object found"

...

To read out the energy from the front end enclosure (FEE) gas detector, use getFeeGasDet(). This returns and array of 4 numbers:

none
Code Block
none
titlegetFeeGasDet
none
    fee_energy_array = evt.getFeeGasDet()
    gdENRC11 = fee_energy_array[0]
    gdENRC12 = fee_energy_array[1]
    gdENRC21 = fee_energy_array[2]
    gdENRC22 = fee_energy_array[3]

    energy = (gdENRC21 + gdENRC22) / 2.0
    # or use the first two that has a different gain:
    energy = (gdENRC11 + gdENRC12) / 2.0

...

To read out fit time and charge of the phase cavity, use getPhaseCavity() which returns a structure with the following fields:

none
Code Block
none
titlegetPhaseCavity
none
     pc = evt.getPhaseCavity()
     try:
         pcFitTime1 = pc.fFitTime1
         pcFitTime2 = pc.fFitTime2
         pcCharge1 = pc.fCharge1
         pcCharge2 = pc.fCharge2
         print "PhaseCavity: ", pcFitTime1,  pcFitTime2, pcCharge1, pcCharge2
      except :
         print "No Phase Cavity object found"

Event code

Code Block
nonenone
titleEnvData
none
def event(self, evt, env):
    evrdata = evt.getEvrData("NoDetector-0|Evr-0")
    
    for i in range (evrdata.numFifoEvents()):
        print "Event code: ", evrdata.fifoEvent(i).EventCode

In the example above, the address of the EvrData object is given as "NoDetector-0|Evr-0". The address may be different in other cases, so make sure you have the correct address. If you don't know what it is, you can use 'pyxtcreader -vv <xtcfile> | less' to browse your xtcfile and look for it. Look for a lines with 'contains=EvrConfig_V' or 'contains=EvrData_V'. The address will be found on the same line in 'src=DetInfo(<address>)'

Encoder data (delay scanner)

Code Block
nonenone
titleEncoderData
none
def event(self,evt,env):
    try:
        encoder = evt.get(xtc.TypeId.Type.Id_EncoderData, self.enc_source )
        encoder_value = encoder.value()
    except:
        print "No encoder found in this event"
        return

...

The time of the event can be obtained within the event function:

none
Code Block
none
titlegetTime
none
def event ( self, evt, env ) :
    event_time = evt.getTime().seconds() + 1.0e-9*evt.getTime().nanoseconds() )

...

Currently there are two data structures that holds data from the same type of devices. Depending on DAQ
configuration, they are either DetInfo type or BldInfo type. Here are examples for extracting both types
in the user module event function:

Code Block
nonenone
titleDetInfo
none
def event(self, evt, env):
    # raw data
    ipmRaw = evt.get(xtc.TypeId.Type.Id_IpimbData, source )
    try:
        ch = [ipmRaw.channel0(),
              ipmRaw.channel1(),
              ipmRaw.channel2(),
              ipmRaw.channel3() ]
                
        ch_volt = [ipmRaw.channel0Volts(),
                   ipmRaw.channel1Volts(),
                   ipmRaw.channel2Volts(),
                   ipmRaw.channel3Volts()]
    except:
        pass

    # feature-extracted data
    ipmFex = evt.get(xtc.TypeId.Type.Id_IpmFex, source )
    try:
         # array of 4 numbers
         fex_channel = ipmFex.channel 

         # scalar values
         fex_sum = ipmFex.sum 
         fex_xpos = ipmFex.xpos
         fex_ypos = ipmFex.ypos

     except:
         pass

Code Block
nonenone
titleBldInfo
none
def event(self, evt, env):
    ipm = evt.getSharedIpimbValue("HFX-DG3-IMB-02")
    # or equivalently:
    # ipm = evt.get(xtc.TypeId.Type.Id_SharedIpimb, "HFX-DG3-IMB-02")
    try: 
        ### Raw data ###
        # arrays of 4 numbers:
        ch = [ipm.ipimbData.channel0(),
              ipm.ipimbData.channel1(),
              ipm.ipimbData.channel2(),
              ipm.ipimbData.channel3()]
        ch_volt = [ipm.ipimbData.channel0Volts(),
                   ipm.ipimbData.channel1Volts(),
                   ipm.ipimbData.channel2Volts(),
                   ipm.ipimbData.channel3Volts()]

        ### Feature-extracted data ###
        # array of 4 numbers:
        fex_channels = ipm.ipmFexData.channel 
        
        # scalars:
        fex_sum = ipm.ipmFexData.sum 
        fex_xpos = ipm.ipmFexData.xpos
        fex_ypos = ipm.ipmFexData.ypos

     except:
         pass

...

In each event, we add the image array returned from the getPrincetonValue function:

none
Code Block
none
titlegetPrincetonValue
none
  def event ( self, evt, env ) :

       frame = evt.getPrincetonValue( self.address, env)
       if frame :
           # accumulate the data
           if self.data is None :
               self.data = np.float_(frame.data())
           else :
               self.data += frame.data()

...

PnCCD image

Code Block
nonenone
titlegetPnCcdValue
none
def event(self,evt,env):
    try:
        frame = evt.getPnCcdValue( self.source, env )
        image = frame.data()
    except:
        pass

...

These all use the generic getFrameValue function:

Code Block
nonenone
titlegetFrameValue
none
def event(self,evt,env):
    try:
        frame = evt.getFrameValue( self.source )
        image = frame.data()
    except:
        pass

...

The Fast CCD is read out as two 8-bit images, therefore you need this extra line to convert it to the right format.

none
Code Block
none
titlegetFrameValue
none
def event(self,evt,env):
    try:
        frame = evt.getFrameValue( self.source )
        image = frame.data()
    except:
        pass

    # convert to 16-bit integer
    image.dtype = np.uint16

...

Here's an example of getting CsPad data from an event:

none
Code Block
none
titlegetCsPadQuads
none
def event(self,evt,env):
    quads = evt.getCsPadQuads(self.img_source, env)
    if not quads :
        print '*** cspad information is missing ***'
        return
        
    # dump information about quadrants
    print "Number of quadrants: %d" % len(quads)
    
    for q in quads:
        print "  Quadrant %d" % q.quad()
        print "    virtual_channel: %s" % q.virtual_channel()
        print "    lane: %s" % q.lane()
        print "    tid: %s" % q.tid()
        print "    acq_count: %s" % q.acq_count()
        print "    op_code: %s" % q.op_code()
        print "    seq_count: %s" % q.seq_count()
        print "    ticks: %s" % q.ticks()
        print "    fiducials: %s" % q.fiducials()
        print "    frame_type: %s" % q.frame_type()
        print "    sb_temp: %s" % map(q.sb_temp, range(4))
            
        # image data as 3-dimentional array
        data = q.data()

Wiki MarkupSo far so good. 'quads' is a *list* of CsPad Element objects, and not necessarily ordered in the expected way. So you'll need to use q.quad() to obtain the quad number.
q.data() gives you a 3D numpy array \ [row\]\[col\]\[sec\]. Here sections will be ordered as expected, but be aware in case of missing sections, that you may need to check the
configuration object. You can get that from the env object, typically something you do in beginjob:

Code Block
none
none
def beginjob(self,evt,env):
    config = env.getConfig(xtc.TypeId.Type.Id_CspadConfig, self.img_source)
    if not config:
        print '*** cspad config object is missing ***'
        return        
    print "Cspad configuration"
    print "  N quadrants   : %d" % config.numQuads()
    print "  Quad mask     : %#x" % config.quadMask()
    print "  payloadSize   : %d" % config.payloadSize()
    print "  badAsicMask0  : %#x" % config.badAsicMask0()
    print "  badAsicMask1  : %#x" % config.badAsicMask1()
    print "  asicMask      : %#x" % config.asicMask()
    print "  numAsicsRead  : %d" % config.numAsicsRead()

   # get the indices of sections in use:
   qn = range(0,config.numQuads())               
   self.sections = map(config.sections, qn )        

...

The CSPad detector image can be drawn by positioning the sections from the data array into a large image array. This is done in cspad_simple.py above. The positions are extracted from optical meterology measurements and additional calibrations. Alternatively one can find the coordinate of each individual pixel from a pixel map, based on the same optical metrology measurements. This is described in details here

Epics Process Variables and ControlConfig

EPICS data is different from DAQ event data. It stores the conditions and settings of the instruments, but values typically change more slowly than your
average shot-by-shot data, and EPICS data is typically updated only when it changes, or every second, or similar. It is not stored in the 'evt' (event) object,
but in the 'env' (environment) object. You typically would read it only at the beginning of each job or if your doing a scan, you'd read it in every calibration cycle:

Code Block
nonenone
titleenv.epicsStore()
none
def begincalibcycle(self,evt,env):

    ## The returned value should be of the type epics.EpicsPvTime.
    pv = env.epicsStore().value( pv_name )
    if not pv:
        logging.warning('EPICS PV %s does not exist', pv_name)
    else:
        value = pv.value 
        status = pv.status 
        alarm_severity = pv.severity 
Code Block
nonenone
titleControlConfig
none
def begincalibcycle(self,evt,env):
    ctrl_config = env.getConfig(xtc.TypeId.Type.Id_ControlConfig)
    
    nControls = ctrl_config.npvControls()
    for ic in range (0, nControls ):

        cpv = ctrl_config.pvControl(ic)
        name = cpv.name()
        value = cpv.value()