Tools

Mapping structures

icet.tools.map_structure_to_reference(relaxed, reference, inert_species=None, tol_positions=0.0001, suppress_warnings=False, assume_no_cell_relaxation=False)[source]

Maps a relaxed structure onto a reference structure. The function returns a tuple comprising the ideal supercell most closely matching the relaxed structure and a dictionary with supplementary information concerning the mapping. The latter includes for example the largest deviation of any position in the relaxed structure from its reference position (drmax), the average deviation of the positions in the relaxed structure from the reference positions (dravg), and the strain tensor for the relaxed structure relative to the reference structure (strain_tensor).

The Atoms object that provide further supplemental information via custom per-atom arrays including the atomic displacements (Displacement, Displacement_Magnitude) as well as the distances to the three closest sites (Minimum_Distances).

Parameters
  • relaxed (Atoms) – relaxed input structure

  • reference (Atoms) – reference structure, which can but need not be the primitive structure

  • inert_species (Optional[List[str]]) – list of chemical symbols (e.g., ['Au', 'Pd']) that are never substituted for a vacancy; the number of inert sites is used to rescale the volume of the relaxed structure to match the reference structure.

  • tol_positions (float) – tolerance factor applied when scanning for overlapping positions in Angstrom (forwarded to ase.build.make_supercell())

  • suppress_warnings (bool) – if True, print no warnings of large strain or relaxation distances

  • assume_no_cell_relaxation (bool) –

    if False volume and cell metric of the relaxed structure are rescaled to match the reference structure; this can be unnecessary (and counterproductive) for some structures, e.g., with many vacancies

    Note: When setting this parameter to False the reference cell metric must be obtainable via an integer transformation matrix from the reference cell metric. In other words the relaxed structure should not involve relaxations of the volume or the cell metric.

Example

The following code snippet illustrates the general usage. It first creates a primitive FCC cell, which is latter used as reference structure. To emulate a relaxed structure obtained from, e.g., a density functional theory calculation, the code then creates a 4x4x4 conventional FCC supercell, which is populated with two different atom types, has distorted cell vectors, and random displacements to the atoms. Finally, the present function is used to map the structure back the ideal lattice:

>>> from ase.build import bulk
>>> reference = bulk('Au', a=4.09)
>>> structure = bulk('Au', cubic=True, a=4.09).repeat(4)
>>> structure.set_chemical_symbols(10 * ['Ag'] + (len(structure) - 10) * ['Au'])
>>> structure.set_cell(structure.cell * 1.02, scale_atoms=True)
>>> structure.rattle(0.1)
>>> mapped_structure, info = map_structure_to_reference(structure, reference)
Return type

Tuple[Atoms, dict]

Structure enumeration

icet.tools.enumerate_structures(structure, sizes, chemical_symbols, concentration_restrictions=None, niggli_reduce=None, symprec=1e-05, position_tolerance=None)[source]

Yields a sequence of enumerated structures. The function generates all inequivalent structures that are permissible given a certain lattice. Using the chemical_symbols and concentration_restrictions keyword arguments it is possible to specify which chemical_symbols are to be included on which site and in which concentration range.

The function is sensitive to the boundary conditions of the input structure. An enumeration of, for example, a surface can thus be performed by setting structure.pbc = [True, True, False].

The algorithm implemented here was developed by Gus L. W. Hart and Rodney W. Forcade in Phys. Rev. B 77, 224115 (2008) [HarFor08] and Phys. Rev. B 80, 014120 (2009) [HarFor09].

Parameters
  • structure (Atoms) – primitive structure from which derivative superstructures should be generated

  • sizes (List[int]) – number of sites (included in enumeration)

  • chemical_symbols (list) – chemical species with which to decorate the structure, e.g., ['Au', 'Ag']; see below for more examples

  • concentration_restrictions (Optional[dict]) – allowed concentration range for one or more element in chemical_symbols, e.g., {'Au': (0, 0.2)} will only enumerate structures in which the Au content is between 0 and 20 %; here, concentration is always defined as the number of atoms of the specified kind divided by the number of all atoms.

  • niggli_reduction – if True perform a Niggli reduction with spglib for each structure; the default is True if structure is periodic in all directions, False otherwise.

  • symprec (float) – tolerance imposed when analyzing the symmetry using spglib

  • position_tolerance (Optional[float]) – tolerance applied when comparing positions in Cartesian coordinates; by default this value is set equal to symprec

