Chapter 2: Diffraction
HOLZ Lines¶
part of
MSE672: Introduction to Transmission Electron Microscopy
Spring 2025
by Gerd Duscher
Microscopy Facilities
Institute of Advanced Materials & Manufacturing
Materials Science & Engineering
The University of Tennessee, Knoxville
Background and methods to analysis and quantification of data acquired with transmission electron microscopes
import sys
import importlib.metadata
def test_package(package_name):
"""Test if package exists and returns version or -1"""
try:
version = importlib.metadata.version(package_name)
except importlib.metadata.PackageNotFoundError:
version = '-1'
return version
if test_package('pyTEMlib') < '0.2024.1.0':
print('installing pyTEMlib')
!{sys.executable} -m pip install git+https://github.com/pycroscopy/pyTEMlib.git@main -q --upgrade
if 'google.colab' in sys.modules:
!{sys.executable} -m pip install numpy==1.24.4
print('done')installing pyTEMlib
done
Import numerical and plotting python packages¶
Import the python packages that we will use:
Beside the basic numerical (numpy) and plotting (pylab of matplotlib) libraries,
and some libraries from the book
kinematic scattering library.
%matplotlib widget
import matplotlib.pyplot as plt
import numpy as np
import sys
if 'google.colab' in sys.modules:
from google.colab import output
output.enable_custom_widget_manager()
# additional package
import itertools
import scipy.constants as const
import ipywidgets as ipyw
# Import libraries from pyTEMlib
import pyTEMlib
import pyTEMlib.kinematic_scattering as ks # Kinematic scattering Library
# Atomic form factors from Kirklands book
### And we use the image tool library of Quantifit
import pyTEMlib.file_tools as ft
import pyTEMlib
__notebook_version__ = '2024.02.21'
print('pyTEM version: ', pyTEMlib.__version__)
print('notebook version: ', __notebook_version__)pyTEM version: 0.2024.09.0
notebook version: 2024.02.21
Define crystal¶
### Please choose another crystal like: Silicon, Aluminium, GaAs , ZnO
atoms = ks.structure_by_name('silicon')
atomsLattice(symbols='Si8', pbc=True, cell=[5.43088, 5.43088, 5.43088])Plot the unit cell¶
from ase.visualize.plot import plot_atoms
plot_atoms(atoms, radii=0.3, rotation=('0x,4y,0z'))<Axes: title={'center': 'graphite: [0 0 1]'}>Parameters for Diffraction Calculation¶
tags = {}
atoms.info['experimental'] = tags
tags['acceleration_voltage_V'] = 99 *1000.0 #V
tags['convergence_angle_mrad'] = 0
tags['zone_hkl'] = np.array([2,2,1]) # incident neares zone axis: defines Laue Zones!!!!
tags['mistilt'] = np.array([0,0,0]) # mistilt in degrees
tags['Sg_max'] = .01 # 1/Ang maximum allowed excitation error ; This parameter is related to the thickness
tags['hkl_max'] = 36 # Highest evaluated Miller indices
Kinematic Scattering Calculation¶
ks.kinematic_scattering(atoms, False)Plot Selected Area Electron Diffraction Pattern¶
#####################
# Plot ZOLZ SAED Pattern #
#####################
## Get information as dictionary
tagsD = atoms.info['diffraction']
#We plot only the allowed diffraction spots
points = tagsD['allowed']['g']
# we sort them by order of Laue zone
ZOLZ = tagsD['allowed']['ZOLZ']
HOLZ = tagsD['allowed']['HOLZ']
# Plot
fig = plt.figure()
ax = fig.add_subplot(111)
# We plot the x,y axis only; the z -direction is set to zero - this is our projection
ax.scatter(points[ZOLZ,0], points[ZOLZ,1], c='red', s=20)
ax.scatter(points[HOLZ,0], points[HOLZ,1], c='green', s=20)
# zero spot plotting
ax.scatter(0,0, c='red', s=100)
ax.scatter(0,0, c='white', s=40)
ax.axis('equal')
FOV = 18
plt.ylim(-FOV,FOV); plt.xlim(-FOV,FOV); plt.show()import pyTEMlib.animation as animate
plt.figure()
animate.deficient_holz_line(exact_bragg=False, laue_zone=1)
#animate.deficient_holz_line(exact_bragg='True', laue_zone=1, color='blue')
animate.deficient_holz_line(exact_bragg='True', laue_zone=1, color='red', shift=True)
HOLZ Line Construction¶
Position of deficient HOLZ line
What is :¶
No such environment: eqnarray* at position 7: \begin{̲e̲q̲n̲a̲r̲r̲a̲y̲*̲}̲
d\theta …
\begin{eqnarray*}
d\theta &+&(90-\theta_B)+ \varphi = 90\\
d\theta &=& \theta_B -\varphi\\
&&\\
\sin(\theta_B) &=&|\vec{g}/2|/ |\vec{K}_0| \\
\tan(\phi) &=& |\vec{g}_{HOLZ}|/|\vec{g}-\vec{g}_{HOLZ}|\\
&&\\
|\vec{g}_{deficient}| &=& 2\sin( d\theta/ 2 )* |\vec{K}_0|\\
\end{eqnarray*}For exact Bragg position in ZOLZ
then
with:
Because is the same as we can now calculate the deficient HOLZ lines
No such environment: eqnarray* at position 7: \begin{̲e̲q̲n̲a̲r̲r̲a̲y̲*̲}̲
\vec{K}_{B…
\begin{eqnarray*}
\vec{K}_{Bg} &=& ( (\vec{g}-\vec{g}_{HOLZ})/ |\vec{g}-\vec{g}_{HOLZ}|*|\vec{g}_{HOLZ}| \\
&& + \vec{K}_{0}/|\vec{K}_{0}|*|\vec{g}-\vec{g}_{HOLZ}|)\\
\vec{K}_{Bg} &=&(\vec{g}_{ZOLZ}/|\vec{g}_{ZOLZ}|*|\vec{g}_{HOLZ}|) \\
&+&\vec{K}_{0}/|\vec{K}_{0}|*|\vec{g}-\vec{g}_{HOLZ}|)\\
\vec{K}_{B} &=& \vec{K}_{bg}/|\vec{K}_{Bg}|*\sqrt{|\lambda^2-g^2/4)}\\
\vec{K}_d &=& -(\vec{g}/2+\vec{K}_B)\\
\vec{g}_{deficient} &=& \vec{K}_{0}-\vec{K}_{d}\\
\end{eqnarray*}For exact Bragg position in ZOLZ then
This is our Kikuchi line equation
atoms.info['experimental']['nearest_zone_axes']
nearest = atoms.info['experimental']['nearest_zone_axes']
print('Next nearest zone axes are:')
for i in range(1, nearest['amount']):
print(f" {nearest[str(i)]['hkl']}: mistilt: {np.rad2deg(nearest[str(i)]['mistilt_alpha']):6.2f}, "
f"{np.rad2deg(nearest[str(i)]['mistilt_beta']):6.2f}")Next nearest zone axes are:
[5. 5. 3.]: mistilt: 1.20, 4.40
[5. 5. 2.]: mistilt: -1.07, -4.76
#Calculate angle between K0 and deficient cone vector
#For dynamic calculations K0 is replaced by Kg
K0 = np.linalg.norm(atoms.info['experimental']['incident_wave_vector'])
g_allowed = atoms.info['diffraction']['allowed']['g']
g_norm_allowed = np.linalg.norm(g_allowed, axis = 1)
dtheta = np.arcsin(g_norm_allowed/K0/2.)
np.arcsin(np.abs(g_allowed[:,2])/g_norm_allowed)
#calculate length of distance of deficient cone to K0 in ZOLZ plane
gd_length =2*np.sin((dtheta)/2 )*K0
#Calculate nearest point of HOLZ and Kikuchi lines
gd = g_allowed.copy()
gd[:,0] = -gd[:,0]*gd_length/g_norm_allowed
gd[:,1] = -gd[:,1]*gd_length/g_norm_allowed
gd[:,2] = 0.
###calculate and save line in Hough space coordinates (distance and theta)
slope = gd[:,0]/(gd[:,1]+1e-20)
distance = gd_length
theta = np.arctan(slope)Now everything together in a single cell¶
We change the lattice parameter (Vegard’s law of alloys) by a few pm and observe the effect on the pattern.
# ----- Input -----------
unit_cell_change_pm = -1.0
# -----------------------
atoms = ks.structure_by_name('silicon')
cell = atoms.cell.lengths()
atoms.set_cell(cell+unit_cell_change_pm/100, scale_atoms=True)
atoms
atoms.info['experimental'] = {'crystal_name': 'silicon',
'acceleration_voltage_V': 100.8*1000.0, #V
'convergence_angle_mrad': 5.,
'Sg_max': .03, # 1/Ang maximum allowed excitation error ; This parameter is related to the thickness
'hkl_max': 14, # Highest evaluated Miller indices
'zone_hkl': np.array([1, 2, -2]),
'mistilt_alpha degree': 0., # -45#-35-0.28-1+2.42
'mistilt_beta degree': 0.,
'plot_FOV': .5}
ks.kinematic_scattering(atoms)
atoms.info['output']=(ks.plotHOLZ_parameter())
atoms.info['output']['plot_reflections']=False
atoms.info['output']['plot_Kikuchi']=False
atoms.info['output']['linewidth_HOLZ'] = -1
atoms.info['output']['plot_HOLZ']=True
ks.plot_diffraction_pattern(atoms)
plt.gca().set_xlim(-.2,.2)
plt.gca().set_ylim(-.2,.2)atoms = ks.structure_by_name('graphite')
atomsAtoms(symbols='C4', pbc=False, cell=[[2.46772414, 0.0, 0.0], [-1.2338620699999996, 2.1371117947721068, 0.0], [0.0, 0.0, 6.711]])Now for graphite and low acceleration voltages.¶
Change the acceleration voltage and see what happens.
Tip: 60.49keV is especially interesting
### Please choose another crystal like: Silicon, Aluminium, GaAs , ZnO
# ----- Input -----------
crystal_name = 'graphite'
acceleration_voltage_V = 60.49 * 1000.0
# -----------------------
atoms = ks.structure_by_name(crystal_name)
atoms.info['experimental'] = {'crystal_name': crystal_name,
'acceleration_voltage_V': acceleration_voltage_V, #V
'convergence_angle_mrad': 7.,
'Sg_max': .03, # 1/Ang maximum allowed excitation error ; This parameter is related to the thickness
'hkl_max': 9, # Highest evaluated Miller indices
'zone_hkl': np.array([0, 0, 1]),
'mistilt_alpha degree': .0, # -45#-35-0.28-1+2.42
'mistilt_beta degree': 0.,
'plot_FOV': .5}
ks.kinematic_scattering(atoms, verbose=False)
atoms.info['output']=(ks.plotHOLZ_parameter())
atoms.info['output']['plot_reflections']=True
atoms.info['output']['plot_Kikuchi']=False
atoms.info['output']['linewidth_HOLZ'] = 1
atoms.info['output']['plot_HOLZ']=True
ks.plot_diffraction_pattern(atoms)
plt.title(crystal_name + ': ' + str(atoms.info['experimental']['zone_hkl']))
plt.gca().set_xlim(-.17,.17)
plt.gca().set_ylim(-.17,.17)
Navigation¶
Back: Kikuchi Lines
Chapter 2: Diffraction
List of Content: Front