Example #1
0
    def get_plot_data(self):
        """
        Produces suitable Rotations for the construction of a wireframe for self
        """
        from orix.vector import Vector3d

        # gets a grid of vector directions
        theta = np.linspace(0, 2 * np.pi - EPSILON, 361)
        rho = np.linspace(0, np.pi - EPSILON, 181)
        theta, rho = np.meshgrid(theta, rho)
        g = Vector3d.from_polar(rho, theta)

        # get the cell vector normal norms
        n = Rodrigues.from_rotation(self).norm.data[:, np.newaxis, np.newaxis]
        if n.size == 0:
            return Rotation.from_neo_euler(AxAngle.from_axes_angles(g, np.pi))

        d = (-self.axis).dot_outer(g.unit).data
        x = n * d
        omega = 2 * np.arctan(np.where(x != 0, x**-1, np.pi))

        # keeps the smallest allowed angle
        omega[omega < 0] = np.pi
        omega = np.min(omega, axis=0)
        r = Rotation.from_neo_euler(AxAngle.from_axes_angles(g.unit, omega))
        return r
Example #2
0
    def fundamental_sector(self):
        from orix.vector.neo_euler import AxAngle
        from orix.vector.spherical_region import SphericalRegion
        symmetry = self.antipodal
        symmetry = symmetry[symmetry.angle > 0]
        axes, order = symmetry.get_highest_order_axis()
        if order > 6:
            return Vector3d.empty()
        axis = Vector3d.zvector().get_nearest(axes, inclusive=True)
        r = Rotation.from_neo_euler(
            AxAngle.from_axes_angles(axis, 2 * np.pi / order))

        diads = symmetry.diads
        nearest_diad = axis.get_nearest(diads)
        if nearest_diad.size == 0:
            nearest_diad = axis.perpendicular

        n1 = axis.cross(nearest_diad).unit
        n2 = -(r * n1)
        next_diad = r * nearest_diad
        n = Vector3d.stack((n1, n2)).flatten()
        sr = SphericalRegion(n.unique())
        inside = symmetry[symmetry.axis < sr]
        if inside.size == 0:
            return sr
        axes, order = inside.get_highest_order_axis()
        axis = axis.get_nearest(axes)
        r = Rotation.from_neo_euler(
            AxAngle.from_axes_angles(axis, 2 * np.pi / order))
        nearest_diad = next_diad
        n1 = axis.cross(nearest_diad).unit
        n2 = -(r * n1)
        n = Vector3d(np.concatenate((n.data, n1.data, n2.data)))
        sr = SphericalRegion(n.unique())
        return sr
Example #3
0
def detector2sample(
    sample_tilt: float,
    detector_tilt: float,
    convention: Optional[str] = None,
) -> Rotation:
    """Rotation U_S to align detector frame D with sample frame S.

    Parameters
    ----------
    sample_tilt
        Sample tilt in degrees.
    detector_tilt
        Detector tilt in degrees.
    convention
        Which sample reference frame to use, either the one used by EDAX
        TSL (default), "tsl", or the one used by Bruker, "bruker".

    Returns
    -------
    Rotation
    """
    # Rotation about sample (microscope) X axis
    tilt = -np.deg2rad((sample_tilt - 90) - detector_tilt)
    ax_angle = neo_euler.AxAngle.from_axes_angles(Vector3d.xvector(), tilt)
    r = Rotation.from_neo_euler(ax_angle)

    if convention != "bruker":
        # Followed by a 90 degree rotation about the sample Z axis,
        # if the TSL sample reference frame is used
        ax_angle_bruker2tsl = neo_euler.AxAngle.from_axes_angles(
            Vector3d.zvector(), np.pi / 2)
        r = Rotation.from_neo_euler(ax_angle_bruker2tsl) * r

    return r.to_matrix()[0]
