{ "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": { "tags": [] }, "source": [ "# Uniform sampling of orientation space\n", "\n", "In this tutorial we will uniformly sample orientation space *SO(3)* of crystallographic proper point groups.\n", "The proper point groups given in the two widely used notations of Hermann-Mauguin and Schoenflies are\n", "\n", "| Hermann-Mauguin | Schoenflies |\n", "| --------------- | ----------- |\n", "| *1* | *C1* |\n", "| *2* | *C2* |\n", "| *222* | *D2* |\n", "| *4* | *C4* |\n", "| *422* | *D4* |\n", "| *3* | *C3* |\n", "| *32* | *D3* |\n", "| *6* | *C6* |\n", "| *622* | *D6* |\n", "| *23* | *T* |\n", "| *432* | *O* |\n", "\n", "We can sample either the entire orientation space with [orix.sampling.get_sample_fundamental()](../reference/generated/orix.sampling.get_sample_fundamental.rst) or around a certain orientation with [orix.sampling.get_sample_local()](../reference/generated/orix.sampling.get_sample_local.rst).\n", "Both functions accept a resolution $\\left<\\theta\\right>$ parameter, which is the average disorientation (smallest misorientation) between sampled orientations. get_sample_fundamental() also accepts a proper point group specifier, either by passing the point_group or space_group, from which the proper point group will be determined (for example space group 225 $Fm\\bar{3}m$ -> point group $m\\bar{3}m$ -> proper point group *432*).\n", "This ensures that the returned orientations are within the point group's fundamental zone.\n", "\n", "Three sampling methods are available, and their names accepted as strings to the method parameter in the two functions are: \n", "1. \"cubochoric\" \n", "2. \"haar_euler\" \n", "3. \"quaternion\" \n", "\n", "The *cubochoric* sampling method is presented in Singh and De Graef (2016) and Rosca et al. (2014).\n", "The starting point of the method is a uniform sampling of a cube.\n", "The number of semi-edge steps of this cube $N$ is determined either from resolution $\\left<\\theta\\right>$ by the empirical expression\n", "\n", "$$\n", "N = \\mathrm{int}\\left[\\frac{131.97049}{\\left<\\theta\\right> - 0.03732}\\right],\n", "$$\n", "\n", "or it can be passed directly to the two functions in the semi_edge_steps parameter when method=\"cubochoric\".\n", "As an example, for the cubic point group *432*, $N$ = 100 will result in 333 227 uniformly sampled orientations on *SO(3)*, with $\\left<\\theta\\right> = 1.36^{\\circ}$.\n", "After the cube is sampled, these coordinates are projected onto *SO(3)* as explained in the above two references.\n", "The sample contains the identity rotation.\n", "The implementation in orix should produce identical results to the one from EMsoft's command line program [*EMsampleRFZ*](https://github.com/EMsoft-org/EMsoft/wiki/EMsampleRFZ), which the implementation is based upon.\n", "\n", "The sample from the *Haar Euler* method is proportional to $\\cos{\\beta} d\\alpha d\\beta d\\gamma$, and explained further on [Stack Overflow](https://math.stackexchange.com/questions/3316481/).\n", "\n", "The *quaternion* sampling method is implemented as described in La Valle (2006)." ] }, { "cell_type": "code", "execution_count": null, "id": "2b47ffdf", "metadata": {}, "outputs": [], "source": [ "%matplotlib inline\n", "\n", "import matplotlib.pyplot as plt\n", "import numpy as np\n", "\n", "from orix.quaternion import Orientation, symmetry\n", "from orix.sampling import get_sample_fundamental\n", "from orix.vector import Vector3d\n", "\n", "\n", "plt.rcParams.update(\n", " {\n", " \"axes.grid\": True,\n", " \"figure.figsize\": (15, 5),\n", " \"font.size\": 20,\n", " \"lines.linewidth\": 2,\n", " }\n", ")" ] }, { "cell_type": "markdown", "id": "91c4aeb4-8a99-4002-b52e-f8d15d2db87c", "metadata": {}, "source": [ "## Sample\n", "\n", "Let's first generate sampled orientations for the point group *432* with an average disorientation of $2^{\\circ}$" ] }, { "cell_type": "code", "execution_count": null, "id": "baf91454-0d71-4123-9945-ceecc0aeb4d4", "metadata": {}, "outputs": [], "source": [ "pg432 = symmetry.O\n", "resolution2 = 3" ] }, { "cell_type": "code", "execution_count": null, "id": "a08c8562-ae3e-4839-a89c-1aea194b795f", "metadata": {}, "outputs": [], "source": [ "rot_cube = get_sample_fundamental(\n", " resolution2, point_group=pg432, method=\"cubochoric\"\n", ")\n", "# rot_cube = get_sample_fundamental(point_group=pg432, method=\"cubochoric\", semi_edge_steps=67) # Gives identical results\n", "rot_cube" ] }, { "cell_type": "code", "execution_count": null, "id": "855c52bd-ba98-4244-b83b-e4266d36e384", "metadata": {}, "outputs": [], "source": [ "ori_cube = Orientation(rot_cube, symmetry=pg432)\n", "ori_cube" ] }, { "cell_type": "code", "execution_count": null, "id": "2bcaada0-b513-4d24-b24d-3c8a63d3cb90", "metadata": {}, "outputs": [], "source": [ "rot_euler = get_sample_fundamental(\n", " resolution2, point_group=pg432, method=\"haar_euler\"\n", ")\n", "ori_euler = Orientation(rot_euler, symmetry=pg432)\n", "print(ori_euler.size)" ] }, { "cell_type": "code", "execution_count": null, "id": "e20a5167-6721-4ffd-8cb9-367bf206ce81", "metadata": {}, "outputs": [], "source": [ "rot_quat = get_sample_fundamental(\n", " resolution2, point_group=pg432, method=\"quaternion\"\n", ")\n", "ori_quat = Orientation(rot_quat, symmetry=pg432)\n", "print(ori_quat.size)" ] }, { "cell_type": "markdown", "id": "32e33ab9-1750-4825-95bf-1718eca35679", "metadata": {}, "source": [ "## Visualize samples\n", "\n", "We will only check the uniformity of these methods' sampling of the Rodrigues Fundamental Zone (RFZ) qualitatively here.\n", "\n", "Let's draw a random sample of 10 000 orientations from each full sample and plot them within the RFZ, with a view from the top and the side" ] }, { "cell_type": "code", "execution_count": null, "id": "e6ac2630-89e2-44b9-a188-d49828d04b06", "metadata": {}, "outputs": [], "source": [ "np.random.seed(42) # For reproducibility of the random sample\n", "n = 10000\n", "ori_cube2 = ori_cube.get_random_sample(n)\n", "ori_euler2 = ori_euler.get_random_sample(n)\n", "ori_quat2 = ori_quat.get_random_sample(n)" ] }, { "cell_type": "code", "execution_count": null, "id": "f313bdb6-9a2c-44f8-8f05-c0655dee655b", "metadata": {}, "outputs": [], "source": [ "fig = plt.figure(figsize=(15, 10))\n", "scatter_kwargs = dict(\n", " projection=\"rodrigues\",\n", " figure=fig,\n", " wireframe_kwargs=dict(color=\"k\", linewidth=1, alpha=0.1),\n", " s=5,\n", ")\n", "\n", "ori_cube2.scatter(position=231, c=\"C0\", **scatter_kwargs)\n", "ori_euler2.scatter(position=232, c=\"C1\", **scatter_kwargs)\n", "ori_quat2.scatter(position=233, c=\"C2\", **scatter_kwargs)\n", "\n", "ori_cube2.scatter(position=234, c=\"C0\", **scatter_kwargs)\n", "ori_euler2.scatter(position=235, c=\"C1\", **scatter_kwargs)\n", "ori_quat2.scatter(position=236, c=\"C2\", **scatter_kwargs)\n", "\n", "titles = [\"cubochoric\", \"haar_euler\", \"quaternion\"]\n", "for i, title in zip([0, 1, 2], titles):\n", " fig.axes[i].view_init(elev=90, azim=0)\n", " fig.axes[i].set_title(titles[i])\n", "for i in [3, 4, 5]:\n", " fig.axes[i].view_init(elev=0, azim=0)" ] }, { "cell_type": "markdown", "id": "af2e11bb", "metadata": {}, "source": [ "Let's also plot the orientations' crystal directions in the x, y, and z sample directions in the inverse pole figure, per distribution" ] }, { "cell_type": "code", "execution_count": null, "id": "7036a94d", "metadata": { "nbsphinx-thumbnail": { "tooltip": "Three routes to the sampling of orientation space SO(3)" }, "tags": [ "nbsphinx-thumbnail" ] }, "outputs": [], "source": [ "directions = Vector3d(((1, 0, 0), (0, 1, 0), (0, 0, 1)))\n", "\n", "ori_cube2.scatter(\"ipf\", direction=directions, c=\"C0\", s=5)" ] }, { "cell_type": "code", "execution_count": null, "id": "01e5af7f", "metadata": {}, "outputs": [], "source": [ "ori_euler2.scatter(\"ipf\", direction=directions, c=\"C1\", s=5)" ] }, { "cell_type": "code", "execution_count": null, "id": "800060e9", "metadata": {}, "outputs": [], "source": [ "ori_quat2.scatter(\"ipf\", direction=directions, c=\"C2\", s=5)" ] } ], "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 }