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