Chapter 3: Imaging
3.9. Thermal-Diffuse Scattering#
part of
MSE672: Introduction to Transmission Electron Microscopy
Spring 2024
Gerd Duscher | Khalid Hattar |
Microscopy Facilities | Tennessee Ion Beam Materials Laboratory |
Materials Science & Engineering | Nuclear Engineering |
Institute of Advanced Materials & Manufacturing | |
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
3.9.1. Load important packages#
In Colab, the next code cell must be run first
3.9.1.1. Check Installed Packages#
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
3.9.1.2. 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'
3.9.2. 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
3.9.2.1. Defining the structure#
Here we make a SrTiO\(_3\) 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);
Simulation cell: Cell([31.24, 31.24, 702.9])
3.9.2.2. 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)
3.9.2.3. Multislice#
exit_wave = wave.multislice(potential)
exit_wave.compute()
exit_wave.show()
[########################################] | 100% Completed | 20.33 s
<abtem.visualize.MeasurementVisualization2D at 0x1d3e33fa8d0>
3.9.2.4. 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')
3.9.3. 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.
3.9.3.1. 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 \(x_{RMS}\) 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 \(x_{RMS}\) for most materials is about 0.1Å:
The relation of the Debey-Waller Factors in the literature to the deviation of position \(x_{RMS} = \sqrt{B/(8\pi^2)}\).
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');
3.9.3.2. 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 | 62.66 s
3.9.3.3. 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')
Text(0.5, 1.0, 'Thermal ensemble')
3.9.3.4. 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')
Text(0.5, 1.0, 'Averaged thermal ensemble')
3.9.4. CBED and Thermal Diffuse Scattering#
3.9.4.1. 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()
[########################################] | 100% Completed | 105.51 ms
[########################################] | 100% Completed | 205.21 ms
<abtem.visualize.MeasurementVisualization2D at 0x1d3e4b35450>
3.9.4.2. Symmetric ensemble#
cbed = probe.multislice(potential).compute()
cbed.show(power=0.25)
[########################################] | 100% Completed | 17.85 ss
<abtem.visualize.MeasurementVisualization2D at 0x1d3ea9a5550>
3.9.4.3. 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.25)
[########################## ] | 66% Completed | 53.63 sms
3.9.4.4. Comparison#
plt.close('all')
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10,5), sharex=True, sharey=True)
cbed.show(power=.3, cmap='jet', ax=ax1)
ax1.set_title('Symmetric ensemble')
tds_cbed.mean(0).show(power=.3, cmap='jet', ax=ax2)
ax2.set_title('Thermal ensemble')
Text(0.5, 1.0, '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
3.9.5. 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.