{ "cells": [ { "cell_type": "markdown", "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", "metadata": {}, "source": [ "# Visualizing Crystal Poles in the Pole Density Function\n", "\n", "In this tutorial we will quantify the distribution of crystallographic poles, which is useful, for example, in texture analysis, using the Pole Distribution Function (PDF)." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%matplotlib inline\n", "import numpy as np\n", "from matplotlib import pyplot as plt\n", "from mpl_toolkits.mplot3d import Axes3D\n", "from orix import plot\n", "from orix.crystal_map import Phase\n", "from orix.data import ti_orientations\n", "from orix.sampling import sample_S2\n", "from orix.vector import Miller, Vector3d\n", "\n", "# We'll want our plots to look a bit larger than the default size\n", "plt.rcParams.update(\n", " {\n", " \"figure.figsize\": (10, 5),\n", " \"lines.markersize\": 2,\n", " \"font.size\": 15,\n", " \"axes.grid\": False,\n", " }\n", ")\n", "w, h = plt.rcParams[\"figure.figsize\"]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "First, we load some sample orientations from a Titanium sample dataset which represent crystal orientations in the sample reference frame.\n", "These orientations have a defined $622$ point group symmetry:\n", "\n", "
\n", "\n", "Note\n", "\n", "If not previously downloaded, running this cell will download some example data from an online repository to a local cache, see the docstring of [ti_orientations()](../reference/generated/orix.data.ti_orientations.rst) for more details.\n", "\n", "
" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ori = ti_orientations(allow_download=True)\n", "ori" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's look at the sample's $\\{01\\bar{1}1\\}$ texture plotted in the stereographic projection.\n", "\n", "First we must define the crystal's point group and generate the set of symmetrically unique $(01\\bar{1}1)$ poles: " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "m = Miller(hkil=(0, 1, -1, 1), phase=Phase(point_group=ori.symmetry))\n", "m = m.symmetrise(unique=True)\n", "m" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now let's compute the direction of these poles in the sample reference frame.\n", "\n", "This is done using the [Orientation](../reference/generated/orix.quaternion.Orientation.rst)-[Vector3d](../reference/generated/orix.vector.Vector3d.rst) outer product.\n", "We can pass lazy=True parameter to perform the computation in chunks using Dask, this helps to reduce memory usage when there are many computations to be performed. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "poles = (~ori).outer(m, lazy=True, progressbar=True, chunk_size=2000)\n", "poles.shape" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can plot these poles in the sterographic projection:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "poles.scatter(\n", " hemisphere=\"both\",\n", " alpha=0.02,\n", " figure_kwargs=dict(figsize=(2 * h, h)),\n", " axes_labels=[\"X\", \"Y\"],\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In this case there are many individual data points, which makes it difficult to interpret whether regions contain higher or lower pole density.\n", "\n", "In this case we can use the [Vector3d.pole_density_function()](../reference/generated/orix.vector.Vector3d.pole_density_function.rst) to measure the pole density on the unit sphere $S_2$.\n", "Internally this uses the equal area parameterization to calculate cells on $S_2$ with the same solid angle.\n", "In this representation randomly oriented vectors have the same probability of intercepting each cell, thus we can represent our sample's PDF as Multiples of Random Density (MRD).\n", "This follows the work of Rohrer et al.(2004).\n", "\n", "Below is the equal area sampling representation on $S_2$ in both the stereographic projection and 3D with a resolution of 10°:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fig = plt.figure(figsize=(2 * h, h))\n", "ax0 = fig.add_subplot(121, projection=\"stereographic\")\n", "ax1 = fig.add_subplot(122, projection=\"3d\")\n", "\n", "v_mesh = sample_S2(resolution=10, method=\"equal_area\")\n", "\n", "ax0.hemisphere = \"upper\"\n", "\n", "ax0.scatter(v_mesh)\n", "ax0.show_hemisphere_label()\n", "ax0.set_labels(\"X\", \"Y\", None)\n", "\n", "ax1.scatter(*v_mesh.data.T)\n", "\n", "lim = 1\n", "ax1.set_xlim(-lim, lim)\n", "ax1.set_ylim(-lim, lim)\n", "ax1.set_zlim(-lim, lim)\n", "\n", "ax1.set_xticks((-1, 0, 1))\n", "ax1.set_yticks((-1, 0, 1))\n", "ax1.set_zticks((-1, 0, 1))\n", "\n", "ax1.set_xlabel(\"X\")\n", "ax1.set_ylabel(\"Y\")\n", "ax1.set_zlabel(\"Z\")\n", "\n", "ax1.set_box_aspect((1, 1, 1))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For randomly distributed vectors on $S_2$, we can can see that MRD tends to 1 with an increasing number of vectors:\n", "\n", "NB. PDF plots are displayed on the same color scale." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "num = (10_000, 100_000, 1_000_000, 10_000_000)\n", "\n", "fig, ax = plt.subplots(\n", " nrows=2,\n", " ncols=2,\n", " figsize=(2 * h, 2 * h),\n", " subplot_kw=dict(projection=\"stereographic\"),\n", ")\n", "ax = ax.ravel()\n", "\n", "for i, n in enumerate(num):\n", " v = Vector3d(np.random.randn(n, 3)).unit\n", " ax[i].pole_density_function(v, log=False, vmin=0.8, vmax=1.2)\n", " ax[i].set_labels(\"X\", \"Y\", None)\n", " ax[i].set_title(str(n))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can also change the sampling angular resolution on $S_2$, the colormap with the cmap parameter, and broadening of the density distribution with sigma:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fig, ax = plt.subplots(\n", " nrows=2,\n", " ncols=2,\n", " figsize=(2 * h, 2 * h),\n", " subplot_kw=dict(projection=\"stereographic\"),\n", ")\n", "ax = ax.ravel()\n", "\n", "v = Vector3d(np.random.randn(1_000_000, 3)).unit\n", "\n", "ax[0].pole_density_function(v, log=False, resolution=1)\n", "ax[0].set_title(\"Sampling resolution: 1$\\degree$\")\n", "\n", "# change sampling resolution on S2\n", "ax[1].pole_density_function(v, log=False, resolution=5)\n", "ax[1].set_title(\"Sampling resolution: 5$\\degree$\")\n", "\n", "# increase peak broadening\n", "ax[2].pole_density_function(v, log=False, resolution=1, sigma=15)\n", "ax[2].set_title(\"Sampling resolution: 1$\\degree$\\n$\\sigma$: 15$\\degree$\")\n", "\n", "# change colormap\n", "ax[3].pole_density_function(v, log=False, resolution=1, cmap=\"gray_r\")\n", "ax[3].set_title('Sampling resolution: 1$\\degree$\\ncmap: \"gray_r\"')\n", "\n", "for a in ax:\n", " a.set_labels(\"X\", \"Y\", None)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Poles from real samples tend not to be randomly oriented, as the material microstructure is arranged into regions of similar crystal orientation, known as grains.\n", "\n", "The PDF for the measured $\\{01\\bar{1}1\\}$ poles from the Titanium sample loaded at the beginning of the tutorial:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "poles.pole_density_function(\n", " hemisphere=\"both\", log=False, figure_kwargs=dict(figsize=(2 * h, h))\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can also plot these densities on a log scale to reduce the contrast between high and low density regions.\n", "\n", "By comparing the point data shown at the top of the notebook with the calculated pole densities from PDF, we can see that not all regions in the point data representation have the same density and that PDF is needed for better quantification:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fig, ax = plt.subplots(\n", " ncols=2, subplot_kw=dict(projection=\"stereographic\"), figsize=(2 * h, h)\n", ")\n", "\n", "ax[0].hemisphere = \"upper\"\n", "ax[1].hemisphere = \"upper\"\n", "\n", "ax[0].scatter(poles, s=2, alpha=0.02)\n", "ax[1].pole_density_function(poles, log=True)\n", "\n", "for a in ax:\n", " a.set_labels(\"X\", \"Y\", None)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A clear example of this can be shown by combining the PDF and point data onto the same plot:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "nbsphinx-thumbnail": { "tooltip": "Comparing PDF with pole point data in the stereographic projection." }, "tags": [ "nbsphinx-thumbnail" ] }, "outputs": [], "source": [ "fig = poles.scatter(\n", " alpha=0.01,\n", " c=\"w\",\n", " return_figure=True,\n", " axes_labels=[\"X\", \"Y\"],\n", " show_hemisphere_label=True,\n", ")\n", "poles.pole_density_function(log=True, figure=fig)" ] } ], "metadata": { "interpreter": { "hash": "4396f389b93e7269692bd3bea4c62813bbe379469bde939b058805f538feec11" }, "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": 4 }