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.UREYBRAD;
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.lang.String.format;
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 UreyBradleyType class defines one harmonic UreyBradley cross term.
58   *
59   * @author Michael J. Schnieders
60   * @since 1.0
61   */
62  @FFXProperty(name = "ureybrad", clazz = String.class, propertyGroup = PotentialFunctionParameter, description = """
63      [3 integers and 2 reals]
64      Provides the values for a single Urey-Bradley cross term potential parameter.
65      The integer modifiers give the atom class numbers for the three kinds of atoms
66      involved in the angle for which a Urey-Bradley term is to be defined.
67      The real number modifiers give the force constant value for the term and the target value for the 1-3 distance in Angstroms.
68      The default units for the force constant are kcal/mole/Ang^2, but this can be controlled via the ureyunit keyword
69      """)
70  public final class UreyBradleyType extends BaseType implements Comparator<String> {
71  
72    /**
73     * Default conversion Urey-Bradley stretch energy to kcal/mole.
74     */
75    public static final double DEFAULT_UREY_UNIT = 1.0;
76    /**
77     * Default cubic coefficient in Urey-Bradley stretch potential.
78     */
79    public static final double DEFAULT_UREY_CUBIC = 0.0;
80    /**
81     * Default quartic coefficient in Urey-Bradley stretch potential.
82     */
83    public static final double DEFAULT_UREY_QUARTIC = 0.0;
84  
85    /**
86     * Convert Urey-Bradley stretch energy to kcal/mole.
87     */
88    @FFXProperty(name = "ureyunit", propertyGroup = EnergyUnitConversion, defaultValue = "1.0", description = """
89        Sets the scale factor needed to convert the energy value computed by the Urey-Bradley potential into units of kcal/mole.
90        The correct value is force field dependent and typically provided in the header of the master force field parameter file.
91        """)
92    public double ureyUnit = DEFAULT_UREY_UNIT;
93  
94    /**
95     * Cubic coefficient in bond stretch potential.
96     */
97    @FFXProperty(name = "urey-cubic", propertyGroup = LocalGeometryFunctionalForm, defaultValue = "0.0", description = """
98        Sets the value of the cubic term in the Taylor series expansion form of the Urey-Bradley potential energy.
99        The real number modifier gives the value of the coefficient as a multiple of the quadratic coefficient.
100       The default value in the absence of the urey-cubic keyword is zero; i.e., the cubic Urey-Bradley term is omitted.
101       """)
102   public double cubic = DEFAULT_UREY_CUBIC;
103 
104   /**
105    * Quartic coefficient in bond stretch potential.
106    */
107   @FFXProperty(name = "urey-quartic", propertyGroup = LocalGeometryFunctionalForm, defaultValue = "0.0", description = """
108       Sets the value of the quartic term in the Taylor series expansion form of the Urey-Bradley potential energy.
109       The real number modifier gives the value of the coefficient as a multiple of the quadratic coefficient.
110       The default value in the absence of the urey-quartic keyword is zero; i.e., the quartic Urey-Bradley term is omitted.
111       """)
112   public double quartic = DEFAULT_UREY_QUARTIC;
113 
114   /**
115    * A Logger for the UreyBradleyType class.
116    */
117   private static final Logger logger = Logger.getLogger(UreyBradleyType.class.getName());
118   /**
119    * Atom classes that form this Urey-Bradley cross term.
120    */
121   public final int[] atomClasses;
122   /**
123    * Force constant (Kcal/mole/angstroms^2).
124    */
125   public final double forceConstant;
126   /**
127    * Equilibrium 1-3 separation (Angstroms).
128    */
129   public final double distance;
130 
131   /**
132    * UreyBradleyType constructor.
133    *
134    * @param atomClasses   Atom classes.
135    * @param forceConstant Force constant (Kcal/mole/angstroms^2).
136    * @param distance      Equilibrium 1-3 separation (Angstroms).
137    */
138   public UreyBradleyType(int[] atomClasses, double forceConstant, double distance) {
139     super(UREYBRAD, sortKey(atomClasses));
140     this.atomClasses = atomClasses;
141     this.forceConstant = forceConstant;
142     this.distance = distance;
143   }
144 
145   /**
146    * average.
147    *
148    * @param ureyBradleyType1 a {@link ffx.potential.parameters.UreyBradleyType} object.
149    * @param ureyBradleyType2 a {@link ffx.potential.parameters.UreyBradleyType} object.
150    * @param atomClasses      an array of {@link int} objects.
151    * @return a {@link ffx.potential.parameters.UreyBradleyType} object.
152    */
153   public static UreyBradleyType average(UreyBradleyType ureyBradleyType1,
154                                         UreyBradleyType ureyBradleyType2, int[] atomClasses) {
155     if (ureyBradleyType1 == null || ureyBradleyType2 == null || atomClasses == null) {
156       return null;
157     }
158 
159     double forceConstant = (ureyBradleyType1.forceConstant + ureyBradleyType2.forceConstant) / 2.0;
160     double distance = (ureyBradleyType1.distance + ureyBradleyType2.distance) / 2.0;
161 
162     return new UreyBradleyType(atomClasses, forceConstant, distance);
163   }
164 
165   /**
166    * Construct a UreyBradleyType from an input string.
167    *
168    * @param input  The overall input String.
169    * @param tokens The input String tokenized.
170    * @return a UreyBradleyType instance.
171    */
172   public static UreyBradleyType parse(String input, String[] tokens) {
173     if (tokens.length < 5) {
174       logger.log(Level.WARNING, "Invalid UREYBRAD type:\n{0}", input);
175     } else {
176       try {
177         int[] atomClasses = new int[3];
178         atomClasses[0] = parseInt(tokens[1]);
179         atomClasses[1] = parseInt(tokens[2]);
180         atomClasses[2] = parseInt(tokens[3]);
181         double forceConstant = parseDouble(tokens[4]);
182         double distance = parseDouble(tokens[5]);
183         return new UreyBradleyType(atomClasses, forceConstant, distance);
184       } catch (NumberFormatException e) {
185         String message = "Exception parsing UREYBRAD type:\n" + input + "\n";
186         logger.log(Level.SEVERE, message, e);
187       }
188     }
189     return null;
190   }
191 
192   /**
193    * This method sorts the atom classes as: min, c[1], max
194    *
195    * @param c atomClasses
196    * @return lookup key
197    */
198   public static String sortKey(int[] c) {
199     if (c == null || c.length != 3) {
200       return null;
201     }
202     if (c[0] > c[2]) {
203       int temp = c[0];
204       c[0] = c[2];
205       c[2] = temp;
206     }
207 
208     return c[0] + " " + c[1] + " " + c[2];
209   }
210 
211   /**
212    * {@inheritDoc}
213    */
214   @Override
215   public int compare(String key1, String key2) {
216     String[] keys1 = key1.split(" ");
217     String[] keys2 = key2.split(" ");
218     int[] c1 = new int[3];
219     int[] c2 = new int[3];
220     for (int i = 0; i < 3; i++) {
221       c1[i] = parseInt(keys1[i]);
222       c2[i] = parseInt(keys2[i]);
223     }
224     if (c1[1] < c2[1]) {
225       return -1;
226     } else if (c1[1] > c2[1]) {
227       return 1;
228     } else if (c1[0] < c2[0]) {
229       return -1;
230     } else if (c1[0] > c2[0]) {
231       return 1;
232     } else if (c1[2] < c2[2]) {
233       return -1;
234     } else if (c1[2] > c2[2]) {
235       return 1;
236     }
237     return 0;
238   }
239 
240   /**
241    * {@inheritDoc}
242    */
243   @Override
244   public boolean equals(Object o) {
245     if (this == o) {
246       return true;
247     }
248     if (o == null || getClass() != o.getClass()) {
249       return false;
250     }
251     UreyBradleyType ureyBradleyType = (UreyBradleyType) o;
252     return Arrays.equals(atomClasses, ureyBradleyType.atomClasses);
253   }
254 
255   /**
256    * {@inheritDoc}
257    */
258   @Override
259   public int hashCode() {
260     return Arrays.hashCode(atomClasses);
261   }
262 
263   /**
264    * Increment the atom classes by a specified amount.
265    *
266    * @param increment The increment to apply to the atom classes.
267    */
268   public void incrementClasses(int increment) {
269     for (int i = 0; i < atomClasses.length; i++) {
270       atomClasses[i] += increment;
271     }
272     setKey(sortKey(atomClasses));
273   }
274 
275   /**
276    * Remap new atom classes to known internal ones.
277    *
278    * @param typeMap a lookup between new atom types and known atom types.
279    */
280   public void patchClasses(HashMap<AtomType, AtomType> typeMap) {
281     int count = 0;
282     for (AtomType newType : typeMap.keySet()) {
283       for (int atomClass : atomClasses) {
284         if (atomClass == newType.atomClass) {
285           count++;
286         }
287       }
288     }
289     if (count > 0 && count < atomClasses.length) {
290       for (AtomType newType : typeMap.keySet()) {
291         for (int i = 0; i < atomClasses.length; i++) {
292           if (atomClasses[i] == newType.atomClass) {
293             AtomType knownType = typeMap.get(newType);
294             atomClasses[i] = knownType.atomClass;
295           }
296         }
297       }
298       setKey(sortKey(atomClasses));
299     }
300   }
301 
302   /**
303    * {@inheritDoc}
304    *
305    * <p>Nicely formatted Urey-Bradley string.
306    */
307   @Override
308   public String toString() {
309     return format("ureybrad  %5d  %5d  %5d  %6.2f  %7.4f", atomClasses[0], atomClasses[1],
310         atomClasses[2], forceConstant, distance);
311   }
312 }