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