Example #4
0
    def from_symmetry(cls, s1, s2=C1):
        """The set of unique (mis)orientations of a symmetrical object.

        Parameters
        ----------
        s1, s2 : Symmetry

        """
        s1, s2 = get_proper_groups(s1, s2)
        large_cell_normals = _get_large_cell_normals(s1, s2)
        disjoint = s1 & s2
        # if s1._tuples == s2._tuples:
        #     disjoint = disjoint.laue
        fundamental_sector = disjoint.fundamental_sector()
        fundamental_sector_normals = Rotation.from_neo_euler(
            AxAngle.from_axes_angles(fundamental_sector, np.pi)
        )
        normals = Rotation(
            np.concatenate([large_cell_normals.data, fundamental_sector_normals.data])
        )
        orientation_region = cls(normals)
        vertices = orientation_region.vertices()
        if vertices.size:
            orientation_region = orientation_region[
                np.any(
                    np.isclose(orientation_region.dot_outer(vertices).data, 0), axis=1
                )
            ]
        return orientation_region
Example #5
0
def test_antipodal(rotation, improper):
    rotation = Rotation(rotation)
    rotation.improper = improper
    a = rotation.antipodal
    assert np.allclose(a[0].data, rotation.data)
    assert np.allclose(a[1].data, -rotation.data)
    assert np.allclose(a[0].improper, rotation.improper)
    assert np.allclose(a[1].improper, rotation.improper)
Example #6
0
 def test_to_matrix(self):
     r = Rotation([[1, 0, 0, 0], [3, 0, 0, 0], [0, 1, 0, 0],
                   [0, 2, 0, 0]]).reshape(2, 2)
     om = np.array(
         [np.eye(3),
          np.eye(3),
          np.diag([1, -1, -1]),
          np.diag([1, -1, -1])])
     assert np.allclose(r.to_matrix(), om.reshape((2, 2, 3, 3)))
Example #7
0
 def test_from_matrix(self):
     r = Rotation([[1, 0, 0, 0], [3, 0, 0, 0], [0, 1, 0, 0], [0, 2, 0, 0]])
     om = np.array(
         [np.eye(3), np.eye(3), np.diag([1, -1, -1]), np.diag([1, -1, -1])]
     )
     assert np.allclose(Rotation.from_matrix(om).data, r.data)
     assert np.allclose(
         Rotation.from_matrix(om.reshape((2, 2, 3, 3))).data, r.reshape(2, 2).data
     )
def get_local_grid(resolution=2, center=None, grid_width=10):
    """
    Generates a grid of rotations about a given rotation

    Parameters
    ----------
    resolution : float, optional
        The characteristic distance between a rotation and its neighbour (degrees)
    center : euler angle tuple or orix.quaternion.rotation.Rotation, optional
        The rotation at which the grid is centered. If None (default) uses the identity
    grid_width : float, optional
        The largest angle of rotation away from center that is acceptable (degrees)

    Returns
    -------
    rotation_list : list of tuples
    """
    if isinstance(center, tuple):
        z = np.deg2rad(np.asarray(center))
        center = Rotation.from_euler(z,
                                     convention="bunge",
                                     direction="crystal2lab")

    orix_grid = get_sample_local(resolution=resolution,
                                 center=center,
                                 grid_width=grid_width)
    rotation_list = get_list_from_orix(orix_grid, rounding=2)
    return rotation_list
Example #9
0
 def get_plot_data(self):
     from orix.vector import Vector3d
     theta = np.linspace(0, 2 * np.pi + 1e-9, 361)
     rho = np.linspace(0, np.pi, 181)
     theta, rho = np.meshgrid(theta, rho)
     g = Vector3d.from_polar(rho, theta)
     n = Rodrigues.from_rotation(self).norm.data[:, np.newaxis, np.newaxis]
     if n.size == 0:
         return Rotation.from_neo_euler(AxAngle.from_axes_angles(g, np.pi))
     d = (-self.axis).dot_outer(g.unit).data
     x = n * d
     x = 2 * np.arctan(x**-1)
     x[x < 0] = np.pi
     x = np.min(x, axis=0)
     r = Rotation.from_neo_euler(AxAngle.from_axes_angles(g.unit, x))
     return r
