Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1from collections import OrderedDict 

2from typing import List 

3 

4import numpy as np 

5 

6from ase import Atoms 

7from icet import ClusterSpace 

8from icet.core.sublattices import Sublattices 

9from mchammer.calculators.base_calculator import BaseCalculator 

10 

11 

12class TargetVectorCalculator(BaseCalculator): 

13 """ 

14 A ``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 

20 

21 .. math:: 

22 Q = - \\omega L + \\sum_{\\alpha} 

23 \\left| \\Gamma_{\\alpha} - \\Gamma^{\\text{target}}_{\\alpha} 

24 \\right|. 

25 

26 Here, :math:`\\Gamma_{\\alpha}` are components in the cluster vector 

27 and :math:`\\Gamma^\\text{target}_{\\alpha}` the corresponding 

28 target values. The factor :math:`\\omega` is the radius of the 

29 largest pair cluster such that all clusters with the same or smaller 

30 radii have :math:`\\Gamma_{\\alpha} - 

31 \\Gamma^\\text{target}_{\\alpha} = 0`. 

32 

33 Parameters 

34 ---------- 

35 structure 

36 structure for which to set up calculator 

37 cluster_space 

38 cluster space from which to build calculator 

39 target_vector 

40 vector to which any vector will be compared 

41 weights 

42 weighting of each component in cluster vector 

43 comparison, by default 1.0 for all components 

44 optimality_weight 

45 factor :math:`L`, a high value of which effectively 

46 favors a complete series of optimal cluster correlations 

47 for the smallest pairs (see above) 

48 optimality_tol 

49 tolerance for determining whether a perfect match 

50 has been achieved (used in conjunction with :math:`L`) 

51 name 

52 human-readable identifier for this calculator 

53 """ 

54 

55 def __init__(self, structure: Atoms, cluster_space: ClusterSpace, 

56 target_vector: List[float], 

57 weights: List[float] = None, 

58 optimality_weight: float = 1.0, 

59 optimality_tol: float = 1e-5, 

60 name: str = 'Target vector calculator') -> None: 

61 super().__init__(name=name) 

62 

63 if len(target_vector) != len(cluster_space): 63 ↛ 64line 63 didn't jump to line 64, because the condition on line 63 was never true

64 raise ValueError('Cluster space and target vector ' 

65 'must have the same length') 

66 self.cluster_space = cluster_space 

67 self.target_vector = target_vector 

68 

69 if weights is None: 

70 weights = np.array([1.0] * len(cluster_space)) 

71 else: 

72 if len(weights) != len(cluster_space): 

73 raise ValueError('Cluster space and weights ' 

74 'must have the same length') 

75 self.weights = np.array(weights) 

76 

77 if optimality_weight is not None: 

78 self.optimality_weight = optimality_weight 

79 self.optimality_tol = optimality_tol 

80 self.orbit_data = self.cluster_space.orbit_data 

81 else: 

82 self.optimality_weight = None 

83 self.optimality_tol = None 

84 self.orbit_data = None 

85 

86 self._cluster_space = cluster_space 

87 self._structure = structure 

88 self._sublattices = self._cluster_space.get_sublattices(structure) 

89 

90 def calculate_total(self, occupations: List[int]) -> float: 

91 """ 

92 Calculates and returns the similarity value :math:`Q` 

93 of the current configuration. 

94 

95 Parameters 

96 ---------- 

97 occupations 

98 the entire occupation vector (i.e. list of atomic species) 

99 """ 

100 self._structure.set_atomic_numbers(occupations) 

101 cv = self.cluster_space.get_cluster_vector(self._structure) 

102 return compare_cluster_vectors(cv, self.target_vector, 

103 self.orbit_data, 

104 weights=self.weights, 

105 optimality_weight=self.optimality_weight, 

106 tol=self.optimality_tol) 

107 

108 def calculate_change(self): 

109 raise NotImplementedError 

110 

111 @property 

112 def sublattices(self) -> Sublattices: 

113 """Sublattices of the calculators structure.""" 

114 return self._sublattices 

115 

116 

117def compare_cluster_vectors(cv_1: np.ndarray, cv_2: np.ndarray, 

118 orbit_data: List[OrderedDict], 

119 weights: List[float] = None, 

120 optimality_weight: float = 1.0, 

121 tol: float = 1e-5) -> float: 

122 """ 

123 Calculate a quantity that measures similarity between two cluster 

124 vecors. 

125 

126 Parameters 

127 ---------- 

128 cv_1 

129 cluster vector 1 

130 cv_2 

131 cluster vector 2 

132 orbit_data 

133 orbit data as obtained by ``ClusterSpace.orbit_data`` 

134 weights 

135 Weight assigned to each cluster vector element 

136 optimality_weight 

137 quantity :math:`L` in [WalTiwJon13]_ 

138 (see :class:`mchammer.calculators.TargetVectorCalculator`) 

139 tol 

140 numerical tolerance for determining whether two elements are 

141 exactly equal 

142 """ 

143 if weights is None: 

144 weights = np.ones(len(cv_1)) 

145 diff = abs(cv_1 - cv_2) 

146 score = np.dot(diff, weights) 

147 if optimality_weight: 

148 longest_optimal_radius = 0 

149 for orbit_index, d in enumerate(diff): 

150 orbit = orbit_data[orbit_index] 

151 if orbit['order'] != 2: 

152 continue 

153 if d < tol: 

154 longest_optimal_radius = orbit['radius'] 

155 else: 

156 break 

157 score -= optimality_weight * longest_optimal_radius 

158 return score