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