Example #10
0
    def rotate(self, axis=None, angle=0):
        """Convenience function for rotating this vector.

        Shapes of 'axis' and 'angle' must be compatible with shape of this
        vector for broadcasting.

        Parameters
        ----------
        axis : Vector3d or array_like, optional
            The axis of rotation. Defaults to the z-vector.
        angle : array_like, optional
            The angle of rotation, in radians.

        Returns
        -------
        Vector3d
            A new vector with entries rotated.

        Examples
        --------
        >>> from math import pi
        >>> v = Vector3d((0, 1, 0))
        >>> axis = Vector3d((0, 0, 1))
        >>> angles = [0, pi/4, pi/2, 3*pi/4, pi]
        >>> v.rotate(axis=axis, angle=angles)


        """
        from orix.quaternion.rotation import Rotation
        from orix.vector.neo_euler import AxAngle
        axis = Vector3d.zvector() if axis is None else axis
        angle = 0 if angle is None else angle
        q = Rotation.from_neo_euler(AxAngle.from_axes_angles(axis, angle))
        return q * self
def detector2reciprocal_lattice(
    sample_tilt: float,
    detector_tilt: float,
    lattice: Lattice,
    rotation: Rotation,
) -> np.ndarray:
    """Rotation U_Kstar from detector to reciprocal crystal lattice
    frame Kstar.

    Parameters
    ----------
    sample_tilt
        Sample tilt in degrees.
    detector_tilt
        Detector tilt in degrees.
    lattice
        Crystal lattice.
    rotation
        Unit cell rotation from the sample frame S.

    Returns
    -------
    np.ndarray
    """
    # Rotation U_S to align the detector frame D with the sample frame S
    _detector2sample = detector2sample(sample_tilt, detector_tilt)

    # Rotation U_O from S to the Cartesian crystal frame C
    sample2cartesian = rotation.to_matrix()

    # Rotation U_A from C to the reciprocal crystal lattice frame Kstar
    structure_matrix = get_direct_structure_matrix(lattice)
    cartesian2reciprocal = np.linalg.inv(structure_matrix).T

    return cartesian2reciprocal.dot(sample2cartesian).dot(_detector2sample)
Example #12
0
def uniform_SO3_sample(resolution):
    """
    Returns rotations that are evenly spaced according to the Haar measure on
    SO3

    Parameters
    ----------
    resolution : float
        The characteristic distance between a rotation and its neighbour (degrees)

    Returns
    -------
    q : orix.quaternion.rotation.Rotation
        grid containing appropriate rotations
    """
    num_steps = int(np.ceil(360 / resolution))
    if num_steps % 2 == 1:
        num_steps = int(num_steps + 1)
    half_steps = int(num_steps / 2)

    alpha = np.linspace(0, 2 * np.pi, num=num_steps, endpoint=False)
    beta = np.arccos(np.linspace(1, -1, num=half_steps, endpoint=False))
    gamma = np.linspace(0, 2 * np.pi, num=num_steps, endpoint=False)
    q = np.asarray(list(product(alpha, beta, gamma)))

    # convert to quaternions
    q = Rotation.from_euler(q, convention="bunge", direction="crystal2lab")
    # remove duplicates
    q = q.unique()
    return q
Example #13
0
 def test_from_to_matrix(self):
     om = np.array(
         [np.eye(3),
          np.eye(3),
          np.diag([1, -1, -1]),
          np.diag([1, -1, -1])])
     assert np.allclose(Rotation.from_matrix(om).to_matrix(), om)
Example #14
0
 def faces(self):
     normals = Rotation(self)
     vertices = self.vertices()
     faces = []
     for n in normals:
         faces.append(vertices[np.isclose(vertices.dot(n).data, 0)])
     faces = [f for f in faces if f.size > 2]
     return faces
