Detectors¶
Pixel-Array Detectors¶
The PADGeometry
class contains the data and methods needed to deal with “pixel-array detectors” (PADs).
This detector is assumed to consist of a regular 2D grid of pixels specified by the following parameters:
\(\vec{t}\) is the vector that points from the origin to the center of the first pixel in memory.
\(\vec{f}\) is the vector that points from the first pixel in memory to the next pixel in the fast-scan direction.
\(\vec{s}\) The vector that points from the first pixel in memory, to the next pixel in the slow-scan direction.
\(n_f\) is the number of pixels along the fast-scan direction.
\(n_s\) is the number of pixels along the slow-scan direction.
In the above:
The \(\vec{f}\) and \(\vec{s}\) vectors form the basis of the 2D grid of pixels. As such, they also define the “pixel size”.
The term “fast-scan” corresponds to the right-most index of an
ndarray
containing PAD data.The term “slow-scan” corresponds to the left-most index of an
ndarray
containing PAD data.In the default memory buffer layout of an
ndarray
, the fast-scan direction corresponds to pixels that are contiguous in memory, and which have the smallest stride. If the phrase “contiguous in memory” and the term “stride” does not mean anything to you, then you need to read the numpy documentation forndarray
.
Note
The reborn package never uses angles to describe detector geometry. Angles cause a lot of confusion that is easily avoided with the use of vectors.
Additional vectors that are important for calculating things related to x-ray scattering, but which are not inherently related to detector geometry, are:
\(\hat{b}\) is the incident beam vector that describes the nominal direction of the x-ray beam.
\(\hat{e}_1\) is the direction of the principle electric field vector, which would be the direction of the electric field in the case of linearly polarized x-rays. The second vector that is needed for unpolarized or elliptically polarized x-rays is always \(\hat{e}_2 = \hat{b}\times\hat{e}_1\) .
With the above vectors specified, we may now generate the quantities that will be useful when doing diffraction analysis and simulations. Central to many calculations is the vector pointing from the origin (where the target is located) to a detector pixel indexed by \(i\) and \(j\), is
Note that the above associates the \(i\) index with the fast scan. Therefore, the correct way to access this
element in a numpy array is data[j, i], because the right-most index is the fast-scan index (for an ndarray
that
is in the default “c-contiguous” layout).
Now let’s compute the scattering vector for pixel \(i,j\):
where \(\lambda\) is the photon wavelength. Next we can compute the scattering angle of a pixel:
For linearly polarized light, the polarization correction is
If the light is not linearly polarized, then the polarization factor is a weighted sum of the above component and this one:
The solid angle of a pixel is approximately equal to
where the vector normal to the PAD is
The PADGeometry
class can currently generate the above quantities for you, along with other helpful quantities. The
PADGeometryList
class combines multiple PADGeometry
instances.
Note
Once the above is understood, you might want to look at the example of how to use the PAD geometry tools provided by reborn.
Working with multiple PADs¶
XFELs frequently use detectors that are split up in to many separate PADs. The PADGeometryList
class is a special
sub-class of the python list that provides convenience methods for working with multiple PADs. Some
examples are provided.
Data and slicing¶
By default, PADGeometry
and PADGeometryList
assume that the data for each PAD is stored in contiguous memory blocks.
However, there are many cases in which PAD data are not contiguous due to hardware considerations.
For example, 4 PADs on a single silicon chip might be stored in a 2x2 arrangement in order to maximize read/write
speeds, but each PAD is not contiguous.
If your raw data is not contiguous, then you probably have an important decision to make:
Option 1 is to write a specialized functions that extract all the PAD data arrays and make them contiguous before passing them into your analysis pipeline. This is good if you care about having an analysis pipeline that is agnostic to the origin of the data, and which can easily handle mixtures of differently sized PADs.
Option 2 is to maintain the raw data layout throughout your analysis. This is good if you want to easily save processed data in the same layout as the raw data, and if you have some geometry files that refer to the raw data layout.
If you wish to maintain the raw data layout, you may configure a PADGeometry
instance to contain information about how
the individual PAD data should be sliced from the parent ndarray
.
This is specified by the following parameters:
There are PADGeometry
methods such as slice_from_parent
that might make your code a bit cleaner.
Reading and writing PAD geometry info¶
There are methods in the detector class for reading and writing the information needed to save/recall a
PADGeometryList
. They are currently saved in json format, but this will likely change now that we know that json
files do not accommodate comments.
Working with CrystFEL geometry files¶
reborn includes a module to help with reading CrystFEL
geom files. If you just want a PADGeometryList
then you can simply use the
geometry_file_to_pad_geometry_list()
function.
Note that the parent_data_shape
and parent_data_slice
attributes will be set by this function.
CrystFEL geom files contain a lot more than static geometry information. They also contain information about
detector properties (e.g. saturation levels, common-mode noise and conversions between digital data units and deposited x-ray energy),
information about how to obtain encoder values that specify detector positions,
formatting of the files that contain the diffraction data, and
how programs like indexamajig should treat the data (e.g. the no_index card)
If you want to read in the complete information from a geom file you can convert it to a python dictionary using the
load_crystfel_geometry()
function, which is just a wrapper
for the corresponding function in the cfelpyutils package.