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-2021.
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 static ffx.potential.parameters.ForceField.ForceFieldType.OPBEND;
41  import static ffx.utilities.KeywordGroup.EnergyUnitConversion;
42  import static ffx.utilities.KeywordGroup.LocalGeometryFunctionalForm;
43  import static ffx.utilities.KeywordGroup.PotentialFunctionParameter;
44  import static java.lang.Double.parseDouble;
45  import static java.lang.Integer.parseInt;
46  import static java.util.Arrays.copyOf;
47  import static org.apache.commons.math3.util.FastMath.PI;
48  import static org.apache.commons.math3.util.FastMath.pow;
49  
50  import ffx.utilities.FFXKeyword;
51  import java.util.Arrays;
52  import java.util.Comparator;
53  import java.util.HashMap;
54  import java.util.logging.Level;
55  import java.util.logging.Logger;
56  
57  /**
58   * The OutOfPlaneBendType class defines one Allinger style out-of-plane angle bending energy type.
59   *
60   * @author Michael J. Schnieders
61   * @since 1.0
62   */
63  @FFXKeyword(name = "opbend", clazz = String.class, keywordGroup = PotentialFunctionParameter,
64      description = "[4 integers and 1 real] "
65          + "Provides the values for a single out-of-plane bending potential parameter. "
66          + "The first integer modifier is the atom class of the out-of-plane atom and the second integer is the atom class of the central trigonal atom. "
67          + "The third and fourth integers give the atom classes of the two remaining atoms attached to the trigonal atom. "
68          + "Values of zero for the third and fourth integers are treated as wildcards, and can represent any atom type. "
69          + "The real number modifier gives the force constant value for the out-of-plane angle. "
70          + "The default units for the force constant are kcal/mole/radian^2, but this can be controlled via the opbendunit keyword.")
71  public final class OutOfPlaneBendType extends BaseType implements Comparator<String> {
72  
73    /** Default cubic coefficient in out-of-plane angle bending potential. */
74    public static final double DEFAULT_OPBEND_CUBIC = 0.0;
75    /** Default quartic coefficient in out-of-plane angle bending potential. */
76    public static final double DEFAULT_OPBEND_QUARTIC = 0.0;
77    /** Default pentic coefficient in out-of-plane angle bending potential. */
78    public static final double DEFAULT_OPBEND_PENTIC = 0.0;
79    /** Default quintic coefficient in out-of-plane angle bending potential. */
80    public static final double DEFAULT_OPBEND_SEXTIC = 0.0;
81  
82    /** Cubic coefficient in out-of-plane angle bending potential. */
83    @FFXKeyword(name = "opbend-cubic", keywordGroup = LocalGeometryFunctionalForm, defaultValue = "0.0",
84        description =
85            "Sets the value of the cubic term in the Taylor series expansion form of the out-of-plane bending potential energy. "
86                + "The real number modifier gives the value of the coefficient as a multiple of the quadratic coefficient. "
87                + "This term multiplied by the out-of-plane bending energy unit conversion factor, the force constant, "
88                + "and the cube of the deviation of the out-of-plane angle from zero gives the cubic contribution to the out-of-plane bending energy. "
89                + "The default value in the absence of the opbend-cubic keyword is zero; i.e., the cubic out-of-plane bending term is omitted.")
90    public double cubic = DEFAULT_OPBEND_CUBIC;
91  
92    /** Quartic coefficient in out-of-plane angle bending potential. */
93    @FFXKeyword(name = "opbend-quartic", keywordGroup = LocalGeometryFunctionalForm, defaultValue = "0.0",
94        description =
95            "Sets the value of the quartic term in the Taylor series expansion form of the out-of-plane bending potential energy. "
96                + "The real number modifier gives the value of the coefficient as a multiple of the quadratic coefficient. "
97                + "This term multiplied by the out-of-plane bending energy unit conversion factor, the force constant, "
98                + "and the forth power of the deviation of the out-of-plane angle from zero gives the quartic contribution to the out-of-plane bending energy. "
99                + "The default value in the absence of the opbend-quartic keyword is zero; i.e., the quartic out-of-plane bending term is omitted.")
100   public double quartic = DEFAULT_OPBEND_QUARTIC;
101 
102   /** Quintic coefficient in out-of-plane angle bending potential. */
103   @FFXKeyword(name = "opbend-pentic", keywordGroup = LocalGeometryFunctionalForm, defaultValue = "0.0",
104       description =
105           "Sets the value of the fifth power term in the Taylor series expansion form of the out-of-plane bending potential energy. "
106               + "The real number modifier gives the value of the coefficient as a multiple of the quadratic coefficient. "
107               + "This term multiplied by the out-of-plane bending energy unit conversion factor, the force constant, "
108               + "and the fifth power of the deviation of the out-of-plane angle from zero gives the pentic contribution to the out-of-plane bending energy. "
109               + "The default value in the absence of the opbend-pentic keyword is zero; i.e., the pentic out-of-plane bending term is omitted.")
110   public double pentic = DEFAULT_OPBEND_PENTIC;
111 
112   /** Sextic coefficient in out-of-plane angle bending potential. */
113   @FFXKeyword(name = "opbend-sextic", keywordGroup = LocalGeometryFunctionalForm, defaultValue = "0.0",
114       description =
115           "Sets the value of the sixth power term in the Taylor series expansion form of the out-of-plane bending potential energy. "
116               + "The real number modifier gives the value of the coefficient as a multiple of the quadratic coefficient. "
117               + "This term multiplied by the out-of-plane bending energy unit conversion factor, the force constant, "
118               + "and the sixth power of the deviation of the out-of-plane angle from zero gives the sextic contribution to the out-of-plane bending energy. "
119               + "The default value in the absence of the opbend-sextic keyword is zero; i.e., the sextic out-of-plane bending term is omitted.")
120   public double sextic = DEFAULT_OPBEND_SEXTIC;
121 
122   /**
123    * Convert Out-of-Plane bending energy to kcal/mole.
124    * <p>
125    * TINKER v.5 and v.6 Units: 1.0 / (180.0/PI)^2 = 0.00030461741979
126    * <p>
127    * TINKER v.4 Units: 0.02191418
128    * <p>
129    * Ratio of v.4 to v.5/6 = 0.02191418 / 1.0 / (180.0/PI)^2 = 71.94
130    */
131   @FFXKeyword(name = "opbendunit", keywordGroup = EnergyUnitConversion, defaultValue = "(Pi/180)^2",
132       description =
133           "Sets the scale factor needed to convert the energy value computed by the out-of-plane bending potential into units of kcal/mole. "
134               + "The correct value is force field dependent and typically provided in the header of the master force field parameter file.")
135   public double opBendUnit = DEFAULT_OPBEND_UNIT;
136 
137   public static final double DEFAULT_OPBEND_UNIT = pow(PI / 180.0, 2);
138   /** A Logger for the OutOfPlaneBendType class. */
139   private static final Logger logger = Logger.getLogger(OutOfPlaneBendType.class.getName());
140   /** Atom classes for this out-of-plane angle bending type. */
141   public final int[] atomClasses;
142   /** Force constant (Kcal/mol/Angstrom). */
143   public final double forceConstant;
144 
145   /**
146    * OutOfPlaneBendType Constructor.
147    *
148    * @param atomClasses int[]
149    * @param forceConstant double
150    */
151   public OutOfPlaneBendType(int[] atomClasses, double forceConstant) {
152     super(OPBEND, sortKey(atomClasses));
153     this.atomClasses = atomClasses;
154     this.forceConstant = forceConstant;
155   }
156 
157   /**
158    * Average two OutOfPlaneBendType instances. The atom classes that define the new type must be
159    * supplied.
160    *
161    * @param outOfPlaneBendType1 a {@link ffx.potential.parameters.OutOfPlaneBendType} object.
162    * @param outOfPlaneBendType2 a {@link ffx.potential.parameters.OutOfPlaneBendType} object.
163    * @param atomClasses an array of {@link int} objects.
164    * @return a {@link ffx.potential.parameters.OutOfPlaneBendType} object.
165    */
166   public static OutOfPlaneBendType average(
167       OutOfPlaneBendType outOfPlaneBendType1,
168       OutOfPlaneBendType outOfPlaneBendType2,
169       int[] atomClasses) {
170     if (outOfPlaneBendType1 == null || outOfPlaneBendType2 == null || atomClasses == null) {
171       return null;
172     }
173 
174     double forceConstant =
175         (outOfPlaneBendType1.forceConstant + outOfPlaneBendType2.forceConstant) / 2.0;
176 
177     return new OutOfPlaneBendType(atomClasses, forceConstant);
178   }
179 
180   /**
181    * Construct an OutOfPlaneBendType from an input string.
182    *
183    * @param input The overall input String.
184    * @param tokens The input String tokenized.
185    * @return an OutOfPlaneBendType instance.
186    */
187   public static OutOfPlaneBendType parse(String input, String[] tokens) {
188     if (tokens.length < 6) {
189       logger.log(Level.WARNING, "Invalid OPBEND type:\n{0}", input);
190     } else {
191       try {
192         int[] atomClasses = new int[4];
193         atomClasses[0] = parseInt(tokens[1]);
194         atomClasses[1] = parseInt(tokens[2]);
195         atomClasses[2] = parseInt(tokens[3]);
196         atomClasses[3] = parseInt(tokens[4]);
197         double forceConstant = parseDouble(tokens[5]);
198         return new OutOfPlaneBendType(atomClasses, forceConstant);
199       } catch (NumberFormatException e) {
200         String message = "Exception parsing OPBEND type:\n" + input + "\n";
201         logger.log(Level.SEVERE, message, e);
202       }
203     }
204     return null;
205   }
206 
207   /**
208    * This method sorts the atom classes for the out-of-plane angle bending type.
209    *
210    * @param c atomClasses
211    * @return lookup key
212    */
213   public static String sortKey(int[] c) {
214     if (c == null || c.length != 4) {
215       return null;
216     }
217     return c[0] + " " + c[1] + " " + c[2] + " " + c[3];
218   }
219 
220   /** {@inheritDoc} */
221   @Override
222   public int compare(String s1, String s2) {
223     String[] keys1 = s1.split(" ");
224     String[] keys2 = s2.split(" ");
225 
226     for (int i = 0; i < 4; i++) {
227       int c1 = parseInt(keys1[i]);
228       int c2 = parseInt(keys2[i]);
229       if (c1 < c2) {
230         return -1;
231       } else if (c1 > c2) {
232         return 1;
233       }
234     }
235     return 0;
236   }
237 
238   /** {@inheritDoc} */
239   @Override
240   public boolean equals(Object o) {
241     if (this == o) {
242       return true;
243     }
244     if (o == null || getClass() != o.getClass()) {
245       return false;
246     }
247     OutOfPlaneBendType outOfPlaneBendType = (OutOfPlaneBendType) o;
248     return Arrays.equals(atomClasses, outOfPlaneBendType.atomClasses);
249   }
250 
251   /** {@inheritDoc} */
252   @Override
253   public int hashCode() {
254     return Arrays.hashCode(atomClasses);
255   }
256 
257   /**
258    * incrementClasses
259    *
260    * @param increment a int.
261    */
262   public void incrementClasses(int increment) {
263     for (int i = 0; i < atomClasses.length; i++) {
264       if (atomClasses[i] > 0) {
265         atomClasses[i] += increment;
266       }
267     }
268     setKey(sortKey(atomClasses));
269   }
270 
271   /**
272    * Remap new atom classes to known internal ones.
273    *
274    * @param typeMap a lookup between new atom types and known atom types.
275    * @return a {@link ffx.potential.parameters.OutOfPlaneBendType} object.
276    */
277   public OutOfPlaneBendType patchClasses(HashMap<AtomType, AtomType> typeMap) {
278     int count = 0;
279     int len = atomClasses.length;
280 
281     // Look for new OutOfPlaneBends that contain 1 mapped atom classes.
282     for (AtomType newType : typeMap.keySet()) {
283 
284       for (int atomClass : atomClasses) {
285         if (atomClass == newType.atomClass) {
286           count++;
287         }
288       }
289     }
290 
291     // If found, create a new OutOfPlaneBend that bridges to known classes.
292     if (count == 1) {
293       int[] newClasses = copyOf(atomClasses, len);
294       for (AtomType newType : typeMap.keySet()) {
295         for (int i = 0; i < len; i++) {
296           if (atomClasses[i] == newType.atomClass) {
297             AtomType knownType = typeMap.get(newType);
298             newClasses[i] = knownType.atomClass;
299           }
300         }
301       }
302       return new OutOfPlaneBendType(newClasses, forceConstant);
303     }
304     return null;
305   }
306 
307   /**
308    * {@inheritDoc}
309    *
310    * <p>Nicely formatted out-of-plane angle bending string.
311    */
312   @Override
313   public String toString() {
314     return String.format(
315         "opbend  %5d  %5d  %5d  %5d  %6.2f",
316         atomClasses[0], atomClasses[1], atomClasses[2], atomClasses[3], forceConstant);
317   }
318 }