Utillity functions and classes

ase.utils.opencew(filename, world=None)[source]

Create and open filename exclusively for writing.

If master cpu gets exclusive write access to filename, a file descriptor is returned (a dummy file descriptor is returned on the slaves). If the master cpu does not get write access, None is returned on all processors.

ase.utils.gcd(a, b)[source]

Greatest common divisor of a and b.

class ase.utils.timing.Timer(print_levels=1000)[source]

Timer object.

Use like this:

timer = Timer()
timer.start('description')
# do something
timer.stop()

or:

with timer('description'):
    # do something

To get a summary call:

timer.write()
class ase.utils.timing.timer(name)[source]

Decorator for timing a method call.

Example:

from ase.utils.timing import timer, Timer

class A:
    def __init__(self):
        self.timer = Timer()
        
    @timer('Add two numbers')
    def add(self, x, y):
        return x + y

Equation of state

The EquationOfState class can be used to find equilibrium volume, energy, and bulk modulus for solids:

class ase.utils.eos.EquationOfState(volumes, energies, eos='sjeos')[source]

Fit equation of state for bulk systems.

The following equation is used:

sjeos (default)
    A third order inverse polynomial fit 10.1103/PhysRevB.67.026103

                    2      3        -1/3
E(V) = c + c t + c t  + c t ,  t = V
        0   1     2      3

taylor
    A third order Taylor series expansion about the minimum volume

murnaghan
    PRB 28, 5480 (1983)

birch
    Intermetallic compounds: Principles and Practice,
    Vol I: Principles. pages 195-210

birchmurnaghan
    PRB 70, 224107

pouriertarantola
    PRB 70, 224107

vinet
    PRB 70, 224107

antonschmidt
    Intermetallics 11, 23-32 (2003)

p3
    A third order polynomial fit

Use:

eos = EquationOfState(volumes, energies, eos='sjeos')
v0, e0, B = eos.fit()
eos.plot()

See also

The Equation of state tutorial.

Geometry tools

exception ase.utils.geometry.IncompatibleCellError[source]

Exception raised if stacking fails due to incompatible cells between atoms1 and atoms2.

ase.utils.geometry.cut(atoms, a=(1, 0, 0), b=(0, 1, 0), c=None, clength=None, origo=(0, 0, 0), nlayers=None, extend=1.0, tolerance=0.01, maxatoms=None)[source]

Cuts out a cell defined by a, b, c and origo from a sufficiently repeated copy of atoms.

Typically, this function is used to create slabs of different sizes and orientations. The vectors a, b and c are in scaled coordinates and defines the returned cell and should normally be integer-valued in order to end up with a periodic structure. However, for systems with sub-translations, like fcc, integer multiples of 1/2 or 1/3 might also make sence for some directions (and will be treated correctly).

Parameters:

atoms: Atoms instance
This should correspond to a repeatable unit cell.
a: int | 3 floats
The a-vector in scaled coordinates of the cell to cut out. If integer, the a-vector will be the scaled vector from origo to the atom with index a.
b: int | 3 floats
The b-vector in scaled coordinates of the cell to cut out. If integer, the b-vector will be the scaled vector from origo to the atom with index b.
c: None | int | 3 floats
The c-vector in scaled coordinates of the cell to cut out. if integer, the c-vector will be the scaled vector from origo to the atom with index c. If None it will be along cross(a, b) converted to real space and normalised with the cube root of the volume. Note that this in general is not perpendicular to a and b for non-cubic systems. For cubic systems however, this is redused to c = cross(a, b).
clength: None | float
If not None, the length of the c-vector will be fixed to clength Angstroms. Should not be used together with nlayers.
origo: int | 3 floats
Position of origo of the new cell in scaled coordinates. If integer, the position of the atom with index origo is used.
nlayers: None | int
If nlayers is not None, the returned cell will have nlayers atomic layers in the c-direction.
extend: 1 or 3 floats
The extend argument scales the effective cell in which atoms will be included. It must either be three floats or a single float scaling all 3 directions. By setting to a value just above one, e.g. 1.05, it is possible to all the corner and edge atoms in the returned cell. This will of cause make the returned cell non-repeatable, but is very usefull for visualisation.
tolerance: float
Determines what is defined as a plane. All atoms within tolerance Angstroms from a given plane will be considered to belong to that plane.
maxatoms: None | int
This option is used to auto-tune tolerance when nlayers is given for high zone axis systems. For high zone axis one needs to reduce tolerance in order to distinguise the atomic planes, resulting in the more atoms will be added and eventually MemoryError. A too small tolerance, on the other hand, might result in inproper splitting of atomic planes and that too few layers are returned. If maxatoms is not None, tolerance will automatically be gradually reduced until nlayers atomic layers is obtained, when the number of atoms exceeds maxatoms.

