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.dynamics;
39
40 import edu.rit.pj.Comm;
41 import ffx.algorithms.AlgorithmListener;
42 import ffx.algorithms.Terminatable;
43 import ffx.algorithms.dynamics.integrators.BetterBeeman;
44 import ffx.algorithms.dynamics.integrators.Integrator;
45 import ffx.algorithms.dynamics.integrators.IntegratorEnum;
46 import ffx.algorithms.dynamics.integrators.Respa;
47 import ffx.algorithms.dynamics.integrators.Stochastic;
48 import ffx.algorithms.dynamics.integrators.VelocityVerlet;
49 import ffx.algorithms.dynamics.thermostats.Adiabatic;
50 import ffx.algorithms.dynamics.thermostats.Berendsen;
51 import ffx.algorithms.dynamics.thermostats.Bussi;
52 import ffx.algorithms.dynamics.thermostats.Thermostat;
53 import ffx.algorithms.dynamics.thermostats.ThermostatEnum;
54 import ffx.algorithms.thermodynamics.OrthogonalSpaceTempering;
55 import ffx.crystal.Crystal;
56 import ffx.numerics.Constraint;
57 import ffx.numerics.Potential;
58 import ffx.numerics.math.RunningStatistics;
59 import ffx.potential.MolecularAssembly;
60 import ffx.potential.SystemState;
61 import ffx.potential.UnmodifiableState;
62 import ffx.potential.bonded.Atom;
63 import ffx.potential.bonded.LambdaInterface;
64 import ffx.potential.extended.ExtendedSystem;
65 import ffx.potential.openmm.OpenMMDualTopologyEnergy;
66 import ffx.potential.openmm.OpenMMEnergy;
67 import ffx.potential.parameters.ForceField;
68 import ffx.potential.parsers.DYNFilter;
69 import ffx.potential.parsers.PDBFilter;
70 import ffx.potential.parsers.XPHFilter;
71 import ffx.potential.parsers.XYZFilter;
72 import ffx.utilities.TinkerUtils;
73 import org.apache.commons.configuration2.CompositeConfiguration;
74
75 import java.io.File;
76 import java.util.ArrayList;
77 import java.util.Arrays;
78 import java.util.EnumSet;
79 import java.util.List;
80 import java.util.logging.Level;
81 import java.util.logging.Logger;
82
83 import static ffx.utilities.Constants.FSEC_TO_PSEC;
84 import static ffx.utilities.Constants.KCAL_TO_GRAM_ANG2_PER_PS2;
85 import static ffx.utilities.Constants.NS2SEC;
86 import static ffx.utilities.FileUtils.relativePathTo;
87 import static java.lang.String.format;
88 import static java.util.Arrays.fill;
89 import static org.apache.commons.io.FilenameUtils.getExtension;
90 import static org.apache.commons.io.FilenameUtils.removeExtension;
91
92
93
94
95
96
97
98 public class MolecularDynamics implements Runnable, Terminatable {
99
100 private static final Logger logger = Logger.getLogger(MolecularDynamics.class.getName());
101
102
103
104
105 private static final double DEFAULT_LOG_INTERVAL = 0.25;
106
107
108
109 private static final double DEFAULT_RESTART_INTERVAL = 1.0;
110
111
112
113 private static final double DEFAULT_TRAJECTORY_INTERVAL = 10.0;
114
115
116
117 private static final int DEFAULT_DYNAMICS_SLEEP_TIME = 25000;
118
119
120
121
122
123
124 protected MolecularAssembly[] molecularAssembly;
125
126
127
128 private final Potential potential;
129
130
131
132 protected final AlgorithmListener algorithmListener;
133
134
135
136 private final Integrator integrator;
137
138
139
140 private Thermostat thermostat;
141
142
143
144 boolean constantPressure = false;
145
146
147
148 Barostat barostat = null;
149
150
151
152 protected final SystemState state;
153
154
155
156 protected UnmodifiableState initialState;
157
158
159
160
161 private UnmodifiableState storedState;
162
163
164
165
166 private final RunningStatistics temperatureStats = new RunningStatistics();
167
168
169
170 private final RunningStatistics potentialEnergyStats = new RunningStatistics();
171
172
173
174 private final RunningStatistics kineticEnergyStats = new RunningStatistics();
175
176
177
178 private final RunningStatistics totalEnergyStats = new RunningStatistics();
179
180
181
182
183
184
185 protected double dt = 0.001;
186
187
188
189 private long nSteps = 1000;
190
191
192
193 double targetTemperature = 298.15;
194
195
196
197 protected boolean initVelocities = true;
198
199
200
201
202 protected boolean automaticWriteouts = true;
203
204
205
206 protected double totalSimTime = 0.0;
207
208
209
210
211 protected double restartInterval = DEFAULT_RESTART_INTERVAL;
212
213
214
215 protected int restartFrequency;
216
217
218
219 private double trajectoryInterval = DEFAULT_TRAJECTORY_INTERVAL;
220
221
222
223 protected int trajectoryFrequency;
224
225
226
227 private double logInterval = DEFAULT_LOG_INTERVAL;
228
229
230
231 protected int logFrequency;
232
233
234
235
236 protected String fileType = "XYZ";
237
238
239
240 boolean saveSnapshotAsPDB = true;
241
242
243
244 PDBFilter[] pdbFilter = null;
245
246
247
248 File restartFile = null;
249
250
251
252 boolean loadRestart = false;
253
254
255
256 DYNFilter dynFilter = null;
257
258
259
260 private File fallbackDynFile;
261
262
263
264
265 Level basicLogging = Level.INFO;
266
267
268
269 private MDVerbosity verbosityLevel = MDVerbosity.VERBOSE;
270
271
272
273
274 boolean initialized = false;
275
276
277
278 private boolean terminate = false;
279
280
281
282 protected boolean done;
283
284
285
286
287 private ExtendedSystem esvSystem;
288
289
290
291 private Stochastic esvIntegrator;
292
293
294
295 private Adiabatic esvThermostat;
296
297
298
299 private int printEsvFrequency = -1;
300
301
302
303
304 private boolean nonEquilibriumLambda;
305
306
307
308 private NonEquilbriumDynamics nonEquilibriumDynamics;
309
310
311
312
313
314
315
316
317
318
319
320 public MolecularDynamics(MolecularAssembly assembly, Potential potentialEnergy,
321 AlgorithmListener listener, ThermostatEnum requestedThermostat,
322 IntegratorEnum requestedIntegrator) {
323 this.molecularAssembly = new MolecularAssembly[1];
324 this.molecularAssembly[0] = assembly;
325 this.algorithmListener = listener;
326 this.potential = potentialEnergy;
327 if (potentialEnergy instanceof Barostat) {
328 constantPressure = true;
329 barostat = (Barostat) potentialEnergy;
330 }
331
332 int numberOfVariables = potentialEnergy.getNumberOfVariables();
333 state = new SystemState(numberOfVariables);
334 state.setMass(potential.getMass());
335
336 CompositeConfiguration properties = molecularAssembly[0].getProperties();
337
338 if (requestedIntegrator == null) {
339 String integrate = properties.getString("integrate", "VERLET").trim();
340 try {
341 integrate = integrate.toUpperCase().replace("-", "_");
342 requestedIntegrator = IntegratorEnum.valueOf(integrate.toUpperCase());
343 } catch (Exception e) {
344 requestedIntegrator = IntegratorEnum.VERLET;
345 }
346 }
347
348 boolean oMMLogging = potential instanceof OpenMMEnergy;
349 List<Constraint> constraints = potentialEnergy.getConstraints();
350
351
352 integrator = switch (requestedIntegrator) {
353 case RESPA, MTS -> {
354 Respa respa = new Respa(state);
355 int in = molecularAssembly[0].getProperties().getInt("respa-dt", 4);
356 if (in < 2) {
357 in = 2;
358 }
359 if (!oMMLogging) {
360 respa.setInnerTimeSteps(in);
361 }
362 logger.log(Level.FINE, format(" Created a RESPA integrator with %d inner time steps.", in));
363 yield respa;
364 }
365 case STOCHASTIC, LANGEVIN -> {
366 double friction = properties.getDouble("friction", 91.0);
367 logger.log(Level.FINE, format(" Friction set at %.3f collisions/picosecond", friction));
368 Stochastic stochastic = new Stochastic(friction, state);
369 if (properties.containsKey("randomseed")) {
370 stochastic.setRandomSeed(properties.getInt("randomseed", 0));
371 }
372
373
374
375 requestedThermostat = ThermostatEnum.ADIABATIC;
376 yield stochastic;
377 }
378 case BEEMAN -> new BetterBeeman(state);
379 default -> new VelocityVerlet(state);
380 };
381
382 integrator.addConstraints(constraints);
383
384
385 if (requestedThermostat == null) {
386 String thermo = properties.getString("thermostat", "Berendsen").trim();
387 try {
388 thermo = thermo.toUpperCase().replace("-", "_");
389 requestedThermostat = ThermostatEnum.valueOf(thermo.toUpperCase());
390 } catch (Exception e) {
391 requestedThermostat = ThermostatEnum.BERENDSEN;
392 }
393 }
394
395
396 thermostat = switch (requestedThermostat) {
397 case BERENDSEN -> {
398 double tau = properties.getDouble("tau-temperature", 0.2);
399 yield new Berendsen(state, potentialEnergy.getVariableTypes(), targetTemperature, tau,
400 constraints);
401 }
402 case BUSSI -> {
403 double tau = properties.getDouble("tau-temperature", 0.2);
404 Bussi bussi = new Bussi(state, potentialEnergy.getVariableTypes(), targetTemperature, tau,
405 constraints);
406 if (properties.containsKey("randomseed")) {
407 bussi.setRandomSeed(properties.getInt("randomseed", 0));
408 }
409 yield bussi;
410 }
411 default -> new Adiabatic(state, potentialEnergy.getVariableTypes(), constraints);
412 };
413
414 if (properties.containsKey("randomseed")) {
415 thermostat.setRandomSeed(properties.getInt("randomseed", 0));
416 }
417
418
419 if (integrator instanceof Stochastic) {
420 boolean removecom = assembly.getForceField().getBoolean("removecom", false);
421 thermostat.setRemoveCenterOfMassMotion(removecom);
422 if (removecom) {
423 logger.info(" Removing center of mass motion from stochastic simulation.");
424 }
425 }
426
427 done = true;
428 fallbackDynFile = defaultFallbackDyn(assembly);
429 }
430
431
432
433
434
435
436
437
438
439
440
441
442
443 public static MolecularDynamics dynamicsFactory(MolecularAssembly assembly,
444 Potential potentialEnergy, AlgorithmListener listener, ThermostatEnum requestedThermostat,
445 IntegratorEnum requestedIntegrator) {
446
447 return dynamicsFactory(assembly, potentialEnergy, listener, requestedThermostat,
448 requestedIntegrator, defaultEngine(assembly, potentialEnergy));
449 }
450
451
452
453
454
455
456
457
458
459
460
461
462
463 public static MolecularDynamics dynamicsFactory(MolecularAssembly assembly,
464 Potential potentialEnergy, AlgorithmListener listener, ThermostatEnum requestedThermostat,
465 IntegratorEnum requestedIntegrator, MDEngine engine) {
466 Barostat barostat = null;
467 if (potentialEnergy instanceof Barostat) {
468 barostat = (Barostat) potentialEnergy;
469 potentialEnergy = barostat.getCrystalPotential();
470 }
471 switch (engine) {
472 case OPENMM, OMM -> {
473 if (potentialEnergy instanceof OpenMMEnergy ||
474 potentialEnergy instanceof OpenMMDualTopologyEnergy) {
475 MolecularDynamicsOpenMM molecularDynamicsOpenMM =
476 new MolecularDynamicsOpenMM(assembly, potentialEnergy, listener, requestedThermostat,
477 requestedIntegrator);
478 if (barostat != null) {
479 molecularDynamicsOpenMM.setBarostat(barostat);
480 }
481 return molecularDynamicsOpenMM;
482 } else {
483 logger.info(format(" Requested OpenMM molecular dynamics engine %s, but the potential does not use OpenMM: %s",
484 engine, potentialEnergy));
485 return new MolecularDynamics(assembly, potentialEnergy, listener, requestedThermostat, requestedIntegrator);
486 }
487 }
488 default -> {
489 return new MolecularDynamics(assembly, potentialEnergy, listener, requestedThermostat, requestedIntegrator);
490 }
491 }
492 }
493
494 private static MDEngine defaultEngine(MolecularAssembly molecularAssembly,
495 Potential potentialEnergy) {
496 CompositeConfiguration properties = molecularAssembly.getProperties();
497 String mdEngine = properties.getString("MD-engine");
498 if (mdEngine != null) {
499 if (mdEngine.equalsIgnoreCase("OMM")) {
500 logger.info(" Creating OpenMM Dynamics Object");
501 return MDEngine.OPENMM;
502 } else {
503 logger.info(" Creating FFX Dynamics Object");
504 return MDEngine.FFX;
505 }
506 } else {
507
508 boolean ommLeaves = potentialEnergy.getUnderlyingPotentials().stream()
509 .anyMatch((Potential p) -> p instanceof OpenMMEnergy);
510 ommLeaves = ommLeaves || potentialEnergy instanceof OpenMMEnergy;
511 if (ommLeaves) {
512 return MDEngine.OPENMM;
513 } else {
514 return MDEngine.FFX;
515 }
516 }
517 }
518
519
520
521
522
523
524
525 private static File defaultFallbackDyn(MolecularAssembly assembly) {
526 String firstFileName = removeExtension(assembly.getFile().getAbsolutePath());
527 return new File(firstFileName + ".dyn");
528 }
529
530
531
532
533
534
535
536 public void addAssembly(MolecularAssembly assembly) {
537 molecularAssembly = Arrays.copyOf(molecularAssembly, molecularAssembly.length + 1);
538 molecularAssembly[molecularAssembly.length - 1] = assembly;
539 }
540
541
542
543
544
545
546 public void attachExtendedSystem(ExtendedSystem system, double reportFreq) {
547 if (esvSystem != null) {
548 logger.warning("An ExtendedSystem is already attached to this MD!");
549 }
550 esvSystem = system;
551 SystemState esvState = esvSystem.getState();
552 this.esvIntegrator = new Stochastic(esvSystem.getThetaFriction(), esvState);
553 if (!esvSystem.getConstraints().isEmpty()) {
554 esvIntegrator.addConstraints(esvSystem.getConstraints());
555 }
556 this.esvThermostat = new Adiabatic(esvState, potential.getVariableTypes());
557 printEsvFrequency = intervalToFreq(reportFreq, "Reporting (logging) interval");
558 logger.info(
559 format(" Attached extended system (%s) to molecular dynamics.", esvSystem.toString()));
560 logger.info(format(" Extended System Theta Friction: %f", esvSystem.getThetaFriction()));
561 logger.info(format(" Extended System Theta Mass: %f", esvSystem.getThetaMass()));
562 logger.info(format(" Extended System Lambda Print Frequency: %d (fsec)", printEsvFrequency));
563 }
564
565
566
567
568
569
570
571
572 public void setNonEquilibriumLambda(boolean nonEquilibrium, int nEQSteps, boolean reverseNEQ) {
573 nonEquilibriumLambda = nonEquilibrium;
574 if (nonEquilibriumLambda) {
575 nonEquilibriumDynamics = new NonEquilbriumDynamics(nEQSteps, reverseNEQ);
576 } else {
577 nonEquilibriumDynamics = null;
578 }
579 }
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595 public void dynamic(final long nSteps, final double timeStep, final double loggingInterval,
596 final double trajectoryInterval, final double temperature, final boolean initVelocities,
597 String fileType, double restartInterval, final File dyn) {
598 this.fileType = fileType;
599 setRestartFrequency(restartInterval);
600 dynamic(nSteps, timeStep, loggingInterval, trajectoryInterval, temperature, initVelocities, dyn);
601 }
602
603
604
605
606
607
608
609
610
611
612
613
614
615 public void dynamic(final long nSteps, final double timeStep, final double loggingInterval,
616 final double trajectoryInterval, final double temperature, final boolean initVelocities,
617 final File dyn) {
618
619
620 if (!done) {
621 logger.warning(" Programming error - a thread invoked dynamic when it was already running.");
622 return;
623 }
624
625 init(nSteps, timeStep, loggingInterval, trajectoryInterval, fileType, restartInterval,
626 temperature, initVelocities, dyn);
627
628 Thread dynamicThread = new Thread(this);
629 dynamicThread.start();
630 synchronized (this) {
631 try {
632 while (dynamicThread.isAlive()) {
633 wait(0, DEFAULT_DYNAMICS_SLEEP_TIME);
634 }
635 } catch (InterruptedException e) {
636 String message = " Molecular dynamics interrupted.";
637 logger.log(Level.WARNING, message, e);
638 }
639 }
640 if (!verbosityLevel.isQuiet()) {
641 logger.info(" Done with an MD round.");
642 }
643 }
644
645
646
647
648
649
650 public MolecularAssembly[] getAssemblyArray() {
651 return molecularAssembly.clone();
652 }
653
654
655
656
657
658
659 public File getDynFile() {
660 return restartFile;
661 }
662
663
664
665
666
667
668 public double getInitialKineticEnergy() {
669 return initialState.kineticEnergy();
670 }
671
672
673
674
675
676
677 public double getInitialPotentialEnergy() {
678 return initialState.potentialEnergy();
679 }
680
681
682
683
684
685
686 public double getInitialTemperature() {
687 return initialState.temperature();
688 }
689
690
691
692
693
694
695 public double getInitialTotalEnergy() {
696 return initialState.getTotalEnergy();
697 }
698
699
700
701
702
703
704 public int getIntervalSteps() {
705 return 1;
706 }
707
708
709
710
711
712
713 public void setIntervalSteps(int intervalSteps) {
714
715 }
716
717
718
719
720
721
722 public double getKineticEnergy() {
723 return state.getKineticEnergy();
724 }
725
726
727
728
729
730
731 public double getPotentialEnergy() {
732 return state.getPotentialEnergy();
733 }
734
735
736
737
738
739
740 public double getTemperature() {
741 return state.getTemperature();
742 }
743
744
745
746
747
748
749 public Thermostat getThermostat() {
750 return thermostat;
751 }
752
753
754
755
756
757
758 public void setThermostat(Thermostat thermostat) {
759 this.thermostat = thermostat;
760 }
761
762
763
764
765
766
767 public double getTimeStep() {
768 return dt;
769 }
770
771
772
773
774
775
776 private void setTimeStep(double step) {
777 dt = step * FSEC_TO_PSEC;
778
779 setRestartFrequency(restartInterval);
780 setLoggingFrequency(logInterval);
781 setTrajectoryFrequency(trajectoryInterval);
782 }
783
784
785
786
787
788
789 public double getTotalEnergy() {
790 return state.getTotalEnergy();
791 }
792
793 public MDVerbosity getVerbosityLevel() {
794 return verbosityLevel;
795 }
796
797 public void setVerbosityLevel(MDVerbosity level) {
798 verbosityLevel = level;
799 if (level == MDVerbosity.SILENT) {
800 basicLogging = Level.FINE;
801 } else {
802 basicLogging = Level.INFO;
803 }
804 }
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820 public void init(final long nSteps, final double timeStep, final double loggingInterval,
821 final double trajectoryInterval, final String fileType, final double restartInterval,
822 final double temperature, final boolean initVelocities, final File dyn) {
823
824
825 if (!done) {
826 logger.warning(
827 " Programming error - attempt to modify parameters of a running MolecularDynamics instance.");
828 return;
829 }
830
831 if (integrator instanceof Stochastic) {
832 if (constantPressure) {
833 logger.log(basicLogging, "\n Stochastic dynamics in the NPT ensemble");
834 } else {
835 logger.log(basicLogging, "\n Stochastic dynamics in the NVT ensemble");
836 }
837 } else if (!(thermostat instanceof Adiabatic)) {
838 if (constantPressure) {
839 logger.log(basicLogging, "\n Molecular dynamics in the NPT ensemble");
840 } else {
841 logger.log(basicLogging, "\n Molecular dynamics in the NVT ensemble");
842 }
843 } else {
844 if (constantPressure) {
845 logger.severe("\n NPT Molecular dynamics requires a thermostat");
846 } else {
847 logger.log(basicLogging, "\n Molecular dynamics in the NVE ensemble");
848 }
849 }
850
851 this.nSteps = nSteps;
852 totalSimTime = 0.0;
853
854
855 setTimeStep(timeStep);
856
857
858 setLoggingFrequency(loggingInterval);
859 setTrajectoryFrequency(trajectoryInterval);
860 setRestartFrequency(restartInterval);
861
862 checkFrequency("Reporting ", logFrequency);
863 if (automaticWriteouts) {
864
865 checkFrequency("Trajectory Write-Out", trajectoryFrequency);
866 checkFrequency("Restart ", restartFrequency);
867 }
868
869
870 saveSnapshotAsPDB = true;
871 if (fileType.equalsIgnoreCase("XYZ") || fileType.equalsIgnoreCase("ARC")) {
872 saveSnapshotAsPDB = false;
873 } else if (!fileType.equalsIgnoreCase("PDB")) {
874 logger.warning("Snapshot file type unrecognized; saving snapshots as PDB.\n");
875 }
876
877 setArchiveFile();
878
879 this.restartFile = (dyn == null) ? fallbackDynFile : dyn;
880 loadRestart = restartFile.exists() && !initialized;
881
882 if (dynFilter == null) {
883 dynFilter = new DYNFilter(molecularAssembly[0].getName());
884 }
885
886 this.targetTemperature = temperature;
887 this.initVelocities = initVelocities;
888 done = false;
889
890 if (loadRestart) {
891 logger.info(" Continuing from " + restartFile.getAbsolutePath());
892 }
893
894 if (!verbosityLevel.isQuiet()) {
895 logger.info(format(" Integrator: %15s", integrator.toString()));
896 logger.info(format(" Thermostat: %15s", thermostat.toString()));
897 logger.info(format(" Number of steps: %8d", nSteps));
898 logger.info(format(" Time step: %8.3f (fsec)", timeStep));
899 logger.info(format(" Print interval: %8.3f (psec)", loggingInterval));
900 logger.info(format(" Save interval: %8.3f (psec)", trajectoryInterval));
901 if (molecularAssembly.length > 1) {
902 for (int i = 0; i < molecularAssembly.length; i++) {
903 File archiveFile = molecularAssembly[i].getArchiveFile();
904 logger.info(format(" Archive file %3d: %s", (i + 1), archiveFile.getAbsolutePath()));
905 }
906 } else {
907 logger.info(format(" Archive file: %s",
908 molecularAssembly[0].getArchiveFile().getAbsolutePath()));
909 }
910 logger.info(format(" Restart file: %s", restartFile.getAbsolutePath()));
911 }
912 }
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927 public void init(final long nSteps, final double timeStep, final double loggingInterval,
928 final double trajectoryInterval, final double temperature, final boolean initVelocities,
929 final File dyn) {
930 init(nSteps, timeStep, loggingInterval, trajectoryInterval, "XYZ", restartInterval, temperature,
931 initVelocities, dyn);
932 }
933
934
935
936
937 @Override
938 public void run() {
939 try {
940 preRunOps();
941 } catch (IllegalStateException ise) {
942 return;
943 }
944 initializeEnergies();
945 postInitEnergies();
946 mainLoop();
947 postRun();
948 }
949
950
951
952
953
954
955
956
957 public void setAutomaticWriteouts(boolean autoWriteout) {
958 this.automaticWriteouts = autoWriteout;
959 }
960
961
962
963
964
965
966 public void setFallbackDynFile(File fallback) {
967 this.fallbackDynFile = fallback;
968 }
969
970
971
972
973
974
975 public void setFileType(String fileType) {
976 this.fileType = fileType;
977 }
978
979
980
981
982
983
984
985 public void setObtainVelAcc(boolean obtainVA) {
986
987 }
988
989
990
991
992
993
994
995 public void setRestartFrequency(double restartInterval) throws IllegalArgumentException {
996 restartFrequency = intervalToFreq(restartInterval, "Restart interval");
997 this.restartInterval = restartInterval;
998 }
999
1000
1001
1002
1003
1004
1005 public void setArchiveFiles(File[] archiveFiles) {
1006 logger.info(" Setting archive files:\n " + Arrays.toString(archiveFiles));
1007 int n = molecularAssembly.length;
1008 assert archiveFiles.length == n;
1009 for (int i = 0; i < n; i++) {
1010 molecularAssembly[i].setArchiveFile(archiveFiles[i]);
1011 }
1012 }
1013
1014
1015
1016
1017 @Override
1018 public void terminate() {
1019 terminate = true;
1020 while (!done) {
1021 synchronized (this) {
1022 try {
1023 wait(1);
1024 } catch (Exception e) {
1025 logger.log(Level.WARNING, " Exception terminating dynamics.\n", e);
1026 }
1027 }
1028 }
1029 }
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040 public EnumSet<MDWriteAction> writeFilesForStep(long step, boolean trySnapshot,
1041 boolean tryRestart) {
1042 return writeFilesForStep(step, trySnapshot, tryRestart, null);
1043 }
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056 public EnumSet<MDWriteAction> writeFilesForStep(long step, boolean trySnapshot, boolean tryRestart,
1057 String[] extraLines) {
1058 List<String> linesList =
1059 (extraLines == null) ? new ArrayList<>() : new ArrayList<>(Arrays.asList(extraLines));
1060
1061 if (potential instanceof LambdaInterface) {
1062 String lamString = format("Lambda: %.8f", ((LambdaInterface) potential).getLambda());
1063 linesList.add(lamString);
1064 }
1065
1066 String tempString = format("Temp: %.2f", thermostat.getTargetTemperature());
1067 linesList.add(tempString);
1068
1069 String timeString = format(" Time: %7.3e", totalSimTime);
1070 linesList.add(timeString);
1071
1072 if (esvSystem != null) {
1073 String pHString = format("pH: %.2f", esvSystem.getConstantPh());
1074 linesList.add(pHString);
1075 }
1076
1077 Comm world = Comm.world();
1078 if (world != null && world.size() > 1) {
1079 String rankString = format("Rank: %d", world.rank());
1080 linesList.add(rankString);
1081 }
1082
1083 String[] allLines = new String[linesList.size()];
1084 allLines = linesList.toArray(allLines);
1085
1086 EnumSet<MDWriteAction> written = EnumSet.noneOf(MDWriteAction.class);
1087 if (step != 0) {
1088
1089 if (trySnapshot && trajectoryFrequency > 0 && step % trajectoryFrequency == 0) {
1090
1091
1092 potential.setCoordinates(state.x());
1093
1094
1095 if (totalEnergyStats.getCount() > 0) {
1096 logger.info(format("\n Average Values for the Last %d Out of %d Dynamics Steps\n",
1097 trajectoryFrequency, step));
1098 logger.info(format(" Simulation Time %16.4f Picosecond", step * dt));
1099 logger.info(format(" Total Energy %16.4f Kcal/mole (+/-%9.4f)", totalEnergyStats.getMean(),
1100 totalEnergyStats.getStandardDeviation()));
1101 logger.info(format(" Potential Energy %16.4f Kcal/mole (+/-%9.4f)",
1102 potentialEnergyStats.getMean(), potentialEnergyStats.getStandardDeviation()));
1103 logger.info(format(" Kinetic Energy %16.4f Kcal/mole (+/-%9.4f)", kineticEnergyStats.getMean(),
1104 kineticEnergyStats.getStandardDeviation()));
1105 logger.info(format(" Temperature %16.4f Kelvin (+/-%9.4f)\n", temperatureStats.getMean(),
1106 temperatureStats.getStandardDeviation()));
1107 totalEnergyStats.reset();
1108 potentialEnergyStats.reset();
1109 kineticEnergyStats.reset();
1110 temperatureStats.reset();
1111 }
1112
1113 if (esvSystem != null) {
1114 for (Atom atom : molecularAssembly[0].getAtomList()) {
1115 int atomIndex = atom.getIndex() - 1;
1116 atom.setOccupancy(esvSystem.getTitrationLambda(atomIndex));
1117 atom.setTempFactor(esvSystem.getTautomerLambda(atomIndex));
1118 }
1119 }
1120 appendSnapshot(allLines);
1121 written.add(MDWriteAction.SNAPSHOT);
1122 }
1123
1124
1125 if (tryRestart && restartFrequency > 0 && step % restartFrequency == 0) {
1126 writeRestart();
1127 written.add(MDWriteAction.RESTART);
1128 }
1129 }
1130 return written;
1131 }
1132
1133
1134
1135
1136 public void writeRestart() {
1137 String dynName = relativePathTo(restartFile).toString();
1138 double[] x = state.x();
1139 double[] v = state.v();
1140 double[] a = state.a();
1141 double[] aPrevious = state.aPrevious();
1142 if (dynFilter.writeDYN(restartFile, molecularAssembly[0].getCrystal(), x, v, a, aPrevious)) {
1143 logger.log(basicLogging, format(" Wrote dynamics restart to: %s.", dynName));
1144 } else {
1145 logger.log(basicLogging, format(" Writing dynamics restart failed: %s.", dynName));
1146 }
1147 if (esvSystem != null) {
1148 esvSystem.writeRestart();
1149 esvSystem.writeLambdaHistogram(false);
1150 }
1151 potential.writeAdditionalRestartInfo(true);
1152 }
1153
1154
1155
1156
1157 private void setArchiveFile() {
1158 for (MolecularAssembly assembly : molecularAssembly) {
1159 File file = assembly.getFile();
1160 String filename = removeExtension(file.getAbsolutePath());
1161 File archiveFile = assembly.getArchiveFile();
1162 if (archiveFile == null) {
1163 archiveFile = new File(filename + ".arc");
1164 assembly.setArchiveFile(XYZFilter.version(archiveFile));
1165 }
1166 }
1167 }
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177 private int intervalToFreq(double interval, String describe) throws IllegalArgumentException {
1178 if (!Double.isFinite(interval) || interval <= 0) {
1179 throw new IllegalArgumentException(
1180 format(" %s must be " + "positive finite value in ps, was %10.4g", describe, interval));
1181 }
1182 if (interval >= dt) {
1183 return (int) (interval / dt);
1184 } else {
1185 logger.warning(format(" Specified %s of %.6f ps < time step %.6f ps; "
1186 + "interval is set to once per time step!", describe, interval, dt));
1187 return 1;
1188 }
1189 }
1190
1191
1192
1193
1194
1195
1196 private void setLoggingFrequency(double logInterval) {
1197 logFrequency = intervalToFreq(logInterval, "Reporting (logging) interval");
1198 this.logInterval = logInterval;
1199 }
1200
1201
1202
1203
1204
1205
1206 private void setTrajectoryFrequency(double snapshotInterval) {
1207 trajectoryFrequency = intervalToFreq(snapshotInterval, "Trajectory writeout interval");
1208 this.trajectoryInterval = snapshotInterval;
1209 }
1210
1211
1212
1213
1214 void preRunOps() throws IllegalStateException {
1215 done = false;
1216 terminate = false;
1217
1218
1219 thermostat.setTargetTemperature(targetTemperature);
1220 boolean quiet = verbosityLevel.isQuiet();
1221 thermostat.setQuiet(quiet);
1222 if (integrator instanceof Stochastic stochastic) {
1223 stochastic.setTemperature(targetTemperature);
1224 }
1225
1226
1227 integrator.setTimeStep(dt);
1228
1229 if (!initialized) {
1230
1231 if (loadRestart) {
1232 Crystal crystal = molecularAssembly[0].getCrystal();
1233 double[] x = state.x();
1234 double[] v = state.v();
1235 double[] a = state.a();
1236 double[] aPrevious = state.aPrevious();
1237 if (!dynFilter.readDYN(restartFile, crystal, x, v, a, aPrevious)) {
1238 String message = " Could not load the restart file - dynamics terminated.";
1239 logger.log(Level.WARNING, message);
1240 done = true;
1241 throw new IllegalStateException(message);
1242 } else {
1243 molecularAssembly[0].setCrystal(crystal);
1244 potential.setCoordinates(x);
1245 potential.setVelocity(v);
1246 potential.setAcceleration(a);
1247 potential.setPreviousAcceleration(aPrevious);
1248 }
1249 } else {
1250
1251 potential.getCoordinates(state.x());
1252
1253 if (initVelocities) {
1254 thermostat.maxwell(targetTemperature);
1255 } else {
1256 fill(state.v(), 0.0);
1257 }
1258 }
1259 } else {
1260
1261
1262 if (initVelocities) {
1263 thermostat.maxwell(targetTemperature);
1264 }
1265 }
1266 }
1267
1268
1269
1270
1271 private void initializeEnergies() {
1272
1273 double[] x = state.x();
1274 double[] gradient = state.gradient();
1275
1276 boolean propagateLambda = true;
1277 if (potential instanceof OrthogonalSpaceTempering orthogonalSpaceTempering) {
1278 propagateLambda = orthogonalSpaceTempering.getPropagateLambda();
1279 orthogonalSpaceTempering.setPropagateLambda(false);
1280 }
1281
1282 if (esvSystem != null && potential instanceof OpenMMEnergy) {
1283 state.setPotentialEnergy(((OpenMMEnergy) potential).energyAndGradientFFX(x, gradient));
1284 } else {
1285 state.setPotentialEnergy(potential.energyAndGradient(x, gradient));
1286 }
1287
1288 if (potential instanceof OrthogonalSpaceTempering orthogonalSpaceTempering) {
1289 orthogonalSpaceTempering.setPropagateLambda(propagateLambda);
1290 }
1291
1292
1293 if (!loadRestart || initialized || integrator instanceof Respa) {
1294
1295 if (integrator instanceof Respa) {
1296 potential.setEnergyTermState(Potential.STATE.SLOW);
1297 potential.energyAndGradient(x, gradient);
1298 }
1299
1300 int numberOfVariables = state.getNumberOfVariables();
1301 double[] a = state.a();
1302 double[] mass = state.getMass();
1303 for (int i = 0; i < numberOfVariables; i++) {
1304 a[i] = -KCAL_TO_GRAM_ANG2_PER_PS2 * gradient[i] / mass[i];
1305 }
1306 state.copyAccelerationsToPrevious();
1307 }
1308
1309 if (esvSystem != null) {
1310 SystemState esvState = esvSystem.getState();
1311 double[] esvA = esvState.a();
1312 double[] esvMass = esvState.getMass();
1313 int nESVs = esvState.getNumberOfVariables();
1314 double[] gradESV = esvSystem.postForce();
1315 for (int i = 0; i < nESVs; i++) {
1316 esvA[i] = -KCAL_TO_GRAM_ANG2_PER_PS2 * gradESV[i] / esvMass[i];
1317 }
1318 }
1319
1320
1321 thermostat.computeKineticEnergy();
1322 if (esvSystem != null) {
1323 esvThermostat.computeKineticEnergy();
1324 double kineticEnergy = thermostat.getKineticEnergy();
1325 double esvKineticEnergy = esvThermostat.getKineticEnergy();
1326 state.setKineticEnergy(kineticEnergy + esvKineticEnergy);
1327 }
1328
1329
1330 initialState = new UnmodifiableState(state);
1331
1332
1333 temperatureStats.reset();
1334 potentialEnergyStats.reset();
1335 kineticEnergyStats.reset();
1336 totalEnergyStats.reset();
1337 }
1338
1339
1340
1341
1342 void postInitEnergies() {
1343 initialized = true;
1344
1345 logger.log(basicLogging,
1346 format("\n %8s %12s %12s %12s %8s %8s", "Time", "Kinetic", "Potential", "Total", "Temp", "CPU"));
1347 logger.log(basicLogging,
1348 format(" %8s %12s %12s %12s %8s %8s", "psec", "kcal/mol", "kcal/mol", "kcal/mol", "K", "sec"));
1349 logger.log(basicLogging, format(" %8s %12.4f %12.4f %12.4f %8.2f", "", state.getKineticEnergy(),
1350 state.getPotentialEnergy(), state.getTotalEnergy(), state.getTemperature()));
1351
1352
1353 storeState();
1354 }
1355
1356
1357
1358
1359 void postRun() {
1360
1361 if (integrator instanceof Respa) {
1362 potential.setEnergyTermState(Potential.STATE.BOTH);
1363 }
1364
1365
1366 if (!terminate) {
1367 logger.log(basicLogging, format(" Completed %8d time steps\n", nSteps));
1368 }
1369
1370
1371 done = true;
1372 terminate = false;
1373 }
1374
1375
1376
1377
1378
1379
1380 protected void appendSnapshot(String[] extraLines) {
1381 int numAssemblies = molecularAssembly.length;
1382 int currentAssembly = 0;
1383
1384 for (MolecularAssembly assembly : molecularAssembly) {
1385 File archiveFile = assembly.getArchiveFile();
1386 ForceField forceField = assembly.getForceField();
1387 CompositeConfiguration properties = assembly.getProperties();
1388
1389
1390 String name = assembly.getName();
1391 String[] tokens = name.split(" +");
1392 StringBuilder stringBuilder = new StringBuilder();
1393 int numTokens = tokens.length;
1394 for (int i = 0; i < numTokens; i++) {
1395 if (tokens[i].equalsIgnoreCase("Energy:") || tokens[i].equalsIgnoreCase("Density:")) {
1396
1397 i++;
1398 } else {
1399 stringBuilder.append(" ").append(tokens[i]);
1400 }
1401 }
1402 assembly.setName(stringBuilder.toString());
1403
1404
1405 if (archiveFile != null && !saveSnapshotAsPDB) {
1406 String aiName = relativePathTo(archiveFile).toString();
1407 if (esvSystem == null) {
1408 XYZFilter xyzFilter = new XYZFilter(archiveFile, assembly, forceField, properties);
1409 if (xyzFilter.writeFile(archiveFile, true, extraLines)) {
1410 logger.log(basicLogging, format(" Appended snapshot to: %s", aiName));
1411 } else {
1412 logger.warning(format(" Appending snapshot failed: %s", aiName));
1413 }
1414 } else {
1415 XPHFilter xphFilter = new XPHFilter(archiveFile, assembly, forceField, properties, esvSystem);
1416 if (xphFilter.writeFile(archiveFile, true, extraLines)) {
1417 logger.log(basicLogging, format(" Appended to XPH archive %s", aiName));
1418 } else {
1419 logger.warning(format(" Appending to XPH archive %s failed.", aiName));
1420 }
1421 }
1422 } else if (saveSnapshotAsPDB) {
1423 if (pdbFilter == null) {
1424 pdbFilter = new PDBFilter[numAssemblies];
1425 }
1426 if (pdbFilter[currentAssembly] == null) {
1427 File file = assembly.getFile();
1428 String extName = getExtension(file.getName());
1429 File pdbFile;
1430 if (extName.toLowerCase().startsWith("pdb")) {
1431
1432 pdbFile = TinkerUtils.version(file);
1433 } else {
1434 String filename = removeExtension(file.getAbsolutePath());
1435 pdbFile = new File(filename + ".pdb");
1436 }
1437 pdbFilter[currentAssembly] = new PDBFilter(pdbFile, assembly, forceField, properties);
1438 pdbFilter[currentAssembly].setModelNumbering(0);
1439 }
1440 File pdbFile = pdbFilter[currentAssembly].getFile();
1441 String aiName = relativePathTo(pdbFile).toString();
1442 if (pdbFilter[currentAssembly].writeFile(pdbFile, true, extraLines)) {
1443 logger.log(basicLogging, format(" Appended to PDB file %s", aiName));
1444 } else {
1445 logger.warning(format(" Appending to PDB file to %s failed.", aiName));
1446 }
1447 }
1448 currentAssembly++;
1449 }
1450 }
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460 protected long logThermoForTime(long step, long time) {
1461 if (step % logFrequency == 0) {
1462 return logThermodynamics(time);
1463 } else {
1464 return time;
1465 }
1466 }
1467
1468
1469
1470
1471
1472
1473
1474 private long logThermodynamics(long time) {
1475 time = System.nanoTime() - time;
1476 logger.log(basicLogging,
1477 format(" %7.3e %12.4f %12.4f %12.4f %8.2f %8.3f", totalSimTime, state.getKineticEnergy(),
1478 state.getPotentialEnergy(), state.getTotalEnergy(), state.getTemperature(),
1479 time * NS2SEC));
1480 return System.nanoTime();
1481 }
1482
1483
1484
1485
1486 private void mainLoop() {
1487
1488
1489 long time = System.nanoTime();
1490 int removeCOMMotionFrequency = molecularAssembly[0].getForceField().getInteger("removecomfrequency", 100);
1491 if (thermostat.getRemoveCenterOfMassMotion()) {
1492 logger.info(format(" COM will be removed every %3d step(s).", removeCOMMotionFrequency));
1493 }
1494
1495 if (nonEquilibriumLambda) {
1496
1497 nSteps = nonEquilibriumDynamics.setMDSteps(nSteps);
1498 LambdaInterface lambdaInterface = (LambdaInterface) potential;
1499 double lambda = nonEquilibriumDynamics.getInitialLambda();
1500 lambdaInterface.setLambda(lambda);
1501 }
1502
1503
1504 for (long step = 1; step <= nSteps; step++) {
1505
1506
1507 if (nonEquilibriumLambda && nonEquilibriumDynamics.isUpdateStep(step)) {
1508 Potential.STATE respaState = potential.getEnergyTermState();
1509 if (integrator instanceof Respa) {
1510 if (respaState != Potential.STATE.BOTH) {
1511 potential.setEnergyTermState(Potential.STATE.BOTH);
1512 }
1513 }
1514 LambdaInterface lambdaInterface = (LambdaInterface) potential;
1515 double currentLambda = lambdaInterface.getLambda();
1516 double currentEnergy = state.getPotentialEnergy();
1517
1518 double newLambda = nonEquilibriumDynamics.getNextLambda(step, currentLambda);
1519 lambdaInterface.setLambda(newLambda);
1520
1521 double newEnergy = potential.energy(state.x());
1522
1523 double dW = newEnergy - currentEnergy;
1524 nonEquilibriumDynamics.addWork(dW);
1525 logger.info(format(" Non-equilibrium L=%5.3f Work=%12.6f", newLambda, nonEquilibriumDynamics.getWork()));
1526
1527
1528 if (integrator instanceof Respa) {
1529 potential.setEnergyTermState(respaState);
1530 }
1531 }
1532
1533 if (step > 1) {
1534 List<Constraint> constraints = potential.getConstraints();
1535
1536 long constraintFails = constraints.stream()
1537 .filter((Constraint c) -> !c.constraintSatisfied(state.x(), state.v(), 1E-7, 1E-7)).count();
1538 if (constraintFails > 0) {
1539 logger.info(format(" %d constraint failures in step %d", constraintFails, step));
1540 }
1541 }
1542
1543
1544 thermostat.halfStep(dt);
1545
1546
1547 integrator.preForce(potential);
1548 if (esvSystem != null) {
1549 esvIntegrator.preForce(potential);
1550
1551 esvSystem.preForce();
1552 }
1553
1554
1555 if (esvSystem != null && potential instanceof OpenMMEnergy) {
1556 state.setPotentialEnergy(((OpenMMEnergy) potential).energyAndGradientFFX(state.x(), state.gradient()));
1557 } else {
1558 state.setPotentialEnergy(potential.energyAndGradient(state.x(), state.gradient()));
1559 }
1560
1561
1562 if (integrator instanceof Respa r) {
1563 double potentialEnergy = state.getPotentialEnergy();
1564 state.setPotentialEnergy(potentialEnergy + r.getHalfStepEnergy());
1565 }
1566
1567
1568 integrator.postForce(state.gradient());
1569 if (esvSystem != null) {
1570 double[] dEdL = esvSystem.postForce();
1571 esvIntegrator.postForce(dEdL);
1572 }
1573
1574
1575 thermostat.computeKineticEnergy();
1576
1577
1578 thermostat.fullStep(dt);
1579
1580
1581 thermostat.computeKineticEnergy();
1582 if (esvSystem != null) {
1583
1584 esvThermostat.computeKineticEnergy();
1585 }
1586
1587
1588 if (thermostat.getRemoveCenterOfMassMotion() && step % removeCOMMotionFrequency == 0) {
1589 thermostat.centerOfMassMotion(true, false);
1590 }
1591
1592
1593 if (esvSystem != null) {
1594 double kineticEnergy = thermostat.getKineticEnergy();
1595 double esvKineticEnergy = esvThermostat.getKineticEnergy();
1596 state.setKineticEnergy(kineticEnergy + esvKineticEnergy);
1597 }
1598
1599
1600 temperatureStats.addValue(state.getTemperature());
1601 potentialEnergyStats.addValue(state.getPotentialEnergy());
1602 kineticEnergyStats.addValue(state.getKineticEnergy());
1603 totalEnergyStats.addValue(state.getTotalEnergy());
1604
1605
1606 potential.setVelocity(state.v());
1607 potential.setAcceleration(state.a());
1608 potential.setPreviousAcceleration(state.aPrevious());
1609
1610
1611 totalSimTime += dt;
1612 time = logThermoForTime(step, time);
1613 if (step % printEsvFrequency == 0 && esvSystem != null) {
1614 logger.log(basicLogging, format(" %s", esvSystem.getLambdaList()));
1615 }
1616
1617 if (automaticWriteouts) {
1618 writeFilesForStep(step, true, true);
1619 }
1620
1621
1622 if (algorithmListener != null && step % logFrequency == 0) {
1623 for (MolecularAssembly assembly : molecularAssembly) {
1624 algorithmListener.algorithmUpdate(assembly);
1625 }
1626 }
1627
1628
1629 if (terminate) {
1630 logger.info(format("\n Terminating after %8d time steps\n", step));
1631 break;
1632 }
1633 }
1634 }
1635
1636
1637
1638
1639
1640
1641
1642
1643 private void checkFrequency(String describe, int frequency) {
1644 if (frequency > nSteps) {
1645 logger.fine(
1646 format(" Specified %s frequency of %d is greater than the number of steps %d", describe,
1647 frequency, nSteps));
1648 }
1649 }
1650
1651
1652
1653
1654
1655
1656 public void setCoordinates(double[] coords) {
1657 state.setCoordinates(coords);
1658 }
1659
1660
1661
1662
1663
1664
1665 public double[] getCoordinates() {
1666 return state.getCoordinatesCopy();
1667 }
1668
1669
1670
1671
1672 public void storeState() {
1673 storedState = state.getUnmodifiableState();
1674 }
1675
1676
1677
1678
1679
1680
1681 public void revertState() throws Exception {
1682 if (storedState == null) {
1683 throw new Exception();
1684 }
1685 revertState(storedState);
1686 }
1687
1688
1689
1690
1691
1692
1693 private void revertState(UnmodifiableState state) {
1694 this.state.revertState(state);
1695 potential.setVelocity(state.v());
1696 potential.setAcceleration(state.a());
1697 potential.setPreviousAcceleration(state.aPrevious());
1698
1699 int numberOfVariables = this.state.getNumberOfVariables();
1700 Atom[] atoms = molecularAssembly[0].getActiveAtomArray();
1701 if (atoms.length * 3 == numberOfVariables) {
1702 int index = 0;
1703 double[] x = state.x();
1704 double[] gradient = state.gradient();
1705 for (Atom atom : atoms) {
1706 atom.moveTo(x[index], x[index + 1], x[index + 2]);
1707 atom.setXYZGradient(gradient[index], gradient[index + 1], gradient[index + 2]);
1708 index += 3;
1709 }
1710 }
1711 }
1712
1713 }