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.ATOM;
41  import static ffx.utilities.PropertyGroup.PotentialFunctionParameter;
42  import static java.lang.Double.parseDouble;
43  import static java.lang.Integer.parseInt;
44  import static java.lang.String.format;
45  import static org.apache.commons.math3.util.FastMath.abs;
46  
47  import ffx.utilities.FFXProperty;
48  
49  import java.util.Comparator;
50  import java.util.Objects;
51  import java.util.logging.Level;
52  import java.util.logging.Logger;
53  
54  /**
55   * The AtomType class represents one molecular mechanics atom type.
56   *
57   * @author Michael J. Schnieders
58   * @since 1.0
59   */
60  @FFXProperty(name = "atom", clazz = String.class, propertyGroup = PotentialFunctionParameter, description = """
61      [2 integers, name, quoted string, integer, real and integer]
62      Provides the values needed to define a single force field atom type.
63      The first two integer modifiers denote the atom type and class numbers.
64      If the type and class are identical, only a single integer value is required.
65      The next modifier is a three-character atom name, followed by an 24-character or less atom description contained in single quotes.
66      The next two modifiers are the atomic number and atomic mass.
67      The final integer modifier is the "valence" of the atom, defined as the expected number of attached or bonded atoms.
68      """)
69  public final class AtomType extends BaseType implements Comparator<String> {
70  
71    /**
72     * A Logger for the AngleType class.
73     */
74    private static final Logger logger = Logger.getLogger(AtomType.class.getName());
75    /**
76     * Short name (ie CH3/CH2 etc).
77     */
78    public final String name;
79    /**
80     * Description of the atom's bonding environment.
81     */
82    public final String environment;
83    /**
84     * Atomic Number.
85     */
86    public final int atomicNumber;
87    /**
88     * Atomic weight. "An atomic weight (relative atomic weight) of an element from a specified source
89     * is the ratio of the average atomicWeight per atom of the element to 1/12 of the atomicWeight of
90     * an atom of 12C"
91     */
92    public final double atomicWeight;
93    /**
94     * Valence number for this type.
95     */
96    public final int valence;
97    /**
98     * Atom type.
99     */
100   public int type;
101   /**
102    * Atom class.
103    */
104   public int atomClass;
105 
106   /**
107    * AtomType Constructor.
108    *
109    * @param type         int
110    * @param atomClass    int
111    * @param name         String
112    * @param environment  String
113    * @param atomicNumber int
114    * @param atomicWeight double
115    * @param valence      int
116    */
117   public AtomType(int type, int atomClass, String name, String environment, int atomicNumber,
118                   double atomicWeight, int valence) {
119     super(ATOM, Integer.toString(type));
120     this.type = type;
121     this.atomClass = atomClass;
122     this.name = name;
123     this.environment = environment;
124     this.atomicNumber = atomicNumber;
125     this.atomicWeight = atomicWeight;
126     this.valence = valence;
127   }
128 
129   /**
130    * Construct an AtomType from an input string.
131    *
132    * @param input  The overall input String.
133    * @param tokens The input String tokenized.
134    * @return an AtomType instance.
135    */
136   public static AtomType parse(String input, String[] tokens) {
137     if (tokens.length < 7) {
138       logger.log(Level.WARNING, "Invalid ATOM type:\n{0}", input);
139     } else {
140       try {
141         int index = 1;
142         // Atom Type
143         int type = parseInt(tokens[index++]);
144         // Atom Class
145         int atomClass;
146         // The following try/catch checks for one of the following two cases:
147         //
148         // NUMBER TYPE CLASS IDENTIFIER ... (example is OPLS-AA)
149         // vs.
150         // NUMBER TYPE IDENTIFIER ... (example is OPLS-UA)
151         //
152         // If there is no atom class, the exception will be caught
153         // and the atomClass field will remain equal to null.
154         try {
155           atomClass = parseInt(tokens[index]);
156           // If the parseInt succeeds, this force field has atom classes.
157           index++;
158         } catch (NumberFormatException e) {
159           // Some force fields do not use atom classes.
160           atomClass = -1;
161         }
162         // Name
163         String name = tokens[index].intern();
164         // The "environment" string may contain spaces,
165         // and is therefore surrounded in quotes located at "first" and
166         // "last".
167         int first = input.indexOf("\"");
168         int last = input.lastIndexOf("\"");
169         if (first >= last) {
170           logger.log(Level.WARNING, "Invalid ATOM type:\n{0}", input);
171           return null;
172         }
173         // Environment
174         String environment = input.substring(first, last + 1).intern();
175         // Shrink the tokens array to only include entries
176         // after the environment field.
177         tokens = input.substring(last + 1).trim().split(" +");
178         index = 0;
179         // Atomic Number
180         int atomicNumber = parseInt(tokens[index++]);
181         // Atomic Mass
182         double mass = parseDouble(tokens[index++]);
183         // Hybridization
184         int hybridization = parseInt(tokens[index]);
185 
186         AtomType atomType = new AtomType(type, atomClass, name, environment, atomicNumber, mass,
187             hybridization);
188         if (!checkAtomicNumberAndMass(atomicNumber, mass)) {
189           // Ignore united atom (UA) entries.
190           if (!environment.toUpperCase().contains("UA")) {
191             logger.warning(" Atomic number and weight do not agree:\n" + atomType);
192           }
193         }
194         return atomType;
195       } catch (NumberFormatException e) {
196         String message = "Exception parsing AtomType:\n" + input + "\n";
197         logger.log(Level.SEVERE, message, e);
198       }
199     }
200     return null;
201   }
202 
203   /**
204    * {@inheritDoc}
205    */
206   @Override
207   public int compare(String s1, String s2) {
208     int t1 = parseInt(s1);
209     int t2 = parseInt(s2);
210     return Integer.compare(t1, t2);
211   }
212 
213   /**
214    * {@inheritDoc}
215    */
216   @Override
217   public boolean equals(Object o) {
218     if (this == o) {
219       return true;
220     }
221     if (o == null || getClass() != o.getClass()) {
222       return false;
223     }
224     AtomType atomType = (AtomType) o;
225     return atomType.type == this.type;
226   }
227 
228   /**
229    * {@inheritDoc}
230    */
231   @Override
232   public int hashCode() {
233     return Objects.hash(type);
234   }
235 
236   /**
237    * {@inheritDoc}
238    *
239    * <p>Nicely formatted atom type string.
240    */
241   @Override
242   public String toString() {
243     String s;
244     if (atomClass >= 0) {
245       s = format("atom  %5d  %5d  %-4s  %-25s  %3d  %8.4f  %d", type, atomClass, name, environment,
246           atomicNumber, atomicWeight, valence);
247     } else {
248       s = format("atom  %5d  %-4s  %-25s  %3d  %8.4f  %d", type, name, environment, atomicNumber,
249           atomicWeight, valence);
250     }
251     return s;
252   }
253 
254   /**
255    * incrementClassAndType
256    *
257    * @param classIncrement The value to increment the atom class by.
258    * @param typeIncrement  The value to increment the atom type by.
259    */
260   void incrementClassAndType(int classIncrement, int typeIncrement) {
261     atomClass += classIncrement;
262     type += typeIncrement;
263     setKey(Integer.toString(type));
264   }
265 
266   /**
267    * Check if the supplied atomic mass is within 0.1 AMU of the IUPAC value for the given atomic
268    * number.
269    * <p>
270    * For atomic numbers outside the range 1 to 118, true always is returned.
271    *
272    * @param atomicNumber The atomic number.
273    * @param mass         The atomic mass.
274    * @return True if the given mass is within the given tolerance of the IUPAC value for the atomic
275    * number.
276    */
277   public static boolean checkAtomicNumberAndMass(int atomicNumber, double mass) {
278     return checkAtomicNumberAndMass(atomicNumber, mass, 0.1);
279   }
280 
281   /**
282    * Check if the supplied atomic mass is within the supplied tolerance (in AMU) of the IUPAC value
283    * for the given atomic number.
284    * <p>
285    * For atomic numbers outside the range 1 to 118, true always is returned.
286    *
287    * @param atomicNumber The atomic number.
288    * @param mass         The atomic mass.
289    * @param tolerance    The error tolerance in AMU.
290    * @return True if the given mass is within the given tolerance of the IUPAC value for the atomic
291    * number.
292    */
293   public static boolean checkAtomicNumberAndMass(int atomicNumber, double mass, double tolerance) {
294     // Ignore atomic numbers outside the range 1 to 118.
295     if (atomicNumber == 0 || atomicNumber >= atomicMass.length) {
296       return true;
297     }
298 
299     double expected = atomicMass[atomicNumber - 1];
300     return abs(expected - mass) < tolerance;
301   }
302 
303   /**
304    * <a href="https://iupac.qmul.ac.uk/AtWt">IUPAC Commission</a> on Isotopic Abundances and Atomic
305    * Weights.
306    * Retrieved on 1/24/22.
307    */
308   private static final double[] atomicMass = { /* H Hydrogen */  1.008,
309       /* 2 He Helium */ 4.002,
310       /* 3 Li Lithium */ 6.94,
311       /* 4 Be Beryllium */ 9.012,
312       /* 5 B Boron */ 10.81,
313       /* 6 C Carbon */ 12.011,
314       /* 7 N Nitrogen */ 14.007,
315       /* 8 O Oxygen */ 15.999,
316       /* 9 F Fluorine */ 18.998,
317       /* 10 Ne Neon */ 20.1797,
318       /* 11 Na Sodium */ 22.989,
319       /* 12 Mg Magnesium */ 24.305,
320       /* 13 Al Aluminium */ 26.981,
321       /* 14 Si Silicon */ 28.085,
322       /* 15 P Phosphorus */ 30.973,
323       /* 16 S Sulfur */ 32.06,
324       /* 17 Cl Chlorine */ 35.45,
325       /* 18 Ar Argon */ 39.948,
326       /* 19 K Potassium */ 39.0983,
327       /* 20 Ca Calcium */ 40.078,
328       /* 21 Sc Scandium */ 44.955,
329       /* 22 Ti Titanium */ 47.867,
330       /* 23 V Vanadium */ 50.9415,
331       /* 24 Cr Chromium */ 51.9961,
332       /* 25 Mn Manganese */ 54.938,
333       /* 26 Fe Iron */ 55.845,
334       /* 27 Co Cobalt */ 58.933,
335       /* 28 Ni Nickel */ 58.6934,
336       /* 29 Cu Copper */ 63.546,
337       /* 30 Zn Zinc */ 65.38,
338       /* 31 Ga Gallium */ 69.723,
339       /* 32 Ge Germanium */ 72.630,
340       /* 33 As Arsenic */ 74.921,
341       /* 34 Se Selenium */ 78.971,
342       /* 35 Br Bromine */ 79.904,
343       /* 36 Kr Krypton */ 83.798,
344       /* 37 Rb Rubidium */ 85.4678,
345       /* 38 Sr Strontium */ 87.62,
346       /* 39 Y Yttrium */ 88.905,
347       /* 40 Zr Zirconium */ 91.224,
348       /* 41 Nb Niobium */ 92.906,
349       /* 42 Mo Molybdenum */ 95.95,
350       /* 43 Tc Technetium */ 97.0,
351       /* 44 Ru Ruthenium */ 101.07,
352       /* 45 Rh Rhodium */ 102.905,
353       /* 46 Pd Palladium */ 106.42,
354       /* 47 Ag Silver */ 107.8682,
355       /* 48 Cd Cadmium */ 112.414,
356       /* 49 In Indium */ 114.818,
357       /* 50 Sn Tin */ 118.710,
358       /* 51 Sb Antimony */ 121.760,
359       /* 52 Te Tellurium */ 127.60,
360       /* 53 I Iodine */ 126.904,
361       /* 54 Xe Xenon */ 131.293,
362       /* 55 Cs Caesium */ 132.905,
363       /* 56 Ba Barium */ 137.327,
364       /* 57 La Lanthanum */ 138.905,
365       /* 58 Ce Cerium */ 140.116,
366       /* 59 Pr Praseodymium */ 140.907,
367       /* 60 Nd Neodymium */ 144.242,
368       /* 61 Pm Promethium */ 145.0,
369       /* 62 Sm Samarium */ 150.36,
370       /* 63 Eu Europium */ 151.964,
371       /* 64 Gd Gadolinium */ 157.25,
372       /* 65 Tb Terbium */ 158.925,
373       /* 66 Dy Dysprosium */ 162.500,
374       /* 67 Ho Holmium */ 164.930,
375       /* 68 Er Erbium */ 167.259,
376       /* 69 Tm Thulium */ 168.934,
377       /* 70 Yb Ytterbium */ 173.045,
378       /* 71 Lu Lutetium */ 174.9668,
379       /* 72 Hf Hafnium */ 178.486,
380       /* 73 Ta Tantalum */ 180.947,
381       /* 74 W Tungsten */ 183.84,
382       /* 75 Re Rhenium */ 186.207,
383       /* 76 Os Osmium */ 190.23,
384       /* 77 Ir Iridium */ 192.217,
385       /* 78 Pt Platinum */ 195.084,
386       /* 79 Au Gold */ 196.966,
387       /* 80 Hg Mercury */ 200.592,
388       /* 81 Tl Thallium */ 204.38,
389       /* 82 Pb Lead */ 207.2,
390       /* 83 Bi Bismuth */ 208.980,
391       /* 84 Po Polonium */ 209.0,
392       /* 85 At Astatine */ 210.0,
393       /* 86 Rn Radon */ 222.0,
394       /* 87 Fr Francium */ 223.0,
395       /* 88 Ra Radium */ 226.0,
396       /* 89 Ac Actinium */ 227.0,
397       /* 90 Th Thorium */ 232.0377,
398       /* 91 Pa Protactinium */ 231.035,
399       /* 92 U Uranium */ 238.028,
400       /* 93 Np Neptunium */ 237.0,
401       /* 94 Pu Plutonium */ 244.0,
402       /* 95 Am Americium */ 243.0,
403       /* 96 Cm Curium */ 247.0,
404       /* 97 Bk Berkelium */ 247.0,
405       /* 98 Cf Californium */ 251.0,
406       /* 99 Es Einsteinium */ 252.0,
407       /* 100 Fm Fermium */ 257.0,
408       /* 101 Md Mendelevium */ 258.0,
409       /* 102 No Nobelium */ 259.0,
410       /* 103 Lr Lawrencium */ 262.0,
411       /* 104 Rf Rutherfordium */ 267.0,
412       /* 105 Db Dubnium */ 270.0,
413       /* 106 Sg Seaborgium */ 269.0,
414       /* 107 Bh Bohrium */ 270.0,
415       /* 108 Hs Hassium */ 270.0,
416       /* 109 Mt Meitnerium */ 278.0,
417       /* 110 Ds Darmstadtium */ 281.0,
418       /* 111 Rg Roentgenium */ 281.0,
419       /* 112 Cn Copernicium */ 285.0,
420       /* 113 Nh Nihonium */ 286.0,
421       /* 114 Fl Flerovium */ 289.0,
422       /* 115 Mc Moscovium */ 289.0,
423       /* 116 Lv Livermorium */ 293.0,
424       /* 117 Ts Tennessine */ 293.0,
425       /* 118 Og Oganesson */ 294.0};
426 }