1use super::*;
3
4use std::collections::{HashMap, HashSet};
5
6use ::edip::EdipParameters;
7use gchemol::neighbors::Neighborhood;
8use vecfx::*;
9#[derive(Clone, Debug, Default)]
13pub struct Edip {
14 virial: f64,
15 nh: Neighborhood,
17}
18
19impl Edip {
20 fn update_nh(&mut self, mol: &Molecule) {
21 self.nh = Neighborhood::new();
22 self.nh.update(mol.positions().enumerate());
24 if let Some(lat) = mol.get_lattice() {
25 self.nh.set_lattice(lat.matrix().into());
26 }
27 }
28}
29
30impl ChemicalModel for Edip {
31 fn compute(&mut self, mol: &Molecule) -> Result<Computed> {
32 const search_radius: f64 = 4.0;
33
34 let not_silicon = mol.symbols().any(|x| x != "Si");
36 if not_silicon {
37 bail!("EDIP potential model only works for Silicon");
38 }
39
40 self.update_nh(mol);
41 let n = mol.natoms();
42 let positions = mol.positions().collect_vec();
43 let mut neighbors = vec![];
44 let mut distances = HashMap::new();
45 let lat = mol.get_lattice();
47 for i in 0..n {
48 let mut connected = HashSet::new();
49 for x in self.nh.neighbors(i, search_radius) {
50 let j = x.node;
52 let pi: Vector3f = positions[i].into();
53 let pj: Vector3f = positions[j].into();
54 let d = if let Some(image) = x.image {
55 let t = lat.unwrap().to_cart(image);
57 pj + t - pi
58 } else {
59 pj - pi
60 };
61 distances.insert((i, j), d.into());
62 connected.insert(j);
63 }
64
65 neighbors.push(connected);
66 }
67
68 let params = EdipParameters::silicon();
69 let mut forces = vec![[0.0; 3]; n];
70 let (energy, virial) = ::edip::compute_forces(&mut forces, &neighbors, &distances, ¶ms);
71
72 let mut computed = Computed::default();
73 computed.set_energy(energy);
74 computed.set_forces(forces);
75
76 self.virial = virial;
78
79 Ok(computed)
80 }
81}
82#[test]
86fn test_edip() -> Result<()> {
87 use gchemol::prelude::*;
88 use gchemol::Molecule;
89 use vecfx::*;
90
91 let f = "./tests/files/si10.xyz";
92 let mol = Molecule::from_file(f)?;
93
94 let mut model = Edip::default();
95 let computed = model.compute(&mol)?;
96 let f = computed.get_forces().unwrap();
97 let f_norm = f.as_flat().as_vector_slice().norm();
98 assert!(dbg!(f_norm) <= 0.005);
99 let energy = computed.get_energy().unwrap();
100 approx::assert_relative_eq!(energy, -39.57630354331939, epsilon = 1e-5);
101 approx::assert_relative_eq!(model.virial, 0.00224989660978845, epsilon = 1e-5);
102
103 let f = "./tests/files/si5.xyz";
104 let mol = Molecule::from_file(f)?;
105 let computed = model.compute(&mol)?;
106 let energy = computed.get_energy().unwrap();
107 approx::assert_relative_eq!(energy, -14.566606, epsilon = 1e-5);
108 let virial = model.virial;
110 approx::assert_relative_eq!(virial, -3.643552, epsilon = 1e-5);
111
112 let f = computed.get_forces().unwrap();
113 #[rustfmt::skip]
114 let f_expected = [ -0.19701000, -0.62522600, 0.02948000,
115 -0.23330600, 0.43698600, 0.42511400,
116 0.43297600, 0.18333500, -0.44864300,
117 -1.75669300, 0.50149400, -1.48879300,
118 1.75403300, -0.49658900, 1.48284200];
119 approx::assert_relative_eq!(
120 f.as_flat().as_vector_slice(),
121 f_expected.as_vector_slice(),
122 epsilon = 1E-5,
123 );
124
125 let mol = Molecule::from_file("./tests/files/si-3x3x3.cif")?;
127 let computed = model.compute(&mol)?;
128 let energy = computed.get_energy().unwrap();
129 let f = computed.get_forces().unwrap();
130 dbg!(f.as_flat().as_vector_slice().norm());
131
132 Ok(())
133}
134