1use gchemol_gut::prelude::*;
17use vecfx::*;
18mod mic;
22mod supercell;
23mod utils;
24
25use crate::utils::*;
26#[derive(Debug, Clone, Copy, Deserialize, Serialize)]
31pub struct Lattice {
32 matrix: Matrix3f,
34 origin: Vector3f,
36 inv_matrix: Matrix3f,
38}
39
40impl Default for Lattice {
41 fn default() -> Self {
42 let matrix = Matrix3f::identity();
43 let inv_matrix = get_inv_matrix(&matrix);
44 Lattice {
45 matrix,
46 inv_matrix,
47 origin: Vector3f::zeros(),
48 }
49 }
50}
51impl Lattice {
55 pub fn new<T: Into<Vector3f> + Copy>(tvs: [T; 3]) -> Self {
57 let vectors = [tvs[0].into(), tvs[1].into(), tvs[2].into()];
58 let matrix = Matrix3f::from_columns(&vectors);
59 Self::from_matrix(matrix)
60 }
61
62 pub fn from_matrix<T: Into<Matrix3f>>(tvs: T) -> Self {
64 let matrix = tvs.into();
65 let inv_matrix = get_inv_matrix(&matrix);
66 Lattice {
67 matrix,
68 inv_matrix,
69 ..Default::default()
70 }
71 }
72
73 pub fn from_params(a: f64, b: f64, c: f64, alpha: f64, beta: f64, gamma: f64) -> Self {
76 let alpha = alpha.to_radians();
77 let beta = beta.to_radians();
78 let gamma = gamma.to_radians();
79
80 let acos = alpha.cos();
81 let bcos = beta.cos();
82 let gcos = gamma.cos();
83 let gsin = gamma.sin();
84 let v = (1. - acos.powi(2) - bcos.powi(2) - gcos.powi(2) + 2.0 * acos * bcos * gcos).sqrt();
85
86 let va = [a, 0.0, 0.0];
87
88 let vb = [b * gcos, b * gsin, 0.0];
89
90 let vc = [c * bcos, c * (acos - bcos * gcos) / gsin, c * v / gsin];
91
92 Lattice::new([va, vb, vc])
93 }
94
95 pub fn widths(&self) -> [f64; 3] {
98 let volume = self.volume();
99 let [van, vbn, vcn] = self.lengths();
100
101 let wa = volume / (vbn * vcn);
102 let wb = volume / (vcn * van);
103 let wc = volume / (van * vbn);
104
105 [wa, wb, wc]
106 }
107
108 pub fn volume(&self) -> f64 {
111 get_cell_volume(self.matrix)
112 }
113
114 pub fn set_origin<T: Into<Vector3f>>(&mut self, loc: T) {
116 self.origin = loc.into()
117 }
118
119 pub fn lengths(&self) -> [f64; 3] {
121 get_cell_lengths(self.matrix).into()
122 }
123
124 pub fn angles(&self) -> [f64; 3] {
126 get_cell_angles(self.matrix).into()
127 }
128
129 pub fn scale_by(&mut self, v: f64) {
131 assert!(v.is_sign_positive(), "invalid scale factor: {v}");
132 self.matrix *= v;
133 self.inv_matrix = get_inv_matrix(&self.matrix);
134 }
135
136 pub fn scale_by_a(&mut self, v: f64) {
138 self.scale_by_abc(v, 0)
139 }
140
141 pub fn scale_by_b(&mut self, v: f64) {
143 self.scale_by_abc(v, 1)
144 }
145
146 pub fn scale_by_c(&mut self, v: f64) {
148 self.scale_by_abc(v, 2)
149 }
150
151 fn scale_by_abc(&mut self, v: f64, i: usize) {
153 assert!(v.is_sign_positive(), "invalid scale factor: {v}");
154 let mut x = self.matrix.column_mut(i);
155 x *= v;
156 self.inv_matrix = get_inv_matrix(&self.matrix);
157 }
158
159 pub fn origin(&self) -> Vector3f {
161 self.origin
162 }
163
164 pub fn to_frac<T: Into<Vector3f>>(&self, p: T) -> Vector3f {
166 self.inv_matrix * (p.into() - self.origin)
167 }
168
169 pub fn to_cart<T: Into<Vector3f>>(&self, p: T) -> Vector3f {
171 self.matrix * p.into() + self.origin
172 }
173
174 pub fn vector_a(&self) -> Vector3f {
176 self.matrix.column(0).into()
177 }
178
179 pub fn vector_b(&self) -> Vector3f {
181 self.matrix.column(1).into()
182 }
183
184 pub fn vector_c(&self) -> Vector3f {
186 self.matrix.column(2).into()
187 }
188
189 pub fn vectors(&self) -> [Vector3f; 3] {
191 [self.vector_a(), self.vector_b(), self.vector_c()]
192 }
193
194 pub fn matrix(&self) -> Matrix3f {
196 self.matrix
197 }
198
199 pub fn inv_matrix(&self) -> Matrix3f {
201 self.inv_matrix
202 }
203
204 pub fn is_orthorhombic(&self) -> bool {
206 let diag = self.matrix.diagonal();
207 let m = Matrix3f::from_diagonal(&diag);
208 m == self.matrix
209 }
210
211 pub fn wrap<T: Into<Vector3f>>(&self, vec: T) -> Vector3f {
214 let f = self.to_frac(vec);
215 let fcoords_wrapped = self.wrap_frac(f);
216 self.to_cart(fcoords_wrapped)
217 }
218
219 pub fn wrap_frac<T: Into<Vector3f>>(&self, f: T) -> Vector3f {
222 let f = f.into();
223 let fcoords_wrapped = [f.x - f.x.floor(), f.y - f.y.floor(), f.z - f.z.floor()];
224 fcoords_wrapped.into()
225 }
226
227 pub fn distance<T: Into<Vector3f>>(&self, pi: T, pj: T) -> f64 {
234 let p = pj.into() - pi.into();
235 let pmic = self.apply_mic(p);
236 pmic.norm()
237 }
238
239 pub fn apply_mic<T: Into<[f64; 3]>>(&self, p: T) -> Vector3f {
241 let p = p.into();
242 let v_naive = self.apply_mic_tuckerman(p);
244 if self.is_orthorhombic() {
245 v_naive
246 } else {
247 let r_max = 0.5 * self.widths().min();
248 if v_naive.norm() < r_max {
249 v_naive
250 } else {
251 self.apply_mic_brute_force(p)
252 }
253 }
254 }
255}
256