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 switch (engine) {
467 case OPENMM, OMM -> {
468 Barostat barostat = null;
469 Potential openmmPotential = potentialEnergy;
470 if (potentialEnergy instanceof Barostat) {
471 barostat = (Barostat) potentialEnergy;
472 logger.info(" dynamicsFactory: Molecular dynamics has barostat:" + barostat);
473 openmmPotential = barostat.getCrystalPotential();
474 }
475 if (openmmPotential instanceof OpenMMEnergy ||
476 openmmPotential instanceof OpenMMDualTopologyEnergy) {
477 MolecularDynamicsOpenMM molecularDynamicsOpenMM =
478 new MolecularDynamicsOpenMM(assembly, openmmPotential, listener,
479 requestedThermostat, requestedIntegrator);
480 if (barostat != null) {
481 molecularDynamicsOpenMM.setBarostat(barostat);
482 }
483 return molecularDynamicsOpenMM;
484 } else {
485 logger.info(format(" Requested OpenMM molecular dynamics engine %s, but the potential does not use OpenMM: %s",
486 engine, potentialEnergy));
487 return new MolecularDynamics(assembly, potentialEnergy, listener, requestedThermostat, requestedIntegrator);
488 }
489 }
490 default -> {
491 return new MolecularDynamics(assembly, potentialEnergy, listener, requestedThermostat, requestedIntegrator);
492 }
493 }
494 }
495
496 private static MDEngine defaultEngine(MolecularAssembly molecularAssembly,
497 Potential potentialEnergy) {
498 CompositeConfiguration properties = molecularAssembly.getProperties();
499 String mdEngine = properties.getString("MD-engine");
500 if (mdEngine != null) {
501 if (mdEngine.equalsIgnoreCase("OMM")) {
502 logger.info(" Creating OpenMM Dynamics Object");
503 return MDEngine.OPENMM;
504 } else {
505 logger.info(" Creating FFX Dynamics Object");
506 return MDEngine.FFX;
507 }
508 } else {
509
510 boolean ommLeaves = potentialEnergy.getUnderlyingPotentials().stream()
511 .anyMatch((Potential p) -> p instanceof OpenMMEnergy);
512 ommLeaves = ommLeaves || potentialEnergy instanceof OpenMMEnergy;
513 if (ommLeaves) {
514 return MDEngine.OPENMM;
515 } else {
516 return MDEngine.FFX;
517 }
518 }
519 }
520
521
522
523
524
525
526
527 private static File defaultFallbackDyn(MolecularAssembly assembly) {
528 String firstFileName = removeExtension(assembly.getFile().getAbsolutePath());
529 return new File(firstFileName + ".dyn");
530 }
531
532
533
534
535
536
537
538 public void addAssembly(MolecularAssembly assembly) {
539 molecularAssembly = Arrays.copyOf(molecularAssembly, molecularAssembly.length + 1);
540 molecularAssembly[molecularAssembly.length - 1] = assembly;
541 }
542
543
544
545
546
547
548 public void attachExtendedSystem(ExtendedSystem system, double reportFreq) {
549 if (esvSystem != null) {
550 logger.warning("An ExtendedSystem is already attached to this MD!");
551 }
552 esvSystem = system;
553 SystemState esvState = esvSystem.getState();
554 this.esvIntegrator = new Stochastic(esvSystem.getThetaFriction(), esvState);
555 if (!esvSystem.getConstraints().isEmpty()) {
556 esvIntegrator.addConstraints(esvSystem.getConstraints());
557 }
558 this.esvThermostat = new Adiabatic(esvState, potential.getVariableTypes());
559 printEsvFrequency = intervalToFreq(reportFreq, "Reporting (logging) interval");
560 logger.info(
561 format(" Attached extended system (%s) to molecular dynamics.", esvSystem.toString()));
562 logger.info(format(" Extended System Theta Friction: %f", esvSystem.getThetaFriction()));
563 logger.info(format(" Extended System Theta Mass: %f", esvSystem.getThetaMass()));
564 logger.info(format(" Extended System Lambda Print Frequency: %d (fsec)", printEsvFrequency));
565 }
566
567
568
569
570
571
572
573
574 public void setNonEquilibriumLambda(boolean nonEquilibrium, int nEQSteps, boolean reverseNEQ) {
575 nonEquilibriumLambda = nonEquilibrium;
576 if (nonEquilibriumLambda) {
577 nonEquilibriumDynamics = new NonEquilbriumDynamics(nEQSteps, reverseNEQ);
578 } else {
579 nonEquilibriumDynamics = null;
580 }
581 }
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597 public void dynamic(final long nSteps, final double timeStep, final double loggingInterval,
598 final double trajectoryInterval, final double temperature, final boolean initVelocities,
599 String fileType, double restartInterval, final File dyn) {
600 this.fileType = fileType;
601 setRestartFrequency(restartInterval);
602 dynamic(nSteps, timeStep, loggingInterval, trajectoryInterval, temperature, initVelocities, dyn);
603 }
604
605
606
607
608
609
610
611
612
613
614
615
616
617 public void dynamic(final long nSteps, final double timeStep, final double loggingInterval,
618 final double trajectoryInterval, final double temperature, final boolean initVelocities,
619 final File dyn) {
620
621
622 if (!done) {
623 logger.warning(" Programming error - a thread invoked dynamic when it was already running.");
624 return;
625 }
626
627 init(nSteps, timeStep, loggingInterval, trajectoryInterval, fileType, restartInterval,
628 temperature, initVelocities, dyn);
629
630 Thread dynamicThread = new Thread(this);
631 dynamicThread.start();
632 synchronized (this) {
633 try {
634 while (dynamicThread.isAlive()) {
635 wait(0, DEFAULT_DYNAMICS_SLEEP_TIME);
636 }
637 } catch (InterruptedException e) {
638 String message = " Molecular dynamics interrupted.";
639 logger.log(Level.WARNING, message, e);
640 }
641 }
642 if (!verbosityLevel.isQuiet()) {
643 logger.info(" Done with an MD round.");
644 }
645 }
646
647
648
649
650
651
652 public MolecularAssembly[] getAssemblyArray() {
653 return molecularAssembly.clone();
654 }
655
656
657
658
659
660
661 public File getDynFile() {
662 return restartFile;
663 }
664
665
666
667
668
669
670 public double getInitialKineticEnergy() {
671 return initialState.kineticEnergy();
672 }
673
674
675
676
677
678
679 public double getInitialPotentialEnergy() {
680 return initialState.potentialEnergy();
681 }
682
683
684
685
686
687
688 public double getInitialTemperature() {
689 return initialState.temperature();
690 }
691
692
693
694
695
696
697 public double getInitialTotalEnergy() {
698 return initialState.getTotalEnergy();
699 }
700
701
702
703
704
705
706 public int getIntervalSteps() {
707 return 1;
708 }
709
710
711
712
713
714
715 public void setIntervalSteps(int intervalSteps) {
716
717 }
718
719
720
721
722
723
724 public double getKineticEnergy() {
725 return state.getKineticEnergy();
726 }
727
728
729
730
731
732
733 public double getPotentialEnergy() {
734 return state.getPotentialEnergy();
735 }
736
737
738
739
740
741
742 public double getTemperature() {
743 return state.getTemperature();
744 }
745
746
747
748
749
750
751 public Thermostat getThermostat() {
752 return thermostat;
753 }
754
755
756
757
758
759
760 public void setThermostat(Thermostat thermostat) {
761 this.thermostat = thermostat;
762 }
763
764
765
766
767
768
769 public double getTimeStep() {
770 return dt;
771 }
772
773
774
775
776
777
778 private void setTimeStep(double step) {
779 dt = step * FSEC_TO_PSEC;
780
781 setRestartFrequency(restartInterval);
782 setLoggingFrequency(logInterval);
783 setTrajectoryFrequency(trajectoryInterval);
784 }
785
786
787
788
789
790
791 public double getTotalEnergy() {
792 return state.getTotalEnergy();
793 }
794
795 public MDVerbosity getVerbosityLevel() {
796 return verbosityLevel;
797 }
798
799 public void setVerbosityLevel(MDVerbosity level) {
800 verbosityLevel = level;
801 if (level == MDVerbosity.SILENT) {
802 basicLogging = Level.FINE;
803 } else {
804 basicLogging = Level.INFO;
805 }
806 }
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822 public void init(final long nSteps, final double timeStep, final double loggingInterval,
823 final double trajectoryInterval, final String fileType, final double restartInterval,
824 final double temperature, final boolean initVelocities, final File dyn) {
825
826
827 if (!done) {
828 logger.warning(
829 " Programming error - attempt to modify parameters of a running MolecularDynamics instance.");
830 return;
831 }
832
833 if (integrator instanceof Stochastic) {
834 if (constantPressure) {
835 logger.log(basicLogging, "\n Stochastic dynamics in the NPT ensemble");
836 } else {
837 logger.log(basicLogging, "\n Stochastic dynamics in the NVT ensemble");
838 }
839 } else if (!(thermostat instanceof Adiabatic)) {
840 if (constantPressure) {
841 logger.log(basicLogging, "\n Molecular dynamics in the NPT ensemble");
842 } else {
843 logger.log(basicLogging, "\n Molecular dynamics in the NVT ensemble");
844 }
845 } else {
846 if (constantPressure) {
847 logger.severe("\n NPT Molecular dynamics requires a thermostat");
848 } else {
849 logger.log(basicLogging, "\n Molecular dynamics in the NVE ensemble");
850 }
851 }
852
853 this.nSteps = nSteps;
854 totalSimTime = 0.0;
855
856
857 setTimeStep(timeStep);
858
859
860 setLoggingFrequency(loggingInterval);
861 setTrajectoryFrequency(trajectoryInterval);
862 setRestartFrequency(restartInterval);
863
864 checkFrequency("Reporting ", logFrequency);
865 if (automaticWriteouts) {
866
867 checkFrequency("Trajectory Write-Out", trajectoryFrequency);
868 checkFrequency("Restart ", restartFrequency);
869 }
870
871
872 saveSnapshotAsPDB = true;
873 if (fileType.equalsIgnoreCase("XYZ") || fileType.equalsIgnoreCase("ARC")) {
874 saveSnapshotAsPDB = false;
875 } else if (!fileType.equalsIgnoreCase("PDB")) {
876 logger.warning("Snapshot file type unrecognized; saving snapshots as PDB.\n");
877 }
878
879 setArchiveFile();
880
881 this.restartFile = (dyn == null) ? fallbackDynFile : dyn;
882 loadRestart = restartFile.exists() && !initialized;
883
884 if (dynFilter == null) {
885 dynFilter = new DYNFilter(molecularAssembly[0].getName());
886 }
887
888 this.targetTemperature = temperature;
889 this.initVelocities = initVelocities;
890 done = false;
891
892 if (loadRestart) {
893 logger.info(" Continuing from " + restartFile.getAbsolutePath());
894 }
895
896 if (!verbosityLevel.isQuiet()) {
897 logger.info(format(" Integrator: %15s", integrator.toString()));
898 logger.info(format(" Thermostat: %15s", thermostat.toString()));
899 logger.info(format(" Number of steps: %8d", nSteps));
900 logger.info(format(" Time step: %8.3f (fsec)", timeStep));
901 logger.info(format(" Print interval: %8.3f (psec)", loggingInterval));
902 logger.info(format(" Save interval: %8.3f (psec)", trajectoryInterval));
903 if (molecularAssembly.length > 1) {
904 for (int i = 0; i < molecularAssembly.length; i++) {
905 File archiveFile = molecularAssembly[i].getArchiveFile();
906 logger.info(format(" Archive file %3d: %s", (i + 1), archiveFile.getAbsolutePath()));
907 }
908 } else {
909 logger.info(format(" Archive file: %s",
910 molecularAssembly[0].getArchiveFile().getAbsolutePath()));
911 }
912 logger.info(format(" Restart file: %s", restartFile.getAbsolutePath()));
913 }
914 }
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929 public void init(final long nSteps, final double timeStep, final double loggingInterval,
930 final double trajectoryInterval, final double temperature, final boolean initVelocities,
931 final File dyn) {
932 init(nSteps, timeStep, loggingInterval, trajectoryInterval, "XYZ", restartInterval, temperature,
933 initVelocities, dyn);
934 }
935
936
937
938
939 @Override
940 public void run() {
941 try {
942 preRunOps();
943 } catch (IllegalStateException ise) {
944 return;
945 }
946 initializeEnergies();
947 postInitEnergies();
948 mainLoop();
949 postRun();
950 }
951
952
953
954
955
956
957
958
959 public void setAutomaticWriteouts(boolean autoWriteout) {
960 this.automaticWriteouts = autoWriteout;
961 }
962
963
964
965
966
967
968 public void setFallbackDynFile(File fallback) {
969 this.fallbackDynFile = fallback;
970 }
971
972
973
974
975
976
977 public void setFileType(String fileType) {
978 this.fileType = fileType;
979 }
980
981
982
983
984
985
986
987 public void setObtainVelAcc(boolean obtainVA) {
988
989 }
990
991
992
993
994
995
996
997 public void setRestartFrequency(double restartInterval) throws IllegalArgumentException {
998 restartFrequency = intervalToFreq(restartInterval, "Restart interval");
999 this.restartInterval = restartInterval;
1000 }
1001
1002
1003
1004
1005
1006
1007 public void setArchiveFiles(File[] archiveFiles) {
1008 logger.info(" Setting archive files:\n " + Arrays.toString(archiveFiles));
1009 int n = molecularAssembly.length;
1010 assert archiveFiles.length == n;
1011 for (int i = 0; i < n; i++) {
1012 molecularAssembly[i].setArchiveFile(archiveFiles[i]);
1013 }
1014 }
1015
1016
1017
1018
1019 @Override
1020 public void terminate() {
1021 terminate = true;
1022 while (!done) {
1023 synchronized (this) {
1024 try {
1025 wait(1);
1026 } catch (Exception e) {
1027 logger.log(Level.WARNING, " Exception terminating dynamics.\n", e);
1028 }
1029 }
1030 }
1031 }
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042 public EnumSet<MDWriteAction> writeFilesForStep(long step, boolean trySnapshot,
1043 boolean tryRestart) {
1044 return writeFilesForStep(step, trySnapshot, tryRestart, null);
1045 }
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058 public EnumSet<MDWriteAction> writeFilesForStep(long step, boolean trySnapshot, boolean tryRestart,
1059 String[] extraLines) {
1060 List<String> linesList =
1061 (extraLines == null) ? new ArrayList<>() : new ArrayList<>(Arrays.asList(extraLines));
1062
1063 if (potential instanceof LambdaInterface) {
1064 String lamString = format("Lambda: %.8f", ((LambdaInterface) potential).getLambda());
1065 linesList.add(lamString);
1066 }
1067
1068 String tempString = format("Temp: %.2f", thermostat.getTargetTemperature());
1069 linesList.add(tempString);
1070
1071 String timeString = format(" Time: %7.3e", totalSimTime);
1072 linesList.add(timeString);
1073
1074 if (esvSystem != null) {
1075 String pHString = format("pH: %.2f", esvSystem.getConstantPh());
1076 linesList.add(pHString);
1077 }
1078
1079 Comm world = Comm.world();
1080 if (world != null && world.size() > 1) {
1081 String rankString = format("Rank: %d", world.rank());
1082 linesList.add(rankString);
1083 }
1084
1085 String[] allLines = new String[linesList.size()];
1086 allLines = linesList.toArray(allLines);
1087
1088 EnumSet<MDWriteAction> written = EnumSet.noneOf(MDWriteAction.class);
1089 if (step != 0) {
1090
1091 if (trySnapshot && trajectoryFrequency > 0 && step % trajectoryFrequency == 0) {
1092
1093
1094 potential.setCoordinates(state.x());
1095
1096
1097 if (totalEnergyStats.getCount() > 0) {
1098 logger.info(format("\n Average Values for the Last %d Out of %d Dynamics Steps\n",
1099 trajectoryFrequency, step));
1100 logger.info(format(" Simulation Time %16.4f Picosecond", step * dt));
1101 logger.info(format(" Total Energy %16.4f Kcal/mole (+/-%9.4f)", totalEnergyStats.getMean(),
1102 totalEnergyStats.getStandardDeviation()));
1103 logger.info(format(" Potential Energy %16.4f Kcal/mole (+/-%9.4f)",
1104 potentialEnergyStats.getMean(), potentialEnergyStats.getStandardDeviation()));
1105 logger.info(format(" Kinetic Energy %16.4f Kcal/mole (+/-%9.4f)", kineticEnergyStats.getMean(),
1106 kineticEnergyStats.getStandardDeviation()));
1107 logger.info(format(" Temperature %16.4f Kelvin (+/-%9.4f)\n", temperatureStats.getMean(),
1108 temperatureStats.getStandardDeviation()));
1109 totalEnergyStats.reset();
1110 potentialEnergyStats.reset();
1111 kineticEnergyStats.reset();
1112 temperatureStats.reset();
1113 }
1114
1115 if (esvSystem != null) {
1116 for (Atom atom : molecularAssembly[0].getAtomList()) {
1117 int atomIndex = atom.getIndex() - 1;
1118 atom.setOccupancy(esvSystem.getTitrationLambda(atomIndex));
1119 atom.setTempFactor(esvSystem.getTautomerLambda(atomIndex));
1120 }
1121 }
1122 appendSnapshot(allLines);
1123 written.add(MDWriteAction.SNAPSHOT);
1124 }
1125
1126
1127 if (tryRestart && restartFrequency > 0 && step % restartFrequency == 0) {
1128 writeRestart();
1129 written.add(MDWriteAction.RESTART);
1130 }
1131 }
1132 return written;
1133 }
1134
1135
1136
1137
1138 public void writeRestart() {
1139 String dynName = relativePathTo(restartFile).toString();
1140 double[] x = state.x();
1141 double[] v = state.v();
1142 double[] a = state.a();
1143 double[] aPrevious = state.aPrevious();
1144 if (dynFilter.writeDYN(restartFile, molecularAssembly[0].getCrystal(), x, v, a, aPrevious)) {
1145 logger.log(basicLogging, format(" Wrote dynamics restart to: %s.", dynName));
1146 } else {
1147 logger.log(basicLogging, format(" Writing dynamics restart failed: %s.", dynName));
1148 }
1149 if (esvSystem != null) {
1150 esvSystem.writeRestart();
1151 esvSystem.writeLambdaHistogram(false);
1152 }
1153 potential.writeAdditionalRestartInfo(true);
1154 }
1155
1156
1157
1158
1159 private void setArchiveFile() {
1160 for (MolecularAssembly assembly : molecularAssembly) {
1161 File file = assembly.getFile();
1162 String filename = removeExtension(file.getAbsolutePath());
1163 File archiveFile = assembly.getArchiveFile();
1164 if (archiveFile == null) {
1165 archiveFile = new File(filename + ".arc");
1166 assembly.setArchiveFile(XYZFilter.version(archiveFile));
1167 }
1168 }
1169 }
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179 private int intervalToFreq(double interval, String describe) throws IllegalArgumentException {
1180 if (!Double.isFinite(interval) || interval <= 0) {
1181 throw new IllegalArgumentException(
1182 format(" %s must be " + "positive finite value in ps, was %10.4g", describe, interval));
1183 }
1184 if (interval >= dt) {
1185 return (int) (interval / dt);
1186 } else {
1187 logger.warning(format(" Specified %s of %.6f ps < time step %.6f ps; "
1188 + "interval is set to once per time step!", describe, interval, dt));
1189 return 1;
1190 }
1191 }
1192
1193
1194
1195
1196
1197
1198 private void setLoggingFrequency(double logInterval) {
1199 logFrequency = intervalToFreq(logInterval, "Reporting (logging) interval");
1200 this.logInterval = logInterval;
1201 }
1202
1203
1204
1205
1206
1207
1208 private void setTrajectoryFrequency(double snapshotInterval) {
1209 trajectoryFrequency = intervalToFreq(snapshotInterval, "Trajectory writeout interval");
1210 this.trajectoryInterval = snapshotInterval;
1211 }
1212
1213
1214
1215
1216 void preRunOps() throws IllegalStateException {
1217 done = false;
1218 terminate = false;
1219
1220
1221 thermostat.setTargetTemperature(targetTemperature);
1222 boolean quiet = verbosityLevel.isQuiet();
1223 thermostat.setQuiet(quiet);
1224 if (integrator instanceof Stochastic stochastic) {
1225 stochastic.setTemperature(targetTemperature);
1226 }
1227
1228
1229 integrator.setTimeStep(dt);
1230
1231 if (!initialized) {
1232
1233 if (loadRestart) {
1234 Crystal crystal = molecularAssembly[0].getCrystal();
1235 double[] x = state.x();
1236 double[] v = state.v();
1237 double[] a = state.a();
1238 double[] aPrevious = state.aPrevious();
1239 if (!dynFilter.readDYN(restartFile, crystal, x, v, a, aPrevious)) {
1240 String message = " Could not load the restart file - dynamics terminated.";
1241 logger.log(Level.WARNING, message);
1242 done = true;
1243 throw new IllegalStateException(message);
1244 } else {
1245 molecularAssembly[0].setCrystal(crystal);
1246 potential.setCoordinates(x);
1247 potential.setVelocity(v);
1248 potential.setAcceleration(a);
1249 potential.setPreviousAcceleration(aPrevious);
1250 }
1251 } else {
1252
1253 potential.getCoordinates(state.x());
1254
1255 if (initVelocities) {
1256 thermostat.maxwell(targetTemperature);
1257 } else {
1258 fill(state.v(), 0.0);
1259 }
1260 }
1261 } else {
1262
1263
1264 if (initVelocities) {
1265 thermostat.maxwell(targetTemperature);
1266 }
1267 }
1268 }
1269
1270
1271
1272
1273 private void initializeEnergies() {
1274
1275 double[] x = state.x();
1276 double[] gradient = state.gradient();
1277
1278 boolean propagateLambda = true;
1279 if (potential instanceof OrthogonalSpaceTempering orthogonalSpaceTempering) {
1280 propagateLambda = orthogonalSpaceTempering.getPropagateLambda();
1281 orthogonalSpaceTempering.setPropagateLambda(false);
1282 }
1283
1284 if (esvSystem != null && potential instanceof OpenMMEnergy) {
1285 state.setPotentialEnergy(((OpenMMEnergy) potential).energyAndGradientFFX(x, gradient));
1286 } else {
1287 state.setPotentialEnergy(potential.energyAndGradient(x, gradient));
1288 }
1289
1290 if (potential instanceof OrthogonalSpaceTempering orthogonalSpaceTempering) {
1291 orthogonalSpaceTempering.setPropagateLambda(propagateLambda);
1292 }
1293
1294
1295 if (!loadRestart || initialized || integrator instanceof Respa) {
1296
1297 if (integrator instanceof Respa) {
1298 potential.setEnergyTermState(Potential.STATE.SLOW);
1299 potential.energyAndGradient(x, gradient);
1300 }
1301
1302 int numberOfVariables = state.getNumberOfVariables();
1303 double[] a = state.a();
1304 double[] mass = state.getMass();
1305 for (int i = 0; i < numberOfVariables; i++) {
1306 a[i] = -KCAL_TO_GRAM_ANG2_PER_PS2 * gradient[i] / mass[i];
1307 }
1308 state.copyAccelerationsToPrevious();
1309 }
1310
1311 if (esvSystem != null) {
1312 SystemState esvState = esvSystem.getState();
1313 double[] esvA = esvState.a();
1314 double[] esvMass = esvState.getMass();
1315 int nESVs = esvState.getNumberOfVariables();
1316 double[] gradESV = esvSystem.postForce();
1317 for (int i = 0; i < nESVs; i++) {
1318 esvA[i] = -KCAL_TO_GRAM_ANG2_PER_PS2 * gradESV[i] / esvMass[i];
1319 }
1320 }
1321
1322
1323 thermostat.computeKineticEnergy();
1324 if (esvSystem != null) {
1325 esvThermostat.computeKineticEnergy();
1326 double kineticEnergy = thermostat.getKineticEnergy();
1327 double esvKineticEnergy = esvThermostat.getKineticEnergy();
1328 state.setKineticEnergy(kineticEnergy + esvKineticEnergy);
1329 }
1330
1331
1332 initialState = new UnmodifiableState(state);
1333
1334
1335 temperatureStats.reset();
1336 potentialEnergyStats.reset();
1337 kineticEnergyStats.reset();
1338 totalEnergyStats.reset();
1339 }
1340
1341
1342
1343
1344 void postInitEnergies() {
1345 initialized = true;
1346
1347 logger.log(basicLogging,
1348 format("\n %8s %12s %12s %12s %8s %8s", "Time", "Kinetic", "Potential", "Total", "Temp", "CPU"));
1349 logger.log(basicLogging,
1350 format(" %8s %12s %12s %12s %8s %8s", "psec", "kcal/mol", "kcal/mol", "kcal/mol", "K", "sec"));
1351 logger.log(basicLogging, format(" %8s %12.4f %12.4f %12.4f %8.2f", "", state.getKineticEnergy(),
1352 state.getPotentialEnergy(), state.getTotalEnergy(), state.getTemperature()));
1353
1354
1355 storeState();
1356 }
1357
1358
1359
1360
1361 void postRun() {
1362
1363 if (integrator instanceof Respa) {
1364 potential.setEnergyTermState(Potential.STATE.BOTH);
1365 }
1366
1367
1368 if (!terminate) {
1369 logger.log(basicLogging, format(" Completed %8d time steps\n", nSteps));
1370 }
1371
1372
1373 done = true;
1374 terminate = false;
1375 }
1376
1377
1378
1379
1380
1381
1382 protected void appendSnapshot(String[] extraLines) {
1383 int numAssemblies = molecularAssembly.length;
1384 int currentAssembly = 0;
1385
1386 for (MolecularAssembly assembly : molecularAssembly) {
1387 File archiveFile = assembly.getArchiveFile();
1388 ForceField forceField = assembly.getForceField();
1389 CompositeConfiguration properties = assembly.getProperties();
1390
1391
1392 String name = assembly.getName();
1393 String[] tokens = name.split(" +");
1394 StringBuilder stringBuilder = new StringBuilder();
1395 int numTokens = tokens.length;
1396 for (int i = 0; i < numTokens; i++) {
1397 if (tokens[i].equalsIgnoreCase("Energy:") || tokens[i].equalsIgnoreCase("Density:")) {
1398
1399 i++;
1400 } else {
1401 stringBuilder.append(" ").append(tokens[i]);
1402 }
1403 }
1404 assembly.setName(stringBuilder.toString());
1405
1406
1407 if (archiveFile != null && !saveSnapshotAsPDB) {
1408 String aiName = relativePathTo(archiveFile).toString();
1409 if (esvSystem == null) {
1410 XYZFilter xyzFilter = new XYZFilter(archiveFile, assembly, forceField, properties);
1411 if (xyzFilter.writeFile(archiveFile, true, extraLines)) {
1412 logger.log(basicLogging, format(" Appended snapshot to: %s", aiName));
1413 } else {
1414 logger.warning(format(" Appending snapshot failed: %s", aiName));
1415 }
1416 } else {
1417 XPHFilter xphFilter = new XPHFilter(archiveFile, assembly, forceField, properties, esvSystem);
1418 if (xphFilter.writeFile(archiveFile, true, extraLines)) {
1419 logger.log(basicLogging, format(" Appended to XPH archive %s", aiName));
1420 } else {
1421 logger.warning(format(" Appending to XPH archive %s failed.", aiName));
1422 }
1423 }
1424 } else if (saveSnapshotAsPDB) {
1425 if (pdbFilter == null) {
1426 pdbFilter = new PDBFilter[numAssemblies];
1427 }
1428 if (pdbFilter[currentAssembly] == null) {
1429 File file = assembly.getFile();
1430 String extName = getExtension(file.getName());
1431 File pdbFile;
1432 if (extName.toLowerCase().startsWith("pdb")) {
1433
1434 pdbFile = TinkerUtils.version(file);
1435 } else {
1436 String filename = removeExtension(file.getAbsolutePath());
1437 pdbFile = new File(filename + ".pdb");
1438 }
1439 pdbFilter[currentAssembly] = new PDBFilter(pdbFile, assembly, forceField, properties);
1440 pdbFilter[currentAssembly].setModelNumbering(0);
1441 }
1442 File pdbFile = pdbFilter[currentAssembly].getFile();
1443 String aiName = relativePathTo(pdbFile).toString();
1444 if (pdbFilter[currentAssembly].writeFile(pdbFile, true, extraLines)) {
1445 logger.log(basicLogging, format(" Appended to PDB file %s", aiName));
1446 } else {
1447 logger.warning(format(" Appending to PDB file to %s failed.", aiName));
1448 }
1449 }
1450 currentAssembly++;
1451 }
1452 }
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462 protected long logThermoForTime(long step, long time) {
1463 if (step % logFrequency == 0) {
1464 return logThermodynamics(time);
1465 } else {
1466 return time;
1467 }
1468 }
1469
1470
1471
1472
1473
1474
1475
1476 private long logThermodynamics(long time) {
1477 time = System.nanoTime() - time;
1478 logger.log(basicLogging,
1479 format(" %7.3e %12.4f %12.4f %12.4f %8.2f %8.3f", totalSimTime, state.getKineticEnergy(),
1480 state.getPotentialEnergy(), state.getTotalEnergy(), state.getTemperature(),
1481 time * NS2SEC));
1482 return System.nanoTime();
1483 }
1484
1485
1486
1487
1488 private void mainLoop() {
1489
1490
1491 long time = System.nanoTime();
1492 int removeCOMMotionFrequency = molecularAssembly[0].getForceField().getInteger("REMOVE-COM-FREQUENCY", 100);
1493 if (thermostat.getRemoveCenterOfMassMotion()) {
1494 logger.info(format(" COM will be removed every %3d step(s).", removeCOMMotionFrequency));
1495 }
1496
1497 if (nonEquilibriumLambda) {
1498
1499 nSteps = nonEquilibriumDynamics.setMDSteps(nSteps);
1500 LambdaInterface lambdaInterface = (LambdaInterface) potential;
1501 double lambda = nonEquilibriumDynamics.getInitialLambda();
1502 lambdaInterface.setLambda(lambda);
1503 }
1504
1505
1506 for (long step = 1; step <= nSteps; step++) {
1507
1508
1509 if (nonEquilibriumLambda && nonEquilibriumDynamics.isUpdateStep(step)) {
1510 Potential.STATE respaState = potential.getEnergyTermState();
1511 if (integrator instanceof Respa) {
1512 if (respaState != Potential.STATE.BOTH) {
1513 potential.setEnergyTermState(Potential.STATE.BOTH);
1514 }
1515 }
1516 LambdaInterface lambdaInterface = (LambdaInterface) potential;
1517 double currentLambda = lambdaInterface.getLambda();
1518 double currentEnergy = state.getPotentialEnergy();
1519
1520 double newLambda = nonEquilibriumDynamics.getNextLambda(step, currentLambda);
1521 lambdaInterface.setLambda(newLambda);
1522
1523 double newEnergy = potential.energy(state.x());
1524
1525 double dW = newEnergy - currentEnergy;
1526 nonEquilibriumDynamics.addWork(dW);
1527 logger.info(format(" Non-equilibrium L=%5.3f Work=%12.6f", newLambda, nonEquilibriumDynamics.getWork()));
1528
1529
1530 if (integrator instanceof Respa) {
1531 potential.setEnergyTermState(respaState);
1532 }
1533 }
1534
1535 if (step > 1) {
1536 List<Constraint> constraints = potential.getConstraints();
1537
1538 long constraintFails = constraints.stream()
1539 .filter((Constraint c) -> !c.constraintSatisfied(state.x(), state.v(), 1E-7, 1E-7)).count();
1540 if (constraintFails > 0) {
1541 logger.info(format(" %d constraint failures in step %d", constraintFails, step));
1542 }
1543 }
1544
1545
1546 thermostat.halfStep(dt);
1547
1548
1549 integrator.preForce(potential);
1550 if (esvSystem != null) {
1551 esvIntegrator.preForce(potential);
1552
1553 esvSystem.preForce();
1554 }
1555
1556
1557 if (esvSystem != null && potential instanceof OpenMMEnergy) {
1558 state.setPotentialEnergy(((OpenMMEnergy) potential).energyAndGradientFFX(state.x(), state.gradient()));
1559 } else {
1560 state.setPotentialEnergy(potential.energyAndGradient(state.x(), state.gradient()));
1561 }
1562
1563
1564 if (integrator instanceof Respa r) {
1565 double potentialEnergy = state.getPotentialEnergy();
1566 state.setPotentialEnergy(potentialEnergy + r.getHalfStepEnergy());
1567 }
1568
1569
1570 integrator.postForce(state.gradient());
1571 if (esvSystem != null) {
1572 double[] dEdL = esvSystem.postForce();
1573 esvIntegrator.postForce(dEdL);
1574 }
1575
1576
1577 thermostat.computeKineticEnergy();
1578
1579
1580 thermostat.fullStep(dt);
1581
1582
1583 thermostat.computeKineticEnergy();
1584 if (esvSystem != null) {
1585
1586 esvThermostat.computeKineticEnergy();
1587 }
1588
1589
1590 if (thermostat.getRemoveCenterOfMassMotion() && step % removeCOMMotionFrequency == 0) {
1591 thermostat.centerOfMassMotion(true, false);
1592 }
1593
1594
1595 if (esvSystem != null) {
1596 double kineticEnergy = thermostat.getKineticEnergy();
1597 double esvKineticEnergy = esvThermostat.getKineticEnergy();
1598 state.setKineticEnergy(kineticEnergy + esvKineticEnergy);
1599 }
1600
1601
1602 temperatureStats.addValue(state.getTemperature());
1603 potentialEnergyStats.addValue(state.getPotentialEnergy());
1604 kineticEnergyStats.addValue(state.getKineticEnergy());
1605 totalEnergyStats.addValue(state.getTotalEnergy());
1606
1607
1608 potential.setVelocity(state.v());
1609 potential.setAcceleration(state.a());
1610 potential.setPreviousAcceleration(state.aPrevious());
1611
1612
1613 totalSimTime += dt;
1614 time = logThermoForTime(step, time);
1615 if (step % printEsvFrequency == 0 && esvSystem != null) {
1616 logger.log(basicLogging, format(" %s", esvSystem.getLambdaList()));
1617 }
1618
1619 if (automaticWriteouts) {
1620 writeFilesForStep(step, true, true);
1621 }
1622
1623
1624 if (algorithmListener != null && step % logFrequency == 0) {
1625 for (MolecularAssembly assembly : molecularAssembly) {
1626 algorithmListener.algorithmUpdate(assembly);
1627 }
1628 }
1629
1630
1631 if (terminate) {
1632 logger.info(format("\n Terminating after %8d time steps\n", step));
1633 break;
1634 }
1635 }
1636 }
1637
1638
1639
1640
1641
1642
1643
1644
1645 private void checkFrequency(String describe, int frequency) {
1646 if (frequency > nSteps) {
1647 logger.fine(
1648 format(" Specified %s frequency of %d is greater than the number of steps %d", describe,
1649 frequency, nSteps));
1650 }
1651 }
1652
1653
1654
1655
1656
1657
1658 public void setCoordinates(double[] coords) {
1659 state.setCoordinates(coords);
1660 }
1661
1662
1663
1664
1665
1666
1667 public double[] getCoordinates() {
1668 return state.getCoordinatesCopy();
1669 }
1670
1671
1672
1673
1674 public void storeState() {
1675 storedState = state.getUnmodifiableState();
1676 }
1677
1678
1679
1680
1681
1682
1683 public void revertState() throws Exception {
1684 if (storedState == null) {
1685 throw new Exception();
1686 }
1687 revertState(storedState);
1688 }
1689
1690
1691
1692
1693
1694
1695 private void revertState(UnmodifiableState state) {
1696 this.state.revertState(state);
1697 potential.setVelocity(state.v());
1698 potential.setAcceleration(state.a());
1699 potential.setPreviousAcceleration(state.aPrevious());
1700
1701 int numberOfVariables = this.state.getNumberOfVariables();
1702 Atom[] atoms = molecularAssembly[0].getActiveAtomArray();
1703 if (atoms.length * 3 == numberOfVariables) {
1704 int index = 0;
1705 double[] x = state.x();
1706 double[] gradient = state.gradient();
1707 for (Atom atom : atoms) {
1708 atom.moveTo(x[index], x[index + 1], x[index + 2]);
1709 atom.setXYZGradient(gradient[index], gradient[index + 1], gradient[index + 2]);
1710 index += 3;
1711 }
1712 }
1713 }
1714
1715 }