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