Skip to content

Problem in map_into_symmetry_reduced_zone #368

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
maclariz opened this issue Jun 17, 2022 · 10 comments
Open

Problem in map_into_symmetry_reduced_zone #368

maclariz opened this issue Jun 17, 2022 · 10 comments

Comments

@maclariz
Copy link

maclariz commented Jun 17, 2022

Running map_into_symmetry_reduced_zone() on sensible data (single crystal, all similar orientations) yields the following (just a few lines shown for illustration):

[-0.9681  0.0373 -0.0222  0.2467]
[-0.9685  0.0373 -0.0222  0.2452]
[-0.9685  0.0373  0.0222 -0.2451]
[-0.9686  0.0368  0.016  -0.2455]
[-0.9688  0.0373  0.0223 -0.2442]

Fixing this manually in a quick for loop gets me to:

[-0.96812383,  0.03732545,  0.02216656, -0.24668136],
[-0.96849277,  0.03729216,  0.02222252, -0.2452289 ],
[-0.96851503,  0.03729006,  0.02222605, -0.24514094],
[-0.96856316,  0.03676689,  0.01601705, -0.24551387],
[-0.96875969,  0.03726781,  0.02226333, -0.24417231],

(which is just a numpy array)

reimporting as an Orientation object, it then just transforms back to the first. Obviously, the constant sign flips in this data on index 2 and 3 in each quaternion play merry hell with any calculations!

@harripj
Copy link
Collaborator

harripj commented Jun 17, 2022

@maclariz could you provide the original orientation data before calling map_into_reduced_symmetry_zone()? Also the symmetry?

@maclariz
Copy link
Author

maclariz commented Jun 20, 2022

@harripj Here you go:

 [ 0.5101 -0.0107  0.0421 -0.859 ]
 [ 0.5114 -0.0107  0.0421 -0.8582]
 [ 0.2451 -0.0222  0.0373 -0.9685]
 [ 0.2455 -0.016   0.0368 -0.9686]
 [ 0.2442 -0.0223  0.0373 -0.9688]

@maclariz
Copy link
Author

@harripj m3m (FCC)

@maclariz
Copy link
Author

maclariz commented Jun 20, 2022

The following sorted out my data and resulted in a consistent set of orientations that give consistent misorientation angles:

# Take the data into a pure numpy array
ori5 = ori3.data

# Drop into a setting where index 0 (cos angle) is positive, 1 (prop to x) and 2 (prop to y) are negative
# and index 3 (prop to z) are positive
ori5[:,0] = abs(ori5[:,0])
ori5[:,1] = -abs(ori5[:,1])
ori5[:,2] = -abs(ori5[:,2])
ori5[:,3] = abs(ori5[:,3])

# Transform back into orientation object without calling symmetry
ori5 = Orientation(ori5)
ori5

# Make a new mean orientation object without symmetry
mean4_1 = all4.mean()
mean4_1

# Make new misorientation object
ori6 = ori5-mean4_1

this is in no way a general solution, but works on a dataset where all orientations are broadly similar and map into a set of distinct orientation in a roughly 30 degree range (except for the odd noisy misindexed point).

@pc494
Copy link
Member

pc494 commented Jun 21, 2022

Hi @maclariz, I just eyeballed this in my lunchtime, but I think the sign flips might just be part of how fundamental zones worked. I would be interested to know what happens if you use

def get_distance_matrix(self, chunk_size=20, progressbar=True):
to assess the distances between the rotations after applying the symmetry?

@maclariz
Copy link
Author

maclariz commented Jun 22, 2022

@pc494 I don't know what this really adds, but I'll give you some output from this. This is a distance matrix of 6 points in this area with the orientation flips, converted to degrees (with np.degrees):

array([[ 0.        , 33.23155212,  0.05729578, 33.2888479 ,  0.72280107,0.05729578],
       [33.23155212,  0.        , 33.17425634,  0.05729578, 33.1164664 ,33.2888479 ],
       [ 0.05729578, 33.17425634,  0.        , 33.23155212,  0.71532024, 0.11459156],
       [33.2888479 ,  0.05729578, 33.23155212,  0.        , 33.17374857, 33.34614368],
       [ 0.72280107, 33.1164664 ,  0.71532024, 33.17374857,  0.        , 0.73468719],
       [ 0.05729578, 33.2888479 ,  0.11459156, 33.34614368,  0.73468719,0.        ]])

It would seem there are some different orientations there with very small angles above the lowest, and it is probably flipping between different solutions, purely on metric of lowest angle, rather than sticking with a consistent orientation of the x,y,z axes of the cell...

@maclariz
Copy link
Author

