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-2023.
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.crystal.Crystal;
41  import ffx.potential.bonded.Atom;
42  import ffx.potential.extended.ExtendedSystem;
43  import ffx.potential.nonbonded.NonbondedCutoff;
44  import ffx.potential.nonbonded.VanDerWaals;
45  import ffx.potential.nonbonded.VanDerWaalsForm;
46  import ffx.potential.parameters.ForceField;
47  import ffx.potential.parameters.VDWPairType;
48  import ffx.potential.parameters.VDWType;
49  
50  import java.util.HashMap;
51  import java.util.Map;
52  import java.util.logging.Level;
53  import java.util.logging.Logger;
54  
55  import static edu.uiowa.jopenmm.OpenMMAmoebaLibrary.OpenMM_AmoebaVdwForce_AlchemicalMethod.OpenMM_AmoebaVdwForce_Decouple;
56  import static edu.uiowa.jopenmm.OpenMMAmoebaLibrary.OpenMM_AmoebaVdwForce_NonbondedMethod.OpenMM_AmoebaVdwForce_CutoffPeriodic;
57  import static edu.uiowa.jopenmm.OpenMMAmoebaLibrary.OpenMM_AmoebaVdwForce_NonbondedMethod.OpenMM_AmoebaVdwForce_NoCutoff;
58  import static edu.uiowa.jopenmm.OpenMMAmoebaLibrary.OpenMM_AmoebaVdwForce_addParticleType;
59  import static edu.uiowa.jopenmm.OpenMMAmoebaLibrary.OpenMM_AmoebaVdwForce_addParticle_1;
60  import static edu.uiowa.jopenmm.OpenMMAmoebaLibrary.OpenMM_AmoebaVdwForce_addTypePair;
61  import static edu.uiowa.jopenmm.OpenMMAmoebaLibrary.OpenMM_AmoebaVdwForce_create;
62  import static edu.uiowa.jopenmm.OpenMMAmoebaLibrary.OpenMM_AmoebaVdwForce_setAlchemicalMethod;
63  import static edu.uiowa.jopenmm.OpenMMAmoebaLibrary.OpenMM_AmoebaVdwForce_setCutoffDistance;
64  import static edu.uiowa.jopenmm.OpenMMAmoebaLibrary.OpenMM_AmoebaVdwForce_setNonbondedMethod;
65  import static edu.uiowa.jopenmm.OpenMMAmoebaLibrary.OpenMM_AmoebaVdwForce_setParticleExclusions;
66  import static edu.uiowa.jopenmm.OpenMMAmoebaLibrary.OpenMM_AmoebaVdwForce_setParticleParameters;
67  import static edu.uiowa.jopenmm.OpenMMAmoebaLibrary.OpenMM_AmoebaVdwForce_setSoftcoreAlpha;
68  import static edu.uiowa.jopenmm.OpenMMAmoebaLibrary.OpenMM_AmoebaVdwForce_setSoftcorePower;
69  import static edu.uiowa.jopenmm.OpenMMAmoebaLibrary.OpenMM_AmoebaVdwForce_setUseDispersionCorrection;
70  import static edu.uiowa.jopenmm.OpenMMAmoebaLibrary.OpenMM_AmoebaVdwForce_updateParametersInContext;
71  import static edu.uiowa.jopenmm.OpenMMAmoebaLibrary.OpenMM_KJPerKcal;
72  import static edu.uiowa.jopenmm.OpenMMAmoebaLibrary.OpenMM_NmPerAngstrom;
73  import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_Boolean.OpenMM_False;
74  import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_Boolean.OpenMM_True;
75  import static java.lang.String.format;
76  
77  /**
78   * The Amoeba vdW Force.
79   */
80  public class AmoebaVDWForce extends OpenMMForce {
81  
82    private static final Logger logger = Logger.getLogger(AmoebaVDWForce.class.getName());
83  
84    /**
85     * The vdW class used to specify no vdW interactions for an atom will be Zero
86     * if all atom classes are greater than zero.
87     * <p>
88     * Otherwise:
89     * vdWClassForNoInteraction = min(atomClass) - 1
90     */
91    private int vdWClassForNoInteraction = 0;
92  
93    /**
94     * A map from vdW class values to OpenMM vdW types.
95     */
96    private final Map<Integer, Integer> vdwClassToOpenMMType = new HashMap<>();
97  
98    /**
99     * The Amoeba vdW Force constructor.
100    *
101    * @param openMMEnergy The OpenMM Energy instance that contains the vdW parameters.
102    */
103   public AmoebaVDWForce(OpenMMEnergy openMMEnergy) {
104     VanDerWaals vdW = openMMEnergy.getVdwNode();
105     if (vdW == null) {
106       return;
107     }
108 
109     forcePointer = OpenMM_AmoebaVdwForce_create();
110 
111     VanDerWaalsForm vdwForm = vdW.getVDWForm();
112     NonbondedCutoff nonbondedCutoff = vdW.getNonbondedCutoff();
113     Crystal crystal = openMMEnergy.getCrystal();
114 
115     double radScale = 1.0;
116     if (vdwForm.radiusSize == VanDerWaalsForm.RADIUS_SIZE.DIAMETER) {
117       radScale = 0.5;
118     }
119 
120     ForceField forceField = openMMEnergy.getMolecularAssembly().getForceField();
121 
122     Map<String, VDWType> vdwTypes = forceField.getVDWTypes();
123 
124     for (VDWType vdwType : vdwTypes.values()) {
125       int atomClass = vdwType.atomClass;
126       if (!vdwClassToOpenMMType.containsKey(atomClass)) {
127         double eps = OpenMM_KJPerKcal * vdwType.wellDepth;
128         double rad = OpenMM_NmPerAngstrom * vdwType.radius * radScale;
129         int type = addParticleType(rad, eps);
130         vdwClassToOpenMMType.put(atomClass, type);
131         if (atomClass <= vdWClassForNoInteraction) {
132           vdWClassForNoInteraction = atomClass - 1;
133         }
134       }
135     }
136 
137     // Add a special vdW type for zero vdW energy and forces (e.g. to support the FFX "use" flag).
138     int type = addParticleType(OpenMM_NmPerAngstrom, 0.0);
139     vdwClassToOpenMMType.put(vdWClassForNoInteraction, type);
140 
141     Map<String, VDWPairType> vdwPairTypeMap = forceField.getVDWPairTypes();
142     for (VDWPairType vdwPairType : vdwPairTypeMap.values()) {
143       int c1 = vdwPairType.atomClasses[0];
144       int c2 = vdwPairType.atomClasses[1];
145       int type1 = vdwClassToOpenMMType.get(c1);
146       int type2 = vdwClassToOpenMMType.get(c2);
147       double rMin = vdwPairType.radius * OpenMM_NmPerAngstrom;
148       double eps = vdwPairType.wellDepth * OpenMM_KJPerKcal;
149       addTypePair(type1, type2, rMin, eps);
150       addTypePair(type2, type1, rMin, eps);
151     }
152 
153     ExtendedSystem extendedSystem = vdW.getExtendedSystem();
154     double[] vdwPrefactorAndDerivs = new double[3];
155 
156     int[] ired = vdW.getReductionIndex();
157     Atom[] atoms = openMMEnergy.getMolecularAssembly().getAtomArray();
158     int nAtoms = atoms.length;
159     for (int i = 0; i < nAtoms; i++) {
160       Atom atom = atoms[i];
161       VDWType vdwType = atom.getVDWType();
162       int atomClass = vdwType.atomClass;
163       type = vdwClassToOpenMMType.get(atomClass);
164       int isAlchemical = atom.applyLambda() ? 1 : 0;
165       double scaleFactor = 1.0;
166       if (extendedSystem != null) {
167         extendedSystem.getVdwPrefactor(i, vdwPrefactorAndDerivs);
168         scaleFactor = vdwPrefactorAndDerivs[0];
169       }
170       addParticle_1(ired[i], type, vdwType.reductionFactor, isAlchemical, scaleFactor);
171     }
172 
173     setCutoffDistance(nonbondedCutoff.off * OpenMM_NmPerAngstrom);
174     if (vdW.getDoLongRangeCorrection()) {
175       setUseDispersionCorrection(OpenMM_True);
176     } else {
177       setUseDispersionCorrection(OpenMM_False);
178     }
179 
180     if (crystal.aperiodic()) {
181       setNonbondedMethod(OpenMM_AmoebaVdwForce_NoCutoff);
182     } else {
183       setNonbondedMethod(OpenMM_AmoebaVdwForce_CutoffPeriodic);
184     }
185 
186     if (openMMEnergy.getSystem().getVdwLambdaTerm()) {
187       setAlchemicalMethod(OpenMM_AmoebaVdwForce_Decouple);
188       setSoftcoreAlpha(vdW.getAlpha());
189       setSoftcorePower((int) vdW.getBeta());
190     }
191 
192     int[][] bondMask = vdW.getMask12();
193     int[][] angleMask = vdW.getMask13();
194 
195     // Create exclusion lists.
196     OpenMMIntArray exclusions = new OpenMMIntArray(0);
197     for (int i = 0; i < nAtoms; i++) {
198       exclusions.append(i);
199       final int[] bondMaski = bondMask[i];
200       for (int value : bondMaski) {
201         exclusions.append(value);
202       }
203       final int[] angleMaski = angleMask[i];
204       for (int value : angleMaski) {
205         exclusions.append(value);
206       }
207       setParticleExclusions(i, exclusions);
208       exclusions.resize(0);
209     }
210     exclusions.destroy();
211 
212     int forceGroup = forceField.getInteger("VDW_FORCE_GROUP", 1);
213     setForceGroup(forceGroup);
214     logger.log(Level.INFO, format("  AMOEBA van der Waals force \t\t%d", forceGroup));
215   }
216 
217   /**
218    * Convenience method to construct an AMOEBA vdW force.
219    *
220    * @param openMMEnergy The OpenMM Energy instance that contains the vdW information.
221    * @return An AMOEBA vdW Force, or null if there are no vdW interactions.
222    */
223   public static OpenMMForce constructForce(OpenMMEnergy openMMEnergy) {
224     VanDerWaals vdW = openMMEnergy.getVdwNode();
225     if (vdW == null) {
226       return null;
227     }
228     return new AmoebaVDWForce(openMMEnergy);
229   }
230 
231   /**
232    * Update the vdW force.
233    *
234    * @param atoms        The atoms to update.
235    * @param openMMEnergy The OpenMM Energy instance that contains the vdW parameters.
236    */
237   public void updateForce(Atom[] atoms, OpenMMEnergy openMMEnergy) {
238     VanDerWaals vdW = openMMEnergy.getVdwNode();
239     VanDerWaalsForm vdwForm = vdW.getVDWForm();
240     double radScale = 1.0;
241     if (vdwForm.radiusSize == VanDerWaalsForm.RADIUS_SIZE.DIAMETER) {
242       radScale = 0.5;
243     }
244 
245     ExtendedSystem extendedSystem = vdW.getExtendedSystem();
246     double[] vdwPrefactorAndDerivs = new double[3];
247 
248     int[] ired = vdW.getReductionIndex();
249     for (Atom atom : atoms) {
250       int index = atom.getXyzIndex() - 1;
251       VDWType vdwType = atom.getVDWType();
252 
253       // Get the OpenMM index for this vdW type.
254       int type = vdwClassToOpenMMType.get(vdwType.atomClass);
255       if (!atom.getUse()) {
256         // Get the OpenMM index for a special vdW type that has no interactions.
257         type = vdwClassToOpenMMType.get(vdWClassForNoInteraction);
258       }
259       int isAlchemical = atom.applyLambda() ? 1 : 0;
260       double eps = OpenMM_KJPerKcal * vdwType.wellDepth;
261       double rad = OpenMM_NmPerAngstrom * vdwType.radius * radScale;
262 
263       double scaleFactor = 1.0;
264       if (extendedSystem != null) {
265         extendedSystem.getVdwPrefactor(index, vdwPrefactorAndDerivs);
266         scaleFactor = vdwPrefactorAndDerivs[0];
267       }
268 
269       setParticleParameters(index, ired[index], rad, eps, vdwType.reductionFactor, isAlchemical, type, scaleFactor);
270     }
271     updateParametersInContext(openMMEnergy.getContext());
272   }
273 
274   /**
275    * Add a particle type to the vdW Force.
276    *
277    * @param rad The radius.
278    * @param eps The well depth.
279    * @return The type.
280    */
281   public int addParticleType(double rad, double eps) {
282     return OpenMM_AmoebaVdwForce_addParticleType(forcePointer, rad, eps);
283   }
284 
285   /**
286    * Add a type pair to the vdW Force.
287    *
288    * @param type1 The first type.
289    * @param type2 The second type.
290    * @param rad   The radius.
291    * @param eps   The well depth.
292    */
293   public void addTypePair(int type1, int type2, double rad, double eps) {
294     OpenMM_AmoebaVdwForce_addTypePair(forcePointer, type1, type2, rad, eps);
295   }
296 
297   /**
298    * Add a particle to the vdW Force.
299    *
300    * @param ired            The particle ired.
301    * @param type            The particle type.
302    * @param reductionFactor The reduction factor.
303    * @param isAlchemical    The alchemical flag.
304    * @param scaleFactor     The scale factor.
305    */
306   public void addParticle_1(int ired, int type, double reductionFactor, int isAlchemical, double scaleFactor) {
307     OpenMM_AmoebaVdwForce_addParticle_1(forcePointer, ired, type, reductionFactor, isAlchemical, scaleFactor);
308   }
309 
310   /**
311    * Set the particle parameters.
312    *
313    * @param index           The particle index.
314    * @param ired            The particle reduction index.
315    * @param rad             The radius.
316    * @param eps             The well depth.
317    * @param reductionFactor The reduction factor.
318    * @param isAlchemical    The alchemical flag.
319    * @param type            The type.
320    * @param scaleFactor     The scale factor.
321    */
322   public void setParticleParameters(int index, int ired, double rad, double eps, double reductionFactor,
323                                     int isAlchemical, int type, double scaleFactor) {
324     OpenMM_AmoebaVdwForce_setParticleParameters(forcePointer, index, ired, rad, eps, reductionFactor, isAlchemical, type, scaleFactor);
325   }
326 
327   /**
328    * Set the cutoff distance.
329    *
330    * @param cutoff The cutoff distance.
331    */
332   public void setCutoffDistance(double cutoff) {
333     OpenMM_AmoebaVdwForce_setCutoffDistance(forcePointer, cutoff);
334   }
335 
336   /**
337    * Set the vdW force to use a long-range dispersion correction.
338    *
339    * @param value The flag.
340    */
341   public void setUseDispersionCorrection(int value) {
342     OpenMM_AmoebaVdwForce_setUseDispersionCorrection(forcePointer, value);
343   }
344 
345   /**
346    * Set the non-bonded method.
347    *
348    * @param method The non-bonded method.
349    */
350   public void setNonbondedMethod(int method) {
351     OpenMM_AmoebaVdwForce_setNonbondedMethod(forcePointer, method);
352   }
353 
354   /**
355    * Set the alchemical method.
356    *
357    * @param method The alchemical method.
358    */
359   public void setAlchemicalMethod(int method) {
360     OpenMM_AmoebaVdwForce_setAlchemicalMethod(forcePointer, method);
361   }
362 
363   /**
364    * Set the softcore power.
365    *
366    * @param vdWSoftcoreAlpha The softcore power.
367    */
368   public void setSoftcoreAlpha(double vdWSoftcoreAlpha) {
369     OpenMM_AmoebaVdwForce_setSoftcoreAlpha(forcePointer, vdWSoftcoreAlpha);
370   }
371 
372   /**
373    * Set the softcore power.
374    *
375    * @param vdwSoftcorePower The softcore power.
376    */
377   public void setSoftcorePower(int vdwSoftcorePower) {
378     OpenMM_AmoebaVdwForce_setSoftcorePower(forcePointer, vdwSoftcorePower);
379   }
380 
381   /**
382    * Set the particle exclusions.
383    *
384    * @param i          The particle index.
385    * @param exclusions The exclusions.
386    */
387   public void setParticleExclusions(int i, OpenMMIntArray exclusions) {
388     OpenMM_AmoebaVdwForce_setParticleExclusions(forcePointer, i, exclusions.getPointer());
389   }
390 
391   /**
392    * Update the parameters in the OpenMM Context.
393    *
394    * @param openMMContext The OpenMM Context.
395    */
396   public void updateParametersInContext(OpenMMContext openMMContext) {
397     if (openMMContext.hasContextPointer()) {
398       OpenMM_AmoebaVdwForce_updateParametersInContext(forcePointer, openMMContext.getContextPointer());
399     }
400   }
401 
402 }