View Javadoc
1   // ******************************************************************************
2   //
3   // Title:       Force Field X.
4   // Description: Force Field X - Software for Molecular Biophysics.
5   // Copyright:   Copyright (c) Michael J. Schnieders 2001-2025.
6   //
7   // This file is part of Force Field X.
8   //
9   // Force Field X is free software; you can redistribute it and/or modify it
10  // under the terms of the GNU General Public License version 3 as published by
11  // the Free Software Foundation.
12  //
13  // Force Field X is distributed in the hope that it will be useful, but WITHOUT
14  // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15  // FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
16  // details.
17  //
18  // You should have received a copy of the GNU General Public License along with
19  // Force Field X; if not, write to the Free Software Foundation, Inc., 59 Temple
20  // Place, Suite 330, Boston, MA 02111-1307 USA
21  //
22  // Linking this library statically or dynamically with other modules is making a
23  // combined work based on this library. Thus, the terms and conditions of the
24  // GNU General Public License cover the whole combination.
25  //
26  // As a special exception, the copyright holders of this library give you
27  // permission to link this library with independent modules to produce an
28  // executable, regardless of the license terms of these independent modules, and
29  // to copy and distribute the resulting executable under terms of your choice,
30  // provided that you also meet, for each linked independent module, the terms
31  // and conditions of the license of that module. An independent module is a
32  // module which is not derived from or based on this library. If you modify this
33  // library, you may extend this exception to your version of the library, but
34  // you are not obligated to do so. If you do not wish to do so, delete this
35  // exception statement from your version.
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   * Create and manage an OpenMM System.
77   *
78   * <p>The definition of a System involves four elements:
79   *
80   * <p>The particles and constraints are defined directly by the System object, while forces are
81   * defined by objects that extend the Force class. After creating a System, call addParticle() once
82   * for each particle, addConstraint() for each constraint, and addForce() for each Force.
83   *
84   * <p>In addition, particles may be designated as "virtual sites". These are particles whose
85   * positions are computed automatically based on the positions of other particles. To define a
86   * virtual site, call setVirtualSite(), passing in a VirtualSite object that defines the rules for
87   * computing its position.
88   */
89  public class OpenMMSystem extends ffx.openmm.System {
90  
91    private static final Logger logger = Logger.getLogger(OpenMMSystem.class.getName());
92  
93    /**
94     * The ForceFieldEnergyOpenMM instance.
95     */
96    private final OpenMMEnergy openMMEnergy;
97    /**
98     * The Force Field in use.
99     */
100   private final ForceField forceField;
101   /**
102    * Array of atoms in the system.
103    */
104   private final Atom[] atoms;
105   /**
106    * This flag indicates bonded force constants and equilibria are updated (e.g. during ManyBody
107    * titration).
108    */
109   private boolean updateBondedTerms = false;
110   /**
111    * OpenMM Custom Bond Force
112    */
113   private BondForce bondForce = null;
114   /**
115    * OpenMM Custom Angle Force
116    */
117   private AngleForce angleForce = null;
118   /**
119    * OpenMM Custom In-Plane Angle Force
120    */
121   private InPlaneAngleForce inPlaneAngleForce = null;
122   /**
123    * OpenMM Custom Stretch-Bend Force
124    */
125   private StretchBendForce stretchBendForce = null;
126   /**
127    * OpenMM Custom Urey-Bradley Force
128    */
129   private UreyBradleyForce ureyBradleyForce = null;
130   /**
131    * OpenMM Custom Out-of-Plane Bend Force
132    */
133   private OutOfPlaneBendForce outOfPlaneBendForce = null;
134   /**
135    * OpenMM Custom Pi-Torsion Force
136    */
137   private PiOrbitalTorsionForce piOrbitalTorsionForce = null;
138   /**
139    * OpenMM AMOEBA Torsion Force.
140    */
141   private TorsionForce torsionForce = null;
142   /**
143    * OpenMM Improper Torsion Force.
144    */
145   private ImproperTorsionForce improperTorsionForce = null;
146   /**
147    * OpenMM Torsion-Torsion Force.
148    */
149   private AmoebaTorsionTorsionForce amoebaTorsionTorsionForce = null;
150   /**
151    * OpenMM Stretch-Torsion Force.
152    */
153   private StretchTorsionForce stretchTorsionForce = null;
154   /**
155    * OpenMM Angle-Torsion Force.
156    */
157   private AngleTorsionForce angleTorsionForce = null;
158   /**
159    * OpenMM Restraint-Torsion Force.
160    */
161   private RestrainTorsionsForce restrainTorsionsForce = null;
162   /**
163    * OpenMM Restrain-Position Force.
164    */
165   private RestrainPositionsForce restrainPositionsForce = null;
166   /**
167    * OpenMM Restrain-Groups Force.
168    */
169   private RestrainGroupsForce restrainGroupsForce = null;
170   /**
171    * OpenMM AMOEBA van der Waals Force.
172    */
173   private AmoebaVdwForce amoebaVDWForce = null;
174   /**
175    * OpenMM AMOEBA Multipole Force.
176    */
177   private AmoebaMultipoleForce amoebaMultipoleForce = null;
178   /**
179    * OpenMM Generalized Kirkwood Force.
180    */
181   private AmoebaGeneralizedKirkwoodForce amoebaGeneralizedKirkwoodForce = null;
182   /**
183    * OpenMM AMOEBA WCA Dispersion Force.
184    */
185   private AmoebaWcaDispersionForce amoebaWcaDispersionForce = null;
186   /**
187    * OpenMM AMOEBA WCA Cavitation Force.
188    */
189   private AmoebaGKCavitationForce amoebaGKCavitationForce = null;
190   /**
191    * OpenMM Custom GB Force.
192    */
193   private FixedChargeGBForce fixedChargeGBForce = null;
194   /**
195    * OpenMM Fixed Charge Non-Bonded Force.
196    */
197   private FixedChargeNonbondedForce fixedChargeNonBondedForce = null;
198   /**
199    * Custom forces to handle alchemical transformations for fixed charge systems.
200    */
201   private FixedChargeAlchemicalForces fixedChargeAlchemicalForces = null;
202   /**
203    * OpenMM thermostat. Currently, an Andersen thermostat is supported.
204    */
205   private AndersenThermostat andersenThermostat = null;
206   /**
207    * Barostat to be added if NPT (isothermal-isobaric) dynamics is requested.
208    */
209   private MonteCarloBarostat monteCarloBarostat = null;
210   /**
211    * OpenMM center-of-mass motion remover.
212    */
213   private CMMotionRemover cmMotionRemover = null;
214   /**
215    * Fixed charge softcore vdW force boolean.
216    */
217   private boolean softcoreCreated = false;
218 
219   /**
220    * OpenMMSystem constructor.
221    *
222    * @param openMMEnergy ForceFieldEnergyOpenMM instance.
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     // Load atoms.
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    * Add forces to the system.
244    */
245   public void addForces() {
246 
247     // Set up rigid constraints.
248     // These flags need to be set before bonds and angles are created below.
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     // Add Bond Force.
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     // Add Angle Force.
273     angleForce = (AngleForce) AngleForce.constructForce(openMMEnergy);
274     addForce(angleForce);
275 
276     // Add In-Plane Angle Force.
277     inPlaneAngleForce = (InPlaneAngleForce) InPlaneAngleForce.constructForce(openMMEnergy);
278     addForce(inPlaneAngleForce);
279 
280     // Add Stretch-Bend Force.
281     stretchBendForce = (StretchBendForce) StretchBendForce.constructForce(openMMEnergy);
282     addForce(stretchBendForce);
283 
284     // Add Urey-Bradley Force.
285     ureyBradleyForce = (UreyBradleyForce) UreyBradleyForce.constructForce(openMMEnergy);
286     addForce(ureyBradleyForce);
287 
288     // Out-of Plane Bend Force.
289     outOfPlaneBendForce = (OutOfPlaneBendForce) OutOfPlaneBendForce.constructForce(openMMEnergy);
290     addForce(outOfPlaneBendForce);
291 
292     // Add Pi-Torsion Force.
293     piOrbitalTorsionForce = (PiOrbitalTorsionForce) PiOrbitalTorsionForce.constructForce(openMMEnergy);
294     addForce(piOrbitalTorsionForce);
295 
296     // Add Torsion Force.
297     torsionForce = (TorsionForce) TorsionForce.constructForce(openMMEnergy);
298     addForce(torsionForce);
299 
300     // Add Improper Torsion Force.
301     improperTorsionForce = (ImproperTorsionForce) ImproperTorsionForce.constructForce(openMMEnergy);
302     addForce(improperTorsionForce);
303 
304     // Add Stretch-Torsion coupling terms.
305     stretchTorsionForce = (StretchTorsionForce) StretchTorsionForce.constructForce(openMMEnergy);
306     addForce(stretchTorsionForce);
307 
308     // Add Angle-Torsion coupling terms.
309     angleTorsionForce = (AngleTorsionForce) AngleTorsionForce.constructForce(openMMEnergy);
310     addForce(angleTorsionForce);
311 
312     // Add Torsion-Torsion Force.
313     amoebaTorsionTorsionForce = (AmoebaTorsionTorsionForce) AmoebaTorsionTorsionForce.constructForce(openMMEnergy);
314     addForce(amoebaTorsionTorsionForce);
315 
316     if (openMMEnergy.getRestrainMode() == ForceFieldEnergy.RestrainMode.ENERGY) {
317       // Add Restrain Positions force.
318       restrainPositionsForce = (RestrainPositionsForce) RestrainPositionsForce.constructForce(openMMEnergy);
319       addForce(restrainPositionsForce);
320 
321       // Add Restrain Bonds force for each functional form.
322       for (BondType.BondFunction function : BondType.BondFunction.values()) {
323         RestrainBondsForce restrainBondsForce = (RestrainBondsForce) RestrainBondsForce.constructForce(function, openMMEnergy);
324         addForce(restrainBondsForce);
325       }
326 
327       // Add Restrain Torsions force.
328       restrainTorsionsForce = (RestrainTorsionsForce) RestrainTorsionsForce.constructForce(openMMEnergy);
329       addForce(restrainTorsionsForce);
330     }
331 
332     // Add Restrain Groups force.
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         // Add vdW Force.
354         amoebaVDWForce = (AmoebaVdwForce) AmoebaVdwForce.constructForce(openMMEnergy);
355         addForce(amoebaVDWForce);
356 
357         // Add Multipole Force.
358         amoebaMultipoleForce = (AmoebaMultipoleForce) AmoebaMultipoleForce.constructForce(openMMEnergy);
359         addForce(amoebaMultipoleForce);
360 
361         // Add Generalized Kirkwood Force.
362         GeneralizedKirkwood gk = openMMEnergy.getGK();
363         if (gk != null) {
364           amoebaGeneralizedKirkwoodForce = (AmoebaGeneralizedKirkwoodForce) AmoebaGeneralizedKirkwoodForce.constructForce(openMMEnergy);
365           addForce(amoebaGeneralizedKirkwoodForce);
366 
367           // Add WCA Dispersion Force.
368           DispersionRegion dispersionRegion = gk.getDispersionRegion();
369           if (dispersionRegion != null) {
370             amoebaWcaDispersionForce = (AmoebaWcaDispersionForce) AmoebaWcaDispersionForce.constructForce(openMMEnergy);
371             addForce(amoebaWcaDispersionForce);
372           }
373 
374           // Add a GaussVol Cavitation Force.
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    * Remove a force from the OpenMM System.
387    * The OpenMM memory associated with the removed Force object is deleted.
388    */
389   public void removeForce(Force force) {
390     if (force != null) {
391       removeForce(force.getForceIndex());
392     }
393   }
394 
395   /**
396    * Add an Andersen thermostat to the system.
397    *
398    * @param targetTemp Target temperature in Kelvins.
399    */
400   public void addAndersenThermostatForce(double targetTemp) {
401     double collisionFreq = forceField.getDouble("COLLISION_FREQ", 0.1);
402     addAndersenThermostatForce(targetTemp, collisionFreq);
403   }
404 
405   /**
406    * Add an Andersen thermostat to the system.
407    *
408    * @param targetTemp    Target temperature in Kelvins.
409    * @param collisionFreq Collision frequency in 1/psec.
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    * Adds a force that removes center-of-mass motion.
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    * Add a Monte Carlo Barostat to the system.
440    *
441    * @param targetPressure The target pressure (in atm).
442    * @param targetTemp     The target temperature.
443    * @param frequency      The frequency to apply the barostat.
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    * Calculate the number of degrees of freedom.
467    *
468    * @return Number of degrees of freedom.
469    */
470   public int calculateDegreesOfFreedom() {
471     // Begin from the 3 times the number of active atoms.
472     int dof = openMMEnergy.getNumberOfVariables();
473     // Remove OpenMM constraints.
474     dof = dof - getNumConstraints();
475     // Remove center of mass motion.
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    * Get the ForceField in use.
489    *
490    * @return ForceField instance.
491    */
492   public ForceField getForceField() {
493     return forceField;
494   }
495 
496   /**
497    * Get the Crystal instance.
498    *
499    * @return the Crystal instance.
500    */
501   public Crystal getCrystal() {
502     return openMMEnergy.getCrystal();
503   }
504 
505   /**
506    * Get the number of variables.
507    *
508    * @return the number of variables.
509    */
510   public int getNumberOfVariables() {
511     return openMMEnergy.getNumberOfVariables();
512   }
513 
514   /**
515    * Destroy the system.
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    * Set the default values of the vectors defining the axes of the periodic box (measured in nm).
531    *
532    * <p>Any newly created Context will have its box vectors set to these. They will affect any
533    * Force added to the System that uses periodic boundary conditions.
534    *
535    * <p>Triclinic boxes are supported, but the vectors must satisfy certain requirements. In
536    * particular, a must point in the x direction, b must point "mostly" in the y direction, and c
537    * must point "mostly" in the z direction. See the documentation for details.
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    * Update parameters if the Use flags and/or Lambda value has changed.
561    *
562    * @param atoms Atoms in this list are considered.
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             // Re-initialize the context.
577             openMMEnergy.getContext().reinitialize(OpenMM_True);
578             softcoreCreated = true;
579           }
580           // Update the lambda value.
581           openMMEnergy.getContext().setParameter("vdw_lambda", lambdaVDW);
582         } else if (amoebaVDWForce != null) {
583           // Update the lambda value.
584           openMMEnergy.getContext().setParameter("AmoebaVdwLambda", lambdaVDW);
585           if (softcoreCreated) {
586             ParticleMeshEwald pme = openMMEnergy.getPmeNode();
587             // Avoid any updateParametersInContext calls if vdwLambdaTerm is true, but not other alchemical terms.
588             if (pme == null || !pme.getLambdaTerm()) {
589               return;
590             }
591           } else {
592             softcoreCreated = true;
593           }
594         }
595       }
596     }
597 
598     // Note Stretch-Torsion and Angle-Torsion terms (for nucleic acids)
599     // and Torsion-Torsion terms (for protein backbones) are not updated yet.
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     // Update fixed charge non-bonded parameters.
639     if (fixedChargeNonBondedForce != null) {
640       fixedChargeNonBondedForce.updateForce(atoms, openMMEnergy);
641     }
642 
643     // Update fixed charge GB parameters.
644     if (fixedChargeGBForce != null) {
645       fixedChargeGBForce.updateForce(atoms, openMMEnergy);
646     }
647 
648     // Update AMOEBA vdW parameters.
649     if (amoebaVDWForce != null) {
650       amoebaVDWForce.updateForce(atoms, openMMEnergy);
651     }
652 
653     // Update AMOEBA polarizable multipole parameters.
654     if (amoebaMultipoleForce != null) {
655       amoebaMultipoleForce.updateForce(atoms, openMMEnergy);
656     }
657 
658     // Update GK force.
659     if (amoebaGeneralizedKirkwoodForce != null) {
660       amoebaGeneralizedKirkwoodForce.updateForce(atoms, openMMEnergy);
661     }
662 
663     // Update WCA Force.
664     if (amoebaWcaDispersionForce != null) {
665       amoebaWcaDispersionForce.updateForce(atoms, openMMEnergy);
666     }
667 
668     // Update WCA Force.
669     if (amoebaGKCavitationForce != null) {
670       amoebaGKCavitationForce.updateForce(atoms, openMMEnergy);
671     }
672   }
673 
674   /**
675    * Adds atoms from the molecular assembly to the OpenMM System and reports to the user the number
676    * of particles added.
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    * This method sets the mass of inactive atoms to zero.
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    * Add a constraint to every bond.
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    * Add a constraint to every bond that includes a hydrogen atom.
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    * Add a constraint to every angle that includes two hydrogen atoms.
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         // Calculate a "false bond" length between atoms 1 and 3 to constrain the angle using the
767         // law of cosines.
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         // Equilibrium angle value in degrees.
775         double angleVal = angle.angleType.angle[angle.nh];
776 
777         // Law of cosines.
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    * Check to see if an angle is a hydrogen angle. This method only returns true for hydrogen
791    * angles that are less than 160 degrees.
792    *
793    * @param angle Angle to check.
794    * @return boolean indicating whether an angle is a hydrogen angle that is less than 160 degrees.
795    */
796   private boolean isHydrogenAngle(Angle angle) {
797     if (angle.containsHydrogen()) {
798       // Equilibrium angle value in degrees.
799       double angleVal = angle.angleType.angle[angle.nh];
800       // Make sure angle is less than 160 degrees because greater than 160 degrees will not be
801       // constrained
802       // well using the law of cosines.
803       if (angleVal < 160.0) {
804         Atom atom1 = angle.getAtom(0);
805         Atom atom2 = angle.getAtom(1);
806         Atom atom3 = angle.getAtom(2);
807         // Setting constraints only on angles where atom1 or atom3 is a hydrogen while atom2 is
808         // not a hydrogen.
809         return atom1.isHydrogen() && atom3.isHydrogen() && !atom2.isHydrogen();
810       }
811     }
812     return false;
813   }
814 
815 }