1#![allow(clippy::excessive_precision)]
6
7use approx::assert_abs_diff_eq;
8use dftd4::prelude::*;
9
10fn test_rational_damping_noargs() {
11 assert!(DFTD4RationalDampingParamBuilder::default().build().is_err());
13
14 let builder = DFTD4RationalDampingParamBuilder::default().a1(0.4).a2(5.0);
15 assert!(builder.build().is_err_and(|e| match &e {
16 DFTD4Error::BuilderError(f) => f.field_name() == "s8",
17 _ => false,
18 }));
19
20 let builder = DFTD4RationalDampingParamBuilder::default().s8(1.0).a2(5.0);
21 assert!(builder.build().is_err_and(|e| match &e {
22 DFTD4Error::BuilderError(f) => f.field_name() == "a1",
23 _ => false,
24 }));
25
26 let builder = DFTD4RationalDampingParamBuilder::default().s8(1.0).a1(0.4);
27 assert!(builder.build().is_err_and(|e| match &e {
28 DFTD4Error::BuilderError(f) => f.field_name() == "a2",
29 _ => false,
30 }));
31}
32
33fn test_structure() {
34 let numbers = vec![6, 7, 6, 7, 6, 6, 6, 8, 7, 6, 8, 7, 6, 6, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1];
36 #[rustfmt::skip]
37 let positions = vec![
38 2.02799738646442, 0.09231312124713, -0.14310895950963,
39 4.75011007621000, 0.02373496014051, -0.14324124033844,
40 6.33434307654413, 2.07098865582721, -0.14235306905930,
41 8.72860718071825, 1.38002919517619, -0.14265542523943,
42 8.65318821103610, -1.19324866489847, -0.14231527453678,
43 6.23857175648671, -2.08353643730276, -0.14218299370797,
44 5.63266886875962, -4.69950321056008, -0.13940509630299,
45 3.44931709749015, -5.48092386085491, -0.14318454855466,
46 7.77508917214346, -6.24427872938674, -0.13107140408805,
47 10.30229550927022, -5.39739796609292, -0.13672168520430,
48 12.07410272485492, -6.91573621641911, -0.13666499342053,
49 10.70038521493902, -2.79078533715849, -0.14148379504141,
50 13.24597858727017, -1.76969072232377, -0.14218299370797,
51 7.40891694074004, -8.95905928176407, -0.11636933482904,
52 1.38702118184179, 2.05575746325296, -0.14178615122154,
53 1.34622199478497, -0.86356704498496, 1.55590600570783,
54 1.34624089204623, -0.86133716815647, -1.84340893849267,
55 5.65596919189118, 4.00172183859480, -0.14131371969009,
56 14.67430918222276, -3.26230980007732, -0.14344911021228,
57 13.50897177220290, -0.60815166181684, 1.54898960808727,
58 13.50780014200488, -0.60614855212345, -1.83214617078268,
59 5.41408424778406, -9.49239668625902, -0.11022772492007,
60 8.31919801555568, -9.74947502841788, 1.56539243085954,
61 8.31511620712388, -9.76854236502758, -1.79108242206824,
62 ];
63
64 assert!(DFTD4Structure::new_f(&numbers, &vec![0.0; 72], None, None, None)
66 .is_err_and(|e| e.to_string().contains("Too close interatomic distances found")));
67
68 assert!(DFTD4Structure::new_f(&[1, 1, 1], &positions, None, None, None)
70 .is_err_and(|e| e.to_string().contains("Invalid dimension for positions")));
71
72 assert!(DFTD4Structure::new_f(&numbers, &[0.0; 7], None, None, None)
74 .is_err_and(|e| e.to_string().contains("Invalid dimension for positions")));
75
76 let mut mol = DFTD4Structure::new(&numbers, &positions, None, None, None);
78
79 assert!(mol
81 .update_f(&[0.0; 7], None)
82 .is_err_and(|e| e.to_string().contains("Invalid dimension for positions")));
83
84 assert!(mol
86 .update_f(&positions, Some(&[0.0; 7]))
87 .is_err_and(|e| e.to_string().contains("Invalid dimension for lattice")));
88
89 assert!(mol
91 .update_f(&[0.0; 72], None)
92 .is_err_and(|e| e.to_string().contains("Too close interatomic distances found"),));
93}
94
95fn test_blypd4() {
96 let thr = 1.0e-7;
98
99 let numbers = vec![1, 1, 6, 5, 1, 15, 8, 17, 13, 15, 5, 1, 9, 15, 1, 15];
100 #[rustfmt::skip]
101 let positions = vec![
102 2.79274810283778, 3.82998228828316, -2.79287054959216,
103 -1.43447454186833, 0.43418729987882, 5.53854345129809,
104 -3.26268343665218, -2.50644032426151, -1.56631149351046,
105 2.14548759959147, -0.88798018953965, -2.24592534506187,
106 -4.30233097423181, -3.93631518670031, -0.48930754109119,
107 0.06107643564880, -3.82467931731366, -2.22333344469482,
108 0.41168550401858, 0.58105573172764, 5.56854609916143,
109 4.41363836635653, 3.92515871809283, 2.57961724984000,
110 1.33707758998700, 1.40194471661647, 1.97530004949523,
111 3.08342709834868, 1.72520024666801, -4.42666116106828,
112 -3.02346932078505, 0.04438199934191, -0.27636197425010,
113 1.11508390868455, -0.97617412809198, 6.25462847718180,
114 0.61938955433011, 2.17903547389232, -6.21279842416963,
115 -2.67491681346835, 3.00175899761859, 1.05038813614845,
116 -4.13181080289514, -2.34226739863660, -3.44356159392859,
117 2.85007173009739, -2.64884892757600, 0.71010806424206,
118 ];
119
120 let model = DFTD4Model::new(&numbers, &positions, None, None, None);
121
122 let param = DFTD4Param::load_rational_damping("blyp", true);
123 let res = model.get_dispersion(¶m, false);
124
125 assert_abs_diff_eq!(res.energy, -0.06991716314879085, epsilon = thr);
126
127 let res = model.get_dispersion(¶m, true);
128
129 assert_abs_diff_eq!(res.energy, -0.06991716314879085, epsilon = thr);
130}
131
132#[cfg(feature = "api-v4_0")]
133fn test_tpssd4s() {
134 let thr = 1.0e-7;
136
137 let numbers = vec![1, 1, 6, 5, 1, 15, 8, 17, 13, 15, 5, 1, 9, 15, 1, 15];
138 #[rustfmt::skip]
139 let positions = vec![
140 2.79274810283778, 3.82998228828316, -2.79287054959216,
141 -1.43447454186833, 0.43418729987882, 5.53854345129809,
142 -3.26268343665218, -2.50644032426151, -1.56631149351046,
143 2.14548759959147, -0.88798018953965, -2.24592534506187,
144 -4.30233097423181, -3.93631518670031, -0.48930754109119,
145 0.06107643564880, -3.82467931731366, -2.22333344469482,
146 0.41168550401858, 0.58105573172764, 5.56854609916143,
147 4.41363836635653, 3.92515871809283, 2.57961724984000,
148 1.33707758998700, 1.40194471661647, 1.97530004949523,
149 3.08342709834868, 1.72520024666801, -4.42666116106828,
150 -3.02346932078505, 0.04438199934191, -0.27636197425010,
151 1.11508390868455, -0.97617412809198, 6.25462847718180,
152 0.61938955433011, 2.17903547389232, -6.21279842416963,
153 -2.67491681346835, 3.00175899761859, 1.05038813614845,
154 -4.13181080289514, -2.34226739863660, -3.44356159392859,
155 2.85007173009739, -2.64884892757600, 0.71010806424206,
156 ];
157
158 let model = DFTD4Model::new_d4s(&numbers, &positions, None, None, None);
159
160 let param = DFTD4Param::load_rational_damping("tpss", true);
161 let res = model.get_dispersion(¶m, false);
162
163 assert_abs_diff_eq!(res.energy, -0.046233140236052253, epsilon = thr);
164
165 let res = model.get_dispersion(¶m, true);
166
167 assert_abs_diff_eq!(res.energy, -0.046233140236052253, epsilon = thr);
168}
169
170fn test_pbed4() {
171 let thr = 1.0e-7;
173
174 let numbers = vec![1, 9, 15, 13, 1, 1, 13, 5, 3, 15, 8, 1, 1, 5, 16, 1];
175 #[rustfmt::skip]
176 let positions = vec![
177 -2.14132037405479, -1.34402701877044, -2.32492500904728,
178 4.46671289205392, -2.04800110524830, 0.44422406067087,
179 -4.92212517643478, -1.73734240529793, 0.96890323821450,
180 -1.30966093045696, -0.52977363497805, 3.44453452239668,
181 -4.34208759006189, -4.30470270977329, 0.39887431726215,
182 0.61788392767516, 2.62484136683297, -3.28228926932647,
183 4.23562873444840, -1.68839322682951, -3.53824299552792,
184 2.23130060612446, 1.93579813100155, -1.80384647554323,
185 -2.32285463652832, 2.90603947535842, -1.39684847191937,
186 2.34557941578250, 2.86074312333371, 1.82827238641666,
187 -3.66431367659153, -0.42910188232667, -1.81957402856634,
188 -0.34927881505446, -1.75988134003940, 5.98017466326572,
189 0.29500802281217, -2.00226104143537, 0.53023447931897,
190 2.10449364205058, -0.56741404446633, 0.30975625014335,
191 -1.59355304432499, 3.69176153150419, 2.87878226787916,
192 4.34858700256050, 2.39171478113440, -2.61802993563738,
193 ];
194
195 let model = DFTD4Model::new(&numbers, &positions, None, None, None);
196
197 let param = DFTD4Param::load_rational_damping("pbe", true);
198 let res = model.get_dispersion(¶m, false);
199
200 assert_abs_diff_eq!(res.energy, -0.028415184156428127, epsilon = thr);
201
202 let res = model.get_dispersion(¶m, true);
203
204 assert_abs_diff_eq!(res.energy, -0.028415184156428127, epsilon = thr);
205}
206
207#[cfg(feature = "api-v3_1")]
208fn test_r2scan3c() {
209 let thr = 1.0e-8;
211
212 let numbers = vec![1, 9, 15, 13, 1, 1, 13, 5, 3, 15, 8, 1, 1, 5, 16, 1];
213 #[rustfmt::skip]
214 let positions = vec![
215 -2.14132037405479, -1.34402701877044, -2.32492500904728,
216 4.46671289205392, -2.04800110524830, 0.44422406067087,
217 -4.92212517643478, -1.73734240529793, 0.96890323821450,
218 -1.30966093045696, -0.52977363497805, 3.44453452239668,
219 -4.34208759006189, -4.30470270977329, 0.39887431726215,
220 0.61788392767516, 2.62484136683297, -3.28228926932647,
221 4.23562873444840, -1.68839322682951, -3.53824299552792,
222 2.23130060612446, 1.93579813100155, -1.80384647554323,
223 -2.32285463652832, 2.90603947535842, -1.39684847191937,
224 2.34557941578250, 2.86074312333371, 1.82827238641666,
225 -3.66431367659153, -0.42910188232667, -1.81957402856634,
226 -0.34927881505446, -1.75988134003940, 5.98017466326572,
227 0.29500802281217, -2.00226104143537, 0.53023447931897,
228 2.10449364205058, -0.56741404446633, 0.30975625014335,
229 -1.59355304432499, 3.69176153150419, 2.87878226787916,
230 4.34858700256050, 2.39171478113440, -2.61802993563738,
231 ];
232
233 let model = DFTD4Model::custom_d4(&numbers, &positions, 2.0, 1.0, 6.0, None, None, None);
235
236 let param = DFTD4RationalDampingParamBuilder::default()
237 .s8(0.0)
238 .a1(0.42)
239 .a2(5.65)
240 .s9(2.0)
241 .build()
242 .unwrap()
243 .new_param();
244 let res = model.get_dispersion(¶m, false);
245
246 assert_abs_diff_eq!(res.energy, -0.008016697276824889, epsilon = thr);
247
248 let res = model.get_dispersion(¶m, true);
249
250 assert_abs_diff_eq!(res.energy, -0.008016697276824889, epsilon = thr);
251}
252
253#[cfg(feature = "api-v3_2")]
254fn test_pair_resolved() {
255 let thr = 1.0e-8;
257
258 let numbers = vec![16, 16, 16, 16, 16, 16, 16, 16];
259 #[rustfmt::skip]
260 let positions = vec![
261 -4.15128787379191, 1.71951973863958, -0.93066267097296,
262 -4.15128787379191, -1.71951973863958, 0.93066267097296,
263 -1.71951973863958, -4.15128787379191, -0.93066267097296,
264 1.71951973863958, -4.15128787379191, 0.93066267097296,
265 4.15128787379191, -1.71951973863958, -0.93066267097296,
266 4.15128787379191, 1.71951973863958, 0.93066267097296,
267 1.71951973863958, 4.15128787379191, -0.93066267097296,
268 -1.71951973863958, 4.15128787379191, 0.93066267097296,
269 ];
270 #[rustfmt::skip]
271 let pair_disp2 = vec![
272 -0.00000000e-0, -5.80599854e-4, -4.74689854e-4, -2.11149449e-4, -1.63163128e-4, -2.11149449e-4, -4.74689854e-4, -5.80599854e-4,
273 -5.80599854e-4, -0.00000000e-0, -5.80599854e-4, -4.74689854e-4, -2.11149449e-4, -1.63163128e-4, -2.11149449e-4, -4.74689854e-4,
274 -4.74689854e-4, -5.80599854e-4, -0.00000000e-0, -5.80599854e-4, -4.74689854e-4, -2.11149449e-4, -1.63163128e-4, -2.11149449e-4,
275 -2.11149449e-4, -4.74689854e-4, -5.80599854e-4, -0.00000000e-0, -5.80599854e-4, -4.74689854e-4, -2.11149449e-4, -1.63163128e-4,
276 -1.63163128e-4, -2.11149449e-4, -4.74689854e-4, -5.80599854e-4, -0.00000000e-0, -5.80599854e-4, -4.74689854e-4, -2.11149449e-4,
277 -2.11149449e-4, -1.63163128e-4, -2.11149449e-4, -4.74689854e-4, -5.80599854e-4, -0.00000000e-0, -5.80599854e-4, -4.74689854e-4,
278 -4.74689854e-4, -2.11149449e-4, -1.63163128e-4, -2.11149449e-4, -4.74689854e-4, -5.80599854e-4, -0.00000000e-0, -5.80599854e-4,
279 -5.80599854e-4, -4.74689854e-4, -2.11149449e-4, -1.63163128e-4, -2.11149449e-4, -4.74689854e-4, -5.80599854e-4, -0.00000000e-0,
280 ];
281 #[rustfmt::skip]
282 let pair_disp3 = vec![
283 0.00000000e-0, 3.39353850e-7, 8.74462839e-7, 1.17634100e-6, 9.86937310e-7, 1.17634100e-6, 8.74462839e-7, 3.39353850e-7,
284 3.39353850e-7, 0.00000000e-0, 3.39353850e-7, 8.74462839e-7, 1.17634100e-6, 9.86937310e-7, 1.17634100e-6, 8.74462839e-7,
285 8.74462839e-7, 3.39353850e-7, 0.00000000e-0, 3.39353850e-7, 8.74462839e-7, 1.17634100e-6, 9.86937310e-7, 1.17634100e-6,
286 1.17634100e-6, 8.74462839e-7, 3.39353850e-7, 0.00000000e-0, 3.39353850e-7, 8.74462839e-7, 1.17634100e-6, 9.86937310e-7,
287 9.86937310e-7, 1.17634100e-6, 8.74462839e-7, 3.39353850e-7, 0.00000000e-0, 3.39353850e-7, 8.74462839e-7, 1.17634100e-6,
288 1.17634100e-6, 9.86937310e-7, 1.17634100e-6, 8.74462839e-7, 3.39353850e-7, 0.00000000e-0, 3.39353850e-7, 8.74462839e-7,
289 8.74462839e-7, 1.17634100e-6, 9.86937310e-7, 1.17634100e-6, 8.74462839e-7, 3.39353850e-7, 0.00000000e-0, 3.39353850e-7,
290 3.39353850e-7, 8.74462839e-7, 1.17634100e-6, 9.86937310e-7, 1.17634100e-6, 8.74462839e-7, 3.39353850e-7, 0.00000000e-0,
291 ];
292
293 let model = DFTD4Model::new(&numbers, &positions, None, None, None);
294
295 let param = DFTD4RationalDampingParamBuilder::default()
296 .s8(1.20065498)
297 .a1(0.40085597)
298 .a2(5.02928789)
299 .build()
300 .unwrap()
301 .new_param();
302 let res = model.get_pairwise_dispersion(¶m);
303
304 for (a, b) in res.pair_energy2.iter().zip(pair_disp2.iter()) {
305 assert_abs_diff_eq!(a, b, epsilon = thr);
306 }
307
308 for (a, b) in res.pair_energy3.iter().zip(pair_disp3.iter()) {
309 assert_abs_diff_eq!(a, b, epsilon = thr);
310 }
311}
312
313#[cfg(feature = "api-v3_1")]
314fn test_properties() {
315 let thr = 1.0e-7;
317
318 let numbers = vec![7, 7, 7, 7, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1];
319 #[rustfmt::skip]
320 let positions = vec![
321 1.97420621099560, 1.97415497783241, 1.97424596974304,
322 6.82182427659395, 2.87346383480995, 7.72099517560089,
323 7.72104957181201, 6.82177051521773, 2.87336561318016,
324 2.87343220660781, 7.72108897828386, 6.82187093171878,
325 3.51863272100286, 2.63865333484548, 1.00652979981286,
326 2.63877594964754, 1.00647313885594, 3.51882748086447,
327 1.00639728563189, 3.51850454450845, 2.63869202592387,
328 8.36624975982697, 2.20896711017229, 8.68870955681018,
329 7.48639684558259, 3.84114715917956, 6.17640982573725,
330 5.85401675167715, 1.32911569888797, 7.05654606696031,
331 7.05646299938990, 5.85409590282274, 1.32879923864813,
332 8.68882633853582, 8.36611541129785, 2.20894120662207,
333 3.84121223226912, 6.17673669892998, 7.48629723649480,
334 1.32897854262127, 7.05658604099926, 5.85414031368096,
335 2.20884896069885, 8.68875820985799, 8.36643568423387,
336 6.17659142004652, 7.48627051643848, 3.84109594690835,
337 ];
338 #[rustfmt::skip]
339 let lattice = vec![
340 9.69523775911749, 0.0, 0.0,
341 0.0, 9.69523775911749, 0.0,
342 0.0, 0.0, 9.69523775911749,
343 ];
344 #[rustfmt::skip]
345 let cn = [
346 2.5775156287150218,
347 2.5775155620078536,
348 2.5775157938667150,
349 2.5775157704485387,
350 0.8591731475439074,
351 0.8591680526841657,
352 0.8591744284869478,
353 0.8591732359038715,
354 0.8591678667769283,
355 0.8591744593270527,
356 0.8591684383407867,
357 0.8591754863625011,
358 0.8591751690053771,
359 0.8591719636497469,
360 0.8591686377934131,
361 0.8591718691634261,
362 ];
363 #[rustfmt::skip]
364 let charges = [
365 -0.86974543285813199,
366 -0.86974501326130316,
367 -0.86974658178316333,
368 -0.86974643466661739,
369 0.28992010784471012,
370 0.28989847135893759,
371 0.28992738637932841,
372 0.28992042841052362,
373 0.28989746859382759,
374 0.28992753735979965,
375 0.28990032127437559,
376 0.28993196014690176,
377 0.28993044356403513,
378 0.28991421277784613,
379 0.28990119160907674,
380 0.28991393324985348,
381 ];
382 #[rustfmt::skip]
383 let alpha = [
384 9.9853045768095097,
385 9.9853025181287016,
386 9.9853102162086920,
387 9.9853094944097140,
388 1.3513315505023076,
389 1.3513939255952379,
390 1.3513106323935196,
391 1.3513306244831862,
392 1.3513968091133417,
393 1.3513101978580895,
394 1.3513885997538553,
395 1.3512974505020905,
396 1.3513018164322617,
397 1.3513485145877058,
398 1.3513860914395939,
399 1.3513493246534483,
400 ];
401
402 let model = DFTD4Model::new(&numbers, &positions, None, Some(&lattice), None);
403
404 let res = model.get_properties();
405
406 for (a, b) in res.cn.iter().zip(cn.iter()) {
407 assert_abs_diff_eq!(a, b, epsilon = thr);
408 }
409
410 for (a, b) in res.charges.iter().zip(charges.iter()) {
411 assert_abs_diff_eq!(a, b, epsilon = thr);
412 }
413
414 for (a, b) in res.alpha.iter().zip(alpha.iter()) {
415 assert_abs_diff_eq!(a, b, epsilon = thr);
416 }
417}
418
419fn test_error_model() {
420 let param = DFTD4Param::load_rational_damping_f("UNKNOWN_METHOD", false);
422 assert!(param.is_err_and(|e| e.to_string().contains("not known")));
423}
424
425#[test]
426fn test() {
427 test_rational_damping_noargs();
428 test_structure();
429 test_blypd4();
430 #[cfg(feature = "api-v4_0")]
431 test_tpssd4s();
432 test_pbed4();
433 #[cfg(feature = "api-v3_1")]
434 test_r2scan3c();
435 #[cfg(feature = "api-v3_2")]
436 test_pair_resolved();
437 #[cfg(feature = "api-v3_1")]
438 test_properties();
439 test_error_model();
440}
441
442fn main() {
443 test_rational_damping_noargs();
444 test_structure();
445 test_blypd4();
446 #[cfg(feature = "api-v4_0")]
447 test_tpssd4s();
448 test_pbed4();
449 #[cfg(feature = "api-v3_1")]
450 test_r2scan3c();
451 #[cfg(feature = "api-v3_2")]
452 test_pair_resolved();
453 #[cfg(feature = "api-v3_1")]
454 test_properties();
455 test_error_model();
456}