This notebook is part of the kikuchipy
documentation https://kikuchipy.org.
Links to the documentation won't work from the notebook.
In this tutorial, we will inspect and visualize the results from EBSD indexing by plotting Kikuchi lines and zone axes onto an EBSD signal. We consider this a geometrical EBSD simulation, since it is only positions of Kikuchi lines and zone axes that are computed. These simulations are based on the work by Aimo Winkelmann in the supplementary material to Britton et al. (2016).
These simulations can be helpful when checking whether indexing results are correct and for interpreting them.
Let's import the necessary libraries
# Exchange inline for notebook or qt5 (from pyqt) for interactive plotting
%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np
from diffpy.structure import Atom, Lattice, Structure
from diffsims.crystallography import ReciprocalLatticeVector
import hyperspy.api as hs
import kikuchipy as kp
from orix.crystal_map import Phase
from orix.quaternion import Rotation
# Plotting parameters
plt.rcParams.update(
{"figure.figsize": (10, 10), "font.size": 20, "lines.markersize": 10}
)
We'll inspect the indexing results of a small nickel EBSD dataset of (3, 3) patterns
# Use kp.load("data.h5") to load your own data
s = kp.data.nickel_ebsd_small()
s
Let's enhance the Kikuchi bands by removing the static and dynamic backgrounds
s.remove_static_background()
s.remove_dynamic_background()
_ = hs.plot.plot_images(
s, axes_decor=None, label=None, colorbar=False, tight_layout=True
)
To project Kikuchi lines and zone axes onto our detector, we need
a description of the crystal phase
the set of Kikuchi bands to consider, e.g. the sets of planes {111}, {200}, {220}, and {311}
the crystal orientations with respect to the reference frame
the position of the detector with respect to the sample, in the form of a sample-detector model which includes the sample and detector tilt and the projection center (shortes distance from the source point on the sample to the detector), given here as (PC$_x$, PC$_y$, PC$_z$)
We'll store the crystal phase information in an orix.crystal_map.Phase instance
phase = Phase(
space_group=225,
structure=Structure(
atoms=[Atom("Ni", [0, 0, 0])],
lattice=Lattice(3.52, 3.52, 3.52, 90, 90, 90),
),
)
print(phase)
print(phase.structure)
We'll build up the reflector list using diffsims.crystallography.ReciprocalLatticeVector
rlv = ReciprocalLatticeVector(
phase=phase, hkl=[[1, 1, 1], [2, 0, 0], [2, 2, 0], [3, 1, 1]]
)
rlv
We'll obtain the symmetrically equivalent vectors and plot each family of vectors in a distinct colour in the stereographic projection
rlv = rlv.symmetrise().unique()
rlv.size
rlv.print_table()
# Dictionary with {hkl} as key and indices into ReciprocalLatticeVector as values
hkl_sets = rlv.get_hkl_sets()
hkl_sets
hkl_colors = np.zeros((rlv.size, 3))
for idx, color in zip(
hkl_sets.values(),
[
[1, 0, 0],
[0, 1, 0],
[0, 0, 1],
[0.75, 0, 0.75],
], # Red, green, blue, magenta
):
hkl_colors[idx] = color
hkl_labels = []
for hkl in rlv.hkl.round(0).astype(int):
hkl_labels.append(str(hkl).replace("[", "(").replace("]", ")"))
rlv.scatter(c=hkl_colors, grid=True, ec="k", vector_labels=hkl_labels)
We can also plot the plane traces, i.e. the Kikuchi lines, in both hemispheres (they are identical for Ni)
rlv.draw_circle(
color=hkl_colors, hemisphere="both", figure_kwargs=dict(figsize=(15, 10))
)
Rotations and the detector-sample geometry for the nine nickel EBSD patterns are stored in the kikuchipy h5ebsd file. These were found by Hough indexing using PyEBSDIndex followed by orientation and PC refinement using a nickel EBSD master pattern simulated with EMsoft. See the Hough indexing and Pattern matching tutorials for details on indexing and the reference frame tutorial for details on the definition of the detector-sample geometry.
rot = s.xmap.rotations
rot = rot.reshape(*s.xmap.shape)
rot # Quaternions
We describe the sample-detector model in an kikuchipy.detectors.EBSDDetector instance. The sample was tilted $70^{\circ}$ about the microscope X direction towards the detector, and the detector normal was orthogonal to the optical axis (beam direction). Using this information, the projection center (PC) was found using PyEBSDIndex as described in the Hough indexing tutorial.
s.detector
s.detector.pc
Now we're ready to create geometrical simulations. We create simulations using the kikuchipy.simulations.KikuchiPatternSimulator, which takes the reflectors as input
simulator = kp.simulations.KikuchiPatternSimulator(rlv)
sim = simulator.on_detector(s.detector, rot)
By passing the detector and crystal orientations to KikuchiPatternSimulator.on_detector(), we've obtained a kikuchipy.simulations.GeometricalKikuchiPatternSimulation, which stores the detector and gnomonic coordinates of the Kikuchi lines and zone axes for each crystal orientation
sim
We see that not all 50 of the reflectors in the reflector list are present in some pattern.
These geometrical simulations can be plotted one-by-one by themselves
sim.plot()
Or, they can be plotted on top of patterns in three ways: passing a pattern to GeometricalKikuchiPatternSimulation.plot()
sim.plot(index=(1, 2), pattern=s.inav[2, 1].data)
Or, we can obtain collections of lines, zone axes and zone axes labels as Matplotlib objects via GeometricalKikuchiPatternSimulation.as_collections() and add them to an existing Matplotlib axis
fig, ax = plt.subplots(ncols=3, nrows=3, figsize=(15, 15))
for idx in np.ndindex(s.axes_manager.navigation_shape[::-1]):
ax[idx].imshow(s.data[idx], cmap="gray")
ax[idx].axis("off")
lines, zone_axes, zone_axes_labels = sim.as_collections(
idx,
zone_axes=True,
zone_axes_labels=True,
zone_axes_labels_kwargs=dict(fontsize=12),
)
ax[idx].add_collection(lines)
ax[idx].add_collection(zone_axes)
for label in zone_axes_labels:
ax[idx].add_artist(label)
fig.tight_layout()
Or, we can obtain the lines, zone axes, zone axes labels and PCs as HyperSpy markers via GeometricalKikuchiPatternSimulation.as_markers() and add them to a signal of the same navigation shape as the simulation instance. This enables navigating the patterns with the geometrical simulations
markers = sim.as_markers()
# To delete previously added permanent markers, do
# del s.metadata.Markers
s.add_marker(markers, plot_marker=False, permanent=True)
s.plot()