Introduction to atomman: Defining atomic systems

Lucas M. Hale, lucas.hale@nist.gov, Materials Science and Engineering Division, NIST.

Disclaimers

1. Introduction

This Notebook provides an introduction to creating and manipulating atomistic systems using atomman.

Library Imports

[1]:

# Standard Python libraries
import os
from copy import deepcopy
import datetime

# http://www.numpy.org/
import numpy as np

# https://github.com/usnistgov/atomman
import atomman as am
import atomman.unitconvert as uc

# Show atomman version
print('atomman version =', am.__version__)

# Show date of Notebook execution
print('Notebook executed on', datetime.date.today())

atomman version = 1.4.10
Notebook executed on 2023-07-28


2. Create a System

Atomic systems are represented with three Python objects: Box, Atoms, and System.

2.1. Box

The Box class defines a generic parallelepiped for the system’s boundaries.

See the 1.1. Box class Jupyter Notebook for more detailed information and options.

2.1.1. Create Box

Boxes can be defined using a variety of formats. Here, we’ll define a box using lattice parameters.

[2]:

# a = 4.05 angstroms (Al fcc lattice constant)
a = uc.set_in_units(4.05, 'angstrom')

# Create cubic box
box = am.Box.cubic(a=a)


2.1.2. Access Box properties

The string of Box shows four Cartesian vectors: the three box vectors (avect, bvect, and cvect) and the origin position.

[3]:

print(box)

avect =  [ 4.050,  0.000,  0.000]
bvect =  [ 0.000,  4.050,  0.000]
cvect =  [ 0.000,  0.000,  4.050]
origin = [ 0.000,  0.000,  0.000]


The parameters of the Box can also be retrieved in a variety of formats. Some examples are shown here.

[4]:

print('a =', box.a)
print('b =', box.b)
print('c =', box.c)
print('alpha =', box.alpha)
print('beta  =', box.beta)
print('gamma =', box.gamma)

a = 4.05
b = 4.05
c = 4.05
alpha = 90.0
beta  = 90.0
gamma = 90.0


2.2. Atoms

The Atoms class collects per-atom properties that can be freely assigned. The only limitations are that the number of atoms is constant, and that values for the per-atom properties must be given for all atoms.

See the 1.2. Atoms class Jupyter Notebook for more detailed information and options.

2.2.1. Create Atoms

When initializing an Atoms object, all keyword arguments (except natoms and prop) will be interpreted as property names. Values for each property must be given for each atom, or only one value given in which case all atoms will be assigned the same value.

By default, each Atoms instance has two per-atom properties:

• atype is an integer atomic type. If not given, atype will be set to 1 for all atoms.

• pos is the 3D vector position. If not given, pos will be set to [0,0,0] for all atoms.

[5]:

# Create Atoms for an fcc unit cell
atype = 1
pos = [[0.0, 0.0, 0.0],
[0.5, 0.5, 0.0],
[0.5, 0.0, 0.5],
[0.0, 0.5, 0.5]]

atoms = am.Atoms(atype=atype, pos=pos)


2.2.2. Accessing Atoms properties

The string of Atoms shows id, atype, and pos for all atoms.

[6]:

print(atoms)

per-atom properties = ['atype', 'pos']
id   atype  pos[0]  pos[1]  pos[2]
0       1   0.000   0.000   0.000
1       1   0.500   0.500   0.000
2       1   0.500   0.000   0.500
3       1   0.000   0.500   0.500


The per-atom properties can be accessed as attributes of the object.

[7]:

print('atoms.atype ->', atoms.atype)
print('atoms.pos[2] ->', atoms.pos[2])

atoms.atype -> [1 1 1 1]
atoms.pos[2] -> [0.5 0.  0.5]


The per-atom properties can also be converted into a flat pandas.DataFrame with the df() method.

[8]:

atoms.df()

[8]:

atype pos[0] pos[1] pos[2]
0 1 0.0 0.0 0.0
1 1 0.5 0.5 0.0
2 1 0.5 0.0 0.5
3 1 0.0 0.5 0.5

2.3. System

The System class gives a full representation of an atomic configuration by combining an atoms instance, a box instance, periodic boundary condition settings, and a list of element symbols.

See the 1.3. System class Jupyter Notebook for more detailed information and options.

2.3.1. Create system

A System is created by combining its components:

• atoms is an Atoms object.

• box is a Box object.

• pbc is a list of three bools, where True indicates the box is periodic along the corresponding box vector.

• symbols is a list of element model symbols for each unique atype value.

• scale is a bool that indicates if atoms.pos are to be scaled relative to the box vectors.

[9]:

system = am.System(atoms=atoms, box=box, pbc=[True, False, True], symbols='Al', scale=True)
print(system)

