1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38 package ffx.algorithms.groovy;
39
40 import static java.lang.String.format;
41 import static java.lang.System.arraycopy;
42 import static java.util.Arrays.copyOf;
43 import static org.apache.commons.io.FileUtils.copyFile;
44 import static org.junit.Assert.assertArrayEquals;
45 import static org.junit.Assert.assertEquals;
46 import static org.junit.Assert.assertNotEquals;
47 import static org.junit.Assert.assertNotNull;
48 import static org.junit.Assert.assertTrue;
49
50 import ffx.algorithms.misc.AlgorithmsTest;
51 import ffx.algorithms.thermodynamics.OrthogonalSpaceTempering;
52 import ffx.algorithms.thermodynamics.OrthogonalSpaceTempering.Histogram;
53 import ffx.crystal.CrystalPotential;
54 import ffx.potential.bonded.LambdaInterface;
55 import java.io.File;
56 import java.io.IOException;
57 import java.util.ArrayList;
58 import java.util.Arrays;
59 import java.util.Collection;
60 import java.util.Collections;
61 import java.util.HashMap;
62 import java.util.List;
63 import java.util.Map;
64 import java.util.regex.Pattern;
65 import java.util.stream.Collectors;
66 import java.util.stream.IntStream;
67 import org.apache.commons.configuration2.Configuration;
68 import org.apache.commons.configuration2.MapConfiguration;
69 import org.apache.commons.io.FilenameUtils;
70 import org.junit.Test;
71 import org.junit.runner.RunWith;
72 import org.junit.runners.Parameterized;
73
74
75
76
77
78
79
80
81
82
83 @RunWith(Parameterized.class)
84 public class ThermodynamicsTest extends AlgorithmsTest {
85
86
87 private static final boolean debugMode = false;
88
89
90
91
92 private static final Map<String, String> DEFAULT_OPTIONS;
93
94
95
96
97 private static final Map<String, String> DEFAULT_PROPERTIES;
98
99
100
101
102
103 private static final Map<String, Boolean> DEFAULT_FLAGS;
104
105
106
107
108 private static final int DEFAULT_GRADIENT_EVALS = 3;
109
110 private static final double DEFAULT_PE_TOL = 1.0E-2;
111 private static final double DEFAULT_DUDL_TOL = 1.0E-2;
112 private static final double DEFAULT_D2UDL2_TOL = 1.0E-2;
113 private static final double DEFAULT_DUDX_TOL = 1.0E-2;
114 private static final double DEFAULT_D2UDXDL_TOL = 1.0E-2;
115
116 static {
117 String[] opts = {"--bM", "0.05", "--dw", "OFF", "--lf", "1.0E-18", "--lm", "1.0E-18", "--np",
118 "1", "--sf", "1.0", "--tp", "2.0",
119 "-b", "ADIABATIC",
120 "-C", "5000",
121 "-d", "0.5",
122 "-F", "XYZ", "-i", "STOCHASTIC",
123
124 "-k", "5.0",
125 "-l", "1.0",
126
127 "-n", "0",
128 "-p", "0",
129 "-Q", "0",
130 "-r", "2.0",
131 "-t", "298.15", "-w", "10.0"};
132
133 int nOpts = opts.length;
134 Map<String, String> optMap = new HashMap<>(nOpts);
135 for (int i = 0; i < nOpts; i += 2) {
136 optMap.put(opts[i], opts[i + 1]);
137 }
138 DEFAULT_OPTIONS = Collections.unmodifiableMap(optMap);
139
140 String[] props = {"ost-alwaystemper", "true", "ost-temperOffset", "0.5",
141 "print-on-failure", "false", "disable-neighbor-updates", "false", "vdw-lambda-alpha", "0.25",
142 "vdw-lambda-exponent", "3", "permanent-lambda-alpha", "1.0", "permanent-lambda-exponent",
143 "3"};
144
145 int nProps = props.length;
146 Map<String, String> propMap = new HashMap<>(nProps);
147 for (int i = 0; i < nProps; i += 2) {
148 propMap.put(props[i], props[i + 1]);
149 }
150 DEFAULT_PROPERTIES = Collections.unmodifiableMap(propMap);
151
152 DEFAULT_FLAGS = Map.of("--rn", false, "-y", false, "-h", false);
153 }
154
155 private final String info;
156 private final ThermoTestMode mode;
157 private final boolean doTest;
158 private final File[] copiedFiles;
159 private final Map<String, String> opts;
160 private final List<String> flags;
161
162
163
164
165 private final double freeEnergy;
166 private final double feTol;
167 private final int numGradAtoms;
168
169 private final double[] pe;
170 private final double[] dudl;
171 private final double[] d2udl2;
172
173
174
175
176 private final double[][][] dudx;
177 private final double[][][] d2udxdl;
178
179 private final double peTol = DEFAULT_PE_TOL;
180 private final double dudlTol = DEFAULT_DUDL_TOL;
181 private final double d2udl2Tol = DEFAULT_D2UDL2_TOL;
182 private final double dudxTol = DEFAULT_DUDX_TOL;
183 private final double d2udxdlTol = DEFAULT_D2UDXDL_TOL;
184
185 Configuration algorithmConfig;
186
187 public ThermodynamicsTest(String info, String[] filenames, ThermoTestMode mode, boolean ciOnly,
188 double freeEnergy, double feTol, int[] gradAtomIndices, double[] pe, double[] dudl,
189 double[] d2udl2, double[][][] dudx, double[][][] d2udxdl, String[] options,
190 String[] properties, String[] flags) throws IOException {
191 this.info = info;
192 this.mode = mode;
193 doTest = (ffxCI || !ciOnly);
194
195 File tempDir;
196 if (doTest) {
197 int nFiles = filenames.length;
198 tempDir = registerTemporaryDirectory().toFile();
199 String tempDirName = tempDir.getAbsolutePath() + File.separator;
200 logger.fine(format(" Running test %s in directory %s", info, tempDirName));
201 copiedFiles = new File[nFiles];
202
203 String[] copiedExtensions = new String[] {"dyn", "key", "properties", "his", "lam", "prm"};
204 for (int i = 0; i < nFiles; i++) {
205 File srcFile = getResourceFile(filenames[i]);
206 File tempFile = new File(tempDirName + FilenameUtils.getName(filenames[i]));
207 copyFile(srcFile, tempFile);
208 logger.fine(format(" Copied file %s to %s", srcFile, tempFile));
209 copiedFiles[i] = tempFile;
210
211 for (String ext : copiedExtensions) {
212 srcFile = new File(format("%s.%s", FilenameUtils.removeExtension(srcFile.getPath()), ext));
213 if (srcFile.exists()) {
214 logger.fine(" Copying extension " + ext);
215 tempFile = new File(
216 format("%s.%s", FilenameUtils.removeExtension(tempFile.getPath()), ext));
217 logger.fine(format(" Copied file %s to %s", srcFile, tempFile));
218 copyFile(srcFile, tempFile);
219 }
220 }
221 }
222
223 switch (mode) {
224 case HELP:
225 assertTrue(format("Help tests must have no file arguments, found %d for %s", nFiles, info),
226 nFiles == 0);
227 break;
228 default:
229 assertTrue(
230 format("Must have 1, 2, or 4 distinct filenames, found %d for test %s", nFiles, info),
231 nFiles == 1 || nFiles == 2 || nFiles == 4);
232 for (int i = 0; i < nFiles; i++) {
233 for (int j = i + 1; j < nFiles; j++) {
234 assertNotEquals(
235 format(" Filenames %d and %d matched in test %s: files %s and %s", i, j, info,
236 filenames[i], filenames[j]), filenames[i], filenames[j]);
237 }
238 }
239 break;
240 }
241 int nOpts = options.length;
242 int nProps = properties.length;
243 int nFlags = flags.length;
244
245 if (nOpts > 0) {
246 assertTrue(format("Unmatched option key %s for test %s", options[nOpts - 1], info),
247 options.length % 2 == 0);
248 }
249 if (nProps > 0) {
250 assertTrue(format("Unmatched property key %s for test %s", properties[nProps - 1], info),
251 properties.length % 2 == 0);
252 }
253 if (nFlags > 0) {
254 assertTrue(format("Unmatched flag key %s for test %s", flags[nFlags - 1], info),
255 flags.length % 2 == 0);
256 }
257
258 Pattern validOption = Pattern.compile("^--?[^D]");
259 Pattern validProperty = Pattern.compile("^[^-]");
260
261 Map<String, String> groovyOpts = new HashMap<>(DEFAULT_OPTIONS);
262 for (int i = 0; i < nOpts; i += 2) {
263 String opti = options[i];
264 assertTrue(format(" Option %s for test %s does not look like a Groovy option!", opti, info),
265 validOption.matcher(opti).find());
266 groovyOpts.put(opti, options[i + 1]);
267 }
268 this.opts = Collections.unmodifiableMap(groovyOpts);
269
270 Map<String, String> addedProps = new HashMap<>(DEFAULT_PROPERTIES);
271 for (int i = 0; i < nProps; i += 2) {
272 String propi = properties[i];
273 assertTrue(format(" Property %s for test %s does not look like a property!", propi, info),
274 validProperty.matcher(propi).find());
275 addedProps.put(propi, properties[i + 1]);
276 }
277 algorithmConfig = new MapConfiguration(addedProps);
278
279 Map<String, Boolean> addedFlags = new HashMap<>(DEFAULT_FLAGS);
280 for (int i = 0; i < nFlags; i += 2) {
281 String flagi = flags[i];
282 assertTrue(format(" Flag %s for test %s does not look like a flag!", flagi, info),
283 validOption.matcher(flagi).find());
284 String vali = flags[i + 1];
285 assertTrue(
286 format(" Value %s for flag %s in test %s is not a true/false value!", vali, flagi, info),
287 vali.equalsIgnoreCase("TRUE") || vali.equalsIgnoreCase("FALSE"));
288 addedFlags.put(flagi, Boolean.parseBoolean(vali));
289 }
290 this.flags = Collections.unmodifiableList(
291 addedFlags.entrySet().stream().filter(Map.Entry::getValue).map(Map.Entry::getKey)
292 .collect(Collectors.toList()));
293
294
295 this.freeEnergy = freeEnergy;
296 this.feTol = feTol;
297 if (mode == ThermoTestMode.FREE) {
298 assertTrue(format(" Free energy tolerance for test %s was %10.4g <= 0!", info, feTol),
299 feTol > 0);
300 }
301
302
303 this.dudx = new double[2][][];
304 this.d2udxdl = new double[2][][];
305 boolean isGradient = (mode == ThermoTestMode.GRAD);
306 if (isGradient) {
307 numGradAtoms = ffxCI ? gradAtomIndices.length
308 : Math.min(DEFAULT_GRADIENT_EVALS, gradAtomIndices.length);
309
310 if (debugMode) {
311 this.pe = this.dudl = this.d2udl2 = null;
312 } else {
313 assertNotNull(gradAtomIndices);
314 assertNotNull(pe);
315 assertNotNull(dudl);
316 assertNotNull(d2udl2);
317
318 this.pe = copyOf(pe, 2);
319 this.dudl = copyOf(dudl, 2);
320 this.d2udl2 = copyOf(d2udl2, 2);
321 for (int i = 0; i < 2; i++) {
322 assertNotNull(dudx[i]);
323 assertNotNull(d2udxdl[i]);
324 this.dudx[i] = new double[numGradAtoms][3];
325 this.d2udxdl[i] = new double[numGradAtoms][3];
326
327 for (int j = 0; j < numGradAtoms; j++) {
328 assertNotNull(dudx[i][j]);
329 assertNotNull(d2udxdl[i][j]);
330 arraycopy(dudx[i][j], 0, this.dudx[i][j], 0, 3);
331 arraycopy(d2udxdl[i][j], 0, this.d2udxdl[i][j], 0, 3);
332 }
333 }
334 }
335 } else {
336
337 numGradAtoms = 0;
338 this.pe = null;
339 this.dudl = null;
340 this.d2udl2 = null;
341 for (int i = 0; i < 2; i++) {
342 this.dudx[i] = null;
343 this.d2udxdl[i] = null;
344 }
345 }
346 } else {
347 logger.fine(" Skipping test " + info);
348 tempDir = null;
349 copiedFiles = new File[0];
350 opts = Collections.emptyMap();
351 this.flags = Collections.emptyList();
352 this.freeEnergy = 0;
353 this.feTol = 0;
354 numGradAtoms = 0;
355 this.pe = new double[0];
356 this.dudl = new double[0];
357 this.d2udl2 = new double[0];
358 this.dudx = new double[0][0][0];
359 this.d2udxdl = new double[0][0][0];
360 }
361 }
362
363 @Parameterized.Parameters
364 public static Collection<Object[]> data() {
365
366
367
368
369 return Arrays.asList(new Object[][] {
370 {"Thermodynamics Help Message Test", new String[] {}, ThermoTestMode.HELP, false, 0, 0, null,
371 null, null, null, null, null, new String[] {}, new String[] {},
372 new String[] {"-h", "true"}},
373 {"Acetamide Implicit Solvation Free Energy: -10.5 kcal/mol",
374 new String[] {"acetamide.gk.xyz"}, ThermoTestMode.FREE, true,
375 -9.2, 1.0, null, null, null, null, null, null,
376 new String[] {"-C", "10", "--ac", "1-9", "-d", "1.0", "-n", "20000", "-w", "5", "--bM",
377 "0.25", "--tp", "2.0"},
378 new String[] {"randomseed", "42", "lambda-bin-width", "0.02"}, new String[] {}},
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515 {
516
517
518 "Calbindin D9k Ca/Mg Simultaneous Bookending: L = 1.0",
519 new String[] {"4icb_ca_a.xyz", "4icb_ca_b.xyz", "4icb_mg_a.xyz", "4icb_mg_b.xyz"},
520 ThermoTestMode.GRAD, true, 0, 0,
521 new int[] {1, 100, 421, 426, 919, 921, 1203, 1204, 1205, 1206, 1207, 1208},
522
523 new double[] {-8478.143922064253, 0}, new double[] {0, 0},
524 new double[] {205.8018333335931, Double.NaN}, new double[][][] {
525 {{1.5229178263056111, -0.15677963159712682, 1.1816872925903983},
526 {-1.224135286541821, 0.3201779704385803, -1.1483158020643944},
527 {1.2969488018273694, -0.8376981151127962, 0.09660091060711373},
528 {-1.0033472564565908, 0.7283612031936482, -0.5947650554794823},
529 {-0.17802197322041113, -0.34399197898615985, -0.6113921282564965},
530 {-0.25687293667935585, 0.07341668289540859, 0.15584681242862208},
531 {-0.530878661936121, 0.7208093361614316, -0.6200446160740853},
532 {0.29982215558798053, -0.40520052950990504, 0.4147977845799842},
533 {0.05351938289450864, 0.3348510487959171, 0.9222262189021788},
534 {-0.8823224520772985, 0.6039483343281296, -0.894180455908895},
535 {-0.08825723194619872, 0.9833060568981935, -0.1570236438711472},
536 {0.1637236555517756, -0.6228719840792594, 1.1789882530493676},},
537
538 new double[12][3]}, new double[][][] {
539 {{2.9163384624158903E-16, -3.231433591192038E-17, 2.290897273901192E-16},
540 {-2.3636319462860973E-16, 6.381190126870034E-17, -2.248191366091609E-16},
541 {2.560037826405337E-16, -1.6896029769367465E-16, 2.109040117467388E-17},
542 {-1.9499561171519864E-16, 1.4182059587979724E-16, -1.147110775007824E-16},
543 {-3.4271779930734056E-17, -6.783853481206671E-17, -1.1756746156916025E-16},
544 {-4.9856018468543316E-17, 1.3508015854151776E-17, 3.0464333860802826E-17},
545 {-1.0242265198852129E-16, 1.38262207830166E-16, -1.1896800686893987E-16},
546 {5.759415657973307E-17, -7.51641112923199E-17, 7.963250894893407E-17},
547 {1.0531891470506986E-17, 6.456878899573994E-17, 1.7727407986513553E-16},
548 {-1.6972089767901735E-16, 1.1349386057403977E-16, -1.734213718396476E-16},
549 {-1.8526781815166296E-17, 1.8642231778983455E-16, -3.858193767513045E-17},
550 {3.214626465908195E-17, -1.2066294994525673E-16, 2.2612900090378923E-16}},
551
552 new double[12][3]}, new String[] {"-l", "1.0", "--sf", "TRIG", "--uaA",
553 "329-345.857-972.1008-1022.1204.1208-1213", "--uaB",
554 "329-345.857-972.1008-1022.1204.1208-1213"},
555 new String[] {"disable-neighbor-updates", "true"}, new String[] {}}, {
556
557
558 "Carp Parvalbumin Ca/Mg Simultaneous Bookending: L = 0.0",
559 new String[] {"5cpv_ca_a.xyz", "5cpv_ca_b.xyz", "5cpv_mg_a.xyz", "5cpv_mg_b.xyz"},
560 ThermoTestMode.GRAD, true, 0, 0,
561 new int[] {1, 303, 1401, 1402, 1482, 1488, 1489, 1602, 1603, 1604, 1605, 1606},
562
563 new double[] {-10389.28471472707, 0}, new double[] {0, 0},
564 new double[] {-204.10881484981655, Double.NaN}, new double[][][] {
565 {{-1.1609578854274971, 0.8877442339271191, -0.381997307744788},
566 {0.17136471973564316, -0.6936945821429843, -0.26469263318865544},
567 {1.7842973607219292, 0.06247987009760436, 0.6436711554923047},
568 {-0.80901615978728, -0.03278744764172048, -0.6933431691420533},
569 {-0.009221945771183293, -0.03024427307941413, -0.22321841071848292},
570 {0.44453213441769357, -0.03618099829897137, -0.09130836864141911},
571 {0.6143060586860591, -0.5593900343333869, 0.7793559372609127},
572 {-0.4099919820212454, 0.2771241953804595, -0.3596606825033808},
573 {0.2058491063283956, -1.6995564152580624, 0.3162941963413184},
574 {0.17045731075675363, 1.3626300067862616, 0.1829234871604033},
575 {-0.1369296520341554, 0.28864175741664555, -0.23181864232968774},
576 {-0.3317934170646355, 0.1069795784815214, 0.07598972633102496},},
577
578 new double[12][3]}, new double[][][] {
579 {{2.2334257856503093E-16, -1.707804485712817E-16, 7.346589191878714E-17},
580 {-3.2975061696419736E-17, 1.334426237792466E-16, 5.09166906746972E-17},
581 {-3.4327908122000154E-16, -1.202864843760528E-17, -1.2390581692419626E-16},
582 {1.5565048660891405E-16, 6.307561537504449E-18, 1.33392733415393E-16},
583 {1.7707118825002895E-18, 5.832093621377665E-18, 4.292309196176272E-17},
584 {-8.548440453491081E-17, 6.970235281623787E-18, 1.754463462531147E-17},
585 {-1.181759625502523E-16, 1.0762112290669118E-16, -1.4995022305853868E-16},
586 {7.888210075095321E-17, -5.330782752663655E-17, 6.918026206876034E-17},
587 {-3.962976007169519E-17, 3.269309449444003E-16, -6.080614905213747E-17},
588 {-3.278713882575124E-17, -2.62121095313077E-16, -3.519199394795007E-17},
589 {2.6347728499845737E-17, -5.55185665906526E-17, 4.459517949566215E-17},
590 {6.382453595443383E-17, -2.0571002605587047E-17, -1.4618517021800703E-17}},
591
592 new double[12][3]}, new String[] {"-l", "0.0", "--sf", "TRIG", "--uaA",
593 "810-829.1339-1447.1476-1490.1603.1605-1610", "--uaB",
594 "810-829.1339-1447.1476-1490.1603.1605-1610"},
595 new String[] {"disable-neighbor-updates", "true"}, new String[] {}},
596 {"Carp Parvalbumin Ca/Mg Simultaneous Bookending: L = 0.5",
597 new String[] {"5cpv_ca_a.xyz", "5cpv_ca_b.xyz", "5cpv_mg_a.xyz", "5cpv_mg_b.xyz"},
598 ThermoTestMode.GRAD, true, 0, 0,
599 new int[] {1, 303, 1401, 1402, 1482, 1488, 1489, 1602, 1603, 1604, 1605, 1606},
600
601 new double[] {-10409.965261439502, 0}, new double[] {-64.96985362393843, 0},
602 new double[] {0, Double.NaN}, new double[][][] {
603 {{-1.1610220532282816, 0.8877825087765396, -0.38190561321870153},
604 {0.17141666802985345, -0.6936868435232721, -0.26468548230330735},
605 {1.7844987915596682, 0.0625323990316684, 0.6441061559020982},
606 {-0.8091313886502285, -0.032792749245311725, -0.6934234392602314},
607 {-0.009206939878777853, -0.030315101956861712, -0.2231346110988761},
608 {0.4443810630713887, -0.036232529267353986, -0.09120578149088898},
609 {0.6143248825840462, -0.5594553644984039, 0.7794994337695262},
610 {-0.4100603484233871, 0.2771150828568485, -0.3596269108370689},
611 {0.2060121473201555, -1.6995185434801163, 0.31609655101286327},
612 {0.1704401628733358, 1.3626093888666202, 0.18294114185436605},
613 {-0.13696614734124157, 0.2886080953858223, -0.23182359983306977},
614 {-0.33178545555019134, 0.10693683664021286, 0.07599211616273438}},
615
616 new double[12][3]}, new double[][][] {
617 {{2.755227653583603E-6, -5.374905775568095E-6, -2.803630881231811E-6},
618 {-2.2802651855613476E-6, 1.9373526711774502E-6, -1.3042348507852353E-6},
619 {-4.54644512082325E-6, 8.588679262899745E-6, -1.638592826225249E-5},
620 {4.125879040195457E-6, -1.1162916089269004E-5, 1.46747768532407E-5},
621 {-6.535106551908143E-6, 7.6152604604473595E-6, -1.0766383081950437E-5},
622 {-1.988285510456933E-6, 4.732964988818367E-6, -5.78566637177147E-6},
623 {-1.3921589704368742E-6, 5.4545292442753635E-6, -3.289226282676694E-6},
624 {-1.1008869904571839E-7, -1.8223435560571488E-7, -1.842757221481861E-6},
625 {3.151330700390531E-6, -7.456983421860741E-6, 6.8281705551953564E-6},
626 {-1.0483102688141344E-6, 1.2986962580896488E-6, -2.329191170602485E-6},
627 {-7.817337568383209E-7, 2.0726923768421557E-6, -7.678219411388909E-7},
628 {-9.764459392158642E-7, 2.0130999769385483E-6, -2.226604200572524E-6}},
629
630 new double[12][3]}, new String[] {"-l", "0.5", "--sf", "TRIG", "--uaA",
631 "810-829.1339-1447.1476-1490.1603.1605-1610", "--uaB",
632 "810-829.1339-1447.1476-1490.1603.1605-1610"},
633 new String[] {"disable-neighbor-updates", "true"}, new String[] {}},
634 {"Carp Parvalbumin Ca/Mg Simultaneous Bookending: L = 1.0",
635 new String[] {"5cpv_ca_a.xyz", "5cpv_ca_b.xyz", "5cpv_mg_a.xyz", "5cpv_mg_b.xyz"},
636 ThermoTestMode.GRAD, true, 0, 0,
637 new int[] {1, 303, 1401, 1402, 1482, 1488, 1489, 1602, 1603, 1604, 1605, 1606},
638
639 new double[] {-10430.645808151916, 0}, new double[] {0, 0},
640 new double[] {204.1088148497838, Double.NaN}, new double[][][] {
641 {{-1.1609561125944228, 0.8877407807597617, -0.3819990910861595},
642 {0.17136321710200875, -0.6936932915411145, -0.2646933907193276},
643 {1.78429435562491, 0.06248553884406727, 0.6436609576859738},
644 {-0.8090134265871809, -0.032794734757721766, -0.693334070333059},
645 {-0.009226164435848005, -0.03023937880286831, -0.22322518458123763},
646 {0.4445308404726829, -0.03617792937772801, -0.09131197683309833},
647 {0.6143051128396353, -0.5593864905811907, 0.7793539392457256},
648 {-0.40999206546827605, 0.27712408910913533, -0.35966183105159466},
649 {0.205851177042927, -1.6995612712832284, 0.31629838909722086},
650 {0.17045662254298133, 1.3626308508037734, 0.1829220337727313},
651 {-0.13693017608623448, 0.2886431027192988, -0.23181908891834802},
652 {-0.3317940476955181, 0.10698088476669532, 0.07598834048574243},},
653
654 new double[12][3]}, new double[][][] {
655 {{-2.2334224114695824E-16, 1.7077979033516612E-16, -7.346623526454586E-17},
656 {3.297478244447361E-17, -1.3344238652197185E-16, -5.091685039740076E-17},
657 {3.4327852444105517E-16, 1.202970024746214E-17, 1.2390381022673677E-16},
658 {-1.5564998133445806E-16, -6.308928600450258E-18, -1.3339093627354278E-16},
659 {-1.7715122022323975E-18, -5.831161020942922E-18, -4.292441046342064E-17},
660 {8.548416104016219E-17, -6.969655660581429E-18, -1.7545343165091794E-17},
661 {1.181757920599496E-16, -1.0762045491951309E-16, 1.499498202444948E-16},
662 {-7.888211423293022E-17, 5.3307805209364545E-17, -6.918048774143343E-17},
663 {3.963014599840059E-17, -3.2693185816148815E-16, 6.080698526185848E-17},
664 {3.2787010444769745E-17, 2.621212543574982E-16, 3.5191708704298895E-17},
665 {-2.6347824234619856E-17, 5.551882042226125E-17, -4.459527352673071E-17},
666 {-6.382465553457344E-17, 2.057124913923137E-17, 1.4618244341429974E-17}},
667
668 new double[12][3]}, new String[] {"-l", "1.0", "--sf", "TRIG", "--uaA",
669 "810-829.1339-1447.1476-1490.1603.1605-1610", "--uaB",
670 "810-829.1339-1447.1476-1490.1603.1605-1610"},
671 new String[] {"disable-neighbor-updates", "true"}, new String[] {}},
672 {"Water-Sodium to Water Dimer Softcoring Test: L = 0.0",
673 new String[] {"water-dimer.xyz", "water-na.xyz"}, ThermoTestMode.GRAD, true, 0, 0,
674 intRange(1, 8),
675
676 new double[] {2.61926128937, 0}, new double[] {17.7107919457, 0},
677 new double[] {-403.664911547, Double.NaN}, new double[][][] {
678 {{-5.677212525948178, 1.424643968949981, -2.2981580317913504},
679 {-16.262337326126076, -4.011524859855653, 2.8309493626240676},
680 {20.234576106276727, -2.5759425460558103, 0.34157831359509483},
681 {-4.107688957377534E-5, -3.0491094575808766E-4, 1.5088773834586063E-4},
682 {3.855038213179264E-5, 3.0678248136167547E-4, -1.527835575474326E-4},
683 {2.526507441982699E-6, -1.8715356035878577E-6, 1.8958192015719713E-6},
684 {1.7049737457975327, 5.1628234369614825, -0.8743696444278124},},
685
686 new double[7][3]}, new double[][][] {
687 {{34.717862394027094, -6.918833553692483, 13.770559137316184},
688 {44.6501751603733, 28.43111292615495, -17.0699023179925},
689 {-125.31576661843137, 11.778366126287878, -3.1016651209654698}, {0.0, 0.0, 0.0},
690 {0.0, 0.0, 0.0}, {0.0, 0.0, 0.0},
691 {45.94772906403098, -33.29064549875034, 6.401008301641785}},
692
693 new double[7][3]}, new String[] {"-l", "0.0", "--ac", "1-3", "--ac2", "1"},
694 new String[] {}, new String[] {}},
695 {"Water-Sodium to Water Dimer Softcoring Test: L = 0.1",
696 new String[] {"water-dimer.xyz", "water-na.xyz"}, ThermoTestMode.GRAD, true, 0, 0,
697 intRange(1, 8),
698
699 new double[] {2.850550906, 0}, new double[] {-8.71629068942, 0},
700 new double[] {-141.329823284, Double.NaN}, new double[][][] {
701 {{-2.9145419263022183, 0.8087080881505835, -1.1826969161418945},
702 {-10.75486342640376, -1.8502906018217953, 1.441002156956333},
703 {10.24415407077068, -1.5066777027112754, 0.13037812748160396},
704 {-3.940188541217106E-5, -3.046447158662098E-4, 1.504274409501606E-4},
705 {4.348535004930246E-5, 3.0537557756647634E-4, -1.5274632518595476E-4},
706 {5.712456879867664E-6, -9.857865557163607E-7, 1.145744081896436E-6},
707 {3.425241486013779, 2.548260471307343, -0.38868219515588814}},
708
709 new double[7][3]}, new double[][][] {
710 {{21.219764941164165, -5.301043079657732, 8.702685150224386},
711 {59.404183336652935, 15.559560209446712, -10.86515770297192},
712 {-76.64822167834306, 9.426371612748719, -1.3109489861942616},
713 {8.074643396228061E-5, 1.2233389408248271E-5, -2.0734472804670857E-5},
714 {2.2173087875276693E-4, -6.414406402810961E-5, 2.6740754942568507E-6},
715 {1.7176239889004114E-4, 3.3106310006263015E-5, -3.056405648898283E-5},
716 {-3.9762008391856365, -19.684869938173087, 3.4734701633955916}},
717
718 new double[7][3]}, new String[] {"-l", "0.1", "--ac", "1-3", "--ac2", "1"},
719 new String[] {}, new String[] {}},
720 {"Water-Sodium to Water Dimer Softcoring Test: L = 0.25",
721 new String[] {"water-dimer.xyz", "water-na.xyz"}, ThermoTestMode.GRAD, false, 0, 0,
722 intRange(1, 8),
723
724 new double[] {0.974157909812, 0}, new double[] {-10.6503844408, 0},
725 new double[] {85.6111397243, Double.NaN}, new double[][][] {
726 {{-0.8087917281519832, 0.230920162100479, -0.31677660566831417},
727 {-3.2828492735876607, -0.4439185686570825, 0.3694313047851273},
728 {2.7179567720851567, -0.4453048785572774, 0.02913967164983769},
729 {1.1191654242300788E-4, -2.8436948793288743E-4, 1.1810143809370248E-4},
730 {3.948395026959898E-4, 1.992169489195181E-4, -1.434611972003E-4},
731 {4.178187790317917E-4, 1.7909105729778537E-5, -3.118731586794815E-5},
732 {1.3727596548303362, 0.6583705285471645, -0.08173782369167623}},
733
734 new double[7][3]}, new double[][][] {
735 {{8.203573000276744, -2.407260166945433, 3.3325694163231305},
736 {34.07610421632959, 4.853369454929932, -4.037664708947347},
737 {-28.691594190062066, 4.581997199828557, -0.30050567027187414},
738 {0.0030722784633713677, 4.013278190369727E-4, -6.302447025815397E-4},
739 {0.006927249436665227, -0.0021139142799777464, 2.0753922987915073E-4},
740 {0.008777938573427544, 2.1069797116961348E-4, -5.578096868382651E-4},
741 {-13.606860493017741, -7.026604599323282, 1.006581478055631}},
742
743 new double[7][3]}, new String[] {"-l", "0.25", "--ac", "1-3", "--ac2", "1"},
744 new String[] {}, new String[] {}},
745 {"Water-Sodium to Water Dimer Softcoring Test: L = 0.4",
746 new String[] {"water-dimer.xyz", "water-na.xyz"}, ThermoTestMode.GRAD, true, 0, 0,
747 intRange(1, 8),
748
749 new double[] {0.110037916292, 0}, new double[] {-2.26607999096, 0},
750 new double[] {30.1632932351, Double.NaN}, new double[][][] {
751 {{-0.1135918356399739, 0.031116766115897736, -0.041752199556192734},
752 {-0.4844299250151845, -0.052731371080211295, 0.044681832632104526},
753 {0.34363344315228866, -0.058702201304027674, 0.003020221799192616},
754 {0.0015984934497727645, -9.5805104515793E-5, -1.7187445022315994E-4},
755 {0.0036936320812564253, -8.190880189616524E-4, -2.9551973622635126E-5},
756 {0.004965647007413021, 1.9626489810683822E-5, -2.4565648661058724E-4},
757 {0.24413054496442746, 0.08121207290200799, -0.005502771964648001}},
758
759 new double[7][3]}, new double[][][] {
760 {{1.9790078473632116, -0.5466360053457613, 0.7442354166761601},
761 {7.5148029466680395, 1.0124425518627698, -0.8308708232627937},
762 {-6.43514862353287, 1.0832749443479637, -0.07362559817071525},
763 {0.020724339018201313, 0.002599652594099935, -0.003961656523433774},
764 {0.04599278248257384, -0.014257367173055743, 0.0016722631878542601},
765 {0.0653051230131746, -5.218785659658921E-4, -0.002695798950450919},
766 {-3.1906844150123317, -1.5369018977200506, 0.16524619704337926}},
767
768 new double[7][3]}, new String[] {"-l", "0.4", "--ac", "1-3", "--ac2", "1"},
769 new String[] {}, new String[] {}},
770 {"Water-Sodium to Water Dimer Softcoring Test: L = 0.5",
771 new String[] {"water-dimer.xyz", "water-na.xyz"}, ThermoTestMode.GRAD, true, 0, 0,
772 intRange(1, 8),
773
774 new double[] {-0.00563228503, 0}, new double[] {-0.384474276341, 0},
775 new double[] {9.92344157784, Double.NaN}, new double[][][] {
776 {{-0.009836012990445158, 0.0029589899860514987, -0.0034155479511113907},
777 {-0.10184729530732066, 5.327593924820649E-4, 0.0057353060076526016},
778 {0.016303644915009106, -0.003689130235597261, -0.0025312483345677947},
779 {0.0038107513266330815, 0.003205959229414844, -8.159303166222604E-4},
780 {0.015696774651107862, -0.0037059448135031083, 2.7367574864321055E-4},
781 {0.013110199299505135, -0.0030608519096883596, -6.447454233376755E-4},
782 {0.06276193810551062, 0.00375821835084032, 0.0013984902693433081}},
783
784 new double[7][3]}, new double[][][] {
785 {{0.34392939856659827, -0.09158200189901379, 0.12910527928175775},
786 {1.1760470096366977, 0.23616310164517534, -0.05057458142688201},
787 {-1.0121014533251116, 0.15248505767669873, -0.07620425570370314},
788 {0.011511611202389766, 0.10091080897132744, -0.010683387778191795},
789 {0.26561965255269504, -0.057037692991738595, 0.004170051640670491},
790 {0.06748923520446536, -0.09680681673784088, -0.004103281313994225},
791 {-0.8524954538377344, -0.24413245666460826, 0.008290175300342928}},
792
793 new double[7][3]}, new String[] {"-l", "0.5", "--ac", "1-3", "--ac2", "1"},
794 new String[] {}, new String[] {}},
795 {"Water-Sodium to Water Dimer Softcoring Test: L = 0.6",
796 new String[] {"water-dimer.xyz", "water-na.xyz"}, ThermoTestMode.GRAD, true, 0, 0,
797 intRange(1, 8),
798
799 new double[] {-0.010482021204, 0}, new double[] {0.180367758296, 0},
800 new double[] {3.43489697899, Double.NaN}, new double[][][] {
801 {{-0.008649580249889975, 0.0022485600112194097, -8.650726609048702E-4},
802 {-0.11266057761820013, 0.024694362034725272, 0.0260361864833212},
803 {0.015218945213069365, -0.00914553962283122, -0.02282021358689983},
804 {2.5066123005239126E-4, 0.031224802161732783, -0.0034016265001422597},
805 {0.08190367312390062, -0.018650512458739874, 3.4326425602877264E-4},
806 {0.0064502702618650975, -0.028665281917196323, -2.4419049163833308E-5},
807 {0.017486608039202628, -0.0017063902089100456, 7.31881057760824E-4}},
808
809 new double[7][3]}, new double[][][] {
810 {{-0.2058541574059063, 0.04582469500939303, -0.032210244958406097},
811 {-1.0401985420569162, 0.37454108615584125, 0.47159019722227746},
812 {0.621062316619004, -0.20684943525738805, -0.39772097441817644},
813 {-0.10747378808409644, 0.5344440256235907, -0.051334182756052026},
814 {1.2489110819012346, -0.2984439482946815, -0.007849154127628813},
815 {-0.2968358770072303, -0.4711447951314603, 0.027030911889720764},
816 {-0.21961103396608955, 0.02162837189470477, -0.009506552851734766}},
817
818 new double[7][3]}, new String[] {"-l", "0.6", "--ac", "1-3", "--ac2", "1"},
819 new String[] {}, new String[] {}},
820 {"Water-Sodium to Water Dimer Softcoring Test: L = 0.75",
821 new String[] {"water-dimer.xyz", "water-na.xyz"}, ThermoTestMode.GRAD, false, 0, 0,
822 intRange(1, 8),
823
824 new double[] {0.0829830017977, 0}, new double[] {1.27996801166, 0},
825 new double[] {11.9168667081, Double.NaN}, new double[][][] {
826 {{-0.08520546853249629, 0.02002391005107648, -0.015337962582326878},
827 {-0.5072588908421117, 0.17043258439271164, 0.2050240947794576},
828 {0.25349423271871935, -0.0805529898238775, -0.17425804293531943},
829 {-0.0506141349576535, 0.22437919802386463, -0.02673413275920512},
830 {0.5612744057443229, -0.15045901450002216, -0.007975329992730817},
831 {-0.17340305926115046, -0.1836639508243553, 0.019217340035074935},
832 {0.0017129151303696705, -1.5973731939765025E-4, 6.403345504972649E-5}},
833
834 new double[7][3]}, new double[][][] {
835 {{-0.9272841243095468, 0.22051093159987895, -0.19112897890018363},
836 {-4.804523425931252, 1.8204016841668706, 2.205227241175209},
837 {2.994591363051441, -0.8476847561428067, -1.8543652609150763},
838 {-0.6630357716342526, 2.3143378077667545, -0.3143349864556007},
839 {5.981286181791292, -1.7558797902974297, -0.1325990985326137},
840 {-2.5478012735172113, -1.7548767300847088, 0.28852239893802845},
841 {-0.033232949450470756, 0.003190852991440346, -0.0013213153097627725}},
842
843 new double[7][3]}, new String[] {"-l", "0.75", "--ac", "1-3", "--ac2", "1"},
844 new String[] {}, new String[] {}},
845 {"Water-Sodium to Water Dimer Softcoring Test: L = 0.9",
846 new String[] {"water-dimer.xyz", "water-na.xyz"}, ThermoTestMode.GRAD, true, 0, 0,
847 intRange(1, 8),
848
849 new double[] {0.397347160918, 0}, new double[] {2.75351951766, 0},
850 new double[] {5.37105253479, Double.NaN}, new double[][][] {
851 {{-0.31777515190383454, 0.06654081254942476, -0.061155191586619},
852 {-1.6398611619313017, 0.5966978877811928, 0.7834547554508479},
853 {1.0488049692859205, -0.2533323908962118, -0.6671198866431937},
854 {-0.2740673658300599, 0.8081103620907575, -0.10766129298323851},
855 {2.047913761610311, -0.6075094312234967, -0.06048821698815812},
856 {-0.8650389649167101, -0.6105053956998422, 0.11296922972554538},
857 {2.3913685674263586E-5, -1.84460182457579E-6, 6.030248161425271E-7}},
858
859 new double[7][3]}, new double[][][] {
860 {{-2.2518864370570797, 0.34060365423349903, -0.39403631649939214},
861 {-10.009715065439687, 3.6614921088174124, 5.8097209404351},
862 {7.999725481943763, -1.2317995270963276, -5.0518709728060704},
863 {-2.770170337863771, 5.712033354860753, -0.7489455032193499},
864 {13.829283560251287, -4.313426777210048, -0.6802886194434065},
865 {-6.796164346239962, -4.168990151630222, 1.065450769329658},
866 {-0.001072855594543585, 8.733802493437212E-5, -3.0297796539917875E-5}},
867
868 new double[7][3]}, new String[] {"-l", "0.9", "--ac", "1-3", "--ac2", "1"},
869 new String[] {}, new String[] {}},
870 {"Water-Sodium to Water Dimer Softcoring Test: L = 1.0",
871 new String[] {"water-dimer.xyz", "water-na.xyz"}, ThermoTestMode.GRAD, true, 0, 0,
872 intRange(1, 8),
873
874 new double[] {0.678991455919, 0}, new double[] {2.62955540214, 0},
875 new double[] {-9.66072732215, Double.NaN}, new double[][][] {
876 {{-0.5967303838923714, 0.09294823835932634, -0.10065639850802355},
877 {-2.7182918130174105, 0.9717379671351778, 1.5205922317233873},
878 {2.059991765566265, -0.34808314525235373, -1.325262659176951},
879 {-0.6963970726796157, 1.5240570186788684, -0.18878016013974244},
880 {3.628422351985993, -1.0902413269297175, -0.16292088956721837},
881 {-1.6769948479628611, -1.150418751991301, 0.2570278756685478}, {0.0, 0.0, 0.0}},
882
883 new double[7][3]}, new double[][][] {
884 {{-3.3452690489458763, 0.1345828814666643, -0.35806274829197704},
885 {-10.93663217405725, 3.541368037656955, 8.988383737482236},
886 {12.27657065565445, -0.4907488150661682, -8.232590002516137},
887 {-5.952373992671734, 8.67661156047595, -0.8110392583955152},
888 {17.201456551782357, -5.049385958157128, -1.4130599622896023},
889 {-9.243751991761942, -6.81242770637627, 1.826368234010993}, {0.0, 0.0, 0.0}},
890
891 new double[7][3]}, new String[] {"-l", "1.0", "--ac", "1-3", "--ac2", "1"},
892 new String[] {}, new String[] {}}, {"Water-Sodium to Water Dimer Free Energy Test",
893 new String[] {"water-dimer.xyz", "water-na.xyz"}, ThermoTestMode.FREE, true, 16.0, 16.0, null,
894 null, null, null, null, null,
895 new String[] {"-d", "1.0", "-l", "0.5", "--ac", "1-3", "--ac2", "1", "-n", "20000", "-Q",
896 "5000", "-k", "20.0", "-w", "20.0", "-r", "5.0", "-C", "10"},
897 new String[] {"disable-neighbor-updates", "true", "lambda-bin-width", "0.025",
898 "flambda-bin-width", "5.0", "randomseed", "2019"}, new String[] {}},
899 {"Dual Well Gradient Test: L = 0.5",
900 new String[] {"dualWell-cis.xyz", "dualWell-trans.xyz"}, ThermoTestMode.GRAD, false, 0, 0,
901 new int[] {0, 1, 2, 3}, new double[] {1.0, 1.0}, new double[] {2.0, 2.0},
902 new double[] {0, Double.NaN}, new double[][][] {
903 {{8.561159028211611E-6, 5.743324479222663E-6, -1.4984967045837012E-10},
904 {-7.001815021252073E-6, -4.427074139684514E-6, 1.2219984010349385E-10},
905 {-2.1489889641599235E-6, 7.92540413855834E-9, 8.443778889936074E-12},
906 {5.896449572003855E-7, -1.3241757436767075E-6, 1.9206051464940198E-11}},
907 new double[4][3]}, new double[][][] {
908 {{-4.504521213498369E-18, -1.6250327093073752E-17, -8.651645616845026E-13},
909 {-4.504521213498369E-18, -1.6250327093073752E-17, -8.651646266038085E-13},
910 {4.504097697024742E-18, 1.6249944935630753E-17, 8.651645891779005E-13},
911 {4.504203576143149E-18, 1.6249903576600125E-17, 8.651645991104025E-13}},
912 new double[4][3]}, new String[] {"-l", "0.5"},
913 new String[] {"pj.nt", "1", "lambda-bin-width", "0.02", "flambda-bin-width", "0.20",
914 "disable-neighbor-updates", "true", "ost-temperOffset", "6.0", "randomseed", "2020"},
915 new String[0]}, {"Dual Well 1-Step MC-OST Test",
916 new String[] {"dualWell-cis.xyz",
917 "dualWell-trans.xyz"}, ThermoTestMode.FREE, true, 0, 1.0, null,
918 null, null, null, null, null,
919 new String[] {"-l", "0.5", "--bM", "0.1", "-b", "ADIABATIC", "-i", "VERLET", "-d", "2.0",
920 "-k", "1000.0", "-w", "1000.0", "-r", "1.0", "-Q", "1000", "-n", "250000", "-t",
921 "298.15", "--tp", "6.0", "--mcMD", "10", "--mcL", "0.10"},
922 new String[] {"pj.nt", "1", "lambda-bin-width", "0.02", "flambda-bin-width", "0.20",
923 "disable-neighbor-updates", "true", "ost-temperOffset", "6.0", "randomseed", "445"},
924 new String[] {"--mc", "true"}}, {"Dual Well 2-Step MC-OST Test",
925 new String[] {"dualWell-cis.xyz",
926 "dualWell-trans.xyz"}, ThermoTestMode.FREE, true, 0, 1.0, null,
927 null, null, null, null, null,
928 new String[] {"-l", "0.5", "--bM", "0.1", "-b", "ADIABATIC", "-i", "VERLET", "-d", "2.0",
929 "-k", "1000.0", "-w", "1000.0", "-r", "1.0", "-Q", "1000", "-n", "250000", "-t",
930 "298.15", "--tp", "6.0", "--mcMD", "10", "--mcL", "0.10"},
931 new String[] {"pj.nt", "1", "lambda-bin-width", "0.02", "flambda-bin-width", "0.20",
932 "disable-neighbor-updates", "true", "ost-temperOffset", "6.0", "randomseed", "445"},
933 new String[] {"--mc", "true", "--ts", "true"}}, {"Short 1-Step MC-OST Test",
934 new String[] {"dualWell-cis.xyz",
935 "dualWell-trans.xyz"}, ThermoTestMode.FREE, true, 0, 2.0, null,
936 null, null, null, null, null,
937 new String[] {"-l", "0.5", "--bM", "0.1", "-b", "ADIABATIC", "-i", "VERLET", "-d", "2.0",
938 "-k", "1000.0", "-w", "1000.0", "-r", "0.01", "-Q", "100", "-n", "200", "-t", "100.0",
939 "--tp", "6.0", "--mcMD", "10", "--mcL", "0.10"},
940 new String[] {"pj.nt", "1", "lambda-bin-width", "0.02", "flambda-bin-width", "0.20",
941 "disable-neighbor-updates", "true", "ost-temperOffset", "6.0", "randomseed", "445"},
942 new String[] {"--mc", "true"}}, {"Short 2-Step MC-OST Test",
943 new String[] {"dualWell-cis.xyz",
944 "dualWell-trans.xyz"}, ThermoTestMode.FREE, true, 0, 2.0, null,
945 null, null, null, null, null,
946 new String[] {"-l", "0.5", "--bM", "0.1", "-b", "ADIABATIC", "-i", "VERLET", "-d", "2.0",
947 "-k", "1000.0", "-w", "1000.0", "-r", "0.01", "-Q", "100", "-n", "200", "-t", "100.0",
948 "--tp", "6.0", "--mcMD", "10", "--mcL", "0.10"},
949 new String[] {"pj.nt", "1", "lambda-bin-width", "0.02", "flambda-bin-width", "0.20",
950 "disable-neighbor-updates", "true", "ost-temperOffset", "6.0", "randomseed", "445"},
951 new String[] {"--mc", "true", "--ts", "true"}}});
952 }
953
954
955
956
957
958
959
960
961
962 private static boolean approxEquals(double v1, double v2, double absTol) {
963 double diff = v1 - v2;
964 return Math.abs(diff) < absTol;
965 }
966
967 private static int[] intRange(int low, int high) {
968 return IntStream.range(low, high).toArray();
969 }
970
971 @Test
972 public void testThermodynamics() {
973 if (doTest) {
974 logger.info(format(" Thermodynamics test: %s\n", info));
975 switch (mode) {
976 case HELP:
977 testHelp();
978 break;
979 case FREE:
980 testFreeEnergy();
981 break;
982 case GRAD:
983 testStaticGradients();
984 break;
985 default:
986 throw new IllegalStateException(
987 format(" Thermodynamics test mode %s not recognized!", mode));
988 }
989 } else {
990 logger.info(format(" Skipping test %s: use ffx.ci true to enable!", info));
991 }
992 }
993
994 private void testHelp() {
995 String[] args = {"-h"};
996 binding.setVariable("args", args);
997
998
999 Thermodynamics thermodynamics = new Thermodynamics(binding).run();
1000 algorithmsScript = thermodynamics;
1001 }
1002
1003 private String[] assembleArgs() {
1004 List<String> argList = new ArrayList<>(flags);
1005 opts.forEach((String k, String v) -> {
1006 argList.add(k);
1007 argList.add(v);
1008 });
1009 argList.addAll(Arrays.stream(copiedFiles).map(File::getPath).collect(Collectors.toList()));
1010 return argList.toArray(new String[0]);
1011 }
1012
1013
1014
1015
1016
1017 private void testFreeEnergy() {
1018 binding.setVariable("args", assembleArgs());
1019
1020
1021 Thermodynamics thermodynamics = new Thermodynamics(binding);
1022 thermodynamics.setProperties(algorithmConfig);
1023 algorithmsScript = thermodynamics;
1024
1025 thermodynamics.run();
1026 OrthogonalSpaceTempering orthogonalSpaceTempering = thermodynamics.getOST();
1027 Histogram histogram = orthogonalSpaceTempering.getHistogram();
1028 double delG = histogram.updateFreeEnergyDifference(true, false);
1029 assertEquals(format(" Test %s: not within tolerance %12.5g", info, feTol), freeEnergy, delG, feTol);
1030 }
1031
1032
1033 private void testStaticGradients() {
1034
1035 binding.setVariable("args", assembleArgs());
1036
1037
1038 Thermodynamics thermodynamics = new Thermodynamics(binding);
1039 thermodynamics.setProperties(algorithmConfig);
1040 algorithmsScript = thermodynamics;
1041 thermodynamics.run();
1042
1043 OrthogonalSpaceTempering orthogonalSpaceTempering = thermodynamics.getOST();
1044 orthogonalSpaceTempering.setPropagateLambda(false);
1045 CrystalPotential under = thermodynamics.getPotential();
1046 int nVars = orthogonalSpaceTempering.getNumberOfVariables();
1047
1048 double[] x = new double[nVars];
1049 x = orthogonalSpaceTempering.getCoordinates(x);
1050 double[] gOSTPre = new double[nVars];
1051 double[] gUnderPre = new double[nVars];
1052
1053 logger.info(" Testing the OST potential before bias added.");
1054 EnergyResult ostPre = testGradientSet("Unbiased OST", orthogonalSpaceTempering, x, gOSTPre, 0);
1055 logger.info(" Testing the underlying CrystalPotential before bias added.");
1056 EnergyResult underPre = testGradientSet("Unbiased potential", under, x, gUnderPre, 0);
1057
1058
1059 ostPre.assertResultsEqual(underPre);
1060 }
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072 private EnergyResult testGradientSet(String description, CrystalPotential potential, double[] x,
1073 double[] g, int tableIndex) {
1074 assertTrue(format(" Potential %s is not a lambda interface!", potential),
1075 potential instanceof LambdaInterface);
1076
1077 EnergyResult er = new EnergyResult(description, potential, x, g);
1078
1079 checkThGradScalar(er.energy, pe, tableIndex, peTol, "potential energy");
1080 checkThGradScalar(er.firstLam, dudl, tableIndex, dudlTol, "dU/dL");
1081 checkThGradArray(er.gradient, dudx, tableIndex, dudxTol, "dU/dX gradient");
1082 if (er.hasSecondDerivatives) {
1083 checkThGradScalar(er.secondLam, d2udl2, tableIndex, d2udl2Tol, "d2U/dL2");
1084 checkThGradArray(er.lamGradient, d2udxdl, tableIndex, d2udxdlTol, "d2U/dXdL gradient");
1085 }
1086
1087 return er;
1088 }
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099 private void checkThGradScalar(double actual, double[] expected, int tableIndex, double tol,
1100 String description) {
1101 if (debugMode) {
1102 logger.info(format(" %s is %20.12g", description, actual));
1103 } else {
1104 assertEquals(format(" Expected %s %12.6g, received %12.6g from test %s", description,
1105 expected[tableIndex], actual, info), expected[tableIndex], actual, tol);
1106 }
1107 }
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119 private void checkThGradArray(double[] actual, double[][][] expected, int tableIndex, double tol,
1120 String description) {
1121 double[] actualSlice = new double[3];
1122 StringBuilder sb = null;
1123 for (int i = 0; i < numGradAtoms; i++) {
1124 int i3 = i * 3;
1125 arraycopy(actual, i3, actualSlice, 0, 3);
1126 if (debugMode) {
1127 if (i == 0) {
1128 sb = new StringBuilder(
1129 format("\n Array %s for test %s at atom %d\n", description, info, i));
1130 sb.append(format(" Expected %s\n", Arrays.toString(expected[tableIndex][i])));
1131 }
1132 sb.append(format("%s\n", Arrays.toString(actualSlice)));
1133 } else {
1134 double[] exp = expected[tableIndex][i];
1135 assertArrayEquals(format(" Discrepancy found on array of %s for test %s on atom %d."
1136 + "\n Expected: %s\n Found: %s", description, info, i, Arrays.toString(exp),
1137 Arrays.toString(actualSlice)), exp, actualSlice, tol);
1138 }
1139 }
1140 if (debugMode) {
1141 logger.info(sb.toString());
1142 }
1143 }
1144
1145 private enum ThermoTestMode {
1146 HELP, FREE, GRAD
1147 }
1148
1149
1150 private class EnergyResult {
1151
1152 final double energy;
1153 final double firstLam;
1154 final double secondLam;
1155 final int nVars;
1156 final boolean hasSecondDerivatives;
1157 private final String description;
1158
1159 private final double[] gradient;
1160 private final double[] lamGradient;
1161
1162 public EnergyResult(String description, CrystalPotential potential, double[] x, double[] g) {
1163 this.description = description;
1164
1165 LambdaInterface linter = (LambdaInterface) potential;
1166 energy = potential.energyAndGradient(x, g, false);
1167 firstLam = linter.getdEdL();
1168 nVars = g.length;
1169 gradient = copyOf(g, nVars);
1170
1171 hasSecondDerivatives = !(potential instanceof OrthogonalSpaceTempering);
1172 if (hasSecondDerivatives) {
1173 secondLam = linter.getd2EdL2();
1174 lamGradient = new double[g.length];
1175 linter.getdEdXdL(lamGradient);
1176 } else {
1177 secondLam = 0;
1178 lamGradient = null;
1179 }
1180 }
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190 public void assertResultsEqual(EnergyResult other) {
1191 assertEquals(
1192 format(" Test %s: potential energy for %s did not " + "match %s, %12.6g != %12.6g.", info,
1193 this.toString(), other.toString(), this.energy, other.energy), this.energy,
1194 other.energy, peTol);
1195 assertEquals(format(" Test %s: dU/dL for %s did not " + "match %s, %12.6g != %12.6g.", info,
1196 this.toString(), other.toString(), this.firstLam, other.firstLam), this.firstLam,
1197 other.firstLam, dudlTol);
1198 assertArrayEquals(format(" Test %s: dU/dX for %s did not match %s.", info, this.toString(),
1199 other.toString()), this.gradient, other.gradient, dudxTol);
1200
1201 if (hasSecondDerivatives && other.hasSecondDerivatives) {
1202 assertEquals(
1203 format(" Test %s: d2U/dL2 for %s did not " + "match %s, %12.6g != %12.6g.", info,
1204 toString(), other.toString(), this.secondLam, other.secondLam), this.secondLam,
1205 other.secondLam, d2udl2Tol);
1206 assertArrayEquals(
1207 format(" Test %s: d2U/dXdL for %s did not match %s.", info, this.toString(),
1208 other.toString()), this.lamGradient, other.lamGradient, d2udxdlTol);
1209 }
1210 }
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221 public void assertResultsInequal(EnergyResult other) {
1222 int diffsFound = 0;
1223 StringBuilder sb = new StringBuilder(" Equalities found between ");
1224 sb = sb.append(toString()).append(" and ").append(other.toString());
1225 sb = sb.append(" for test ").append(info);
1226 boolean equalFound = false;
1227
1228 if (approxEquals(energy, other.energy, peTol)) {
1229 equalFound = true;
1230 sb.append(format("\n Potential energy %12.6g == other potential energy %12.6g", energy,
1231 other.energy));
1232 } else {
1233 ++diffsFound;
1234 }
1235
1236 if (approxEquals(firstLam, other.firstLam, dudlTol)) {
1237 equalFound = true;
1238 sb.append(format("\n Lambda derivative %12.6g == other lambda derivative %12.6g", firstLam,
1239 other.firstLam));
1240 } else {
1241 ++diffsFound;
1242 }
1243
1244 for (int i = 0; i < nVars; i++) {
1245 if (approxEquals(gradient[i], other.gradient[i], dudxTol)) {
1246 equalFound = true;
1247 sb.append(format("\n Gradient %d %12.6g == other gradient %12.6g", i, gradient[i],
1248 other.gradient[i]));
1249 } else {
1250 ++diffsFound;
1251 }
1252 }
1253
1254 if (hasSecondDerivatives && other.hasSecondDerivatives) {
1255 if (approxEquals(secondLam, other.secondLam, d2udl2Tol)) {
1256 equalFound = true;
1257 sb.append(
1258 format("\n Lambda derivative %12.6g == other lambda derivative %12.6g", secondLam,
1259 other.secondLam));
1260 } else {
1261 ++diffsFound;
1262 }
1263
1264 for (int i = 0; i < nVars; i++) {
1265 if (approxEquals(lamGradient[i], other.lamGradient[i], d2udxdlTol)) {
1266 equalFound = true;
1267 sb.append(format("\n Lambda gradient %d %12.6g == other lambda gradient %12.6g", i,
1268 lamGradient[i], other.lamGradient[i]));
1269 } else {
1270 ++diffsFound;
1271 }
1272 }
1273 }
1274
1275 if (equalFound) {
1276 logger.info(sb.toString());
1277 }
1278 assertTrue(format(" No inequalities found between %s and %s for test %s!", this.toString(),
1279 other.toString(), info), diffsFound > 0);
1280 }
1281
1282 @Override
1283 public String toString() {
1284 return description;
1285 }
1286 }
1287 }