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

## 46 statements

, created at 2023-11-30 21:04 +0000

1import warnings

2import numpy as np

3from numpy.typing import NDArray

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

9class Lattice:

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

12 """Representation of a crystal supercell

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

15 matrix P such that:

17 dot(P, p) = S

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

20 ASE. An inverse cell is defined as:

22 c_inv = inv(c).T

24 and the reciprocal cell is defined as:

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

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:

32 dot(P.T, S_inv) = p_inv

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.

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.

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 """

53 self._primitive = np.array(primitive)

54 self._supercell = np.array(supercell)

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

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

62 @property

63 def primitive(self):

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

65 return self._primitive

67 @property

68 def supercell(self):

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

70 return self._supercell

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

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

82 @property

83 def P(self):

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

85 return self._P

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

93 @property

94 def qpoints(self):

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

96 return self._qpoints

98 def __len__(self):

99 return len(self._qpoints)

101 def reduced_to_cartesian(self, qpoints):

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

103 return qpoints @ self.reciprocal_primitive

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

109 def make_path(self, start, stop):

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

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

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