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 perform kinematical Kikuchi pattern simulations of nickel, a variant of the $\sigma$-phase (Fe, Cr) in steels, and silicon carbide 6H.
We can generate kinematical master patterns using KikuchiPatternSimulator.calculate_master_pattern(). The simulator must be created from a ReciprocalLatticeVector instance that satisfy the following conditions:
structure
used to create the phase
used in ReciprocalLatticeVector
. This can be achieved by creating a Phase
instance with all asymmetric atom positions listed, creating a reflector list, and then calling ReciprocalLatticeVector.sanitise_phase(). The phase can be created manually or imported from a valid CIF file with Phase.from_cif().structure
have their elements described by the symbol (Ni), not by the atomic number (28).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
import pyvista as pv
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
# Plotting parameters
plt.rcParams.update(
{"figure.figsize": (10, 10), "font.size": 20, "lines.markersize": 10}
)
# See https://docs.pyvista.org/user-guide/jupyter/index.html
pv.set_jupyter_backend("static")
We'll compare our kinematical simulations to dynamical simulations performed with EMsoft (see Callahan and De Graef (2013)), since we have a nickel master pattern available in the kikuchipy.data module
mp_ni_dyn = kp.data.nickel_ebsd_master_pattern_small(
projection="stereographic"
)
Inspect the phase
phase_ni = mp_ni_dyn.phase.deepcopy()
print(phase_ni)
print(phase_ni.structure.lattice)
Change lattice parameters from nm to Ångström
lat_ni = phase_ni.structure.lattice # Shallow copy
lat_ni.setLatPar(lat_ni.a * 10, lat_ni.b * 10, lat_ni.c * 10)
print(phase_ni.structure.lattice)
We'll build up the reflector list by:
ref_ni = ReciprocalLatticeVector.from_min_dspacing(phase_ni, 0.5)
# Exclude non-allowed reflectors (not available for hexagonal or trigonal
# phases!)
ref_ni = ref_ni[ref_ni.allowed]
ref_ni = ref_ni.unique(use_symmetry=True).symmetrise()
Sanitise the phase
by completing the unit cell
ref_ni.phase.structure
ref_ni.sanitise_phase()
ref_ni.phase.structure
We can now calculate the structure factors $F$.
Two parametrizations are available, from Kirkland (1998) ("xtables"
, the default) and Lobato and Van Dyck (2014) ("lobato"
)
ref_ni.calculate_structure_factor()
F_ni = abs(ref_ni.structure_factor)
ref_ni = ref_ni[F_ni > 0.05 * F_ni.max()]
ref_ni.print_table()
Calculate the Bragg angle $\theta_{\mathrm{B}}$
ref_ni.calculate_theta(20e3)
We can now create our simulator and plot the simulation
simulator_ni = kp.simulations.KikuchiPatternSimulator(ref_ni)
simulator_ni.reflectors.size
Plotting the band centers with intensities scaled by the structure factor $|F|$
simulator_ni.plot()
Or no scaling, $|F|$ = 1 (scaling="square"
for the structure factor squared $|F|^2$)
simulator_ni.plot(scaling=None)
We can also plot the Kikuchi bands, showing both hemispheres, also adding the crystal axes alignment
fig = simulator_ni.plot(hemisphere="both", mode="bands", return_figure=True)
ax = fig.axes[0]
ax.scatter(simulator_ni.phase.a_axis, c="r", ec="w")
ax.scatter(simulator_ni.phase.b_axis, c="g", ec="w")
ax.scatter(simulator_ni.phase.c_axis, c="b", ec="w")
fig.tight_layout()
The simulation can be plotted in the spherical projection as well using Matplotlib or PyVista, provided that it is installed
simulator_ni.plot("spherical", mode="bands")
simulator_ni.plot("spherical", mode="bands", backend="pyvista")
When we're happy with the reflector list in the simulator, we can generate our kinematical master pattern
mp_ni_kin = simulator_ni.calculate_master_pattern(half_size=200)
The returned master pattern is an instance of EBSDMasterPattern in the stereographic projection
mp_ni_kin
A spherical plot (requires that PyVista is installed)
mp_ni_kin.plot_spherical(style="points")
Comparing kinematical and dynamical simulations
# Exclude outside equator
ni_dyn_data = mp_ni_dyn.data.astype("float32")
ni_kin_data = mp_ni_kin.data.astype("float32")
mask = ni_dyn_data == 0
ni_dyn_data[mask] = np.nan
ni_kin_data[mask] = np.nan
fig, axes = plt.subplots(ncols=2, layout="tight")
for ax, data, title in zip(axes, [ni_kin_data, ni_dyn_data], ["kinematical", "dynamical"]):
ax.imshow(data, cmap="gray")
ax.axis("off")
ax.set(title=f"Ni {title} 20 kV")
Warning
Use dynamical simulations when performing pattern matching, not kinematical simulations. The latter intensities are not realistic, as demonstrated in the above comparison.
Finally, we can transform the master pattern in the stereographic projection to one in the Lambert projection
mp_ni_kin_lp = mp_ni_kin.as_lambert()
mp_ni_kin_lp.plot()
We can then project parts of this pattern onto our EBSD detector using get_patterns(). Let's do this for the (3, 3) patterns used to demonstrate geometrical simulations in the geometrical EBSD simulations tutorial. These patterns are stored with the indexed solutions and an optimized detector-sample geometry (both found using PyEBSDIndex, see the Hough indexing for details)
s = kp.data.nickel_ebsd_small(lazy=True) # Don't load the patterns
Gr = s.xmap.rotations
Gr = Gr.reshape(*s.xmap.shape)
print(Gr)
print(s.detector)
s_kin = mp_ni_kin_lp.get_patterns(Gr, s.detector, energy=20, compute=True)
_ = hs.plot.plot_images(
s_kin, axes_decor=None, label=None, colorbar=False, tight_layout=True
)
Feel free to compare these patterns to the experimental patterns in the geometrical EBSD simulations tutorial!
phase_sigma = Phase(
name="sigma",
space_group=136,
structure=Structure(
atoms=[
Atom("Cr", [0, 0, 0], 0.5),
Atom("Fe", [0, 0, 0], 0.5),
Atom("Cr", [0.31773, 0.31773, 0], 0.5),
Atom("Fe", [0.31773, 0.31773, 0], 0.5),
Atom("Cr", [0.06609, 0.26067, 0], 0.5),
Atom("Fe", [0.06609, 0.26067, 0], 0.5),
Atom("Cr", [0.13122, 0.53651, 0], 0.5),
Atom("Fe", [0.13122, 0.53651, 0], 0.5),
],
lattice=Lattice(8.802, 8.802, 4.548, 90, 90, 90),
),
)
phase_sigma
ref_sigma = ReciprocalLatticeVector.from_min_dspacing(phase_sigma, 1)
ref_sigma.sanitise_phase()
ref_sigma.calculate_structure_factor("lobato")
F_sigma = abs(ref_sigma.structure_factor)
ref_sigma = ref_sigma[F_sigma > 0.05 * F_sigma.max()]
ref_sigma.calculate_theta(20e3)
ref_sigma.print_table()
simulator_sigma = kp.simulations.KikuchiPatternSimulator(ref_sigma)
simulator_sigma
fig = simulator_sigma.plot(
hemisphere="both", mode="bands", return_figure=True
)
ax = fig.axes[0]
ax.scatter(simulator_sigma.phase.a_axis, c="r", ec="w")
ax.scatter(simulator_sigma.phase.b_axis, c="g", ec="w")
ax.scatter(simulator_sigma.phase.c_axis, c="b", ec="w")
fig.tight_layout()
simulator_sigma.plot("spherical", mode="bands", backend="pyvista")
mp_sigma = simulator_sigma.calculate_master_pattern()
mp_sigma.plot()
mp_sigma.plot_spherical(style="points")
phase_sic = Phase(
name="sic_6h",
space_group=186,
structure=Structure(
atoms=[
Atom("Si", [1 / 3, 2 / 3, 0.20778]),
Atom("C", [1 / 3, 2 / 3, 0.33298]),
Atom("Si", [1 / 3, 2 / 3, 0.54134]),
Atom("C", [1 / 3, 2 / 3, 0.66647]),
Atom("C", [0, 0, 0]),
Atom("Si", [0, 0, 0.37461]),
],
lattice=Lattice(3.081, 3.081, 15.2101, 90, 90, 120),
),
)
phase_sic
ref_sic = ReciprocalLatticeVector.from_min_dspacing(phase_sic)
ref_sic.sanitise_phase()
ref_sic.calculate_structure_factor()
F_sic = abs(ref_sic.structure_factor)
ref_sic = ref_sic[F_sic > 0.05 * F_sic.max()]
ref_sic.calculate_theta(20e3)
ref_sic.print_table()
simulator_sic = kp.simulations.KikuchiPatternSimulator(ref_sic)
simulator_sic
fig = simulator_sic.plot(hemisphere="both", mode="bands", return_figure=True)
ax = fig.axes[0]
ax.scatter(simulator_sic.phase.a_axis, c="r", ec="w")
ax.scatter(simulator_sic.phase.b_axis, c="g", ec="w")
ax.scatter(simulator_sic.phase.c_axis, c="b", ec="w")
fig.tight_layout()
simulator_sic.plot("spherical", mode="bands", backend="pyvista")
mp_sic = simulator_sic.calculate_master_pattern(
hemisphere="both", half_size=200
)
mp_sic
mp_sic.plot(navigator=None)
mp_sic.plot_spherical(style="points")