cloak.toggle.type = text cloak.toggle.open = + show cloak.toggle.close = - hide |
A quick walk-through of the tools that exist for analysis of xtc files with python.
The main focus is on pyana, and the examples are from and for XPP primarily,
but may be useful examples to other experiments too.
Outline of contents:
http://docs.python.org/tutorial/
Analysis Workbook. Python-based Analysis
Prior to this, you may need to set up your account for offline analysis:
Analysis Workbook. Account Setup
The general version of this is in Analysis Workbook. Quick Tour
|
pyxtcreader -h usage: pyxtcreader [options] xtc-files ... options: -h, --help show this help message and exit -v, --verbose -l L1_OFFSET, --l1-offset=L1_OFFSET |
Loops through the xtc datagrams and dumps info to screen. I recommend piping it to 'less'.
Try the same with different verbosity levels:
|
xtcscanner -h usage: xtcscanner [options] xtc-files ... options: -h, --help show this help message and exit -n NDATAGRAMS, --ndatagrams=NDATAGRAMS -v, --verbose -l L1_OFFSET, --l1-offset=L1_OFFSET -e, --epics |
Similar to pyxtcreader in that it loops throug xtc datagrams, but doesn't print to screen. Internally counts the datatypes it finds, and at the end dumps a summary only. Optinally prints out epics information (default no).
|
You should see output similar to this:
Scanning.... Start parsing files: ['/reg/d/psdm/xpp/xppi0310/xtc/e81-r0098-s00-c00.xtc', '/reg/d/psdm/xpp/xppi0310/xtc/e81-r0098-s01-c00.xtc'] 14826 datagrams read in 4.120000 s . . . . . . . ------------------------------------------------------------- XtcScanner information: - 61 calibration cycles. - Events per calib cycle: [240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240] Information from 1 control channels found: fs2:ramp_angsft_target Information from 11 devices found BldInfo:EBeam: EBeamBld_V1 (14641) BldInfo:FEEGasDetEnergy: FEEGasDetEnergy (14563) Any (78) BldInfo:NH2-SB1-IPM-01: SharedIpimb (14641) BldInfo:PhaseCavity: PhaseCavity (14641) DetInfo:EpicsArch-0|NoDevice-0: Epics_V1 (107580) DetInfo:NoDetector-0|Evr-0: EvrConfig_V4 (62) EvrData_V3 (14640) DetInfo:XppSb2Ipm-1|Ipimb-0: IpimbConfig_V1 (1) IpmFexConfig_V1 (1) IpimbData_V1 (14640) IpmFex_V1 (14640) DetInfo:XppSb3Ipm-1|Ipimb-0: IpimbConfig_V1 (1) IpmFexConfig_V1 (1) IpimbData_V1 (14640) IpmFex_V1 (14640) DetInfo:XppSb3Pim-1|Ipimb-0: IpimbConfig_V1 (1) IpmFexConfig_V1 (1) IpimbData_V1 (14640) IpmFex_V1 (14640) DetInfo:XppSb4Pim-1|Ipimb-0: IpimbConfig_V1 (1) IpmFexConfig_V1 (1) IpimbData_V1 (14640) IpmFex_V1 (14640) ProcInfo:: RunControlConfig_V1 (62) XtcScanner is done! ------------------------------------------------------------- |
XTC Explorer - Old - GUI interface that builds pyana modules for you.
|
(' Now you have a local version of the XtcExplorer package in your directory. That allows you to edit the source code and customize the analysis modules in the Exercise for later: |
The xtcexplorer has several shortcomings. It tries to be very generic, and thus is sometimes slower than it would have to be. It is also currently only capable of plotting from a single device in each plot, so many correlation plots will need to be added by hand. However, it is a simple tool to just look at the data contents, and provides many examples through its pyana modules.
For some more useful analysis examples, in the following we'll stick to writing customized pyana modules and running pyana from the command line.
But before getting to the pyana modules, I'll briefly touch on a few items general to python that may be useful: saving files, matplotlib for plotting, and IPython for interactive work.
These are packages that you may want to look into. Pretty much all our examples here are using them:
Other useful links:
Here are a few examples of how you can save data arrays in python.
|
|
For more examples, see How to access HDF5 data from Python and http://code.google.com/p/h5py/ |
One of the most commonly used tools for plotting in python: matplotlib. Other alternatives exist too.
Matplotlib:
import matplotlib.pyplot as plt plt.plot(array) plt.show() |
from matplotlib.pyplot import * ion() plot(array) draw() |
The LCLS offline analysis group does have plans for a real interactive pyana, but currently this is not available.
2011-11-04 iPsana Interactive Analysis Framework.pdf
The version available in our offline release system is
IPython 0.9.1 – An enhanced Interactive Python.
so this is the one I've been using in these examples.
Not a whole lot more than a python shell.
However, the latest IPython has loads of new and interesting features...
This example reads in a file produced by the "point detector delay scan" example below.
Sometimes you need to issue the draw() command twice, for some reason. After drawing you can keep working on the arrays and plot more... |
Like the other frameworks, pyana is an executable that loops through the XTC file and calls all
requested user modules at certain transitions. All the analysts need to do is to fill in the
relevant functions in their user analysis module:
# useful imports import numpy as np import matplotlib.pyplot as plt from pypdsdata.xtc import TypeId class mymodule (object) : """Class whose instance will be used as a user analysis module. """ def __init__ ( self, source = "" threshold = "" ): """Class constructor. The parameters to the constructor are passed from pyana configuration file. If parameters do not have default values here then the must be defined in pyana.cfg. All parameters are passed as strings, convert to correct type before use. @param source name of device, format 'Det-ID|Dev-ID' @param threshold threshold value (remember to convert from string) """ self.source = source self.threshold = float(threshold) def beginjob( self, evt, env ) : """This method is called once at the beginning of the job. It should do a one-time initialization possible extracting values from event data (which is a Configure object) or environment. @param evt event data object @param env environment object """ pass def beginrun( self, evt, env ) : """This optional method is called if present at the beginning of the new run. @param evt event data object @param env environment object """ pass def begincalibcycle( self, evt, env ) : """This optional method is called if present at the beginning of the new calibration cycle. @param evt event data object @param env environment object """ pass def event( self, evt, env ) : """This method is called for every L1Accept transition. @param evt event data object @param env environment object """ pass def endcalibcycle( self, env ) : """This optional method is called if present at the end of the calibration cycle. @param env environment object """ pass def endrun( self, env ) : """This optional method is called if present at the end of the run. @param env environment object """ pass def endjob( self, env ) : """This method is called at the end of the job. It should do final cleanup, e.g. close all open files. @param env environment object """ pass |
For the following two examples, check out the latest version of the
(Note, if you don't already have a Kerberos ticket, you need to issue a ' |
Pyana and psana has follows this naming scheme for labeling the datatypes from various devices. You can find the
names by investigating the xtc file with the above-mentioned tools (pyxtcreader, xtcscanner, xtcexplorer).
To see some examples of how to fetch the various data types in pyana (or psana), look at Devices and Datatypes.
The python code for this pyana module resides in pyana_examples/src/xppt_delayscan.py
. In this example, we do a point detector delay scan, where we get the time as scan points via a control PV, and where time rebinning based on phase cavity measurement is used to improve the time resolution. One IPIMB device (a.k.a. IPM3) is used for normalization (i0, I Zero) (parameter name ipimb_norm) and another IPIMB device (a.k.a. PIM3) channel 1 is used as the signal (parameter name ipimb_sig).
Open an editor and save the following in a file named pyana.cfg:
If you look at the code (pyana_examples/src/xppt_delayscan.py) you'll notice there are no detector names in there. The names of the detectors in the XTC file are passed as parameters from the configuration file above. The Run pyana (start with 200 events):
|
xppt_delayscan.py
:
env
object, and since it only changes at the beginningbegincalibcycle
function is the appropriate place to get it:
def begincalibcycle( self, evt, env ) : |
ctrl_config = env.getConfig(TypeId.Type.Id_ControlConfig) for ic in range (0, ctrl_config.npvControls() ): cpv = ctrl_config.pvControl(ic) if cpv.name()=="fs2:ramp_angsft_target": # store the value in a class variable (visible in every class method) self.current_pv_value = cpv.value() ) |
evt
object, andevent
member function is the place to get it:
def event( self, evt, env ) : |
ipmN_raw = evt.get(TypeId.Type.Id_IpimbData, "XppSb3Ipm-1|Ipimb-0") ipmN_fex = evt.get(TypeId.Type.Id_IpmFex, "XppSb3Ipm-1|Ipimb-0") ipmN_norm = ipmN_fex.sum |
ipmS_raw = evt.get(TypeId.Type.Id_IpimbData, "XppSb3Pim-1|Ipimb-0" ) ipmS_fex = evt.get(TypeId.Type.Id_IpmFex, "XppSb3Pim-1|Ipimb-0" ) ipm_sig = ipmS_fex.channel[1] |
pc = evt.getPhaseCavity() phasecav1 = pc.fFitTime1 phasecav2 = pc.fFitTime2 charge1 = pc.fCharge1 charge2 = pc.fCharge2 |
delaytime = self.current_pv_value + phasecav1*1e3 # The "histograms" are nothing but python lists. Append to them, and turn them into arrays at the end. self.h_ipm_rsig.append( ipm_sig ) self.h_ipm_nsig.append( ipm_sig/ipm_norm ) self.h_delaytime.append( delaytime ) |
Here are a collection of useful algorithms for image analysis: http://docs.scipy.org/doc/scipy/reference/ndimage.html
The python code for this pyana module example resides in pyana_examples/src/xppt_image_analysis.py
.
This particular example is done with a CSPad image, but only a single section is available. For more typical CSPad module, see next section.
Edit
Then run the xppt_image_analysis pyana module on xppi0112 run 55:
*Edit
|
Here are some code snippet highlights from the xppt_image_analysis.py module:
def event( self, evt, env ) : elements = evt.getCsPadQuads(self.source, env) image = elements[0].data().reshape(185, 388) |
# Region of Interest (RoI) if self.roi is None: self.roi = [ 0, image.shape[1], 0, image.shape[0] ] # [x1,x2,y1,y2] print "ROI [x1, x2, y1, y2] = ", self.roi |
roi_array = image[self.roi[2]:self.roi[3],self.roi[0]:self.roi[1]] cms = scipy.ndimage.measurements.center_of_mass(roi_array) print "Center-of-mass of the ROI: (x, y) = (%.2f, %.2f)" %(self.roi[0]+cms[1],self.roi[2]+cms[0]) |
fig = plt.figure(1,figsize=(16,5)) axes1 = fig.add_subplot(121) axes2 = fig.add_subplot(122) axim1 = axes1.imshow(image) axes1.set_title("Full image") axim2 = axes2.imshow(roi_array, extent=(self.roi[0],self.roi[1],self.roi[3],self.roi[2])) axes2.set_title("Region of Interest") # rectangular ROI selector rect = UpdatingRect([0, 0], 0, 0, facecolor='None', edgecolor='red', picker=10) rect.set_bounds(*axes2.viewLim.bounds) axes1.add_patch(rect) # Connect for changing the view limits axes2.callbacks.connect('xlim_changed', rect) axes2.callbacks.connect('ylim_changed', rect) |
self.roi
and computes the center-of-mass:
def onpick(event): xrange = axes2.get_xbound() yrange = axes2.get_ybound() self.roi = [ xrange[0], xrange[1], yrange[0], yrange[1]] roi_array = image[self.roi[2]:self.roi[3],self.roi[0]:self.roi[1]] cms = scipy.ndimage.measurements.center_of_mass(roi_array) print "Center-of-mass of the ROI: (x, y) = (%.2f, %.2f)" % (self.roi[0]+cms[1],self.roi[2]+cms[0]) fig.canvas.mpl_connect('pick_event', onpick) plt.draw() |
The python code for this pyana module example resides in XtcExplorer/src/pyana_image.py
.
Try some plotting of CSPad data using xtcexplorer. Launch the explorer and load xpp48712 run 66 (a dark run):
|
CSPad data in xtc is a list of elements. In pyana get the list from the evt (event) object (notice the need for the env (environment) object too!):
elements = evt.getCsPadQuads(self.source, env) |
elements
here is a python list of ElementV1 or ElementV2 (or later versions) objects, each representing one quadrant. The list is not ordered, so to know which quadrant you have, you have to check with element.quad()
. To store a local array of the whole CSPad detector, you can do the following.
def beginjob ( self, evt, env ) : config = env.getConfig(xtc.TypeId.Type.Id_CspadConfig, self.source) if not config: print '*** cspad config object is missing ***' return quads = range(4) # memorize this list of sections for later self.sections = map(config.sections, quads) |
def event(self, evt, env): elements = evt.getCsPadQuads(self.source,env) pixel_array = np.zeros((4,8,185,388), dtype="uint16") for element in elements: data = element.data() # the 3-dimensional data array (list of 2d sections) quad = element.quad() # current quadrant number (integer value) # if any sections are missing, insert zeros if len( data ) < 8 : zsec = np.zeros( (185,388), dtype=data.dtype) for i in range (8) : if i not in self.sections[quad] : data = np.insert( data, i, zsec, axis=0 ) pixel_array[quad] = data |
What we have so far gives you a 4d numpy array of all pixels. And if you want to store it in e.g. a numpy array, you can reshape it down to 2 dimensions (this is the format of the official pedestal files made by the translator):
pixels = pixel_array.reshape(1480,388) np.save("pixel_pedestal_file.npy", pixels ) |
To get a rough picture of the full detector, here's an example of how XtcExplorer/src/cspad.py does it:
def get_quad_image( self, data3d, qn) : """get_quad_image Get an image for this quad (qn) @param data3d 3d data array (row vs. col vs. section) @param qn quad number """ pairs = [] for i in range (8) : # 1) insert gap between asics in the 2x1 asics = np.hsplit( data3d[i], 2) gap = np.zeros( (185,3), dtype=data3d.dtype ) # # gap should be 3 pixels wide pair = np.hstack( (asics[0], gap, asics[1]) ) # all sections are originally 185 (rows) x 388 (columns) # Re-orient each section in the quad if i==0 or i==1 : pair = pair[:,::-1].T # reverse columns, switch columns to rows. if i==4 or i==5 : pair = pair[::-1,:].T # reverse rows, switch rows to columns pairs.append( pair ) if self.small_angle_tilt : pair = scipy.ndimage.interpolation.rotate(pair,self.tilt_array[qn][i]) # make the array for this quadrant quadrant = np.zeros( (850, 850), dtype=data3d.dtype ) # insert the 2x1 sections according to for sec in range (8): nrows, ncols = pairs[sec].shape # colp,rowp are where the top-left corner of a section should be placed rowp = 850 - self.sec_offset[0] - (self.section_centers[0][qn][sec] + nrows/2) colp = 850 - self.sec_offset[1] - (self.section_centers[1][qn][sec] + ncols/2) quadrant[rowp:rowp+nrows, colp:colp+ncols] = pairs[sec][0:nrows,0:ncols] return quadrant |
Then combine all four quadrant images into the full detector image:
self.image = np.zeros((2*850+100, 2*850+100 ), dtype="float64") for quad in xrange (4): quad_image = self.get_quad_image( self.pixels[quad], quad ) self.qimages[quad] = quad_image if quad>0: # reorient the quad_image as needed quad_image = np.rot90( quad_image, 4-quad) qoff_x = self.quad_offset[0,quad] qoff_y = self.quad_offset[1,quad] self.image[qoff_x:qoff_x+850, qoff_y:qoff_y+850]=quad_image return self.image |
Notice that the code snippets above make use of some predefined quantities which it reads in from "calibration files". The files contains calibrated numerical values for individual sections' and quads' rotations and shifts. All of these files are located in the experiment's 'calib' folder, but is not generated automatically. The XtcExplorer currently has a local version which is not correct but which is close enough to display a reasonable image. For how the files have been read in, you can take a look at XtcExplorer/src/cspad.py
's read_alignment
function.
For how to find the correct constants for each experiment, look at the CSPAD Alignment page.
Pyana jobs are designed to do batch analysis, but matplotlib plotting does not go well with this. If you want your job to produce graphics, make sure to use a matplotlib backend that writes the graphics directly to file, e.g. png files.
Pyana can make use of multiple core processing. On the command line, add the option '-p N' where N is the number of cores to use.
Extra care needs to be taken when plotting. Also, output files need to be made with the pyana mkfile command. The output will be merged at the end of the job, but may not be in order. So if you need events to be written to a file in chronological order, you're better off using single core processing.