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.potential.bonded.Bond;
42 import ffx.utilities.FFXProperty;
43 import org.w3c.dom.Document;
44 import org.w3c.dom.Element;
45
46 import java.util.ArrayList;
47 import java.util.Arrays;
48 import java.util.Collections;
49 import java.util.Comparator;
50 import java.util.HashMap;
51 import java.util.List;
52 import java.util.Objects;
53 import java.util.logging.Level;
54 import java.util.logging.Logger;
55
56 import static ffx.potential.parameters.ForceField.ForceFieldType.POLARIZE;
57 import static ffx.utilities.Constants.ANG_TO_NM;
58 import static ffx.utilities.PropertyGroup.PotentialFunctionParameter;
59 import static java.lang.Double.parseDouble;
60 import static java.lang.Integer.parseInt;
61 import static java.lang.String.format;
62 import static java.lang.String.valueOf;
63 import static java.lang.System.arraycopy;
64 import static org.apache.commons.math3.util.FastMath.pow;
65
66
67
68
69
70
71
72
73
74 @FFXProperty(name = "polarize", clazz = String.class, propertyGroup = PotentialFunctionParameter, description = """
75 [1 integer, up to 3 reals and up to 8 integers]
76 Provides the values for a single atomic dipole polarizability parameter.
77 The initial integer modifier, if positive, gives the atom type number for which a polarizability parameter is to be defined.
78 If the first integer modifier is negative, then the parameter value to follow applies only to the specific atom whose atom number is the negative of the modifier.
79 The first real number modifier gives the value of the dipole polarizability in Ang^3.
80 The second real number modifier, if present, gives the Thole damping value.
81 A Thole value of zero implies undamped polarization.
82 The third real modifier, if present, gives a direct field damping value only used with the AMOEBA+ polarization model.
83 The remaining integer modifiers list the atom type numbers of atoms directly bonded to the current atom and which will be considered to be part of the current atom’s polarization group.
84 If the parameter is for a specific atom, then the integers defining the polarization group are ignored.
85 """)
86 public final class PolarizeType extends BaseType implements Comparator<String> {
87
88 private static final Logger logger = Logger.getLogger(PolarizeType.class.getName());
89
90 private static final double sixth = 1.0 / 6.0;
91 public static final double DEFAULT_DIRECT_11_SCALE = 0.0;
92 public static final double DEFAULT_DIRECT_12_SCALE = 1.0;
93 public static final double DEFAULT_DIRECT_13_SCALE = 1.0;
94 public static final double DEFAULT_DIRECT_14_SCALE = 1.0;
95 public static final double DEFAULT_POLAR_12_SCALE = 0.0;
96 public static final double DEFAULT_POLAR_13_SCALE = 0.0;
97 public static final double DEFAULT_POLAR_14_SCALE = 1.0;
98 public static final double DEFAULT_POLAR_15_SCALE = 1.0;
99 public static final double DEFAULT_POLAR_12_INTRA = 0.0;
100 public static final double DEFAULT_POLAR_13_INTRA = 0.0;
101 public static final double DEFAULT_POLAR_14_INTRA = 0.5;
102 public static final double DEFAULT_POLAR_15_INTRA = 1.0;
103
104
105
106 public final double thole;
107
108
109
110 public double pdamp;
111
112
113
114 public final double ddp;
115
116
117
118 public final double polarizability;
119
120
121
122 public int type;
123
124
125
126 public int[] polarizationGroup;
127
128
129
130
131
132
133
134
135
136 public PolarizeType(int atomType, double polarizability, double thole, double ddp,
137 int[] polarizationGroup) {
138 super(POLARIZE, Integer.toString(atomType));
139 this.type = atomType;
140 this.thole = thole;
141 this.polarizability = polarizability;
142 this.ddp = 0.0;
143 this.polarizationGroup = polarizationGroup;
144 if (thole == 0.0) {
145 pdamp = 0.0;
146 } else {
147 pdamp = pow(polarizability, sixth);
148 }
149 }
150
151
152
153
154
155
156
157 public PolarizeType(PolarizeType polarizeType, double polarizability) {
158 this(polarizeType.type, polarizability, polarizeType.thole, polarizeType.ddp,
159 Arrays.copyOf(polarizeType.polarizationGroup, polarizeType.polarizationGroup.length));
160
161 pdamp = polarizeType.pdamp;
162 }
163
164
165
166
167
168
169
170
171
172 public static void assignPolarizationGroups(Atom[] atoms, int[][] ip11, int[][] ip12,
173 int[][] ip13) {
174
175
176 List<Integer> group = new ArrayList<>();
177 List<Integer> polarizationGroup = new ArrayList<>();
178 for (Atom ai : atoms) {
179 group.clear();
180 polarizationGroup.clear();
181 int index = ai.getIndex() - 1;
182 group.add(index);
183 polarizationGroup.add(ai.getType());
184 PolarizeType polarizeType = ai.getPolarizeType();
185 if (polarizeType != null) {
186 if (polarizeType.polarizationGroup != null) {
187 for (int i : polarizeType.polarizationGroup) {
188 if (!polarizationGroup.contains(i)) {
189 polarizationGroup.add(i);
190 }
191 }
192 growGroup(polarizationGroup, group, ai);
193 Collections.sort(group);
194 ip11[index] = new int[group.size()];
195 int j = 0;
196 for (int k : group) {
197 ip11[index][j++] = k;
198 }
199 } else {
200 ip11[index] = new int[group.size()];
201 int j = 0;
202 for (int k : group) {
203 ip11[index][j++] = k;
204 }
205 }
206 } else {
207 String message = "The polarize keyword was not found for atom " + (index + 1) + " with type "
208 + ai.getType();
209 logger.severe(message);
210 }
211 }
212
213
214 int nAtoms = atoms.length;
215 int[] mask = new int[nAtoms];
216 List<Integer> list = new ArrayList<>();
217 List<Integer> keep = new ArrayList<>();
218 for (int i = 0; i < nAtoms; i++) {
219 mask[i] = -1;
220 }
221 for (int i = 0; i < nAtoms; i++) {
222 list.clear();
223 for (int j : ip11[i]) {
224 list.add(j);
225 mask[j] = i;
226 }
227 keep.clear();
228 for (int j : list) {
229 Atom aj = atoms[j];
230 List<Bond> bonds = aj.getBonds();
231 for (Bond b : bonds) {
232 Atom ak = b.get1_2(aj);
233 int k = ak.getIndex() - 1;
234 if (mask[k] != i) {
235 keep.add(k);
236 }
237 }
238 }
239 list.clear();
240 for (int j : keep) {
241 for (int k : ip11[j]) {
242 list.add(k);
243 }
244 }
245 Collections.sort(list);
246 ip12[i] = new int[list.size()];
247 int j = 0;
248 for (int k : list) {
249 ip12[i][j++] = k;
250 }
251 }
252
253
254 for (int i = 0; i < nAtoms; i++) {
255 mask[i] = -1;
256 }
257 for (int i = 0; i < nAtoms; i++) {
258 for (int j : ip11[i]) {
259 mask[j] = i;
260 }
261 for (int j : ip12[i]) {
262 mask[j] = i;
263 }
264 list.clear();
265 for (int j : ip12[i]) {
266 for (int k : ip12[j]) {
267 if (mask[k] != i) {
268 if (!list.contains(k)) {
269 list.add(k);
270 }
271 }
272 }
273 }
274 ip13[i] = new int[list.size()];
275 Collections.sort(list);
276 int j = 0;
277 for (int k : list) {
278 ip13[i][j++] = k;
279 }
280 }
281 }
282
283
284
285
286
287
288
289
290
291
292
293 public static PolarizeType average(PolarizeType polarizeType1, PolarizeType polarizeType2,
294 int atomType, int[] polarizationGroup) {
295 if (polarizeType1 == null || polarizeType2 == null) {
296 return null;
297 }
298 double thole = (polarizeType1.thole + polarizeType2.thole) / 2.0;
299 double polarizability = (polarizeType1.polarizability + polarizeType2.polarizability) / 2.0;
300 double ddp = (polarizeType1.ddp + polarizeType2.ddp) / 2.0;
301 return new PolarizeType(atomType, polarizability, thole, ddp, polarizationGroup);
302 }
303
304
305
306
307
308
309
310
311 public static PolarizeType parse(String input, String[] tokens) {
312 if (tokens.length < 4) {
313 logger.log(Level.WARNING, "Invalid POLARIZE type:\n{0}", input);
314 } else {
315 try {
316 int atomType = parseInt(tokens[1]);
317 double polarizability = parseDouble(tokens[2]);
318 double thole = parseDouble(tokens[3]);
319
320
321 double ddp = 0.0;
322 int nextToken = 4;
323 try {
324
325
326 parseInt(tokens[nextToken]);
327 } catch (NumberFormatException e) {
328 ddp = parseDouble(tokens[nextToken]);
329 nextToken = 5;
330 } catch (ArrayIndexOutOfBoundsException e) {
331
332 }
333 int entries = tokens.length - nextToken;
334 int[] polarizationGroup = null;
335 if (entries > 0) {
336 polarizationGroup = new int[entries];
337 for (int i = nextToken; i < tokens.length; i++) {
338 polarizationGroup[i - nextToken] = parseInt(tokens[i]);
339 }
340 }
341 return new PolarizeType(atomType, polarizability, thole, ddp, polarizationGroup);
342 } catch (NumberFormatException e) {
343 String message = "Exception parsing POLARIZE type:\n" + input + "\n";
344 logger.log(Level.SEVERE, message, e);
345 }
346 }
347 return null;
348 }
349
350
351
352
353
354
355
356
357
358 private static void growGroup(List<Integer> polarizationGroup, List<Integer> group, Atom seed) {
359 List<Bond> bonds = seed.getBonds();
360 for (Bond bi : bonds) {
361 Atom aj = bi.get1_2(seed);
362 int tj = aj.getType();
363 boolean added = false;
364 for (int g : polarizationGroup) {
365 if (g == tj) {
366 Integer index = aj.getIndex() - 1;
367 if (!group.contains(index)) {
368 group.add(index);
369 added = true;
370 break;
371 }
372 }
373 }
374 if (added) {
375 PolarizeType polarizeType = aj.getPolarizeType();
376 for (int i : polarizeType.polarizationGroup) {
377 if (!polarizationGroup.contains(i)) {
378 polarizationGroup.add(i);
379 }
380 }
381 growGroup(polarizationGroup, group, aj);
382 }
383 }
384 }
385
386
387
388
389
390
391
392
393 public static void growGroup(List<Integer> group, Atom seed) {
394 List<Bond> bonds = seed.getBonds();
395 for (Bond bond : bonds) {
396 Atom atom = bond.get1_2(seed);
397 int tj = atom.getType();
398 boolean added = false;
399 PolarizeType polarizeType = seed.getPolarizeType();
400 for (int type : polarizeType.polarizationGroup) {
401 if (type == tj) {
402 Integer index = atom.getIndex() - 1;
403 if (!group.contains(index)) {
404 group.add(index);
405 added = true;
406 break;
407 }
408 }
409 }
410 if (added) {
411 growGroup(group, atom);
412 }
413 }
414 }
415
416
417
418
419
420
421 public void add(int atomType) {
422 for (int i : polarizationGroup) {
423 if (atomType == i) {
424 return;
425 }
426 }
427 int len = polarizationGroup.length;
428 int[] newGroup = new int[len + 1];
429 arraycopy(polarizationGroup, 0, newGroup, 0, len);
430 newGroup[len] = atomType;
431 polarizationGroup = newGroup;
432 }
433
434
435
436
437 @Override
438 public int compare(String s1, String s2) {
439 int t1 = parseInt(s1);
440 int t2 = parseInt(s2);
441 return Integer.compare(t1, t2);
442 }
443
444
445
446
447 @Override
448 public boolean equals(Object o) {
449 if (this == o) {
450 return true;
451 }
452 if (o == null || getClass() != o.getClass()) {
453 return false;
454 }
455 PolarizeType polarizeType = (PolarizeType) o;
456 return polarizeType.type == this.type;
457 }
458
459
460
461
462 @Override
463 public int hashCode() {
464 return Objects.hash(type);
465 }
466
467
468
469
470
471
472 @Override
473 public String toString() {
474 StringBuilder polarizeString = new StringBuilder(
475 format("polarize %5d %8.5f %8.5f", type, polarizability, thole));
476 if (ddp != 0.0) {
477 polarizeString.append(format(" %8.5f", ddp));
478 }
479 if (polarizationGroup != null) {
480 for (int a : polarizationGroup) {
481 polarizeString.append(format(" %5d", a));
482 }
483 }
484 return polarizeString.toString();
485 }
486
487
488
489
490
491
492
493 public static void addXMLAttributes(Element node, ForceField forceField) {
494 node.setAttribute("direct11Scale", valueOf(forceField.getDouble("direct-11-scale", DEFAULT_DIRECT_11_SCALE)));
495 node.setAttribute("direct12Scale", valueOf(forceField.getDouble("direct-12-scale", DEFAULT_DIRECT_12_SCALE)));
496 node.setAttribute("direct13Scale", valueOf(forceField.getDouble("direct-13-scale", DEFAULT_DIRECT_13_SCALE)));
497 node.setAttribute("direct14Scale", valueOf(forceField.getDouble("direct-14-scale", DEFAULT_DIRECT_14_SCALE)));
498 node.setAttribute("mutual11Scale", valueOf(forceField.getDouble("mutual-11-scale", 1.0)));
499 node.setAttribute("mutual12Scale", valueOf(forceField.getDouble("mutual-12-scale", 1.0)));
500 node.setAttribute("mutual13Scale", valueOf(forceField.getDouble("mutual-13-scale", 1.0)));
501 node.setAttribute("mutual14Scale", valueOf(forceField.getDouble("mutual-14-scale", 1.0)));
502 node.setAttribute("polar12Scale", valueOf(forceField.getDouble("polar-12-scale", DEFAULT_POLAR_12_SCALE)));
503 node.setAttribute("polar13Scale", valueOf(forceField.getDouble("polar-13-scale", DEFAULT_POLAR_13_SCALE)));
504 node.setAttribute("polar14Scale", valueOf(forceField.getDouble("polar-14-scale", DEFAULT_POLAR_14_SCALE)));
505 node.setAttribute("polar15Scale", valueOf(forceField.getDouble("polar-15-scale", DEFAULT_POLAR_15_SCALE)));
506
507
508 node.setAttribute("polar14Intra", valueOf(forceField.getDouble("polar-14-intra", DEFAULT_POLAR_14_INTRA)));
509 }
510
511
512
513
514 public Element toXML(Document doc) {
515 Element node = doc.createElement("Polarize");
516 node.setAttribute("type", format("%d", type));
517
518 node.setAttribute("polarizability", format("%f", polarizability * ANG_TO_NM * ANG_TO_NM * ANG_TO_NM));
519 node.setAttribute("thole", format("%f", thole));
520
521 int i = 1;
522 if (polarizationGroup != null) {
523 for (int a : polarizationGroup) {
524 node.setAttribute(format("pgrp%d", i), format("%d", a));
525 i++;
526 }
527 }
528 return node;
529 }
530
531
532
533
534
535
536 void incrementType(int increment) {
537 type += increment;
538 setKey(Integer.toString(type));
539 if (polarizationGroup != null) {
540 for (int i = 0; i < polarizationGroup.length; i++) {
541 polarizationGroup[i] += increment;
542 }
543 }
544 }
545
546
547
548
549
550
551
552 boolean patchTypes(HashMap<AtomType, AtomType> typeMap) {
553 if (polarizationGroup == null) {
554 return false;
555 }
556
557
558 int len = polarizationGroup.length;
559 int added = 0;
560 for (AtomType newType : typeMap.keySet()) {
561 for (int i = 1; i < len; i++) {
562 if (polarizationGroup[i] == newType.type) {
563 AtomType knownType = typeMap.get(newType);
564 added++;
565 polarizationGroup = Arrays.copyOf(polarizationGroup, len + added);
566 polarizationGroup[len + added - 1] = knownType.type;
567 }
568 }
569 }
570
571 return added > 0;
572 }
573 }