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 ffx.numerics.Potential;
41  import ffx.openmm.amoeba.TorsionTorsionForce;
42  import ffx.potential.MolecularAssembly;
43  import edu.uiowa.jopenmm.OpenMM_Vec3;
44  import ffx.crystal.Crystal;
45  import ffx.potential.ForceFieldEnergy;
46  import ffx.potential.bonded.Atom;
47  import ffx.potential.bonded.TorsionTorsion;
48  import ffx.potential.nonbonded.ParticleMeshEwald;
49  import ffx.potential.nonbonded.VanDerWaals;
50  import ffx.potential.terms.TorsionTorsionPotentialEnergy;
51  
52  import javax.annotation.Nullable;
53  import java.util.logging.Logger;
54  
55  import static edu.uiowa.jopenmm.OpenMMAmoebaLibrary.OpenMM_NmPerAngstrom;
56  import static java.lang.String.format;
57  
58  /**
59   * Create and manage an OpenMM Dual-Topology System.
60   *
61   * <p>The definition of a System involves four elements:
62   *
63   * <p>The particles and constraints are defined directly by the System object, while forces are
64   * defined by objects that extend the Force class. After creating a System, call addParticle() once
65   * for each particle, addConstraint() for each constraint, and addForce() for each Force.
66   *
67   * <p>In addition, particles may be designated as "virtual sites". These are particles whose
68   * positions are computed automatically based on the positions of other particles. To define a
69   * virtual site, call setVirtualSite(), passing in a VirtualSite object that defines the rules for
70   * computing its position.
71   */
72  public class OpenMMDualTopologySystem extends OpenMMSystem {
73  
74    private static final Logger logger = Logger.getLogger(OpenMMDualTopologySystem.class.getName());
75  
76    /**
77     * The OpenMMDualTopologyEnergy instance.
78     */
79    private final OpenMMDualTopologyEnergy openMMDualTopologyEnergy;
80  
81    /**
82     * The ForceFieldEnergy instance for the first topology.
83     */
84    protected ForceFieldEnergy forceFieldEnergy;
85    /**
86     * The ForceFieldEnergy instance for the second topology.
87     */
88    protected ForceFieldEnergy forceFieldEnergy2;
89    /**
90     * OpenMM Custom Bond Force for topology 2.
91     */
92    protected BondForce bondForce2 = null;
93    /**
94     * OpenMM Custom Angle Force for topology 2.
95     */
96    protected AngleForce angleForce2 = null;
97    /**
98     * OpenMM Custom In-Plane Angle Force for topology 2.
99     */
100   protected InPlaneAngleForce inPlaneAngleForce2 = null;
101   /**
102    * OpenMM Custom Stetch-Bend Force for topology 2.
103    */
104   protected StretchBendForce stretchBendForce2 = null;
105   /**
106    * OpenMM Custom Urey-Bradley Force for topology 2.
107    */
108   protected UreyBradleyForce ureyBradleyForce2 = null;
109   /**
110    * OpenMM Custom Out-of-Plane Bend Force for topology 2.
111    */
112   protected OutOfPlaneBendForce outOfPlaneBendForce2 = null;
113   /**
114    * OpenMM Custom Pi-Orbital Torsion Force for topology 2.
115    */
116   protected PiOrbitalTorsionForce piOrbitalTorsionForce2 = null;
117   /**
118    * OpenMM Custom Torsion Force for topology 2.
119    */
120   protected TorsionForce torsionForce2 = null;
121   /**
122    * OpenMM Custom Improper Torsion Force for topology 2.
123    */
124   protected ImproperTorsionForce improperTorsionForce2 = null;
125   /**
126    * OpenMM Custom Stretch-Torsion Force for topology 2.
127    */
128   protected StretchTorsionForce stretchTorsionForce2 = null;
129   /**
130    * OpenMM Custom Angle-Torsion Force for topology 2.
131    */
132   protected AngleTorsionForce angleTorsionForce2 = null;
133   /**
134    * OpenMM Custom Restrain Torsion Force for topology 2.
135    */
136   protected RestrainTorsionsForce restrainTorsionsForce2 = null;
137   /**
138    * OpenMM Custom Torsion-Torsion Force for topology 2.
139    * ToDo: There is no updateParametersInContext method for the AmoebaTorsionTorsionForce.
140    * We assume that the AmoebaTorsionTorsionForce is constant along the alchemical path.
141    */
142   protected AmoebaTorsionTorsionForce amoebaTorsionTorsionForce2 = null;
143   /**
144    * OpenMM AMOEBA van der Waals Force for topology 2.
145    */
146   private AmoebaVdwForce amoebaVDWForce2 = null;
147   /**
148    * OpenMM AMOEBA Multipole Force for topology 2.
149    */
150   private AmoebaMultipoleForce amoebaMultipoleForce2 = null;
151 
152   /**
153    * OpenMMDualTopologyEnergy constructor.
154    *
155    * @param openMMDualTopologyEnergy OpenMMDualTopologyEnergy instance.
156    */
157   public OpenMMDualTopologySystem(OpenMMDualTopologyEnergy openMMDualTopologyEnergy) {
158     this.openMMDualTopologyEnergy = openMMDualTopologyEnergy;
159 
160     forceFieldEnergy = openMMDualTopologyEnergy.getForceFieldEnergy(0);
161     forceFieldEnergy2 = openMMDualTopologyEnergy.getForceFieldEnergy(1);
162     forceField = openMMDualTopologyEnergy.getMolecularAssembly(0).getForceField();
163 
164     // This array contains the shared and alchemical atoms for the dual topology system.
165     atoms = openMMDualTopologyEnergy.getDualTopologyAtoms(0);
166 
167     // Load atoms.
168     try {
169       addAtoms();
170     } catch (Exception e) {
171       logger.severe(" Atom without mass encountered.");
172     }
173 
174     logger.info(format("\n OpenMM dual-topology system created with %d atoms.", atoms.length));
175   }
176 
177   /**
178    * Get the Potential in use.
179    *
180    * @return The Potential.
181    */
182   public Potential getPotential() {
183     return openMMDualTopologyEnergy;
184   }
185 
186   /**
187    * Get the Crystal instance.
188    *
189    * @return the Crystal instance.
190    */
191   @Override
192   public Crystal getCrystal() {
193     return forceFieldEnergy.getCrystal();
194   }
195 
196   /**
197    * Set the default values of the vectors defining the axes of the periodic box (measured in nm).
198    *
199    * <p>Any newly created Context will have its box vectors set to these. They will affect any
200    * Force added to the System that uses periodic boundary conditions.
201    *
202    * <p>Triclinic boxes are supported, but the vectors must satisfy certain requirements. In
203    * particular, a must point in the x direction, b must point "mostly" in the y direction, and c
204    * must point "mostly" in the z direction. See the documentation for details.
205    */
206   @Override
207   protected void setDefaultPeriodicBoxVectors() {
208     Crystal crystal = forceFieldEnergy.getCrystal();
209     if (!crystal.aperiodic()) {
210       OpenMM_Vec3 a = new OpenMM_Vec3();
211       OpenMM_Vec3 b = new OpenMM_Vec3();
212       OpenMM_Vec3 c = new OpenMM_Vec3();
213       double[][] Ai = crystal.Ai;
214       a.x = Ai[0][0] * OpenMM_NmPerAngstrom;
215       a.y = Ai[0][1] * OpenMM_NmPerAngstrom;
216       a.z = Ai[0][2] * OpenMM_NmPerAngstrom;
217       b.x = Ai[1][0] * OpenMM_NmPerAngstrom;
218       b.y = Ai[1][1] * OpenMM_NmPerAngstrom;
219       b.z = Ai[1][2] * OpenMM_NmPerAngstrom;
220       c.x = Ai[2][0] * OpenMM_NmPerAngstrom;
221       c.y = Ai[2][1] * OpenMM_NmPerAngstrom;
222       c.z = Ai[2][2] * OpenMM_NmPerAngstrom;
223       setDefaultPeriodicBoxVectors(a, b, c);
224     }
225   }
226 
227   /**
228    * Add forces to the system.
229    */
230   @Override
231   public void addForces() {
232     setDefaultPeriodicBoxVectors();
233 
234     // Set up rigid constraints.
235     // These flags need to be set before bonds and angles are created below.
236     boolean rigidHydrogen = forceField.getBoolean("RIGID_HYDROGEN", false);
237     boolean rigidBonds = forceField.getBoolean("RIGID_BONDS", false);
238     boolean rigidHydrogenAngles = forceField.getBoolean("RIGID_HYDROGEN_ANGLES", false);
239     if (rigidHydrogen || rigidBonds || rigidHydrogenAngles) {
240       logger.severe(" Dual Topology does not support rigid hydrogen atoms.");
241     }
242 
243     logger.info("\n Bonded Terms\n");
244 
245     // Add Bond Force.
246     bondForce = (BondForce) BondForce.constructForce(0, openMMDualTopologyEnergy);
247     bondForce2 = (BondForce) BondForce.constructForce(1, openMMDualTopologyEnergy);
248     addForce(bondForce);
249     addForce(bondForce2);
250 
251     // Add Angle Force.
252     angleForce = (AngleForce) AngleForce.constructForce(0, openMMDualTopologyEnergy);
253     angleForce2 = (AngleForce) AngleForce.constructForce(1, openMMDualTopologyEnergy);
254     addForce(angleForce);
255     addForce(angleForce2);
256 
257     // Add In-Plane Angle Force.
258     inPlaneAngleForce = (InPlaneAngleForce) InPlaneAngleForce.constructForce(0, openMMDualTopologyEnergy);
259     inPlaneAngleForce2 = (InPlaneAngleForce) InPlaneAngleForce.constructForce(1, openMMDualTopologyEnergy);
260     addForce(inPlaneAngleForce);
261     addForce(inPlaneAngleForce2);
262 
263     // Add Stretch-Bend Force.
264     stretchBendForce = (StretchBendForce) StretchBendForce.constructForce(0, openMMDualTopologyEnergy);
265     stretchBendForce2 = (StretchBendForce) StretchBendForce.constructForce(1, openMMDualTopologyEnergy);
266     addForce(stretchBendForce);
267     addForce(stretchBendForce2);
268 
269     // Add Urey-Bradley Force.
270     ureyBradleyForce = (UreyBradleyForce) UreyBradleyForce.constructForce(0, openMMDualTopologyEnergy);
271     ureyBradleyForce2 = (UreyBradleyForce) UreyBradleyForce.constructForce(1, openMMDualTopologyEnergy);
272     addForce(ureyBradleyForce);
273     addForce(ureyBradleyForce2);
274 
275     // Add Out-of-Plane Bend Force.
276     outOfPlaneBendForce = (OutOfPlaneBendForce) OutOfPlaneBendForce.constructForce(0, openMMDualTopologyEnergy);
277     outOfPlaneBendForce2 = (OutOfPlaneBendForce) OutOfPlaneBendForce.constructForce(1, openMMDualTopologyEnergy);
278     addForce(outOfPlaneBendForce);
279     addForce(outOfPlaneBendForce2);
280 
281     // Add Pi-Torsion Force.
282     piOrbitalTorsionForce = (PiOrbitalTorsionForce) PiOrbitalTorsionForce.constructForce(0, openMMDualTopologyEnergy);
283     piOrbitalTorsionForce2 = (PiOrbitalTorsionForce) PiOrbitalTorsionForce.constructForce(1, openMMDualTopologyEnergy);
284     addForce(piOrbitalTorsionForce);
285     addForce(piOrbitalTorsionForce2);
286 
287     // Add Torsion Force.
288     torsionForce = (TorsionForce) TorsionForce.constructForce(0, openMMDualTopologyEnergy);
289     torsionForce2 = (TorsionForce) TorsionForce.constructForce(1, openMMDualTopologyEnergy);
290     addForce(torsionForce);
291     addForce(torsionForce2);
292 
293     // Add Improper Torsion Force.
294     improperTorsionForce = (ImproperTorsionForce) ImproperTorsionForce.constructForce(0, openMMDualTopologyEnergy);
295     improperTorsionForce2 = (ImproperTorsionForce) ImproperTorsionForce.constructForce(1, openMMDualTopologyEnergy);
296     addForce(improperTorsionForce);
297     addForce(improperTorsionForce2);
298 
299     // Add Stretch-Torsion coupling terms.
300     stretchTorsionForce = (StretchTorsionForce) StretchTorsionForce.constructForce(0, openMMDualTopologyEnergy);
301     stretchTorsionForce2 = (StretchTorsionForce) StretchTorsionForce.constructForce(1, openMMDualTopologyEnergy);
302     addForce(stretchTorsionForce);
303     addForce(stretchTorsionForce2);
304 
305     // Add Angle-Torsion coupling terms.
306     angleTorsionForce = (AngleTorsionForce) AngleTorsionForce.constructForce(0, openMMDualTopologyEnergy);
307     angleTorsionForce2 = (AngleTorsionForce) AngleTorsionForce.constructForce(1, openMMDualTopologyEnergy);
308     addForce(angleTorsionForce);
309     addForce(angleTorsionForce2);
310 
311     if (openMMDualTopologyEnergy.getForceFieldEnergy(0).getRestrainMode() == ForceFieldEnergy.RestrainMode.ALCHEMICAL) {
312       // Add Restrain-Torsions Force
313       restrainTorsionsForce = (RestrainTorsionsForce) RestrainTorsionsForce.constructForce(0, openMMDualTopologyEnergy);
314       restrainTorsionsForce2 = (RestrainTorsionsForce) RestrainTorsionsForce.constructForce(1, openMMDualTopologyEnergy);
315       addForce(restrainTorsionsForce);
316       addForce(restrainTorsionsForce2);
317     }
318 
319     // Add Torsion-Torsion Force.
320     // ToDo: There is no updateParametersInContext method for the AmoebaTorsionTorsionForce.
321     amoebaTorsionTorsionForce = (AmoebaTorsionTorsionForce) AmoebaTorsionTorsionForce.constructForce(0, openMMDualTopologyEnergy);
322     addForce(amoebaTorsionTorsionForce);
323     // amoebaTorsionTorsionForce2 = (AmoebaTorsionTorsionForce) AmoebaTorsionTorsionForce.constructForce(1, openMMDualTopologyEnergy);
324     // addForce(amoebaTorsionTorsionForce2);
325 
326     // Check that the number of Torsion-Torsion forces in the two topologies is equal.
327     TorsionTorsionPotentialEnergy torsionTorsionPotentialEnergy = openMMDualTopologyEnergy.getForceFieldEnergy(0).getTorsionTorsionPotentialEnergy();
328     TorsionTorsionPotentialEnergy torsionTorsionPotentialEnergy2 = openMMDualTopologyEnergy.getForceFieldEnergy(0).getTorsionTorsionPotentialEnergy();
329     int numTorsionTorsions = 0;
330     int numTorsionTorsions2 = 0;
331     if (torsionTorsionPotentialEnergy != null) {
332       numTorsionTorsions = torsionTorsionPotentialEnergy.getNumberOfTorsionTorsions();
333     }
334     if (torsionTorsionPotentialEnergy2 != null) {
335       numTorsionTorsions2 = torsionTorsionPotentialEnergy2.getNumberOfTorsionTorsions();
336     }
337     if (numTorsionTorsions != numTorsionTorsions2) {
338       logger.severe(" The number of Torsion-Torsion forces in the two topologies do not match: "
339           + numTorsionTorsions + " vs. " + numTorsionTorsions2);
340     } else if (numTorsionTorsions != 0) {
341       TorsionTorsion[] torsionTorsion = torsionTorsionPotentialEnergy.getTorsionTorsionArray();
342       TorsionTorsion[] torsionTorsion2 = torsionTorsionPotentialEnergy2.getTorsionTorsionArray();
343       // Check that the Torsion-Torsion instances match.
344       for (int i = 0; i < numTorsionTorsions; i++) {
345         TorsionTorsion tt1 = torsionTorsion[i];
346         TorsionTorsion tt2 = torsionTorsion2[i];
347         if (!tt1.equals(tt2)) {
348           logger.severe(" The Torsion-Torsion terms in the two topologies do not match: " + tt1 + " vs. " + tt2);
349         }
350       }
351     }
352 
353     VanDerWaals vdW1 = forceFieldEnergy.getVdwNode();
354     VanDerWaals vdW2 = forceFieldEnergy2.getVdwNode();
355     if (vdW1 != null || vdW2 != null) {
356       logger.info("\n Non-Bonded Terms");
357 
358       if (vdW1 != null) {
359         // Add the vdW Force for Topology 1.
360         amoebaVDWForce = (AmoebaVdwForce) AmoebaVdwForce.constructForce(0, openMMDualTopologyEnergy);
361         addForce(amoebaVDWForce);
362       }
363       if (vdW2 != null) {
364         // Add the vdW Force for Topology 2.
365         amoebaVDWForce2 = (AmoebaVdwForce) AmoebaVdwForce.constructForce(1, openMMDualTopologyEnergy);
366         amoebaVDWForce2.setLambdaName("AmoebaVdwLambda2");
367         addForce(amoebaVDWForce2);
368       }
369 
370       ParticleMeshEwald pme = forceFieldEnergy.getPmeNode();
371       ParticleMeshEwald pme2 = forceFieldEnergy2.getPmeNode();
372       if (pme != null) {
373         amoebaMultipoleForce = (AmoebaMultipoleForce) AmoebaMultipoleForce.constructForce(0, openMMDualTopologyEnergy);
374         addForce(amoebaMultipoleForce);
375       }
376       if (pme2 != null) {
377         amoebaMultipoleForce2 = (AmoebaMultipoleForce) AmoebaMultipoleForce.constructForce(1, openMMDualTopologyEnergy);
378         addForce(amoebaMultipoleForce2);
379       }
380 
381     }
382   }
383 
384   /**
385    * Get the number of variables.
386    *
387    * @return the number of variables.
388    */
389   @Override
390   public int getNumberOfVariables() {
391     int nActive = 0;
392     int nAtoms = atoms.length;
393     for (int i = 0; i < nAtoms; i++) {
394       if (atoms[i].isActive()) {
395         nActive++;
396       }
397     }
398     int ret = nActive * 3;
399     return ret;
400   }
401 
402   /**
403    * Calculate the number of degrees of freedom.
404    *
405    * @return Number of degrees of freedom.
406    */
407   @Override
408   public int calculateDegreesOfFreedom() {
409     int dof = getNumberOfVariables();
410 
411     // Remove OpenMM constraints.
412     dof = dof - getNumConstraints();
413 
414     // Remove center of mass motion.
415     if (cmMotionRemover != null) {
416       dof -= 3;
417     }
418 
419     return dof;
420   }
421 
422   /**
423    * Destroy the dual-topology system.
424    */
425   @Override
426   public void free() {
427     if (getPointer() != null) {
428       logger.fine(" Free OpenMM dual-topology system.");
429       destroy();
430       logger.fine(" Free OpenMM dual-topology system completed.");
431     }
432   }
433 
434   /**
435    * Update parameters if the Use flags and/or Lambda value has changed.
436    *
437    * @param atoms Atoms in this list are considered.
438    */
439   @Override
440   public void updateParameters(@Nullable Atom[] atoms) {
441 
442 //    if (updateBondedTerms) {
443     if (bondForce != null) {
444       bondForce.updateForce(0, openMMDualTopologyEnergy);
445     }
446     if (bondForce2 != null) {
447       bondForce2.updateForce(1, openMMDualTopologyEnergy);
448     }
449     if (angleForce != null) {
450       angleForce.updateForce(0, openMMDualTopologyEnergy);
451     }
452     if (angleForce2 != null) {
453       angleForce2.updateForce(1, openMMDualTopologyEnergy);
454     }
455     if (inPlaneAngleForce != null) {
456       inPlaneAngleForce.updateForce(0, openMMDualTopologyEnergy);
457     }
458     if (inPlaneAngleForce2 != null) {
459       inPlaneAngleForce2.updateForce(1, openMMDualTopologyEnergy);
460     }
461     if (stretchBendForce != null) {
462       stretchBendForce.updateForce(0, openMMDualTopologyEnergy);
463     }
464     if (stretchBendForce2 != null) {
465       stretchBendForce2.updateForce(1, openMMDualTopologyEnergy);
466     }
467     if (ureyBradleyForce != null) {
468       ureyBradleyForce.updateForce(0, openMMDualTopologyEnergy);
469     }
470     if (ureyBradleyForce2 != null) {
471       ureyBradleyForce2.updateForce(1, openMMDualTopologyEnergy);
472     }
473     if (outOfPlaneBendForce != null) {
474       outOfPlaneBendForce.updateForce(0, openMMDualTopologyEnergy);
475     }
476     if (outOfPlaneBendForce2 != null) {
477       outOfPlaneBendForce2.updateForce(1, openMMDualTopologyEnergy);
478     }
479     if (piOrbitalTorsionForce != null) {
480       piOrbitalTorsionForce.updateForce(0, openMMDualTopologyEnergy);
481     }
482     if (piOrbitalTorsionForce2 != null) {
483       piOrbitalTorsionForce2.updateForce(1, openMMDualTopologyEnergy);
484     }
485     if (torsionForce != null) {
486       torsionForce.updateForce(0, openMMDualTopologyEnergy);
487     }
488     if (torsionForce2 != null) {
489       torsionForce2.updateForce(1, openMMDualTopologyEnergy);
490     }
491     if (improperTorsionForce != null) {
492       improperTorsionForce.updateForce(0, openMMDualTopologyEnergy);
493     }
494     if (improperTorsionForce2 != null) {
495       improperTorsionForce2.updateForce(1, openMMDualTopologyEnergy);
496     }
497     if (stretchTorsionForce != null) {
498       stretchTorsionForce.updateForce(0, openMMDualTopologyEnergy);
499     }
500     if (stretchTorsionForce2 != null) {
501       stretchTorsionForce2.updateForce(1, openMMDualTopologyEnergy);
502     }
503     if (angleTorsionForce != null) {
504       angleTorsionForce.updateForce(0, openMMDualTopologyEnergy);
505     }
506     if (angleTorsionForce2 != null) {
507       angleTorsionForce2.updateForce(1, openMMDualTopologyEnergy);
508     }
509     if (restrainTorsionsForce != null) {
510       restrainTorsionsForce.updateForce(0, openMMDualTopologyEnergy);
511     }
512     if (restrainTorsionsForce2 != null) {
513       restrainTorsionsForce2.updateForce(1, openMMDualTopologyEnergy);
514     }
515 
516     // ToDo: there is no support in the OpenMM AmoebaTorsionTorsionForce to updateParametersInContext.
517 
518     if (amoebaVDWForce != null) {
519       VanDerWaals vanDerWaals = forceFieldEnergy.getVdwNode();
520       if (vanDerWaals.getLambdaTerm()) {
521         double lambdaVDW = vanDerWaals.getLambda();
522         openMMDualTopologyEnergy.getContext().setParameter("AmoebaVdwLambda", lambdaVDW);
523       }
524       atoms = forceFieldEnergy.getAtomArray();
525       amoebaVDWForce.updateForce(atoms, 0, openMMDualTopologyEnergy);
526     }
527     if (amoebaVDWForce2 != null) {
528       VanDerWaals vanDerWaals = forceFieldEnergy2.getVdwNode();
529       if (vanDerWaals.getLambdaTerm()) {
530         double lambdaVDW = vanDerWaals.getLambda();
531         openMMDualTopologyEnergy.getContext().setParameter("AmoebaVdwLambda2", lambdaVDW);
532       }
533       atoms = forceFieldEnergy2.getAtomArray();
534       amoebaVDWForce2.updateForce(atoms, 1, openMMDualTopologyEnergy);
535     }
536     if (amoebaMultipoleForce != null) {
537       atoms = forceFieldEnergy.getAtomArray();
538       amoebaMultipoleForce.updateForce(atoms, 0, openMMDualTopologyEnergy);
539     }
540     if (amoebaMultipoleForce2 != null) {
541       atoms = forceFieldEnergy2.getAtomArray();
542       amoebaMultipoleForce2.updateForce(atoms, 1, openMMDualTopologyEnergy);
543     }
544   }
545 }