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 static ffx.potential.parameters.ForceField.ForceFieldType.TORTORS;
41 import static ffx.utilities.PropertyGroup.EnergyUnitConversion;
42 import static ffx.utilities.PropertyGroup.PotentialFunctionParameter;
43 import static java.lang.Double.parseDouble;
44 import static java.lang.Integer.parseInt;
45 import static java.lang.String.format;
46 import static java.lang.System.arraycopy;
47 import static java.util.Arrays.sort;
48 import static org.apache.commons.math3.util.FastMath.abs;
49
50 import ffx.utilities.FFXProperty;
51
52 import java.io.BufferedReader;
53 import java.io.IOException;
54 import java.util.Arrays;
55 import java.util.Comparator;
56 import java.util.HashMap;
57 import java.util.logging.Level;
58 import java.util.logging.Logger;
59
60
61
62
63
64
65
66 @FFXProperty(name = "tortors", clazz = String[].class, propertyGroup = PotentialFunctionParameter, description = """
67 [7 integers, then multiple lines of 2 integers and 1 real]
68 Provides the values for a single torsion-torsion parameter.
69 The first five integer modifiers give the atom class numbers for the atoms involved in the two adjacent torsional angles to be defined.
70 The last two integer modifiers contain the number of data grid points that lie along each axis of the torsion-torsion map.
71 For example, this value will be 13 for a 30 degree torsional angle spacing, i.e., 360/30 = 12, but 13
72 values are required since data values for -180 and +180 degrees must both be supplied.
73 The subsequent lines contain the torsion-torsion map data as the integer values in degrees of each
74 torsional angle and the target energy value in kcal/mole.
75 """)
76 public final class TorsionTorsionType extends BaseType implements Comparator<String> {
77
78
79
80
81 public static final double DEFAULT_TORTOR_UNIT = 1.0;
82
83
84
85 @FFXProperty(name = "tortorunit", propertyGroup = EnergyUnitConversion, defaultValue = "1.0", description = """
86 Sets the scale factor needed to convert the energy value computed by the torsion-torsion potential into units of kcal/mole.
87 The correct value is force field dependent and typically provided in the header of the master force field parameter file.
88 """)
89 public double torTorUnit = DEFAULT_TORTOR_UNIT;
90
91 private static final Logger logger = Logger.getLogger(TorsionTorsionType.class.getName());
92
93
94
95 public final int[] atomClasses;
96
97
98
99 public final double[] energy;
100
101
102
103 public final int nx;
104
105
106
107 public final int ny;
108
109
110
111 public final double[] tx;
112
113
114
115 public final double[] ty;
116
117
118
119 public final double[] dx;
120
121
122
123 public final double[] dy;
124
125
126
127 public final double[] dxy;
128
129
130
131 private final int[] gridPoints;
132
133
134
135
136
137
138
139
140
141
142 public TorsionTorsionType(int[] atomClasses, int[] gridPoints, double[] torsion1,
143 double[] torsion2, double[] energy) {
144 super(TORTORS, sortKey(atomClasses));
145 this.atomClasses = atomClasses;
146 nx = gridPoints[0];
147 ny = gridPoints[1];
148 if (nx != ny) {
149 logger.severe("Untested TORTOR parameters: nx != ny: " + nx + ", " + ny);
150 }
151
152 this.energy = energy;
153 this.gridPoints = gridPoints;
154 tx = new double[nx];
155 ty = new double[ny];
156 dx = new double[nx * ny];
157 dy = new double[nx * ny];
158 dxy = new double[nx * ny];
159 sort(torsion1);
160 sort(torsion2);
161 tx[0] = torsion1[0];
162 ty[0] = torsion2[0];
163 int j1 = 1;
164 int j2 = 1;
165 for (int i = 1; i < nx; i++) {
166 while (torsion1[j1] == tx[i - 1]) {
167 j1++;
168 }
169 while (torsion2[j2] == ty[i - 1]) {
170 j2++;
171 }
172 tx[i] = torsion1[j1];
173 ty[i] = torsion2[j2];
174 }
175
176
177 boolean isCyclic = true;
178 double eps = 0.0001;
179 if (abs(tx[0] - tx[nx - 1]) - 360.0 > eps) {
180 isCyclic = false;
181 if (logger.isLoggable(Level.FINEST)) {
182 logger.finest(" tortor is aperiodic: " + tx[0] + ", " + tx[nx - 1]);
183 }
184 }
185 if (isCyclic) {
186 for (int i = 0; i < ny; i++) {
187 int k = i * nx;
188 if (abs(energy[k] - energy[k + nx - 1]) > eps) {
189 isCyclic = false;
190 if (logger.isLoggable(Level.FINEST)) {
191 logger.finest(" tortor is apreriodic: " + k + ", " + (k + nx - 1) + ": " + abs(
192 energy[k] - energy[k + nx - 1]));
193 }
194 break;
195 }
196 }
197 }
198 if (isCyclic) {
199 int k = (ny - 1) * nx;
200 for (int i = 0; i < nx; i++) {
201 if (abs(energy[i] - energy[i + k]) > eps) {
202 if (logger.isLoggable(Level.FINEST)) {
203 logger.fine(
204 " tortor is aperiodic: " + i + ", " + i + k + ": " + abs(energy[i] - energy[i + k]));
205 }
206 isCyclic = false;
207 break;
208 }
209 }
210 }
211
212 boolean cyclic = isCyclic;
213 double[] tmp1 = new double[nx];
214 double[] tmp2 = new double[nx];
215 double[] tmp3 = new double[nx];
216 double[] tmp4 = new double[nx];
217 double[] tmp5 = new double[nx];
218 double[] tmp6 = new double[nx];
219 double[] tmp7 = new double[nx];
220 double[] bs = new double[nx];
221 double[] cs = new double[nx];
222 double[] ds = new double[nx];
223
224
225 arraycopy(tx, 0, tmp1, 0, nx);
226
227 int m = 0;
228 for (int j = 0; j < ny; j++) {
229 arraycopy(energy, m, tmp2, 0, nx);
230 if (cyclic) {
231 cspline(nx - 1, tmp1, tmp2, bs, cs, ds, tmp3, tmp4, tmp5, tmp6, tmp7);
232 } else {
233 nspline(nx - 1, tmp1, tmp2, 0.0, 0.0, bs, cs, tmp3, tmp4, tmp5, tmp6, tmp7);
234 }
235 arraycopy(bs, 0, dx, m, nx);
236 m = m + nx;
237 }
238
239
240 arraycopy(ty, 0, tmp1, 0, ny);
241
242 m = 0;
243 for (int j = 0; j < nx; j++) {
244 for (int k = 0; k < ny; k++) {
245 tmp2[k] = energy[m + k * nx];
246 }
247 if (cyclic) {
248 cspline(ny - 1, tmp1, tmp2, bs, cs, ds, tmp3, tmp4, tmp5, tmp6, tmp7);
249 } else {
250 nspline(ny - 1, tmp1, tmp2, 0.0, 0.0, bs, cs, tmp3, tmp4, tmp5, tmp6, tmp7);
251 }
252 for (int k = 0; k < ny; k++) {
253 dy[m + k * nx] = bs[k];
254 }
255 m = m + 1;
256 }
257
258
259 m = 0;
260 for (int j = 0; j < nx; j++) {
261 for (int k = 0; k < ny; k++) {
262 tmp2[k] = dx[m + k * nx];
263 }
264 if (cyclic) {
265 cspline(ny - 1, tmp1, tmp2, bs, cs, ds, tmp3, tmp4, tmp5, tmp6, tmp7);
266 } else {
267 nspline(ny - 1, tmp1, tmp2, 0.0, 0.0, bs, cs, tmp3, tmp4, tmp5, tmp6, tmp7);
268 }
269 for (int k = 0; k < ny; k++) {
270 dxy[m + k * nx] = bs[k];
271 }
272 m = m + 1;
273 }
274 }
275
276
277
278
279
280
281
282
283
284 public static TorsionTorsionType average(TorsionTorsionType torsionTorsionType1,
285 TorsionTorsionType torsionTorsionType2, int[] atomClasses) {
286 if (torsionTorsionType1 == null || torsionTorsionType2 == null || atomClasses == null) {
287 return null;
288 }
289 return null;
290 }
291
292
293
294
295
296
297
298
299
300 public static TorsionTorsionType parse(String input, String[] tokens, BufferedReader br) {
301 if (tokens.length < 8) {
302 logger.log(Level.WARNING, "Invalid TORTORS type:\n{0}", input);
303 } else {
304 try {
305 int[] atomClasses = new int[5];
306 for (int i = 0; i < 5; i++) {
307 atomClasses[i] = parseInt(tokens[i + 1]);
308 }
309 int[] gridPoints = new int[2];
310 gridPoints[0] = parseInt(tokens[6]);
311 gridPoints[1] = parseInt(tokens[7]);
312 int points = gridPoints[0] * gridPoints[1];
313 double[] torsion1 = new double[points];
314 double[] torsion2 = new double[points];
315 double[] energy = new double[points];
316 for (int i = 0; i < points; i++) {
317 input = br.readLine();
318 tokens = input.trim().split(" +");
319 if (tokens.length != 3) {
320 logger.log(Level.WARNING, "Invalid TORTORS type:\n{0}", input);
321 return null;
322 }
323 torsion1[i] = parseDouble(tokens[0]);
324 torsion2[i] = parseDouble(tokens[1]);
325 energy[i] = parseDouble(tokens[2]);
326 }
327 return new TorsionTorsionType(atomClasses, gridPoints, torsion1, torsion2, energy);
328 } catch (NumberFormatException | IOException e) {
329 String message = "Exception parsing TORTORS type:\n" + input + "\n";
330 logger.log(Level.SEVERE, message, e);
331 }
332 }
333 return null;
334 }
335
336
337
338
339
340
341
342
343 public static TorsionTorsionType parse(String input, String[] tokens) {
344 if (tokens.length < 8) {
345 logger.log(Level.WARNING, "Invalid TORTORS type:\n{0}", input);
346 } else {
347 try {
348 int[] atomClasses = new int[5];
349 for (int i = 0; i < 5; i++) {
350 atomClasses[i] = parseInt(tokens[i + 1]);
351 }
352 int[] gridPoints = new int[2];
353 gridPoints[0] = parseInt(tokens[6]);
354 gridPoints[1] = parseInt(tokens[7]);
355 int points = gridPoints[0] * gridPoints[1];
356 int numTokens = points * 3 + 8;
357 if (tokens.length < numTokens) {
358 logger.log(Level.WARNING, "Invalid TORTORS type:\n{0}", input);
359 return null;
360 }
361 double[] torsion1 = new double[points];
362 double[] torsion2 = new double[points];
363 double[] energy = new double[points];
364 int index = 8;
365 for (int i = 0; i < points; i++) {
366 torsion1[i] = parseDouble(tokens[index++]);
367 torsion2[i] = parseDouble(tokens[index++]);
368 energy[i] = parseDouble(tokens[index++]);
369 }
370 return new TorsionTorsionType(atomClasses, gridPoints, torsion1, torsion2, energy);
371 } catch (NumberFormatException e) {
372 String message = "Exception parsing TORTORS type:\n" + input + "\n";
373 logger.log(Level.SEVERE, message, e);
374 }
375 }
376 return null;
377 }
378
379
380
381
382
383
384
385 public static String reverseKey(int[] c) {
386 return c[4] + " " + c[3] + " " + c[2] + " " + c[1] + " " + c[0];
387 }
388
389
390
391
392
393
394
395 public static String sortKey(int[] c) {
396 return c[0] + " " + c[1] + " " + c[2] + " " + c[3] + " " + c[4];
397 }
398
399
400
401
402
403
404
405
406
407
408
409
410 private static int cytsy(int n, double[] dm, double[] du, double[] cr, double[] rs, double[] c) {
411 if (n < 3) {
412 return -2;
413 }
414
415
416 if (cytsyp(n, dm, du, cr) == 1) {
417
418 cytsys(n, dm, du, cr, rs, c);
419 return 1;
420 }
421 return -1;
422 }
423
424
425
426
427
428
429
430
431
432
433
434 private static int cytsyp(int n, double[] dm, double[] du, double[] cr) {
435 double eps = 0.00000001;
436
437 if (n < 3) {
438 return -2;
439 }
440
441
442 double row = abs(dm[1]) + abs(du[1]) + abs(du[n]);
443 if (row == 0.0) {
444 return 0;
445 }
446 double d = 1.0 / row;
447 if (dm[1] < 0.0) {
448 return -1;
449 } else if (abs(dm[1]) * d < eps) {
450 return 0;
451 }
452
453
454 double temp1 = du[1];
455 double temp2 = 0.0;
456 du[1] = du[1] / dm[1];
457 cr[1] = du[n] / dm[1];
458 for (int i = 2; i < n; i++) {
459 row = abs(dm[i]) + abs(du[i]) + abs(temp1);
460 if (row == 0.0) {
461 return 0;
462 }
463 d = 1.0 / row;
464 dm[i] = dm[i] - temp1 * du[i - 1];
465 if (dm[i] < 0.0) {
466 return -1;
467 } else if (abs(dm[i]) * d < eps) {
468 return 0;
469 }
470 if (i < (n - 1)) {
471 cr[i] = -temp1 * cr[i - 1] / dm[i];
472 temp1 = du[i];
473 du[i] = du[i] / dm[i];
474 } else {
475 temp2 = du[i];
476 du[i] = (du[i] - temp1 * cr[i - 1]) / dm[i];
477 }
478 }
479 row = abs(du[n]) + abs(dm[n]) + abs(temp2);
480 if (row == 0.0) {
481 return 0;
482 }
483 d = 1.0 / row;
484 dm[n] = dm[n] - dm[n - 1] * du[n - 1] * du[n - 1];
485 temp1 = 0.0;
486 for (int i = 1; i < n - 1; i++) {
487 temp1 = temp1 + dm[i] * cr[i] * cr[i];
488 }
489 dm[n] = dm[n] - temp1;
490 if (dm[n] < 0.0) {
491 return -1;
492 } else if (abs(dm[n]) * d < eps) {
493 return 0;
494 }
495 return 1;
496 }
497
498
499
500
501
502
503
504
505
506
507
508 private static void cytsys(int n, double[] dm, double[] du, double[] cr, double[] rs, double[] c) {
509
510 double temp = rs[1];
511 rs[1] = temp / dm[1];
512 double sum = cr[1] * temp;
513 for (int i = 2; i < n; i++) {
514 temp = rs[i] - du[i - 1] * temp;
515 rs[i] = temp / dm[i];
516 if (i != (n - 1)) {
517 sum = sum + cr[i] * temp;
518 }
519 }
520 temp = rs[n] - du[n - 1] * temp;
521 temp = temp - sum;
522 rs[n] = temp / dm[n];
523
524
525 c[n] = rs[n];
526 c[n - 1] = rs[n - 1] - du[n - 1] * c[n];
527 for (int i = n - 2; i >= 1; i--) {
528 c[i] = rs[i] - du[i] * c[i + 1] - cr[i] * c[n];
529 }
530 }
531
532
533
534
535 @Override
536 public int compare(String key1, String key2) {
537 String[] keys1 = key1.split(" ");
538 String[] keys2 = key2.split(" ");
539 int c1 = parseInt(keys1[2]);
540 int c2 = parseInt(keys2[2]);
541 if (c1 < c2) {
542 return -1;
543 } else if (c1 > c2) {
544 return 1;
545 }
546 return 0;
547 }
548
549
550
551
552 @Override
553 public boolean equals(Object o) {
554 if (this == o) {
555 return true;
556 }
557 if (o == null || getClass() != o.getClass()) {
558 return false;
559 }
560 TorsionTorsionType torsionTorsionType = (TorsionTorsionType) o;
561 return Arrays.equals(atomClasses, torsionTorsionType.atomClasses);
562 }
563
564
565
566
567 @Override
568 public int hashCode() {
569 return Arrays.hashCode(atomClasses);
570 }
571
572
573
574
575
576
577 public void incrementClasses(int increment) {
578 for (int i = 0; i < atomClasses.length; i++) {
579 atomClasses[i] += increment;
580 }
581 setKey(sortKey(atomClasses));
582 }
583
584
585
586
587
588
589 public void patchClasses(HashMap<AtomType, AtomType> typeMap) {
590 int count = 0;
591 for (AtomType newType : typeMap.keySet()) {
592
593 for (int atomClass : atomClasses) {
594 if (atomClass == newType.atomClass) {
595 count++;
596 }
597 }
598 }
599 if (count > 0 && count < atomClasses.length) {
600 for (AtomType newType : typeMap.keySet()) {
601 for (int i = 0; i < atomClasses.length; i++) {
602 if (atomClasses[i] == newType.atomClass) {
603 AtomType knownType = typeMap.get(newType);
604 atomClasses[i] = knownType.atomClass;
605 }
606 }
607 }
608 setKey(sortKey(atomClasses));
609 }
610 }
611
612
613
614
615
616
617 @Override
618 public String toString() {
619 StringBuilder tortorBuffer = new StringBuilder("tortors");
620 for (int i : atomClasses) {
621 tortorBuffer.append(format(" %5d", i));
622 }
623 tortorBuffer.append(format(" %2d %2d", gridPoints[0], gridPoints[1]));
624 for (int i = 0; i < energy.length; i++) {
625 int nxi = i % nx;
626 int nyi = i / ny;
627 tortorBuffer.append(format(" \\\n % 6.1f % 6.1f % 8.5f", tx[nxi], ty[nyi], energy[i]));
628 }
629 return tortorBuffer.toString();
630 }
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648 private void nspline(int n, double[] x0, double[] y0, double y21, double y2n, double[] s1,
649 double[] s2, double[] h, double[] g, double[] dy, double[] dla, double[] dmu) {
650
651
652 for (int i = 0; i < n; i++) {
653 h[i] = x0[i + 1] - x0[i];
654 dy[i] = (y0[i + 1] - y0[i]) / h[i];
655 }
656
657
658 for (int i = 1; i < n; i++) {
659 dla[i] = h[i] / (h[i] + h[i - 1]);
660 dmu[i] = 1.0 - dla[i];
661 g[i] = 3.0 * (dla[i] * dy[i - 1] + dmu[i] * dy[i]);
662 }
663
664
665 dla[n] = 1.0;
666 dla[0] = 0.0;
667 dmu[n] = 0.0;
668 dmu[0] = 1.0;
669 g[0] = 3.0 * dy[0] - 0.5 * h[0] * y21;
670 g[n] = 3.0 * dy[n - 1] + 0.5 * h[n - 1] * y2n;
671
672
673 dmu[0] = 0.5 * dmu[0];
674 g[0] = 0.5 * g[0];
675 for (int i = 1; i <= n; i++) {
676 double t = 2.0 - dmu[i - 1] * dla[i];
677 dmu[i] = dmu[i] / t;
678 g[i] = (g[i] - g[i - 1] * dla[i]) / t;
679 }
680
681 for (int i = n - 1; i >= 0; i--) {
682 g[i] = g[i] - dmu[i] * g[i + 1];
683 }
684
685
686 arraycopy(g, 0, s1, 0, n + 1);
687
688
689 s2[0] = y21;
690 s2[n] = y2n;
691 for (int i = 1; i < n; i++) {
692 s2[i] =
693 6.0 * (y0[i + 1] - y0[i]) / (h[i] * h[i]) - 4.0 * s1[i] / h[i] - 2.0 * s1[i + 1] / h[i];
694 }
695 }
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711 private void cspline(int n, double[] xn, double[] fn, double[] b, double[] c, double[] d,
712 double[] h, double[] du, double[] dm, double[] rc, double[] rs) {
713 double eps = 0.000001;
714 if (abs(fn[n] - fn[0]) > eps) {
715 logger.severe("TORTOR values are not periodic.");
716 }
717
718
719 double mean = 0.5 * (fn[0] + fn[n]);
720 fn[0] = mean;
721 fn[n] = mean;
722
723
724 for (int i = 0; i < n; i++) {
725 h[i] = xn[i + 1] - xn[i];
726 }
727 h[n] = h[0];
728
729 if (n - 1 >= 0) {
730 arraycopy(h, 1, du, 1, n - 1);
731 }
732
733 du[n] = h[0];
734 for (int i = 1; i <= n; i++) {
735 dm[i] = 2.0 * (h[i - 1] + h[i]);
736 }
737
738
739 double temp1 = (fn[1] - fn[0]) / h[0];
740 for (int i = 1; i < n; i++) {
741 double temp2 = (fn[i + 1] - fn[i]) / h[i];
742 rs[i] = 3.0 * (temp2 - temp1);
743 temp1 = temp2;
744 }
745 rs[n] = 3.0 * ((fn[1] - fn[0]) / h[0] - temp1);
746
747
748 if (cytsy(n, dm, du, rc, rs, c) != 1) {
749 return;
750 }
751
752
753 c[0] = c[n];
754 for (int i = 0; i < n; i++) {
755 b[i] = (fn[i + 1] - fn[i]) / h[i] - h[i] / 3.0 * (c[i + 1] + 2.0 * c[i]);
756 d[i] = (c[i + 1] - c[i]) / (3.0 * h[i]);
757 }
758 b[n] = (fn[1] - fn[n]) / h[n] - h[n] / 3.0 * (c[1] + 2.0 * c[n]);
759 }
760 }