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