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.ANGTORS;
41  import static ffx.utilities.Constants.DEGREES_PER_RADIAN;
42  import static ffx.utilities.KeywordGroup.EnergyUnitConversion;
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.lang.String.format;
47  import static java.util.Arrays.copyOf;
48  
49  import ffx.utilities.FFXKeyword;
50  import java.util.Arrays;
51  import java.util.Comparator;
52  import java.util.HashMap;
53  import java.util.logging.Level;
54  import java.util.logging.Logger;
55  
56  /**
57   * The AngleTorsionType class defines one angle-torsion energy type.
58   *
59   * @author Michael J. Schnieders
60   * @since 1.0
61   */
62  @FFXKeyword(name = "angtors", clazz = String.class, keywordGroup = PotentialFunctionParameter,
63      description = "[4 integers and 6 reals] "
64          + "Provides the values for a single bond angle bending-torsional angle parameter. "
65          + "The integer modifiers give the atom class numbers for the four kinds of atoms involved in the torsion and its contained angles. "
66          + "The real number modifiers give the force constant values for both angles coupled with 1-, 2- and 3-fold torsional terms."
67          + "The default units for the force constants are kcal/mole/radian, but this can be controlled via the angtorunit keyword.")
68  public final class AngleTorsionType extends BaseType implements Comparator<String> {
69  
70    public static final double DEFAULT_ANGTOR_UNIT = 1.0 / DEGREES_PER_RADIAN;
71  
72    /** Convert angle-torsion to kcal/mole. */
73    @FFXKeyword(name = "angtorunit", keywordGroup = EnergyUnitConversion, defaultValue = "Pi/180",
74        description = "Sets the scale factor needed to convert the energy value computed by the angle bending-torsional angle cross term into units of kcal/mole. "
75            + "The correct value is force field dependent and typically provided in the header of the master force field parameter file.")
76    public double angtorunit = DEFAULT_ANGTOR_UNIT;
77  
78    /** A Logger for the AngleTorsionType class. */
79    private static final Logger logger = Logger.getLogger(AngleTorsionType.class.getName());
80    /** Atom classes for this stretch-torsion type. */
81    public final int[] atomClasses;
82    /** Force constants. */
83    public final double[] forceConstants;
84  
85    /**
86     * AngleTorsionType Constructor.
87     *
88     * @param atomClasses Atomic classes.
89     * @param forceConstants Force constants.
90     */
91    public AngleTorsionType(int[] atomClasses, double[] forceConstants) {
92      super(ANGTORS, sortKey(atomClasses));
93      this.atomClasses = atomClasses;
94      this.forceConstants = forceConstants;
95    }
96  
97    /**
98     * average.
99     *
100    * @param angleTorsionType1 a {@link ffx.potential.parameters.AngleTorsionType} object.
101    * @param angleTorsionType2 a {@link ffx.potential.parameters.AngleTorsionType} object.
102    * @param atomClasses an array of {@link int} objects.
103    * @return a {@link ffx.potential.parameters.AngleTorsionType} object.
104    */
105   public static AngleTorsionType average(
106       AngleTorsionType angleTorsionType1, AngleTorsionType angleTorsionType2, int[] atomClasses) {
107     if (angleTorsionType1 == null || angleTorsionType2 == null || atomClasses == null) {
108       return null;
109     }
110     int len = angleTorsionType1.forceConstants.length;
111     if (len != angleTorsionType2.forceConstants.length) {
112       return null;
113     }
114     double[] forceConstants = new double[len];
115     for (int i = 0; i < len; i++) {
116       forceConstants[i] =
117           (angleTorsionType1.forceConstants[i] + angleTorsionType2.forceConstants[i]) / 2.0;
118     }
119     return new AngleTorsionType(atomClasses, forceConstants);
120   }
121 
122   /**
123    * Construct an AngleTorsionType from an input string.
124    *
125    * @param input The overall input String.
126    * @param tokens The input String tokenized.
127    * @return an AngleTorsionType instance.
128    */
129   public static AngleTorsionType parse(String input, String[] tokens) {
130     if (tokens.length < 10) {
131       logger.log(Level.WARNING, "Invalid ANGTORS type:\n{0}", input);
132     } else {
133       try {
134         int[] atomClasses = new int[4];
135         atomClasses[0] = parseInt(tokens[1]);
136         atomClasses[1] = parseInt(tokens[2]);
137         atomClasses[2] = parseInt(tokens[3]);
138         atomClasses[3] = parseInt(tokens[4]);
139         double[] constants = new double[6];
140         constants[0] = parseDouble(tokens[5]);
141         constants[1] = parseDouble(tokens[6]);
142         constants[2] = parseDouble(tokens[7]);
143         constants[3] = parseDouble(tokens[8]);
144         constants[4] = parseDouble(tokens[9]);
145         constants[5] = parseDouble(tokens[10]);
146         return new AngleTorsionType(atomClasses, constants);
147       } catch (NumberFormatException e) {
148         String message = "Exception parsing ANGTORS type:\n" + input + "\n";
149         logger.log(Level.SEVERE, message, e);
150       }
151     }
152     return null;
153   }
154 
155   /**
156    * This method sorts the atom classes for the angle-torsion.
157    *
158    * @param c atomClasses
159    * @return lookup key
160    * @since 1.0
161    */
162   public static String sortKey(int[] c) {
163     return c[0] + " " + c[1] + " " + c[2] + " " + c[3];
164   }
165 
166   /**
167    * {@inheritDoc}
168    *
169    * @since 1.0
170    */
171   @Override
172   public int compare(String s1, String s2) {
173     String[] keys1 = s1.split(" ");
174     String[] keys2 = s2.split(" ");
175     int[] c1 = new int[4];
176     int[] c2 = new int[4];
177 
178     for (int i = 0; i < 4; i++) {
179       c1[i] = parseInt(keys1[i]);
180       c2[i] = parseInt(keys2[i]);
181     }
182 
183     if (c1[1] < c2[1]) {
184       return -1;
185     } else if (c1[1] > c2[1]) {
186       return 1;
187     } else if (c1[2] < c2[2]) {
188       return -1;
189     } else if (c1[2] > c2[2]) {
190       return 1;
191     } else if (c1[0] < c2[0]) {
192       return -1;
193     } else if (c1[0] > c2[0]) {
194       return 1;
195     } else if (c1[3] < c2[3]) {
196       return -1;
197     } else if (c1[3] > c2[3]) {
198       return 1;
199     }
200 
201     return 0;
202   }
203 
204   /** {@inheritDoc} */
205   @Override
206   public boolean equals(Object o) {
207     if (this == o) {
208       return true;
209     }
210     if (o == null || getClass() != o.getClass()) {
211       return false;
212     }
213     AngleTorsionType angleTorsionType = (AngleTorsionType) o;
214     return Arrays.equals(atomClasses, angleTorsionType.atomClasses);
215   }
216 
217   /** {@inheritDoc} */
218   @Override
219   public int hashCode() {
220     return Arrays.hashCode(atomClasses);
221   }
222 
223   /**
224    * incrementClasses
225    *
226    * @param increment a int.
227    */
228   public void incrementClasses(int increment) {
229     for (int i = 0; i < atomClasses.length; i++) {
230       atomClasses[i] += increment;
231     }
232     setKey(sortKey(atomClasses));
233   }
234 
235   /**
236    * Remap new atom classes to known internal ones.
237    *
238    * @param typeMap a lookup between new atom types and known atom types.
239    * @return a {@link ffx.potential.parameters.AngleTorsionType} object.
240    */
241   public AngleTorsionType patchClasses(HashMap<AtomType, AtomType> typeMap) {
242     int count = 0;
243     int len = atomClasses.length;
244 
245     // Check if this Type contain 1 or 2 mapped atom classes.
246     for (AtomType newType : typeMap.keySet()) {
247 
248       for (int atomClass : atomClasses) {
249         if (atomClass == newType.atomClass) {
250           count++;
251         }
252       }
253     }
254 
255     // If found, create a new AngleTorsionType that bridges to known classes.
256     if (count == 1 || count == 2) {
257       int[] newClasses = copyOf(atomClasses, len);
258       for (AtomType newType : typeMap.keySet()) {
259         for (int i = 0; i < len; i++) {
260           if (atomClasses[i] == newType.atomClass) {
261             AtomType knownType = typeMap.get(newType);
262             newClasses[i] = knownType.atomClass;
263           }
264         }
265       }
266       return new AngleTorsionType(newClasses, forceConstants);
267     }
268     return null;
269   }
270 
271   /**
272    * {@inheritDoc}
273    *
274    * <p>Nicely formatted Angle-Torsion string.
275    */
276   @Override
277   public String toString() {
278     return format(
279         "angtors  %5d  %5d  %5d  %5d  %6.3f  %6.3f  %6.3f  %6.3f  %6.3f  %6.3f",
280         atomClasses[0],
281         atomClasses[1],
282         atomClasses[2],
283         atomClasses[3],
284         forceConstants[0],
285         forceConstants[1],
286         forceConstants[2],
287         forceConstants[3],
288         forceConstants[4],
289         forceConstants[5]);
290   }
291 }