def get_grid_around_beam_direction(beam_rotation,
                                   resolution,
                                   angular_range=(0, 360)):
    """
    Creates a rotation list of rotations for which the rotation is about given beam direction

    Parameters
    ----------
    beam_rotation : tuple
        A desired beam direction as a rotation (rzxz eulers), usually found via get_rotation_from_z_to_direction

    resolution : float
        The resolution of the grid (degrees)

    angular_range : tuple
        The minimum (included) and maximum (excluded) rotation around the beam direction to be included

    Returns
    -------
    rotation_list : list of tuples

    Example
    -------
    >>> from diffsims.generators.zap_map_generator import get_rotation_from_z_to_direction
    >>> beam_rotation = get_rotation_from_z_to_direction(structure,[1,1,1])
    >>> grid = get_grid_around_beam_direction(beam_rotation,1)
    """
    z = np.deg2rad(np.asarray(beam_rotation))
    beam_rotation = Rotation.from_euler(z,
                                        convention="bunge",
                                        direction="crystal2lab")

    angles = np.deg2rad(
        np.arange(start=angular_range[0],
                  stop=angular_range[1],
                  step=resolution))
    axes = np.repeat([[0, 0, 1]], angles.shape[0], axis=0)
    in_plane_rotation = Rotation.from_neo_euler(
        AxAngle.from_axes_angles(axes, angles))

    orix_grid = beam_rotation * in_plane_rotation
    rotation_list = get_list_from_orix(orix_grid, rounding=2)
    return rotation_list
Example #16
0
    def vertices(self):
        """The vertices of the asymmetric domain.

        Returns
        -------
        Rotation

        """
        normal_combinations = list(itertools.combinations(self, 3))
        if len(normal_combinations) < 1:
            return Rotation.empty()
        c1, c2, c3 = zip(*normal_combinations)
        c1, c2, c3 = Rotation.stack(c1).flatten(), Rotation.stack(
            c2).flatten(), Rotation.stack(c3).flatten()
        v = Rotation.triple_cross(c1, c2, c3)
        v = v[~np.any(np.isnan(v.data), axis=-1)]
        v = v[v < self].unique()
        surface = np.any(np.isclose(v.dot_outer(self).data, 0), axis=1)
        return v[surface]
Example #17
0
    def test_orientations_symmetry(self, point_group, rotation, expected_orientation):
        r = Rotation(rotation)
        cm = CrystalMap(rotations=r, phase_id=np.array([0]))
        cm.phases = PhaseList(Phase("a", point_group=point_group))

        o = cm.orientations

        assert np.allclose(
            o.data, Orientation(r).set_symmetry(point_group).data, atol=1e-3
        )
        assert np.allclose(o.data, expected_orientation, atol=1e-3)
Example #18
0
def test_get_sample_local_width(big, small):
    """ Checks that width follows the expected trend (X - Sin(X)) """
    resolution = np.pi

    z = get_sample_local(resolution=resolution, grid_width=small)

    assert np.all(z.angle_with(Rotation([1, 0, 0, 0])) < np.deg2rad(small))
    assert np.any(
        z.angle_with(Rotation([1, 0, 0, 0])) > np.deg2rad(small - 1.5 * resolution)
    )

    x_size = z.size
    assert x_size > 0
    y_size = get_sample_local(resolution=np.pi, grid_width=big).size
    x_v = np.deg2rad(small) - np.sin(np.deg2rad(small))
    y_v = np.deg2rad(big) - np.sin(np.deg2rad(big))
    exp = y_size / x_size
    theory = y_v / x_v

    # resolution/width is high, so we must be generous on tolerance
    assert np.isclose(exp, theory, rtol=0.2)
Example #19
0
def _get_large_cell_normals(s1, s2):
    dp = get_distinguished_points(s1, s2)
    normals = Rodrigues.zero(dp.shape + (2, ))
    planes1 = dp.axis * np.tan(dp.angle.data / 4)
    planes2 = -dp.axis * np.tan(dp.angle.data / 4)**-1
    planes2.data[np.isnan(planes2.data)] = 0
    normals[:, 0] = planes1
    normals[:, 1] = planes2
    normals: Rotation = Rotation.from_neo_euler(normals).flatten().unique(
        antipodal=False)
    if not normals.size:
        return normals
    _, inv = normals.axis.unique(return_inverse=True)
    axes_unique = []
    angles_unique = []
    for i in np.unique(inv):
        n = normals[inv == i]
        axes_unique.append(n.axis.data[0])
        angles_unique.append(n.angle.data.max())
    normals = Rotation.from_neo_euler(
        AxAngle.from_axes_angles(np.array(axes_unique), angles_unique))
    return normals
