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#[cfg(feature = "api-v4_2")]
426fn test_smooth_realspace_cutoff() {
427 let numbers = vec![6, 6, 6, 1, 1, 1, 1];
429 #[rustfmt::skip]
430 let positions = vec![
431 0.00000000000000, 0.00000000000000, -1.79755622305860,
432 0.00000000000000, 0.00000000000000, 0.95338756106749,
433 0.00000000000000, 0.00000000000000, 3.22281255790261,
434 -0.96412815539807, -1.66991895015711, -2.53624948351102,
435 -0.96412815539807, 1.66991895015711, -2.53624948351102,
436 1.92825631079613, 0.00000000000000, -2.53624948351102,
437 0.00000000000000, 0.00000000000000, 5.23010455462158,
438 ];
439
440 let param = DFTD4Param::load_rational_damping("pbe", false);
441
442 let mut model = DFTD4Model::new(&numbers, &positions, None, None, None);
444 let ref_energy = model.get_dispersion(¶m, false).energy;
445
446 model.set_realspace_cutoff_smooth(4.0, 4.0, 30.0, 2.0, 2.0);
448 let res_energy = model.get_dispersion(¶m, false).energy;
449
450 assert_ne!(ref_energy, res_energy);
451}
452
453#[test]
454fn test() {
455 test_rational_damping_noargs();
456 test_structure();
457 test_blypd4();
458 #[cfg(feature = "api-v4_0")]
459 test_tpssd4s();
460 test_pbed4();
461 #[cfg(feature = "api-v3_1")]
462 test_r2scan3c();
463 #[cfg(feature = "api-v3_2")]
464 test_pair_resolved();
465 #[cfg(feature = "api-v3_1")]
466 test_properties();
467 test_error_model();
468 #[cfg(feature = "api-v4_2")]
469 test_smooth_realspace_cutoff();
470}
471
472fn main() {
473 test_rational_damping_noargs();
474 test_structure();
475 test_blypd4();
476 #[cfg(feature = "api-v4_0")]
477 test_tpssd4s();
478 test_pbed4();
479 #[cfg(feature = "api-v3_1")]
480 test_r2scan3c();
481 #[cfg(feature = "api-v3_2")]
482 test_pair_resolved();
483 #[cfg(feature = "api-v3_1")]
484 test_properties();
485 test_error_model();
486 #[cfg(feature = "api-v4_2")]
487 test_smooth_realspace_cutoff();
488}