Coverage for mchammer/calculators/target_vector_calculator.py: 97%
53 statements
« prev ^ index » next coverage.py v7.5.0, created at 2024-12-26 04:12 +0000
« prev ^ index » next coverage.py v7.5.0, created at 2024-12-26 04:12 +0000
1from collections import OrderedDict
2from typing import List
4import numpy as np
6from ase import Atoms
7from icet import ClusterSpace
8from icet.core.sublattices import Sublattices
9from mchammer.calculators.base_calculator import BaseCalculator
12class TargetVectorCalculator(BaseCalculator):
13 r"""
14 A :class:`TargetVectorCalculator` enables evaluation of the similarity
15 between a structure and a target cluster vector. Such a comparison
16 can be carried out in many ways, and this implementation follows the
17 measure proposed by van de Walle *et al.* in Calphad **42**, 13
18 (2013) [WalTiwJon13]_. Specifically, the objective function
19 :math:`Q` is calculated as
21 .. math::
22 Q = - \omega L + \sum_{\alpha}
23 \left||\Gamma_{\alpha} - \Gamma^{\text{target}}_{\alpha}\right||.
25 Here, :math:`\Gamma_{\alpha}` are components in the cluster vector
26 and :math:`\Gamma^\text{target}_{\alpha}` the corresponding
27 target values. The factor :math:`\omega` is the radius of the
28 largest pair cluster such that all clusters with the same or smaller
29 radii have :math:`\Gamma_{\alpha} -
30 \Gamma^\text{target}_{\alpha} = 0`.
32 Parameters
33 ----------
34 structure
35 Structure for which to set up calculator.
36 cluster_space
37 Cluster space from which to build calculator.
38 target_vector
39 Vector to which any vector will be compared.
40 weights
41 Weighting of each component in cluster vector
42 comparison. By default set to 1.0 for all components.
43 optimality_weight
44 Factor :math:`L`, a high value of which effectively
45 favors a complete series of optimal cluster correlations
46 for the smallest pairs (see above).
47 optimality_tol
48 Tolerance for determining whether a perfect match
49 has been achieved (used in conjunction with :math:`L`).
50 name
51 Human-readable identifier for this calculator.
52 """
54 def __init__(self, structure: Atoms, cluster_space: ClusterSpace,
55 target_vector: List[float],
56 weights: List[float] = None,
57 optimality_weight: float = 1.0,
58 optimality_tol: float = 1e-5,
59 name: str = 'Target vector calculator') -> None:
60 super().__init__(name=name)
62 if len(target_vector) != len(cluster_space): 62 ↛ 63line 62 didn't jump to line 63, because the condition on line 62 was never true
63 raise ValueError('Cluster space and target vector '
64 'must have the same length')
65 self.cluster_space = cluster_space
66 self.target_vector = target_vector
68 if weights is None:
69 weights = np.array([1.0] * len(cluster_space))
70 else:
71 if len(weights) != len(cluster_space):
72 raise ValueError('Cluster space and weights '
73 'must have the same length')
74 self.weights = np.array(weights)
76 if optimality_weight is not None:
77 self.optimality_weight = optimality_weight
78 self.optimality_tol = optimality_tol
79 self.as_list = self.cluster_space.as_list
80 else:
81 self.optimality_weight = None
82 self.optimality_tol = None
83 self.as_list = None
85 self._cluster_space = cluster_space
86 self._structure = structure
87 self._sublattices = self._cluster_space.get_sublattices(structure)
89 def calculate_total(self, occupations: List[int]) -> float:
90 """
91 Calculates and returns the similarity value :math:`Q`
92 of the current configuration.
94 Parameters
95 ----------
96 occupations
97 The entire occupation vector (i.e., list of atomic species).
98 """
99 self._structure.set_atomic_numbers(occupations)
100 cv = self.cluster_space.get_cluster_vector(self._structure)
101 return compare_cluster_vectors(cv, self.target_vector,
102 self.as_list,
103 weights=self.weights,
104 optimality_weight=self.optimality_weight,
105 tol=self.optimality_tol)
107 def calculate_change(self):
108 raise NotImplementedError
110 @property
111 def sublattices(self) -> Sublattices:
112 """ Sublattices of the calculators structure. """
113 return self._sublattices
116def compare_cluster_vectors(cv_1: np.ndarray, cv_2: np.ndarray,
117 as_list: List[OrderedDict],
118 weights: List[float] = None,
119 optimality_weight: float = 1.0,
120 tol: float = 1e-5) -> float:
121 """
122 Calculate a quantity that measures similarity between two cluster
123 vecors.
125 Parameters
126 ----------
127 cv_1
128 Cluster vector 1.
129 cv_2
130 Cluster vector 2.
131 as_list
132 Orbit data as obtained by :attr:`ClusterSpace.as_list`.
133 weights
134 Weight assigned to each cluster vector element.
135 optimality_weight
136 Wuantity :math:`L` in [WalTiwJon13]_
137 (see :class:`mchammer.calculators.TargetVectorCalculator`).
138 tol
139 Numerical tolerance for determining whether two elements are equal.
140 """
141 if weights is None:
142 weights = np.ones(len(cv_1))
143 diff = abs(cv_1 - cv_2)
144 score = np.dot(diff, weights)
145 if optimality_weight:
146 longest_optimal_radius = 0
147 for orbit_index, d in enumerate(diff):
148 orbit = as_list[orbit_index]
149 if orbit['order'] != 2:
150 continue
151 if d < tol:
152 longest_optimal_radius = orbit['radius']
153 else:
154 break
155 score -= optimality_weight * longest_optimal_radius
156 return score