Example #20
0
def test_mul_rotation(r1, i1, r2, i2, expected, expected_i):
    r1 = Rotation(r1)
    r1.improper = i1
    r2 = Rotation(r2)
    r2.improper = i2
    r = r1 * r2
    assert isinstance(r, Rotation)
    assert np.allclose(r.data, expected)
    assert np.all(r.improper == expected_i)
def detector2sample(sample_tilt: float, detector_tilt: float) -> Rotation:
    """Rotation U_S to align detector frame D with sample frame S.

    Parameters
    ----------
    sample_tilt
        Sample tilt in degrees.
    detector_tilt
        Detector tilt in degrees.

    Returns
    -------
    Rotation
    """
    x_axis = Vector3d.xvector()
    tilt = -np.deg2rad((sample_tilt - 90) - detector_tilt)
    ax_angle = neo_euler.AxAngle.from_axes_angles(x_axis, tilt)
    return Rotation.from_neo_euler(ax_angle).to_matrix()[0]
Example #22
0
def dict2crystalmap(dictionary):
    """Get a crystal map from necessary items in a dictionary.

    Parameters
    ----------
    dictionary : dict
        Dictionary with crystal map information.

    Returns
    -------
    CrystalMap
    """
    dictionary = copy.deepcopy(dictionary)

    data = dictionary["data"]
    header = dictionary["header"]

    # New dictionary with CrystalMap initialization arguments as keys
    crystal_map_dict = {
        # Use dstack and squeeze to allow more rotations per data point
        "rotations":
        Rotation.from_euler(
            np.dstack((data.pop("phi1"), data.pop("Phi"),
                       data.pop("phi2"))).squeeze(), ),
        "scan_unit":
        header["scan_unit"],
        "phase_list":
        dict2phaselist(header["phases"]),
        "phase_id":
        data.pop("phase_id"),
        "is_in_data":
        data.pop("is_in_data"),
    }
    # Add standard items by updating the new dictionary
    for direction in ["z", "y", "x"]:
        this_direction = data.pop(direction)
        if hasattr(this_direction, "__iter__") is False:
            this_direction = None
        crystal_map_dict[direction] = this_direction
    _ = [data.pop(i) for i in ["id"]]
    # What's left should be properties like quality metrics etc.
    crystal_map_dict.update({"prop": data})

    return CrystalMap(**crystal_map_dict)
Example #23
0
def loadang(file_string: str):
    """Load ``.ang`` files.

    Parameters
    ----------
    file_string : str
        Path to the ``.ang`` file. This file is assumed to list the Euler
        angles in the Bunge convention in the first three columns.

    Returns
    -------
    Rotation

    """
    from orix.quaternion.rotation import Rotation
    data = np.loadtxt(file_string)
    euler = data[:, :3]
    rotation = Rotation.from_euler(euler)
    return rotation
Example #24
0
def loadctf(file_string: str):
    """Load ``.ang`` files.

    Parameters
    ----------
    file_string : str
        Path to the ``.ctf`` file. This file is assumed to list the Euler
        angles in the Bunge convention in the columns 5, 6, and 7.

    Returns
    -------
    Rotation

    """

    from orix.quaternion.rotation import Rotation
    data = np.loadtxt(file_string, skiprows=17)[:, 5:8]
    euler = np.radians(data)
    rotation = Rotation.from_euler(euler)
    return rotation
