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 ffx.algorithms.AlgorithmListener;
41 import ffx.algorithms.dynamics.integrators.IntegratorEnum;
42 import ffx.algorithms.dynamics.thermostats.ThermostatEnum;
43 import ffx.crystal.Crystal;
44 import ffx.crystal.CrystalPotential;
45 import ffx.numerics.Potential;
46 import ffx.potential.MolecularAssembly;
47 import ffx.potential.UnmodifiableState;
48 import ffx.potential.bonded.Atom;
49 import ffx.potential.bonded.LambdaInterface;
50 import ffx.potential.openmm.OpenMMContext;
51 import ffx.potential.openmm.OpenMMPotential;
52 import ffx.potential.openmm.OpenMMState;
53 import ffx.potential.openmm.OpenMMSystem;
54
55 import javax.annotation.Nullable;
56 import java.io.File;
57 import java.util.logging.Logger;
58
59 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_State_DataType.OpenMM_State_Energy;
60 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_State_DataType.OpenMM_State_Forces;
61 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_State_DataType.OpenMM_State_Positions;
62 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_State_DataType.OpenMM_State_Velocities;
63 import static ffx.utilities.Constants.NS2SEC;
64 import static java.lang.String.format;
65
66
67
68
69
70
71 public class MolecularDynamicsOpenMM extends MolecularDynamics {
72
73 private static final Logger logger = Logger.getLogger(MolecularDynamicsOpenMM.class.getName());
74
75
76
77 private final IntegratorEnum integratorType;
78
79
80
81 private final ThermostatEnum thermostatType;
82
83
84
85 private final CrystalPotential crystalPotential;
86
87
88
89 private final OpenMMPotential openMMPotential;
90
91
92
93 private String integratorString;
94
95
96
97 private int intervalSteps;
98
99
100
101 private boolean running;
102
103
104
105 private long time;
106
107
108
109 private boolean getAllVars = true;
110
111
112
113
114 private Runnable obtainVariables = this::getAllOpenMMVariables;
115
116
117
118
119
120
121
122
123
124
125
126
127 public MolecularDynamicsOpenMM(MolecularAssembly assembly, Potential potential,
128 AlgorithmListener listener, ThermostatEnum thermostat, IntegratorEnum integrator) {
129 super(assembly, potential, listener, thermostat, integrator);
130
131 logger.info("\n Initializing OpenMM molecular dynamics.");
132
133
134 running = false;
135 crystalPotential = (CrystalPotential) potential;
136 openMMPotential = (OpenMMPotential) potential;
137
138 thermostatType = thermostat;
139 integratorType = integrator;
140 integratorToString(integratorType);
141 }
142
143
144
145
146
147 public void setBarostat(@Nullable Barostat barostat) {
148 if (barostat != null) {
149 this.barostat = barostat;
150 this.constantPressure = true;
151 barostat.setActive(false);
152 } else {
153 this.barostat = null;
154 this.constantPressure = false;
155 }
156 }
157
158
159
160
161
162
163
164
165
166
167
168 @Override
169 public void dynamic(long numSteps, double timeStep, double printInterval, double saveInterval,
170 double temperature, boolean initVelocities, File dyn) {
171
172 if (!done) {
173 logger.warning(" Programming error - a thread invoked dynamic when it was already running.");
174 return;
175 }
176
177
178 init(numSteps, timeStep, printInterval, saveInterval, fileType, restartInterval, temperature,
179 initVelocities, dyn);
180
181 if (intervalSteps == 0 || intervalSteps > numSteps) {
182
183 intervalSteps = (int) numSteps;
184 }
185
186
187 preRunOps();
188
189
190 setOpenMMState();
191
192
193 getOpenMMEnergies();
194
195
196 initialState = new UnmodifiableState(state);
197
198
199 boolean forceCreation = openMMPotential.setActiveAtoms();
200
201
202
203 openMMPotential.updateContext(integratorString, dt, targetTemperature, forceCreation);
204
205
206 postInitEnergies();
207
208
209 mainLoop(numSteps);
210
211
212 postRun();
213 }
214
215
216
217
218 @Override
219 public int getIntervalSteps() {
220 return intervalSteps;
221 }
222
223
224
225
226
227
228 @Override
229 public void setIntervalSteps(int intervalSteps) {
230 this.intervalSteps = intervalSteps;
231 }
232
233
234
235
236 @Override
237 public double getTimeStep() {
238 return dt;
239 }
240
241
242
243
244 @Override
245 public void init(long numSteps, double timeStep, double loggingInterval, double trajectoryInterval,
246 String fileType, double restartInterval, double temperature, boolean initVelocities,
247 File dyn) {
248
249 super.init(numSteps, timeStep, loggingInterval, trajectoryInterval, fileType, restartInterval,
250 temperature, initVelocities, dyn);
251
252 boolean isLangevin = IntegratorEnum.isStochastic(integratorType);
253
254 OpenMMSystem openMMSystem = openMMPotential.getSystem();
255 if (!isLangevin && !thermostatType.equals(ThermostatEnum.ADIABATIC)) {
256
257 openMMSystem.addAndersenThermostatForce(targetTemperature);
258 }
259
260 if (constantPressure) {
261
262
263 double pressure = barostat.getPressure();
264 int frequency = barostat.getMeanBarostatInterval();
265 openMMSystem.addMonteCarloBarostatForce(pressure, targetTemperature, frequency);
266 }
267
268
269 if (!isLangevin) {
270
271 openMMSystem.addCOMMRemoverForce();
272 }
273
274
275 if (crystalPotential instanceof LambdaInterface lambdaInferface) {
276 lambdaInferface.setLambda(lambdaInferface.getLambda());
277 }
278
279 }
280
281 @Override
282 public void revertState() throws Exception {
283 super.revertState();
284 setOpenMMState();
285 }
286
287
288
289
290 @Override
291 public void setFileType(String fileType) {
292 this.fileType = fileType;
293 }
294
295
296
297
298
299
300
301 @Override
302 public void setObtainVelAcc(boolean obtainVA) {
303
304 getAllVars = obtainVA;
305 obtainVariables = obtainVA ? this::getAllOpenMMVariables : this::getOpenMMEnergiesAndPositions;
306 }
307
308 @Override
309 public void writeRestart() {
310 if (!getAllVars) {
311
312 getAllOpenMMVariables();
313 }
314 super.writeRestart();
315 }
316
317 @Override
318 protected void appendSnapshot(String[] extraLines) {
319 if (!getAllVars) {
320
321 getOpenMMEnergiesAndPositions();
322 }
323 super.appendSnapshot(extraLines);
324 }
325
326
327
328
329
330
331 private void takeOpenMMSteps(int intervalSteps) {
332 OpenMMContext openMMContext = openMMPotential.getContext();
333 openMMContext.integrate(intervalSteps);
334 }
335
336
337
338
339 private void setOpenMMState() {
340 OpenMMContext openMMContext = openMMPotential.getContext();
341
342 openMMContext.setPeriodicBoxVectors(crystalPotential.getCrystal());
343
344 crystalPotential.setCoordinates(state.x());
345
346 crystalPotential.setVelocity(state.v());
347 }
348
349
350
351
352 private void getOpenMMEnergies() {
353 OpenMMState openMMState = openMMPotential.getOpenMMState(OpenMM_State_Energy);
354 state.setKineticEnergy(openMMState.kineticEnergy);
355 state.setPotentialEnergy(openMMState.potentialEnergy);
356 state.setTemperature(openMMPotential.getSystem().getTemperature(openMMState.kineticEnergy));
357 openMMState.destroy();
358 }
359
360
361
362
363 void postInitEnergies() {
364 super.postInitEnergies();
365 running = true;
366 }
367
368 private void mainLoop(long numSteps) {
369 long i = 0;
370 time = System.nanoTime();
371
372 while (i < numSteps) {
373
374
375 long takeStepsTime = -System.nanoTime();
376 takeOpenMMSteps(intervalSteps);
377 takeStepsTime += System.nanoTime();
378 logger.finest(String.format("\n Took steps in %6.3f", takeStepsTime * NS2SEC));
379 totalSimTime += intervalSteps * dt;
380
381
382 i += intervalSteps;
383
384 long secondUpdateTime = -System.nanoTime();
385 updateFromOpenMM(i, running);
386 secondUpdateTime += System.nanoTime();
387
388 logger.finest(format("\n Update finished in %6.3f", secondUpdateTime * NS2SEC));
389 }
390 }
391
392
393
394
395 private void getOpenMMEnergiesAndPositions() {
396 int mask = OpenMM_State_Energy | OpenMM_State_Positions;
397 OpenMMState openMMState = openMMPotential.getOpenMMState(mask);
398 state.setPotentialEnergy(openMMState.potentialEnergy);
399 state.setKineticEnergy(openMMState.kineticEnergy);
400 state.setTemperature(openMMPotential.getSystem().getTemperature(openMMState.kineticEnergy));
401 Crystal crystal = crystalPotential.getCrystal();
402 if (!crystal.aperiodic()) {
403 double[][] cellVectors = openMMState.getPeriodicBoxVectors();
404 crystal.setCellVectors(cellVectors);
405 crystalPotential.setCrystal(crystal);
406 }
407
408
409 Atom[] atoms = openMMPotential.getSystem().getAtoms();
410 openMMState.getActivePositions(state.x(), atoms);
411 openMMState.destroy();
412 }
413
414
415
416
417 private void getAllOpenMMVariables() {
418 int mask = OpenMM_State_Energy | OpenMM_State_Positions | OpenMM_State_Velocities | OpenMM_State_Forces;
419 OpenMMState openMMState = openMMPotential.getOpenMMState(mask);
420 state.setPotentialEnergy(openMMState.potentialEnergy);
421 state.setKineticEnergy(openMMState.kineticEnergy);
422 state.setTemperature(openMMPotential.getSystem().getTemperature(openMMState.kineticEnergy));
423 Crystal crystal = crystalPotential.getCrystal();
424 if (!crystal.aperiodic()) {
425 double[][] cellVectors = openMMState.getPeriodicBoxVectors();
426 crystal.setCellVectors(cellVectors);
427 crystalPotential.setCrystal(crystal);
428 }
429
430
431 Atom[] atoms = openMMPotential.getSystem().getAtoms();
432 openMMState.getActivePositions(state.x(), atoms);
433 openMMState.getActiveVelocities(state.v(), atoms);
434 openMMState.getActiveAccelerations(state.a(), atoms);
435 openMMState.destroy();
436 }
437
438
439
440
441
442
443
444
445 private void updateFromOpenMM(long i, boolean running) {
446
447 double priorPE = state.getPotentialEnergy();
448
449 obtainVariables.run();
450
451 if (running) {
452 if (i == 0) {
453 logger.log(basicLogging,
454 format("\n %8s %12s %12s %12s %8s %8s", "Time", "Kinetic", "Potential", "Total", "Temp", "CPU"));
455 logger.log(basicLogging,
456 format(" %8s %12s %12s %12s %8s %8s", "psec", "kcal/mol", "kcal/mol", "kcal/mol", "K", "sec"));
457 logger.log(basicLogging,
458 format(" %8s %12.4f %12.4f %12.4f %8.2f", "", state.getKineticEnergy(),
459 state.getPotentialEnergy(), state.getTotalEnergy(), state.getTemperature()));
460 }
461 time = logThermoForTime(i, time);
462
463 if (automaticWriteouts) {
464 writeFilesForStep(i, true, true);
465 }
466 }
467 }
468
469
470
471
472
473
474 private void integratorToString(IntegratorEnum integrator) {
475 if (integrator == null) {
476 integratorString = "VERLET";
477 logger.info(" An integrator was not specified. Verlet will be used.");
478 } else {
479 switch (integratorType) {
480 default -> integratorString = "VERLET";
481 case STOCHASTIC, LANGEVIN -> integratorString = "LANGEVIN";
482 case RESPA, MTS -> integratorString = "MTS";
483 case STOCHASTIC_MTS, LANGEVIN_MTS -> integratorString = "LANGEVIN-MTS";
484 }
485 }
486 }
487 }