1use super::util::Axis;
4use crate::alloy::Alloy;
5use crate::error::{Result, VegasLatticeError};
6use crate::mask::Mask;
7use crate::site::Site;
8use crate::vertex::Vertex;
9use rand::Rng;
10use serde::{Deserialize, Serialize};
11use std::iter::repeat;
12use std::str::FromStr;
13
14#[derive(Clone, Debug, Serialize, Deserialize)]
20pub struct Lattice {
21 size: (f64, f64, f64),
22 sites: Vec<Site>,
23 vertices: Vec<Vertex>,
24}
25
26impl Lattice {
27 pub fn try_new(size: (f64, f64, f64)) -> Result<Self> {
29 if size.0 < 0.0 || size.1 < 0.0 || size.2 < 0.0 {
30 return Err(VegasLatticeError::NegativeSize);
31 }
32 Ok(Lattice {
33 size,
34 sites: Vec::new(),
35 vertices: Vec::new(),
36 })
37 }
38
39 pub fn sc(a: f64) -> Self {
41 let sites = vec![Site::new("A")];
42 let vertices = vec![
43 Vertex::new(0, 0, (1, 0, 0)),
44 Vertex::new(0, 0, (0, 1, 0)),
45 Vertex::new(0, 0, (0, 0, 1)),
46 ];
47 Lattice {
48 size: (a, a, a),
49 sites,
50 vertices,
51 }
52 }
53
54 pub fn bcc(a: f64) -> Self {
56 let sites = vec![
57 Site::new("A"),
58 Site::new("B").with_position((0.5 * a, 0.5 * a, 0.5 * a)),
59 ];
60 let vertices = vec![
61 Vertex::new(0, 1, (0, 0, 0)),
62 Vertex::new(0, 1, (0, -1, 0)),
63 Vertex::new(0, 1, (-1, 0, 0)),
64 Vertex::new(0, 1, (-1, -1, 0)),
65 Vertex::new(0, 1, (0, 0, -1)),
66 Vertex::new(0, 1, (0, -1, -1)),
67 Vertex::new(0, 1, (-1, 0, -1)),
68 Vertex::new(0, 1, (-1, -1, -1)),
69 ];
70 Lattice {
71 size: (a, a, a),
72 sites,
73 vertices,
74 }
75 }
76
77 pub fn fcc(a: f64) -> Self {
79 let sites = vec![
80 Site::new("A"),
81 Site::new("B").with_position((0.5 * a, 0.5 * a, 0.0)),
82 Site::new("C").with_position((0.5 * a, 0.0, 0.5 * a)),
83 Site::new("D").with_position((0.0, 0.5 * a, 0.5 * a)),
84 ];
85 let vertices = vec![
86 Vertex::new(0, 1, (0, 0, 0)),
88 Vertex::new(0, 1, (-1, 0, 0)),
89 Vertex::new(0, 1, (-1, -1, 0)),
90 Vertex::new(0, 1, (0, -1, 0)),
91 Vertex::new(0, 2, (0, 0, 0)),
93 Vertex::new(0, 2, (-1, 0, 0)),
94 Vertex::new(0, 2, (-1, 0, -1)),
95 Vertex::new(0, 2, (0, 0, -1)),
96 Vertex::new(0, 3, (0, 0, 0)),
98 Vertex::new(0, 3, (0, -1, 0)),
99 Vertex::new(0, 3, (0, -1, -1)),
100 Vertex::new(0, 3, (0, 0, -1)),
101 ];
102 Lattice {
103 size: (a, a, a),
104 sites,
105 vertices,
106 }
107 }
108
109 pub fn size(&self) -> (f64, f64, f64) {
111 self.size
112 }
113
114 pub fn sites(&self) -> &[Site] {
116 &self.sites
117 }
118
119 pub fn vertices(&self) -> &[Vertex] {
121 &self.vertices
122 }
123
124 pub fn try_with_size(mut self, size: (f64, f64, f64)) -> Result<Self> {
126 self.size = size;
127 self.validate()
128 }
129
130 pub fn try_with_sites(mut self, sites: Vec<Site>) -> Result<Self> {
132 self.sites = sites;
133 self.validate()
134 }
135
136 pub fn try_with_vertices(mut self, vertices: Vec<Vertex>) -> Result<Self> {
138 self.vertices = vertices;
139 self.validate()
140 }
141
142 fn are_vertices_consistent(&self) -> bool {
143 self.vertices
144 .iter()
145 .map(|vertex| vertex.source())
146 .chain(self.vertices.iter().map(|vertex| vertex.target()))
147 .all(|id| id < self.sites.len())
148 }
149
150 fn validate(self) -> Result<Self> {
152 if !self.are_vertices_consistent() {
153 return Err(VegasLatticeError::InconsistentVertices);
154 }
155 if self.size.0 < 0.0 || self.size.1 < 0.0 || self.size.2 < 0.0 {
156 return Err(VegasLatticeError::NegativeSize);
157 }
158 Ok(self)
159 }
160
161 fn drop_along(mut self, axis: Axis) -> Self {
163 self.vertices.retain(|v| {
164 let delta = v.delta();
165 match axis {
166 Axis::X => delta.0 == 0,
167 Axis::Y => delta.1 == 0,
168 Axis::Z => delta.2 == 0,
169 }
170 });
171 self
172 }
173
174 pub fn drop_x(self) -> Self {
176 self.drop_along(Axis::X)
177 }
178
179 pub fn drop_y(self) -> Self {
181 self.drop_along(Axis::Y)
182 }
183
184 pub fn drop_z(self) -> Self {
186 self.drop_along(Axis::Z)
187 }
188
189 pub fn drop_all(self) -> Self {
191 self.drop_x().drop_y().drop_z()
192 }
193
194 #[inline]
195 fn size_along(&self, axis: Axis) -> f64 {
196 match axis {
197 Axis::X => self.size.0,
198 Axis::Y => self.size.1,
199 Axis::Z => self.size.2,
200 }
201 }
202
203 fn expand_along(mut self, axis: Axis, amount: usize) -> Self {
205 let size = self.size_along(axis);
206 let n_sites = self.sites.len();
207 let n_vertices = self.vertices.len();
208
209 self.sites = (0..amount)
210 .flat_map(|i| repeat(i).take(n_sites))
211 .zip(self.sites().iter().cycle())
212 .map(|(index, site)| match axis {
213 Axis::X => site.clone().move_x((index as f64) * size),
214 Axis::Y => site.clone().move_y((index as f64) * size),
215 Axis::Z => site.clone().move_z((index as f64) * size),
216 })
217 .collect();
218
219 self.vertices = (0..amount)
220 .flat_map(|i| repeat(i).take(n_vertices))
221 .zip(self.vertices.iter().cycle())
222 .map(|(index, vertex)| match axis {
223 Axis::X => vertex.clone().move_x(index, n_sites, amount),
224 Axis::Y => vertex.clone().move_y(index, n_sites, amount),
225 Axis::Z => vertex.clone().move_z(index, n_sites, amount),
226 })
227 .collect();
228
229 match axis {
230 Axis::X => self.size.0 *= amount as f64,
231 Axis::Y => self.size.1 *= amount as f64,
232 Axis::Z => self.size.2 *= amount as f64,
233 }
234
235 self
236 }
237
238 pub fn expand_x(self, amount: usize) -> Self {
240 self.expand_along(Axis::X, amount)
241 }
242
243 pub fn expand_y(self, amount: usize) -> Self {
245 self.expand_along(Axis::Y, amount)
246 }
247
248 pub fn expand_z(self, amount: usize) -> Self {
250 self.expand_along(Axis::Z, amount)
251 }
252
253 pub fn expand_all(self, amount: usize) -> Self {
255 self.expand_x(amount).expand_y(amount).expand_z(amount)
256 }
257
258 pub fn expand(self, x: usize, y: usize, z: usize) -> Self {
260 self.expand_x(x).expand_y(y).expand_z(z)
261 }
262
263 pub fn apply_mask<R: Rng + ?Sized>(mut self, mask: Mask, rng: &mut R) -> Self {
267 let site_mask: Vec<_> = self
268 .sites
269 .iter()
270 .map(|s| {
271 let (x, y, _) = s.position();
272 mask.keep(x, y, rng)
273 })
274 .collect();
275 let mut counter = 0;
276 let new_indices: Vec<_> = (0..self.sites.len())
277 .map(|i| {
278 if site_mask[i] {
279 counter += 1;
280 counter - 1
281 } else {
282 i
283 }
284 })
285 .collect();
286 self.sites = self
287 .sites
288 .into_iter()
289 .enumerate()
290 .filter(|&(i, ref _s)| site_mask[i])
291 .map(|(_i, s)| s)
292 .collect();
293 self.vertices = self
294 .vertices
295 .into_iter()
296 .filter(|v| site_mask[v.source()] && site_mask[v.target()])
297 .map(|v| v.reindex(&new_indices))
298 .collect();
299 self
300 }
301
302 pub fn alloy_sites<R: Rng + ?Sized>(
304 mut self,
305 source: &str,
306 target: Alloy,
307 rng: &mut R,
308 ) -> Self {
309 self.sites = self
310 .sites
311 .into_iter()
312 .map(|site| {
313 if site.kind() != source {
314 site
315 } else {
316 site.with_kind(target.pick(rng))
317 }
318 })
319 .collect();
320 self
321 }
322}
323
324impl FromStr for Lattice {
325 type Err = VegasLatticeError;
326 fn from_str(source: &str) -> Result<Lattice> {
327 let lattice: Lattice = serde_json::from_str(source)?;
328 lattice.validate()
329 }
330}
331
332#[cfg(test)]
333mod test {
334 use crate::{Lattice, Site, Vertex};
335
336 #[test]
337 fn drop_example() {
338 let lattice = Lattice::sc(1.0);
339 let lattice = lattice.drop_x();
340 assert!(lattice.vertices().len() == 2);
341 }
342
343 #[test]
344 fn drop_example_actually_dropping() {
345 let lattice = Lattice::sc(1.0);
346 let lattice = lattice.drop_all();
347 assert!(lattice.vertices().is_empty());
348 }
349
350 #[test]
351 fn single_lattice_expansion_1d() {
352 let lattice = Lattice::sc(1.0);
353 let output = lattice.expand_x(2);
354 assert_eq!(output.sites.len(), 2);
355 assert!((output.sites[1].position().0 - 1.0).abs() < 1e-10);
356 }
357
358 #[test]
359 fn double_lattice_expansion_1d() {
360 let lattice = Lattice::sc(1.0);
361 let lattice = lattice.expand_x(2);
362 let output = lattice.expand_x(2);
363 assert_eq!(output.sites.len(), 4);
364 assert!((output.sites[1].position().0 - 1.0).abs() < 1e-10);
365 assert!((output.sites[2].position().0 - 2.0).abs() < 1e-10);
366 assert!((output.sites[3].position().0 - 3.0).abs() < 1e-10);
367 }
368
369 #[test]
370 fn single_lattice_expansion_1d_vertices() {
371 let lattice = Lattice::sc(1.0)
372 .try_with_vertices(vec![Vertex::new(0, 0, (1, 0, 0))])
373 .unwrap();
374 let output = lattice.expand_x(2);
375 assert_eq!(output.vertices.len(), 2);
376 assert_eq!(output.vertices[0].source(), 0);
377 assert_eq!(output.vertices[0].target(), 1);
378 assert_eq!(output.vertices[0].delta().0, 0);
379 assert_eq!(output.vertices[1].source(), 1);
380 assert_eq!(output.vertices[1].target(), 0);
381 assert_eq!(output.vertices[1].delta().0, 1);
382 }
383
384 #[test]
385 fn single_lattice_expansion_1d_negative_vertices() {
386 let lattice = Lattice::sc(1.0)
387 .try_with_vertices(vec![Vertex::new(0, 0, (-1, 0, 0))])
388 .unwrap();
389 let output = lattice.expand_x(2);
390 assert_eq!(output.vertices.len(), 2);
391 assert_eq!(output.vertices[0].source(), 0);
392 assert_eq!(output.vertices[0].target(), 1);
393 assert_eq!(output.vertices[0].delta().0, -1);
394 assert_eq!(output.vertices[1].source(), 1);
395 assert_eq!(output.vertices[1].target(), 0);
396 assert_eq!(output.vertices[1].delta().0, 0);
397 }
398
399 #[test]
400 fn test_sc_lattice() {
401 let lattice = Lattice::sc(1.0);
402 assert_eq!(lattice.size(), (1.0, 1.0, 1.0));
403 assert_eq!(lattice.sites().len(), 1);
404 assert_eq!(lattice.vertices().len(), 3)
405 }
406
407 #[test]
408 fn test_bcc_lattice() {
409 let lattice = Lattice::bcc(1.0);
410 assert_eq!(lattice.size(), (1.0, 1.0, 1.0));
411 assert_eq!(lattice.sites().len(), 2);
412 assert_eq!(lattice.vertices().len(), 8)
413 }
414
415 #[test]
416 fn test_fcc_lattice() {
417 let lattice = Lattice::fcc(1.0);
418 assert_eq!(lattice.size(), (1.0, 1.0, 1.0));
419 assert_eq!(lattice.sites().len(), 4);
420 assert_eq!(lattice.vertices().len(), 12)
421 }
422
423 #[test]
424 fn test_wit_size() {
425 let lattice = Lattice::sc(1.0).try_with_size((2.0, 2.0, 2.0)).unwrap();
426 assert_eq!(lattice.size(), (2.0, 2.0, 2.0));
427 }
428
429 #[test]
430 fn test_with_sites() {
431 let lattice = Lattice::sc(1.0)
432 .try_with_sites(vec![Site::new("Fe"), Site::new("Ni")])
433 .unwrap();
434 assert_eq!(lattice.sites().len(), 2);
435 }
436
437 #[test]
438 fn test_with_vertices() {
439 let lattice = Lattice::sc(1.0)
440 .try_with_vertices(vec![
441 Vertex::new(0, 0, (1, 0, 0)),
442 Vertex::new(0, 0, (0, 1, 0)),
443 ])
444 .unwrap();
445 assert_eq!(lattice.vertices().len(), 2);
446 }
447
448 #[test]
449 fn test_lattice_with_inconsistent_vertices() {
450 let result = Lattice::sc(1.0).try_with_vertices(vec![Vertex::new(0, 1, (1, 0, 0))]);
452 assert!(result.is_err());
453 }
454
455 #[test]
456 fn test_lattice_with_negative_size() {
457 let result = Lattice::try_new((-1.0, 1.0, 1.0));
458 assert!(result.is_err());
459 }
460
461 #[test]
462 fn test_lattice_can_be_read_from_string() {
463 let lattice = r#"{
464 "size": [1.0, 1.0, 1.0],
465 "sites": [
466 {
467 "kind": "Fe",
468 "position": [0.0, 0.0, 0.0]
469 }
470 ],
471 "vertices": [
472 {
473 "source": 0,
474 "target": 0,
475 "delta": [0, 0, 1]
476 }
477 ]
478 }"#;
479 let lattice: Lattice = lattice.parse().unwrap();
480 assert_eq!(lattice.size(), (1.0, 1.0, 1.0));
481 assert_eq!(lattice.sites().len(), 1);
482 assert_eq!(lattice.vertices().len(), 1);
483 }
484}