Example:

>>> import ase
>>> from ase.lattice.spacegroup import crystal
>>>
# Create an aluminium (111) slab with three layers
#
# First an unit cell of Al
>>> a = 4.05
>>> aluminium = crystal('Al', [(0,0,0)], spacegroup=225,
...                     cellpar=[a, a, a, 90, 90, 90])
>>>
# Then cut out the slab
>>> al111 = cut(aluminium, (1,-1,0), (0,1,-1), nlayers=3)
>>>
# Visualisation of the skutterudite unit cell
#
# Again, create a skutterudite unit cell
>>> a = 9.04
>>> skutterudite = crystal(
...     ('Co', 'Sb'),
...     basis=[(0.25,0.25,0.25), (0.0, 0.335, 0.158)],
...     spacegroup=204,
...     cellpar=[a, a, a, 90, 90, 90])
>>>
# Then use *origo* to put 'Co' at the corners and *extend* to
# include all corner and edge atoms.
>>> s = cut(skutterudite, origo=(0.25, 0.25, 0.25), extend=1.01)
>>> ase.view(s)  
ase.utils.geometry.find_mic(D, cell, pbc=True)[source]

Finds the minimum-image representation of vector(s) D

ase.utils.geometry.get_layers(atoms, miller, tolerance=0.001)[source]

Returns two arrays describing which layer each atom belongs to and the distance between the layers and origo.

Parameters:

miller: 3 integers
The Miller indices of the planes. Actually, any direction in reciprocal space works, so if a and b are two float vectors spanning an atomic plane, you can get all layers parallel to this with miller=np.cross(a,b).
tolerance: float
The maximum distance in Angstrom along the plane normal for counting two atoms as belonging to the same plane.

Returns:

tags: array of integres
Array of layer indices for each atom.
levels: array of floats
Array of distances in Angstrom from each layer to origo.

Example:

>>> import numpy as np
>>> from ase.lattice.spacegroup import crystal
>>> atoms = crystal('Al', [(0,0,0)], spacegroup=225, cellpar=4.05)
>>> np.round(atoms.positions, decimals=5)
array([[ 0.   ,  0.   ,  0.   ],
       [ 0.   ,  2.025,  2.025],
       [ 2.025,  0.   ,  2.025],
       [ 2.025,  2.025,  0.   ]])
>>> get_layers(atoms, (0,0,1))
(array([0, 1, 1, 0]), array([ 0.   ,  2.025]))
ase.utils.geometry.minimize_tilt(atoms, order=[0, 1, 2], fold_atoms=True)[source]

Minimize the tilt angles of the unit cell.

ase.utils.geometry.minimize_tilt_ij(atoms, modified=1, fixed=0, fold_atoms=True)[source]

Minimize the tilt angle for two given axes.

The problem is underdetermined. Therefore one can choose one axis that is kept fixed.

ase.utils.geometry.rotate(atoms, a1, a2, b1, b2, rotate_cell=True, center=(0, 0, 0))[source]

Rotate atoms, such that a1 will be rotated in the direction of a2 and b1 in the direction of b2. The point at center is fixed. Use center=’COM’ to fix the center of mass. If rotate_cell is true, the cell will be rotated together with the atoms.

Note that the 000-corner of the cell is by definition fixed at origo. Hence, setting center to something other than (0, 0, 0) will rotate the atoms out of the cell, even if rotate_cell is True.

ase.utils.geometry.rotation_matrix(a1, a2, b1, b2)[source]

Returns a rotation matrix that rotates the vectors a1 in the direction of a2 and b1 in the direction of b2.

In the case that the angle between a2 and b2 is not the same as between a1 and b1, a proper rotation matrix will anyway be constructed by first rotate b2 in the b1, b2 plane.

ase.utils.geometry.sort(atoms, tags=None)[source]

Return a new Atoms object with sorted atomic order. The default is to order according to chemical symbols, but if tags is not None, it will be used instead. A stable sorting algorithm is used.

Example:

