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.potential.parameters.ForceField.ForceFieldType;
41  import ffx.utilities.FFXProperty;
42  import org.w3c.dom.Document;
43  import org.w3c.dom.Element;
44  
45  import java.util.Arrays;
46  import java.util.Comparator;
47  import java.util.logging.Level;
48  import java.util.logging.Logger;
49  
50  import static ffx.utilities.Constants.ANG_TO_NM;
51  import static ffx.utilities.Constants.KCAL_TO_KJ;
52  import static ffx.utilities.PropertyGroup.PotentialFunctionParameter;
53  import static java.lang.Double.parseDouble;
54  import static java.lang.Integer.parseInt;
55  import static java.lang.String.format;
56  
57  /**
58   * The VDWPairType class defines a van der Waals Pair type.
59   *
60   * @author Michael J. Schnieders
61   * @since 1.0
62   */
63  @FFXProperty(name = "vdwpr", clazz = String.class, propertyGroup = PotentialFunctionParameter, description = """
64      [2 integers and 2 reals]
65      Provides the values for the vdw parameters for a single special heteroatomic pair of atoms.
66      The integer modifiers give the pair of atom class numbers for which special vdw parameters are to be defined.
67      The two real number modifiers give the values of the minimum energy contact distance in Angstroms and the well depth at the minimum distance in kcal/mole.
68      """)
69  public final class VDWPairType extends BaseType implements Comparator<String> {
70  
71    private static final Logger logger = Logger.getLogger(VDWPairType.class.getName());
72    /**
73     * The radius of the minimum well depth energy (angstroms).
74     */
75    public final double radius;
76    /**
77     * The minimum energy of the vdw function (kcal/mol).
78     */
79    public final double wellDepth;
80    /**
81     * Atom classes that form this bond stretch.
82     */
83    public final int[] atomClasses;
84  
85    /**
86     * van der Waals constructor. If the reduction factor is .LE. 0.0, no reduction is used for this
87     * atom type.
88     *
89     * @param atomClasses The atom class that uses this van der Waals Pair.
90     * @param radius      The radius of the minimum well depth energy (angstroms).
91     * @param wellDepth   The minimum energy of the vdw function (kcal/mol).
92     */
93    public VDWPairType(int[] atomClasses, double radius, double wellDepth) {
94      super(ForceFieldType.VDWPR, sortKey(atomClasses));
95      this.atomClasses = atomClasses;
96      this.radius = radius;
97      this.wellDepth = wellDepth;
98    }
99  
100   /**
101    * Average.
102    *
103    * @param vdwType1    a {@link VDWPairType} object.
104    * @param vdwType2    a {@link VDWPairType} object.
105    * @param atomClasses The atom classes that uses this van der Waals Pair.
106    * @return a {@link VDWPairType} object.
107    */
108   public static VDWPairType average(VDWPairType vdwType1, VDWPairType vdwType2, int[] atomClasses) {
109     if (vdwType1 == null || vdwType2 == null) {
110       return null;
111     }
112 
113     double radius = (vdwType1.radius + vdwType2.radius) / 2.0;
114     double wellDepth = (vdwType1.wellDepth + vdwType2.wellDepth) / 2.0;
115 
116     return new VDWPairType(atomClasses, radius, wellDepth);
117   }
118 
119   /**
120    * Construct a VDWPairType from multiple input lines.
121    *
122    * @param input  The overall input String.
123    * @param tokens The input String tokenized.
124    * @return a VDWType instance.
125    */
126   public static VDWPairType parse(String input, String[] tokens) {
127     if (tokens.length < 5) {
128       logger.log(Level.WARNING, "Invalid VDWPR type:\n{0}", input);
129     } else {
130       try {
131         int atomClass1 = parseInt(tokens[1]);
132         int atomClass2 = parseInt(tokens[2]);
133         double radius = parseDouble(tokens[3]);
134         double wellDepth = parseDouble(tokens[4]);
135         return new VDWPairType(new int[]{atomClass1, atomClass2}, radius, wellDepth);
136       } catch (NumberFormatException e) {
137         String message = "Exception parsing VDWPR type:\n" + input + "\n";
138         logger.log(Level.SEVERE, message, e);
139       }
140     }
141     return null;
142   }
143 
144   /**
145    * {@inheritDoc}
146    */
147   @Override
148   public int compare(String key1, String key2) {
149     String[] keys1 = key1.split(" ");
150     String[] keys2 = key2.split(" ");
151     int[] c1 = new int[2];
152     int[] c2 = new int[2];
153     for (int i = 0; i < 2; i++) {
154       c1[i] = Integer.parseInt(keys1[i]);
155       c2[i] = Integer.parseInt(keys2[i]);
156     }
157 
158     if (c1[0] < c2[0]) {
159       return -1;
160     } else if (c1[0] > c2[0]) {
161       return 1;
162     } else if (c1[1] < c2[1]) {
163       return -1;
164     } else if (c1[1] > c2[1]) {
165       return 1;
166     }
167 
168     return 0;
169   }
170 
171   /**
172    * {@inheritDoc}
173    */
174   @Override
175   public boolean equals(Object o) {
176     if (this == o) {
177       return true;
178     }
179     if (o == null || getClass() != o.getClass()) {
180       return false;
181     }
182     VDWPairType vdwPairType = (VDWPairType) o;
183     return Arrays.equals(atomClasses, vdwPairType.atomClasses);
184   }
185 
186   /**
187    * {@inheritDoc}
188    */
189   @Override
190   public int hashCode() {
191     return Arrays.hashCode(atomClasses);
192   }
193 
194   /**
195    * {@inheritDoc}
196    *
197    * <p>Nicely formatted van der Waals type.
198    */
199   @Override
200   public String toString() {
201     return format("vdwpr  %5d  %5d  %11.9f  %11.9f", atomClasses[0], atomClasses[1], radius, wellDepth);
202   }
203 
204   /**
205    * Write VDWPairType to OpenMM XML format.
206    */
207   public Element toXML(Document doc) {
208     Element node = doc.createElement("Pair");
209     node.setAttribute("class1", format("%d", atomClasses[0]));
210     node.setAttribute("class2", format("%d", atomClasses[1]));
211     // Convert Angstroms to nm.
212     node.setAttribute("sigma", format("%f", radius * ANG_TO_NM));
213     // Convert Kcal/mol to KJ/mol
214     node.setAttribute("epsilon", format("%f", wellDepth * KCAL_TO_KJ));
215     return node;
216   }
217 
218   /**
219    * This method sorts the atom classes as: min, max
220    *
221    * @param c atomClasses
222    * @return lookup key
223    */
224   public static String sortKey(int[] c) {
225     if (c == null || c.length != 2) {
226       return null;
227     }
228 
229     int temp;
230     if (c[1] <= c[0]) {
231       temp = c[1];
232       c[1] = c[0];
233       c[0] = temp;
234     }
235 
236     return c[0] + " " + c[1];
237   }
238 
239   /**
240    * Increment the atom classes by a specified amount.
241    *
242    * @param increment The increment to add to the atom classes.
243    */
244   public void incrementClasses(int increment) {
245     for (int i = 0; i < atomClasses.length; i++) {
246       atomClasses[i] += increment;
247     }
248     setKey(sortKey(atomClasses));
249   }
250 
251 }