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