def test_init_empty_phaselist(self, empty_input): pl = PhaseList(empty_input) assert repr(pl) == "No phases." pl.add(Phase("al", point_group="m-3m")) assert repr(pl) == ( "Id Name Space group Point group Proper point group Color\n" " 0 al None m-3m 432 tab:blue")
def test_add_to_phaselist_raises(self): """Trying to add a Phase with a name already in the PhaseList raises a ValueError. """ pl = PhaseList(names=["a"]) with pytest.raises(ValueError, match="'a' is already in the phase list"): pl.add(Phase("a"))
def test_init_with_single_structure(self): structure = Structure() names = ["a", "b"] pl = PhaseList(names=names, structures=structure) assert pl.names == names assert pl.structures == [structure] * 2
def test_get_phaselist_size(self, n_names): phase_names_pool = "abcd" phase_names = [phase_names_pool[i] for i in range(n_names)] pl = PhaseList(names=phase_names) assert pl.size == n_names
def test_init_with_too_many_phases( self, crystal_map_input, phase_names, phase_ids, desired_phase_names ): """More phases than phase IDs.""" phase_list = PhaseList(names=phase_names, ids=phase_ids) xmap = CrystalMap(phase_list=phase_list, **crystal_map_input) assert xmap.phases.names == desired_phase_names
def test_init_phaselist_from_phase(self): p = Phase(name="austenite", point_group="432", color="C2") pl = PhaseList(p) assert pl.names == [p.name] assert pl.point_groups == [p.point_group] assert pl.space_groups == [p.space_group] assert pl.colors == [p.color] assert pl.colors_rgb == [p.color_rgb]
def test_add_phaselist_to_phaselist(self): """Add a PhaseList to a PhaseList, also ensuring that new IDs are given. """ names = ["a", "b"] sg_no = [10, 20] pl1 = PhaseList(names=names, space_groups=sg_no) assert pl1.ids == [0, 1] names2 = ["c", "d"] sg_no2 = [30, 40] ids = [4, 5] pl2 = PhaseList(names=names2, space_groups=sg_no2, ids=ids) pl1.add(pl2) assert pl1.names == names + names2 assert pl1.space_groups == ([GetSpaceGroup(i) for i in sg_no] + [GetSpaceGroup(i) for i in sg_no2]) assert pl1.ids == [0, 1, 2, 3]
def test_get_phaselist_ids(self, n_names, phase_ids, desired_names, desired_phase_ids): phase_names_pool = "abc" phase_names = [phase_names_pool[i] for i in range(n_names)] pl = PhaseList(names=phase_names, ids=phase_ids) assert pl.names == desired_names assert pl.ids == desired_phase_ids
def test_add_phase_in_empty_phaselist(self): """Add Phase to empty PhaseList.""" sg_no = 10 name = "a" pl = PhaseList() pl.add(Phase(name, space_group=sg_no)) assert pl.ids == [0] assert pl.names == [name] assert pl.space_groups == [GetSpaceGroup(sg_no)] assert pl.structures == [Structure()]
def test_init_set_to_nones(self): phase_ids = [1, 2] pl = PhaseList(ids=phase_ids) assert pl.ids == phase_ids assert pl.names == [""] * 2 assert pl.point_groups == [None] * 2 assert pl.space_groups == [None] * 2 assert pl.colors == ["tab:blue", "tab:orange"] assert pl.structures == [Structure()] * 2
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)
def phase_list(request): names, space_groups, point_group_names, colors, lattices, atoms = request.param # Apparently diffpy.structure don't allow iteration over a list of lattices structures = [Structure(lattice=lattices[i], atoms=a) for i, a in enumerate(atoms)] return PhaseList( names=names, space_groups=space_groups, point_groups=point_group_names, colors=colors, structures=structures, )
def test_make_not_indexed(self): phase_names = ["a", "b", "c"] phase_colors = ["r", "g", "b"] pl = PhaseList(names=phase_names, colors=phase_colors, ids=[-1, 0, 1]) assert pl.names == phase_names assert pl.colors == phase_colors pl.add_not_indexed() phase_names[0] = "not_indexed" phase_colors[0] = "w" assert pl.names == phase_names assert pl.colors == phase_colors
def phases_in_data(self): """List of phases in data. Needed because it can be useful to have phases not in data but in `self.phases`. """ unique_ids = np.unique(self.phase_id) phase_list = self.phases[np.intersect1d(unique_ids, self.phases.ids)] if isinstance(phase_list, Phase): # One phase in data # Get phase ID so it carries over to the new `PhaseList` object phase = phase_list # Since it's actually a single phase phase_id = self.phases.id_from_name(phase.name) return PhaseList(phases=phase, ids=phase_id) else: # Multiple phases in data return phase_list
def test_init_phaselist_from_phases(self, phase_collection): p1 = Phase(name="austenite", point_group=432, color=None) p2 = Phase(name="ferrite", point_group="432", color="C1") if phase_collection == "dict": phases = {1: p1, 2: p2} else: # phase_collection == "list": phases = [p1, p2] pl = PhaseList(phases) assert pl.names == [p.name for p in [p1, p2]] assert pl.point_groups == [p.point_group for p in [p1, p2]] assert pl.space_groups == [p.space_group for p in [p1, p2]] assert pl.colors == [p.color for p in [p1, p2]] assert pl.colors_rgb == [p.color_rgb for p in [p1, p2]]
def test_init_with_phase_list(self, crystal_map_input): point_groups = [C2, C3, C4] phase_list = PhaseList(point_groups=point_groups) cm = CrystalMap(phase_list=phase_list, **crystal_map_input) n_point_groups = len(point_groups) n_phase_ids = len(cm.phases.ids) n_different = n_point_groups - n_phase_ids if n_different < 0: point_groups += [None] * abs(n_different) assert [ cm.phases.point_groups[i] == point_groups[i] for i in range(n_phase_ids) ] unique_phase_ids = list(np.unique(crystal_map_input["phase_id"]).astype(int)) assert cm.phases.ids == unique_phase_ids
def test_add_list_phases_to_phaselist(self): """Add a list of Phase objects to PhaseList, also ensuring that unique colors are given. """ names = ["a", "b"] sg_no = [10, 20] colors = ["tab:blue", "tab:orange"] pl = PhaseList(names=names, space_groups=sg_no) assert pl.colors == colors new_names = ["c", "d"] new_sg_no = [30, 40] pl.add([Phase(name=n, space_group=i) for n, i in zip(new_names, new_sg_no)]) assert pl.names == names + new_names assert pl.space_groups == ( [GetSpaceGroup(i) for i in sg_no] + [GetSpaceGroup(i) for i in new_sg_no] ) assert pl.colors == colors + ["tab:green", "tab:red"]
def test_iterate_phaselist(self): names = ["al", "ni", "sigma"] point_groups = [3, 432, "m-3m"] colors = ["g", "b", "r"] structures = [ Structure(), Structure(lattice=Lattice(1, 2, 3, 90, 90, 90)), Structure(), ] pl = PhaseList( names=names, point_groups=point_groups, colors=colors, structures=structures ) for i, ((phase_id, phase), n, s, c, structure) in enumerate( zip(pl, names, point_groups, colors, structures) ): assert phase_id == i assert phase.name == n assert phase.point_group.name == str(s) assert phase.color == c assert phase.structure == structure
def test_init_phaselist_from_strings( self, names, space_groups, point_groups, colors, phase_ids, desired_names, desired_space_groups, desired_point_groups, desired_colors, desired_phase_ids, ): pl = PhaseList( names=names, space_groups=space_groups, point_groups=point_groups, colors=colors, ids=phase_ids, ) actual_point_group_names = [] actual_space_group_names = [] for _, p in pl: if p.point_group is None: actual_point_group_names.append(None) else: actual_point_group_names.append(p.point_group.name) if p.space_group is None: actual_space_group_names.append(None) else: actual_space_group_names.append(p.space_group.short_name) assert pl.names == desired_names assert actual_space_group_names == desired_space_groups assert actual_point_group_names == desired_point_groups assert pl.colors == desired_colors assert pl.ids == desired_phase_ids
def test_phase_legend(self, crystal_map, phase_names, phase_colors, legend_properties): cm = crystal_map cm[0, 0].phase_id = 1 cm.phases = PhaseList(names=phase_names, point_groups=[3, 3], colors=phase_colors) fig = plt.figure() ax = fig.add_subplot(projection=PLOT_MAP) _ = ax.plot_map(cm, legend_properties=legend_properties) legend = ax.legend_ assert legend._fontsize == 11.0 assert [i._text for i in legend.texts] == phase_names frame_alpha = legend_properties.pop("framealpha", 0.6) assert legend.get_frame().get_alpha() == frame_alpha for k, v in legend_properties.items(): assert legend.__getattribute__(k) == v plt.close("all")
def test_init_with_single_point_group(self, crystal_map_input): point_group = O phase_list = PhaseList(point_groups=point_group) cm = CrystalMap(phase_list=phase_list, **crystal_map_input) assert np.allclose(cm.phases.point_groups[0].data, point_group.data)
def test_get_item_not_indexed(self, phase_slice): ids = np.arange(-1, 9) # [-1, 0, 1, 2, ...] pl = PhaseList(ids=ids) # [-1, 0, 1, 2, ...] pl.add_not_indexed() # [-1, 0, 1, 2, ...] assert np.allclose(pl[phase_slice].ids, ids[phase_slice])
def __init__( self, rotations, phase_id=None, x=None, y=None, z=None, phase_list=None, prop=None, scan_unit=None, is_in_data=None, ): """ Parameters ---------- rotations : orix.quaternion.rotation.Rotation Rotation of each data point. Must be passed with all spatial dimensions in the first array axis (flattened). May contain multiple rotations per point, included in the second array axes. Crystal map data size is set equal to the first array axis' size. phase_id : numpy.ndarray, optional Phase ID of each pixel. IDs equal to -1 are considered not indexed. If None is passed (default), all points are considered to belong to one phase with ID 0. x : numpy.ndarray, optional Map x coordinate of each data point. If None is passed, the map is assumed to be 1D, and it is set to an array of increasing integers from 0 to the length of the `phase_id` array. y : numpy.ndarray, optional Map y coordinate of each data point. If None is passed, the map is assumed to be 1D, and it is set to None. z : numpy.ndarray, optional Map z coordinate of each data point. If None is passed, the map is assumed to be 2D or 1D, and it is set to None. phase_list : PhaseList, optional A list of phases in the data with their with names, space groups, point groups, and structures. The order in which the phases appear in the list is important, as it is this, and not the phases' IDs, that is used to link the phases to the input `phase_id` if the IDs aren't exactly the same as in `phase_id`. If None (default), a phase list with as many phases as there are unique phase IDs in `phase_id` is created. prop : dict of numpy.ndarray, optional Dictionary of properties of each data point. scan_unit : str, optional Length unit of the data. If None (default), "px" is used. is_in_data : np.ndarray, optional Array of booleans signifying whether a point is in the data. Examples -------- >>> from diffpy.structure import Atom, Lattice, Structure >>> import numpy as np >>> from orix.crystal_map import CrystalMap >>> from orix.quaternion.rotation import Rotation >>> euler1, euler2, euler3, x, y, iq, dp, phase_id = np.loadtxt( ... "/some/file.ang", unpack=True) >>> euler_angles = np.column_stack((euler1, euler2, euler3)) >>> rotations = Rotation.from_euler(euler_angles) >>> properties = {"iq": iq, "dp": dp} >>> structures = [ ... Structure( ... title="austenite", ... atoms=[Atom("fe", [0] * 3)], ... lattice=Lattice(0.360, 0.360, 0.360, 90, 90, 90) ... ), ... Structure( ... title="ferrite", ... atoms=[Atom("fe", [0] * 3)], ... lattice=Lattice(0.287, 0.287, 0.287, 90, 90, 90) ... ) ... ] >>> pl = PhaseList(space_groups=[225, 229], structures=structures) >>> cm = CrystalMap( ... rotations=rotations, ... phase_id=phase_id, ... x=x, ... y=y, ... phase_list=pl, ... prop=properties, ... ) """ # Set rotations if not isinstance(rotations, Rotation): raise ValueError( f"rotations must be of type {Rotation}, not {type(rotations)}." ) self._rotations = rotations # Set data size data_size = rotations.shape[0] # Set phase IDs if phase_id is None: # Assume single phase data phase_id = np.zeros(data_size) phase_id = phase_id.astype(int) self._phase_id = phase_id # Set data point IDs point_id = np.arange(data_size) self._id = point_id # Set spatial coordinates if x is None and y is None and z is None: x = np.arange(data_size) self._x = x self._y = y self._z = z # Create phase list # Sorted in ascending order unique_phase_ids = np.unique(phase_id) include_not_indexed = False if unique_phase_ids[0] == -1: include_not_indexed = True unique_phase_ids = unique_phase_ids[1:] # Also sorted in ascending order if phase_list is None: self.phases = PhaseList(ids=unique_phase_ids) else: phase_list = copy.deepcopy(phase_list) phase_ids = phase_list.ids n_different = len(phase_ids) - len(unique_phase_ids) if n_different > 0: # Remove superfluous phases by removing the phases whose # ID is not in the ID array, in descending list order for i in phase_ids[::-1]: if i not in unique_phase_ids: del phase_list[i] n_different -= 1 if n_different == 0: break elif n_different < 0: # Create new phase list adding the missing phases with # default initial values phase_list = PhaseList( names=phase_list.names, space_groups=phase_list.space_groups, point_groups=phase_list.point_groups, colors=phase_list.colors, structures=phase_list.structures, ids=unique_phase_ids, ) # Ensure phase list IDs correspond to IDs in phase_id array new_ids = list(unique_phase_ids.astype(int)) phase_list._dict = dict(zip(new_ids, phase_list._dict.values())) self.phases = phase_list # Set whether measurements are indexed is_indexed = np.ones(data_size, dtype=bool) is_indexed[np.where(phase_id == -1)] = False # Add "not_indexed" to phase list and ensure not indexed points # have correct phase ID if include_not_indexed: self.phases.add_not_indexed() self._phase_id[~is_indexed] = -1 # Set array with True for points in data if is_in_data is None: is_in_data = np.ones(data_size, dtype=bool) self.is_in_data = is_in_data # Set scan unit if scan_unit is None: scan_unit = "px" self.scan_unit = scan_unit # Set properties if prop is None: prop = {} self._prop = CrystalMapProperties(prop, id=point_id) # Set original data shape (needed if data shape changes in # __getitem__()) self._original_shape = self._data_shape_from_coordinates()
def test_get_phaselist_colors_rgb(self): pl = PhaseList(names=["a", "b", "c"], colors=["r", "g", (0, 0, 1)]) assert pl.colors == ["r", "g", "b"] assert np.allclose(pl.colors_rgb, [(1.0, 0.0, 0.0), [0, 0.5, 0], (0, 0, 1)])