Versions Compared

Key

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

...

If the detector pixel coordinates should be presented relative to IP or other setup component, have an offset or flipped around axis one more line must to be added, for example

Code Block
 SETUP-IP# 0HDR CSPAD2X2:V1PARENT 0IND -100 200 1000000 0 180 0 0 0 0

 

 

 

 

 

 

Code Block
#file: pyimgalgos/src/GeometryObject.py

def rotation(X, Y, C, S) :
    Xrot = X*C - Y*S 
    Yrot = Y*C + X*S 
    return Xrot, Yrot

class GeometryObject :
    ...
    def transform_geo_coord_arrays(self, X, Y, Z) :
        ...
	    # define Cx, Cy, Cz, Sx, Sy, Sz - cosines and sines of rotation + tilt angles
 OBJECT IND    X0[um]  Y0[um]  Z0[um]   ROT-Z ROT-Y ROT-X     TILT-Z   TILT-Y   TILT-X 
SETUP-IP       0   CSPAD2X2:V1   0      -100     200 1000000       0   180     0          0       ...

 0       X1, Y1 = rotation(X,  Y,  Cz, Sz)
        Z2, X2 = rotation(Z,  X1, Cy, Sy)
        Y3, Z3 = rotation(Y1, Z2, Cx, Sx)

        Zt = Z3 + self.z0
        Yt = Y3 + self.y0
        Xt = X2 + self.x0

        return Xt, Yt, Zt 

 

 

Pixel coordinates in the detector should be accessed by the method like

              xarr, yarr, zarr = get_pixel_coords()

For example, Python interface for CSPAD

Code Block
from pyimgalgos.GeometryAccess import GeometryAccess
    ...
    fname_geometry='<path>/geometry/0-end.data'
    geometry = GeometryAccess(fname_geometry)
    X,Y,Z = geometry.get_pixel_coords()

will return X,Y,Z arrays of the shape [4,8,185,388], or similar for CSPAD quad, one has to show the top object for coordinate arrays, say it is  'QUAD:V1'  with index 1:

Code Block
    geometry = GeometryAccess(fname_geometry)
    X,Y,Z = geometry.get_pixel_coords('QUAD:V1', 1)

return X,Y,Z arrays of the shape [8,185,388].

 

Software

Interface description is available in doxygen

Geometry file producer

  • CalibManager.OpticAlignmentCspadV1.py - for cxi-CSPAD
  • TBD for xpp-CSPAD, ePix, etc. whenever available

Alignment

  • TBD: under CalibManager.GUIGeometry.py

Hierarchical geometry model in C++

  • PSCalib::SegGeometry - Interface definition of the pixel geometry for basic segments/sensors
  • PSCalib::SegGeometryStore - static factory method for switching between different devices
  • PSCalib::SegGeometryCspad2x1V1 - defines 2x1 pixel geometry through the base class SegGeometry interface methods
  • PSCalib::GeometryObject,
  • PSCalib::GeometryAccess,

Hierarchical geometry model in Python

The same package and file names as in C++ but with file name extension .py:

...

 0

 

 

Interface implementation

Program interface to the detector geometry parameters consists of a few modules with the same names up to extensions for C++ and Python in the PSCalib package. Sensor geometry:

  • PSCalib.SegGeometry - abstract interface for sensor pixels geometry description.
  • PSCalib.SegGeometryCspad2x1V1 - implementation of interface for CSPAD 2x1 sensor of version V1.
  • PSCalib.SegGeometryStore - static factory method for all available sensor geometry descriptors.

Hierarchical model:

  • PSCalib.GeometryObject - class to support one object/node in hierarchical structure.
  • PSCalib.GeometryAccess - class to support hierarchical structure and access to the geometry parameters.

C++ program interface description with examples is available in Doxygen documentation. Below we consider Python interface only.

 

Access to sensor geometry information