Examples

The following code snippet illustrates how to enumerate structures with up to 6 atoms in the unit cell for a binary alloy without any constraints:

>>> from ase.build import bulk
>>> prim = bulk('Ag')
>>> for structure in enumerate_structures(structure=prim,
...                                       sizes=range(1, 5),
...                                       chemical_symbols=['Ag', 'Au']):
...     pass # Do something with the structure

To limit the concentration range to 10 to 40% Au the code should be modified as follows:

>>> conc_restr = {'Au': (0.1, 0.4)}
>>> for structure in enumerate_structures(structure=prim,
...                                       sizes=range(1, 5),
...                                       chemical_symbols=['Ag', 'Au'],
...                                       concentration_restrictions=conc_restr):
...     pass # Do something with the structure

Often one would like to consider mixing on only one sublattice. This can be achieved as illustrated for a Ga(1-x)Al(x)As alloy as follows:

>>> prim = bulk('GaAs', crystalstructure='zincblende', a=5.65)
>>> for structure in enumerate_structures(structure=prim,
...                                       sizes=range(1, 9),
...                                       chemical_symbols=[['Ga', 'Al'], ['As']]):
...     pass # Do something with the structure
Return type

Atoms

icet.tools.enumerate_supercells(structure, sizes, niggli_reduce=None, symprec=1e-05, position_tolerance=None)[source]

Yields a sequence of enumerated supercells. The function generates all inequivalent supercells that are permissible given a certain lattice. Any supercell can be reduced to one of the supercells generated.

The function is sensitive to the boundary conditions of the input structure. An enumeration of, for example, a surface can thus be performed by setting structure.pbc = [True, True, False].

The algorithm is based on Gus L. W. Hart and Rodney W. Forcade in Phys. Rev. B 77, 224115 (2008) [HarFor08] and Phys. Rev. B 80, 014120 (2009) [HarFor09].

Parameters
  • structure (Atoms) – primitive structure from which supercells should be generated

  • sizes (List[int]) – number of sites (included in enumeration)

  • niggli_reduction – if True perform a Niggli reduction with spglib for each supercell; the default is True if structure is periodic in all directions, False otherwise.

  • symprec (float) – tolerance imposed when analyzing the symmetry using spglib

  • position_tolerance (Optional[float]) – tolerance applied when comparing positions in Cartesian coordinates; by default this value is set equal to symprec

Examples

The following code snippet illustrates how to enumerate supercells with up to 6 atoms in the unit cell:

>>> from ase.build import bulk
>>> prim = bulk('Ag')
>>> for supercell in enumerate_supercells(structure=prim, sizes=range(1, 7)):
...     pass # Do something with the supercell
Return type

Atoms

Generation of special structures

icet.tools.structure_generation.generate_sqs(cluster_space, max_size, target_concentrations, include_smaller_cells=True, T_start=5.0, T_stop=0.001, n_steps=None, optimality_weight=1.0, random_seed=None, tol=1e-05)[source]

Given a cluster_space, generate a special quasirandom structure (SQS), i.e., a structure that for a given supercell size provides the best possible approximation to a random alloy [ZunWeiFer90].

In the present case, this means that the generated structure will have a cluster vector that as closely as possible matches the cluster vector of an infintely large randomly occupated supercell. Internally the function uses a simulated annealing algorithm and the difference between two cluster vectors is calculated with the measure suggested by A. van de Walle et al. in Calphad 42, 13-18 (2013) [WalTiwJon13] (for more information, see mchammer.calculators.TargetVectorCalculator).

