.. _analysis: .. index:: Analysis Analysis ******** The output of the reconstruction step is a voxel map where each voxel contains a coefficient vector which parametrizes an ODF. Thse coefficients can be interpreted in terms of the corresponding ODF model, which can be used to compute (inverse) pole figures and Euler sections for each voxel. In practice we need to compute derived quantities from these ODFs to be able to visualize and analyze the reconstructions. Scalar quanitites ----------------- To get an overview of the real-space shape of the sample, there are two main scalar quantities we use: * *Density*: What we have been reconstrucing actuallt an un-normalized ODF where the normalization constant is a measure of the density of crystalline material in the voxel. * *Texture index*: The texture index is a measure of how far the texture deviates from an uniform distribution of lattice orientations. For a single orientation, the index in principle goes to infinity. The way these quantities are computed is different for different ODF models: :: dens_RBF = np.sum(RBF_coeffs, axis=-1) texindex_RBF = np.sqrt(np.var(RBF_coeffs, axis = -1)) / np.mean(RBF_coeffs, axis = -1) dens_GSH = GSH_coeffs[..., 0] texindex_GSH = np.sqrt(np.sum(GSH_coeffs[..., 1:]**2, axis=-1)) / GSH_coeffs[..., 0] For many samples, we expect the density to be uniform inside the sample. This is not enforced by the reconstruciton and therefore provides a good first test of whether the reconstruction is successful. Both wrong geometry parameters and bad alignment tend to give artifacts in the density map. The figure below shows a good reconstruction. .. figure:: img/density_histogram.png Outline and density histogram of a single phase metal sample. The texture index should be interpreted with a bit of scepticism. In normal texture analysis, the index is only understood in a sample-averaged sense, so the voxel-wise texture index should not be compared with such values. Furthermore the value is very sensitive to the settings of the reconstruction, so the numerical values should not be taken too seriously, but it appears to be a good measure to identify regions of the sample with stronger and weaker texture. Single voxel ODFs ----------------- The most fine-grained way to inspect the resonstructions is by plotting the texture of single voxels with the usual plots used in texture analysis. Furthermore, you can sum over regions to obtain ODFs of certain parts of you sample. The ODFs reconstructed by tomography are not normalized. So if you want MRD-units label (mutiples of random distribution) in your plots, you have to normalize first, which is done by dividing out the density. Pole figures and inverse pole figures can be computed by the ODF object using the methods `make_polefigure_map` and `make_inverse_polefigure_map` and can be plotted with `matplotlib` functions: :: h_vector = B @ hkl_list[hkl_index] polefigure, theta_grid, phi_grid = odf.make_polefigure_map(coefficients_vector, h_vector) ax = plt.subplot(1, 1, 1, polar=True) ax.pcolormesh(phi_grid, np.arctan(theta_grid/2), polefigure) plt.show() .. figure:: img/snail_pole_figures.png Pole figures re-computed from a texture reconstruction. Aside from (inverse) pole-figures, we also have a routines to plot RBF coefficients in Rodriguez-vector representaion, simulated diffraction patterns, and a number of functions to compute Euler angle sections. They are demonstrated in the jupyter notebook: :ref:`Evaluate quality of reconstruction`. Orientation field ----------------- For some samples we can define a single main orientation in each voxel. For RBF reconstructions we can find an estimate of this main orientation by picking the grid orientation from each voxel that corresponds to the largest coefficient. We can improve this estimate a bit by calculating a center-of mass orientation in a tangent space at this point. These functions are demonstrated in :ref:`Computing the main orientation field `. For GSH reconstructions, we have a method using the embedding approach in real spherical harmonics basis implemented in :py:mod:`odftt.analysis.harmonics_fitting` but it is only implemented for hexagonal and cubic. The algorithms are explained in more detail in a separate :ref:`jupyter notebook`. .. figure:: img/snail_orientation_field.png Orientation field from snail shell sample computed two different ways. Grain segmentation ------------------ With large grained samples, it makes a lot of sense to do data analysis in a grain-by-grain manner. For this purpose we have a two-step grain segmentation workflow for RBF reconstuctions. First, a clusteriung algorithms is run on each single-voxel ODF to produce a short list of **texture components** with a centroid orientation and a weight. Second, the texture components are clustered into grains where for each texture component, a search is conducted over the 6 neighboring voxels (4 in 2D) and if a texture component is found with centroid orientation lower that a tunable threshold, those texture indicies are joined into a grain. The grain-segmentation workflow is documented in :ref:`Grain segmentation workflow`. Jupyter notebooks ================= .. toctree:: :maxdepth: 1 notebooks/demonstration_evaluate_reconstruction notebooks/demonstration_compute_orientation_field notebooks/demonstration_grain_segmentation