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
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
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)
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
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 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
def test_from_to_matrix2(self, e): r = Rotation.from_euler(e.reshape((5, 2, 3))) assert np.allclose(Rotation.from_matrix(r.to_matrix()).data, r.data)
def test_unsupported_conv_from(self, e): with pytest.raises(ValueError, match="The chosen convention is not one of "): _ = Rotation.from_euler(e, convention="unsupported")
def test_passing_degrees_warns(self): with pytest.warns(UserWarning, match="Angles are assumed to be in radians, "): r = Rotation.from_euler([90, 0, 0], convention="bunge") assert np.allclose(r.data, [0.5253, 0, 0, -0.8509], atol=1e-4)
def test_direction_kwarg_dumb(self, e): with pytest.raises(ValueError, match="The chosen direction is not one of "): _ = Rotation.from_euler(e, direction="dumb_direction")
def test_unsupported_conv_to(self, e): r = Rotation.from_euler(e) with pytest.raises(ValueError, match="The chosen convention is not supported,"): _ = r.to_euler(convention="unsupported")
def test_direction_kwarg(self, e): _ = Rotation.from_euler(e, direction="lab2crystal")
def test_Krakow_Hielscher(self, e): _ = Rotation.from_euler(e, convention="Krakow_Hielscher")
def test_direction_kwarg(e): r = Rotation.from_euler(e, direction="lab2crystal")
def test_direction_kwarg_dumb(e): r = Rotation.from_euler(e, direction="dumb_direction")
def test_unsupported_conv_to(e): r = Rotation.from_euler(e) r.to_euler(convention="unsupported")
def file_reader(filename): """Return a :class:`~orix.crystal_map.crystal_map.CrystalMap` object from a file in EDAX TLS's .ang format. The map in the input is assumed to be 2D. Many vendors produce an .ang file. Supported vendors are * EDAX TSL * NanoMegas ASTAR Index * EMsoft (from program `EMdpmerge`) * orix All points satisfying the following criteria are classified as not indexed: * EDAX TSL: confidence index == -1 Parameters ---------- filename : str Path and file name. Returns ------- CrystalMap """ # Get file header with open(filename) as f: header = _get_header(f) # Get phase names and crystal symmetries from header (potentially empty) phase_ids, phase_names, symmetries, lattice_constants = _get_phases_from_header( header) structures = [] for name, abcABG in zip(phase_names, lattice_constants): structures.append(Structure(title=name, lattice=Lattice(*abcABG))) # Read all file data file_data = np.loadtxt(filename) # Get vendor and column names n_rows, n_cols = file_data.shape vendor, column_names = _get_vendor_columns(header, n_cols) # Data needed to create a CrystalMap object data_dict = { "euler1": None, "euler2": None, "euler3": None, "x": None, "y": None, "phase_id": None, "prop": {}, } for column, name in enumerate(column_names): if name in data_dict.keys(): data_dict[name] = file_data[:, column] else: data_dict["prop"][name] = file_data[:, column] # Add phase list to dictionary data_dict["phase_list"] = PhaseList( names=phase_names, point_groups=symmetries, structures=structures, ids=phase_ids, ) # Set which data points are not indexed if vendor in ["orix", "tsl"]: data_dict["phase_id"][np.where(data_dict["prop"]["ci"] == -1)] = -1 # TODO: Add not-indexed convention for INDEX ASTAR # Set scan unit if vendor in ["tsl", "emsoft"]: scan_unit = "um" else: # NanoMegas scan_unit = "nm" data_dict["scan_unit"] = scan_unit # Create rotations data_dict["rotations"] = Rotation.from_euler( np.column_stack((data_dict.pop("euler1"), data_dict.pop("euler2"), data_dict.pop("euler3"))), ) return CrystalMap(**data_dict)
def test_unsupported_conv_from(e): r = Rotation.from_euler(e, convention="unsupported")
def test_to_from_euler(self, e): """Checks that going euler2quat2euler gives no change.""" r = Rotation.from_euler(e) e2 = r.to_euler() assert np.allclose(e, e2.data)
def file_reader(filename, refined=False, **kwargs): """Return a :class:`~orix.crystal_map.crystal_map.CrystalMap` object from a file in EMsoft's dictionary indexing dot product file format. Parameters ---------- filename : str Path and file name. refined : bool, optional Whether to return refined orientations (default is False). kwargs Keyword arguments passed to :func:`h5py.File`. Returns ------- CrystalMap """ mode = kwargs.pop("mode", "r") f = File(filename, mode=mode, **kwargs) # Get groups for convenience ebsd_group = f["Scan 1/EBSD"] data_group = ebsd_group["Data"] header_group = ebsd_group["Header"] phase_group = header_group["Phase/1"] # Get map shape and step sizes ny = header_group["nRows"][:][0] nx = header_group["nColumns"][:][0] step_y = header_group["Step Y"][:][0] map_size = ny * nx # Some of the data needed to create a CrystalMap object phase_name, point_group, structure = _get_phase(phase_group) data_dict = { # Get map coordinates ("Y Position" data set is not correct in EMsoft as of # 2020-04, see: # https://github.com/EMsoft-org/EMsoft/blob/7762e1961508fe3e71d4702620764ceb98a78b9e/Source/EMsoftHDFLib/EMh5ebsd.f90#L1093) "x": data_group["X Position"][:], # y = data_group["Y Position"][:] "y": np.sort(np.tile(np.arange(ny) * step_y, nx)), # Get phase IDs "phase_id": data_group["Phase"][:], # Get phase name, point group and structure (lattice) "phase_list": PhaseList( Phase(name=phase_name, point_group=point_group, structure=structure)), "scan_unit": "um", } # Get rotations if refined: euler = data_group["RefinedEulerAngles"][:] # Radians else: # Get n top matches for each pixel top_match_idx = data_group["TopMatchIndices"][:][:map_size] - 1 dictionary_size = data_group["FZcnt"][:][0] # Degrees dictionary_euler = data_group[ "DictionaryEulerAngles"][:][:dictionary_size] dictionary_euler = np.deg2rad(dictionary_euler) euler = dictionary_euler[top_match_idx, :] data_dict["rotations"] = Rotation.from_euler(euler) # Get number of top matches kept per data point n_top_matches = f["NMLparameters/EBSDIndexingNameListType/nnk"][:][0] data_dict["prop"] = _get_properties( data_group=data_group, n_top_matches=n_top_matches, map_size=map_size, ) f.close() return CrystalMap(**data_dict)