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