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