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
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
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]
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
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
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 _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
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]
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
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))