.. _conventions: .. index:: Conventions Conventions *********** When dealing with polar coordinates, spherical harmonics, rotations, and crystal symmetries, there are a lot of arbitrary choices of how to define angles, normalization, phase factors and so on. This page tries to make these choices explicit. Coordinate systems ================== Vectors are represented by numpy-arrays where the last dimension is of length 3 where the last index is in the ordering :math:`x, y, z`. Vectors can live in one of two coordinate systems. The **lattice coordinate system** and the **sample coordinate system**. Miller index 3-tuples are not vectors, but can be turned into lattice space vectors by matrix-multiplying by the reciprocal lattice matrix. Vector-variables are named named :math:`\mathbf{y}` or ``y`` to specify that they live in sample-coordinates while variables named :math:`\mathbf{h}` or ``h`` live in lattice coordinates. We refer to any voxel-map as a *tomogram*. Such tomograms have the first 3 dimensions equal to ``geometry.volume_shape``. The edges of the voxel-map are parallel to the coordinate axes of the sample coordinate system. with :math:`x, y, z` ordering and going from zero to positive. We rarely make use of explicit real-space coordinates but we consider the **corner** of the voxel represented by ``tomogram[0, 0, 0, ...]`` to be the origin. In order to have this coordinate system be equal to the lab coordinates used by various instruments, the ``mumott.Geometry`` obects allows a lot of freedom in defining various diffractometer geometries. For 2D geometries we still represent the tomograms with 3D numpy-arrays but one dimension is kept length-1. For a cSAXS-style geometry (beam parallel to z-axis, rotation around y-axis) the 1-length array is the 2nd dimension. For an PETRA III-ID21.1 style geometry (beam parallel to x-axis, rotation around z-axis) the empty dimension is the 3rd. Polar coordinates and spherical harmonics ========================================= While we mostly represent unit vectors in 3-component basis, we sometimes make use of polar coordinates. The polar angle is called ``theta`` or :math:`\theta` and goes from the +z pole (:math:`\theta=0`) to the -z pole (:math:`\theta=\pi`). The azimuthal angle is called ``phi`` or :math:`\phi` and goes from the (+x)-z half plane (:math:`\phi=0`) towards the (+y)-z half-plane (:math:`\phi=\pi/2`). The use of :math:`\theta` for the polar angle clashes with the common use of :math:`2\theta` for the scattering angle. I try to use :math:`2\theta/2` whenever the half-scattering angle is needed to avoid confusion. This choice of coordinates leads to the mapping, from unit-3 vectors to polar coordinates: .. math:: \begin{bmatrix} x \\ y \\ z \end{bmatrix} = \begin{bmatrix} \cos\phi\sin\theta \\ \sin\phi\sin\theta \\ \cos\theta \end{bmatrix} and .. math:: \begin{bmatrix} \theta \\ \phi \end{bmatrix} = \begin{bmatrix} \arccos(z) \\ \arctan(y/x) \end{bmatrix} The second mapping should be implemented with some care:: theta = np.arctan2(np.sqrt(vectors[..., 0]**2 + vectors[..., 1]**2), vectors[..., 2]) phi = np.arctan2(vectors[..., 1], vectors[..., 0]) We use real-spherical harmonics with the notational trick of using neagative *m* index to denote sine-like harmonics and positive *m* for cosine-like harmonics. The spherical harmonics used are in these coordinates with normalization convention .. math:: \int_0^\pi\int_0^{2\pi}\mathrm{Y}_\ell^m(\theta,\phi)^2\mathrm{d}\phi\sin(\theta)\mathrm{d}\theta = 4\pi and without the Condon-Shotley phase factor. Rotations, Orientation, Misorientations ======================================= A transformation of vectors from one coordinate system to another is called a *rotation*. A transformation from some conventional reference is called an *orientation*. A rotation from one *orientation* to another is called a *misorientation*. The reference coordinate system used to define orientaitons is implicitly given by a choice of lattice vectors, :math:`\mathbf{a}`, :math:`\mathbf{b}`, :math:`\mathbf{c}`. An explcit choice of coordinates for these defines the lattice coordinate system. One common convention is :math:`\mathbf{a}||\hat{x}`, :math:`\mathbf{b}` in :math:`\mathrm{Span}(\hat{x}, \hat{y})` which means the lattice matrix: :math:`\mathrm{A} = [\mathbf{a}, \mathbf{b}, \mathbf{c}]` is upper-triangular. When an :math:`\mathrm{A}` is chosen, the reciprocal lattice matrix follows: .. math:: \mathrm{B} = 2\pi \mathrm{A}^{-\mathrm{T}}. :math:`\mathrm{B}` allows Miller indicies to be converted into lattice-space vectors: :math:`\mathbf{h} = \mathrm{B}[h, k, \ell]^{\mathrm{T}}`. Lattice symmetries are specific rotations that leave the lattice invariant. The explicit form of the symmetry rotations depends on the choice of lattice vectors and the user has to make sure that this choice is self-consisent. On such choice of consistent lattice vectors and symmetry rotations are implemented in ``odftt.crystallography`` and ``odftt.texture.point_groups`` for orthorhombic, hexagonal, and cubic crystals. Crystal symmetries are applied only to vectors in the lattice coordinate system. An orientation is defined as the rotation that turns a vector in lattice coordinates (for a given crystallite) into a vector in sample-space coordinates. As different crystalllites have different orientations we can define an *orientation field* which is the central object of interest in texture analysis. Lattice symmetries give rise to the concept of *symmetry-equivalent orientations*. We say that any orientation .. math:: g = g_0 s:\text{ }s \in S is equivalent to the orientaiton :math:`g_0` where :math:`S` is the set of lattice symmetries. For visualization and statistics, we need to pick a single representative of this equivalence class. Conventionally we pick the one with lower Euler norm, giving rise to the concept of the *asymmetric zone*, the subspace of orientaion space containing all such representatives. We say the misorientation :math:`m'{}_{1\rightarrow2}` from :math:`g_1` to :math:`g_2` is the rotation that converts the orientation :math:`g_1` into :math:`g_2`. But since several orientation are equivalent, we only consider the smallest such rotation, that connects two equivalent orientations by finding the minimum: .. math:: m'{}_{1\rightarrow2} = g_2 s^* g_1^{-1} \text{ where } s^* = \arg\min_s |g_2 s g_1^{-1}|\text{ for }s\in S where :math:`|\cdot|` denotes the Euler-norm of a rotation. This gives the minimal rotation to transform :math:`g_1` into an equivalent of :math:`g_2` applied in sample coordinates. Most commonly, we are rather interested in the misorientation in lattice coordinates. For this we pull the misorientation into the lattice frame of the first crystallite: .. math:: m_{1\rightarrow2} = (g_1s)^{-1} m'{}_{1\rightarrow2} g_1s = s^{-1}(g_1^{-1} g_2 s^*)s Since the lattice coordinate system has symmetries, this gives us an equivalence class of misorientations with the same magnitude. To select a representative, we choose the unique element with the rotation axis in the fundamental zone of :math:`S`. Eg. in cubic symmetry, the spherical triangle spanned by Miller indicies (001), (101), (111). This space of misorientations is the appropriate for doing statistics eg. to determine twin-laws. While it is not obvious, and I will not attempt to prove it, the misorientation determined this way does not depend on the order of the two orientations. Representation of rotations =========================== As often as posisble, rotations are represented by ``scipy.spatial.transform.Rotation`` objects. This package is used so often that we conventionally import it under the alias ``R``. For equaitons, where present, orientations are written as :math:`g` misorientations as :math:`m` (unfortunately clashing with the magnetic index of Spherical Harmonics) and other rotations, such as the tomographic rotation of the sample, with :math:`\mathrm{R}`. When ``R`` doesn't cut it, mainly due to it's lack of multi-dimensional arrays. We make heavy use of Rodriguez vectors using the ``R.from_rotvec()`` and ``R.as_rotvec()`` methods. In those cases, the vector index is last in the array and in (x,y,z) ordering. Same as other vectors. For example we would have:: main_orientation = R.from_rotvec( main_orientation_tomogram[x_index, y_index, z_index, :]) ) *Generalized spherical harmonics* are naturally expressed in Euler-angles. We use the ``'zyz'`` angles, that can be generated by ``R.as_euler('zyz')``. The three Euler angles are called :math:`\Psi, \Theta, \Phi` or ``(Psi, Theta, Phi)``. The names are suggestive of the connection to polar coordinates. Namely that :math:`(\Psi, \Theta, \Phi)` rotates the z-axis into the point with polar coordinates :math:`(\theta, \phi) = (\Theta, \Phi)`. :math:`\Psi` represents an initial rotation about the z-axis. We define the generalized spherical harmonics, :math:`\mathrm{Z}_\ell^{m,n}(g)`, to match our choice of spherical harmonics by the property: .. math:: \mathrm{Y}_\ell^m(\mathbf{y}) = \sum_{n=-\ell}^\ell \mathrm{Z}_\ell^{n,m}(g) \mathrm{Y}_\ell^n(g^{-1}\mathbf{y}) where we are abusing notation to write the spherical harmonic as a function of a unit-vector rather than of two polar coordinates. With our choice of Euler angles we get the property: .. math:: \mathrm{Z}_\ell^{0,m}(\Psi, \Theta, \Phi) = \frac{1}{\sqrt{2\ell+1}}\mathrm{Y}_\ell^m(\Theta,\Phi), which is the sense in which they generalize the spherical harmonics. The normalization convention is .. math:: \int_0^{2\pi} \int_0^\pi \int_0^{2\pi} \mathrm{Z}_\ell^{m,n}(\Psi, \Theta, \Phi)^2 \mathrm{d}\Psi\sin\Theta\mathrm{d}\Theta\mathrm{d}\Phi = \frac{8\pi^2}{2\ell+1}.