Chapter 3: Imaging
3.2. Resolution Limit#
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.
3.2.1. Load important packages#
3.2.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
print('done')
done
3.2.1.2. Load Packages#
We will use
a Bessel function from the scipy.special package as well as
numpy and matplotlib (installed with magic comand %pylab.)
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
if 'google.colab' in sys.modules:
from google.colab import output
output.enable_custom_widget_manager()
import scipy.special
import pyTEMlib.kinematic_scattering as ks
You don't have igor2 installed. If you wish to open igor files, you will need to install it (pip install igor2) before attempting.
You don't have gwyfile installed. If you wish to open .gwy files, you will need to install it (pip install gwyfile) before attempting.
Symmetry functions of spglib enabled
Using kinematic_scattering library version {_version_ } by G.Duscher
3.2.2. Distinguish Features#
The Intensity of the Fraunhofer diffraction pattern of a circular aperture (the Airy pattern) is given by the squared modulus of the Fourier transform of the circular aperture:
with
\(J_1\) being the Bessel Function of the first kind of order one,
\(k = {1}/{\lambda}\) is the wavenumber,
\(a\) is the radius of the aperture, and
\(\theta\) is the angle of observation, i.e. the angle between the axis of the circular aperture and the line between aperture center and observation point.
\(x = ka \sin \theta \)
The electron probe of a STEM is in first order an Airy pattern.
Below, we plot the normalized Airy pattern where we set \(I_0\) to 1.
Please, change the seperation
value to see the effect.
# ------Input ------
separation = 3.5
# ------------------
# The x-values we want
x = x1 = np.linspace(-15, 15, 400)
# The normalized intensity: I_0 = 1
norm_I = 4 * (scipy.special.j1(x1) / x1)**2
norm_I_shifted = 4 * (scipy.special.j1(x1-separation) / (x1-separation))**2
plt.figure()
plt.plot(x, norm_I, label='Airy 1')
plt.plot(x, norm_I_shifted, label='Airy 2')
plt.plot(x, norm_I_shifted+norm_I, label='sum')
plt.xlabel('$x$')
plt.ylabel('$I(x)/I_0$')
plt.legend();
3.2.3. Point Resolution#
The Rayleigh Criterion gives us the point resolution, which is the distance two objects have to be seperated to be distinguished (without prior knowledge of the shape). This Rayleigh criterion is based on the first zero fo the Bessel function \(J_1(x)\), which occurs at \(x+0 = k a \sin \theta ≈ 3.8317\).
This will give the well know form of the Rayleigh Criterion for the angular resolution limit \(\theta_R\):
with:
\(\lambda\): wavelength
\(a\): aperture radius
\(d\): aperture diameter
If one knows the shape and/or size of the objects one can determine features seperated less than the resolution limit. This is known as the information limit.
acceleration_voltage = 200*1e3 # in eV
d = 1 # in 1/nm
lambda_e = ks.get_wavelength(acceleration_voltage)
theta_R = lambda_e/d
print(f'angular resolution {theta_R*1000:.1f} mrad')
angular resolution 25.1 mrad
3.2.4. In 2D#
Please note that I use quite a low gamma value, set gamma to one and see what happens.
I set the distance (shift parameter) at the Rayleigh criterium, please change the shift value (by adding and subtracting a number (-3 to 5) to see what happens.
separation = 3.8317 + 0.0
[x,y] = np.meshgrid(x1,x1);
rr1 = np.sqrt((x+separation/2)**2+y**2)
rr2 = np.sqrt((x-separation/2)**2+y**2)
I1 = 4 * (scipy.special.j1(rr1) / rr1)**2
I2 = 4 * (scipy.special.j1(rr2) / rr2)**2
plt.figure()
gamma = 0.0001
plt.imshow(np.log2(gamma+I2+I1)); # please note that I use quite a high gamma value, set gamma to one and see.
3.2.5. Composite figure#
Let’s put all the above together in one plot.
separation = [2,3.8317,5] # list of shit values
text= [' unresolved', ' Rayleigh Criterion', ' resolved']
gamma = 0.03 # gamma value for 2D plots
fig,ax = plt.subplots(3,2, figsize=(9,6))
for i in range(3):
shift = separation[i]
## image
rr1 = np.sqrt((x+shift/2)**2+y**2)
rr2 = np.sqrt((x-shift/2)**2+y**2)
I1 = 4 * (scipy.special.j1(rr1) / rr1)**2
I2 = 4 * (scipy.special.j1(rr2) / rr2)**2
ax[i,0].imshow(np.log2(gamma+I2+I1)[100:300,:], extent =[x1[0],x1[-1],x1[100],x1[300]])
ax[i,0].text(x1[0],x1[300],f'shift = {shift:.2f}', color = 'yellow', verticalalignment = 'top');
ax[i,0].set_ylabel('y')
## intensity plot
norm_I1 = 4 * (scipy.special.j1((x1+shift/2)) / (x1+shift/2))**2
norm_I2 = 4 * (scipy.special.j1((x1-shift/2)) / (x1-shift/2))**2
ax[i,1].plot(x1, norm_I1, label='Airy 1')
ax[i,1].plot(x1, norm_I2, label='Airy 2')
ax[i,1].plot(x1, norm_I1+norm_I2, label='sum')
ax[i,1].set_xlim(-10,10)
_,ymax = ax[i,1].get_ylim()
ax[i,1].text(-10,ymax*.95,text[i], color = 'black', verticalalignment = 'top');
ax[i,1].set_ylabel('$I(x)/I_0$')
ax[i,1].legend();
if i<2:
ax[i,0].xaxis.set_ticklabels([])
ax[i,1].xaxis.set_ticklabels([])
plt.tight_layout();
ax[2,0].set_xlabel('$x$')
ax[2,1].set_xlabel('$x$');
3.2.6. Summary#
Here we visualized the Rayleigh Criterion.
Remember Resolution and Magnification are NOT related