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