HRTEM quickstart#

This notebook demonstrates a basic HRTEM simulation of a double-walled carbon nanotube.

Configuration#

We start by (optionally) setting our configuration. See documentation for details.

abtem.config.set(
    {
        "device": "cpu",
        "fft": "fftw",
        "diagnostics.task_progress": False,
        "diagnostics.progress_bar": False,
    }
);

Atomic model#

In this section we create the atomic model. See our walkthough or our tutorial on atomic models.

We create two nanotubes with different radii using the nanotube function from ASE.

tube1 = ase.build.nanotube(10, 0, length=5)

tube2 = ase.build.nanotube(16, 0, length=5)

# combine the two nanotubes into a single structure
tubes = tube1 + tube2

# add vacuum in the x- and y-direction
tubes.center(vacuum=4.0, axis=(0, 1))

abtem.show_atoms(tubes, plane="xy", title="Beam view");
../../../_images/e55975a36fd24fea9262ec2beb2e007821e119855160263707063ada8676a724.png

We rotate the nanotubes such the beam travels perpendicular to the length of the nanotubes.

rotated_tubes = tubes.copy()

# rotate cell and atoms by 90 degrees around y
rotated_tubes.rotate("y", 90, rotate_cell=True)

# standardize unit cell (done automatically in an abTEM simulation)
rotated_tubes = abtem.standardize_cell(rotated_tubes)

fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 4))
abtem.show_atoms(rotated_tubes, plane="xy", ax=ax1, title="Beam view")
abtem.show_atoms(rotated_tubes, plane="yz", ax=ax2, title="Side view");
../../../_images/f4374e173eb3595ee24ef73fcad94ebe9969d9de0049dc808a030d84847e12c3.png

Potential#

We create an ensemble of potentials using the frozen phonon model. See our walkthrough on frozen phonons.

frozen_phonons = abtem.FrozenPhonons(rotated_tubes, 32, sigmas=0.1)

We create a potential from the frozen phonons model, see walkthrough on potentials.

potential = abtem.Potential(
    frozen_phonons,
    sampling=0.05,
    projection="infinite",
    slice_thickness=1,
)

Wave function#

We create a plane wave function at an energy of 100 keV. See our walkthrough on wave functions.

wave = abtem.PlaneWave(energy=100e3)

Multislice#

We run the multislice algorithm to calculate the exit waves, see our walkthrough on multislice.

exit_wave = wave.multislice(potential)
exit_wave.compute();

Contrast transfer function#

We create a contrast transfer function of the objective lens, see our walkthrough on the contrast transfer function

Cs = -8e-6 * 1e10  # spherical aberration (-8 um)

ctf = abtem.CTF(Cs=Cs, energy=wave.energy, defocus="scherzer")

print(f"defocus = {ctf.defocus:.2f} Å")
defocus = -66.65 Å
ctf.semiangle_cutoff = ctf.crossover_angle

We include partial coherence in the quasi-coherent approximation. For more accurate descriptions of partial coherence, see our tutorial on partial coherence.

Cc = 1.0e-3 * 1e10  # chromatic aberration (1.2 mm)
energy_spread = 0.35  # standard deviation energy spread (0.35 eV)

focal_spread = Cc * energy_spread / exit_wave.energy

incoherent_ctf = ctf.copy()
incoherent_ctf.focal_spread = focal_spread

We show a profiles of the contrast transfer functions.

fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5))
ctf.profiles().show(ax=ax1)
incoherent_ctf.profiles().show(ax=ax2, legend=True);
../../../_images/8bba90f453617dd62b3c25a53c209975ba2b37fc031e3d42dd1a3651b4b57596.png

We apply the contrast transfer function, then calculate the intensities of the wave functions.

measurement_ensemble = exit_wave.apply_ctf(incoherent_ctf).intensity()

measurement_ensemble.shape
(32, 426, 411)

The result is an ensemble of images, one for each frozen phonon, we average the ensemble to obtain the final image.

measurement = measurement_ensemble.mean(0)

measurement.show();
../../../_images/561636e73c8428400beb88006e062db376406207fc148a083cf4c25c47d9c3de.png

Postprocessing#

Many tasks requires additional post-processing steps. Below we apply Poisson noise to simulate the shot noise of a given finite electron dose.

noisy_measurement = measurement.poisson_noise(dose_per_area=1e4)

noisy_measurement.show()
<abtem.visualize.visualizations.Visualization at 0x1d5762650>
../../../_images/f577a55befc0216e804601e4174aea6e2744692eae6c70de4cf1ff8f07aa54f8.png

Showing the results as a line profile often provides a better sense of relative intensities.

start = (0, 0)
end = (0, potential.extent[1])

measurement.interpolate_line(start, end).show();
../../../_images/cd399a982665102614fbf766b940c8ba6f104fb7eaf5ba87b4d5da4e5216fcf6.png

Hide code cell content

# this cell produces a thumbnail for the online documentation

visualization = measurement.show()
visualization.axis(mode="none", spines=False)
plt.savefig("../thumbnails/hrtem_quickstart.png", bbox_inches="tight", pad_inches=0)
../../../_images/d98c86b885203344f43a229aef70d74ecdcce135e388acdecd40c9e55b5ae686.png