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.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   * Create and manage an OpenMM System.
81   *
82   * <p>The definition of a System involves four elements:
83   *
84   * <p>The particles and constraints are defined directly by the System object, while forces are
85   * defined by objects that extend the Force class. After creating a System, call addParticle() once
86   * for each particle, addConstraint() for each constraint, and addForce() for each Force.
87   *
88   * <p>In addition, particles may be designated as "virtual sites". These are particles whose
89   * positions are computed automatically based on the positions of other particles. To define a
90   * virtual site, call setVirtualSite(), passing in a VirtualSite object that defines the rules for
91   * computing its position.
92   */
93  public class OpenMMSystem extends ffx.openmm.System {
94  
95    private static final Logger logger = Logger.getLogger(OpenMMSystem.class.getName());
96  
97    /**
98     * The OpenMMEnergy instance.
99     */
100   private final OpenMMEnergy openMMEnergy;
101   /**
102    * The Force Field in use.
103    */
104   protected ForceField forceField;
105   /**
106    * Array of atoms in the system.
107    */
108   protected Atom[] atoms;
109 
110   /**
111    * This flag indicates bonded force constants and equilibria are updated (e.g. during ManyBody
112    * titration).
113    */
114   protected boolean updateBondedTerms = false;
115   /**
116    * Fixed charge softcore vdW force boolean.
117    */
118   protected boolean softcoreCreated = false;
119 
120   /**
121    * OpenMM Custom Bond Force
122    */
123   protected BondForce bondForce = null;
124   /**
125    * OpenMM Custom Angle Force
126    */
127   protected AngleForce angleForce = null;
128   /**
129    * OpenMM Custom In-Plane Angle Force
130    */
131   protected InPlaneAngleForce inPlaneAngleForce = null;
132   /**
133    * OpenMM Custom Stretch-Bend Force
134    */
135   protected StretchBendForce stretchBendForce = null;
136   /**
137    * OpenMM Custom Urey-Bradley Force
138    */
139   protected UreyBradleyForce ureyBradleyForce = null;
140   /**
141    * OpenMM Custom Out-of-Plane Bend Force
142    */
143   protected OutOfPlaneBendForce outOfPlaneBendForce = null;
144   /**
145    * OpenMM Custom Pi-Torsion Force
146    */
147   protected PiOrbitalTorsionForce piOrbitalTorsionForce = null;
148   /**
149    * OpenMM AMOEBA Torsion Force.
150    */
151   protected TorsionForce torsionForce = null;
152   /**
153    * OpenMM Improper Torsion Force.
154    */
155   protected ImproperTorsionForce improperTorsionForce = null;
156   /**
157    * OpenMM Torsion-Torsion Force.
158    */
159   protected AmoebaTorsionTorsionForce amoebaTorsionTorsionForce = null;
160   /**
161    * OpenMM Stretch-Torsion Force.
162    */
163   protected StretchTorsionForce stretchTorsionForce = null;
164   /**
165    * OpenMM Angle-Torsion Force.
166    */
167   protected AngleTorsionForce angleTorsionForce = null;
168   /**
169    * OpenMM Restraint-Torsion Force.
170    */
171   protected RestrainTorsionsForce restrainTorsionsForce = null;
172   /**
173    * OpenMM Restrain-Position Force.
174    */
175   protected RestrainPositionsForce restrainPositionsForce = null;
176   /**
177    * OpenMM Restrain-Groups Force.
178    */
179   protected RestrainGroupsForce restrainGroupsForce = null;
180   /**
181    * OpenMM AMOEBA van der Waals Force.
182    */
183   protected AmoebaVdwForce amoebaVDWForce = null;
184   /**
185    * OpenMM AMOEBA Multipole Force.
186    */
187   protected AmoebaMultipoleForce amoebaMultipoleForce = null;
188   /**
189    * OpenMM Generalized Kirkwood Force.
190    */
191   protected AmoebaGeneralizedKirkwoodForce amoebaGeneralizedKirkwoodForce = null;
192   /**
193    * OpenMM AMOEBA WCA Dispersion Force.
194    */
195   protected AmoebaWcaDispersionForce amoebaWcaDispersionForce = null;
196   /**
197    * OpenMM AMOEBA WCA Cavitation Force.
198    */
199   protected AmoebaGKCavitationForce amoebaGKCavitationForce = null;
200   /**
201    * OpenMM Custom GB Force.
202    */
203   protected FixedChargeGBForce fixedChargeGBForce = null;
204   /**
205    * OpenMM Fixed Charge Non-Bonded Force.
206    */
207   protected FixedChargeNonbondedForce fixedChargeNonBondedForce = null;
208   /**
209    * Custom forces to handle alchemical transformations for fixed charge systems.
210    */
211   protected FixedChargeAlchemicalForces fixedChargeAlchemicalForces = null;
212 
213   /**
214    * OpenMM thermostat. Currently, an Andersen thermostat is supported.
215    */
216   protected AndersenThermostat andersenThermostat = null;
217   /**
218    * Barostat to be added if NPT (isothermal-isobaric) dynamics is requested.
219    */
220   protected MonteCarloBarostat monteCarloBarostat = null;
221   /**
222    * OpenMM center-of-mass motion remover.
223    */
224   protected CMMotionRemover cmMotionRemover = null;
225 
226   /**
227    * OpenMMSystem constructor.
228    */
229   public OpenMMSystem() {
230     openMMEnergy = null;
231     forceField = null;
232     atoms = null;
233   }
234 
235   /**
236    * OpenMMSystem constructor.
237    *
238    * @param openMMEnergy ForceFieldEnergyOpenMM instance.
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     // Load atoms.
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    * Get the Potential in use.
259    *
260    * @return The Potential.
261    */
262   public Potential getPotential() {
263     return openMMEnergy;
264   }
265 
266   /**
267    * Get the atoms in the system.
268    * @return Array of atoms in the system.
269    */
270   public Atom[] getAtoms() {
271     return atoms;
272   }
273 
274   /**
275    * Add forces to the system.
276    */
277   public void addForces() {
278 
279     // Set up rigid constraints.
280     // These flags need to be set before bonds and angles are created below.
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     // Add Bond Force.
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     // Add Angle Force.
305     angleForce = (AngleForce) AngleForce.constructForce(openMMEnergy);
306     addForce(angleForce);
307 
308     // Add In-Plane Angle Force.
309     inPlaneAngleForce = (InPlaneAngleForce) InPlaneAngleForce.constructForce(openMMEnergy);
310     addForce(inPlaneAngleForce);
311 
312     // Add Stretch-Bend Force.
313     stretchBendForce = (StretchBendForce) StretchBendForce.constructForce(openMMEnergy);
314     addForce(stretchBendForce);
315 
316     // Add Urey-Bradley Force.
317     ureyBradleyForce = (UreyBradleyForce) UreyBradleyForce.constructForce(openMMEnergy);
318     addForce(ureyBradleyForce);
319 
320     // Out-of Plane Bend Force.
321     outOfPlaneBendForce = (OutOfPlaneBendForce) OutOfPlaneBendForce.constructForce(openMMEnergy);
322     addForce(outOfPlaneBendForce);
323 
324     // Add Pi-Torsion Force.
325     piOrbitalTorsionForce = (PiOrbitalTorsionForce) PiOrbitalTorsionForce.constructForce(openMMEnergy);
326     addForce(piOrbitalTorsionForce);
327 
328     // Add Torsion Force.
329     torsionForce = (TorsionForce) TorsionForce.constructForce(openMMEnergy);
330     addForce(torsionForce);
331 
332     // Add Improper Torsion Force.
333     improperTorsionForce = (ImproperTorsionForce) ImproperTorsionForce.constructForce(openMMEnergy);
334     addForce(improperTorsionForce);
335 
336     // Add Stretch-Torsion coupling terms.
337     stretchTorsionForce = (StretchTorsionForce) StretchTorsionForce.constructForce(openMMEnergy);
338     addForce(stretchTorsionForce);
339 
340     // Add Angle-Torsion coupling terms.
341     angleTorsionForce = (AngleTorsionForce) AngleTorsionForce.constructForce(openMMEnergy);
342     addForce(angleTorsionForce);
343 
344     // Add Torsion-Torsion Force.
345     amoebaTorsionTorsionForce = (AmoebaTorsionTorsionForce) AmoebaTorsionTorsionForce.constructForce(openMMEnergy);
346     addForce(amoebaTorsionTorsionForce);
347 
348     if (openMMEnergy.getRestrainMode() == ForceFieldEnergy.RestrainMode.ENERGY) {
349       // Add Restrain Positions force.
350       restrainPositionsForce = (RestrainPositionsForce) RestrainPositionsForce.constructForce(openMMEnergy);
351       addForce(restrainPositionsForce);
352 
353       // Add Restrain Bonds force for each functional form.
354       for (BondType.BondFunction function : BondType.BondFunction.values()) {
355         RestrainDistanceForce restrainDistanceForce = (RestrainDistanceForce) RestrainDistanceForce.constructForce(function, openMMEnergy);
356         addForce(restrainDistanceForce);
357       }
358 
359       // Add Restrain Torsions force.
360       restrainTorsionsForce = (RestrainTorsionsForce) RestrainTorsionsForce.constructForce(openMMEnergy);
361       addForce(restrainTorsionsForce);
362     }
363 
364     // Add Restrain Groups force.
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         // Add vdW Force.
386         amoebaVDWForce = (AmoebaVdwForce) AmoebaVdwForce.constructForce(openMMEnergy);
387         addForce(amoebaVDWForce);
388 
389         // Add Multipole Force.
390         amoebaMultipoleForce = (AmoebaMultipoleForce) AmoebaMultipoleForce.constructForce(openMMEnergy);
391         addForce(amoebaMultipoleForce);
392 
393         // Add Generalized Kirkwood Force.
394         GeneralizedKirkwood gk = openMMEnergy.getGK();
395         if (gk != null) {
396           amoebaGeneralizedKirkwoodForce = (AmoebaGeneralizedKirkwoodForce) AmoebaGeneralizedKirkwoodForce.constructForce(openMMEnergy);
397           addForce(amoebaGeneralizedKirkwoodForce);
398 
399           // Add WCA Dispersion Force.
400           DispersionRegion dispersionRegion = gk.getDispersionRegion();
401           if (dispersionRegion != null) {
402             amoebaWcaDispersionForce = (AmoebaWcaDispersionForce) AmoebaWcaDispersionForce.constructForce(openMMEnergy);
403             addForce(amoebaWcaDispersionForce);
404           }
405 
406           // Add a GaussVol Cavitation Force.
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    * Remove a force from the OpenMM System.
419    * The OpenMM memory associated with the removed Force object is deleted.
420    */
421   public void removeForce(Force force) {
422     if (force != null) {
423       removeForce(force.getForceIndex());
424     }
425   }
426 
427   /**
428    * Add an Andersen thermostat to the system.
429    *
430    * @param targetTemp Target temperature in Kelvins.
431    */
432   public void addAndersenThermostatForce(double targetTemp) {
433     double collisionFreq = forceField.getDouble("COLLISION_FREQ", 0.1);
434     addAndersenThermostatForce(targetTemp, collisionFreq);
435   }
436 
437   /**
438    * Add an Andersen thermostat to the system.
439    *
440    * @param targetTemp    Target temperature in Kelvins.
441    * @param collisionFreq Collision frequency in 1/psec.
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    * Adds a force that removes center-of-mass motion.
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    * Add a Monte Carlo Barostat to the system.
472    *
473    * @param targetPressure The target pressure (in atm).
474    * @param targetTemp     The target temperature.
475    * @param frequency      The frequency to apply the barostat.
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    * Calculate the number of degrees of freedom.
499    *
500    * @return Number of degrees of freedom.
501    */
502   public int calculateDegreesOfFreedom() {
503     // Begin from the 3 times the number of active atoms.
504     int dof = openMMEnergy.getNumberOfVariables();
505     // Remove OpenMM constraints.
506     dof = dof - getNumConstraints();
507     // Remove center of mass motion.
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    * Get the ForceField in use.
521    *
522    * @return ForceField instance.
523    */
524   public ForceField getForceField() {
525     return forceField;
526   }
527 
528   /**
529    * Get the Crystal instance.
530    *
531    * @return the Crystal instance.
532    */
533   public Crystal getCrystal() {
534     return openMMEnergy.getCrystal();
535   }
536 
537   /**
538    * Get the number of variables.
539    *
540    * @return the number of variables.
541    */
542   public int getNumberOfVariables() {
543     return openMMEnergy.getNumberOfVariables();
544   }
545 
546   /**
547    * Destroy the system.
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    * Set the default values of the vectors defining the axes of the periodic box (measured in nm).
563    *
564    * <p>Any newly created Context will have its box vectors set to these. They will affect any
565    * Force added to the System that uses periodic boundary conditions.
566    *
567    * <p>Triclinic boxes are supported, but the vectors must satisfy certain requirements. In
568    * particular, a must point in the x direction, b must point "mostly" in the y direction, and c
569    * must point "mostly" in the z direction. See the documentation for details.
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    * Update parameters if the Use flags and/or Lambda value has changed.
593    *
594    * @param atoms Atoms in this list are considered.
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             // Re-initialize the context.
609             openMMEnergy.getContext().reinitialize(OpenMM_True);
610             softcoreCreated = true;
611           }
612           // Update the lambda value.
613           openMMEnergy.getContext().setParameter("vdw_lambda", lambdaVDW);
614         } else if (amoebaVDWForce != null) {
615           // Update the lambda value.
616           openMMEnergy.getContext().setParameter("AmoebaVdwLambda", lambdaVDW);
617           if (softcoreCreated) {
618             ParticleMeshEwald pme = openMMEnergy.getPmeNode();
619             // Avoid any updateParametersInContext calls if vdwLambdaTerm is true, but not other alchemical terms.
620             if (pme == null || !pme.getLambdaTerm()) {
621               return;
622             }
623           } else {
624             softcoreCreated = true;
625           }
626         }
627       }
628     }
629 
630     // Note Stretch-Torsion and Angle-Torsion terms (for nucleic acids)
631     // and Torsion-Torsion terms (for protein backbones) are not updated yet.
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     // Update fixed charge non-bonded parameters.
671     if (fixedChargeNonBondedForce != null) {
672       // Need to pass all atoms due to non-bonded exceptions.
673       fixedChargeNonBondedForce.updateForce(this.atoms, openMMEnergy);
674     }
675 
676     // Update fixed charge GB parameters.
677     if (fixedChargeGBForce != null) {
678       fixedChargeGBForce.updateForce(atoms, openMMEnergy);
679     }
680 
681     // Update AMOEBA vdW parameters.
682     if (amoebaVDWForce != null) {
683       amoebaVDWForce.updateForce(atoms, openMMEnergy);
684     }
685 
686     // Update AMOEBA polarizable multipole parameters.
687     if (amoebaMultipoleForce != null) {
688       amoebaMultipoleForce.updateForce(atoms, openMMEnergy);
689     }
690 
691     // Update GK force.
692     if (amoebaGeneralizedKirkwoodForce != null) {
693       amoebaGeneralizedKirkwoodForce.updateForce(atoms, openMMEnergy);
694     }
695 
696     // Update WCA Force.
697     if (amoebaWcaDispersionForce != null) {
698       amoebaWcaDispersionForce.updateForce(atoms, openMMEnergy);
699     }
700 
701     // Update WCA Force.
702     if (amoebaGKCavitationForce != null) {
703       amoebaGKCavitationForce.updateForce(atoms, openMMEnergy);
704     }
705   }
706 
707   /**
708    * Adds atoms from the molecular assembly to the OpenMM System and reports to the user the number
709    * of particles added.
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    * This method sets the mass of inactive atoms to zero.
726    *
727    * @return Returns true if any inactive atoms were found and set to zero mass.
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    * Add a constraint to every bond.
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    * Add a constraint to every bond that includes a hydrogen atom.
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    * Add a constraint to every angle that includes two hydrogen atoms.
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         // Calculate a "false bond" length between atoms 1 and 3 to constrain the angle using the
809         // law of cosines.
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         // Equilibrium angle value in degrees.
817         double angleVal = angle.angleType.angle[angle.nh];
818 
819         // Law of cosines.
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    * Check to see if an angle is a hydrogen angle. This method only returns true for hydrogen
833    * angles that are less than 160 degrees.
834    *
835    * @param angle Angle to check.
836    * @return boolean indicating whether an angle is a hydrogen angle that is less than 160 degrees.
837    */
838   protected boolean isHydrogenAngle(Angle angle) {
839     if (angle.containsHydrogen()) {
840       // Equilibrium angle value in degrees.
841       double angleVal = angle.angleType.angle[angle.nh];
842       // Make sure angle is less than 160 degrees because greater than 160 degrees will not be
843       // constrained
844       // well using the law of cosines.
845       if (angleVal < 160.0) {
846         Atom atom1 = angle.getAtom(0);
847         Atom atom2 = angle.getAtom(1);
848         Atom atom3 = angle.getAtom(2);
849         // Setting constraints only on angles where atom1 or atom3 is a hydrogen while atom2 is
850         // not a hydrogen.
851         return atom1.isHydrogen() && atom3.isHydrogen() && !atom2.isHydrogen();
852       }
853     }
854     return false;
855   }
856 
857 }