Chapter 3: Imaging
Thermal-Diffuse Scattering¶
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.
Note: This notebook needs a Linux environment and is most easily run in Colab with the button above
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.2.3':
print('installing pyTEMlib')
!{sys.executable} -m pip install --upgrade pyTEMlib -q
if test_package('abtem') < '1.0.0b17':
print('installing abtem')
!{sys.executable} -m pip install --upgrade abtem -q
print('done')installing pyTEMlib
installing abtem
done
Load Packages¶
We will use
numpy and matplotlib (installed with magic comand %pylab.)
physical constants from scipy
The pyTEMlib kinematic scattering librarty is only used to determine the wavelength.
%matplotlib widget
import matplotlib.pyplot as plt
import numpy as np
import sys
import os
if 'google.colab' in sys.modules:
from google.colab import output
output.enable_custom_widget_manager()
# import atomic simulation environment
import ase
import ase.spacegroup
import ase.visualize
# import abintio-tem library
import abtem
__notebook__ = 'CH3-08-Thermal_Diffuse_Scattering'
__notebook_version__ = '2021_03_29'Multislice Algorithm¶
As in the Dynamic Diffraction part in the Multislice notebook, we first define the potential of the slices.
However, here we do this with the abtem package and the structure is made with the ase package
Defining the structure¶
Here we make a SrTiO crystal again
atom_pos = [(0.0, 0.0, 0.0), (0.5, 0.5, 0.5), (0.5, 0.5, 0.0)]
srtio3 = ase.spacegroup.crystal(['Sr','Ti','O'], atom_pos, spacegroup=221, cellpar=3.905, size=(4, 4, 16))
srtio3.center()
ase.visualize.view(srtio3, viewer='x3d') However, as before we need more atoms laterally and more layers.
Please note:
all length in abTEM are in Angstrom
atom_pos = [(0.0, 0.0, 0.0), (0.5, 0.5, 0.5), (0.5, 0.5, 0.0)]
srtio3 = ase.spacegroup.crystal(['Sr','Ti','O'], atom_pos, spacegroup=221, cellpar=3.905, size=(8, 8, 180))
srtio3.center()
print(f"Simulation cell: {srtio3.cell}")
abtem.show_atoms(srtio3);Defining the potential¶
potential = abtem.Potential(srtio3,
gpts=512,
slice_thickness=3.904/2,
parametrization='kirkland',
projection='infinite')
potential.sampling
wave = abtem.PlaneWave(energy=200e3, sampling=.05)Multislice¶
exit_wave = wave.multislice(potential)
exit_wave.compute()
exit_wave.show()Plot Diffraction Pattern¶
diffraction_pattern = exit_wave.diffraction_patterns()
diffraction_pattern.crop(60).block_direct()
view = diffraction_pattern.show(power=.2, figsize=(6,6), cmap='jet')
Thermal Diffuse Scattering¶
The atoms in any real material are not exactly located at their symmetrical lattice points due to thermal and zero-point vibrations. The frozen phonon approximation is a simple to simulate the effects of thermal vibrations.
The scattering that is associated with thermal lattice vibrations is called thermal diffuse scattering (TDS).
In the following, we investigate the effect of thermal diffuse scattering in the frozen phonon approximation on diffraction pattern.
Structure and Debey Waller Factor¶
Each atom is allowed to deviate from its symmetrical lattice position with a Gaussian distribution and
an root mean square rms deviation of in each of three directions. This is equivalent to the Einstein model of the density of states for phonons.
This standard deviation of this Gaussiam distribution for most materials is about 0.1Å:
The relation of the Debey-Waller Factors in the literature to the deviation of position .
The FrozenPhonon class of the abtem package generates offsets from such a Gaussian distribution using a random number generator with a given seed.
The standard deviation of the Gaussian distribution is provided for each element as a dictionary.
# ------ Input ------ #
number_of_frozen_phonon_runs = 12
# --------------------#
frozen_phonons = abtem.FrozenPhonons(srtio3, number_of_frozen_phonon_runs, {'Sr' : .1, 'Ti' : .1, 'O' : .1}, seed=1)atoms_conf = next(iter(frozen_phonons))
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10,5))
abtem.show_atoms(srtio3, ax=ax1)
abtem.show_atoms(atoms_conf, ax=ax2);
ax1.set_title('Symmetric ensemble')
ax2.set_title('Thermal ensemble');atoms_conf = next(iter(frozen_phonons))
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10,5))
abtem.show_atoms(srtio3, ax=ax1)
abtem.show_atoms(atoms_conf, ax=ax2);
ax1.set_title('Symmetric ensemble')
ax2.set_title('Thermal ensemble');Building a potential¶
tds_potential = abtem.Potential(frozen_phonons, gpts=512, slice_thickness=3.905/2,
projection='infinite', parametrization='kirkland')
print(f"Real space sampling: {tds_potential.sampling} Angstrom ")Real space sampling: (0.061015625, 0.061015625) Angstrom
tds_exit_waves = wave.multislice(tds_potential).compute()[########################################] | 100% Completed | 118.36 s
Averaging thermal diffuse scattering calculations.¶
The need for averaging of the thermal diffuse scattering becomes clear in the following figure.
Each atom position has to have many deviations to be averaged out in the center. This is waht makes this process so long and what makes the process obvious for (embarrassingly) parallel methods.
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10,5), sharex=True, sharey=True)
tds_exit_waves[0].show(ax=ax1)
ax1.set_title('Single configuration')
tds_exit_waves.intensity().mean(0).show(ax=ax2)
ax2.set_title('Thermal ensemble')Plot Diffraction Pattern¶
The Kikuchi notebook explained that the source for the Kikuchi pattern formation is inside the sample. Here we have the origin of this source. The thermal diffuse scattering makes the electrons scatter into high angles and that is needed to capture these features in a simulation.
So when you see Kikuchi bands you actually see the effect of phonons.
diffraction_patterns_static = exit_wave.diffraction_patterns(
max_angle="valid", block_direct=True)
tds_diffraction_pattern = tds_exit_waves.diffraction_patterns(
max_angle="valid", block_direct=True)
fig, (ax1, ax2, ax3) = plt.subplots(1, 3, figsize=(10,5), sharex=True, sharey=True)
diffraction_pattern.show(power=.2, cmap='jet', ax=ax1)
ax1.set_title('Symmetric ensemble')
tds_diffraction_pattern[0].show(power=.2, cmap='jet', ax=ax2)
ax2.set_title('Single thermal ensemble')
tds_diffraction_pattern.mean(0).show(ax=ax3, cmap='jet', power=.2, )
ax3.set_title('Averaged thermal ensemble')CBED and Thermal Diffuse Scattering¶
Make the probe¶
The probe has to be on the same grid (matrix, pixels) as the potential, which is ensured with the grid.match function.
# ---- Input ----- #
convergence_angle = 2 # in mrad of half angle
acceleration_voltage = 200e3 # in V
defocus = 40 # in nm
# -----------------#
probe = abtem.Probe(energy=acceleration_voltage, semiangle_cutoff=convergence_angle,
aberrations = {'C10': defocus, 'C30':3e6})# , focal_spread=20)
probe.grid.match(potential)
waves = probe.build().compute()
probe.profiles().show();
waves.show()Symmetric ensemble¶
cbed = probe.multislice(potential).compute()
cbed.show(power=0.25)TDS¶
For the thermal diffuse scattering like above for the symetric ensemble we reuse the potential from the plane wave calculation and just change the probe. The probe was definded in the symmetric enseble calculation above.
The calculation takes about 1 min on my laptop.
center = (tds_potential.extent[0] / 2, tds_potential.extent[1] / 2)
probe.grid.match(tds_potential)
tds_cbed = probe.multislice(potential=tds_potential).compute()
tds_cbed.mean(0).show(power=0.5)Comparison¶
plt.close('all')fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10,5), sharex=True, sharey=True)
cbed.show(power=.5, cmap='jet', ax=ax1)
ax1.set_title('Symmetric ensemble')
tds_cbed.mean(0).show(power=.5, cmap='jet', ax=ax2)
ax2.set_title('Thermal ensemble')Redo this calculation with non overlapping disks in the convergence angle (let’s say 7 mrad at 80keV) You can jump to Make the probe
Summary¶
For a quantitative image simulation we need to do dynamic scattering theory.
The dynamic scattering theory is done within the multislice algorithm that treats each slice like a weak phase object.
Thermal defuse scattering needs to be included into the multislice calculations for a good simulation
The thermal diffuse scattering can be approximated by the frozen phonon approximation but it is computationally intensive.