Source code for mchammer.calculators.constituent_strain_calculator

from icet.tools import ConstituentStrain
from icet import ClusterExpansion
from mchammer.calculators import ClusterExpansionCalculator
import numpy as np


[docs] class ConstituentStrainCalculator(ClusterExpansionCalculator): """ Calculator for handling cluster expansions with strain. Parameters ---------- constituent_strain :class:`ConstituentStrain` object defining the strain energy properties of the system. The supercell used to create this object should correspond to the one used when running Monte Carlo simulations with this calculator cluster_expansion Cluster expansion based on which to set up :class:`ClusterExpansionCalculator`. name Human-readable identifier for this calculator. scaling Scaling factor applied to the property value predicted by the cluster expansion. """ def __init__(self, constituent_strain: ConstituentStrain, cluster_expansion: ClusterExpansion, name: str = 'Constituent Strain Calculator', scaling: float | None = None): self.constituent_strain = constituent_strain super().__init__(structure=constituent_strain.supercell, cluster_expansion=cluster_expansion, name=name, scaling=scaling)
[docs] def set_occupations(self, occupations: list[int]) -> None: """ Sets the configuration this calculator describes, both for the underlying cluster expansion calculator and for the :class:`ConstituentStrain` object. Parameters ---------- occupations The entire occupation vector (atomic numbers). """ super().set_occupations(occupations) # The structure factor stored for each k-point describes one # configuration, and the change evaluation reads it as the value # before the change rather than recomputing it from the occupations # it is given. Moving to a configuration that is not reachable from # the stored one through accepted changes therefore has to rebuild it, # which a total evaluation does for every k-point. self.constituent_strain.get_constituent_strain(np.asarray(occupations))
[docs] def calculate_total(self, *, occupations: np.ndarray) -> float: """ Calculates and returns the total property value of the current configuration. Parameters ---------- occupations The entire occupation vector (i.e., an array of atomic numbers as integers). """ e = super().calculate_total(occupations=occupations) # The structure factors are left where they are, since a total # evaluation scores the occupations it is given and must not move the # configuration the next change is evaluated against. e += len(occupations) * \ self.constituent_strain.get_constituent_strain( occupations, update_structure_factors=False) return e
[docs] def calculate_change(self, *, sites: list[int], current_occupations: list[int], new_site_occupations: list[int]) -> float: """ Calculates and returns the sum of the contributions to the property due to the sites specified in :attr:`sites`. Parameters ---------- sites Indices of sites at which occupations will be changed. current_occupations Entire occupation vector (atomic numbers) before change. new_site_occupations Atomic numbers after change at the sites defined by :attr:`sites`. """ if len(new_site_occupations) > 1: raise NotImplementedError('Only single flips are currently allowed in ' 'conjunction with the constituent strain calculator.') e = super().calculate_change(sites=sites, current_occupations=current_occupations, new_site_occupations=new_site_occupations) de_cs = self.constituent_strain.get_constituent_strain_change(current_occupations, sites[0]) e += len(current_occupations) * de_cs return e
[docs] def accept_change(self, *, sites: list[int] | None = None, species: list[int] | None = None) -> None: """Advances the configuration held by the underlying cluster expansion calculator and informs the :class:`ConstituentStrain` object that the most recent change was accepted, such that the new structure factor can be stored. Parameters ---------- sites Indices of the sites whose occupations changed. species New occupations (atomic numbers) on those sites. """ super().accept_change(sites=sites, species=species) # An empty change advances nothing, so the structure factors must not # advance either. Forwarding it would commit the candidate the last # change evaluation staged, which is the trial that was rejected. if len(sites) == 0: return self.constituent_strain.accept_change()