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