Reading and Writing Atoms

Overview

Teaching: 25 min
Exercises: 15 min
Questions
  • How do I read/write structure(s) from/to a file?

  • Which file formats does ASE support?

  • How do I visualise a sequence of structures?

Objectives
  • Read in a structure from a file

  • Write a structure to a file

  • Find further information about ASE read and write functions

  • Generate and write out a sequence of structures

  • Use NumPy slicing to read in a sequence of structures

  • Visualise a sequence of structures

Code connection

In this episode we explore the ase.io module, which contains functions for reading and writing Atoms objects.

ASE can read a variety of file formats

Downloading structures

The Materials Project (MP) contains over 150,000 entries. To obtain a .cif through a browser window navigate to an entry and use the “export as” button on the structure visualiser. Alternatively, we have uploaded a small selection of MP structures which you can download using wget. For example, you can run the following command in your Jupyter Notebook to get the ZnS.cif file:

%%bash 

wget https://raw.githubusercontent.com/ASE-Workshop-2023/tutorial/gh-pages/data/ZnS.cif

There is also a Materials Project Application Programming Interface (API) for accessing structures (and more!) programmatically.

import ase.io
from pathlib import Path
from ase.visualize import view

imported_crystal = ase.io.read("./files/ZnS.cif", format='cif')

view(imported_crystal, viewer='ngl')

image of ZnS structure

vim "./files/ZnS.cif"

ASE can write a variety of file formats

ase.io.write('geometry.in', imported_crystal, scaled=True)
cat geometry.in
#=======================================================
# FHI-aims file: geometry.in
# Created using the Atomic Simulation Environment (ASE)
# Wed Apr 12 11:18:21 2023
#=======================================================
lattice_vector 5.3873657499999998 0.0000000000000000 0.0000000000000000 
lattice_vector 0.0000000000000000 5.3873657499999998 0.0000000000000000 
lattice_vector 0.0000000000000000 0.0000000000000000 5.3873657499999998 
atom_frac 0.0000000000000000 0.0000000000000000 0.0000000000000000 Zn
atom_frac 0.5000000000000000 0.5000000000000000 0.0000000000000000 Zn
atom_frac 0.5000000000000000 0.0000000000000000 0.5000000000000000 Zn
atom_frac 0.0000000000000000 0.5000000000000000 0.5000000000000000 Zn
atom_frac 0.2500000000000000 0.2500000000000000 0.2500000000000000 S
atom_frac 0.2500000000000000 0.7500000000000000 0.7500000000000000 S
atom_frac 0.7500000000000000 0.7500000000000000 0.2500000000000000 S
atom_frac 0.7500000000000000 0.2500000000000000 0.7500000000000000 S

Finding information

More information about the read and write supported formats can be found here. A summary can be produced from the command-line

%%bash
ase info --formats
ase.io.write?
ase.io.write(
   filename: Union[str, pathlib.PurePath, IO],
       images: Union[ase.atoms.Atoms, Sequence[ase.atoms.Atoms]],
   format: str = None,
   parallel: bool = True,
   append: bool = False,
   \*\*kwargs: Any,
) -> None

Exercise: Converting structures

Import a structure file relevant to your own research, and write it to a different format. See what keywords are available for your favourite formats; for example, VASP users are likely to be interested in using
vasp5=True.

Some I/O formats support a sequence of atoms

atoms = imported_crystal

atoms_sequence = []
num_frames = 10

for frame in range(num_frames):
    atoms.rattle(stdev=0.02, seed=frame)  # rattle modifies the atoms in-place
    atoms_sequence.append(atoms.copy())
    
ase.io.write('ZnS_rattle.xyz', atoms_sequence, format='extxyz')

Discussion

In this example, why do we need to make a new copy of Atoms in each step of the loop?

List slicing can be used to select a sequence of frames

atoms_frame = ase.io.read('ZnS_rattle.xyz')
type(atoms_frame)
ase.atoms.Atoms
atoms_all_frames = ase.io.read('ZnS_rattle.xyz', index=':')
print(type(atoms_all_frames))
print(len(atoms_all_frames))
list
10
atoms_alternating = ase.io.read('ZnS_rattle.xyz', index='::2')
print(type(atoms_all_frames))
print(len(atoms_all_frames))
<class 'list'>
5

From the horse’s mouth

You can read about the index parameter and more by inspecting the documentation of this function.

To visualise a sequence of frames use the ASE GUI or write an animation

view(atoms_alternating, viewer='ngl')
ase.io.write('rattle.gif', atoms_sequence)

Key Points

  • ASE can read a variety of file formats

  • ASE can write a variety of file formats

  • There are lots of read and write related functions in modules under ase.io

  • Some I/O formats support a sequence of Atoms

  • List slicing can be used to select a sequence of frames

  • To visualise a sequence of frames use the ASE GUI or write an animation