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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

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 

 

 

class TargetVectorCalculator(BaseCalculator): 

""" 

A ``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 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__(structure=structure, name=name) 

 

63 ↛ 64line 63 didn't jump to line 64, because the condition on line 63 was never true 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.orbit_data = self.cluster_space.orbit_data 

else: 

self.optimality_weight = None 

self.optimality_tol = None 

self.orbit_data = None 

 

self._cluster_space = cluster_space 

 

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.orbit_data, 

weights=self.weights, 

optimality_weight=self.optimality_weight, 

tol=self.optimality_tol) 

 

def calculate_local_contribution(self, local_indices, occupations: List[int]) -> float: 

""" 

Not yet implemented, forwards calculation to 

calculate_total. 

""" 

return self.calculate_total(occupations) 

 

@property 

def sublattices(self) -> Sublattices: 

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

sl = self.cluster_space.get_sublattices(self.structure) 

return sl 

 

 

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

orbit_data: 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 

orbit_data 

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

weights 

Weight assigned to each cluster vector element 

optimality_weight 

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

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

tol 

numerical tolerance for determining whether two elements are 

exactly 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 = orbit_data[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