maclariz commented Jun 22, 2022

@pc494 Here is the result of .to_matrix() on the same 6 data points:

array([[[ 0.87929659, -0.4723087 , -0.06133516],
        [ 0.47562573,  0.8775068 ,  0.06133494],
        [ 0.024853  , -0.08310418,  0.99623091]],

       [[ 0.87873789,  0.47334738,  0.06133494],
        [-0.47666228,  0.87694416,  0.06133516],
        [-0.02475448, -0.08313358,  0.99623091]],

       [[ 0.87882384, -0.47318776, -0.06133516],
        [ 0.476503  ,  0.87703074,  0.06133494],
        [ 0.02476988, -0.08312899,  0.99623091]],

       [[ 0.8792108 ,  0.47246841,  0.06133494],
        [-0.4757851 ,  0.87742039,  0.06133516],
        [-0.0248376 , -0.08310878,  0.99623091]],

       [[ 0.87893279, -0.47441358, -0.04908061],
        [ 0.47676917,  0.87674227,  0.0633573 ],
        [ 0.01297348, -0.07908693,  0.9967833 ]],

       [[ 0.87976846, -0.47142916, -0.06133516],
        [ 0.47474799,  0.87798199,  0.06133494],
        [ 0.02493609, -0.08307929,  0.99623091]]])

It is simply arbitrary sign flips on axes that is messing this up.

@harripj
Copy link
Collaborator

harripj commented Jun 22, 2022

So if I run:

o = Orientation([[ 0.5101, -0.0107,  0.0421, -0.859 ],
                [ 0.5114, -0.0107,  0.0421, -0.8582],
                [ 0.2451, -0.0222,  0.0373, -0.9685],
                [ 0.2455, -0.016 ,  0.0368, -0.9686],
                [ 0.2442, -0.0223,  0.0373, -0.9688]], symmetry=Oh)
# distance matrix original data
d1 =o.get_distance_matrix()

# distance matrix symmetry reduced data
o2 = o.map_into_symmetry_reduced_zone()
d2 = o2.get_distance_matrix()

# compute fractional error
diff = (d2 - d1) / d1
diff[np.diag_indices(diff.shape[0])] = 0
np.abs(diff.max())
0.0175

which tells me there is <2% error between o1 and o2, which is still quite large.

@maclariz I get different results on my machine, which we should clarify before going forward:

>>> o
Orientation (5,) m-3m
[[ 0.5101 -0.0107  0.0421 -0.859 ]
 [ 0.5114 -0.0107  0.0421 -0.8582]
 [ 0.2451 -0.0222  0.0373 -0.9685]
 [ 0.2455 -0.016   0.0368 -0.9686]
 [ 0.2442 -0.0223  0.0373 -0.9688]]

>>> o2
Orientation (5,) m-3m
[[ 0.9681  0.0222  0.0373 -0.2467]
 [ 0.9685  0.0222  0.0373 -0.2452]
 [ 0.9685  0.0373  0.0222  0.2451]
 [ 0.9686  0.0368  0.016   0.2455]
 [ 0.9688  0.0373  0.0223  0.2442]]

@hakonanes
Copy link
Member

I've changed the order of which symmetry elements are applied to the left and right of (mis)orientations when reducing them to the fundamental zone in #442. After this change, I get the following result

import numpy as np
from orix.quaternion import Orientation, symmetry


o = Orientation([[ 0.5101, -0.0107,  0.0421, -0.859 ],
                [ 0.5114, -0.0107,  0.0421, -0.8582],
                [ 0.2451, -0.0222,  0.0373, -0.9685],
                [ 0.2455, -0.016 ,  0.0368, -0.9686],
                [ 0.2442, -0.0223,  0.0373, -0.9688]], symmetry=symmetry.Oh)

# distance matrix original data
d1 = o.get_distance_matrix()

# distance matrix symmetry reduced data
o2 = o.reduce()  # Replaces map_into_symmetry_reduced_zone() 
print(o2)
# Orientation (5,) m-3m
# [[ 0.9681 -0.0373  0.0222 -0.2467]
# [ 0.9685 -0.0373  0.0222 -0.2452]
# [ 0.9685 -0.0373 -0.0222  0.2451]
# [ 0.9686 -0.0368 -0.016   0.2455]
# [ 0.9688 -0.0373 -0.0223  0.2442]]

d2 = o2.get_distance_matrix()

np.allclose(d1, d2)
# True

@maclariz, @harripj, I would be very happy if you could check out the branch in #442 and try reduce() on some Orientation and Misorientation issues you might have.

@argerlt
Copy link
Contributor

