Reconstruction

The main goal of this python package is the reconstruction of azimuthally re-grouped XRD-CT data into a voxel-map of orientation distribution functions.

The starting point is a 5D numpy array containing the azimuthally integrated data in the following format:

  1. Tomographic rotation \(\omega\)

  2. Tomographic scanning axis 1

  3. Tomographic scanning axis 2

  4. Detector azimuth \(\eta\)

  5. Bragg peak \(hk\ell\)

Furthermore, the data should already have been corrected for transmission and normalized by the scaling factors.

When you have such a numpy array, you are ready to set-up the reconstruction.

Orientation distribution functions

We provide three different ways to represent the orientation distribution functions in individual voxels.

These basis function sets are implemented by the objects in odftt.texture.odfs. The two first are essentially just collections of functions used to calulate pole-figures and inverse pole figures from coefficient vectors. Their main use in the reconstrction workflow is to produce the polefigure_matrixes that are used to define the forward model of the reconstruction using the class-methods odf.evaluate_pfmatrix.

The single grain ODF has some more methods, since the single grain workflow calculates pole-figure values on-the-fly. It can however still be used with pre-computed matrixes like the two other.

Geometry parameters

The geometry of the experiment is handled with the mumott.Geometry-object. It cotains a collection of vectors that define directions of various experimental properties in laboratory coordinates primarily used to compute the \(\mathbf{q}\)-vectors probed in the experiment. The normalized vectors are given by:

\[\hat{\mathbf{q}}(\omega, \eta, 2 \theta) = \mathrm{R}_{\mathbf{\omega}}(\omega)^\mathrm{T}(-\sin\theta\mathbf{p}_0 + \cos\theta (\cos\eta \mathbf{q}_{0^\circ} + \sin\eta \mathbf{q}_{90^\circ}))\]

the important quantities in this expression are:

  • \(\mathrm{R}_{\mathbf{\omega}}\) Is a rotation matrix and denotes the setting of the goniometer. It denotes the active rotation of the sample in the laboratory coordinates.

  • \(\theta\) is half of the scattering angle.

  • \(\mathbf{p}_0\) is the direction of the incident beam. From source to detector.

  • \(\eta\) is the azimuthal detector angle.

  • \(\mathbf{q}_{0^\circ/90^\circ}\) is the direction vector on the detector (normal to \(\mathbf{p}_0\)) which corresponds to \(\eta=0^\circ/\eta=90^\circ\)

The geometry is also used to compute the Radon-transforms. Therefore, it also needs the real-space geometry information \(\mathbf{j}_0\) and \(\mathbf{k}_0\) which are the scanning directions (unit vectors) of the first and second raster scan dimension. Be aware that the sign should corespond to the direction that the beam moves in sample-fixed coordinates, not to the way the sample is scanning in laboratory coordinates.

The rotations are assumed to be centered in the reconstruction volume. If there is some misalignment, you need to specify offset vectors for each projection. For stable single-axis geometries commonly used in XRD-CT beamlines the alignment is only there to correct for the offset of the rotation-axis. In two-axis set-ups commonly used in tensor-tomography, there can be more misalignment and we usually do projection-by-projection alignment based on tomographic consistency.

The jupyter notebook Data corrections contains a code block which can be used to generate a quite general 2D geometry for the most common slice-by-slice experiment with parametrized aligment.

Running the reconstruction

The ODF objects can be used to compute the pole-figure values at the probed \(\mathbf{q}\)-vectors and \(hk\ell\) orders. From these matrixes and the geometry object, we can initialize a model object which composes the two into a single linear operator which implements a forward and adjoint methods that the optimizer can use.

The resontruction itself is a simple linear least-squares fitting problem. At the moment there is one optimizer implemented FISTA which seems to work well enough. It initializes from an model object and an array containing the azimuthally integrated data.

Jupyter notebooks