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 itertools import combinations, permutations 

2from typing import List 

3 

4import numpy as np 

5from ase import Atoms 

6from ..core.orbit import Orbit 

7from ..core.orbit_list import OrbitList 

8from ..core.lattice_site import LatticeSite 

9 

10 

11def _is_sites_in_orbit(orbit: Orbit, sites: List[LatticeSite]) -> bool: 

12 """Checks if the list of lattice sites is found among the equivalent 

13 sites for the orbit. 

14 

15 Parameters 

16 ---------- 

17 orbit 

18 orbit 

19 sites 

20 list of lattice sites 

21 """ 

22 

23 # Ensure that the number of sites matches the order of the orbit 

24 if len(sites) != orbit.order: 24 ↛ 25line 24 didn't jump to line 25, because the condition on line 24 was never true

25 return False 

26 

27 equivalent_sites = orbit.equivalent_clusters 

28 

29 # Check if the set of lattice sites is found among the equivalent sites 

30 if set(sites) in [set(es) for es in equivalent_sites]: 

31 return True 

32 

33 # Go through all equivalent sites 

34 sites_indices = [s.index for s in sites] 

35 for orbit_sites in equivalent_sites: 

36 orbit_sites_indices = [s.index for s in orbit_sites] 

37 

38 # Skip if the site indices do not match 

39 if set(sites_indices) != set(orbit_sites_indices): 

40 continue 

41 

42 # Loop over all possible ways of pairing sites from the two lists 

43 for comb_sites in [list(zip(sites, pos)) 

44 for pos in permutations(orbit_sites)]: 

45 

46 # Skip all cases that include pairs of sites with different site 

47 # indices 

48 if any(cs[0].index != cs[1].index for cs in comb_sites): 

49 continue 

50 

51 # If the relative offsets for all pairs of sites match, the two 

52 # clusters are equivalent 

53 relative_offsets = [cs[0].unitcell_offset - cs[1].unitcell_offset 

54 for cs in comb_sites] 

55 if all(np.array_equal(ro, relative_offsets[0]) 

56 for ro in relative_offsets): 

57 return True 

58 return False 

59 

60 

61def get_transformation_matrix(structure: Atoms, 

62 full_orbit_list: OrbitList) -> np.ndarray: 

63 """ 

64 Determines the matrix that transforms the cluster functions in the form 

65 of spin variables, (:math:`\\sigma_i\\in\\{-1,1\\}`), to their binary 

66 equivalents, (:math:`x_i\\in\\{0,1\\}`). The form is obtained by 

67 performing the substitution (:math:`\\sigma_i=1-2x_i`) in the 

68 cluster expansion expression of the total energy. 

69 

70 Parameters 

71 ---------- 

72 structure 

73 atomic configuration 

74 full_orbit_list 

75 full orbit list 

76 """ 

77 # Go through all clusters associated with each active orbit and 

78 # determine its contribution to each orbit 

79 orbit_indices = range(len(full_orbit_list)) 

80 transformation = np.zeros((len(orbit_indices) + 1, 

81 len(orbit_indices) + 1)) 

82 transformation[0, 0] = 1.0 

83 for i, orb_index in enumerate(orbit_indices, 1): 

84 orbit = full_orbit_list.get_orbit(orb_index) 

85 repr_sites = orbit.sites_of_representative_cluster 

86 # add contributions to the lower order orbits to which the 

87 # subclusters belong 

88 for sub_order in range(orbit.order + 1): 

89 n_terms_target = len(list(combinations(repr_sites, sub_order))) 

90 n_terms_actual = 0 

91 if sub_order == 0: 

92 transformation[0, i] += 1.0 

93 n_terms_actual += 1 

94 if sub_order == orbit.order: 

95 transformation[i, i] += (-2.0) ** (sub_order) 

96 n_terms_actual += 1 

97 else: 

98 comb_sub_sites = combinations(repr_sites, sub_order) 

99 for sub_sites in comb_sub_sites: 

100 for j, sub_index in enumerate(orbit_indices, 1): 

101 sub_orbit = full_orbit_list.get_orbit(sub_index) 

102 if sub_orbit.order != sub_order: 

103 continue 

104 if _is_sites_in_orbit(sub_orbit, sub_sites): 

105 transformation[j, i] += (-2.0) ** (sub_order) 

106 n_terms_actual += 1 

107 # check that the number of contributions matches the number 

108 # of subclusters 

109 assert(n_terms_actual == n_terms_target) 

110 

111 return transformation 

112 

113 

114def transform_parameters(structure: Atoms, 

115 full_orbit_list: OrbitList, 

116 parameters: np.ndarray) -> np.ndarray: 

117 """ 

118 Transforms the list of parameters, obtained using cluster functions in the 

119 form of of spin variables, (:math:`\\sigma_i\\in\\{-1,1\\}`), to their 

120 equivalents for the case of binary variables, 

121 (:math:`x_i\\in\\{0,1\\}`). 

122 

123 Parameters 

124 ---------- 

125 structure 

126 atomic configuration 

127 full_orbit_list 

128 full orbit list 

129 parameters 

130 parameter vector (spin variables) 

131 """ 

132 A = get_transformation_matrix(structure, full_orbit_list) 

133 return np.dot(A, parameters)