{ "cells": [ { "cell_type": "markdown", "id": "67195845-843f-4de5-b0ee-0addce775c64", "metadata": { "nbsphinx": "hidden", "tags": [] }, "source": [ "This notebook is part of the *orix* documentation https://orix.readthedocs.io. Links to the documentation won’t work from the notebook." ] }, { "cell_type": "markdown", "id": "f549c08a-bed7-4431-914c-b3edc609f476", "metadata": {}, "source": [ "# Crystal reference frame\n", "\n", "In this tutorial we will see how the crystal and sample reference frames are aligned in `orix`" ] }, { "cell_type": "code", "execution_count": null, "id": "d97dfe58-a726-4a19-b327-d5c3b6b15844", "metadata": {}, "outputs": [], "source": [ "%matplotlib inline\n", "\n", "from diffpy.structure import Atom, Lattice, Structure\n", "import matplotlib.pyplot as plt\n", "import numpy as np\n", "\n", "from orix.crystal_map import Phase\n", "from orix.quaternion import Rotation\n", "from orix.vector import Miller\n", "\n", "\n", "plt.rcParams.update(\n", " {\n", " \"figure.figsize\": (10, 5),\n", " \"font.size\": 20,\n", " \"axes.grid\": True,\n", " \"lines.markersize\": 10,\n", " \"lines.linewidth\": 3,\n", " }\n", ")" ] }, { "cell_type": "markdown", "id": "a3e6c3aa-7cd7-4ed5-88cf-124b9b193532", "metadata": {}, "source": [ "## Alignment and the structure matrix\n", "\n", "The direct Bravais lattice is characterized by basis vectors ($\\mathbf{a}, \\mathbf{b}, \\mathbf{c}$) with unit cell edge lengths ($a$, $b$, $c$) and angles ($\\alpha$, $\\beta$, $\\gamma$).\n", "The reciprocal lattice has basis vectors given by\n", "\n", "$$\n", "\\mathbf{a^*} = \\frac{\\mathbf{b} \\times \\mathbf{c}}{\\mathbf{a} \\cdot (\\mathbf{b} \\times \\mathbf{c})},\\:\\:\n", "\\mathbf{b^*} = \\frac{\\mathbf{c} \\times \\mathbf{a}}{\\mathbf{a} \\cdot (\\mathbf{b} \\times \\mathbf{c})},\\:\\:\n", "\\mathbf{c^*} = \\frac{\\mathbf{a} \\times \\mathbf{b}}{\\mathbf{a} \\cdot (\\mathbf{b} \\times \\mathbf{c})},\n", "$$\n", "\n", "with reciprocal lattice parameters ($a^*$, $b^*$, $c^*$) and angles ($\\alpha^*$, $\\beta^*$, $\\gamma^*$).\n", "\n", "Using these two crystallographic lattices, we can define a standard Cartesian (orthonormal) reference frame by the unit vectors ($\\mathbf{e_1}, \\mathbf{e_2}, \\mathbf{e_3}$). In principle, the direct lattice reference frame can be oriented arbitrarily in the Cartesian reference frame. In `orix` we have chosen\n", "\n", "$$\n", "\\mathbf{e_1} ||\\: \\frac{\\mathbf{a}}{a},\\:\\:\n", "\\mathbf{e_2} ||\\: \\mathbf{e_3} \\times \\mathbf{e_1},\\:\\:\n", "\\mathbf{e_3} ||\\: \\frac{\\mathbf{c^*}}{c^*}.\n", "$$\n", "\n", "This alignment is used for example in Rowenhorst et al. (2015) and De Graef (2003), the latter which was the basis for the *EMsoft* Fortran suite of programs.\n", "Another common option is $\\mathbf{e_1} || \\mathbf{a^*}/a^*$, $\\mathbf{e_2} || \\mathbf{e_3} \\times \\mathbf{e_1}$, $\\mathbf{e_3} || \\mathbf{c}/c$, which is used for example in Britton et al. (2016) and the `diffpy.structure` Python package, which we'll come back to.\n", "\n", "In calculations, it is useful to describe the transformation of the Cartesian unit *row* vectors to the coordinates of the direct lattice vectors by the structure *row* matrix $\\mathbf{A}$ (also called the crystal *base*).\n", "Given the chosen alignment of basis vectors with the Cartesian reference frame, $\\mathbf{A}$ is defined as\n", "\n", "\\begin{equation}\n", "\\mathbf{A} = \n", "\\begin{pmatrix}\n", "a & 0 & 0\\\\\n", "b\\cos\\gamma & b\\sin\\gamma & 0\\\\\n", "c\\cos\\beta & -c\\frac{(\\cos\\beta\\cos\\gamma - \\cos\\alpha)}{\\sin\\gamma} & \\frac{\\mathrm{V}}{ab\\sin\\gamma}\n", "\\end{pmatrix},\n", "\\end{equation}\n", "\n", "where $V$ is the volume of the unit cell.\n", "\n", "In `orix`, we use the [Lattice](https://www.diffpy.org/diffpy.structure/mod_lattice.html#diffpy.structure.lattice.Lattice) class in `diffpy.structure` to keep track of these properties internally.\n", "Let's create a trigonal crystal with lattice parameters $(a, b, c)$ = (1.7, 1.7, 1.4) nm and ($\\alpha, \\beta, \\gamma$) = $(90^{\\circ}, 90^{\\circ}, 120^{\\circ})$" ] }, { "cell_type": "code", "execution_count": null, "id": "9f05c3e3-74fd-41ae-8660-ac997ac74d62", "metadata": {}, "outputs": [], "source": [ "lattice = Lattice(1.7, 1.7, 1.4, 90, 90, 120)\n", "lattice" ] }, { "cell_type": "markdown", "id": "932dcf90-8f5d-4e22-a121-d5676614b028", "metadata": {}, "source": [ "`diffpy.structure` stores the structure matrix in the `Lattice.base` property" ] }, { "cell_type": "code", "execution_count": null, "id": "20a8f43f-b2be-477c-900c-7771cf5c8a47", "metadata": {}, "outputs": [], "source": [ "lattice.base" ] }, { "cell_type": "markdown", "id": "bc67a5a6-3407-4bb6-b03a-58958eceb024", "metadata": {}, "source": [ "and we see that `diffpy.structure` does not use the `orix` alignment mentioned above, since $\\mathbf{e1} \\nparallel \\mathbf{a} / a$.\n", "Instead, we see that $\\mathbf{e3} \\parallel \\mathbf{c} / c$, which is in line with the alternative alignment mentioned above.\n", "\n", "Thus, the alignment is updated internally whenever a [Phase](../reference/generated/orix.crystal_map.Phase.rst) is created, a class which brings together this crystal lattice and a point group [Symmetry](../reference/generated/orix.quaternion.Symmetry.rst)" ] }, { "cell_type": "code", "execution_count": null, "id": "0c95a661-e26d-4b07-9ac6-d4908f37305f", "metadata": {}, "outputs": [], "source": [ "phase = Phase(point_group=\"321\", structure=Structure(lattice=lattice))\n", "phase.structure.lattice.base" ] }, { "cell_type": "markdown", "id": "e35f63a1-1008-41e0-a2e7-fcc54695de98", "metadata": {}, "source": [ "
\n", "\n", "Warning\n", "\n", "Using another alignment than the one described above has undefined behaviour in orix.\n", "Therefore, it is important that the structure matrix of a `Phase` instance is not changed.\n", "\n", "
" ] }, { "cell_type": "markdown", "id": "b4603803-0883-4b1e-b8c4-5c11eae7421e", "metadata": {}, "source": [ "
\n", "\n", "Note\n", " \n", "The lattice is included in a [Structure](https://www.diffpy.org/diffpy.structure/package.html#diffpy.structure.structure.Structure) because the latter class brings together a lattice and [atoms](https://www.diffpy.org/diffpy.structure/mod_atom.html#diffpy.structure.atom.Atom), which is useful when simulating diffraction.\n", "\n", "
" ] }, { "cell_type": "markdown", "id": "c3b093cb-aad7-45e2-9148-d63d66eb389b", "metadata": {}, "source": [ "We can visualize the alignment of the direct and reciprocal lattice basis vectors with the Cartesian reference frame using the stereographic projection" ] }, { "cell_type": "code", "execution_count": null, "id": "55815802-4745-4329-80ff-4310ac3fd66e", "metadata": {}, "outputs": [], "source": [ "abc = Miller(uvw=[[1, 0, 0], [0, 1, 0], [0, 0, 1]], phase=phase)\n", "abcr = Miller(hkl=[[1, 0, 0], [0, 1, 0], [0, 0, 1]], phase=phase)" ] }, { "cell_type": "code", "execution_count": null, "id": "157d6a1d-81b9-4516-a479-7e293b250319", "metadata": {}, "outputs": [], "source": [ "fig = abc.scatter(\n", " c=[\"r\", \"g\", \"b\"],\n", " marker=\"o\",\n", " return_figure=True,\n", " axes_labels=[\"e1\", \"e2\"],\n", " hemisphere=\"both\",\n", ")\n", "abcr.scatter(c=[\"r\", \"g\", \"b\"], marker=\"x\", s=300, figure=fig)" ] }, { "cell_type": "markdown", "id": "96a9cde8-866e-4c22-be91-e27aa7e8265f", "metadata": {}, "source": [ "## Alignment of symmetry operations\n", "\n", "To see where the crystallographic axes about which the point group symmetry operations rotate, we can add symmetry operations to the figure, like is done in the [Visualizing point groups](point_groups.ipynb) tutorial for all point groups supported in `orix`" ] }, { "cell_type": "code", "execution_count": null, "id": "26f0ba6c-d0bd-4222-ba17-17eaf42f98fd", "metadata": { "nbsphinx-thumbnail": { "tooltip": "Alignment of crystal reference frames and symmetry operations" }, "tags": [ "nbsphinx-thumbnail" ] }, "outputs": [], "source": [ "r = Rotation.from_axes_angles([0, 1, 0], 65, degrees=True)\n", "phase.point_group.plot(figure=fig, orientation=r, fc=\"none\", ec=\"C0\", s=150)\n", "fig" ] }, { "cell_type": "markdown", "id": "fa96e593-2ac8-40eb-8806-d455cddf382f", "metadata": {}, "source": [ "## Converting crystal vectors\n", "\n", "The reference frame of the stereographic projection above is the Cartesian reference frame ($\\mathbf{e_1}, \\mathbf{e_2}, \\mathbf{e_3}$).\n", "The Cartesian coordinates $(\\mathbf{x}, \\mathbf{y}, \\mathbf{z})$ of $(\\mathbf{a}, \\mathbf{b}, \\mathbf{c})$ and $(\\mathbf{a^*}, \\mathbf{b^*}, \\mathbf{c^*})$ were found using $\\mathbf{A}$ in the following conversions\n", "\n", "\\begin{align}\n", "(x, y, z) &= [uvw] \\cdot \\mathbf{A},\\\\\n", "(x, y, z) &= (hkl) \\cdot (\\mathbf{A}^{-1})^T.\n", "\\end{align}\n", "\n", "Let's compute the internal conversions directly and check for equality" ] }, { "cell_type": "code", "execution_count": null, "id": "5690186c-af02-4b14-8742-a7f98f66159c", "metadata": {}, "outputs": [], "source": [ "np.allclose(abc.data, np.dot(abc.uvw, phase.structure.lattice.base))" ] }, { "cell_type": "code", "execution_count": null, "id": "c08f6f6c-e39d-4e25-b398-e99f37ee22ba", "metadata": {}, "outputs": [], "source": [ "np.allclose(\n", " abcr.data,\n", " np.dot(abcr.hkl, np.linalg.inv(phase.structure.lattice.base).T),\n", ")" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.10.8" } }, "nbformat": 4, "nbformat_minor": 5 }