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.potential.openmm;
39
40 import edu.uiowa.jopenmm.OpenMM_Vec3;
41 import ffx.crystal.Crystal;
42 import ffx.openmm.AndersenThermostat;
43 import ffx.openmm.CMMotionRemover;
44 import ffx.openmm.Force;
45 import ffx.openmm.MonteCarloBarostat;
46 import ffx.potential.ForceFieldEnergy;
47 import ffx.potential.MolecularAssembly;
48 import ffx.potential.bonded.Angle;
49 import ffx.potential.bonded.Atom;
50 import ffx.potential.bonded.Bond;
51 import ffx.potential.nonbonded.GeneralizedKirkwood;
52 import ffx.potential.nonbonded.ParticleMeshEwald;
53 import ffx.potential.nonbonded.VanDerWaals;
54 import ffx.potential.nonbonded.VanDerWaalsForm;
55 import ffx.potential.nonbonded.implicit.ChandlerCavitation;
56 import ffx.potential.nonbonded.implicit.DispersionRegion;
57 import ffx.potential.parameters.BondType;
58 import ffx.potential.parameters.ForceField;
59 import ffx.utilities.Constants;
60 import org.apache.commons.configuration2.CompositeConfiguration;
61
62 import javax.annotation.Nullable;
63 import java.util.logging.Logger;
64
65 import static edu.uiowa.jopenmm.OpenMMAmoebaLibrary.OpenMM_NmPerAngstrom;
66 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_Boolean.OpenMM_True;
67 import static ffx.potential.parameters.VDWType.VDW_TYPE.LENNARD_JONES;
68 import static ffx.utilities.Constants.KCAL_TO_GRAM_ANG2_PER_PS2;
69 import static ffx.utilities.Constants.kB;
70 import static java.lang.String.format;
71 import static org.apache.commons.math3.util.FastMath.cos;
72 import static org.apache.commons.math3.util.FastMath.sqrt;
73 import static org.apache.commons.math3.util.FastMath.toRadians;
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89 public class OpenMMSystem extends ffx.openmm.System {
90
91 private static final Logger logger = Logger.getLogger(OpenMMSystem.class.getName());
92
93
94
95
96 private final OpenMMEnergy openMMEnergy;
97
98
99
100 private final ForceField forceField;
101
102
103
104 private final Atom[] atoms;
105
106
107
108
109 private boolean updateBondedTerms = false;
110
111
112
113 private BondForce bondForce = null;
114
115
116
117 private AngleForce angleForce = null;
118
119
120
121 private InPlaneAngleForce inPlaneAngleForce = null;
122
123
124
125 private StretchBendForce stretchBendForce = null;
126
127
128
129 private UreyBradleyForce ureyBradleyForce = null;
130
131
132
133 private OutOfPlaneBendForce outOfPlaneBendForce = null;
134
135
136
137 private PiOrbitalTorsionForce piOrbitalTorsionForce = null;
138
139
140
141 private TorsionForce torsionForce = null;
142
143
144
145 private ImproperTorsionForce improperTorsionForce = null;
146
147
148
149 private AmoebaTorsionTorsionForce amoebaTorsionTorsionForce = null;
150
151
152
153 private StretchTorsionForce stretchTorsionForce = null;
154
155
156
157 private AngleTorsionForce angleTorsionForce = null;
158
159
160
161 private RestrainTorsionsForce restrainTorsionsForce = null;
162
163
164
165 private RestrainPositionsForce restrainPositionsForce = null;
166
167
168
169 private RestrainGroupsForce restrainGroupsForce = null;
170
171
172
173 private AmoebaVdwForce amoebaVDWForce = null;
174
175
176
177 private AmoebaMultipoleForce amoebaMultipoleForce = null;
178
179
180
181 private AmoebaGeneralizedKirkwoodForce amoebaGeneralizedKirkwoodForce = null;
182
183
184
185 private AmoebaWcaDispersionForce amoebaWcaDispersionForce = null;
186
187
188
189 private AmoebaGKCavitationForce amoebaGKCavitationForce = null;
190
191
192
193 private FixedChargeGBForce fixedChargeGBForce = null;
194
195
196
197 private FixedChargeNonbondedForce fixedChargeNonBondedForce = null;
198
199
200
201 private FixedChargeAlchemicalForces fixedChargeAlchemicalForces = null;
202
203
204
205 private AndersenThermostat andersenThermostat = null;
206
207
208
209 private MonteCarloBarostat monteCarloBarostat = null;
210
211
212
213 private CMMotionRemover cmMotionRemover = null;
214
215
216
217 private boolean softcoreCreated = false;
218
219
220
221
222
223
224 public OpenMMSystem(OpenMMEnergy openMMEnergy) {
225 this.openMMEnergy = openMMEnergy;
226
227 MolecularAssembly molecularAssembly = openMMEnergy.getMolecularAssembly();
228 forceField = molecularAssembly.getForceField();
229 atoms = molecularAssembly.getAtomArray();
230
231
232 try {
233 addAtoms();
234 } catch (Exception e) {
235 logger.severe(" Atom without mass encountered.");
236 }
237
238 logger.info(format("\n OpenMM system created with %d atoms.", atoms.length));
239
240 }
241
242
243
244
245 public void addForces() {
246
247
248
249 boolean rigidHydrogen = forceField.getBoolean("RIGID_HYDROGEN", false);
250 boolean rigidBonds = forceField.getBoolean("RIGID_BONDS", false);
251 boolean rigidHydrogenAngles = forceField.getBoolean("RIGID_HYDROGEN_ANGLES", false);
252 if (rigidHydrogen) {
253 addHydrogenConstraints();
254 }
255 if (rigidBonds) {
256 addUpBondConstraints();
257 }
258 if (rigidHydrogenAngles) {
259 setUpHydrogenAngleConstraints();
260 }
261
262 logger.info("\n Bonded Terms\n");
263
264
265 if (rigidBonds) {
266 logger.info(" Not creating AmoebaBondForce because bonds are constrained.");
267 } else {
268 bondForce = (BondForce) BondForce.constructForce(openMMEnergy);
269 addForce(bondForce);
270 }
271
272
273 angleForce = (AngleForce) AngleForce.constructForce(openMMEnergy);
274 addForce(angleForce);
275
276
277 inPlaneAngleForce = (InPlaneAngleForce) InPlaneAngleForce.constructForce(openMMEnergy);
278 addForce(inPlaneAngleForce);
279
280
281 stretchBendForce = (StretchBendForce) StretchBendForce.constructForce(openMMEnergy);
282 addForce(stretchBendForce);
283
284
285 ureyBradleyForce = (UreyBradleyForce) UreyBradleyForce.constructForce(openMMEnergy);
286 addForce(ureyBradleyForce);
287
288
289 outOfPlaneBendForce = (OutOfPlaneBendForce) OutOfPlaneBendForce.constructForce(openMMEnergy);
290 addForce(outOfPlaneBendForce);
291
292
293 piOrbitalTorsionForce = (PiOrbitalTorsionForce) PiOrbitalTorsionForce.constructForce(openMMEnergy);
294 addForce(piOrbitalTorsionForce);
295
296
297 torsionForce = (TorsionForce) TorsionForce.constructForce(openMMEnergy);
298 addForce(torsionForce);
299
300
301 improperTorsionForce = (ImproperTorsionForce) ImproperTorsionForce.constructForce(openMMEnergy);
302 addForce(improperTorsionForce);
303
304
305 stretchTorsionForce = (StretchTorsionForce) StretchTorsionForce.constructForce(openMMEnergy);
306 addForce(stretchTorsionForce);
307
308
309 angleTorsionForce = (AngleTorsionForce) AngleTorsionForce.constructForce(openMMEnergy);
310 addForce(angleTorsionForce);
311
312
313 amoebaTorsionTorsionForce = (AmoebaTorsionTorsionForce) AmoebaTorsionTorsionForce.constructForce(openMMEnergy);
314 addForce(amoebaTorsionTorsionForce);
315
316 if (openMMEnergy.getRestrainMode() == ForceFieldEnergy.RestrainMode.ENERGY) {
317
318 restrainPositionsForce = (RestrainPositionsForce) RestrainPositionsForce.constructForce(openMMEnergy);
319 addForce(restrainPositionsForce);
320
321
322 for (BondType.BondFunction function : BondType.BondFunction.values()) {
323 RestrainBondsForce restrainBondsForce = (RestrainBondsForce) RestrainBondsForce.constructForce(function, openMMEnergy);
324 addForce(restrainBondsForce);
325 }
326
327
328 restrainTorsionsForce = (RestrainTorsionsForce) RestrainTorsionsForce.constructForce(openMMEnergy);
329 addForce(restrainTorsionsForce);
330 }
331
332
333 restrainGroupsForce = (RestrainGroupsForce) RestrainGroupsForce.constructForce(openMMEnergy);
334 addForce(restrainGroupsForce);
335
336 setDefaultPeriodicBoxVectors();
337
338 VanDerWaals vdW = openMMEnergy.getVdwNode();
339 if (vdW != null) {
340 logger.info("\n Non-Bonded Terms");
341 VanDerWaalsForm vdwForm = vdW.getVDWForm();
342 if (vdwForm.vdwType == LENNARD_JONES) {
343 fixedChargeNonBondedForce = (FixedChargeNonbondedForce) FixedChargeNonbondedForce.constructForce(openMMEnergy);
344 addForce(fixedChargeNonBondedForce);
345 if (fixedChargeNonBondedForce != null) {
346 GeneralizedKirkwood gk = openMMEnergy.getGK();
347 if (gk != null) {
348 fixedChargeGBForce = (FixedChargeGBForce) FixedChargeGBForce.constructForce(openMMEnergy);
349 addForce(fixedChargeGBForce);
350 }
351 }
352 } else {
353
354 amoebaVDWForce = (AmoebaVdwForce) AmoebaVdwForce.constructForce(openMMEnergy);
355 addForce(amoebaVDWForce);
356
357
358 amoebaMultipoleForce = (AmoebaMultipoleForce) AmoebaMultipoleForce.constructForce(openMMEnergy);
359 addForce(amoebaMultipoleForce);
360
361
362 GeneralizedKirkwood gk = openMMEnergy.getGK();
363 if (gk != null) {
364 amoebaGeneralizedKirkwoodForce = (AmoebaGeneralizedKirkwoodForce) AmoebaGeneralizedKirkwoodForce.constructForce(openMMEnergy);
365 addForce(amoebaGeneralizedKirkwoodForce);
366
367
368 DispersionRegion dispersionRegion = gk.getDispersionRegion();
369 if (dispersionRegion != null) {
370 amoebaWcaDispersionForce = (AmoebaWcaDispersionForce) AmoebaWcaDispersionForce.constructForce(openMMEnergy);
371 addForce(amoebaWcaDispersionForce);
372 }
373
374
375 ChandlerCavitation chandlerCavitation = gk.getChandlerCavitation();
376 if (chandlerCavitation != null && chandlerCavitation.getGaussVol() != null) {
377 amoebaGKCavitationForce = (AmoebaGKCavitationForce) AmoebaGKCavitationForce.constructForce(openMMEnergy);
378 addForce(amoebaGKCavitationForce);
379 }
380 }
381 }
382 }
383 }
384
385
386
387
388
389 public void removeForce(Force force) {
390 if (force != null) {
391 removeForce(force.getForceIndex());
392 }
393 }
394
395
396
397
398
399
400 public void addAndersenThermostatForce(double targetTemp) {
401 double collisionFreq = forceField.getDouble("COLLISION_FREQ", 0.1);
402 addAndersenThermostatForce(targetTemp, collisionFreq);
403 }
404
405
406
407
408
409
410
411 public void addAndersenThermostatForce(double targetTemp, double collisionFreq) {
412 if (andersenThermostat != null) {
413 removeForce(andersenThermostat);
414 andersenThermostat = null;
415 }
416 andersenThermostat = new AndersenThermostat(targetTemp, collisionFreq);
417 addForce(andersenThermostat);
418 logger.info("\n Adding an Andersen thermostat");
419 logger.info(format(" Target Temperature: %6.2f (K)", targetTemp));
420 logger.info(format(" Collision Frequency: %6.2f (1/psec)", collisionFreq));
421 }
422
423
424
425
426 public void addCOMMRemoverForce() {
427 if (cmMotionRemover != null) {
428 removeForce(cmMotionRemover);
429 cmMotionRemover = null;
430 }
431 int frequency = 100;
432 cmMotionRemover = new CMMotionRemover(frequency);
433 addForce(cmMotionRemover);
434 logger.info("\n Added a Center of Mass Motion Remover");
435 logger.info(format(" Frequency: %6d", frequency));
436 }
437
438
439
440
441
442
443
444
445 public void addMonteCarloBarostatForce(double targetPressure, double targetTemp, int frequency) {
446 if (monteCarloBarostat != null) {
447 removeForce(monteCarloBarostat);
448 monteCarloBarostat = null;
449 }
450 double pressureInBar = targetPressure * Constants.ATM_TO_BAR;
451 monteCarloBarostat = new MonteCarloBarostat(pressureInBar, targetTemp, frequency);
452 CompositeConfiguration properties = openMMEnergy.getMolecularAssembly().getProperties();
453 if (properties.containsKey("barostat-seed")) {
454 int randomSeed = properties.getInt("barostat-seed", 0);
455 logger.info(format(" Setting random seed %d for Monte Carlo Barostat", randomSeed));
456 monteCarloBarostat.setRandomNumberSeed(randomSeed);
457 }
458 addForce(monteCarloBarostat);
459 logger.info("\n Added a Monte Carlo Barostat");
460 logger.info(format(" Target Pressure: %6.2f (atm)", targetPressure));
461 logger.info(format(" Target Temperature: %6.2f (K)", targetTemp));
462 logger.info(format(" MC Move Frequency: %6d", frequency));
463 }
464
465
466
467
468
469
470 public int calculateDegreesOfFreedom() {
471
472 int dof = openMMEnergy.getNumberOfVariables();
473
474 dof = dof - getNumConstraints();
475
476 if (cmMotionRemover != null) {
477 dof -= 3;
478 }
479 return dof;
480 }
481
482 public double getTemperature(double kineticEnergy) {
483 double dof = calculateDegreesOfFreedom();
484 return 2.0 * kineticEnergy * KCAL_TO_GRAM_ANG2_PER_PS2 / (kB * dof);
485 }
486
487
488
489
490
491
492 public ForceField getForceField() {
493 return forceField;
494 }
495
496
497
498
499
500
501 public Crystal getCrystal() {
502 return openMMEnergy.getCrystal();
503 }
504
505
506
507
508
509
510 public int getNumberOfVariables() {
511 return openMMEnergy.getNumberOfVariables();
512 }
513
514
515
516
517 public void free() {
518 if (getPointer() != null) {
519 logger.fine(" Free OpenMM system.");
520 destroy();
521 logger.fine(" Free OpenMM system completed.");
522 }
523 }
524
525 public void setUpdateBondedTerms(boolean updateBondedTerms) {
526 this.updateBondedTerms = updateBondedTerms;
527 }
528
529
530
531
532
533
534
535
536
537
538
539 private void setDefaultPeriodicBoxVectors() {
540 Crystal crystal = openMMEnergy.getCrystal();
541 if (!crystal.aperiodic()) {
542 OpenMM_Vec3 a = new OpenMM_Vec3();
543 OpenMM_Vec3 b = new OpenMM_Vec3();
544 OpenMM_Vec3 c = new OpenMM_Vec3();
545 double[][] Ai = crystal.Ai;
546 a.x = Ai[0][0] * OpenMM_NmPerAngstrom;
547 a.y = Ai[0][1] * OpenMM_NmPerAngstrom;
548 a.z = Ai[0][2] * OpenMM_NmPerAngstrom;
549 b.x = Ai[1][0] * OpenMM_NmPerAngstrom;
550 b.y = Ai[1][1] * OpenMM_NmPerAngstrom;
551 b.z = Ai[1][2] * OpenMM_NmPerAngstrom;
552 c.x = Ai[2][0] * OpenMM_NmPerAngstrom;
553 c.y = Ai[2][1] * OpenMM_NmPerAngstrom;
554 c.z = Ai[2][2] * OpenMM_NmPerAngstrom;
555 setDefaultPeriodicBoxVectors(a, b, c);
556 }
557 }
558
559
560
561
562
563
564 public void updateParameters(@Nullable Atom[] atoms) {
565 VanDerWaals vanDerWaals = openMMEnergy.getVdwNode();
566 if (vanDerWaals != null) {
567 boolean vdwLambdaTerm = vanDerWaals.getLambdaTerm();
568 if (vdwLambdaTerm) {
569 double lambdaVDW = vanDerWaals.getLambda();
570 if (fixedChargeNonBondedForce != null) {
571 if (!softcoreCreated) {
572 fixedChargeAlchemicalForces = new FixedChargeAlchemicalForces(openMMEnergy, fixedChargeNonBondedForce);
573 addForce(fixedChargeAlchemicalForces.getFixedChargeSoftcoreForce());
574 addForce(fixedChargeAlchemicalForces.getAlchemicalAlchemicalStericsForce());
575 addForce(fixedChargeAlchemicalForces.getNonAlchemicalAlchemicalStericsForce());
576
577 openMMEnergy.getContext().reinitialize(OpenMM_True);
578 softcoreCreated = true;
579 }
580
581 openMMEnergy.getContext().setParameter("vdw_lambda", lambdaVDW);
582 } else if (amoebaVDWForce != null) {
583
584 openMMEnergy.getContext().setParameter("AmoebaVdwLambda", lambdaVDW);
585 if (softcoreCreated) {
586 ParticleMeshEwald pme = openMMEnergy.getPmeNode();
587
588 if (pme == null || !pme.getLambdaTerm()) {
589 return;
590 }
591 } else {
592 softcoreCreated = true;
593 }
594 }
595 }
596 }
597
598
599
600 if (updateBondedTerms) {
601 if (bondForce != null) {
602 bondForce.updateForce(openMMEnergy);
603 }
604 if (angleForce != null) {
605 angleForce.updateForce(openMMEnergy);
606 }
607 if (inPlaneAngleForce != null) {
608 inPlaneAngleForce.updateForce(openMMEnergy);
609 }
610 if (stretchBendForce != null) {
611 stretchBendForce.updateForce(openMMEnergy);
612 }
613 if (ureyBradleyForce != null) {
614 ureyBradleyForce.updateForce(openMMEnergy);
615 }
616 if (outOfPlaneBendForce != null) {
617 outOfPlaneBendForce.updateForce(openMMEnergy);
618 }
619 if (piOrbitalTorsionForce != null) {
620 piOrbitalTorsionForce.updateForce(openMMEnergy);
621 }
622 if (torsionForce != null) {
623 torsionForce.updateForce(openMMEnergy);
624 }
625 if (improperTorsionForce != null) {
626 improperTorsionForce.updateForce(openMMEnergy);
627 }
628 }
629
630 if (restrainTorsionsForce != null) {
631 restrainTorsionsForce.updateForce(openMMEnergy);
632 }
633
634 if (atoms == null || atoms.length == 0) {
635 return;
636 }
637
638
639 if (fixedChargeNonBondedForce != null) {
640 fixedChargeNonBondedForce.updateForce(atoms, openMMEnergy);
641 }
642
643
644 if (fixedChargeGBForce != null) {
645 fixedChargeGBForce.updateForce(atoms, openMMEnergy);
646 }
647
648
649 if (amoebaVDWForce != null) {
650 amoebaVDWForce.updateForce(atoms, openMMEnergy);
651 }
652
653
654 if (amoebaMultipoleForce != null) {
655 amoebaMultipoleForce.updateForce(atoms, openMMEnergy);
656 }
657
658
659 if (amoebaGeneralizedKirkwoodForce != null) {
660 amoebaGeneralizedKirkwoodForce.updateForce(atoms, openMMEnergy);
661 }
662
663
664 if (amoebaWcaDispersionForce != null) {
665 amoebaWcaDispersionForce.updateForce(atoms, openMMEnergy);
666 }
667
668
669 if (amoebaGKCavitationForce != null) {
670 amoebaGKCavitationForce.updateForce(atoms, openMMEnergy);
671 }
672 }
673
674
675
676
677
678 private void addAtoms() throws Exception {
679 for (Atom atom : atoms) {
680 double mass = atom.getMass();
681 if (mass < 0.0) {
682 throw new Exception(" Atom with mass less than 0.");
683 }
684 if (mass == 0.0) {
685 logger.info(format(" Atom %s has zero mass.", atom));
686 }
687 addParticle(mass);
688 }
689 }
690
691
692
693
694 public void updateAtomMass() {
695 int index = 0;
696 for (Atom atom : atoms) {
697 double mass = 0.0;
698 if (atom.isActive()) {
699 mass = atom.getMass();
700 }
701 setParticleMass(index++, mass);
702 }
703 }
704
705 public boolean hasAmoebaCavitationForce() {
706 return amoebaGKCavitationForce != null;
707 }
708
709
710
711
712 private void addUpBondConstraints() {
713 Bond[] bonds = openMMEnergy.getBonds();
714 if (bonds == null || bonds.length < 1) {
715 return;
716 }
717
718 logger.info(" Adding constraints for all bonds.");
719 for (Bond bond : bonds) {
720 Atom atom1 = bond.getAtom(0);
721 Atom atom2 = bond.getAtom(1);
722 int iAtom1 = atom1.getXyzIndex() - 1;
723 int iAtom2 = atom2.getXyzIndex() - 1;
724 addConstraint(iAtom1, iAtom2, bond.bondType.distance * OpenMM_NmPerAngstrom);
725 }
726 }
727
728
729
730
731 private void addHydrogenConstraints() {
732 Bond[] bonds = openMMEnergy.getBonds();
733 if (bonds == null || bonds.length < 1) {
734 return;
735 }
736
737 logger.info(" Adding constraints for hydrogen bonds.");
738 for (Bond bond : bonds) {
739 Atom atom1 = bond.getAtom(0);
740 Atom atom2 = bond.getAtom(1);
741 if (atom1.isHydrogen() || atom2.isHydrogen()) {
742 BondType bondType = bond.bondType;
743 int iAtom1 = atom1.getXyzIndex() - 1;
744 int iAtom2 = atom2.getXyzIndex() - 1;
745 addConstraint(iAtom1, iAtom2, bondType.distance * OpenMM_NmPerAngstrom);
746 }
747 }
748 }
749
750
751
752
753 private void setUpHydrogenAngleConstraints() {
754 Angle[] angles = openMMEnergy.getAngles();
755 if (angles == null || angles.length < 1) {
756 return;
757 }
758
759 logger.info(" Adding hydrogen angle constraints.");
760
761 for (Angle angle : angles) {
762 if (isHydrogenAngle(angle)) {
763 Atom atom1 = angle.getAtom(0);
764 Atom atom3 = angle.getAtom(2);
765
766
767
768 Bond bond1 = angle.getBond(0);
769 double distance1 = bond1.bondType.distance;
770
771 Bond bond2 = angle.getBond(1);
772 double distance2 = bond2.bondType.distance;
773
774
775 double angleVal = angle.angleType.angle[angle.nh];
776
777
778 double falseBondLength = sqrt(
779 distance1 * distance1 + distance2 * distance2 - 2.0 * distance1 * distance2 * cos(
780 toRadians(angleVal)));
781
782 int iAtom1 = atom1.getXyzIndex() - 1;
783 int iAtom3 = atom3.getXyzIndex() - 1;
784 addConstraint(iAtom1, iAtom3, falseBondLength * OpenMM_NmPerAngstrom);
785 }
786 }
787 }
788
789
790
791
792
793
794
795
796 private boolean isHydrogenAngle(Angle angle) {
797 if (angle.containsHydrogen()) {
798
799 double angleVal = angle.angleType.angle[angle.nh];
800
801
802
803 if (angleVal < 160.0) {
804 Atom atom1 = angle.getAtom(0);
805 Atom atom2 = angle.getAtom(1);
806 Atom atom3 = angle.getAtom(2);
807
808
809 return atom1.isHydrogen() && atom3.isHydrogen() && !atom2.isHydrogen();
810 }
811 }
812 return false;
813 }
814
815 }