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 }