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