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