pyextal.acom.geometry ===================== .. py:module:: pyextal.acom.geometry .. autoapi-nested-parse:: Reciprocal-space geometry for orientation determination. Everything here works from the metric tensors the Bloch engine already parsed (`SimParams.gmx`, `SimParams.gmxr`), so no crystallography is duplicated and no external package is involved. The one idea worth stating plainly: a Cartesian frame is built once from the direct metric, after which every question about lengths, angles and handedness is ordinary 3D vector algebra. Handedness matters — a left-handed in-plane basis produces a mirrored pattern that still "indexes", which is exactly the class of error that is hard to spot by eye. Functions --------- .. autoapisummary:: pyextal.acom.geometry.cartesian_frames pyextal.acom.geometry.zone_axis_cartesian pyextal.acom.geometry.inplane_basis pyextal.acom.geometry.project pyextal.acom.geometry.excitation_error Module Contents --------------- .. py:function:: cartesian_frames(gmx) Builds consistent direct and reciprocal Cartesian basis matrices. `D` holds the direct lattice vectors as columns in a Cartesian frame, chosen so that ``D.T @ D == gmx``. `R` holds the reciprocal lattice vectors, so that ``R.T @ R == inv(gmx)``. Because ``R = inv(D).T`` the two frames share an orientation, which makes the Miller-index dot product exact:: (R @ hkl) . (D @ uvw) == hkl . uvw :param gmx: (3, 3) direct-space metric tensor. :type gmx: np.ndarray :returns: ``(D, R)``, each (3, 3). :rtype: tuple[np.ndarray, np.ndarray] .. py:function:: zone_axis_cartesian(D, uvw) Unit Cartesian vector along a direct-space zone axis. :param D: Direct basis matrix from :func:`cartesian_frames`. :type D: np.ndarray :param uvw: Zone axis as integer direct-space indices. :type uvw: array-like :returns: (3,) unit vector. :rtype: np.ndarray .. py:function:: inplane_basis(D, R, uvw, gg_hkl) Builds a right-handed orthonormal basis of the zone's reciprocal plane. `e1` runs along the projection of `gg_hkl`, `e2` completes a right-handed set with the zone axis, so a reflection's detector coordinates are ``(g . e1, g . e2)`` and never come out mirrored. :param D: Direct basis matrix. :type D: np.ndarray :param R: Reciprocal basis matrix. :type R: np.ndarray :param uvw: Zone axis, direct-space indices. :type uvw: array-like :param gg_hkl: In-plane reference reflection defining the x axis. :type gg_hkl: array-like :returns: ``(e1, e2, n)`` unit Cartesian vectors; `n` is the zone axis. :rtype: tuple[np.ndarray, np.ndarray, np.ndarray] :raises ValueError: If `gg_hkl` has no component in the zone plane. .. py:function:: project(R, hkl, e1, e2) Projects reflections onto the in-plane basis. :param R: Reciprocal basis matrix. :type R: np.ndarray :param hkl: (n, 3) Miller indices. :type hkl: np.ndarray :param e1: In-plane x unit vector. :type e1: np.ndarray :param e2: In-plane y unit vector. :type e2: np.ndarray :returns: (n, 2) in-plane coordinates in inverse Angstroms. :rtype: np.ndarray .. py:function:: excitation_error(R, hkl, n, bigk, tilt=None) Excitation error s_g for each reflection, in inverse Angstroms. Uses the standard centre-of-Laue-circle expression ``s_g = -(2 K.g + |g|^2) / (2 |K + g|)`` with the incident wavevector taken along the zone axis unless a tilt is supplied. :param R: Reciprocal basis matrix. :type R: np.ndarray :param hkl: (n, 3) Miller indices. :type hkl: np.ndarray :param n: Unit zone-axis Cartesian vector. :type n: np.ndarray :param bigk: Refraction-corrected |K|, from `SimParams.bigK`. :type bigk: float :param tilt: (3,) tangential component of K in Miller indices; the untilted zone-axis case is used when None. :type tilt: np.ndarray, optional :returns: (n,) excitation errors. :rtype: np.ndarray