This page was generated from doc/uniform_sampling_of_orientation_space.ipynb. Interactive online version: Binder badge.

Uniform sampling of orientation space

This notebook shows how to uniformly sample orientation space SO(3) of crystallographic proper point groups. The proper point groups given in the two widely used notations of Hermann-Mauguin and Schoenflies are

Hermann-Mauguin

Schoenflies

1

C1

2

C2

222

D2

4

C4

422

D4

3

C3

32

D3

6

C6

622

D6

23

T

432

O

We can sample either the entire orientation space with orix.sampling.get_sample_fundamental() or around a certain orientation with orix.sampling.get_sample_local(). 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). This ensures that the returned orientations are within the point group’s fundamental zone.

Three sampling methods are available, and their names accepted as strings to the method parameter in the two functions are:
1. “cubochoric”
2. “haar_euler”
3. “quaternion”

The cubochoric sampling method is presented in [SDeGraef16] and [RMDeGraef14]. The starting point of the method is a uniform sampling of a cube. The number of semi-edge steps of this cube \(N\) is determined either from resolution \(\left<\theta\right>\) by the empirical expression

\[N = \mathrm{int}\left[\frac{131.97049}{\left<\theta\right> - 0.03732}\right],\]

or it can be passed directly to the two functions in the semi_edge_steps parameter when method="cubochoric". 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}\). After the cube is sampled, these coordinates are projected onto SO(3) as explained in the above two references. The sample contains the identity rotation. The implementation in orix should produce identical results to the one from EMsoft’s command line program *EMsampleRFZ*, which the implementation is based upon.

The sample from the Haar Euler method is proportional to \(\cos{\beta} d\alpha d\beta d\gamma\), and explained further on Stack Overflow.

The quaternion sampling method is implemented as described in [LaV06].

We will only check the uniformity of these methods’ sampling of the Rodrigues Fundamental Zone (RFZ) qualitatively here, as returned from get_sample_fundamental(). From each of the methods’ sampled orientations, we draw a sufficiently large random sample to plot. We plot the orientations in the RFZ and how they rotate Z vectors in the northern hemisphere of the stereographic projection.

[1]:
%matplotlib inline

import matplotlib.pyplot as plt
import numpy as np
from orix.quaternion import Orientation, symmetry
from orix.sampling import get_sample_fundamental
from orix.vector import Vector3d


plt.rcParams.update({
    "axes.grid": True,
    "font.size": 20,
    "lines.linewidth": 2,
})

Let’s first generate sampled orientations for the point group 432 with an average disorientation of \(2^{\circ}\)

[2]:
pg432 = symmetry.O
resolution2 = 3
[3]:
rot_cube = get_sample_fundamental(resolution2, point_group=pg432, method="cubochoric")
#rot_cube = get_sample_fundamental(point_group=pg432, method="cubochoric", semi_edge_steps=67)  # Gives identical results
rot_cube
[3]:
Rotation (30443,)
[[ 0.8562 -0.3474 -0.3474 -0.1595]
 [ 0.8562 -0.3511 -0.3511 -0.1425]
 [ 0.8562 -0.3544 -0.3544 -0.1252]
 ...
 [ 0.8562  0.3544  0.3544  0.1252]
 [ 0.8562  0.3511  0.3511  0.1425]
 [ 0.8562  0.3474  0.3474  0.1595]]
[4]:
ori_cube = Orientation(rot_cube).set_symmetry(pg432)
ori_cube
[4]:
Orientation (30443,) 432
[[ 0.8562 -0.3474 -0.3474 -0.1595]
 [ 0.8562 -0.3511 -0.3511 -0.1425]
 [ 0.8562 -0.3544 -0.3544 -0.1252]
 ...
 [ 0.8562  0.3544  0.3544  0.1252]
 [ 0.8562  0.3511  0.3511  0.1425]
 [ 0.8562  0.3474  0.3474  0.1595]]
[5]:
rot_euler = get_sample_fundamental(resolution2, point_group=pg432, method="haar_euler")
ori_euler = Orientation(rot_euler).set_symmetry(pg432)
print(ori_euler.size)
35083
[6]:
rot_quat = get_sample_fundamental(resolution2, point_group=pg432, method="quaternion")
ori_quat = Orientation(rot_quat).set_symmetry(pg432)
print(ori_quat.size)
34859

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

[7]:
np.random.seed(42)  # For reproducibility of the random sample
n = 10000
ori_cube2 = ori_cube.get_random_sample(n)
ori_euler2 = ori_euler.get_random_sample(n)
ori_quat2 = ori_quat.get_random_sample(n)
[8]:
fig = plt.figure(figsize=(15, 10))
scatter_kwargs = dict(
    projection="rodrigues",
    figure=fig,
    wireframe_kwargs=dict(color="k", linewidth=1, alpha=0.1),
    s=5,
)

ori_cube2.scatter(position=231, c="C0", **scatter_kwargs)
ori_euler2.scatter(position=232, c="C1", **scatter_kwargs)
ori_quat2.scatter(position=233, c="C2", **scatter_kwargs)

ori_cube2.scatter(position=234, c="C0", **scatter_kwargs)
ori_euler2.scatter(position=235, c="C1", **scatter_kwargs)
ori_quat2.scatter(position=236, c="C2", **scatter_kwargs)

titles = ["cubochoric", "haar_euler", "quaterion"]
for i, title in zip([0, 1, 2], titles):
    fig.axes[i].view_init(elev=90, azim=0)
    fig.axes[i].set_title(titles[i])
for i in [3, 4, 5]:
    fig.axes[i].view_init(elev=0, azim=0)
_images/uniform_sampling_of_orientation_space_11_0.png

Let’s also Z vectors rotated by the random samples from each of the orientation distributions in the stereographic projection

[9]:
vz = Vector3d.zvector()
vx = Vector3d.xvector()

fig, ax = plt.subplots(
    nrows=2, ncols=3, subplot_kw=dict(projection="stereographic"), figsize=(15, 10)
)

ax[0, 0].scatter(ori_cube2 * vz, c="C0", s=5)
ax[0, 0].set_labels("x", "y")
ax[0, 0].set_title("cubochoric", pad=20)
ax[0, 1].scatter(ori_euler2 * vz, c="C1", s=5)
ax[0, 1].set_title("haar_euler", pad=20)
ax[0, 2].scatter(ori_quat2 * vz, c="C2", s=5)
ax[0, 2].set_title("quaternion", pad=20)
ax[1, 0].scatter(ori_cube2 * vx, c="C0", s=5)
ax[1, 1].scatter(ori_euler2 * vx, c="C1", s=5)
ax[1, 2].scatter(ori_quat2 * vx, c="C2", s=5)
_images/uniform_sampling_of_orientation_space_13_0.png