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