Observers#
SiteOccupancyObserver#
- class mchammer.observers.SiteOccupancyObserver(cluster_space, structure, sites, interval=None)[source]
This class represents a site occupation factor (SOF) observer.
A SOF observer allows to compute the site occupation factors along the trajectory sampled by a Monte Carlo (MC) simulation.
- Parameters:
cluster_space (
ClusterSpace) – Cluster space from which the allowed species are extracted.structure (
Atoms) – Supercell consistent with primitive structure incluster_space. Used to determine which species are allowed on each site.sites (
dict[str,list[int]]) – Dictionary containing lists of sites that are to be considered. The keys are taken as the names of the sites. The indices refer to the primitive structure associated with the cluster space.interval (
int|None) – Observation interval in trial steps. By default the ensemble the observer is attached to sets the interval to the number of sites in the structure.
Example
The following snippet illustrates how to use the site occupancy factor (SOF) observer in a Monte Carlo simulation of a surface slab. Here, the SOF observer is used to monitor the concentrations of different species at the surface, the first subsurface layer, and the remaining ‘bulk’. A minimal cluster expansion is used with slightly modified surface interactions in order to obtain an example that can be run without much ado. In practice, one should of course use a proper cluster expansion:
>>> from ase.build import fcc111 >>> from icet import ClusterExpansion, ClusterSpace >>> from mchammer.calculators import ClusterExpansionCalculator >>> from mchammer.ensembles import CanonicalEnsemble >>> from mchammer.observers import SiteOccupancyObserver >>> # prepare reference structure >>> prim = fcc111('Au', size=(1, 1, 10), vacuum=10.0) >>> prim.translate((0.1, 0.1, 0.0)) >>> prim.wrap() >>> prim.pbc = True # icet requires pbc in all directions >>> # prepare cluster expansion >>> cs = ClusterSpace(prim, cutoffs=[3.7], chemical_symbols=['Ag', 'Au']) >>> params = [0] + 5 * [0] + 10 * [0.1] >>> params[1] = 0.01 >>> params[6] = 0.12 >>> ce = ClusterExpansion(cs, params) >>> print(ce) >>> # prepare initial configuration based on a 2x2 supercell >>> structure = prim.repeat((2, 2, 1)) >>> for k in range(20): >>> structure[k].symbol = 'Ag' >>> # set up MC simulation >>> calc = ClusterExpansionCalculator(structure, ce) >>> mc = CanonicalEnsemble(structure=structure, calculator=calc, temperature=600, ... dc_filename='myrun_sof.dc') >>> # set up observer and attach it to the MC simulation >>> sites = {'surface': [0, 9], 'subsurface': [1, 8], ... 'bulk': list(range(2, 8))} >>> sof = SiteOccupancyObserver(cs, structure, sites, interval=len(structure)) >>> mc.attach_observer(sof) >>> # run 1000 trial steps >>> mc.run(1000)
After having run this snippet one can access the SOFs via the data container:
>>> print(mc.data_container.data)
- get_observable(structure)[source]
Returns the site occupation factors for a given atomic configuration.
- Parameters:
structure (
Atoms) – Input atomic structure.- Return type:
dict[str,list[float]]
- property interval: int | None
Observation interval in trial steps.
- property return_type: type
Type of the value
get_observable()returns.
- property tag: str
Human-readable tag used for identifying the observer.
ShortRangeOrderObserver#
- class mchammer.observers.ShortRangeOrderObserver(cluster_space, structure, radius, pairs=None, interval=None)[source]
This class represents a Warren-Cowley short-range order (SRO) observer for systems with an arbitrary number of species.
A shell is one pair orbit of the cluster space, meaning one set of pairs that are equivalent under the symmetry of the lattice. Shells are numbered by increasing radius, starting from one. Two shells can share a radius without being the same shell, since pairs at the same distance need not be related by symmetry. An example is hexagonal close packing at the ideal axial ratio, where the six neighbors within the basal plane and the six neighbors outside it lie at the same distance but form two shells.
For a system with a single sublattice, a pair of species \(A\) and \(B\) and a shell \(m\), the SRO parameter is
\[\alpha^{(m)}_{AB} = 1 - \frac{P^{(m)}_{B|A}}{c_B},\]where \(P^{(m)}_{B|A}\) is the probability of finding a \(B\) atom in shell \(m\) around an \(A\) atom and \(c_B\) is the concentration of species \(B\). The form that is evaluated here is
\[\alpha^{(m)}_{AB} = 1 - \frac{N^{(m)}_{AB}} {\langle N^{(m)}_{AB}\rangle_\mathrm{rnd}},\]where \(N^{(m)}_{AB}\) is the number of \(A\)-\(B\) pairs in shell \(m\) and \(\langle\cdot\rangle_\mathrm{rnd}\) is the number of such pairs expected when the species are distributed at random over each sublattice, at the concentrations of the structure at hand. On a single sublattice the two expressions agree. The parameter is negative if \(A\)-\(B\) pairs are more common than in a random alloy and positive if they are less common. It is symmetric under exchange of \(A\) and \(B\) and bounded from above by one, which is reached when the shell contains no \(A\)-\(B\) pairs at all. The value is
nanif one of the species is absent from a sublattice that the shell connects, since the parameter is undefined in that case.For a system with several sublattices the second expression is a generalization of the first instead of a rewriting of it. The reference is conditioned on the sublattices that the shell connects and is built from the concentration of each of them separately, whereas the textbook expression uses the concentration of the system as a whole. The two therefore differ whenever the sublattices differ in composition. The quantity reported here measures the correlation that remains once the composition of each sublattice is accounted for, which is the useful quantity for an ordered compound with mixing on its sublattices.
A shell that connects two different sublattices distinguishes the two orientations of a pair, since \(A\) on the first sublattice next to \(B\) on the second is a different correlation from \(B\) on the first next to \(A\) on the second. The observer reports one parameter per pair. It therefore rejects a pair whose two species are both allowed on both of the sublattices that such a shell connects, instead of averaging the two orientations into a single number.
On a single sublattice with \(n\) species the parameters of a shell obey the \(n\) sum rules \(\sum_B c_B \alpha^{(m)}_{AB} = 0\), one per species \(A\), which leaves \(n(n-1)/2\) independent parameters per shell. The parameters for unlike pairs form such an independent set and are therefore the ones that are reported by default. These sum rules do not carry over to a shell that connects two different sublattices, where the number of independent parameters is set by the number of species allowed on either side.
A random alloy gives zero in the thermodynamic limit. A cell that holds a fixed number of atoms of each species retains a finite size offset, because the reference treats the sites as independent while the neighbors of a site are in fact drawn without replacement. The offset is \(-1/(N-1)\) for an unlike pair on a sublattice of \(N\) sites, so a cell of 8 sites averages \(-1/7\) instead of zero.
Shells that touch a sublattice with only one allowed species are not part of the cluster space and are hence not reported. Their parameters would be identically zero, since that species occupies every site of the sublattice in question.
- Parameters:
cluster_space (
ClusterSpace) – Cluster space used for initialization. Only the primitive structure and the chemical species are taken from it, since the shells to be analyzed are set by theradiusparameter.structure (
Atoms) – Defines the lattice which the observer will work on. The shells and the sublattices are determined once, at initialization, henceget_observable()accepts only structures with the same cell and the same positions, in the same order.radius (
float) – Cutoff that selects the neighbor shells to be analyzed, given as the largest interatomic distance of a pair that is still included, in Ångstrom.pairs (
list[tuple[str,str]] |None) – Pairs of species for which to compute the SRO parameters. By default all pairs of distinct species that can occur in at least one of the shells considered. Pass explicit pairs in order to restrict the output, or in order to include like pairs such as('Au', 'Au'), which are not included by default. The two species of a pair are reported in alphabetical order, since the parameter is symmetric, hence('Pd', 'Ag')and('Ag', 'Pd')both give keys of the formsro_Ag_Pd_m.interval (
int|None) – Observation interval in trial steps. By default the ensemble the observer is attached to sets the interval to the number of sites in the structure.
Example
The following snippet illustrates how to use the short-range order (SRO) observer in a Monte Carlo simulation of a bulk supercell. 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 >>> from mchammer.calculators import ClusterExpansionCalculator >>> from mchammer.ensembles import CanonicalEnsemble >>> from mchammer.observers import ShortRangeOrderObserver >>> # prepare cluster expansion >>> # the setup emulates a nearest-neighbor Ising model for a ternary >>> # system. The shells analyzed by the observer are set by >>> # its radius parameter and are independent of the cutoffs of the >>> # cluster expansion >>> prim = bulk('Au') >>> cs = ClusterSpace(prim, cutoffs=[3.0], chemical_symbols=['Ag', 'Au', 'Pd']) >>> ce = ClusterExpansion(cs, [0, 0, 0, 0.1, 0.05, -0.02]) >>> # prepare initial configuration >>> structure = prim.repeat(3) >>> structure.symbols = 9 * ['Ag'] + 9 * ['Au'] + 9 * ['Pd'] >>> # set up MC simulation >>> calc = ClusterExpansionCalculator(structure, ce) >>> mc = CanonicalEnsemble(structure=structure, calculator=calc, ... temperature=600, dc_filename='myrun_sro.dc') >>> # set up observer and attach it to the MC simulation >>> sro = ShortRangeOrderObserver( ... cs, structure, interval=len(structure), radius=4.3) >>> mc.attach_observer(sro) >>> # run 1000 trial steps >>> mc.run(1000)
After having run this snippet one can access the SRO parameters via the data container:
>>> print(mc.data_container.data)
The parameters are reported under keys of the form
sro_Ag_Pd_2, where the last field is the shell index. In order to follow only the Ag-Pd correlation one would set up the observer as:>>> sro = ShortRangeOrderObserver( ... cs, structure, interval=len(structure), radius=4.3, ... pairs=[('Ag', 'Pd')])
- get_observable(structure)[source]
Returns the Warren-Cowley short-range order parameters for a given atomic configuration.
The keys of the dictionary are of the form
sro_A_B_m, whereAandBare the two species andmis the shell index, counted from one in order of increasing radius.The observer holds one buffer for the counts of a shell, which it reuses, hence a single instance cannot be used from several threads at the same time.
- Parameters:
structure (
Atoms) – Input atomic structure. It has to have the same cell and the same positions, in the same order, as the structure that the observer was initialized with. Only the occupations may differ, and they have to obey the sublattices of the cluster space.- Return type:
dict[str,float]
- property interval: int | None
Observation interval in trial steps.
- property pairs: list[tuple[str, str]]
Pairs of species for which the SRO parameters are computed.
The two species of a pair appear in alphabetical order, which is the order in which they appear in the keys of the observable as well.
- property return_type: type
Type of the value
get_observable()returns.
- property shells: list[dict]
Shells that the observer analyzes, ordered by increasing radius.
Each entry is a dictionary with the keys
index, which is the shell index that appears in the keys of the observable,radius, which follows the convention ofClusterSpaceand is therefore half the interatomic distance of the pair,sublattices, which holds the symbols of the sublattices that the two sites of the shell belong to, andpairs, which lists the pairs of species reported for the shell. A shell with an empty list of pairs does not contribute to the observable, which is why the shell indices in the observable can have gaps for a system with several sublattices.
- property tag: str
Human-readable tag used for identifying the observer.
StructureFactorObserver#
- class mchammer.observers.StructureFactorObserver(structure, q_points, symbol_pairs=None, form_factors=None, interval=None)[source]
This class represents a structure factor observer.
This observer allows one to compute structure factors along the trajectory sampled by a Monte Carlo (MC) simulation. Structure factors are convenient for monitoring long-range order. The structure factor is defined as:
\[S(\vec{q}) = \frac{1}{\sum_{j=1}^N f_j^2} \sum_{j,k}^N e^{-i \vec{q} \cdot (\vec{R}_k - \vec{R}_j)}\]In addition to this “total” structure factor, this observer calculates pair-specific structure factors, which correspond to parts of the summation defined above, with the summation restricted to pairs of specific types, e.g., Au-Au, Au-Cu and Cu-Cu in the example below.
- Parameters:
structure (
Atoms) – Prototype for the structures for which the structure factor will be computed later. The supercell size (but not its decoration) must be identical. This structure is also used to determine the the possible pairs ifsymbol_pairs=None.q_points (
list[Sequence]) – Array of q-points at which to evaluate the structure factor. The q-points must be commensurate with the supercell, see the notes below.symbol_pairs (
list[tuple[str,str]] |None) – Pairs of species for which structure factors are computed, such as[('Al', 'Cu'), ('Al', 'Al')]. By default all pairs of species that occur in the input structure.form_factors (
dict[str,float] |None) – Form factors for each atom type. This can be used to (coarsely) simulate X-ray or neutron spectra. Note that in general the form factors are q-dependent, see, e.g., here. By default (None) all form factors are set to 1.interval (
int|None) – Observation interval in trial steps. By default the ensemble the observer is attached to sets the interval to the number of sites in the structure.
- Raises:
ValueError – If a q-point is not commensurate with the supercell, if the q-points are not given as three-dimensional vectors, if no pair is defined, if a symbol is not a valid chemical species, if a form factor is missing for one of the requested species, or if a form factor is zero.
Notes
A q-point has to be commensurate with the supercell, meaning that \(\vec{q} \cdot \vec{a}_k\) is an integer multiple of \(2\pi\) for every periodic cell vector \(\vec{a}_k\), in other words that the q-point is a reciprocal lattice vector of the supercell. The reason is that a periodic structure fixes its sites only up to a cell vector. Moving a site by \(\vec{a}_k\) multiplies its phase by \(e^{-i \vec{q} \cdot \vec{a}_k}\), which equals one only for a commensurate q-point, so for any other q-point the structure factor would depend on which periodic image happens to represent each site. Commensurate q-points are also the ones for which the structure factor of a finite supercell approximates the structure factor of the corresponding infinite crystal. A direction along which the structure is not periodic imposes no condition, since it admits no such shift.
A q-point is accepted if its indices with respect to the reciprocal lattice of the supercell are integers to within a small tolerance, and is then snapped onto the exact reciprocal lattice point along the periodic directions, so that the phases are those of the q-point that was meant. The tolerance applies to the indices themselves, which is what bounds the error of a phase, so a high order reflection is held to a tighter relative accuracy than a low order one. A q-point computed from a lattice parameter that differs slightly from the one of the supercell can therefore be accepted at low order and rejected at high order.
Independently of all this, the double sum above factorizes into a product of two single sums over the sites, which is what makes the observer cheap. It stores one phase per site instead of one phase per pair of sites, so that both its setup and its evaluation scale linearly with the number of atoms.
Example
The following snippet illustrates how to use the structure factor observer in a simulated annealing run of dummy Cu-Au model to observe the emergence of a long-range ordered \(L1_2\) structure:
>>> import numpy as np >>> from ase.build import bulk >>> from icet import ClusterSpace, ClusterExpansion >>> from mchammer.calculators import ClusterExpansionCalculator >>> from mchammer.ensembles import CanonicalAnnealing >>> from mchammer.observers import StructureFactorObserver >>> # parameters >>> size = 3 >>> alat = 4.0 >>> symbols = ['Cu', 'Au'] >>> # setup >>> prim = bulk('Cu', a=alat, cubic=True) >>> cs = ClusterSpace(prim, [0.9*alat], symbols) >>> ce = ClusterExpansion(cs, [0, 0, 0.2]) >>> # make supercell >>> supercell = prim.repeat(size) >>> ns = int(0.25 * len(supercell)) >>> supercell.symbols[0:ns] = 'Au' >>> np.random.shuffle(supercell.symbols) >>> # define q-points to sample >>> q_points = [] >>> q_points.append(2 * np.pi / alat * np.array([1, 0, 0])) >>> q_points.append(2 * np.pi / alat * np.array([0, 1, 0])) >>> q_points.append(2 * np.pi / alat * np.array([0, 0, 1])) >>> # set up structure factor observer >>> sfo = StructureFactorObserver(supercell, q_points) >>> # run simulation >>> calc = ClusterExpansionCalculator(supercell, ce) >>> mc = CanonicalAnnealing(supercell, calc, ... T_start=900, T_stop=500, cooling_function='linear', ... n_steps=400*len(supercell)) >>> mc.attach_observer(sfo) >>> mc.run()
After having run this snippet, one can access the structure factors via the data container:
>>> dc = mc.data_container >>> print(dc.data)
The emergence of the ordered low-temperature structure can be monitored by following the temperature dependence of any of the pair-specific structure factors.
- property form_factors: dict[str, float]
Form factors used in structure factor calculation.
- get_observable(structure)[source]
Returns the structure factors for a given atomic configuration.
- Parameters:
structure (
Atoms) – Input atomic structure.- Raises:
ValueError – If the input structure is incompatible with structure used for initialization of the
StructureFactorObserver.- Return type:
dict[str,float]
- property interval: int | None
Observation interval in trial steps.
- property q_points: list[ndarray]
q-points for which structure factor is calculated, as they were requested. The phases are computed from the reciprocal lattice points these were snapped onto, which differ by at most the acceptance tolerance.
- property return_type: type
Type of the value
get_observable()returns.
- property tag: str
Human-readable tag used for identifying the observer.
ClusterCountObserver#
- class mchammer.observers.ClusterCountObserver(cluster_space, structure, interval=None, orbit_indices=None)[source]
This class represents a cluster count observer.
A cluster count observer keeps track of how often each decorated cluster occurs along the trajectory sampled by a Monte Carlo (MC) simulation. Given several canonical MC simulations at different temperatures, for example, it gives access to the temperature dependence of the number of nearest neighbors of a particular species.
The counts are stored in the data container in one column per decorated cluster. The columns are named
0_Al,0_Cu,1_Al_Al,1_Al_Cu,1_Cu_Al,1_Cu_Cuand so on, where the number is the orbit index and the symbols are the species on the sites of the cluster. The count is the number of clusters of that orbit in the structure that carry that decoration.- Parameters:
cluster_space (
ClusterSpace) – Cluster space that defines the clusters to be counted.structure (
Atoms) – Supercell the observer works on.interval (
int|None) – Observation interval in trial steps. By default the ensemble the observer is attached to sets the interval to the number of sites in the structure.orbit_indices (
list[int] |None) – Indices of the orbits whose clusters are counted. By default all orbits are included.
Example
The following snippet counts nearest and next-nearest neighbor pairs along a canonical Monte Carlo trajectory of an Ising-like cluster expansion:
>>> from ase.build import bulk >>> from icet import ClusterExpansion, ClusterSpace >>> from mchammer.calculators import ClusterExpansionCalculator >>> from mchammer.ensembles import CanonicalEnsemble >>> from mchammer.observers import ClusterCountObserver >>> # prepare cluster expansion >>> prim = bulk('Au') >>> cs = ClusterSpace(prim, cutoffs=[4.3], chemical_symbols=['Ag', 'Au']) >>> ce = ClusterExpansion(cs, [0, 0, 0.1, -0.02]) >>> # prepare initial configuration >>> structure = prim.repeat(3) >>> for k in range(5): ... structure[k].symbol = 'Ag' >>> # set up the simulation and attach the observer >>> calculator = ClusterExpansionCalculator(structure, ce) >>> mc = CanonicalEnsemble(structure=structure, calculator=calculator, ... temperature=600) >>> observer = ClusterCountObserver(cs, structure, interval=len(structure)) >>> mc.attach_observer(observer) >>> mc.run(1000) >>> # the counts are available from the data container, for example the >>> # number of nearest neighbor Ag-Au pairs along the trajectory >>> counts = mc.data_container.get('1_Ag_Au')
The counts of a single structure are available as a data frame:
>>> print(observer.get_cluster_counts(structure))
- get_cluster_counts(structure)[source]
Returns the number of clusters of each orbit and decoration in a structure.
- get_observable(structure)[source]
Returns the cluster counts of a configuration.
- Parameters:
structure (
Atoms) – Atomic configuration to observe.- Returns:
The number of clusters for each decorated cluster, keyed by the column name used in the data container.
- Return type:
dict
- property interval: int | None
Observation interval in trial steps.
- property return_type: type
Type of the value
get_observable()returns.
- property tag: str
Human-readable tag used for identifying the observer.
ClusterExpansionObserver#
- class mchammer.observers.ClusterExpansionObserver(cluster_expansion, interval=None)[source]
This class represents a cluster expansion (CE) observer.
A CE observer computes a property described by a CE along the trajectory sampled by a Monte Carlo (MC) simulation. In general this CE differs from the CE that generates the trajectory. In a canonical MC simulation, for example, the latter usually represents an energy, while the observed CE could describe the lattice parameter or the band gap.
The observed value is stored in the data container under the tag of the observer, which is
ClusterExpansionObserverunless another tag is given toBaseEnsemble.attach_observer.- Parameters:
cluster_expansion (
ClusterExpansion) – Cluster expansion to be used for observation.interval (
int|None) – Observation interval in trial steps. By default the ensemble the observer is attached to sets the interval to the number of sites in the structure.
Example
The following snippet samples an Ising-like cluster expansion in the canonical ensemble and observes a second cluster expansion along the trajectory. Both sets of parameters are made up to keep the example self-contained:
>>> from ase.build import bulk >>> from icet import ClusterExpansion, ClusterSpace >>> from mchammer.calculators import ClusterExpansionCalculator >>> from mchammer.ensembles import CanonicalEnsemble >>> from mchammer.observers import ClusterExpansionObserver >>> # prepare the cluster expansions >>> prim = bulk('Au') >>> cs = ClusterSpace(prim, cutoffs=[4.3], chemical_symbols=['Ag', 'Au']) >>> ce_energy = ClusterExpansion(cs, [0, 0, 0.1, -0.02]) >>> ce_volume = ClusterExpansion(cs, [17.0, 0.5, 0.05, 0.01]) >>> # prepare initial configuration >>> structure = prim.repeat(3) >>> for k in range(5): ... structure[k].symbol = 'Ag' >>> # set up the simulation and attach the observer >>> calculator = ClusterExpansionCalculator(structure, ce_energy) >>> mc = CanonicalEnsemble(structure=structure, calculator=calculator, ... temperature=600) >>> observer = ClusterExpansionObserver(ce_volume, interval=len(structure)) >>> mc.attach_observer(observer, tag='volume') >>> mc.run(1000) >>> # the observed values are available from the data container >>> volumes = mc.data_container.get('volume')
- get_observable(structure)[source]
Returns the value the cluster expansion predicts for a configuration.
- Parameters:
structure (
Atoms) – Atomic configuration to observe.- Return type:
float
- property interval: int | None
Observation interval in trial steps.
- property return_type: type
Type of the value
get_observable()returns.
- property tag: str
Human-readable tag used for identifying the observer.
ConstituentStrainObserver#
- class mchammer.observers.ConstituentStrainObserver(constituent_strain, interval=None)[source]
This class represents a constituent strain observer.
It records the constituent strain energy of the configuration separately from the energy the cluster expansion describes, which is useful in a simulation with a
ConstituentStrainCalculator, where the potential stored in the data container is the sum of the two. The value is stored in the data container under the keyconstituent_strain_energyand refers to the whole supercell. The section on constituent strain shows the observer in use.The observer reads the structure factors the
ConstituentStrainobject holds but does not move them. It can therefore share the object with the calculator that samples the same configuration.- Parameters:
constituent_strain (
ConstituentStrain) – Object that defines the strain energy of the system. Its supercell has to be the one the simulation samples.interval (
int|None) – Observation interval in trial steps. By default the ensemble the observer is attached to sets the interval to the number of sites in the structure.
- get_observable(structure)[source]
Returns the constituent strain energy of a configuration.
- Parameters:
structure (
Atoms) – Atomic configuration to observe.- Returns:
The constituent strain energy of the whole supercell under the key
constituent_strain_energy.- Return type:
dict
- property interval: int | None
Observation interval in trial steps.
- property return_type: type
Type of the value
get_observable()returns.
- property tag: str
Human-readable tag used for identifying the observer.