Coverage for local_installation/dynasor/qpoints/lattice.py: 96%

46 statements  

« prev     ^ index     » next       coverage.py v7.3.2, created at 2024-12-07 16:45 +0000

1import warnings 

2import numpy as np 

3from numpy.typing import NDArray 

4 

5 

6from dynasor.qpoints.tools import get_P_matrix, get_commensurate_lattice_points, find_on_line 

7 

8 

9class Lattice: 

10 

11 def __init__(self, primitive: NDArray, supercell: NDArray): 

12 """Representation of a crystal supercell 

13 

14 The supercell S is given by the primitive cell p and a repetition 

15 matrix P such that: 

16 

17 dot(P, p) = S 

18 

19 In this convention the cell vectors are row vectors of p and S as in 

20 ASE. An inverse cell is defined as: 

21 

22 c_inv = inv(c).T 

23 

24 and the reciprocal cell is defined as: 

25 

26 c_rec = 2*pi*inv(c).T 

27 

28 Notice that the inverse cell here is defined with the tranpose so that 

29 the lattic vectors of the inverse/reciprocal lattice are also row 

30 vectors. The above also implies: 

31 

32 dot(P.T, S_inv) = p_inv 

33 

34 The inverse supercell S_inv defines a new lattice in reciprocal space. 

35 Those inverse lattice points which resides inside the inverse primitive 

36 cell p_inv are called commensurate lattice points. These are typically 

37 the only points of interest in MD simulations from a crystallographic 

38 and lattice dynamics point of view. 

39 

40 The convention taken here is that the reciprocal cell carries the 2pi 

41 factor onto the cartesian q-points. This is consistent with e.g. 

42 Kittel. The reduced coordinates are always with respect to the 

43 reciprocal primitive cell. 

44 

45 Parameters 

46 ---------- 

47 primitive 

48 cell metric of the primitive cell with lattice vectors as rows. 

49 supercell 

50 cell metric of the supercell with lattice vectors as rows 

51 """ 

52 

53 self._primitive = np.array(primitive) 

54 self._supercell = np.array(supercell) 

55 

56 self._P = get_P_matrix(self.primitive, self.supercell) 

57 

58 # As stated in the doc the P matrix relating the inverse cells are just P.T 

59 com = get_commensurate_lattice_points(self.P.T) 

60 self._qpoints = com @ self.reciprocal_supercell 

61 

62 @property 

63 def primitive(self): 

64 """Returns the primitive cell with lattice vectors as rows""" 

65 return self._primitive 

66 

67 @property 

68 def supercell(self): 

69 """Returns the supercell with lattice vectors as rows""" 

70 return self._supercell 

71 

72 @property 

73 def reciprocal_primitive(self): 

74 """Returns inv(primitive).T so that the rows are the inverse lattice vectors""" 

75 return 2*np.pi * np.linalg.inv(self.primitive.T) # inverse lattice as rows 

76 

77 @property 

78 def reciprocal_supercell(self): 

79 """Returns inv(super).T so that the rows are the inverse lattice vectors""" 

80 return 2*np.pi * np.linalg.inv(self.supercell.T) # reciprocal lattice as rows 

81 

82 @property 

83 def P(self): 

84 """The P-matrix for this system, P @ primitive = supercell""" 

85 return self._P 

86 

87 def __repr__(self): 

88 rep = (f'{self.__class__.__name__}(' 

89 f'primitive={self.primitive.tolist()}, ' 

90 f'supercell={self.supercell.tolist()})') 

91 return rep 

92 

93 @property 

94 def qpoints(self): 

95 """Cartesian commensurate q-points""" 

96 return self._qpoints 

97 

98 def __len__(self): 

99 return len(self._qpoints) 

100 

101 def reduced_to_cartesian(self, qpoints): 

102 """Convert from reduced to cartesian coordinates""" 

103 return qpoints @ self.reciprocal_primitive 

104 

105 def cartesian_to_reduced(self, qpoints): 

106 """Convert from Cartesian to reduced coordinates.""" 

107 return np.linalg.solve(self.reciprocal_primitive.T, qpoints.T).T 

108 

109 def make_path(self, start, stop): 

110 """Takes qpoints in reduced coordinates and returns all points in between 

111 

112 Parameters 

113 ---------- 

114 start 

115 coordinate of starting point in reduced inverse coordinates. 

116 e.g. a zone mode is given as (0.5, 0, 0), (0,0,0) == (1,0,0) etc. 

117 stop 

118 stop position 

119 

120 Returns 

121 ------- 

122 qpoints 

123 coordinates of commensurate points along path in cartesian reciprocals 

124 dists 

125 fractional distance along path 

126 """ 

127 start = np.array(start) 

128 stop = np.array(stop) 

129 fracs = find_on_line(start, stop, self.P.T) 

130 if not len(fracs): 

131 warnings.warn('No q-points along path!') 

132 return np.zeros(shape=(0, 3)), np.zeros(shape=(0,)) 

133 points = np.array([start + float(f) * (stop - start) for f in fracs]) 

134 qpoints = self.reduced_to_cartesian(points) 

135 dists = np.array([float(f) for f in fracs]) 

136 return qpoints, dists