Mapping structures

A cluster vector calculation requires all atoms to reside on a fixed lattice. Properties of interest, on the other hand, are typically calculated for a structure in which cell metric and atoms have been allowed to relax. Unless the ideal structures have been saved prior to relaxation, one is therefore faced with the task of mapping back the relaxed structure onto the ideal one. In some cases, in particular involving vacancies, relaxation can also lead to atoms moving between sites, in which case remapping is mandatory.

This is the purpose of the function map_structure_to_reference(). The function is also useful to analyze whether the relaxation has gone too far for the cluster expansion to be viable, i.e., whether the ideal structure from which the relaxation started is not a valid representation of the structure for which the property has been obtained.

Import modules

The map_structure_to_reference() function needs to be imported together with some additional functionality from ASE.

from icet.tools import map_structure_to_reference
from ase.build import bulk
from ase import Atom

Prepare dummy structures

First, for the sake of demonstration, a reference structure defining the ideal lattice is created, and a supercell thereof is scaled and rattled to simulate relaxation in an energy minimixation.

reference = bulk('Au', a=4.00)

supercell = reference.repeat(3)
supercell.rattle(0.1)
supercell.set_cell(1.05 * supercell.cell, scale_atoms=True)

# Switch some atoms to Pd
for i in [0, 1, 5, 8, 10]:
    supercell[i].symbol = 'Pd'

Map relaxed structure onto ideal structure

The structure can now be mapped onto a structure in which all atoms reside on ideal lattice sites. The function returns the ideal structure, as well as the maximum and average displacement.

tolerance = 0.8  # Tolerance in Angstrom for how far
# the atoms are allowed to have been displaceed
ideal_structure, r_max, r_av = map_structure_to_reference(supercell,
                                                          reference,
                                                          tolerance)
print('Maximum displacement: {:.3f} Angstrom'.format(r_max))
print('Average displacement: {:.3f} Angstrom'.format(r_av))

Structures with vacancies

If the structure to be mapped contains vacancies additional keywords should e provided, (1) vacancy_type, the chemical symbol that signifies vacancies in the reference structure, and (2) inert_species, a list of elements that are never substituted for a vacancy. The latter allows the code to rescale the volume of the cell and can be omitted, but the mapping is then more likely to fail.

In the example below, a Au-Pd-H-vacancy system is created. In this example, vanadium ('V') represents vacancies. The system of choice consists of two sublattices, one occupied by Au and Pd and another occupied by H and vacancies. Since Au and Pd belong to a sublattice in which we do not allow vacancies, we may set inert_species = ['Au', 'Pd'].

reference = bulk('Au', a=4.00)
reference.append(Atom(('H'), (2.0, 2.0, 2.0)))

supercell = reference.repeat(3)
supercell.rattle(0.1)
supercell.set_cell(1.05 * supercell.cell, scale_atoms=True)

# Switch some Au to Pd and delete some H (to create vacancies)
for i in [0, 4, 6, 2, 7, 3, 17]:
    if supercell[i].symbol == 'Au':
        supercell[i].symbol = 'Pd'
    elif supercell[i].symbol == 'H':
        del supercell[i]

tolerance = 0.8
ideal_structure, r_max, r_av = \
    map_structure_to_reference(supercell, reference, tolerance,
                               vacancy_type='V', inert_species=['Au', 'Pd'])
print('Maximum displacement: {:.3f} Angstrom'.format(r_max))
print('Average displacement: {:.3f} Angstrom'.format(r_av))

Source code

The complete source code is available in examples/map_structure_to_reference.py
"""
This example demonstrates how to map a structure in which the cell has
been scaled and/or the atoms displaced onto an ideal (primitive) structure
"""

# Import modules
from icet.tools import map_structure_to_reference
from ase.build import bulk
from ase import Atom
# End import

# Begin by creating a reference structure, in this case fcc Au.
# Then create a supercell structure, scale the cell and displace the atoms to
# simulate a relaxed structure.
reference = bulk('Au', a=4.00)

supercell = reference.repeat(3)
supercell.rattle(0.1)
supercell.set_cell(1.05 * supercell.cell, scale_atoms=True)

# Switch some atoms to Pd
for i in [0, 1, 5, 8, 10]:
    supercell[i].symbol = 'Pd'

# Map the "relaxed" structure onto an ideal supercell
tolerance = 0.8  # Tolerance in Angstrom for how far
# the atoms are allowed to have been displaceed
ideal_structure, r_max, r_av = map_structure_to_reference(supercell,
                                                          reference,
                                                          tolerance)
print('Maximum displacement: {:.3f} Angstrom'.format(r_max))
print('Average displacement: {:.3f} Angstrom'.format(r_av))

# Map a structure that contains vacancies, in this case Pd-Au-H-Vac, in which
# Pd and Au share one sublattice and Pd and H another.
reference = bulk('Au', a=4.00)
reference.append(Atom(('H'), (2.0, 2.0, 2.0)))

supercell = reference.repeat(3)
supercell.rattle(0.1)
supercell.set_cell(1.05 * supercell.cell, scale_atoms=True)

# Switch some Au to Pd and delete some H (to create vacancies)
for i in [0, 4, 6, 2, 7, 3, 17]:
    if supercell[i].symbol == 'Au':
        supercell[i].symbol = 'Pd'
    elif supercell[i].symbol == 'H':
        del supercell[i]

tolerance = 0.8
ideal_structure, r_max, r_av = \
    map_structure_to_reference(supercell, reference, tolerance,
                               vacancy_type='V', inert_species=['Au', 'Pd'])
print('Maximum displacement: {:.3f} Angstrom'.format(r_max))
print('Average displacement: {:.3f} Angstrom'.format(r_av))