Atomic models with ASE

abTEM uses the Atomic Simulation Environment (ASE) for creating model structures. ASE is a set of tools and Python modules for setting up, manipulating, visualizing and analyzing atomistic simulations. It is also used in conjunction with other simulation codes such as GPAW for running DFT simulations. Here we will introduce the features of ASE needed for image simulations; please refer to the ASE documentation for a general introduction.


The Atoms object defines a collection of atoms. Here is how to set up an \(\mathrm{N}_2\) molecule by directly specifying the position of the two nitrogen atoms (in units of Ångstrom):

from ase import Atoms
n2 = Atoms('2N', positions=[(0., 0., 0.), (0., 0., 1.10)], cell=[2, 2, 2])

abTEM uses just the positional coordinates, atomic numbers, and the unit cell properties for creating electrostatic potentials.

array([[0. , 0. , 0. ],
       [0. , 0. , 1.1]])
array([7, 7])
Cell([2.0, 2.0, 2.0])


ASE can import all the common atomic structure formats, see a full list here. Below we import a .cif-file defining a unit cell of strontium titanate (SrTiO3).

from import read
srtio3 = read('data/srtio3_100.cif')


The simplest way to visualize the atoms is the view function, which should bring up an interactive 3d viewer:

from ase.visualize import view
view(srtio3) # may not work in remote environments (e.g. binder)

If you are working in a remote environment or prefer to embed the viewer in the notebook, you can install nglview. This will allow you to use the nglview backend for the view function.

view(srtio3, viewer='nglview')

The atomic structure can be centered with the ASE .center() method and visualized inline in a Jupyter Notebook using the show_atoms function. This function shows a 2D projection of the structure perpendicular to the specified plane. For example:

from abtem.visualize import show_atoms

show_atoms(srtio3, plane='xy');


abTEM always assumes that the imaging electrons propagate along the \(z\)-axis in the direction from negative to positive coordinate values. Hence, to choose the imaging zone axis, we need to manipulate the atoms so they are properly aligned.

ASE has a range of tools for manipulating imported structures, for example, the surface function, which is used for creating a periodic surface (aligned with the \(z\)-axis) with a given set of Miller indices.

from import surface

srtio3_110 = surface(srtio3, indices=(1, 1, 0), layers=4, periodic=True)

show_atoms(srtio3_110, plane='xy');
Cell([5.522503961066936, 3.905, 11.04500792213387])

Simulations may require a larger crystal, to repeat the atoms by two in the \(x\)- and by three in the \(y\)-direction (but not in the \(z\)-direction) we can write:

srtio3_110 *= (2, 3, 2)

Next, we center the atoms in the cell and add 3 Å of vacuum at the entrance and exit surfaces along the \(z\)-axis. Note that we visualize the structure now from the side, both along the \(y\) and \(x\) axes.

import matplotlib.pyplot as plt, vacuum=3)

fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10,4))
show_atoms(srtio3_110, plane='xz', ax = ax1)
show_atoms(srtio3_110, plane='yz', ax = ax2)
<AxesSubplot:xlabel='y [Å]', ylabel='z [Å]'>

The manipulated structure can also be exported to disk.

from import write

write('data/srtio3_110.cif', srtio3_110)

Procedural creation

ASE also includes modules for procedurally generating special structures such as carbon nanotubes and nanoparticles. Here we create a chirality \((6,0)\) nanotube (length=1 here indicates the minimal commensurate unit cell along the tube axis).

from import nanotube
cnt = nanotube(6, 4, length=1)


As shown above, the electron propagation axis is along the length of the nanotube (\(z\)), which is probably not what you would like for an image simulation. To rotate the nanotube we use the ASE rotate method with the angle given in degrees and the rotation axis chosen to be \(x\). Note that we also want to rotate the unit cell along with the structure.

cnt.rotate(-90, 'x', rotate_cell=True), axis=(0,1))


General Bravais lattice

The ASE lattice module provides a general framework for creating Atoms objects based on a Bravais lattice and a basis. Here we create a class for making graphene.

from ase.lattice.hexagonal import HexagonalFactory

class GrapheneFactory(HexagonalFactory):
    'A factory for creating graphene lattices.'
    xtal_name = 'graphene'
    bravais_basis = [[0, 0, 0], [1.0 / 3., 2.0 / 3.0, 0.]]

Graphene = GrapheneFactory()

graphene = Graphene('C', latticeconstant={'a' : 2.46, 'c' : 6})


The minimal unit cell of graphene is hexagonal, whereas the multislice algorithm requires an orthogonal unit cell. To make the unit cell orthogonal, we can use the abTEM orthogonalize_cell function. It is not always possible to make a periodic cell orthogonal without introducing some strain, hence we can return the strain tensor. Hexagonal cells can be made orthogonal by doubling it size without introducing strain, hence the strain tensor is zero.

from abtem.structures import orthogonalize_cell

orthogonal_graphene, strain = orthogonalize_cell(graphene, return_strain=True)

[[0. 0.]
 [0. 0.]]