avect =  [ 4.050,  0.000,  0.000]
bvect =  [ 0.000,  4.050,  0.000]
cvect =  [ 0.000,  0.000,  4.050]
origin = [ 0.000,  0.000,  0.000]
natoms = 4
natypes = 1
symbols = ('Al',)
pbc = [ True False  True]
per-atom properties = ['atype', 'pos']
id   atype  pos[0]  pos[1]  pos[2]
0       1   0.000   0.000   0.000
1       1   2.025   2.025   0.000
2       1   2.025   0.000   2.025
3       1   0.000   2.025   2.025


2.3.2. View system

You can view a system from within a Jupyter environment. This is useful for taking a quick peak at smaller structures to check that they are correct.

Caution: plotting methods are still preliminary and the method calls are likely to change!

[10]:

am.plot.py3Dmol.view_3d(system, width=400, height=400)


You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
jupyter labextension install jupyterlab_3dmol

Systems can also be created by loading in atomic configurations from either

• files of many different formats,

• representations of configurations used by other Python packages, or

• records stored in the potentials database.

See the 1.4. Load and dump conversions Jupyter Notebook for more detailed information on all the different available styles and options.

Create content in POSCAR format

[11]:

fcc_poscar = """POSCAR for fcc Al standard unit cell
1.0
4.05 0.00 0.00
0.00 4.05 0.00
0.00 0.00 4.05
Al
4
direct
0.00 0.00 0.00
0.50 0.50 0.00
0.50 0.00 0.50
0.00 0.50 0.50"""


• style (str) indicates the format of the content being loaded.

• content (any) the content to be loaded. For text formats, can be a str of the content, a file path or a file-like object.

• **kwargs (any) any extra style-specific keyword arguments.

[12]:

new_system = am.load('poscar', fcc_poscar)
print(new_system)

avect =  [ 4.050,  0.000,  0.000]
bvect =  [ 0.000,  4.050,  0.000]
cvect =  [ 0.000,  0.000,  4.050]
origin = [ 0.000,  0.000,  0.000]
natoms = 4
natypes = 1
symbols = ('Al',)
pbc = [ True  True  True]
per-atom properties = ['atype', 'pos']
id   atype  pos[0]  pos[1]  pos[2]
0       1   0.000   0.000   0.000
1       1   2.025   2.025   0.000
2       1   2.025   0.000   2.025
3       1   0.000   2.025   2.025


The potentials database stores structure information for many crystal prototypes, i.e. space group + wykoff sites without elemental information. Crystals can easily be created from this by loading the prototype and specifying the element(s) and the necessary lattice constants.

[13]:

potdb = am.library.Database()

[14]:

print(potdb.local_database)

database style local at C:\Users\lmh1\Documents\library

[15]:

new_system = am.load('prototype', name='A1--Cu--fcc', a=4.05, symbols='Al')
print(new_system)

avect =  [ 4.050,  0.000,  0.000]
bvect =  [ 0.000,  4.050,  0.000]
cvect =  [ 0.000,  0.000,  4.050]
origin = [ 0.000,  0.000,  0.000]
natoms = 4
natypes = 1
symbols = ('Al',)
pbc = [ True  True  True]
per-atom properties = ['atype', 'pos']
id   atype  pos[0]  pos[1]  pos[2]
0       1   0.000   0.000   0.000
1       1   0.000   2.025   2.025
2       1   2.025   0.000   2.025
3       1   2.025   2.025   0.000


The potentials database also contains relaxed crystal structures for the hosted interatomic potentials. If you know which potential you want to use, then you can load the appropriate relaxed structure.

[16]:

new_system = am.load('crystal', family='A1--Cu--fcc', potential_LAMMPS_id='2003--Zope-R-R--Al--LAMMPS--ipr1')
print(new_system)

avect =  [ 4.050,  0.000,  0.000]
bvect =  [ 0.000,  4.050,  0.000]
cvect =  [ 0.000,  0.000,  4.050]
origin = [ 0.000,  0.000,  0.000]
natoms = 4
natypes = 1
symbols = ('Al',)
pbc = [ True  True  True]
per-atom properties = ['atype', 'pos']
id   atype  pos[0]  pos[1]  pos[2]
0       1   0.000   0.000   0.000
1       1   0.000   2.025   2.025
2       1   2.025   0.000   2.025
3       1   2.025   2.025   0.000


4. System Manipulations

The System class was defined to make it easy for users to manipulate systems and atomic properties. Additionally, there are a few System class methods that assist with some common manipulations.

See the 1.3. System class Jupyter Notebook for more detailed information and options.

4.1. System.rotate()

