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.bonded;
39
40 import static ffx.numerics.math.DoubleMath.sub;
41 import static org.apache.commons.math3.util.FastMath.acos;
42 import static org.apache.commons.math3.util.FastMath.max;
43 import static org.apache.commons.math3.util.FastMath.min;
44 import static org.apache.commons.math3.util.FastMath.sqrt;
45 import static org.apache.commons.math3.util.FastMath.toDegrees;
46
47 import ffx.numerics.atomic.AtomicDoubleArray3D;
48 import ffx.potential.parameters.ForceField;
49 import ffx.potential.parameters.TorsionTorsionType;
50
51 import java.io.Serial;
52 import java.util.List;
53 import java.util.logging.Logger;
54
55
56
57
58
59
60
61 public class TorsionTorsion extends BondedTerm implements LambdaInterface {
62
63 @Serial
64 private static final long serialVersionUID = 1L;
65
66 private static final Logger logger = Logger.getLogger(TorsionTorsion.class.getName());
67 private static final double[][] wt = {
68 {1.0, 0.0, -3.0, 2.0, 0.0, 0.0, 0.0, 0.0, -3.0, 0.0, 9.0, -6.0, 2.0, 0.0, -6.0, 4.0},
69 {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 3.0, 0.0, -9.0, 6.0, -2.0, 0.0, 6.0, -4.0},
70 {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 9.0, -6.0, 0.0, 0.0, -6.0, 4.0},
71 {0.0, 0.0, 3.0, -2.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -9.0, 6.0, 0.0, 0.0, 6.0, -4.0},
72 {0.0, 0.0, 0.0, 0.0, 1.0, 0.0, -3.0, 2.0, -2.0, 0.0, 6.0, -4.0, 1.0, 0.0, -3.0, 2.0},
73 {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, 3.0, -2.0, 1.0, 0.0, -3.0, 2.0},
74 {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -3.0, 2.0, 0.0, 0.0, 3.0, -2.0},
75 {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 3.0, -2.0, 0.0, 0.0, -6.0, 4.0, 0.0, 0.0, 3.0, -2.0},
76 {0.0, 1.0, -2.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, -3.0, 6.0, -3.0, 0.0, 2.0, -4.0, 2.0},
77 {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 3.0, -6.0, 3.0, 0.0, -2.0, 4.0, -2.0},
78 {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -3.0, 3.0, 0.0, 0.0, 2.0, -2.0},
79 {0.0, 0.0, -1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 3.0, -3.0, 0.0, 0.0, -2.0, 2.0},
80 {0.0, 0.0, 0.0, 0.0, 0.0, 1.0, -2.0, 1.0, 0.0, -2.0, 4.0, -2.0, 0.0, 1.0, -2.0, 1.0},
81 {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, 2.0, -1.0, 0.0, 1.0, -2.0, 1.0},
82 {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, -1.0, 0.0, 0.0, -1.0, 1.0},
83 {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, 1.0, 0.0, 0.0, 2.0, -2.0, 0.0, 0.0, -1.0, 1.0}};
84
85 public final Torsion[] torsions = new Torsion[2];
86
87 public TorsionTorsionType torsionTorsionType = null;
88
89 private double lambda = 1.0;
90
91 private double dEdL = 0.0;
92
93 private boolean lambdaTerm = false;
94
95
96
97
98
99
100
101
102
103 public TorsionTorsion(Bond firstBond, Angle angle, Bond lastBond, boolean reversed) {
104 super();
105 atoms = new Atom[5];
106 bonds = new Bond[4];
107 if (!reversed) {
108 atoms[1] = angle.atoms[0];
109 atoms[2] = angle.atoms[1];
110 atoms[3] = angle.atoms[2];
111 atoms[0] = firstBond.get1_2(atoms[1]);
112 atoms[4] = lastBond.get1_2(atoms[3]);
113 bonds[0] = firstBond;
114 bonds[1] = angle.bonds[0];
115 bonds[2] = angle.bonds[1];
116 bonds[3] = lastBond;
117 } else {
118 atoms[1] = angle.atoms[2];
119 atoms[2] = angle.atoms[1];
120 atoms[3] = angle.atoms[0];
121 atoms[0] = lastBond.get1_2(atoms[1]);
122 atoms[4] = firstBond.get1_2(atoms[3]);
123 bonds[0] = lastBond;
124 bonds[1] = angle.bonds[1];
125 bonds[2] = angle.bonds[0];
126 bonds[3] = firstBond;
127 }
128 torsions[0] = atoms[0].getTorsion(atoms[1], atoms[2], atoms[3]);
129 torsions[1] = atoms[4].getTorsion(atoms[3], atoms[2], atoms[1]);
130 setID_Key(false);
131 }
132
133
134
135
136
137
138
139
140
141
142 public static TorsionTorsion torsionTorsionFactory(Bond firstBond, Angle angle, Bond lastBond,
143 ForceField forceField) {
144 int[] c5 = new int[5];
145 Atom atom1 = angle.atoms[0];
146 Atom atom3 = angle.atoms[2];
147 c5[0] = firstBond.get1_2(atom1).getAtomType().atomClass;
148 c5[1] = atom1.getAtomType().atomClass;
149 c5[2] = angle.atoms[1].getAtomType().atomClass;
150 c5[3] = atom3.getAtomType().atomClass;
151 c5[4] = lastBond.get1_2(atom3).getAtomType().atomClass;
152 String key = TorsionTorsionType.sortKey(c5);
153 boolean reversed = false;
154 TorsionTorsionType torsionTorsionType = forceField.getTorsionTorsionType(key);
155 if (torsionTorsionType == null) {
156 key = TorsionTorsionType.reverseKey(c5);
157 torsionTorsionType = forceField.getTorsionTorsionType(key);
158 reversed = true;
159 }
160 if (torsionTorsionType == null) {
161 return null;
162 }
163 TorsionTorsion torsionTorsion = new TorsionTorsion(firstBond, angle, lastBond, reversed);
164 torsionTorsion.torsionTorsionType = torsionTorsionType;
165 return torsionTorsion;
166 }
167
168 private static void bcucof(double t1, double t2, double[] e, double[] dx, double[] dy,
169 double[] dxy, double[][] c) {
170
171 var x16 = new double[16];
172 var cl = new double[16];
173 var t1t2 = t1 * t2;
174
175
176 for (int i = 0; i < 4; i++) {
177 x16[i] = e[i];
178 x16[i + 4] = dx[i] * t1;
179 x16[i + 8] = dy[i] * t2;
180 x16[i + 12] = dxy[i] * t1t2;
181 }
182
183
184 for (int i = 0; i < 16; i++) {
185 double xx = 0.0;
186 for (int k = 0; k < 16; k++) {
187 xx += wt[k][i] * x16[k];
188 }
189 cl[i] = xx;
190 }
191
192
193 int j = 0;
194 for (int i = 0; i < 4; i++) {
195 for (int k = 0; k < 4; k++) {
196 c[i][k] = cl[j++];
197 }
198 }
199 }
200
201
202
203
204
205
206 @Override
207 public double energy(boolean gradient, int threadID, AtomicDoubleArray3D grad,
208 AtomicDoubleArray3D lambdaGrad) {
209 energy = 0.0;
210 value = 0.0;
211 dEdL = 0.0;
212
213 if (!getUse()) {
214 return energy;
215 }
216 var atomA = atoms[0];
217 var atomB = atoms[1];
218 var atomC = atoms[2];
219 var atomD = atoms[3];
220 var atomE = atoms[4];
221 var va = atomA.getXYZ();
222 var vb = atomB.getXYZ();
223 var vc = atomC.getXYZ();
224 var vd = atomD.getXYZ();
225 var ve = atomE.getXYZ();
226 var vba = vb.sub(va);
227 var vcb = vc.sub(vb);
228 var vdc = vd.sub(vc);
229 var ved = ve.sub(vd);
230 var vt = vba.X(vcb);
231 var vu = vcb.X(vdc);
232 var vv = vdc.X(ved);
233 var rt2 = vt.length2();
234 var ru2 = vu.length2();
235 var rv2 = vv.length2();
236 var rtru2 = rt2 * ru2;
237 var rurv2 = ru2 * rv2;
238 if (rtru2 != 0.0 && rurv2 != 0.0) {
239 var rtru = sqrt(rt2 * ru2);
240 var rurv = sqrt(ru2 * rv2);
241 var rcb = vcb.length();
242 var cosine1 = min(1.0, max(-1.0, vt.dot(vu) / rtru));
243 var angle1 = toDegrees(acos(cosine1));
244 var sign = vba.dot(vu);
245 if (sign < 0.0) {
246 angle1 *= -1.0;
247 }
248 var rdc = vdc.length();
249 var cosine2 = min(1.0, max(-1.0, vu.dot(vv) / rurv));
250 var angle2 = toDegrees(acos(cosine2));
251 sign = vcb.dot(vv);
252 if (sign < 0.0) {
253 angle2 *= -1.0;
254 }
255 var t1 = angle1;
256 var t2 = angle2;
257 sign = chktor();
258 t1 *= sign;
259 t2 *= sign;
260
261
262 var nx = torsionTorsionType.nx;
263 var ny = torsionTorsionType.ny;
264 var nlow = 0;
265 var nhigh = nx - 1;
266 while (nhigh - nlow > 1) {
267 var nt = (nhigh + nlow) / 2;
268 if (torsionTorsionType.tx[nt] > t1) {
269 nhigh = nt;
270 } else {
271 nlow = nt;
272 }
273 }
274 var xlow = nlow;
275 nlow = 0;
276 nhigh = ny - 1;
277 while (nhigh - nlow > 1) {
278 var nt = (nhigh + nlow) / 2;
279 if (torsionTorsionType.ty[nt] > t2) {
280 nhigh = nt;
281 } else {
282 nlow = nt;
283 }
284 }
285 var ylow = nlow;
286 var x1l = torsionTorsionType.tx[xlow];
287 var x1u = torsionTorsionType.tx[xlow + 1];
288 var y1l = torsionTorsionType.ty[ylow];
289 var y1u = torsionTorsionType.ty[ylow + 1];
290 var pos2 = (ylow + 1) * nx + xlow;
291 var pos1 = pos2 - nx;
292
293
294 var e = new double[4];
295 e[0] = torsionTorsionType.energy[pos1];
296 e[1] = torsionTorsionType.energy[pos1 + 1];
297 e[2] = torsionTorsionType.energy[pos2 + 1];
298 e[3] = torsionTorsionType.energy[pos2];
299
300 var dx = new double[4];
301 dx[0] = torsionTorsionType.dx[pos1];
302 dx[1] = torsionTorsionType.dx[pos1 + 1];
303 dx[2] = torsionTorsionType.dx[pos2 + 1];
304 dx[3] = torsionTorsionType.dx[pos2];
305
306 var dy = new double[4];
307 dy[0] = torsionTorsionType.dy[pos1];
308 dy[1] = torsionTorsionType.dy[pos1 + 1];
309 dy[2] = torsionTorsionType.dy[pos2 + 1];
310 dy[3] = torsionTorsionType.dy[pos2];
311
312 var dxy = new double[4];
313 dxy[0] = torsionTorsionType.dxy[pos1];
314 dxy[1] = torsionTorsionType.dxy[pos1 + 1];
315 dxy[2] = torsionTorsionType.dxy[pos2 + 1];
316 dxy[3] = torsionTorsionType.dxy[pos2];
317 if (!gradient && !lambdaTerm) {
318 var bcu = bcuint(x1l, x1u, y1l, y1u, t1, t2, e, dx, dy, dxy);
319 energy = torsionTorsionType.torTorUnit * bcu * lambda;
320 dEdL = torsionTorsionType.torTorUnit * bcu;
321 } else {
322 var ansy = new double[2];
323 var bcu1 = bcuint1(x1l, x1u, y1l, y1u, t1, t2, e, dx, dy, dxy, ansy);
324 energy = torsionTorsionType.torTorUnit * bcu1 * lambda;
325 dEdL = torsionTorsionType.torTorUnit * bcu1;
326 var dedang1 = sign * torsionTorsionType.torTorUnit * toDegrees(ansy[0]) * lambda;
327 var dedang2 = sign * torsionTorsionType.torTorUnit * toDegrees(ansy[1]) * lambda;
328
329 var vca = vc.sub(va);
330 var vdb = vd.sub(vb);
331 var vdt = vt.X(vcb).scaleI(dedang1 / (rt2 * rcb));
332 var vdu = vu.X(vcb).scaleI(-dedang1 / (ru2 * rcb));
333
334 var ga = vdt.X(vcb);
335 var gb = vca.X(vdt).addI(vdu.X(vdc));
336 var gc = vdb.X(vdu).addI(vdt.X(vba));
337 var gd = vdu.X(vcb);
338 var ia = atomA.getIndex() - 1;
339 var ib = atomB.getIndex() - 1;
340 var ic = atomC.getIndex() - 1;
341 var id = atomD.getIndex() - 1;
342 var ie = atomE.getIndex() - 1;
343 if (lambdaTerm) {
344 lambdaGrad.add(threadID, ia, ga);
345 lambdaGrad.add(threadID, ib, gb);
346 lambdaGrad.add(threadID, ic, gc);
347 lambdaGrad.add(threadID, id, gd);
348 }
349 if (gradient) {
350 grad.add(threadID, ia, ga.scaleI(lambda));
351 grad.add(threadID, ib, gb.scaleI(lambda));
352 grad.add(threadID, ic, gc.scaleI(lambda));
353 grad.add(threadID, id, gd.scaleI(lambda));
354 }
355
356 var vec = ve.sub(vc);
357 vdt = vu.X(vdc).scaleI(dedang2 / (ru2 * rdc));
358 vdu = vv.X(vdc).scaleI(-dedang2 / (rv2 * rdc));
359
360 gb = vdt.X(vdc);
361 gc = vdb.X(vdt).addI(vdu.X(ved));
362 gd = vdt.X(vcb).addI(vec.X(vdu));
363 var ge = vdu.X(vdc);
364 if (lambdaTerm) {
365 lambdaGrad.add(threadID, ib, gb);
366 lambdaGrad.add(threadID, ic, gc);
367 lambdaGrad.add(threadID, id, gd);
368 lambdaGrad.add(threadID, ie, ge);
369 }
370 if (gradient) {
371 grad.add(threadID, ib, gb.scaleI(lambda));
372 grad.add(threadID, ic, gc.scaleI(lambda));
373 grad.add(threadID, id, gd.scaleI(lambda));
374 grad.add(threadID, ie, ge.scaleI(lambda));
375 }
376 }
377 }
378 return energy;
379 }
380
381
382
383
384
385
386 public Atom getChiralAtom() {
387 Atom atom = null;
388 List<Bond> bnds = atoms[2].getBonds();
389
390
391 if (bnds.size() == 4) {
392
393 Atom atom1 = null;
394 Atom atom2 = null;
395 for (Bond b : bnds) {
396 Atom a = b.get1_2(atoms[2]);
397 if (a != atoms[1] && a != atoms[3]) {
398 if (atom1 == null) {
399 atom1 = a;
400 } else {
401 atom2 = a;
402 }
403 }
404 }
405
406
407
408
409 if (atom1.getType() > atom2.getType()) {
410 atom = atom1;
411 }
412 if (atom2.getType() > atom1.getType()) {
413 atom = atom2;
414 }
415 if (atom1.getAtomicNumber() > atom2.getAtomicNumber()) {
416 atom = atom1;
417 }
418 if (atom2.getAtomicNumber() > atom1.getAtomicNumber()) {
419 atom = atom2;
420 }
421 }
422 return atom;
423 }
424
425
426 @Override
427 public double getLambda() {
428 return lambda;
429 }
430
431
432 @Override
433 public void setLambda(double lambda) {
434 if (applyAllLambda()) {
435 lambdaTerm = true;
436 this.lambda = lambda;
437 } else {
438 lambdaTerm = false;
439 this.lambda = 1.0;
440 }
441 }
442
443
444 @Override
445 public double getd2EdL2() {
446 return 0.0;
447 }
448
449
450 @Override
451 public double getdEdL() {
452 if (lambdaTerm) {
453 return dEdL;
454 } else {
455 return 0.0;
456 }
457 }
458
459
460 @Override
461 public void getdEdXdL(double[] gradient) {
462
463 }
464
465
466 public void log() {
467 logger.info(String.format(" %s %6d-%s %6d-%s %6d-%s %6d-%s %10.4f", "Torsional-Torsion",
468 atoms[0].getIndex(), atoms[0].getAtomType().name, atoms[1].getIndex(),
469 atoms[1].getAtomType().name, atoms[2].getIndex(), atoms[2].getAtomType().name,
470 atoms[3].getIndex(), atoms[3].getAtomType().name, energy));
471 }
472
473
474
475
476
477
478 @Override
479 public String toString() {
480 return String.format("%s (%7.2f,%7.2f,%7.2f)", id, torsions[0].value, torsions[1].value,
481 energy);
482 }
483
484
485
486
487
488
489 private double chktor() {
490
491
492 var vc0 = new double[3];
493
494 var vc1 = new double[3];
495
496 var vc2 = new double[3];
497
498 List<Bond> bonds = atoms[2].getBonds();
499
500 if (bonds.size() == 4) {
501
502 Atom atom1 = null;
503 Atom atom2 = null;
504 for (Bond b : bonds) {
505 Atom a = b.get1_2(atoms[2]);
506 if (a != atoms[1] && a != atoms[3]) {
507 if (atom1 == null) {
508 atom1 = a;
509 } else {
510 atom2 = a;
511 }
512 }
513 }
514
515
516
517
518 Atom atom = null;
519 if (atom1.getType() > atom2.getType()) {
520 atom = atom1;
521 }
522 if (atom2.getType() > atom1.getType()) {
523 atom = atom2;
524 }
525 if (atom1.getAtomicNumber() > atom2.getAtomicNumber()) {
526 atom = atom1;
527 }
528 if (atom2.getAtomicNumber() > atom1.getAtomicNumber()) {
529 atom = atom2;
530 }
531
532
533 if (atom != null) {
534 var ad = new double[3];
535 var a1 = new double[3];
536 var a2 = new double[3];
537 var a3 = new double[3];
538 atom.getXYZ(ad);
539 atoms[1].getXYZ(a1);
540 atoms[2].getXYZ(a2);
541 atoms[3].getXYZ(a3);
542 sub(ad, a2, vc0);
543 sub(a1, a2, vc1);
544 sub(a3, a2, vc2);
545 double volume = vc0[0] * (vc1[1] * vc2[2] - vc1[2] * vc2[1])
546 + vc1[0] * (vc2[1] * vc0[2] - vc2[2] * vc0[1]) + vc2[0] * (vc0[1] * vc1[2]
547 - vc0[2] * vc1[1]);
548 if (volume < 0.0) {
549 return -1.0;
550 }
551 }
552 }
553 return 1.0;
554 }
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571 private double bcuint(double x1l, double x1u, double y1l, double y1u, double t1, double t2,
572 double[] e, double[] dx, double[] dy, double[] dxy) {
573
574 var c = new double[4][4];
575 var deltax = x1u - x1l;
576 var deltay = y1u - y1l;
577 bcucof(deltax, deltay, e, dx, dy, dxy, c);
578 var tx = (t1 - x1l) / deltax;
579 var ux = (t2 - y1l) / deltay;
580 var ret = 0.0;
581 for (int i = 3; i >= 0; i--) {
582 ret = tx * ret + ((c[i][3] * ux + c[i][2]) * ux + c[i][1]) * ux + c[i][0];
583 }
584 return ret;
585 }
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603 private double bcuint1(double x1l, double x1u, double y1l, double y1u, double t1, double t2,
604 double[] e, double[] dx, double[] dy, double[] dxy, double[] ansy) {
605
606 var c = new double[4][4];
607 var deltax = x1u - x1l;
608 var deltay = y1u - y1l;
609 bcucof(deltax, deltay, e, dx, dy, dxy, c);
610 var tx = (t1 - x1l) / deltax;
611 var ux = (t2 - y1l) / deltay;
612 var ret = 0.0;
613 ansy[0] = 0.0;
614 ansy[1] = 0.0;
615 for (int i = 3; i >= 0; i--) {
616 ret = tx * ret + ((c[i][3] * ux + c[i][2]) * ux + c[i][1]) * ux + c[i][0];
617 ansy[0] = ux * ansy[0] + (3.0 * c[3][i] * tx + 2.0 * c[2][i]) * tx + c[1][i];
618 ansy[1] = tx * ansy[1] + (3.0 * c[i][3] * ux + 2.0 * c[i][2]) * ux + c[i][1];
619 }
620 ansy[0] /= deltax;
621 ansy[1] /= deltay;
622 return ret;
623 }
624 }