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