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 }