1   
2   
3   
4   
5   
6   
7   
8   
9   
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  package ffx.potential.parameters;
39  
40  import ffx.potential.bonded.Atom;
41  import ffx.utilities.FFXProperty;
42  import org.w3c.dom.Document;
43  import org.w3c.dom.Element;
44  
45  import java.util.Comparator;
46  import java.util.Map;
47  import java.util.Objects;
48  import java.util.logging.Level;
49  import java.util.logging.Logger;
50  
51  import static ffx.potential.parameters.ForceField.ForceFieldType.ATOM;
52  import static ffx.utilities.PropertyGroup.PotentialFunctionParameter;
53  import static java.lang.Double.parseDouble;
54  import static java.lang.Integer.parseInt;
55  import static java.lang.String.format;
56  import static org.apache.commons.math3.util.FastMath.abs;
57  
58  
59  
60  
61  
62  
63  
64  @FFXProperty(name = "atom", clazz = String.class, propertyGroup = PotentialFunctionParameter, description = """
65      [2 integers, name, quoted string, integer, real and integer]
66      Provides the values needed to define a single force field atom type.
67      The first two integer modifiers denote the atom type and class numbers.
68      If the type and class are identical, only a single integer value is required.
69      The next modifier is a three-character atom name, followed by an 24-character or less atom description contained in single quotes.
70      The next two modifiers are the atomic number and atomic mass.
71      The final integer modifier is the "valence" of the atom, defined as the expected number of attached or bonded atoms.
72      """)
73  public final class AtomType extends BaseType implements Comparator<String> {
74  
75    
76  
77  
78    private static final Logger logger = Logger.getLogger(AtomType.class.getName());
79    
80  
81  
82    public final String name;
83    
84  
85  
86    public final String environment;
87    
88  
89  
90    public final int atomicNumber;
91    
92  
93  
94  
95  
96    public final double atomicWeight;
97    
98  
99  
100   public final int valence;
101   
102 
103 
104   public int type;
105   
106 
107 
108   public int atomClass;
109 
110   
111 
112 
113 
114 
115 
116 
117 
118 
119 
120 
121   public AtomType(int type, int atomClass, String name, String environment, int atomicNumber,
122                   double atomicWeight, int valence) {
123     super(ATOM, Integer.toString(type));
124     this.type = type;
125     this.atomClass = atomClass;
126     this.name = name;
127     this.environment = environment;
128     this.atomicNumber = atomicNumber;
129     this.atomicWeight = atomicWeight;
130     this.valence = valence;
131   }
132 
133   
134 
135 
136 
137 
138 
139 
140   public static AtomType parse(String input, String[] tokens) {
141     if (tokens.length < 7) {
142       logger.log(Level.WARNING, "Invalid ATOM type:\n{0}", input);
143     } else {
144       try {
145         int index = 1;
146         
147         int type = parseInt(tokens[index++]);
148         
149         int atomClass;
150         
151         
152         
153         
154         
155         
156         
157         
158         try {
159           atomClass = parseInt(tokens[index]);
160           
161           index++;
162         } catch (NumberFormatException e) {
163           
164           atomClass = -1;
165         }
166         
167         String name = tokens[index].intern();
168         
169         
170         
171         int first = input.indexOf("\"");
172         int last = input.lastIndexOf("\"");
173         if (first >= last) {
174           logger.log(Level.WARNING, "Invalid ATOM type:\n{0}", input);
175           return null;
176         }
177         
178         String environment = input.substring(first, last + 1).intern();
179         
180         
181         tokens = input.substring(last + 1).trim().split(" +");
182         index = 0;
183         
184         int atomicNumber = parseInt(tokens[index++]);
185         
186         double mass = parseDouble(tokens[index++]);
187         
188         int hybridization = parseInt(tokens[index]);
189 
190         AtomType atomType = new AtomType(type, atomClass, name, environment, atomicNumber, mass,
191             hybridization);
192         if (!checkAtomicNumberAndMass(atomicNumber, mass)) {
193           
194           if (!environment.toUpperCase().contains("UA")) {
195             logger.warning(" Atomic number and weight do not agree:\n" + atomType);
196           }
197         }
198         return atomType;
199       } catch (NumberFormatException e) {
200         String message = "Exception parsing AtomType:\n" + input + "\n";
201         logger.log(Level.SEVERE, message, e);
202       }
203     }
204     return null;
205   }
206 
207   
208 
209 
210   @Override
211   public int compare(String s1, String s2) {
212     int t1 = parseInt(s1);
213     int t2 = parseInt(s2);
214     return Integer.compare(t1, t2);
215   }
216 
217   
218 
219 
220   @Override
221   public boolean equals(Object o) {
222     if (this == o) {
223       return true;
224     }
225     if (o == null || getClass() != o.getClass()) {
226       return false;
227     }
228     AtomType atomType = (AtomType) o;
229     return atomType.type == this.type;
230   }
231 
232   
233 
234 
235   @Override
236   public int hashCode() {
237     return Objects.hash(type);
238   }
239 
240   
241 
242 
243 
244 
245   @Override
246   public String toString() {
247     String s;
248     if (atomClass >= 0) {
249       s = format("atom  %5d  %5d  %-4s  %-25s  %3d  %8.4f  %d", type, atomClass, name, environment,
250           atomicNumber, atomicWeight, valence);
251     } else {
252       s = format("atom  %5d  %-4s  %-25s  %3d  %8.4f  %d", type, name, environment, atomicNumber,
253           atomicWeight, valence);
254     }
255     return s;
256   }
257 
258   
259 
260 
261 
262 
263 
264 
265   public static Element getXMLAtomTypes(Document doc, ForceField forceField) {
266     Element node = doc.createElement("AtomTypes");
267     Map<String, AtomType> types = forceField.getAtomTypes();
268     for (AtomType atomType : types.values()) {
269       node.appendChild(atomType.toXML(doc));
270     }
271     return node;
272   }
273 
274   
275 
276 
277   public Element toXML(Document doc) {
278     Element node = doc.createElement("Type");
279     node.setAttribute("name", format("%d", type));
280     node.setAttribute("class", format("%d", atomClass));
281     if (atomicNumber >= 1) {
282       node.setAttribute("element", format("%s", Atom.ElementSymbol.values()[atomicNumber - 1]));
283     } else {
284       
285       node.setAttribute("element", "");
286     }
287     node.setAttribute("mass", format("%.3f", atomicWeight));
288     return node;
289   }
290 
291   
292 
293 
294 
295 
296 
297   void incrementClassAndType(int classIncrement, int typeIncrement) {
298     atomClass += classIncrement;
299     type += typeIncrement;
300     setKey(Integer.toString(type));
301   }
302 
303   
304 
305 
306 
307 
308 
309 
310 
311 
312 
313 
314   public static boolean checkAtomicNumberAndMass(int atomicNumber, double mass) {
315     return checkAtomicNumberAndMass(atomicNumber, mass, 0.1);
316   }
317 
318   
319 
320 
321 
322 
323 
324 
325 
326 
327 
328 
329 
330   public static boolean checkAtomicNumberAndMass(int atomicNumber, double mass, double tolerance) {
331     
332     if (atomicNumber == 0 || atomicNumber >= atomicMass.length) {
333       return true;
334     }
335 
336     double expected = atomicMass[atomicNumber - 1];
337     return abs(expected - mass) < tolerance;
338   }
339 
340   
341 
342 
343 
344 
345   public static final double[] atomicMass = {   1.008,
346        4.002,
347        6.94,
348        9.012,
349        10.81,
350        12.011,
351        14.007,
352        15.999,
353        18.998,
354        20.1797,
355        22.989,
356        24.305,
357        26.981,
358        28.085,
359        30.973,
360        32.06,
361        35.45,
362        39.948,
363        39.0983,
364        40.078,
365        44.955,
366        47.867,
367        50.9415,
368        51.9961,
369        54.938,
370        55.845,
371        58.933,
372        58.6934,
373        63.546,
374        65.38,
375        69.723,
376        72.630,
377        74.921,
378        78.971,
379        79.904,
380        83.798,
381        85.4678,
382        87.62,
383        88.905,
384        91.224,
385        92.906,
386        95.95,
387        97.0,
388        101.07,
389        102.905,
390        106.42,
391        107.8682,
392        112.414,
393        114.818,
394        118.710,
395        121.760,
396        127.60,
397        126.904,
398        131.293,
399        132.905,
400        137.327,
401        138.905,
402        140.116,
403        140.907,
404        144.242,
405        145.0,
406        150.36,
407        151.964,
408        157.25,
409        158.925,
410        162.500,
411        164.930,
412        167.259,
413        168.934,
414        173.045,
415        174.9668,
416        178.486,
417        180.947,
418        183.84,
419        186.207,
420        190.23,
421        192.217,
422        195.084,
423        196.966,
424        200.592,
425        204.38,
426        207.2,
427        208.980,
428        209.0,
429        210.0,
430        222.0,
431        223.0,
432        226.0,
433        227.0,
434        232.0377,
435        231.035,
436        238.028,
437        237.0,
438        244.0,
439        243.0,
440        247.0,
441        247.0,
442        251.0,
443        252.0,
444        257.0,
445        258.0,
446        259.0,
447        262.0,
448        267.0,
449        270.0,
450        269.0,
451        270.0,
452        270.0,
453        278.0,
454        281.0,
455        281.0,
456        285.0,
457        286.0,
458        289.0,
459        289.0,
460        293.0,
461        293.0,
462        294.0};
463 }