The rotate() method transforms the system such that the new box vectors correspond to three integer [uvw] crystal vectors with respect to the current box vectors. This ensures that atomic compatibilities across all periodic boundaries.

[17]:

# Rotate system to crystal vectors [110], [-110], [001]
uvws = [[ 1, 1, 0],
[-1, 1, 0],
[ 0, 0, 1]]
system = system.rotate(uvws)

# Show system is transformed and expanded
print(system)

avect =  [ 5.728,  0.000,  0.000]
bvect =  [ 0.000,  5.728,  0.000]
cvect =  [ 0.000,  0.000,  4.050]
origin = [ 0.000,  0.000,  0.000]
natoms = 8
natypes = 1
symbols = ('Al',)
pbc = [ True  True  True]
per-atom properties = ['atype', 'pos']
id   atype  pos[0]  pos[1]  pos[2]
0       1   5.728   2.864   4.050
1       1   0.000   0.000   4.050
2       1   2.864   5.728   4.050
3       1   1.432   1.432   2.025
4       1   1.432   4.296   2.025
5       1   2.864   2.864   4.050
6       1   4.296   1.432   2.025
7       1   4.296   4.296   2.025


4.2. System.supersize()

A larger System (i.e. supercell) can be generated using the supersize() method

[18]:

# Make system a 2x2x2 supercell of itself
system = system.supersize(2, 2, 2)

print(system)

avect =  [11.455,  0.000,  0.000]
bvect =  [ 0.000, 11.455,  0.000]
cvect =  [ 0.000,  0.000,  8.100]
origin = [ 0.000,  0.000,  0.000]
natoms = 64
natypes = 1
symbols = ('Al',)
pbc = [ True  True  True]
per-atom properties = ['atype', 'pos']
id   atype  pos[0]  pos[1]  pos[2]
0       1   5.728   2.864   4.050
1       1   0.000   0.000   4.050
2       1   2.864   5.728   4.050
3       1   1.432   1.432   2.025
4       1   1.432   4.296   2.025
5       1   2.864   2.864   4.050
6       1   4.296   1.432   2.025
7       1   4.296   4.296   2.025
8       1  11.455   2.864   4.050
9       1   5.728   0.000   4.050
10       1   8.591   5.728   4.050
11       1   7.159   1.432   2.025
12       1   7.159   4.296   2.025
13       1   8.591   2.864   4.050
14       1  10.023   1.432   2.025
15       1  10.023   4.296   2.025
16       1   5.728   8.591   4.050
17       1   0.000   5.728   4.050
18       1   2.864  11.455   4.050
19       1   1.432   7.159   2.025
20       1   1.432  10.023   2.025
21       1   2.864   8.591   4.050
22       1   4.296   7.159   2.025
23       1   4.296  10.023   2.025
24       1  11.455   8.591   4.050
25       1   5.728   5.728   4.050
26       1   8.591  11.455   4.050
27       1   7.159   7.159   2.025
28       1   7.159  10.023   2.025
29       1   8.591   8.591   4.050
30       1  10.023   7.159   2.025
31       1  10.023  10.023   2.025
32       1   5.728   2.864   8.100
33       1   0.000   0.000   8.100
34       1   2.864   5.728   8.100
35       1   1.432   1.432   6.075
36       1   1.432   4.296   6.075
37       1   2.864   2.864   8.100
38       1   4.296   1.432   6.075
39       1   4.296   4.296   6.075
40       1  11.455   2.864   8.100
41       1   5.728   0.000   8.100
42       1   8.591   5.728   8.100
43       1   7.159   1.432   6.075
44       1   7.159   4.296   6.075
45       1   8.591   2.864   8.100
46       1  10.023   1.432   6.075
47       1  10.023   4.296   6.075
48       1   5.728   8.591   8.100
49       1   0.000   5.728   8.100
50       1   2.864  11.455   8.100
51       1   1.432   7.159   6.075
52       1   1.432  10.023   6.075
53       1   2.864   8.591   8.100
54       1   4.296   7.159   6.075
55       1   4.296  10.023   6.075
56       1  11.455   8.591   8.100
57       1   5.728   5.728   8.100
58       1   8.591  11.455   8.100
59       1   7.159   7.159   6.075
60       1   7.159  10.023   6.075
61       1   8.591   8.591   8.100
62       1  10.023   7.159   6.075
63       1  10.023  10.023   6.075

[19]:

am.plot.py3Dmol.view_3d(system, width=400, height=400)


You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
jupyter labextension install jupyterlab_3dmol

4.3. Other Methods

Other methods include:

• wrap() wraps atoms around periodic boundaries and expands non-periodic boundaries to ensure all atom positions are within the box.

• normalize() transforms the system such that box vectors and atomic positions are compatible with simulation codes, such as LAMMPS.