Source code for mchammer.calculators.target_vector_calculator

from collections import OrderedDict
from typing import List

import numpy as np

from ase import Atoms
from icet import ClusterSpace
from icet.core.sublattices import Sublattices
from mchammer.calculators.base_calculator import BaseCalculator


[docs] class TargetVectorCalculator(BaseCalculator): r""" A :class:`TargetVectorCalculator` enables evaluation of the similarity between a structure and a target cluster vector. Such a comparison can be carried out in many ways, and this implementation follows the measure proposed by van de Walle *et al.* in Calphad **42**, 13 (2013) [WalTiwJon13]_. Specifically, the objective function :math:`Q` is calculated as .. math:: Q = - \omega L + \sum_{\alpha} \left||\Gamma_{\alpha} - \Gamma^{\text{target}}_{\alpha}\right||. Here, :math:`\Gamma_{\alpha}` are components in the cluster vector and :math:`\Gamma^\text{target}_{\alpha}` the corresponding target values. The factor :math:`\omega` is the radius of the largest pair cluster such that all clusters with the same or smaller radii have :math:`\Gamma_{\alpha} - \Gamma^\text{target}_{\alpha} = 0`. Parameters ---------- structure Structure for which to set up calculator. cluster_space Cluster space from which to build calculator. target_vector Vector to which any vector will be compared. weights Weighting of each component in cluster vector comparison. By default set to 1.0 for all components. optimality_weight Factor :math:`L`, a high value of which effectively favors a complete series of optimal cluster correlations for the smallest pairs (see above). optimality_tol Tolerance for determining whether a perfect match has been achieved (used in conjunction with :math:`L`). name Human-readable identifier for this calculator. """ def __init__(self, structure: Atoms, cluster_space: ClusterSpace, target_vector: List[float], weights: List[float] = None, optimality_weight: float = 1.0, optimality_tol: float = 1e-5, name: str = 'Target vector calculator') -> None: super().__init__(name=name) if len(target_vector) != len(cluster_space): raise ValueError('Cluster space and target vector ' 'must have the same length') self.cluster_space = cluster_space self.target_vector = target_vector if weights is None: weights = np.array([1.0] * len(cluster_space)) else: if len(weights) != len(cluster_space): raise ValueError('Cluster space and weights ' 'must have the same length') self.weights = np.array(weights) if optimality_weight is not None: self.optimality_weight = optimality_weight self.optimality_tol = optimality_tol self.as_list = self.cluster_space.as_list else: self.optimality_weight = None self.optimality_tol = None self.as_list = None self._cluster_space = cluster_space self._structure = structure self._sublattices = self._cluster_space.get_sublattices(structure)
[docs] def calculate_total(self, occupations: List[int]) -> float: """ Calculates and returns the similarity value :math:`Q` of the current configuration. Parameters ---------- occupations The entire occupation vector (i.e., list of atomic species). """ self._structure.set_atomic_numbers(occupations) cv = self.cluster_space.get_cluster_vector(self._structure) return compare_cluster_vectors(cv, self.target_vector, self.as_list, weights=self.weights, optimality_weight=self.optimality_weight, tol=self.optimality_tol)
def calculate_change(self): raise NotImplementedError @property def sublattices(self) -> Sublattices: """ Sublattices of the calculators structure. """ return self._sublattices
[docs] def compare_cluster_vectors(cv_1: np.ndarray, cv_2: np.ndarray, as_list: List[OrderedDict], weights: List[float] = None, optimality_weight: float = 1.0, tol: float = 1e-5) -> float: """ Calculate a quantity that measures similarity between two cluster vecors. Parameters ---------- cv_1 Cluster vector 1. cv_2 Cluster vector 2. as_list Orbit data as obtained by :attr:`ClusterSpace.as_list`. weights Weight assigned to each cluster vector element. optimality_weight Wuantity :math:`L` in [WalTiwJon13]_ (see :class:`mchammer.calculators.TargetVectorCalculator`). tol Numerical tolerance for determining whether two elements are equal. """ if weights is None: weights = np.ones(len(cv_1)) diff = abs(cv_1 - cv_2) score = np.dot(diff, weights) if optimality_weight: longest_optimal_radius = 0 for orbit_index, d in enumerate(diff): orbit = as_list[orbit_index] if orbit['order'] != 2: continue if d < tol: longest_optimal_radius = orbit['radius'] else: break score -= optimality_weight * longest_optimal_radius return score