From 4997b22b86b618cd574b370ea55e42ee92e2d8b7 Mon Sep 17 00:00:00 2001 From: Viljar Femoen Date: Mon, 18 Nov 2024 17:28:38 +0100 Subject: [PATCH 1/5] Fix vector consistency; add multi-phase polar markers --- pyxem/signals/indexation_results.py | 87 ++++++++++++++--------------- 1 file changed, 41 insertions(+), 46 deletions(-) diff --git a/pyxem/signals/indexation_results.py b/pyxem/signals/indexation_results.py index bbd0d1719..72368b340 100644 --- a/pyxem/signals/indexation_results.py +++ b/pyxem/signals/indexation_results.py @@ -449,8 +449,12 @@ def extract_vectors_from_orientation_map(result, all_vectors, n_best_index=0): def vectors_from_orientation_map(result, all_vectors, n_best_index=0): index, _, rotation, mirror = result[n_best_index, :].T index = index.astype(int) - if all_vectors.ndim == 0: + if isinstance(all_vectors, DiffractingVector) and all_vectors.ndim == 1: + # Only one simulation vectors = all_vectors + elif all_vectors.ndim == 0: + # Only one simulation, but in an array + vectors = np.atleast_1d(all_vectors)[0] else: vectors = all_vectors[index] # Copy manually, as deepcopy adds a lot of overhead with the phase @@ -463,11 +467,14 @@ def vectors_from_orientation_map(result, all_vectors, n_best_index=0): rotation = Rotation.from_euler( (mirror * rotation, 0, 0), degrees=True, direction="crystal2lab" ) - + coordinate_format = vectors.coordinate_format vectors = ~rotation * vectors.to_miller() vectors = DiffractingVector( - vectors.phase, xyz=vectors.data.copy(), intensity=intensity + vectors.phase, + xyz=-vectors.data.copy(), # Negating for proper alignment - is this flipping z direction? + intensity=intensity, ) + vectors.coordinate_format = coordinate_format vectors.original_hkl = hkl # Mirror if necessary. @@ -907,7 +914,7 @@ def to_markers( annotation_shift: Sequence[float] = None, text_kwargs: dict = None, include_intensity: bool = False, - intesity_scale: float = 1, + intensity_scale: float = 1, fast: bool = True, **kwargs, ) -> Sequence[hs.plot.markers.Markers]: @@ -953,13 +960,13 @@ def to_markers( all_markers = [] for n in range(n_best): vectors = self.to_vectors( - lazy_output=True, navigation_chunks=navigation_chunks + n_best_index=n, lazy_output=True, navigation_chunks=navigation_chunks ) color = marker_colors[n % len(marker_colors)] if include_intensity: intensity = vectors.map( vectors_to_intensity, - scale=intesity_scale, + scale=intensity_scale, inplace=False, ragged=True, output_dtype=object, @@ -1007,6 +1014,11 @@ def to_markers( all_markers = compute_markers(all_markers) return all_markers + @deprecated( + since="0.20.0", + alternative="pyxem.signals.OrientationMap.to_polar_markers", + removal="1.0.0", + ) def to_single_phase_polar_markers( self, signal_axes: Sequence[BaseDataAxis], @@ -1014,6 +1026,20 @@ def to_single_phase_polar_markers( marker_colors: str = ("red", "blue", "green", "orange", "purple"), lazy_output: bool = None, **kwargs, + ) -> Iterator[hs.plot.markers.Markers]: + return self.to_polar_markers( + n_best=n_best, + marker_colors=marker_colors, + lazy_output=lazy_output, + **kwargs, + ) + + def to_polar_markers( + self, + n_best: int = 1, + marker_colors: str = ("red", "blue", "green", "orange", "purple"), + lazy_output: bool = None, + **kwargs, ) -> Iterator[hs.plot.markers.Markers]: """ Convert the orientation map to a set of markers for plotting in polar coordinates. @@ -1038,53 +1064,22 @@ def to_single_phase_polar_markers( An list of markers for each of the n_best solutions """ - ( - r_templates, - theta_templates, - intensities_templates, - ) = self.simulation.polar_flatten_simulations( - signal_axes[1].axis, signal_axes[0].axis - ) - if lazy_output is None: lazy_output = self._lazy - def marker_generator_factory(n_best_entry: int, r_axis, theta_axis): - theta_min, theta_max = theta_axis.min(), theta_axis.max() - - def marker_generator(entry): - index, _, rotation, mirror = entry[n_best_entry] - index = index.astype(int) - mirror = mirror.astype(int) - - r_ind = r_templates[index] - r = r_axis[r_ind] - - theta_ind = theta_templates[index] - theta = theta_axis[::mirror][theta_ind] + np.deg2rad(rotation) - - # Rotate as per https://github.com/pyxem/pyxem/issues/925 - theta += np.pi - - # handle wrap-around theta - theta -= theta_min - theta %= theta_max - theta_min - theta += theta_min - - mask = r != 0 - return np.vstack((theta[mask], r[mask])).T - - return marker_generator + def vec2polar(vector): + r, t = vector.to_flat_polar() + # flip y + t = -t + return np.vstack([t, r]).T all_markers = [] for n in range(n_best): color = marker_colors[n % len(marker_colors)] - markers_signal = self.map( - marker_generator_factory(n, signal_axes[1].axis, signal_axes[0].axis), - inplace=False, - ragged=True, - lazy_output=True, - ) + + vecs = self.to_vectors(n) + markers_signal = vecs.map(vec2polar, inplace=False, ragged=True) + if "sizes" not in kwargs: kwargs["sizes"] = 15 markers = hs.plot.markers.Points.from_signal( From a464f6389f0fccae978e5206399f4663507a5b99 Mon Sep 17 00:00:00 2001 From: Viljar Femoen Date: Tue, 19 Nov 2024 10:35:37 +0100 Subject: [PATCH 2/5] Add test for marker correctness and polar markers --- .../tests/signals/test_indexation_results.py | 65 +++++++++++++++++-- 1 file changed, 61 insertions(+), 4 deletions(-) diff --git a/pyxem/tests/signals/test_indexation_results.py b/pyxem/tests/signals/test_indexation_results.py index 1993b990b..339e0cbd1 100644 --- a/pyxem/tests/signals/test_indexation_results.py +++ b/pyxem/tests/signals/test_indexation_results.py @@ -326,14 +326,22 @@ def test_to_markers_lazy(self, simple_multi_rot_orientation_result): markers = orientations.to_markers() assert isinstance(markers[0].kwargs["offsets"], da.Array) - def test_to_markers_polar(self, simple_multi_rot_orientation_result): + def test_to_single_phase_markers_polar(self, simple_multi_rot_orientation_result): orientations, rotations, s = simple_multi_rot_orientation_result polar = s.get_azimuthal_integral2d( npt=100, npt_azim=180, inplace=False, mean=True ) - markers = orientations.to_single_phase_polar_markers( - polar.axes_manager.signal_axes - ) + from pyxem.common import VisibleDeprecationWarning + + with pytest.warns(VisibleDeprecationWarning): + markers = orientations.to_single_phase_polar_markers( + polar.axes_manager.signal_axes + ) + assert isinstance(markers[0], hs.plot.markers.Markers) + + def test_to_markers_polar(self, simple_multi_rot_orientation_result): + orientations, rotations, s = simple_multi_rot_orientation_result + markers = orientations.to_polar_markers() assert isinstance(markers[0], hs.plot.markers.Markers) def test_to_ipf_markers(self, simple_multi_rot_orientation_result): @@ -442,3 +450,52 @@ def test_to_ipf_correlation_heatmap_markers_multi_phase( ): markers = multi_phase_orientation_result.to_ipf_correlation_heatmap_markers() assert all(isinstance(m, hs.plot.markers.Markers) for m in markers) + + def test_vector_markers_correctness(self): + """ + Check if the markers are plotted correctly by performing orientation mapping + on a non-centrosymmetric signal, where only one quadrant is non-zero, + using a template with only one diffraction spot. + This spot should then be plotted in the correct quadrant. + """ + from pyxem.signals import Diffraction2D + + # Simple signal: 1 in first quadrant, 0 elsewhere + signal = Diffraction2D(np.array([[[[0, 1], [0, 0]]]])) + signal.calibration(center=None) + polar = signal.get_azimuthal_integral2d(npt=10, npt_azim=36) + + from diffpy.structure import Lattice, Atom, Structure + from orix.crystal_map import Phase + + # Primitive cubic structure, any will do + l = Lattice(5, 5, 5, 90, 90, 90) + a = [Atom("Au", xyz=[0, 0, 0], lattice=l)] + s = Structure(a, l) + p = Phase(space_group=221, structure=s) + gen = SimulationGenerator() + sim = gen.calculate_diffraction2d(p, with_direct_beam=False) + + # Set intensities to 0 for all vectors except one + _, _, v = sim.get_simulation(0) + v.intensity[:-1] = 0 + v.intensity[-1] = 1 + + # Perform matching + res = polar.get_orientation(sim) + + # Check cartesian + x, y = res.to_markers()[0].kwargs["offsets"][0, 0][-1] + assert x > 0 + # When plotting, the y-axis points downwards, but pyxem defines it as upwards. + # Therefore, the first quadrant, as pyxem defines it, has negative y when plotting. + assert y < 0 + + # Check polar + r, t = res.to_polar_markers()[0].kwargs["offsets"][0, 0][-1] + assert r > 0 + assert 0 < t < np.pi / 2 + + +if __name__ == "__main__": + TestOrientationResult().test_vector_markers_correctness() From dc1fb5d6c3c04347c6e6549d855320a7784920fd Mon Sep 17 00:00:00 2001 From: Viljar Femoen Date: Tue, 19 Nov 2024 10:39:27 +0100 Subject: [PATCH 3/5] Remove debugging --- pyxem/tests/signals/test_indexation_results.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/pyxem/tests/signals/test_indexation_results.py b/pyxem/tests/signals/test_indexation_results.py index 339e0cbd1..cb5694f3c 100644 --- a/pyxem/tests/signals/test_indexation_results.py +++ b/pyxem/tests/signals/test_indexation_results.py @@ -453,8 +453,8 @@ def test_to_ipf_correlation_heatmap_markers_multi_phase( def test_vector_markers_correctness(self): """ - Check if the markers are plotted correctly by performing orientation mapping - on a non-centrosymmetric signal, where only one quadrant is non-zero, + Check if the markers are plotted correctly by performing orientation mapping + on a non-centrosymmetric signal, where only one quadrant is non-zero, using a template with only one diffraction spot. This spot should then be plotted in the correct quadrant. """ @@ -495,7 +495,3 @@ def test_vector_markers_correctness(self): r, t = res.to_polar_markers()[0].kwargs["offsets"][0, 0][-1] assert r > 0 assert 0 < t < np.pi / 2 - - -if __name__ == "__main__": - TestOrientationResult().test_vector_markers_correctness() From 68d2072f655fbfcc6a36a49f96afa3b69ed62417 Mon Sep 17 00:00:00 2001 From: Viljar Femoen Date: Tue, 19 Nov 2024 10:45:35 +0100 Subject: [PATCH 4/5] Update changelog --- CHANGELOG.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index d99c758be..571a6a29e 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -10,10 +10,15 @@ and this project adheres to `Semantic Versioning Date: Mon, 9 Dec 2024 12:39:58 +0100 Subject: [PATCH 5/5] Fix polar markers for new data layout --- pyxem/signals/indexation_results.py | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/pyxem/signals/indexation_results.py b/pyxem/signals/indexation_results.py index b7e51e417..0c34721d5 100644 --- a/pyxem/signals/indexation_results.py +++ b/pyxem/signals/indexation_results.py @@ -1137,18 +1137,33 @@ def to_polar_markers( """ if lazy_output is None: lazy_output = self._lazy + if not lazy_output: + navigation_chunks = (5,) * self.axes_manager.navigation_dimension + else: + navigation_chunks = None def vec2polar(vector): - r, t = vector.to_flat_polar() + # Bug with dtype of python float + vector = vector.astype(np.float64) + r = np.linalg.norm(vector[:, :2], axis=1) + theta = np.arctan2( + vector[:, 1], + vector[:, 0], + ) # flip y - t = -t - return np.vstack([t, r]).T + theta = -theta + return np.vstack([theta, r]).T all_markers = [] for n in range(n_best): color = marker_colors[n % len(marker_colors)] - vecs = self.to_vectors(n) + vecs = self.to_vectors( + n_best_index=n, + return_object=False, + lazy_output=True, + navigation_chunks=navigation_chunks, + ) markers_signal = vecs.map(vec2polar, inplace=False, ragged=True) if "sizes" not in kwargs: