Skip to content

Commit

Permalink
Merge pull request #639 from hakonanes/fix-637
Browse files Browse the repository at this point in the history
Fix merging of maps with non-indexed points and release v0.8.5
  • Loading branch information
hakonanes authored May 21, 2023
2 parents 49e6814 + 9692eaa commit d5e1236
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 7 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@ its best to adhere to `Semantic Versioning <https://semver.org/spec/v2.0.0.html>
List entries are sorted in descending chronological order. Contributors to each release
were listed in alphabetical order by first name until version 0.7.0.

0.8.5 (2023-05-21)
==================

Fixed
-----
- Not-indexed points in crystal maps are handled correctly when merging.
(`#639 <https://github.com/pyxem/kikuchipy/pull/639>`_)

0.8.4 (2023-04-07)
==================

Expand Down
28 changes: 23 additions & 5 deletions kikuchipy/indexing/_merge_crystal_maps.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,9 @@ def merge_crystal_maps(

# Combined (unsorted) scores array of shape (M, N, K) or (M, K)
scores_dtype = crystal_maps[0].prop[scores_prop].dtype
combined_scores = np.full(comb_shape, np.nan, dtype=scores_dtype)
combined_scores = np.full(
comb_shape, np.nan, dtype=np.dtype(f"f{scores_dtype.itemsize}")
)
for i, (mask, xmap) in enumerate(zip(navigation_masks1d, crystal_maps)):
if mask is not None:
combined_scores[mask, ..., i] = xmap.prop[scores_prop]
Expand All @@ -212,6 +214,18 @@ def merge_crystal_maps(
# Phase of best score in each map point
phase_id = np.nanargmax(sign * best_scores, axis=1)

# Set the phase ID of points marked as not-indexed in all maps to -1
not_indexed = np.zeros((n_maps, map_size), dtype=bool)
for i in range(n_maps):
mask = navigation_masks1d[i]
xmap = crystal_maps[i]
if mask is not None:
not_indexed[i, mask][xmap.phase_id == -1] = True
else:
not_indexed[i, xmap.phase_id == -1] = True
not_indexed = np.logical_and.reduce(not_indexed)
phase_id[not_indexed] = -1

# Get the new crystal map's rotations, scores and indices,
# restricted to one phase per point (uncombined)
new_rotations = np.zeros(comb_shape[:-1] + (4,), dtype="float")
Expand All @@ -221,12 +235,16 @@ def merge_crystal_maps(
new_indices = np.zeros(comb_shape[:-1], dtype="int32")

phase_list = PhaseList()
if -1 in phase_id:
phase_list.add_not_indexed()
for i, (nav_mask1d, xmap) in enumerate(zip(navigation_masks1d, crystal_maps)):
phase_mask = phase_id == i

if phase_mask.any():
current_id = xmap.phases_in_data.ids[0]
phase = xmap.phases_in_data[current_id].deepcopy()
phase_ids = xmap.phases_in_data.ids
if -1 in phase_ids:
phase_ids.remove(-1)
phase = xmap.phases_in_data[phase_ids[0]].deepcopy()
if phase.name in phase_list.names:
# If they are equal, do not duplicate it in the phase
# list but update the phase ID
Expand All @@ -235,12 +253,12 @@ def merge_crystal_maps(
phase_id[phase_mask] = phase_list.id_from_name(phase.name)
else:
name = phase.name
phase.name = name + str(i)
warnings.warn(
f"There are duplicates of phase '{name}' but the phases have "
f"different {different}, will therefore rename this phase's "
f"name to '{name + str(i)}' in the merged PhaseList",
f"name to '{phase.name}' in the merged PhaseList",
)
phase.name = name + str(i)
phase_list.add(phase)
else:
phase_list.add(phase)
Expand Down
40 changes: 39 additions & 1 deletion kikuchipy/indexing/tests/test_merge_crystal_maps.py
Original file line number Diff line number Diff line change
Expand Up @@ -559,7 +559,7 @@ def test_merging_with_navigation_masks(self):
)
# fmt: on

# All points in a map should be used, but not in another one:
# All points in one map should be used, but not in another:
# Only consider xmap1 in the first row and first column (mask it
# out everywhere else)
xmap6 = merge_crystal_maps(
Expand Down Expand Up @@ -644,3 +644,41 @@ def test_merging_with_navigation_masks_raises(self):
[xmap1[~nav_mask1.ravel()], xmap2[~nav_mask2.ravel()]],
navigation_masks=[nav_mask1, list(nav_mask2)],
)

def test_not_indexed(self):
xmap_a = CrystalMap.empty((4, 3))
is_indexed_a = np.array(
[[1, 1, 0], [1, 0, 1], [0, 1, 1], [0, 1, 1]], dtype=bool
).ravel()
xmap_a.phases.add_not_indexed()
xmap_a.phases[0].name = "a"
xmap_a[~is_indexed_a].phase_id = -1
xmap_a.prop["scores"] = np.array(
[[2, 2, 0], [3, 0, 4], [0, 4, 3], [0, 2, 1]], dtype=float
).ravel()
xmap_a._rotations = xmap_a.rotations * Rotation.from_axes_angles(
[0, 0, 1], 30, degrees=True
)

xmap_b = CrystalMap.empty((4, 3))
is_indexed_b = np.array(
[[1, 1, 0], [1, 1, 1], [0, 1, 1], [0, 1, 0]], dtype=bool
).ravel()
xmap_b.phases.add_not_indexed()
xmap_b.phases[0].name = "b"
xmap_b[~is_indexed_b].phase_id = -1
xmap_b.prop["scores"] = np.array(
[[3, 1, 0], [2, 1, 5], [0, 2, 4], [0, 1, 0]], dtype=float
).ravel()
xmap_b._rotations = xmap_b.rotations * Rotation.from_axes_angles(
[0, 0, 1], 60, degrees=True
)

xmap_ab = merge_crystal_maps([xmap_a, xmap_b])

assert np.allclose(xmap_ab.phase_id, [1, 0, -1, 0, 1, 1, -1, 0, 1, -1, 0, 0])
assert np.allclose(
xmap_ab["indexed"].rotations.angle,
np.deg2rad([60, 30, 30, 60, 60, 30, 60, 30, 30]),
)
assert np.allclose(xmap_ab["indexed"].scores, [3, 2, 3, 1, 5, 4, 4, 2, 1])
1 change: 1 addition & 0 deletions kikuchipy/pattern/tests/test_pattern.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ def test_remove_static_background_subtract(self, dummy_signal, dummy_background)
assert np.allclose(p0, p)
assert p0.dtype == p.dtype

@pytest.mark.filterwarnings("ignore:invalid value")
def test_remove_static_background_divide(self, dummy_signal, dummy_background):
p = dummy_signal.inav[0, 0].data
dtype_out = p.dtype
Expand Down
2 changes: 1 addition & 1 deletion kikuchipy/release.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,4 @@
name = "kikuchipy"
platforms = ["Linux", "MacOS X", "Windows"]
status = "Development"
version = "0.8.4"
version = "0.8.5"
2 changes: 2 additions & 0 deletions kikuchipy/signals/tests/test_ebsd.py
Original file line number Diff line number Diff line change
Expand Up @@ -557,6 +557,7 @@ def test_remove_dynamic_background_raises(self, dummy_signal):
with pytest.raises(ValueError, match=f"{filter_domain} must be "):
dummy_signal.remove_dynamic_background(filter_domain=filter_domain)

@pytest.mark.filterwarnings("ignore:invalid value")
def test_inplace(self, dummy_signal):
# Current signal is unaffected
s = dummy_signal.deepcopy()
Expand All @@ -580,6 +581,7 @@ def test_inplace(self, dummy_signal):
s4.compute()
assert np.allclose(s4.data, dummy_signal.data)

@pytest.mark.filterwarnings("ignore:invalid value")
def test_lazy_output(self, dummy_signal):
with pytest.raises(
ValueError, match="`lazy_output=True` requires `inplace=False`"
Expand Down

0 comments on commit d5e1236

Please sign in to comment.