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 }