{ "cells": [ { "cell_type": "markdown", "id": "130a22bd", "metadata": { "nbsphinx": "hidden" }, "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": "a6c24bbd", "metadata": {}, "source": [ "# Crystal directions\n", "\n", "In this tutorial we will perform operations with and plot directions with respect to the crystal reference frame $(\\mathbf{e}_1, \\mathbf{e}_2, \\mathbf{e}_3)$ using Miller indices with the [orix.vector.Miller](../reference/generated/orix.vector.Miller.rst) class.\n", "\n", "Many of the initial examples, explaining basic crystallographic computations with crystal lattice directions $[uvw]$ and crystal lattice planes $(hkl)$, are taken from the textbook *Introduction to Conventional Transmission Electron Microscopy* (De Graef (2003)). Some of the later examples are also inspired by MTEX' excellent documentation on [Miller indices](https://mtex-toolbox.github.io/CrystalDirections.html) and [operations with them](https://mtex-toolbox.github.io/CrystalOperations.html)." ] }, { "cell_type": "code", "execution_count": null, "id": "2b47ffdf", "metadata": { "tags": [] }, "outputs": [], "source": [ "%matplotlib inline\n", "\n", "from diffpy.structure import Lattice, Structure\n", "import matplotlib.pyplot as plt\n", "import numpy as np\n", "\n", "from orix import plot\n", "from orix.crystal_map import Phase\n", "from orix.quaternion import Orientation, Rotation, symmetry\n", "from orix.vector import Miller, Vector3d\n", "\n", "\n", "plt.rcParams.update(\n", " {\n", " \"figure.figsize\": (7, 7),\n", " \"font.size\": 20,\n", " \"axes.grid\": True,\n", " \"lines.markersize\": 10,\n", " \"lines.linewidth\": 2,\n", " }\n", ")" ] }, { "cell_type": "markdown", "id": "e7d884cf", "metadata": {}, "source": [ "To start with, let's create a tetragonal crystal with lattice parameters $a$ = $b$ = 0.5 nm and $c$ = 1 nm and symmetry elements described by point group $S = 4$" ] }, { "cell_type": "code", "execution_count": null, "id": "91532311", "metadata": { "tags": [] }, "outputs": [], "source": [ "tetragonal = Phase(\n", " point_group=\"4\",\n", " structure=Structure(lattice=Lattice(0.5, 0.5, 1, 90, 90, 90)),\n", ")\n", "print(tetragonal)\n", "print(tetragonal.structure.lattice)" ] }, { "cell_type": "markdown", "id": "7e31de5d", "metadata": {}, "source": [ "Here, the [Phase](../reference/generated/orix.crystal_map.Phase.rst) class attaches a point group symmetry, [Symmetry](../reference/generated/orix.quaternion.Symmetry.rst), to a [Structure](https://www.diffpy.org/diffpy.structure/package.html#diffpy.structure.structure.Structure) containing a [Lattice](https://www.diffpy.org/diffpy.structure/mod_lattice.html#diffpy.structure.lattice.Lattice) (where the crystal axes are defined), and possibly some [Atom](https://www.diffpy.org/diffpy.structure/mod_atom.html#diffpy.structure.atom.Atom)s." ] }, { "cell_type": "markdown", "id": "2829c8ca-cfde-4bd0-b295-e46913442503", "metadata": {}, "source": [ "## Directions $[uvw]$\n", "\n", "A crystal lattice direction $\\mathbf{t} = u \\cdot \\mathbf{a} + v \\cdot \\mathbf{b} + w \\cdot \\mathbf{c}$ is a vector with coordinates $(u, v, w)$ with respect to the crystal axes $(\\mathbf{a}, \\mathbf{b}, \\mathbf{c})$, and is denoted $[uvw]$.\n", "We can create a set of these Miller indices by passing a list/array/tuple to `uvw` in [Miller](../reference/generated/orix.vector.Miller.rst)" ] }, { "cell_type": "code", "execution_count": null, "id": "c7865433", "metadata": { "tags": [] }, "outputs": [], "source": [ "t1 = Miller(uvw=[[1, 2, 0], [3, 1, 1]], phase=tetragonal)\n", "t1.scatter(c=[\"b\", \"r\"], axes_labels=[\"e1\", \"e2\"])\n", "t1" ] }, { "cell_type": "markdown", "id": "5dee5ca4", "metadata": {}, "source": [ "Here, we plotted the lattice directions $\\mathbf{t}_i$ in the stereographic projection using the [Vector3d.scatter()](../reference/generated/orix.vector.Vector3d.scatter.rst) plotting method.\n", "This also works for `Miller.scatter()` since the `Miller` class inherits most of the functionality in the `Vector3d` class.\n", "\n", "Let's compute the dot product in nanometres and the angle in degrees between the vectors" ] }, { "cell_type": "code", "execution_count": null, "id": "58fafae1", "metadata": { "tags": [] }, "outputs": [], "source": [ "t1[0].dot(t1[1])" ] }, { "cell_type": "code", "execution_count": null, "id": "377071d3", "metadata": { "tags": [] }, "outputs": [], "source": [ "t1[0].angle_with(t1[1], degrees=True)" ] }, { "cell_type": "markdown", "id": "0e05dc64", "metadata": {}, "source": [ "The length of a direct lattice vector $|\\mathbf{t}|$ is available via the [Miller.length](../reference/generated/orix.vector.Miller.length.rst) property, given in lattice parameter units (nm in this case)" ] }, { "cell_type": "code", "execution_count": null, "id": "bf0c0ee7", "metadata": { "tags": [] }, "outputs": [], "source": [ "Miller(uvw=[0, -0.5, 0.5], phase=tetragonal).length" ] }, { "cell_type": "markdown", "id": "212dde9a", "metadata": {}, "source": [ "## Planes $(hkl)$\n", "\n", "A crystal lattice plane $(hkl)$ is described by its normal vector $\\mathbf{g} = h\\cdot\\mathbf{a^*} + k\\cdot\\mathbf{b^*} + l\\cdot\\mathbf{c^*}$, where $(\\mathbf{a^*}, \\mathbf{b^*}, \\mathbf{c^*})$ are the reciprocal crystal axes.\n", "As for crystal directions $[uvw]$, we can create a set of these Miller indices by passing `hkl` instead of `uvw` to [Miller](../reference/generated/orix.vector.Miller.rst)" ] }, { "cell_type": "code", "execution_count": null, "id": "d681d26c", "metadata": { "tags": [] }, "outputs": [], "source": [ "g1 = Miller(hkl=t1.uvw, phase=tetragonal)\n", "g1.scatter(c=[\"y\", \"g\"], marker=\"*\", axes_labels=[\"e1\", \"e2\"])\n", "g1" ] }, { "cell_type": "markdown", "id": "407ee88b", "metadata": {}, "source": [ "Let's compute the angle in degrees between the lattice plane normals" ] }, { "cell_type": "code", "execution_count": null, "id": "31c42ee7", "metadata": { "tags": [] }, "outputs": [], "source": [ "g1[0].angle_with(g1[1], degrees=True)" ] }, { "cell_type": "markdown", "id": "f6e45bf2", "metadata": {}, "source": [ "Note that the lattice plane normals $(hkl)$ are not always parallel to the lattice directions $[uvw]$, even if the indices are the same" ] }, { "cell_type": "code", "execution_count": null, "id": "a60c2918", "metadata": { "tags": [] }, "outputs": [], "source": [ "fig = t1.scatter(return_figure=True, c=[\"b\", \"r\"])\n", "g1.scatter(figure=fig, c=[\"y\", \"g\"], marker=\"*\", axes_labels=[\"e1\", \"e2\"])" ] }, { "cell_type": "markdown", "id": "6ff2f4e2", "metadata": {}, "source": [ "We can get the reciprocal components of the lattice vector $\\mathbf{t}$ = [114] (i.e. which lattice plane the [114] direction is perpendicular to) by accessing [Miller.hkl](../reference/generated/orix.vector.Miller.hkl.rst) for a `Miller` object with crystal directions (or [Miller.uvw](../reference/generated/orix.vector.Miller.uvw.rst) for a `Miller` object with crystal plane normals)" ] }, { "cell_type": "code", "execution_count": null, "id": "ea017f82", "metadata": { "tags": [] }, "outputs": [], "source": [ "Miller(uvw=[1, 1, 4], phase=tetragonal).hkl" ] }, { "cell_type": "markdown", "id": "a0e03ff3", "metadata": {}, "source": [ "We see that the lattice vector $\\mathbf{t}$ = [114] is perpendicular to the lattice plane $\\mathbf{g}$ = (1 1 16).\n", "\n", "The length of reciprocal lattice vectors can also be accessed via the `Miller.length` property.\n", "The length equals $|\\mathbf{g}| = \\frac{1}{d_{\\mathrm{hkl}}}$ in reciprocal lattice parameter units (nm^-1 in this case)" ] }, { "cell_type": "code", "execution_count": null, "id": "dbf775b0", "metadata": { "tags": [] }, "outputs": [], "source": [ "g1.length" ] }, { "cell_type": "markdown", "id": "d94397d1-b384-46fa-b030-f9a578d64b35", "metadata": {}, "source": [ "We can then obtain the interplanar spacing $d_{\\mathrm{hkl}}$" ] }, { "cell_type": "code", "execution_count": null, "id": "a826d6f6", "metadata": { "tags": [] }, "outputs": [], "source": [ "1 / g1.length" ] }, { "cell_type": "markdown", "id": "14fd8365-564c-4779-b2e2-43a5152616db", "metadata": {}, "source": [ "## Zone axes\n", "\n", "The cross product $\\mathbf{g} = \\mathbf{t}_1 \\times \\mathbf{t}_2$ of the lattice vectors $\\mathbf{t}_1$ = [110] and $\\mathbf{t}_2$ = [111] in the tetragonal crystal, in direct space, is described by a vector expressed in reciprocal space, $(hkl)$" ] }, { "cell_type": "code", "execution_count": null, "id": "b2b3f2a5", "metadata": { "tags": [] }, "outputs": [], "source": [ "t2 = Miller(uvw=[[1, 1, 0], [1, 1, 1]], phase=tetragonal)\n", "g2 = t2[0].cross(t2[1])\n", "g2" ] }, { "cell_type": "markdown", "id": "96055dc5", "metadata": {}, "source": [ "The exact \"indices\" are returned.\n", "However, we can get a new `Miller` instance with the indices rounded down or up to the *closest* smallest integer via the [Miller.round()](../reference/generated/orix.vector.Miller.round.rst) method" ] }, { "cell_type": "code", "execution_count": null, "id": "6279a24a", "metadata": { "tags": [] }, "outputs": [], "source": [ "g2.round()" ] }, { "cell_type": "markdown", "id": "f1825a11", "metadata": {}, "source": [ "The maximum index that `Miller.round()` may return can be set by passing the `max_index` parameter.\n", "\n", "We can plot these direct lattice vectors $[uvw]$ and the vectors normal to the cross product vector, i.e. normal to the reciprocal lattice vector $(hkl)$" ] }, { "cell_type": "code", "execution_count": null, "id": "721fbc40", "metadata": { "tags": [] }, "outputs": [], "source": [ "fig = t2.scatter(return_figure=True, c=\"r\", axes_labels=[\"e1\", \"e2\"])\n", "g2.draw_circle(figure=fig, color=\"b\", linestyle=\"--\")" ] }, { "cell_type": "markdown", "id": "fa1ab807", "metadata": {}, "source": [ "Likewise, the cross product of reciprocal lattice vectors $\\mathbf{g}_1$ = (110) and $\\mathbf{g}_2$ = (111) results in a direct lattice vector" ] }, { "cell_type": "code", "execution_count": null, "id": "2d06c437", "metadata": { "tags": [] }, "outputs": [], "source": [ "g3 = Miller(hkl=t2.uvw, phase=tetragonal)\n", "t3 = g3[0].cross(g3[1]).round()\n", "t3" ] }, { "cell_type": "code", "execution_count": null, "id": "9e21ab76", "metadata": { "tags": [] }, "outputs": [], "source": [ "fig = g3.scatter(return_figure=True, c=\"b\", axes_labels=[\"e1\", \"e2\"])\n", "t3.draw_circle(figure=fig, color=\"r\", linestyle=\"--\")" ] }, { "cell_type": "markdown", "id": "b9fb908e", "metadata": {}, "source": [ "## Trigonal and hexagonal index conventions\n", "\n", "Crystal lattice vectors and planes in lattices with trigonal and hexagonal crystal symmetry are typically expressed in Weber symbols $\\mathbf{t} = [UVTW]$ and Miller-Bravais indices $\\mathbf{g} = (hkil)$. The definition of $[UVTW]$ used in orix follows *Introduction to Conventional Transmission Electron Microscopy* (DeGraef, 2003)\n", "\n", "\\begin{align}\n", "U &= \\frac{2u - v}{3},\\\\\n", "V &= \\frac{2v - u}{3},\\\\\n", "T &= -\\frac{u + v}{3},\\\\\n", "W &= w.\n", "\\end{align}\n", "\n", "For a plane, the $h$, $k$, and $l$ indices are the same in $(hkl)$ and $(hkil)$, and $i = -(h + k)$.\n", "\n", "The first three Miller indices always sum up to zero, i.e.\n", "\n", "\\begin{align}\n", "U + V + T &= 0,\\\\\n", "h + k + i &= 0.\n", "\\end{align}" ] }, { "cell_type": "code", "execution_count": null, "id": "f772cb1f", "metadata": { "tags": [] }, "outputs": [], "source": [ "trigonal = Phase(\n", " point_group=\"321\",\n", " structure=Structure(lattice=Lattice(4.9, 4.9, 5.4, 90, 90, 120)),\n", ")\n", "trigonal" ] }, { "cell_type": "code", "execution_count": null, "id": "594b3c3f", "metadata": { "tags": [] }, "outputs": [], "source": [ "t4 = Miller(UVTW=[2, 1, -3, 1], phase=trigonal)\n", "t4" ] }, { "cell_type": "code", "execution_count": null, "id": "c1b370c2", "metadata": { "tags": [] }, "outputs": [], "source": [ "g4 = Miller(hkil=[1, 1, -2, 3], phase=trigonal)\n", "g4" ] }, { "cell_type": "code", "execution_count": null, "id": "02dd15f9", "metadata": { "tags": [] }, "outputs": [], "source": [ "t4.scatter(c=\"C0\", grid_resolution=(30, 30), axes_labels=[\"e1\", \"e2\"])\n", "g4.scatter(figure=plt.gcf(), c=\"C1\")" ] }, { "cell_type": "markdown", "id": "2d558dc5", "metadata": {}, "source": [ "We can switch between the coordinate format of a vector.\n", "However, this does not change the vector, since all vectors are stored with respect to the cartesian coordinate system internally." ] }, { "cell_type": "code", "execution_count": null, "id": "bbb6babd", "metadata": { "tags": [] }, "outputs": [], "source": [ "print(g4, \"\\n\\n\", g4.data)" ] }, { "cell_type": "code", "execution_count": null, "id": "4f90170d", "metadata": { "tags": [] }, "outputs": [], "source": [ "g4.coordinate_format = \"UVTW\"\n", "print(g4, \"\\n\\n\", g4.data)" ] }, { "cell_type": "markdown", "id": "15396d0c", "metadata": {}, "source": [ "Getting the closest integer indices, however, changes the vector" ] }, { "cell_type": "code", "execution_count": null, "id": "650b799d", "metadata": { "tags": [] }, "outputs": [], "source": [ "g4_round = g4.round()\n", "print(g4_round, \"\\n\\n\", g4_round.data)" ] }, { "cell_type": "markdown", "id": "99a102ad", "metadata": {}, "source": [ "## Symmetrically equivalent directions and planes\n", "\n", "The symmetry operations $s$ of the point group symmetry assigned to a crystal lattice can be applied to describe symmetrically equivalent crystal directions and planes.\n", "This applies to crystals in all seven systems (triclinic, monoclinic, orthorhombic, trigonal, tetragonal, hexagonal, and cubic), but we'll use the cubic crystal as an example because of its high symmetry" ] }, { "cell_type": "code", "execution_count": null, "id": "eaa24f25", "metadata": { "tags": [] }, "outputs": [], "source": [ "cubic = Phase(point_group=\"m-3m\")\n", "print(cubic, \"\\n\", cubic.structure.lattice.abcABG())" ] }, { "cell_type": "markdown", "id": "a42bb3de", "metadata": {}, "source": [ "The directions $\\mathbf{t}$ parallel to the crystal axes ($\\mathbf{a}$, $\\mathbf{b}$, $\\mathbf{c}$) given by $[100]$, $[\\bar{1}00]$, $[010]$, $[0\\bar{1}0]$, $[001]$, and $[00\\bar{1}]$ ($\\bar{1}$ means \"-1\") are symmetrically equivalent, and can be obtained using [Miller.symmetrise()](../reference/generated/orix.vector.Miller.symmetrise.rst)" ] }, { "cell_type": "code", "execution_count": null, "id": "6dee66d9", "metadata": { "tags": [] }, "outputs": [], "source": [ "t100 = Miller(uvw=[1, 0, 0], phase=cubic)\n", "t100.symmetrise(unique=True)" ] }, { "cell_type": "markdown", "id": "cd439e36", "metadata": {}, "source": [ "Without passing `unique=True`, since the cubic crystal symmetry is described by 48 symmetry operations $s$ (or elements), 48 directions $\\mathbf{t}$ would have been returned.\n", "\n", "The six symmetrically equivalent directions, known as a family, may be expressed collectively as $\\left<100\\right>$, the brackets implying all six permutations or variants of 1, 0, 0.\n", "This particular family is said to have a multiplicity of 6" ] }, { "cell_type": "code", "execution_count": null, "id": "209131ba", "metadata": { "tags": [] }, "outputs": [], "source": [ "t100.multiplicity" ] }, { "cell_type": "code", "execution_count": null, "id": "8276d5d0", "metadata": { "tags": [] }, "outputs": [], "source": [ "t6 = Miller(uvw=[[1, 0, 0], [1, 1, 0], [1, 1, 1]], phase=cubic)\n", "t6" ] }, { "cell_type": "code", "execution_count": null, "id": "c490aa37", "metadata": { "tags": [] }, "outputs": [], "source": [ "t6.multiplicity" ] }, { "cell_type": "markdown", "id": "e85074c6", "metadata": {}, "source": [ "Let's plot the symmetrically equivalent directions from the direction families $\\left<100\\right>$, $\\left<110\\right>$, and $\\left<111\\right>$ impinging on the upper hemisphere.\n", "By also returning the indices of which family each symmetrically equivalent direction belongs to from `Miller.symmetrise()`, we can give a unique color per family." ] }, { "cell_type": "code", "execution_count": null, "id": "e66baa45", "metadata": { "tags": [] }, "outputs": [], "source": [ "t7, idx = t6.symmetrise(unique=True, return_index=True)\n", "labels = plot.format_labels(t7.uvw, (\"[\", \"]\"))\n", "\n", "# Get an array with one color per family of vectors\n", "colors = np.array([f\"C{i}\" for i in range(t6.size)])[idx]\n", "\n", "t7.scatter(c=colors, vector_labels=labels, text_kwargs={\"offset\": (0, 0.02)})" ] }, { "cell_type": "markdown", "id": "3b9e14f6", "metadata": {}, "source": [ "Similarly, symmetrically equivalent planes $\\mathbf{g} = (hkl)$ can be collectively expressed as planes of the form $\\{hkl\\}$" ] }, { "cell_type": "code", "execution_count": null, "id": "2213decb", "metadata": { "tags": [] }, "outputs": [], "source": [ "g5 = Miller(hkl=[[1, 0, 0], [1, 1, 0], [1, 1, 1]], phase=cubic)\n", "g5.multiplicity" ] }, { "cell_type": "code", "execution_count": null, "id": "22b968aa", "metadata": { "tags": [] }, "outputs": [], "source": [ "g6, idx = g5.symmetrise(unique=True, return_index=True)\n", "\n", "labels = plot.format_labels(g6.hkl, (\"(\", \")\"))\n", "colors = np.array([f\"C{i}\" for i in range(g5.size)])[idx]\n", "\n", "g6.scatter(c=colors, vector_labels=labels, text_kwargs={\"offset\": (0, 0.02)})" ] }, { "cell_type": "markdown", "id": "e37f191a", "metadata": {}, "source": [ "We computed the angles between directions and plane normals earlier in this tutorial.\n", "In general, [Miller.angle_with()](../reference/generated/orix.vector.Miller.angle_with.rst) does not consider symmetrically equivalent directions, unless `use_symmetry=True` is passed.\n", "Consider $(100)$ and $(\\bar{1}00)$ and $(111)$ and $(\\bar{1}11)$ in the stereographic plot above" ] }, { "cell_type": "code", "execution_count": null, "id": "55f44f37", "metadata": { "tags": [] }, "outputs": [], "source": [ "g7 = Miller(hkl=[[1, 0, 0], [1, 1, 1]], phase=cubic)\n", "g8 = Miller(hkl=[[-1, 0, 0], [-1, 1, 1]], phase=cubic)" ] }, { "cell_type": "code", "execution_count": null, "id": "e98a9e1d", "metadata": { "tags": [] }, "outputs": [], "source": [ "g7.angle_with(g8, degrees=True)" ] }, { "cell_type": "code", "execution_count": null, "id": "6997dfff", "metadata": { "tags": [] }, "outputs": [], "source": [ "g7.angle_with(g8, use_symmetry=True, degrees=True)" ] }, { "cell_type": "markdown", "id": "48abd469-f493-4b10-8ca1-5d4e4261bd86", "metadata": {}, "source": [ "Thus, passing `use_symmetry=True` ensures that the smallest angles between $\\mathbf{g}_1$ and the symmetrically equivalent directions to $\\mathbf{g}_2$ are found." ] }, { "cell_type": "markdown", "id": "f2551ec3-e0e9-4daa-97df-b62b67b2aa7a", "metadata": {}, "source": [ "## Directions and planes in rotated crystals\n", "\n", "Let's consider the orientation of a cubic crystal rotated $45^{\\circ}$ about the sample $\\mathbf{Z}$ axis.\n", "This orientation is denoted $(\\hat{\\mathbf{n}}, \\omega)$ = $([001], -45^{\\circ})$ in axis-angle notation (see discussion by Rowenhorst et al. (2015)).\n", "Orientations in orix are *interpreted* as basis transformations from the sample to the crystal (so-called \"lab2crystal\").\n", "We therefore have to invert the orientation to get a crystal direction or plane normal expressed in the sample reference frame." ] }, { "cell_type": "code", "execution_count": null, "id": "22bcb3d7", "metadata": { "tags": [] }, "outputs": [], "source": [ "O = Orientation.from_axes_angles(\n", " [0, 0, 1], -45, cubic.point_group, degrees=True\n", ")\n", "O" ] }, { "cell_type": "markdown", "id": "3c5c6d4f", "metadata": {}, "source": [ "We can apply this orientation to a crystal direction $\\mathbf{t} = [uvw]$ to find this direction in this particular crystal with respect to the sample coordinate system, denoted $\\mathbf{v} = O^{-1} \\cdot \\mathbf{t}$" ] }, { "cell_type": "code", "execution_count": null, "id": "ce74eeea", "metadata": { "tags": [] }, "outputs": [], "source": [ "t8 = Miller(uvw=[1, 1, 1], phase=cubic)\n", "v = Vector3d(~O * t8)\n", "v" ] }, { "cell_type": "code", "execution_count": null, "id": "b673efce", "metadata": { "tags": [] }, "outputs": [], "source": [ "# [uvw] in unrotated crystal with orientation the identity orientation\n", "fig = t8.scatter(c=\"r\", return_figure=True, axes_labels=[\"X\", \"Y\"])\n", "\n", "# [uvw] in rotated crystal with (n, omega) = ([001], -45 deg)\n", "(~O * t8).scatter(figure=fig, c=\"b\", marker=\"s\")" ] }, { "cell_type": "markdown", "id": "9c3e33a8-10a4-45e9-8a6d-3b04ad92421c", "metadata": {}, "source": [ "We see that the $[111]$ vector in the rotated crystal with orientation $O = (\\hat{\\mathbf{n}}, \\omega) = ([001], -45^{\\circ})$ lies in the sample Y-Z plane.\n", "\n", "We can apply all cubic crystal symmetry operations $s_i$ to obtain the coordinates with respect to the sample reference frame for all crystallographically equivalent, but unique, directions, $\\mathbf{v} = O^{-1} \\cdot (s_i \\cdot \\mathbf{t} \\cdot s_i^{-1})$" ] }, { "cell_type": "code", "execution_count": null, "id": "69c5fa0f", "metadata": { "tags": [] }, "outputs": [], "source": [ "(~O * t8.symmetrise(unique=True)).scatter(\n", " c=\"b\", marker=\"s\", axes_labels=[\"X\", \"Y\"], hemisphere=\"both\"\n", ")" ] }, { "cell_type": "markdown", "id": "c114e169-4148-4866-aa3a-ce645f823121", "metadata": {}, "source": [ "The same applied to a trigonal crystal direction" ] }, { "cell_type": "code", "execution_count": null, "id": "6617936e", "metadata": { "tags": [] }, "outputs": [], "source": [ "O2 = Orientation.from_euler([10, 20, 30], trigonal.point_group, degrees=True)\n", "O2" ] }, { "cell_type": "code", "execution_count": null, "id": "3f70a515", "metadata": { "tags": [] }, "outputs": [], "source": [ "g9 = Miller(hkil=[1, -1, 0, 0], phase=trigonal)\n", "g9" ] }, { "cell_type": "code", "execution_count": null, "id": "6f3cd576", "metadata": { "tags": [] }, "outputs": [], "source": [ "v2 = ~O2 * g9.symmetrise(unique=True)\n", "v2.scatter(\n", " hemisphere=\"both\",\n", " grid_resolution=(30, 30),\n", " figure_kwargs=dict(figsize=(10, 5)),\n", " axes_labels=[\"X\", \"Y\"],\n", ")" ] }, { "cell_type": "markdown", "id": "27daff1b", "metadata": {}, "source": [ "The stereographic plots above are essentially the $\\mathbf{g} = \\{1\\bar{1}00\\}$ pole figure representation of the orientation $O_2$." ] }, { "cell_type": "markdown", "id": "b4b453f1", "metadata": {}, "source": [ "___" ] }, { "cell_type": "markdown", "id": "d2206e91", "metadata": {}, "source": [ "## A diamond [111] pole figure\n", "\n", "Let's make a pole figure in the $\\mathbf{t}$ = [111] direction of the diamond structure, as seen in [this figure from Wikipedia](https://commons.wikimedia.org/wiki/File:DiamondPoleFigure111.png).\n", "\n", "The figure caption reads as follows\n", "\n", "> *The spots in the stereographic projection show the orientation of lattice planes with the 111 in the center. Only poles for a non-forbidden Bragg reflection are shown between Miller indices -10 <= (h,k,l) <= 10. The green spots contain Miller indices up to 3, for example 111, 113, 133, 200 etc in its fundamental order. Red are those raising to 5, ex. 115, 135, 335 etc, while blue are all remaining until 10, such as 119, 779, 10.10.00 etc.*" ] }, { "cell_type": "code", "execution_count": null, "id": "044a66ae", "metadata": { "tags": [] }, "outputs": [], "source": [ "diamond = Phase(space_group=227)\n", "t = Miller.from_highest_indices(phase=diamond, uvw=[10, 10, 10])\n", "t" ] }, { "cell_type": "markdown", "id": "d265c73e", "metadata": {}, "source": [ "Remove duplicates under symmetry using [Miller.unique()](../reference/generated/orix.vector.Miller.unique.rst)" ] }, { "cell_type": "code", "execution_count": null, "id": "3d8a5a21", "metadata": { "tags": [] }, "outputs": [], "source": [ "t2 = t.unique(use_symmetry=True)\n", "t2.size" ] }, { "cell_type": "markdown", "id": "e259e4b9", "metadata": {}, "source": [ "Symmetrise to get all symmetrically equivalent directions" ] }, { "cell_type": "code", "execution_count": null, "id": "3bfb0bab", "metadata": { "tags": [] }, "outputs": [], "source": [ "t3 = t2.symmetrise(unique=True)\n", "t3" ] }, { "cell_type": "markdown", "id": "32b6b396", "metadata": {}, "source": [ "Remove forbidden reflections in face-centered cubic structures (all hkl must be all even or all odd)" ] }, { "cell_type": "code", "execution_count": null, "id": "8cf5fca5", "metadata": { "tags": [] }, "outputs": [], "source": [ "selection = np.sum(np.mod(t3.hkl, 2), axis=1)\n", "allowed = np.array([i not in [1, 2] for i in selection], dtype=bool)\n", "t4 = t3[allowed]\n", "t4" ] }, { "cell_type": "markdown", "id": "8990f81f", "metadata": {}, "source": [ "Assign colors to each class of vectors as per the description on Wikipedia" ] }, { "cell_type": "code", "execution_count": null, "id": "5f34deaf", "metadata": { "tags": [] }, "outputs": [], "source": [ "uvw = np.abs(t4.uvw)\n", "green = np.all(uvw <= 3, axis=-1)\n", "red = np.any(uvw > 3, axis=-1) * np.all(uvw <= 5, axis=-1)\n", "blue = np.any(uvw > 5, axis=-1)\n", "rgb_mask = np.column_stack([red, green, blue])\n", "\n", "# Sanity check\n", "print(np.count_nonzero(rgb_mask) == t4.size)" ] }, { "cell_type": "markdown", "id": "31c237e0", "metadata": {}, "source": [ "Rotate directions so that [111] impinges the unit sphere in the north pole (out of plane direction)" ] }, { "cell_type": "code", "execution_count": null, "id": "7258abd9", "metadata": { "tags": [] }, "outputs": [], "source": [ "vz = Vector3d.zvector()\n", "v111 = Vector3d([1, 1, 1])\n", "R1 = Rotation.from_axes_angles(vz.cross(v111), -vz.angle_with(v111))\n", "R2 = Rotation.from_axes_angles(vz, -15, degrees=True)\n", "t5 = R2 * R1 * t4" ] }, { "cell_type": "markdown", "id": "37614b89", "metadata": {}, "source": [ "Restrict to upper hemisphere and remove duplicates" ] }, { "cell_type": "code", "execution_count": null, "id": "aaf0c214", "metadata": { "tags": [] }, "outputs": [], "source": [ "is_upper = t5.z > 0\n", "t6 = t5[is_upper]\n", "rgb_mask2 = rgb_mask[is_upper]\n", "\n", "_, idx = t6.unit.unique(return_index=True)\n", "t7 = t6[idx]\n", "rgb_mask2 = rgb_mask2[idx]" ] }, { "cell_type": "markdown", "id": "eede6788", "metadata": {}, "source": [ "Finally, plot the vectors" ] }, { "cell_type": "code", "execution_count": null, "id": "39be4cce-f933-4066-9f82-c266f10af70f", "metadata": { "nbsphinx-thumbnail": { "tooltip": "Operating with vectors in the crystal and sample reference frames" }, "tags": [ "nbsphinx-thumbnail" ] }, "outputs": [], "source": [ "rgb = np.zeros_like(t7.uvw)\n", "rgb[rgb_mask2] = 1\n", "\n", "t7.scatter(c=rgb, s=10, grid=False, figure_kwargs=dict(figsize=(12, 12)))" ] } ], "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.11.6" } }, "nbformat": 4, "nbformat_minor": 5 }