diff --git a/orix/quaternion/orientation.py b/orix/quaternion/orientation.py index a11d7be8..ddbd3779 100644 --- a/orix/quaternion/orientation.py +++ b/orix/quaternion/orientation.py @@ -30,6 +30,7 @@ import numpy as np from tqdm import tqdm +from orix.quaternion import Quaternion from orix.quaternion.orientation_region import OrientationRegion from orix.quaternion.rotation import Rotation from orix.quaternion.symmetry import C1, Symmetry, _get_unique_symmetry_elements @@ -792,6 +793,39 @@ def dot_outer(self, other: Orientation) -> np.ndarray: ) return highest_dot_products.transpose(*order) + def mean(self, mean_in_euler_fundamental_region=False) -> Orientation: + """Return the mean orientation with unitary weights. + + Returns + ------- + ori_mean + Mean orientation. + + mean_in_euler_fundamental_region + Calculate mean orientation in euler fundamental region. + + Notes + ----- + The method used here corresponds to Equation (13) in + https://arc.aiaa.org/doi/pdf/10.2514/1.28949. + + Examples + -------- + >>> from orix.quaternion import Orientation, symmetry + >>> oris = Orientation.from_axes_angles([1, 0, 0], np.deg2rad([0, 45]), symmetry.Oh) + >>> oris.mean(mean_in_euler_fundamental_region=True) + Orientation (1,) m-3m + [[0.2469 0. 0.3708 0.8953]] + """ + if mean_in_euler_fundamental_region: + quat = Quaternion.from_euler(self.in_euler_fundamental_region()) + else: + quat = Quaternion(self) + + mean = quat.mean() + + return Orientation(mean, symmetry=self.symmetry) + def plot_unit_cell( self, c: str = "tab:blue", diff --git a/orix/tests/quaternion/test_orientation.py b/orix/tests/quaternion/test_orientation.py index c0a5fac4..d498ef17 100644 --- a/orix/tests/quaternion/test_orientation.py +++ b/orix/tests/quaternion/test_orientation.py @@ -698,3 +698,38 @@ def test_in_fundamental_region(self): ori.symmetry = pg region = np.radians(pg.euler_fundamental_region) assert np.all(np.max(ori.in_euler_fundamental_region(), axis=0) <= region) + + def test_mean(self): + oris = Orientation( + [ + [0.215, 0.9696, -0.1001, -0.0602], + [0.2132, 0.97, -0.0977, -0.0643], + [0.2154, 0.9693, -0.098, -0.067], + [0.2157, 0.9692, -0.0989, -0.0659], + [0.2141, 0.9693, -0.1012, -0.0668], + [0.2132, 0.9696, -0.1001, -0.0659], + [0.2083, 0.9715, -0.0952, -0.0605], + [0.1558, 0.7913, -0.569, -0.1608], + [0.1495, 0.79, -0.5704, -0.1681], + [0.1537, 0.7908, -0.5686, -0.1663], + [0.1552, 0.7918, -0.5677, -0.1634], + [0.1518, 0.792, -0.5679, -0.165], + [0.0484, 0.3993, -0.8886, -0.2203], + [0.0459, 0.4007, -0.888, -0.2209], + ], + symmetry=D6, + ) + mean_not_fund_region = oris.mean() + mean_fund_region = oris.mean(mean_in_euler_fundamental_region=True) + + assert np.allclose( + mean_not_fund_region.data, + Orientation([0.1835, 0.8938, -0.3888, -0.1277], symmetry=D6).data, + atol=0.0001, + ) + + assert np.allclose( + mean_fund_region.data, + Orientation([0.8887, -0.2184, 0.0502, -0.4], symmetry=D6).data, + atol=0.0001, + )