Generic program interface to the sensors’ ideal geometry is presented by the abstract class PSCalib.SegGeometry. It declares methods returning number of pixels, rows, columns, pixel size, area, center coordinates relative to sensor coordinate frame, as listed below.

Code Block
def print_seg_info(self, pbits=0) :
""" Prints segment info for selected bits"""
def size(self) :
""" Returns segment size - total number of pixels in segment"""
def rows(self) :
""" Returns number of rows in segment"""
def cols(self) :
""" Returns number of cols in segment"""
def shape(self) :
""" Returns shape of the segment [rows, cols]"""
def pixel_scale_size(self) :
""" Returns pixel size in um for indexing"""
def pixel_area_array(self) :
""" Returns array of pixel relative areas of shape=[rows, cols]"""
def pixel_size_array(self, axis) :
""" Returns array of pixel size in um for AXIS"""
def pixel_coord_array(self, axis) :
""" Returns pointer to the array of segment pixel coordinates in um for AXIS"""
def pixel_coord_min(self, axis) :
""" Returns minimal value in the array of segment pixel coordinates in um for AXIS"""
def pixel_coord_max(self, axis) :
""" Returns maximal value in the array of segment pixel coordinates in um for AXIS"""
def pixel_mask_array(self, mbits) :
""" Returns array of masked pixels which content depends on bontrol bitword mbits"""

Currently this abstract interface has the only implementation for CSPAD2x1 in the class PSCalib.SegGeometryCspad2x1V1. Other versions of CSPAD2x1, ePix, pnccd, etc. sensors can be added later. Static factory method Create in class PSCalib.SegGeometryStore allows to access any sensor geometry information by the detector independent way. For example, pixel x-, y-, and z-coordinate arrays can be directly retrieved from class PSCalib.SegGeometryCspad2x1V1:

Code Block
from PSCalib.SegGeometryCspad2x1V1 import cspad2x1_one
...
xarr, yarr, zarr = cspad2x1_one.pixel_coord_array()

or using generic access method through the factory method:

Code Block
 from PSCalib.SegGeometryStore import sgs
...
sg = sgs.Create(’SENS2X1:V1’, pbits=0377)
xarr, yarr, zarr = sg.pixel_coord_array()

return the same x-, y-, and z-coordinate arrays of shape=[185,388].

This interface is used internally in the geometry service modules and, until it is absolutely necessary, should not be used directly. Doxygen/Sphinx documentation for C++/Python modules is available in References.

 

 

Pixel coordinates transformation

Child geometry object pixel coordinates are transformed to the parent frame pixel coordinates in accordance with Eqn 3. We use rotation matrix expressed in terms of cosines and sines of rotation angles as shown in Eqs. 4-5 First we apply rotations around z, y, and x axes then translation. Note, that order of operations is important. This transformation algorithm is implemented in the class PSCalib.GeometryObject. For example, in Python module GeometryObject.py all transformations of pixel coordinate arrays are presented by the code

 

Code Block
#file: pyimgalgos/src/GeometryObject.py

def rotation(X, Y, C, S) :
    Xrot = X*C - Y*S 
    Yrot = Y*C + X*S 
    return Xrot, Yrot

class GeometryObject :
    ...
    def transform_geo_coord_arrays(self, X, Y, Z) :
        ...
	    # define Cx, Cy, Cz, Sx, Sy, Sz - cosines and sines of rotation + tilt angles
        ...

        X1, Y1 = rotation(X,  Y,  Cz, Sz)
        Z2, X2 = rotation(Z,  X1, Cy, Sy)
        Y3, Z3 = rotation(Y1, Z2, Cx, Sx)

        Zt = Z3 + self.z0
        Yt = Y3 + self.y0
        Xt = X2 + self.x0

        return Xt, Yt, Zt 

 

Detector geometry access interface

Public methods of the class PSCalib.GeometryAccess:

 

