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

""" 

Handling of Hermite Normal Form matrices 

""" 

 

import numpy as np 

from .smith_normal_form import SmithNormalForm 

 

 

class HermiteNormalForm(object): 

""" 

Hermite Normal Form matrix. 

""" 

 

def __init__(self, H, rotations, translations, basis_shifts): 

self.H = H 

self.snf = SmithNormalForm(H) 

self.transformations = [] 

self.compute_transformations(rotations, translations, basis_shifts) 

 

def compute_transformations(self, rotations, translations, basis_shifts, 

tol=1e-3): 

""" 

Save transformations (based on rotations) that turns the supercell 

into an equivalent supercell. Precompute these transformations, 

consisting of permutation as well as translation and basis shift, for 

later use. 

 

Parameters 

---------- 

rotations : list of ndarrays 

translations : list of ndarrays 

basis_shifts : ndarray 

""" 

 

for R, T, basis_shift in zip(rotations, translations, 

basis_shifts): 

check = np.dot(np.dot(np.linalg.inv(self.H), R), self.H) 

check = check - np.round(check) 

if (abs(check) < tol).all(): 

LRL = np.dot(self.snf.L, np.dot(R, np.linalg.inv(self.snf.L))) 

 

# Should be an integer matrix 

assert (abs(LRL - np.round(LRL)) < tol).all() 

LRL = np.round(LRL).astype(np.int64) 

LT = np.dot(T, self.snf.L.T) 

self.transformations.append([LRL, LT, basis_shift]) 

 

 

def yield_hermite_normal_forms(det, pbc): 

""" 

Yield all Hermite Normal Form matrices with determinant det. 

 

Parameters 

---------- 

det : int 

Target determinant of HNFs 

pbc : list of bools 

Periodic boundary conditions of the primitive structure 

 

Yields 

------ 

ndarray 

3x3 HNF matrix 

""" 

 

# 1D 

if sum(pbc) == 1: 

hnf = np.eye(3, dtype=int) 

69 ↛ 73line 69 didn't jump to line 73, because the loop on line 69 didn't complete for i, bc in enumerate(pbc): 

if bc: 

hnf[i, i] = det 

break 

yield hnf 

 

# 2D 

elif sum(pbc) == 2: 

for a in range(1, det + 1): 

if det % a == 0: 

c = det // a 

for b in range(0, c): 

81 ↛ 82line 81 didn't jump to line 82, because the condition on line 81 was never true if not pbc[0]: 

hnf = [[1, 0, 0], 

[0, a, 0], 

[0, b, c]] 

85 ↛ 86line 85 didn't jump to line 86, because the condition on line 85 was never true elif not pbc[1]: 

hnf = [[a, 0, 0], 

[0, 1, 0], 

[b, 0, c]] 

else: 

hnf = [[a, 0, 0], 

[b, c, 0], 

[0, 0, 1]] 

yield np.array(hnf) 

 

# 3D 

else: 

for a in range(1, det + 1): 

if det % a == 0: 

for c in range(1, det // a + 1): 

if det // a % c == 0: 

f = det // (a * c) 

for b in range(0, c): 

for d in range(0, f): 

for e in range(0, f): 

hnf = [[a, 0, 0], 

[b, c, 0], 

[d, e, f]] 

yield np.array(hnf) 

 

 

def yield_reduced_hnfs(ncells, symmetries, pbc, tol=1e-3): 

""" 

For a fixed determinant N (i.e., a number of atoms N), yield all 

Hermite Normal Forms (HNF) that are inequivalent under symmetry 

operations of the parent lattice.' 

 

Parameters 

---------- 

N : int 

Determinant (or, equivalently, the number of atoms) of the HNF. 

symmetries : dict of lists 

Symmetry operations of the parent lattice. 

pbc : list of bools 

Periodic boundary conditions of the primitive structure 

 

Yields 

------ 

list of ndarrays 

Symmetrically inequivalent HNFs with determinant N. 

""" 

rotations = symmetries['rotations'] 

translations = symmetries['translations'] 

basis_shifts = symmetries['basis_shifts'] 

hnfs = [] 

 

for hnf in yield_hermite_normal_forms(ncells, pbc): 

 

# Throw away HNF:s that yield equivalent supercells 

hnf_inv = np.linalg.inv(hnf) 

duplicate = False 

for R in rotations: 

HR = np.dot(hnf_inv, R) 

for hnf_previous in hnfs: 

check = np.dot(HR, hnf_previous.H) 

check = check - np.round(check) 

if (abs(check) < tol).all(): 

duplicate = True 

break 

if duplicate: 

break 

if duplicate: 

continue 

 

# If it's not a duplicate, save the hnf 

# and the supercell so that it can be compared to 

hnf = HermiteNormalForm(hnf, rotations, translations, basis_shifts) 

hnfs.append(hnf) 

yield hnf