Example #25
0
def nickel_rotations():
    """A set of 25 rotations in a TSL crystal reference frame (RD-TD-ND).

    The rotations are from an EMsoft indexing of patterns in the region
    of interest (row0:row1, col0:col1) = (79:84, 134:139) of the first
    Nickel data set in this set of scans:
    https://zenodo.org/record/3265037.
    """
    return Rotation(
        np.array(
            [
                [0.8662, 0.2033, -0.3483, -0.2951],
                [0.8888, 0.3188, -0.2961, -0.1439],
                [0.8883, 0.3188, -0.2973, -0.1444],
                [0.8884, 0.3187, -0.2975, -0.1437],
                [0.9525, 0.1163, -0.218, -0.1782],
                [0.8658, 0.2031, -0.3486, -0.296],
                [0.8661, 0.203, -0.3486, -0.2954],
                [0.8888, 0.3179, -0.297, -0.1439],
                [0.9728, -0.1634, 0.0677, 0.1494],
                [0.9526, 0.1143, -0.2165, -0.1804],
                [0.8659, 0.2033, -0.3483, -0.2958],
                [0.8663, 0.2029, -0.348, -0.2955],
                [0.8675, 0.1979, -0.3455, -0.298],
                [0.9728, -0.1633, 0.0685, 0.1494],
                [0.9726, -0.1634, 0.0684, 0.1506],
                [0.8657, 0.2031, -0.3481, -0.297],
                [0.8666, 0.2033, -0.3475, -0.2949],
                [0.9111, 0.3315, -0.1267, -0.2095],
                [0.9727, -0.1635, 0.0681, 0.1497],
                [0.9727, -0.1641, 0.0682, 0.1495],
                [0.8657, 0.2024, -0.3471, -0.2986],
                [0.9109, 0.3318, -0.1257, -0.2105],
                [0.9113, 0.3305, -0.1257, -0.2112],
                [0.9725, -0.1643, 0.0691, 0.1497],
                [0.9727, -0.1633, 0.0685, 0.1499],
            ]
        )
    )
Example #26
0
    def test_to_matrix(self):
        r = Rotation([[1, 0, 0, 0], [3, 0, 0, 0], [0, 1, 0, 0], [0, 2, 0, 0]])
        om = np.array(
            [np.eye(3), np.eye(3), np.diag([1, -1, -1]), np.diag([1, -1, -1])]
        )
        # Shapes are handled correctly
        assert np.allclose(r.reshape(2, 2).to_matrix(), om.reshape(2, 2, 3, 3))

        r2 = Rotation(
            [
                [0.1, 0.2, 0.3, 0.4],
                [0.5, 0.6, 0.7, 0.8],
                [0.9, 0.91, 0.92, 0.93],
                [1, 2, 3, 4],
            ]
        )
        om_from_r2 = r2.to_matrix()
        # Inverse equal to transpose
        assert all(np.allclose(np.linalg.inv(i), i.T) for i in om_from_r2)
        # Cross product of any two rows gives the third
        assert all(np.allclose(np.cross(i[:, 0], i[:, 1]), i[:, 2]) for i in om_from_r2)
        # Sum of squares of any column or row equals unity
        assert np.allclose(np.sum(np.square(om_from_r2), axis=1), 1)  # Rows
        assert np.allclose(np.sum(np.square(om_from_r2), axis=2), 1)  # Columns
Example #27
0
 def _tuples(self):
     """set of tuple : the differentiators of this group."""
     s = Rotation(self.flatten())
     tuples = set([tuple(d) for d in s._differentiators()])
     return tuples
Example #28
0
def rotations():
    return Rotation([(2, 4, 6, 8), (-1, -3, -5, -7)])
Example #29
0
def r_tsl2bruker():
    """A rotation from the TSL to Bruker crystal reference frame."""
    return Rotation.from_neo_euler(
        neo_euler.AxAngle.from_axes_angles(Vector3d.zvector(), np.pi / 2))
Example #30
0
 def test_get_rotation_matrix_from_diffpy(self):
     """Checking that getting rotation matrices from diffpy.structure
     works without issue.
     """
     r = Rotation.from_matrix([i.R for i in sg225.symop_list])
     assert not np.isnan(r.data).any()