argerlt commented Apr 18, 2025

Hey, I'm super late to this party, but I believe ORIX was behaving correctly from the start.

@maclariz , your orientations are vaguely close together, but they still fall into different symmetry reduction zones. As such, orix correctly gave you the symmetry-reduced representations, they just look incorrect because they map to opposite sides of the m3m FZ. Code for proof:

data =  np.array([[ 0.5101, -0.0107,  0.0421, -0.859 ],
                  [ 0.5114, -0.0107,  0.0421, -0.8582],
                  [ 0.2451, -0.0222,  0.0373, -0.9685],
                  [ 0.2455, -0.016,   0.0368, -0.9686],
                  [ 0.2442, -0.0223,  0.0373, -0.9688]])

r = Rotation(data)
all_symm_equiv_r = r.outer(Oh)
symm_element_to_get_to_FZ = np.argmin(all_symm_equiv_r.angle,axis=1)
print(symm_element_to_get_to_FZ)

[1 1 2 2 2]

Which shows the first two elements use symmetry element 1 (a 4-fold over the 001) while the last 3 use symmetry element 2 ( a 2-fold over the 001). Importantly, these look EXACTLY alike on an IPF plot.

This is also why @harripj saw a 2% difference between o1 and o2 as well, as they are physically different orientations. they represent twists of slightly different angular intensity around the same axis.

@hakonanes, I think #442 is irrelevant to this specific case, but it fixes the extension of this problem to Misorientation space. (ie, enforcing agreement with the Krakow paper for consistent handling of misorientation FZs: https://royalsocietypublishing.org/doi/10.1098/rspa.2017.0274 )

As another proof, you can calculate the distances between every element. the original rotations give:

r.dot_outer(r)
Out[54]: 
array([[1.        , 0.99999884, 0.95881309, 0.95896   , 0.95854314],
       [0.99999884, 1.        , 0.95837885, 0.95852623, 0.95810752],
       [0.95881309, 0.95837885, 1.        , 0.99998057, 0.99999955],
       [0.95896   , 0.95852623, 0.99998057, 1.        , 0.99997917],
       [0.95854314, 0.95810752, 0.99999955, 0.99997917, 1.        ]])

and Orix's symmetry-reduced orientatoins give:

o.dot_outer(o)
Out[55]: 
array([[1.        , 0.99999884, 0.95881309, 0.95896   , 0.95854314],
       [0.99999884, 1.        , 0.95837885, 0.95852623, 0.95810752],
       [0.95881309, 0.95837885, 1.        , 0.99998057, 0.99999955],
       [0.95896   , 0.95852623, 0.99998057, 1.        , 0.99997917],
       [0.95854314, 0.95810752, 0.99999955, 0.99997917, 1.        ]])

which is the same, but the alternate rotations given in the first comment as "equivalent" give different angular distances, which cannot be arrived at by any combination of symmetry operations.

r_changed.dot_outer(r_changed)
Out[57]: 
array([[1.        , 0.99999888, 0.99999873, 0.99998016, 0.99999664],
       [0.99999888, 1.        , 1.        , 0.99998057, 0.99999941],
       [0.99999873, 1.        , 1.        , 0.99998052, 0.9999995 ],
       [0.99998016, 0.99998057, 0.99998052, 1.        , 0.99997945],
       [0.99999664, 0.99999941, 0.9999995 , 0.99997945, 1.        ]])

This is a bit of an assumption, but if I had to guess, this is linked to the practice in plotting certain vector maps (XRD, IPF maps, and TEM diffraction patterns) where reordering the scalar part of a quaternion is a quick and easy way to reduce it to the fundamental zone of the IPF plot. However, the sign swap done in the first comment does not represent an identical rotation, only a rotation that has identical magnitude and maps to the same xy position in a stereographic projection. The consequences of this are really easy to miss as well since the difference rarely matters (until it does), they have the same disorientation angle, they map the same way in nearly all non-3D plots, and they are equivalent for most diffraction-based calculations. Thus, most checks based on vector analysis would still treat them as equivalent. The plot below is a map of the FZ, with blue dots representing the correct values for the orientations, and the orange spots are the improper remapping.

Image

@maclariz , My (possibly incorrect) assumption, given how close the orange dots are to the blue, is before you got to orix, some previous code flipped the signs and/or ordering on your rotations for convenience, or only gave one of the 3 possible rotations that could have mapped a 100 Miller index to an observed spot in a diffraction experiment. If the second, this could be described by a different and/or incorrect handling of symmetry operations in the prior software.

If anyone disagrees, feel free to refute this, but my understanding is ORIX did what it was supposed to in this case.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants