{ "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 }