Code Block
def __init__(self, path, pbits=0) :
"""Constructor of the class"""
def get_pixel_coords(self, oname=None, oindex=0) :
"""Returns three pixel X,Y,Z coordinate arrays for top or specified geometry object"""
def get_pixel_areas(self, oname=None, oindex=0) :
"""Returns pixel areas array for top or specified geometry object"""
def get_pixel_mask(self, oname=None, oindex=0, mbits=0377) :
"""Returns pixel mask array for top or specified geometry object.
mbits =+1 - mask edges
+2 - two wide-pixel central columns
+4 - non-bounded pixels
+8 - neighbours of non-bounded pixels"""
def get_pixel_scale_size(self, oname=None, oindex=0) :
"""Returns pixel scale size for top or specified geometry object"""
def get_dict_of_comments(self) :
"""Returns dictionary of comments"""
def set_geo_pars(self, oname=None, oindex=0, x0=0, y0=0, z0=0, rot_z=0, rot_y=0, rot_x=0, tilt_z=0, tilt_y=0, tilt_x=0) :
"""Sets geometry parameters for specified or top geometry object"""
def move_geo(self, oname=None, oindex=0, dx=0, dy=0, dz=0) :
"""Moves specified or top geometry object by dx, dy, dz"""
def tilt_geo(self, oname=None, oindex=0, dt_x=0, dt_y=0, dt_z=0) :
"""Tilts specified or top geometry object by dt_x, dt_y, dt_z"""
def print_list_of_geos(self) :
def print_list_of_geos_children(self) :
def print_comments_from_dict(self) :
def print_pixel_coords(self, oname=None, oindex=0) :
"""Partial print of pixel coordinate X,Y,Z arrays for selected or top(by default) geo"""
def get_pixel_coord_indexes(self, oname=None, oindex=0, pix_scale_size_um=None, xy0_off_pix=None) :
"""Returns three pixel X,Y,Z coordinate index arrays for top or specified geometry object"""
def set_print_bits(self, pbits=0) :
""" Sets printout control bitword"""
def set_print_bits(self, pbits=0) :
""" Sets printout control bitword"""
def get_psf(self) :
"""Returns array of vectors in TJ format (psf stands for position-slow-fast vectors)"""
def print_psf(self) :
""" Gets and prints psf array for test purpose"""
#------------------------------
# private methods, which may be useful:
def get_geo(self, oname, oindex) :
"""Returns specified geometry object"""
def get_top_geo(self) :
"""Returns top geometry object"""
# global method:
def img_from_pixel_arrays(iX, iY, W=None, dtype=np.float32) :
"""Returns image from iX, iY coordinate index arrays and associated weights W"""

For example:

Code Block
from PSCalib.GeometryAccess import *
fname_geometry = ’<path>/geometry/0-end.data’
geometry = GeometryAccess(fname_geometry, 0377)
dict_comm = geometry.get_dict_of_comments()
pix_size = geometry.get_pixel_scale_size()

X, Y, Z = geometry.get_pixel_coords(’QUAD:V1’, 1) # for quad
X, Y, Z = geometry.get_pixel_coords() # for top object (CSPAD)
iX, iY = geometry.get_pixel_coord_indexes(’QUAD:V1’, 1, pix_scale_size_um=None, xy0_off_pix=None) # for quad
iX, iY = geometry.get_pixel_coord_indexes(xy0_off_pix=(1000, 1000)) # for top object (CSPAD)
img = img_from_pixel_arrays(iX,iY,W=<intensity-array>)
img = img_from_pixel_arrays(iX,iY) # Image of 0/1 for fake/real pixels
arr = geometry.get_pixel_mask(’QUAD:V1’, 1, 1+2+4+8)
arr.shape = (8,185,388)
d = geometry.get_dict_of_comments()
print "d[’DATE_TIME’] = %s" % d[’DATE_TIME’]

 

Summary

Pixel detector geometry generic parameterization presented in this note is implemented in LCLS analysis software releases since ana-0.13.N. Both C++ and Python interfaces are available.

 

Anchor
references
references

References

Reference to  doxygen  documentation of classes