Skip to content

OrientationRegion does not calculate the proper fundamental zone for most improper misorientations #671

Description

@argerlt

OrientationRegion.from_symmetry does not correctly calculate the fundamental zone for several of the possible misorientation symmetries. Looking at the 32*32 = 1024 possible combinations of point groups, I believe OrientationRegion.from_symmetry returns:

  • The correct value for 704 cases.
  • A NotImplemented Error for the 100 cases where both start and end symmetries contain a roto-inversion but no inversion.
  • A fundamental zone twice as large as the correct value for all 220 cases where either the start or end symmetry contain a rotoinversion, but neither an inversion.

Looking deeper into this, i believe this is occurring because roto-inversions are being incorrectly ignored when they are the highest order symmetry. The example code below shows this effect:

import matplotlib.pyplot as plt
import numpy as np

import orix.quaternion as oqu

import orix.quaternion.symmetry as osm

# These are arranged so Laue groups are first .

groups = [
    # laue first
    osm.Ci,
    osm.C2h,
    osm.D2h,
    osm.C4h,
    osm.D4h,
    osm.S6,
    osm.D3d,
    osm.C6h,
    osm.D6h,
    osm.Th,
    osm.Oh,
   # Non-Laue with inversion centers
    osm.C1,
    osm.C2,
    osm.D2,
    osm.C4,
    osm.D4,
    osm.C3,
    osm.D3,
    osm.C6,
    osm.D6,
    osm.T,
    osm.O,
   # Non-Laue, no inversion but rotoinversion
   osm.Cs,
   osm.C2v,
   osm.S4,
   osm.C4v,
   osm.D2d,
   osm.C3v,
   osm.C3h,
   osm.C6v,
   osm.D3h,
   osm.Td,
    ]
# %%
n = len(groups)
representations_in_FZ = np.zeros([n,n],dtype=float)
r = oqu.Rotation.random(1)
for i, s1 in enumerate(groups):
    print(str(i)+ " of "+str(n))
    for j, s2 in enumerate(groups):
        try:
            fz = oqu.OrientationRegion.from_symmetry(s1,s2)
        except:
            continue
        q = s2.outer(r).outer(s1)
        mask = q<fz
        q_in_fz = oqu.Rotation(q[mask])
        q_in_fz.data[q_in_fz.a<0]=-q_in_fz.data[q_in_fz.a<0]
        total = np.unique(np.around(q_in_fz.data,5),axis=0).shape[0]
        representations_in_FZ [i,j] = total

# %%

fig,ax = plt.subplots()
ax.imshow(representations_in_FZ)
ax.grid('on',color='k')
ax.set_yticks(np.arange(n))
ax.set_xticks(np.arange(n))
ax.xaxis.set_ticklabels([x.name for x in groups],rotation=90)
ax.yaxis.set_ticklabels([x.name for x in groups])

Green means one and only one representation is inside the fundamental zone, yellow means two representations are, and purple means the fundamental zone could not be calculated.

Image

I believe this is occurring due to some incorrectly implemented logic in OrientationRegion.from_symmetry. Replacing this function with the following code seems to remedy the issue.

def get_proper_groups(Gl: Symmetry, Gr: Symmetry) -> tuple[Symmetry, Symmetry]:
    """Return the appropriate groups for the asymmetric domain
    calculation.

    Parameters
    ----------
    Gl
        First point group.
    Gr
        Second point group.

    Returns
    -------
    Gl
        First proper subgroup(s) or proper inversion subgroup(s), as
        appropriate.
    Gr
        Second proper subgroup(s) or proper inversion subgroup(s), as
        appropriate.

    """
    if Gl.is_proper:
        if Gr.is_proper:
            return Gl, Gr
        elif Gr.contains_inversion:
            return Gl, Gr.proper_subgroup
        else:
            return Gl, Gr.laue_proper_subgroup
    if Gr.is_proper:
        if Gl.contains_inversion:
            return Gl.proper_subgroup, Gr
        else:
            return Gl.laue_proper_subgroup, Gr
    if Gl.contains_inversion:
        if Gr.contains_inversion:
            return Gl.proper_subgroup, Gr.proper_subgroup
        else:
            return Gl.proper_subgroup, Gr.laue_proper_subgroup

I'm not sure it matters, but neither the old method nor my new version above exactly match the method described in Krakow 2017 et al, which is more or less a voronoi decomposition in quaternion space. It does seem to give the same answer though for the 32 crystallographic point groups.

If no one sees an error in this implmentation, I can add it to #669, as it is relevant to the definition of a Misorientation's mean.

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions