ASE - The Atomic Simulation Environment#
The Atomic Simulation Environment (ASE) is a versatile, open-source Python toolkit designed to simplify the full lifecycle of atomistic simulations. With ASE, you can easily construct and edit representations of molecules, crystals, and low-dimensional materials. It provides interfaces to a wide range of simulation engines, enabling you to perform calculations at various levels—from quantum mechanical density functional theory to classical molecular dynamics. ASE also streamlines tasks like launching simulations, parsing results, and visualizing structures and trajectories, making it a solid framework for materials modeling and exploratory research.
ASE can interface with a variety of simulation software platforms including VASP, Quantum ESPRESSO, Q-Chem, Gaussian, and others (see the full list) through tools called calculators. ASE can create input files, launch simulations, and parse the output. ASE has a vast set of modules and functions, giving it vast and powerful functionality. We will only scratch the surface in this brief tutorial.
Installation#
To get started with ASE, you first need to install the Python package. If you installed all of the required dependencies at the beginning of this book, you should have the ase
package already installed. Otherwise, you can install it with the command:
pip install ase
Usage#
ASE is designed to simplify atomistic modeling by separating the creation of structures from the actual simulations. A typical ASE workflow involves two main steps:
Creating an
Atoms
object: This represents your molecule or material.Attaching a calculator: This defines the simulation backend to compute energies, forces, or other properties.
Note
Performing atomistic simulations are beyond the scope of this workshop. Here, we will show you the basics, such as how to create and manipulate an Atoms
object. More information about performing simulations using Atoms
and calculator
objects may be found on the ASE documentation page Atoms and Calculators.
Making Atoms
Objects#
The Atoms
object in ASE is the core structure used to represent physical systems. It consists of one or more Atom
objects, with attributes such as symbol
(string), and position
(a 3-element tuple of Cartesian coordinates, in Angstroms). Other atomic properties can be specified, such as mass
, charge
, etc.
Building Simple Molecules#
Let’s start by importing the ase package and building a simple molecule. To build simple molecules, ASE provides a convenient molecule()
function. It allows you to quickly generate common molecules by specifying their chemical formula as a string. The resulting Atoms object can be visualized in 3D, offering an intuitive understanding of molecular geometry.
from ase.build import molecule
from ase.visualize import view
# build an ase.Atoms object to represent a water molecule
water = molecule('H2O')
# make an interactive, 3D visualization of the molecule
view(water, viewer='x3d')
The molecule()
function is provided as a simple way to build an Atoms
object. Here, a molecule is specified using a Python string containing a chemical formula, and only a very limited set of molecules are supported. The list of available molecules is found in the ase.collections.g2.names
list:
from ase.collections import g2
# print the ase.collections.g2.names list
print(g2.names)
['PH3', 'P2', 'CH3CHO', 'H2COH', 'CS', 'OCHCHO', 'C3H9C', 'CH3COF', 'CH3CH2OCH3', 'HCOOH', 'HCCl3', 'HOCl', 'H2', 'SH2', 'C2H2', 'C4H4NH', 'CH3SCH3', 'SiH2_s3B1d', 'CH3SH', 'CH3CO', 'CO', 'ClF3', 'SiH4', 'C2H6CHOH', 'CH2NHCH2', 'isobutene', 'HCO', 'bicyclobutane', 'LiF', 'Si', 'C2H6', 'CN', 'ClNO', 'S', 'SiF4', 'H3CNH2', 'methylenecyclopropane', 'CH3CH2OH', 'F', 'NaCl', 'CH3Cl', 'CH3SiH3', 'AlF3', 'C2H3', 'ClF', 'PF3', 'PH2', 'CH3CN', 'cyclobutene', 'CH3ONO', 'SiH3', 'C3H6_D3h', 'CO2', 'NO', 'trans-butane', 'H2CCHCl', 'LiH', 'NH2', 'CH', 'CH2OCH2', 'C6H6', 'CH3CONH2', 'cyclobutane', 'H2CCHCN', 'butadiene', 'C', 'H2CO', 'CH3COOH', 'HCF3', 'CH3S', 'CS2', 'SiH2_s1A1d', 'C4H4S', 'N2H4', 'OH', 'CH3OCH3', 'C5H5N', 'H2O', 'HCl', 'CH2_s1A1d', 'CH3CH2SH', 'CH3NO2', 'Cl', 'Be', 'BCl3', 'C4H4O', 'Al', 'CH3O', 'CH3OH', 'C3H7Cl', 'isobutane', 'Na', 'CCl4', 'CH3CH2O', 'H2CCHF', 'C3H7', 'CH3', 'O3', 'P', 'C2H4', 'NCCN', 'S2', 'AlCl3', 'SiCl4', 'SiO', 'C3H4_D2d', 'H', 'COF2', '2-butyne', 'C2H5', 'BF3', 'N2O', 'F2O', 'SO2', 'H2CCl2', 'CF3CN', 'HCN', 'C2H6NH', 'OCS', 'B', 'ClO', 'C3H8', 'HF', 'O2', 'SO', 'NH', 'C2F4', 'NF3', 'CH2_s3B1d', 'CH3CH2Cl', 'CH3COCl', 'NH3', 'C3H9N', 'CF4', 'C3H6_Cs', 'Si2H6', 'HCOOCH3', 'O', 'CCH', 'N', 'Si2', 'C2H6SO', 'C5H8', 'H2CF2', 'Li2', 'CH2SCH2', 'C2Cl4', 'C3H4_C3v', 'CH3COCH3', 'F2', 'CH4', 'SH', 'H2CCO', 'CH3CH2NH2', 'Li', 'N2', 'Cl2', 'H2O2', 'Na2', 'BeH', 'C3H4_C2v', 'NO2']
Additionally, by iterating over the atoms in the structure, you can extract and print coordinates, giving a clearer view of the atomic configuration, which is useful for custom analysis or manual editing.
Let’s build a formic acid molecule and print the x, y, and z coordinates of each atom:
# construct a formic acid molecule
atoms = molecule('HCOOH')
# print the symbol and coordinates of each atom.
print('Show atomic coordinates:\n')
for atom in atoms:
x, y, z = atom.position
print(atom.symbol)
print(f' x: {x} Å')
print(f' y: {y} Å')
print(f' z: {z} Å')
Show atomic coordinates:
O
x: -1.040945 Å
y: -0.436432 Å
z: 0.0 Å
C
x: 0.0 Å
y: 0.423949 Å
z: 0.0 Å
O
x: 1.169372 Å
y: 0.103741 Å
z: 0.0 Å
H
x: -0.64957 Å
y: -1.335134 Å
z: 0.0 Å
H
x: -0.377847 Å
y: 1.452967 Å
z: 0.0 Å
We can still make an interactive 3D visualization:
# 3D visualization
view(atoms, viewer='x3d')
Having constructed an ase.Atoms
object to represent water molecule, we could create a calculator
object to run a simulation.
Building Complex Molecules#
For more complex molecular systems, ASE supports importing atomic structures from external sources. You can:
Construct a molecule from a structure file (
*.cif
,*.xyz
, etc.)Read the structure from simulation output
Obtain a structure from a molecular database
Use the ase.io.read()
function to load structures into an Atoms object. This approach is especially powerful when dealing with large, experimentally-derived or computationally-relaxed molecules. You can also export structures to one of many different file formats using the ase.io.write()
function. For more information take a look at the ase.io
documentation.
Building Simple Crystals - Bulk Silicon#
ASE includes tools to create bulk crystals using known crystal structures and lattice parameters. The bulk()
function allows you to specify an element and lattice type (e.g., 'fcc'
, 'bcc'
, 'diamond'
). This returns an Atoms
object with a unit cell that can be visualized or expanded into supercells. Visualizations can be saved as static images using ase.io.write()
, or viewed interactively in 3D. These tools are great for exploring crystal symmetry and preparing inputs for electronic structure calculations.
Now let’s build a periodic Silicon crystal. Silicon crystalizes in a diamond structure, which we can make with the bulk()
function:
from ase.build import bulk
from ase.io import write
# construct bulk silicon
atoms = bulk('Si', 'diamond')
# export visualization as a static PNG image
write('silicon_basis.png', atoms, show_unit_cell=2)
This renders structure as a simple PNG image:

We can also make an interactive 3D viewer:
view(atoms, viewer='x3d')
Building a 2D System - a MXene#
The ase.build
module provides functions for building 2D structures. For example:
graphene_nanoribbon()
may be used to make graphene nanoribbons and graphene sheets.mx2()
may be used to build MXene and transition metal dichalcogenide (TMD) monolayers.
These functions generate primitive unit cells with proper vacuum spacing to simulate surface effects. For simulations using periodic boundary conditions, a vacuum gap in the z-direction avoids artificial interactions between layers. You can also tile these unit cells to form supercells by multiplying the structure in the desired directions, enabling simulations of extended sheets.
Let’s write some code to build the MXene Ti\(_2\)C. Since a structure like this would likely be used in a DFT calculation, and DFT calculations often have periodic boundary conditions, the unit cell features a large air gap to keep separate the main sheet from its images in the z direction. A vacuum of 15 Angstroms should be sufficient:
from ase.build import mx2
from ase.io import write
from ase.visualize import view # 3D interactive image
# This forms a primitive unit cell
Ti2C = mx2('CTi2', vacuum = 15)
We can also expand the unit cell to a supercell by repeating the unit cell in space. To repeat the primitive cell described by the Ti2C
object, we simply multiply Ti2C
by a 3-element tuple. The three integers (nx, ny, nz)
repeat the unit cell along the x, y, and z cell vectors, respectively.
# build sheet supercell
sheet = Ti2C * (3,3,1)
# view the sheet supercell
view(sheet, viewer='x3d', repeat=(4,4,1))
Building a 1D System - a Carbon Nanotube#
ASE makes it easy to construct one-dimensional systems such as carbon nanotubes. The nanotube()
function accepts chiral indices (n, m)
and a desired length to build the corresponding structure. These nanotubes can be visualized and analyzed using the same tools applied to molecules and crystals. ASE handles periodicity in the tube axis direction, allowing realistic modeling of extended nanowires or tubes.
from ase.build import nanotube
from ase.io import write # helps us save an image
from ase.visualize import view # 3D interactive image
# Build a carbon nanotube
atoms = nanotube(n=6, m=0, length=4)
# Save a static image of the nanotube
orientation='12y,-15z'
write('nanotube.png', atoms, show_unit_cell=2, rotation=orientation)
The static image of the nanotube is given below.

An interactive 3D image of the carbon nanotube is shown below.
view(atoms, viewer='x3d')
Building Complex Crystals#
For materials that can’t be easily built using ASE’s built-in functions, the same file-based approach used for complex molecules applies. Use .cif
files or simulation outputs to represent experimental or computational structures. These files can be imported using ase.io.read()
, and edited further if needed. This method supports the exploration of non-standard or large-scale structures and is critical for realistic simulations in materials research.
Exercises#
Exercise 1: Form a nitrogen-vacancy center in a diamond crystal.
The nitrogen vacancy (NV) center in diamond is a point defect that can support a room-temperature qubit system. Write Python code to form a NV center in a diamond supercell, by doing the following:
Define a diamond primitive unit cell using
ase.build.bulk()
.Use the primitive unit cell to form a supercell that is at least a three-by-three-by-three supercell.
Make a nitrogen substitution by swapping one C atom for a N atom.
Remove a C atom adjacent to the N substitution.
Provide a static and an interactive visualization of your crystal.
Below is one example your result could resemble. Here, I have performed a substitution on the atom at the origin, and I have removed an adjacent atom.
Hints:
You can substitute the \(k\)-th atom of an
ase.Atoms
object simply by reassigning its atomic symbol. For example, given anAtoms
objectsi_crystal
representing a pristine Si crystal, we can transform the \(k\)-th atom to a C atom using code like this:
si_crystal[k].symbol = 'C'
See the (documentation) for the
ase.Atoms
class to learn how to delete atoms from anAtoms
object.
A successfully-formed NV center would still require structural optimization.
Solutions#
Show code cell content
from ase.build import bulk
diamond = bulk('C', 'diamond')
crystal = diamond*(3,3,3)
crystal[0].symbol = 'N' # it's in-silico alchemy!
del crystal[1]
"""
Static visualization.
"""
orientation='12y,-15z'
write('nv_center.png', crystal, show_unit_cell=2, rotation=orientation)
# Interactive 3D visualization
view(crystal, viewer='x3d')