>>> import ase
>>> from ase.lattice.spacegroup import crystal
>>>
# Two unit cells of NaCl
>>> a = 5.64
>>> nacl = crystal(['Na', 'Cl'], [(0, 0, 0), (0.5, 0.5, 0.5)],
... spacegroup=225, cellpar=[a, a, a, 90, 90, 90]).repeat((2, 1, 1))
>>> nacl.get_chemical_symbols()
['Na', 'Na', 'Na', 'Na', 'Cl', 'Cl', 'Cl', 'Cl', 'Na', 'Na', 'Na',
        'Na', 'Cl', 'Cl', 'Cl', 'Cl']
>>> nacl_sorted = sort(nacl)
>>> nacl_sorted.get_chemical_symbols()
['Cl', 'Cl', 'Cl', 'Cl', 'Cl', 'Cl', 'Cl', 'Cl', 'Na', 'Na', 'Na',
        'Na', 'Na', 'Na', 'Na', 'Na']
>>> np.all(nacl_sorted.cell == nacl.cell)
True
ase.utils.geometry.stack(atoms1, atoms2, axis=2, cell=None, fix=0.5, maxstrain=0.5, distance=None, reorder=False, output_strained=False)[source]

Return a new Atoms instance with atoms2 stacked on top of atoms1 along the given axis. Periodicity in all directions is ensured.

The size of the final cell is determined by cell, except that the length alongh axis will be the sum of atoms1.cell[axis] and atoms2.cell[axis]. If cell is None, it will be interpolated between atoms1 and atoms2, where fix determines their relative weight. Hence, if fix equals zero, the final cell will be determined purely from atoms1 and if fix equals one, it will be determined purely from atoms2.

An ase.geometry.IncompatibleCellError exception is raised if the cells of atoms1 and atoms2 are incopatible, e.g. if the far corner of the unit cell of either atoms1 or atoms2 is displaced more than maxstrain. Setting maxstrain to None, disable this check.

If distance is not None, the size of the final cell, along the direction perpendicular to the interface, will be adjusted such that the distance between the closest atoms in atoms1 and atoms2 will be equal to distance. This option uses scipy.optimize.fmin() and hence require scipy to be installed.

If reorder is True, then the atoms will be reordred such that all atoms with the same symbol will follow sequensially after each other, eg: ‘Al2MnAl10Fe’ -> ‘Al12FeMn’.

If output_strained is True, then the strained versions of atoms1 and atoms2 are returned in addition to the stacked structure.

Example:

>>> import ase
>>> from ase.lattice.spacegroup import crystal
>>>
# Create an Ag(110)-Si(110) interface with three atomic layers
# on each side.
>>> a_ag = 4.09
>>> ag = crystal(['Ag'], basis=[(0,0,0)], spacegroup=225,
...              cellpar=[a_ag, a_ag, a_ag, 90., 90., 90.])
>>> ag110 = cut(ag, (0, 0, 3), (-1.5, 1.5, 0), nlayers=3)
>>>
>>> a_si = 5.43
>>> si = crystal(['Si'], basis=[(0,0,0)], spacegroup=227,
...              cellpar=[a_si, a_si, a_si, 90., 90., 90.])
>>> si110 = cut(si, (0, 0, 2), (-1, 1, 0), nlayers=3)
>>>
>>> interface = stack(ag110, si110, maxstrain=1)
>>> ase.view(interface)  
>>>
# Once more, this time adjusted such that the distance between
# the closest Ag and Si atoms will be 2.3 Angstrom (requires scipy).
>>> interface2 = stack(ag110, si110,
...                    maxstrain=1, distance=2.3)   
Optimization terminated successfully.
    ...
>>> ase.view(interface2)  
ase.utils.geometry.wrap_positions(positions, cell, pbc=True, center=(0.5, 0.5, 0.5), eps=1e-07)[source]

Wrap positions to unit cell.

Returns positions changed by a multiple of the unit cell vectors to fit inside the space spanned by these vectors. See also the ase.atoms.Atoms.wrap() method.

Parameters:

positions: float ndarray of shape (n, 3)
Positions of the atoms
cell: float ndarray of shape (3, 3)
Unit cell vectors.
pbc: one or 3 bool
For each axis in the unit cell decides whether the positions will be moved along this axis.
center: three float
The positons in fractional coordinates that the new positions will be nearest possible to.
eps: float
Small number to prevent slightly negative coordinates from beeing wrapped.

Example:

>>> from ase.utils.geometry import wrap_positions
>>> wrap_positions([[-0.1, 1.01, -0.5]],
...                [[1, 0, 0], [0, 1, 0], [0, 0, 4]],
...                pbc=[1, 1, 0])
array([[ 0.9 ,  0.01, -0.5 ]])