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, description =
60      "[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(int type, int atomClass, String name, String environment, int atomicNumber,
102       double atomicWeight, int valence) {
103     super(ATOM, Integer.toString(type));
104     this.type = type;
105     this.atomClass = atomClass;
106     this.name = name;
107     this.environment = environment;
108     this.atomicNumber = atomicNumber;
109     this.atomicWeight = atomicWeight;
110     this.valence = valence;
111   }
112 
113   /**
114    * Construct an AtomType from an input string.
115    *
116    * @param input The overall input String.
117    * @param tokens The input String tokenized.
118    * @return an AtomType instance.
119    */
120   public static AtomType parse(String input, String[] tokens) {
121     if (tokens.length < 7) {
122       logger.log(Level.WARNING, "Invalid ATOM type:\n{0}", input);
123     } else {
124       try {
125         int index = 1;
126         // Atom Type
127         int type = parseInt(tokens[index++]);
128         // Atom Class
129         int atomClass;
130         // The following try/catch checks for one of the following two cases:
131         //
132         // NUMBER TYPE CLASS IDENTIFIER ... (example is OPLS-AA)
133         // vs.
134         // NUMBER TYPE IDENTIFIER ... (example is OPLS-UA)
135         //
136         // If there is no atom class, the exception will be caught
137         // and the atomClass field will remain equal to null.
138         try {
139           atomClass = parseInt(tokens[index]);
140           // If the parseInt succeeds, this force field has atom classes.
141           index++;
142         } catch (NumberFormatException e) {
143           // Some force fields do not use atom classes.
144           atomClass = -1;
145         }
146         // Name
147         String name = tokens[index].intern();
148         // The "environment" string may contain spaces,
149         // and is therefore surrounded in quotes located at "first" and
150         // "last".
151         int first = input.indexOf("\"");
152         int last = input.lastIndexOf("\"");
153         if (first >= last) {
154           logger.log(Level.WARNING, "Invalid ATOM type:\n{0}", input);
155           return null;
156         }
157         // Environment
158         String environment = input.substring(first, last + 1).intern();
159         // Shrink the tokens array to only include entries
160         // after the environment field.
161         tokens = input.substring(last + 1).trim().split(" +");
162         index = 0;
163         // Atomic Number
164         int atomicNumber = parseInt(tokens[index++]);
165         // Atomic Mass
166         double mass = parseDouble(tokens[index++]);
167         // Hybridization
168         int hybridization = parseInt(tokens[index]);
169 
170         AtomType atomType = new AtomType(type, atomClass, name, environment, atomicNumber, mass,
171             hybridization);
172         if (!checkAtomicNumberAndMass(atomicNumber, mass)) {
173           // Ignore united atom (UA) entries.
174           if (!environment.toUpperCase().contains("UA")) {
175             logger.warning(" Atomic number and weight do not agree:\n" + atomType);
176           }
177         }
178         return atomType;
179       } catch (NumberFormatException e) {
180         String message = "Exception parsing AtomType:\n" + input + "\n";
181         logger.log(Level.SEVERE, message, e);
182       }
183     }
184     return null;
185   }
186 
187   /** {@inheritDoc} */
188   @Override
189   public int compare(String s1, String s2) {
190     int t1 = parseInt(s1);
191     int t2 = parseInt(s2);
192     return Integer.compare(t1, t2);
193   }
194 
195   /** {@inheritDoc} */
196   @Override
197   public boolean equals(Object o) {
198     if (this == o) {
199       return true;
200     }
201     if (o == null || getClass() != o.getClass()) {
202       return false;
203     }
204     AtomType atomType = (AtomType) o;
205     return atomType.type == this.type;
206   }
207 
208   /** {@inheritDoc} */
209   @Override
210   public int hashCode() {
211     return Objects.hash(type);
212   }
213 
214   /**
215    * {@inheritDoc}
216    *
217    * <p>Nicely formatted atom type string.
218    */
219   @Override
220   public String toString() {
221     String s;
222     if (atomClass >= 0) {
223       s = format("atom  %5d  %5d  %-4s  %-25s  %3d  %8.4f  %d", type, atomClass, name, environment,
224           atomicNumber, atomicWeight, valence);
225     } else {
226       s = format("atom  %5d  %-4s  %-25s  %3d  %8.4f  %d", type, name, environment, atomicNumber,
227           atomicWeight, valence);
228     }
229     return s;
230   }
231 
232   /**
233    * incrementClassAndType
234    *
235    * @param classIncrement The value to increment the atom class by.
236    * @param typeIncrement The value to increment the atom type by.
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    * <a href="https://iupac.qmul.ac.uk/AtWt">IUPAC Commission</a> on Isotopic Abundances and Atomic
283    * Weights.
284    * Retrieved on 1/24/22.
285    */
286   private static final double[] atomicMass = { /* 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 }