pyxem / orix

Analysing crystal orientations and symmetry in Python
https://orix.readthedocs.io
GNU General Public License v3.0
80 stars 48 forks source link

`angle_with()` giving weird results #431

Open anderscmathisen opened 1 year ago

anderscmathisen commented 1 year ago

I am trying to calculate some angles between Orientations by using the angle_with() function and getting some weird numbers.

Below is a code showing what I mean.

from orix.quaternion import Orientation
from orix.quaternion.symmetry import C6v
import orix.plot
import matplotlib.pyplot as plt
import numpy as np

plt.rcParams.update({
    "axes.grid": True,
})

ori1 = Orientation([[0.2283, 0.6892, -0.13, 0.6752]], symmetry=C6v)
ori2 = Orientation([[0.7028, -0.2065, 0.6714, 0.1126]], symmetry=C6v)

print(np.rad2deg(ori1.angle_with(ori2)))

fig = plt.figure()
ax = fig.add_subplot(projection="ipf", symmetry=C6v)
ax.scatter(ori1)
ax.scatter(ori2)

Output: [178.32181777]

In the scatterplot IPF (z-direction), these orientations are, as the image below shows, very close ~5° and I don't understand why angle_with() is giving me such a high angle.

PS: I am running v.0.10.0, but v.0.11.0 gives the same

output

hakonanes commented 1 year ago

Thank you for bringing this up, @anderscmathisen.

I got the same misorientation angle with MTEX v5.8.2:

>> cs = crystalSymmetry('C6v');
%% Inverted, because MTEX defines orientations as crystal -> sample
>> o1 = orientation([0.2283 -0.6892 0.13 -0.6752], cs);
>> o2 = orientation([0.7028 0.2065 -0.6714 -0.1126], cs);
>> angle(o1, o2) / degree

ans =

  178.3219

What you've plotted above is where the sample Z direction points in the two crystals. The points are located about 5 degrees apart because they have been projected into the fundamental sector of 6mm:

from orix.quaternion import Orientation, symmetry
from orix.vector import Vector3d

pg = symmetry.C6v
ori1 = Orientation([0.2283, 0.6892, -0.13, 0.6752], symmetry=pg)
ori2 = Orientation([0.7028, -0.2065, 0.6714, 0.1126], symmetry=pg)

# Rotate sample Z into crystals
v1 = ori1 * Vector3d.zvector()
v2 = ori2 * Vector3d.zvector()

v1.angle_with(v2, degrees=True)  # Outputs: array([55.55565557])

fig = v1.scatter(return_figure=True, c="tab:blue")
v2.scatter(figure=fig, c="tab:orange")
v1.in_fundamental_sector(pg).scatter(figure=fig, c="tab:green")
fig.axes[0].plot(pg.fundamental_sector.edges, color="k")

test

hakonanes commented 1 year ago

Forgot to ask, but what misorientation (angle) do you expect?

anderscmathisen commented 1 year ago

Hmm, yes I suspected it might have something to do with a projection, however, I thought that angle_with(), which according to docs calculates the smallest symmetry reduced angle, should give me the angle in the projected symmetry reduced space, and not over the entire stereographic projection.

The orientations I am calculating angles between are from a TEM tilt series where the sample was tilted 5° between capturing SPED data. (orientations found by template matching using pyxem). And so I was expecting to get about that angle. But the angle_with() function is perhaps not the right one to use in this situation?

hakonanes commented 1 year ago

Orientation.angle_with() calculates the smallest misorientation angle between orientations as they are defined. The largest misorientation angle possible for two crystals with the 6mm point group symmetry is 180 degrees, so an angle of 178 degrees is plausible. If you had imposed a mirror plane across the c axis, giving you the centrosymmetric point group 6/mmm (D6h), the misorientation angle between your two orientations is about 4 degrees. I believe the sudden jump to almost 180 degree misorientation from a tilt of 5 degrees is a case of pseudo-symmetry, i.e. two orientations with a misorientation of about 180 degrees about a or b looking near identical, and the indexing algorithm not being able to distinguish between the two.

I think I understand what you want: Miller.angle_with(..., use_symmetry=True). If you wrap the two vectors v1,2 above in Miller, you can get the symmetry reduced angle between where the sample Z direction points in the two crystals. Continuing on the example above:

from orix.crystal_map import Phase
from orix.vector import Miller

phase_6mm = Phase(point_group=symmetry.C6v)
m1 = Miller(uvw=(ori1 * Vector3d.zvector()).data, phase=phase_6mm)
m2 = Miller(uvw=(ori2 * Vector3d.zvector()).data, phase=phase_6mm)

m1.angle_with(m2, degrees=True)  # Output: array([55.55565557])
m1.angle_with(m2, use_symmetry=True, degrees=True)  # Output: array([3.16904388])