Parameters
  • cluster_space (ClusterSpace) – a cluster space defining the lattice to be occupated

  • max_size (int) – maximum supercell size

  • target_concentrations (dict) –

    concentration of each species in the target structure (for example {'Ag': 0.5, 'Pd': 0.5}

    Concentrations are always expressed with respect to all atoms in the supercell, which implies that the sum of all concentrations should always be 1. In the case of multiple sublattices, a valid specification would thus be {'Au': 0.25, 'Pd': 0.25, 'H': 0.1, 'V': 0.4}.

  • include_smaller_cells (bool) – if True, search among all supercell sizes including max_size, else search only among those exactly matching max_size

  • T_start (float) – artificial temperature at which the simulated annealing starts

  • T_stop (float) – artifical temperature at which the simulated annealing stops

  • n_steps (Optional[float]) – total number of Monte Carlo steps in the simulation

  • optimality_weight (float) – controls weighting \(L\) of perfect correlations, see mchammer.calculators.TargetVectorCalculator

  • random_seed (Optional[int]) – seed for the random number generator used in the Monte Carlo simulation

  • tol (float) – Numerical tolerance

Return type

Atoms

icet.tools.structure_generation.generate_sqs_by_enumeration(cluster_space, max_size, target_concentrations, include_smaller_cells=True, optimality_weight=1.0, tol=1e-05)[source]

Given a cluster_space, generate a special quasirandom structure (SQS), i.e., a structure that for a given supercell size provides the best possible approximation to a random alloy [ZunWeiFer90].

In the present case, this means that the generated structure will have a cluster vector that as closely as possible matches the cluster vector of an infintely large randomly occupied supercell. Internally the function uses a simulated annealing algorithm and the difference between two cluster vectors is calculated with the measure suggested by A. van de Walle et al. in Calphad 42, 13-18 (2013) [WalTiwJon13] (for more information, see mchammer.calculators.TargetVectorCalculator).

This functions generates SQS cells by exhaustive enumeration, which means that the generated SQS cell is guaranteed to be optimal with regard to the specified measure and cell size.

Parameters
  • cluster_space (ClusterSpace) – a cluster space defining the lattice to be occupied

  • max_size (int) – maximum supercell size

  • target_concentrations (dict) –

    concentration of each species in the target structure (for example {'Ag': 0.5, 'Pd': 0.5}

    Concentrations are always expressed with respect to all atoms in the supercell, which implies that the sum of all concentrations should always be 1. In the case of multiple sublattices, a valid specification would thus be {'Au': 0.25, 'Pd': 0.25, 'H': 0.1, 'V': 0.4}.

  • include_smaller_cells (bool) – if True, search among all supercell sizes including max_size, else search only among those exactly matching max_size

  • optimality_weight (float) – controls weighting \(L\) of perfect correlations, see mchammer.calculators.TargetVectorCalculator

  • tol (float) – Numerical tolerance

Return type

Atoms

icet.tools.structure_generation.generate_target_structure(cluster_space, max_size, target_concentrations, target_cluster_vector, include_smaller_cells=True, T_start=5.0, T_stop=0.001, n_steps=None, optimality_weight=1.0, random_seed=None, tol=1e-05)[source]

Given a cluster_space and a target_cluster_vector, generate a structure that as closely as possible matches that cluster vector. The search is performed among all inequivalent supercells shapes up to a certain size.

Internally the function uses a simulated annealing algorithm and the difference between two cluster vectors is calculated with the measure suggested by A. van de Walle et al. in Calphad 42, 13-18 (2013) [WalTiwJon13] (for more information, see mchammer.calculators.TargetVectorCalculator).

Parameters
  • cluster_space (ClusterSpace) – a cluster space defining the lattice to be occupied

  • max_size (int) – maximum supercell size

  • target_concentrations (dict) –

    concentration of each species in the target structure (for example {'Ag': 0.5, 'Pd': 0.5}

    Concentrations are always expressed with respect to all atoms in the supercell, which implies that the sum of all concentrations should always be 1. In the case of multiple sublattices, a valid specification would thus be {'Au': 0.25, 'Pd': 0.25, 'H': 0.1, 'V': 0.4}.

  • target_cluster_vector (List[float]) – cluster vector that the generated structure should match as closely as possible

  • include_smaller_cells (bool) – if True, search among all supercell sizes including max_size, else search only among those exactly matching max_size

  • T_start (float) – artificial temperature at which the simulated annealing starts

  • T_stop (float) – artifical temperature at which the simulated annealing stops

  • n_steps (Optional[float]) – total number of Monte Carlo steps in the simulation

  • optimality_weight (float) – controls weighting \(L\) of perfect correlations, see mchammer.calculators.TargetVectorCalculator

  • random_seed (Optional[int]) – seed for the random number generator used in the Monte Carlo simulation

  • tol (float) – Numerical tolerance

Return type

Atoms

icet.tools.structure_generation.occupy_structure_randomly(structure, cluster_space, target_concentrations)[source]

Occupy a structure with quasirandom order but fulfilling target_concentrations.

Parameters
  • structure (Atoms) – ASE Atoms object that will be occupied randomly

  • cluster_space (ClusterSpace) – cluster space (needed as it carries information about sublattices)

  • target_concentrations (dict) –

    concentration of each species in the target structure (for example {'Ag': 0.5, 'Pd': 0.5}

    Concentrations are always expressed with respect to all atoms in the supercell, which implies that the sum of all concentrations should always be 1. In the case of multiple sublattices, a valid specification would thus be {'Au': 0.25, 'Pd': 0.25, 'H': 0.1, 'V': 0.4}.

Ground state finder

class icet.tools.ground_state_finder.GroundStateFinder(cluster_expansion, structure, solver_name=None, verbose=True)[source]

This class provides functionality for determining the ground states using a binary cluster expansion. This is efficiently achieved through the use of mixed integer programming (MIP) as shown by Larsen et al. in Phys. Rev. Lett. 120, 256101 (2018).

This class relies on the Python-MIP package. Python-MIP can be used together with Gurobi, which is not open source but issues academic licenses free of charge. Pleaase note that Gurobi needs to be installed separately. The GroundStateFinder works also without Gurobi, but if performance is critical, Gurobi is highly recommended.

Warning

In order to be able to use Gurobi with python-mip one must ensure that GUROBI_HOME should point to the installation directory (<installdir>):

export GUROBI_HOME=<installdir>

Note

The current implementation only works for binary systems.

Parameters
  • cluster_expansion (ClusterExpansion) – cluster expansion for which to find ground states

  • structure (Atoms) – atomic configuration

  • solver_name (str, optional) – ‘gurobi’, alternatively ‘grb’, or ‘cbc’, searches for available solvers if not informed

  • verbose (bool, optional) – whether to display solver messages on the screen (default: True)

Example

The following snippet illustrates how to determine the ground state for a Au-Ag alloy. Here, the parameters of the cluster expansion are set to emulate a simple Ising model in order to obtain an example that can be run without modification. In practice, one should of course use a proper cluster expansion:

>>> from ase.build import bulk
>>> from icet import ClusterExpansion, ClusterSpace

>>> # prepare cluster expansion
>>> # the setup emulates a second nearest-neighbor (NN) Ising model
>>> # (zerolet and singlet ECIs are zero; only first and second neighbor
>>> # pairs are included)
>>> prim = bulk('Au')
>>> chemical_symbols = ['Ag', 'Au']
>>> cs = ClusterSpace(prim, cutoffs=[4.3], chemical_symbols=chemical_symbols)
>>> ce = ClusterExpansion(cs, [0, 0, 0.1, -0.02])

>>> # prepare initial configuration
>>> structure = prim.repeat(3)

>>> # set up the ground state finder and calculate the ground state energy
>>> gsf = GroundStateFinder(ce, structure)
>>> ground_state = gsf.get_ground_state({'Ag': 5})
>>> print('Ground state energy:', ce.predict(ground_state))
get_ground_state(species_count, max_seconds=inf, threads=0)[source]

Finds the ground state for a given structure and species count, which refers to the count_species, if provided when initializing the instance of this class, or the first species in the list of chemical symbols for the active sublattice.

Parameters
  • species_count (Dict[str, int]) – dictionary with count for one of the species on each active sublattice

  • max_seconds (float) – maximum runtime in seconds (default: inf)

  • threads (int) – number of threads to be used when solving the problem, given that a positive integer has been provided. If set to 0 the solver default configuration is used while -1 corresponds to all available processing cores.

Return type

Atoms

property model

Python-MIP model

Return type

Model

property optimization_status

Optimization status

Return type

OptimizationStatus

Convex hull construction

This module collects a number of different tools, e.g., for structure generation and analysis.

class icet.tools.ConvexHull(concentrations, energies)[source]

This class provides functionality for extracting the convex hull of the (free) energy of mixing. It is based on the convex hull calculator in SciPy.

Parameters
  • concentrations (list(float) or list(list(float))) – concentrations for each structure listed as [[c1, c2], [c1, c2], ...]; for binaries, in which case there is only one independent concentration, the format [c1, c2, c3, ...] works as well.

  • energies (list(float)) – energy (or energy of mixing) for each structure

concentrations

concentrations of the N structures on the convex hull

Type

np.ndarray

energies

energies of the N structures on the convex hull

Type

np.ndarray

dimensions

number of independent concentrations needed to specify a point in concentration space (1 for binaries, 2 for ternaries etc.)

Type

int

structures

indices of structures that constitute the convex hull (indices are defined by the order of their concentrations and energies are fed when initializing the ConvexHull object)

Type

list(int)

Examples

A ConvexHull object is easily initialized by providing lists of concentrations and energies:

>>> data = {'concentration': [0,    0.2,  0.2,  0.3,  0.4,  0.5,  0.8,  1.0],
...         'mixing_energy': [0.1, -0.2, -0.1, -0.2,  0.2, -0.4, -0.2, -0.1]}
>>> hull = ConvexHull(data['concentration'], data['mixing_energy'])

Now one can for example access the points along the convex hull directly:

>>> for c, e in zip(hull.concentrations, hull.energies):
...     print(c, e)
0.0 0.1
0.2 -0.2
0.5 -0.4
1.0 -0.1

or plot the convex hull along with the original data using e.g., matplotlib:

>>> import matplotlib.pyplot as plt
>>> plt.scatter(data['concentration'], data['mixing_energy'], color='darkred')
>>> plt.plot(hull.concentrations, hull.energies)
>>> plt.show()

It is also possible to extract structures at or close to the convex hull:

>>> low_energy_structures = hull.extract_low_energy_structures(
...     data['concentration'], data['mixing_energy'],
...     energy_tolerance=0.005)

A complete example can be found in the basic tutorial.

extract_low_energy_structures(concentrations, energies, energy_tolerance)[source]

Returns the indices of energies that lie within a certain tolerance of the convex hull.

Parameters
  • concentrations (Union[List[float], List[List[float]]]) –

    concentrations of candidate structures

    If there is one independent concentration, a list of floats is sufficient. Otherwise, the concentrations must be provided as a list of lists, such as [[0.1, 0.2], [0.3, 0.1], ...].

  • energies (List[float]) – energies of candidate structures

  • energy_tolerance (float) – include structures with an energy that is at most this far from the convex hull

Return type

List[int]

get_energy_at_convex_hull(target_concentrations)[source]

Returns the energy of the convex hull at specified concentrations. If any concentration is outside the allowed range, NaN is returned.

Parameters

target_concentrations (Union[List[float], List[List[float]]]) –

concentrations at target points

If there is one independent concentration, a list of floats is sufficient. Otherwise, the concentrations ought to be provided as a list of lists, such as [[0.1, 0.2], [0.3, 0.1], ...].

Return type

ndarray

Other structure tools

icet.tools.get_primitive_structure(structure, no_idealize=True, to_primitive=True, symprec=1e-05)[source]

Returns the primitive structure using spglib.

Parameters
  • structure (Atoms) – input atomic structure

  • no_idealize (bool) – if True lengths and angles are not idealized

  • to_primitive (bool) – convert to primitive structure

  • symprec (float) – tolerance imposed when analyzing the symmetry using spglib

Return type

Atoms

icet.tools.get_wyckoff_sites(structure, map_occupations=None, symprec=1e-05)[source]

Returns the Wyckoff symbols of the input structure. The Wyckoff sites are of general interest for symmetry analysis but can be especially useful when setting up, e.g., a SiteOccupancyObserver. The Wyckoff labels can be conveniently attached as an array to the structure object as demonstrated in the examples section below.

By default the occupation of the sites is part of the symmetry analysis. If a chemically disordered structure is provided this will usually reduce the symmetry substantially. If one is interested in the symmetry of the underlying structure one can control how occupations are handled. To this end, one can provide the map_occupations keyword argument. The latter must be a list, each entry of which is a list of species that should be treated as indistinguishable. As a shortcut, if all species should be treated as indistinguishable one can provide an empty list. Examples that illustrate the usage of the keyword are given below.

Parameters
  • structure (Atoms) – input structure, note that the occupation of the sites is included in the symmetry analysis

  • map_occupations (Optional[List[List[str]]]) – each sublist in this list specifies a group of chemical species that shall be treated as indistinguishable for the purpose of the symmetry analysis

  • symprec (float) – tolerance imposed when analyzing the symmetry using spglib

Examples

Wyckoff sites of a hexagonal-close packed structure:

>>> from ase.build import bulk
>>> structure = bulk('Ti')
>>> wyckoff_sites = get_wyckoff_sites(structure)
>>> print(wyckoff_sites)
['2d', '2d']

The Wyckoff labels can also be attached as an array to the structure, in which case the information is also included when storing the Atoms object:

>>> from ase.io import write
>>> structure.new_array('wyckoff_sites', wyckoff_sites, str)
>>> write('structure.xyz', structure)

The function can also be applied to supercells:

>>> structure = bulk('GaAs', crystalstructure='zincblende', a=3.0).repeat(2)
>>> wyckoff_sites = get_wyckoff_sites(structure)
>>> print(wyckoff_sites)
['4a', '4c', '4a', '4c', '4a', '4c', '4a', '4c',
 '4a', '4c', '4a', '4c', '4a', '4c', '4a', '4c']

Now assume that one is given a supercell of a (Ga,Al)As alloy. Applying the function directly yields much lower symmetry since the symmetry of the original structure is broken:

>>> structure.set_chemical_symbols(
...        ['Ga', 'As', 'Al', 'As', 'Ga', 'As', 'Al', 'As',
...         'Ga', 'As', 'Ga', 'As', 'Al', 'As', 'Ga', 'As'])
>>> print(get_wyckoff_sites(structure))
['8g', '8i', '4e', '8i', '8g', '8i', '2c', '8i',
 '2d', '8i', '8g', '8i', '4e', '8i', '8g', '8i']

Since Ga and Al occupy the same sublattice, they should, however, be treated as indistinguishable for the purpose of the symmetry analysis, which can be achieved via the map_occupations keyword:

>>> print(get_wyckoff_sites(structure, map_occupations=[['Ga', 'Al'], ['As']]))
['4a', '4c', '4a', '4c', '4a', '4c', '4a', '4c',
 '4a', '4c', '4a', '4c', '4a', '4c', '4a', '4c']

If occupations are to ignored entirely, one can simply provide an empty list. In the present case, this turns the zincblende lattice into a diamond lattice, on which case there is only one Wyckoff site:

>>> print(get_wyckoff_sites(structure, map_occupations=[]))
['8a', '8a', '8a', '8a', '8a', '8a', '8a', '8a',
 '8a', '8a', '8a', '8a', '8a', '8a', '8a', '8a']
Return type

List[str]