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.parameters;
39  
40  import ffx.utilities.FFXProperty;
41  import org.w3c.dom.Document;
42  import org.w3c.dom.Element;
43  
44  import java.util.Comparator;
45  import java.util.Map;
46  import java.util.Objects;
47  import java.util.logging.Level;
48  import java.util.logging.Logger;
49  
50  import static ffx.potential.parameters.ForceField.ForceFieldType.VDW;
51  import static ffx.potential.parameters.ForceField.ForceFieldType.VDW14;
52  import static ffx.utilities.Constants.ANG_TO_NM;
53  import static ffx.utilities.Constants.KCAL_TO_KJ;
54  import static ffx.utilities.PropertyGroup.PotentialFunctionParameter;
55  import static java.lang.Double.parseDouble;
56  import static java.lang.Integer.parseInt;
57  import static java.lang.StrictMath.abs;
58  import static java.lang.String.format;
59  import static java.lang.String.valueOf;
60  
61  /**
62   * The VDWType class defines van der Waals type for a normal interaction or a special 1-4
63   * interaction.
64   *
65   * @author Michael J. Schnieders
66   * @since 1.0
67   */
68  @FFXProperty(name = "vdw", clazz = String.class, propertyGroup = PotentialFunctionParameter,
69      description = """
70          [1 integer and 3 reals]
71          Provides values for a single van der Waals parameter. The integer modifier, if positive,
72          gives the atom class number for which vdw parameters are to be defined. Note that vdw parameters are given for atom classes, not atom types.
73          The three real number modifiers give the values of the atom size in Angstroms, homoatomic well depth in kcal/mole,
74          and an optional reduction factor for univalent atoms.
75          """)
76  @FFXProperty(name = "vdw14", clazz = String.class, propertyGroup = PotentialFunctionParameter,
77      description = """
78          [1 integer and 2 reals]
79          Provides values for a single van der Waals parameter for use in 1-4 nonbonded interactions.
80          The integer modifier, if positive, gives the atom class number for which vdw parameters are to be defined.
81          Note that vdw parameters are given for atom classes, not atom types.
82          The two real number modifiers give the values of the atom size in Angstroms and the homoatomic well depth in kcal/mole.
83          Reduction factors, if used, are carried over from the vdw keyword for the same atom class.
84          """)
85  public final class VDWType extends BaseType implements Comparator<String> {
86  
87    private static final Logger logger = Logger.getLogger(VDWType.class.getName());
88    /**
89     * The default gamma parameter in Halgren’s buffered 14-7 vdw potential energy functional form.
90     */
91    public static final double DEFAULT_GAMMA = 0.12;
92    /**
93     * The default delta parameter in Halgren’s buffered 14-7 vdw potential energy functional form.
94     */
95    public static final double DEFAULT_DELTA = 0.07;
96    /**
97     * The default epsilon combining rule.
98     */
99    public static final EPSILON_RULE DEFAULT_EPSILON_RULE = EPSILON_RULE.GEOMETRIC;
100   /**
101    * The default radius combining rule.
102    */
103   public static final RADIUS_RULE DEFAULT_RADIUS_RULE = RADIUS_RULE.ARITHMETIC;
104   /**
105    * The default radius size.
106    */
107   public static final RADIUS_SIZE DEFAULT_RADIUS_SIZE = RADIUS_SIZE.RADIUS;
108   /**
109    * The default radius type.
110    */
111   public static final RADIUS_TYPE DEFAULT_RADIUS_TYPE = RADIUS_TYPE.R_MIN;
112   /**
113    * The default van der Waals functional form type.
114    */
115   public static final VDW_TYPE DEFAULT_VDW_TYPE = VDW_TYPE.LENNARD_JONES;
116   /**
117    * The default van der Waals scale factor for 1-2 (bonded) interactions.
118    */
119   public static final double DEFAULT_VDW_12_SCALE = 0.0;
120   /**
121    * The default van der Waals scale factor for 1-3 (angle) interactions.
122    */
123   public static final double DEFAULT_VDW_13_SCALE = 0.0;
124   /**
125    * The default van der Waals scale factor for 1-4 (torisonal) interactions.
126    */
127   public static final double DEFAULT_VDW_14_SCALE = 1.0;
128 
129   /**
130    * The radius of the minimum well depth energy (angstroms).
131    */
132   public final double radius;
133   /**
134    * The minimum energy of the vdw function (kcal/mol).
135    */
136   public final double wellDepth;
137   /**
138    * Reduction factor for evaluating van der Waals pairs. Valid range: 0.0 .GT. reduction .LE. 1.0
139    * Usually only hydrogen atoms have a reduction factor. Setting the reduction to .LT. 0.0 indicates
140    * it is not being used.
141    */
142   public final double reductionFactor;
143   /**
144    * The atom class that uses this van der Waals parameter.
145    */
146   public int atomClass;
147   /**
148    * Is this a normal vdW parameter or is it for 1-4 interactions.
149    */
150   private final VDWMode vdwMode;
151 
152   /**
153    * van der Waals constructor. If the reduction factor is .LE. 0.0, no reduction is used for this
154    * atom type.
155    *
156    * @param atomClass       The atom class that uses this van der Waals parameter.
157    * @param radius          The radius of the minimum well depth energy (angstroms).
158    * @param wellDepth       The minimum energy of the vdw function (kcal/mol).
159    * @param reductionFactor Reduction factor for evaluating van der Waals pairs.
160    */
161   public VDWType(int atomClass, double radius, double wellDepth, double reductionFactor) {
162     this(atomClass, radius, wellDepth, reductionFactor, VDWMode.NORMAL);
163   }
164 
165   /**
166    * van der Waals constructor. If the reduction factor is .LE. 0.0, no reduction is used for this
167    * atom type.
168    *
169    * @param atomClass       The atom class that uses this van der Waals parameter.
170    * @param radius          The radius of the minimum well depth energy (angstroms).
171    * @param wellDepth       The minimum energy of the vdw function (kcal/mol).
172    * @param reductionFactor Reduction factor for evaluating van der Waals pairs.
173    * @param vdwMode         The VDWMode to use.
174    */
175   public VDWType(int atomClass, double radius, double wellDepth, double reductionFactor,
176                  VDWMode vdwMode) {
177     super(VDW, Integer.toString(atomClass));
178     this.atomClass = atomClass;
179     this.radius = radius;
180     this.wellDepth = abs(wellDepth);
181     this.reductionFactor = reductionFactor;
182     this.vdwMode = vdwMode;
183     if (vdwMode == VDWMode.VDW14) {
184       forceFieldType = VDW14;
185     }
186   }
187 
188   /**
189    * Average two VDWType objects.
190    *
191    * @param vdwType1  The first VDWType.
192    * @param vdwType2  The second VDWType.
193    * @param atomClass The new atom class.
194    * @return The new averaged VDWType.
195    */
196   public static VDWType average(VDWType vdwType1, VDWType vdwType2, int atomClass) {
197     if (vdwType1 == null || vdwType2 == null) {
198       return null;
199     }
200     double radius = (vdwType1.radius + vdwType2.radius) / 2.0;
201     double wellDepth = (vdwType1.wellDepth + vdwType2.wellDepth) / 2.0;
202     double reductionFactor = (vdwType1.reductionFactor + vdwType2.reductionFactor) / 2.0;
203     return new VDWType(atomClass, radius, wellDepth, reductionFactor);
204   }
205 
206   /**
207    * Construct a VDWType from multiple input lines.
208    *
209    * @param input  The overall input String.
210    * @param tokens The input String tokenized.
211    * @return a VDWType instance.
212    */
213   public static VDWType parse(String input, String[] tokens) {
214     if (tokens.length < 4) {
215       logger.log(Level.WARNING, "Invalid VDW type:\n{0}", input);
216     } else {
217       try {
218         int atomType = parseInt(tokens[1]);
219         double radius = parseDouble(tokens[2]);
220         double wellDepth = parseDouble(tokens[3]);
221         double reductionFactor = -1.0;
222         if (tokens.length == 5) {
223           reductionFactor = parseDouble(tokens[4]);
224         }
225         return new VDWType(atomType, radius, wellDepth, reductionFactor);
226       } catch (NumberFormatException e) {
227         String message = "Exception parsing VDW type:\n" + input + "\n";
228         logger.log(Level.SEVERE, message, e);
229       }
230     }
231     return null;
232   }
233 
234   /**
235    * Construct a 1-4 VDWType from multiple input lines.
236    *
237    * @param input  The overall input String.
238    * @param tokens The input String tokenized.
239    * @return a VDWType instance.
240    */
241   public static VDWType parseVDW14(String input, String[] tokens) {
242     if (tokens.length < 4) {
243       logger.log(Level.WARNING, "Invalid VDW type:\n{0}", input);
244     } else {
245       try {
246         int atomType = parseInt(tokens[1]);
247         double radius = parseDouble(tokens[2]);
248         double wellDepth = parseDouble(tokens[3]);
249         double reductionFactor = -1.0;
250         if (tokens.length == 5) {
251           reductionFactor = parseDouble(tokens[4]);
252         }
253         return new VDWType(atomType, radius, wellDepth, reductionFactor, VDWMode.VDW14);
254       } catch (NumberFormatException e) {
255         String message = "Exception parsing VDW14 type:\n" + input + "\n";
256         logger.log(Level.SEVERE, message, e);
257       }
258     }
259     return null;
260   }
261 
262   /**
263    * {@inheritDoc}
264    */
265   @Override
266   public int compare(String s1, String s2) {
267     int t1 = parseInt(s1);
268     int t2 = parseInt(s2);
269     return Integer.compare(t1, t2);
270   }
271 
272   /**
273    * {@inheritDoc}
274    */
275   @Override
276   public boolean equals(Object o) {
277     if (this == o) {
278       return true;
279     }
280     if (o == null || getClass() != o.getClass()) {
281       return false;
282     }
283     VDWType vdwType = (VDWType) o;
284     return (vdwType.atomClass == this.atomClass);
285   }
286 
287   /**
288    * {@inheritDoc}
289    */
290   @Override
291   public int hashCode() {
292     return Objects.hash(atomClass);
293   }
294 
295   /**
296    * {@inheritDoc}
297    *
298    * <p>Nicely formatted van der Waals type.
299    */
300   @Override
301   public String toString() {
302     StringBuilder vdwString = new StringBuilder("vdw");
303     if (vdwMode == VDWMode.VDW14) {
304       vdwString.append("14");
305     }
306 
307     // No reduction factor.
308     if (reductionFactor <= 0e0) {
309       vdwString.append(format("  %5d  %11.9f  %11.9f", atomClass, radius, wellDepth));
310     } else {
311       vdwString.append(
312           format("  %5d  %11.9f  %11.9f  %5.3f", atomClass, radius, wellDepth, reductionFactor));
313     }
314 
315     return vdwString.toString();
316   }
317 
318   /**
319    * Create an AmoebaVdwForce force node.
320    *
321    * @param doc        the Document instance.
322    * @param forceField the ForceField to collect constants from.
323    * @return The AmoebaVdwForce force node.
324    */
325   public static Element getXMLForce(Document doc, ForceField forceField) {
326     Map<String, VDWType> vdwTypes = forceField.getVDWTypes();
327     Map<String, VDWPairType> vdwPairTypes = forceField.getVDWPairTypes();
328     if (!vdwTypes.values().isEmpty() || !vdwPairTypes.values().isEmpty()) {
329       Element node = doc.createElement("AmoebaVdwForce");
330       node.setAttribute("type", forceField.getString("vdwtype", DEFAULT_VDW_TYPE.toString()));
331       node.setAttribute("radiusrule", forceField.getString("radiusrule", DEFAULT_RADIUS_RULE.toString()));
332       node.setAttribute("radiustype", forceField.getString("radiustype", DEFAULT_RADIUS_TYPE.toString()));
333       node.setAttribute("radiussize", forceField.getString("radiussize", DEFAULT_RADIUS_SIZE.toString()));
334       node.setAttribute("epsilonrule", forceField.getString("epsilonrule", DEFAULT_EPSILON_RULE.toString()));
335       // There is not currently support for a 1-2 scale in OpenMM (this is always zero).
336       node.setAttribute("vdw-13-scale", valueOf(forceField.getDouble("vdw-13-scale", DEFAULT_VDW_13_SCALE)));
337       node.setAttribute("vdw-14-scale", valueOf(forceField.getDouble("vdw-14-scale", DEFAULT_VDW_14_SCALE)));
338       node.setAttribute("vdw-15-scale", valueOf(forceField.getDouble("vdw-15-scale", 1.0)));
339       for (VDWType vdwType : vdwTypes.values()) {
340         node.appendChild(vdwType.toXML(doc));
341       }
342       for (VDWPairType vdwPairType : vdwPairTypes.values()) {
343         node.appendChild(vdwPairType.toXML(doc));
344       }
345       return node;
346     }
347     return null;
348   }
349 
350   /**
351    * Write VDWType to OpenMM XML format.
352    */
353   public Element toXML(Document doc) {
354     Element node = doc.createElement("Vdw");
355     node.setAttribute("class", format("%d", atomClass));
356     // Convert Angstroms to nm
357     node.setAttribute("sigma", format("%f", radius * ANG_TO_NM));
358     // Convert Kcal/mol to KJ/mol
359     node.setAttribute("epsilon", format("%f", wellDepth * KCAL_TO_KJ));
360     if (reductionFactor <= 0e0) {
361       node.setAttribute("reduction", "1.0");
362     } else {
363       node.setAttribute("reduction", format("%f", reductionFactor));
364     }
365     return node;
366   }
367 
368   /**
369    * Increment the atom class by a specified amount.
370    *
371    * @param increment The increment to add to the atom class.
372    */
373   void incrementClass(int increment) {
374     atomClass += increment;
375     setKey(Integer.toString(atomClass));
376   }
377 
378   /**
379    * Torsion modes include Normal or In-Plane
380    */
381   public enum VDWMode {
382     NORMAL, VDW14
383   }
384 
385   /**
386    * VDW Type.
387    */
388   public enum VDW_TYPE {
389     BUFFERED_14_7,
390     LENNARD_JONES;
391 
392     public String toString() {
393       return name().replace("_", "-");
394     }
395   }
396 
397   /**
398    * Radius combining rule.
399    */
400   public enum RADIUS_RULE {
401     ARITHMETIC,
402     CUBIC_MEAN,
403     GEOMETRIC;
404 
405     public String toString() {
406       return name().replace("_", "-");
407     }
408   }
409 
410   /**
411    * Radius size in the parameter file (radius or diameter).
412    */
413   public enum RADIUS_SIZE {
414     DIAMETER,
415     RADIUS
416   }
417 
418   /**
419    * Radius type in the parameter file (R-Min or Sigma).
420    */
421   public enum RADIUS_TYPE {
422     R_MIN,
423     SIGMA;
424 
425     public String toString() {
426       return name().replace("_", "-");
427     }
428   }
429 
430   /**
431    * Epsilon combining rule.
432    */
433   public enum EPSILON_RULE {
434     GEOMETRIC,
435     HHG,
436     W_H;
437 
438     public String toString() {
439       return name().replace("_", "-");
440     }
441   }
442 }