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 org.apache.commons.math3.util.FastMath.acos;
41 import static org.apache.commons.math3.util.FastMath.sqrt;
42 import static org.apache.commons.math3.util.FastMath.toDegrees;
43
44 import ffx.numerics.atomic.AtomicDoubleArray3D;
45 import ffx.potential.parameters.ForceField;
46 import ffx.potential.parameters.ImproperTorsionType;
47
48 import java.io.Serial;
49 import java.util.ArrayList;
50 import java.util.Collection;
51 import java.util.List;
52 import java.util.logging.Logger;
53
54
55
56
57
58
59
60 public class ImproperTorsion extends BondedTerm {
61
62 @Serial
63 private static final long serialVersionUID = 1L;
64
65 private static final Logger logger = Logger.getLogger(ImproperTorsion.class.getName());
66
67
68 public ImproperTorsionType improperType = null;
69
70 public double scaleFactor = 1.0;
71
72
73
74
75
76
77
78
79
80 private ImproperTorsion(Atom atom1, Atom atom2, Atom atom3, Atom atom4) {
81 super();
82 atoms = new Atom[4];
83 atoms[0] = atom1;
84 atoms[1] = atom2;
85 atoms[2] = atom3;
86 atoms[3] = atom4;
87 setID_Key(false);
88 }
89
90 private static void createWildCardImproperTorsion(Atom[] atoms, int[] classes,
91 ImproperTorsionType type, ArrayList<ImproperTorsion> improperTorsions) {
92
93
94 if (classes[3] == atoms[3].getAtomType().atomClass || type.atomClasses[3] == 0) {
95
96 } else if (classes[3] == atoms[1].getAtomType().atomClass) {
97 Atom temp = atoms[3];
98 atoms[3] = atoms[1];
99 atoms[1] = temp;
100 } else {
101 Atom temp = atoms[0];
102 atoms[0] = atoms[3];
103 atoms[3] = temp;
104 }
105
106 if (classes[1] == atoms[1].getAtomType().atomClass || type.atomClasses[1] == 0) {
107
108 } else if (classes[1] == atoms[0].getAtomType().atomClass) {
109 Atom temp = atoms[1];
110 atoms[1] = atoms[0];
111 atoms[0] = temp;
112 }
113
114
115 ImproperTorsion improperTorsion = new ImproperTorsion(atoms[0], atoms[1], atoms[2], atoms[3]);
116 improperTorsion.setImproperType(type);
117 improperTorsions.add(improperTorsion);
118 improperTorsion.scaleFactor = 1.0 / 3.0;
119 improperTorsions.add(improperTorsion);
120
121 improperTorsion = new ImproperTorsion(atoms[1], atoms[3], atoms[2], atoms[0]);
122 improperTorsion.setImproperType(type);
123 improperTorsions.add(improperTorsion);
124 improperTorsion.scaleFactor = 1.0 / 3.0;
125 improperTorsions.add(improperTorsion);
126
127 improperTorsion = new ImproperTorsion(atoms[3], atoms[0], atoms[2], atoms[1]);
128 improperTorsion.setImproperType(type);
129 improperTorsions.add(improperTorsion);
130 improperTorsion.scaleFactor = 1.0 / 3.0;
131 improperTorsions.add(improperTorsion);
132 }
133
134
135
136
137
138
139
140
141 static ArrayList<ImproperTorsion> improperTorsionFactory(Atom atom, ForceField forceField) {
142
143 if (atom == null) {
144 return null;
145 }
146
147 Atom[] atoms = new Atom[4];
148 atoms[2] = atom;
149 List<Bond> bonds = atom.getBonds();
150 if (bonds == null || bonds.size() != 3) {
151 return null;
152 }
153
154 for (int i = 0; i < 3; i++) {
155 Bond bond = bonds.get(i);
156 Atom atom2 = bond.get1_2(atom);
157 if (i == 2) {
158 atoms[3] = atom2;
159 } else {
160 atoms[i] = atom2;
161 }
162 }
163
164 ArrayList<ImproperTorsion> improperTorsions = new ArrayList<>();
165 Collection<ImproperTorsionType> types = forceField.getImproperTypes();
166 boolean done = false;
167
168
169 for (ImproperTorsionType type : types) {
170 int[] classes = new int[4];
171 classes[0] = atoms[0].getAtomType().atomClass;
172 classes[1] = atoms[1].getAtomType().atomClass;
173 classes[2] = atoms[2].getAtomType().atomClass;
174 classes[3] = atoms[3].getAtomType().atomClass;
175 boolean assigned = type.assigned(classes, false, false);
176 if (assigned) {
177 done = true;
178
179
180 if (classes[3] == atoms[3].getAtomType().atomClass || type.atomClasses[3] == 0) {
181
182 } else if (classes[3] == atoms[1].getAtomType().atomClass) {
183 Atom temp = atoms[3];
184 atoms[3] = atoms[1];
185 atoms[1] = temp;
186 } else {
187 Atom temp = atoms[0];
188 atoms[0] = atoms[3];
189 atoms[3] = temp;
190 }
191
192 if (classes[1] == atoms[1].getAtomType().atomClass || type.atomClasses[1] == 0) {
193
194 } else if (classes[1] == atoms[0].getAtomType().atomClass) {
195 Atom temp = atoms[1];
196 atoms[1] = atoms[0];
197 atoms[0] = temp;
198 }
199
200 ImproperTorsion improperTorsion =
201 new ImproperTorsion(atoms[0], atoms[1], atoms[2], atoms[3]);
202 improperTorsion.setImproperType(type);
203 improperTorsions.add(improperTorsion);
204 int c0 = type.atomClasses[0];
205 int c1 = type.atomClasses[1];
206 int c3 = type.atomClasses[3];
207 if (c0 == c1 && c1 == c3) {
208 improperTorsion.scaleFactor = 1.0 / 6.0;
209 improperTorsion = new ImproperTorsion(atoms[0], atoms[3], atoms[2], atoms[1]);
210 improperTorsion.setImproperType(type);
211 improperTorsion.scaleFactor = 1.0 / 6.0;
212 improperTorsions.add(improperTorsion);
213 improperTorsion = new ImproperTorsion(atoms[1], atoms[0], atoms[2], atoms[3]);
214 improperTorsion.setImproperType(type);
215 improperTorsion.scaleFactor = 1.0 / 6.0;
216 improperTorsions.add(improperTorsion);
217 improperTorsion = new ImproperTorsion(atoms[1], atoms[3], atoms[2], atoms[0]);
218 improperTorsion.setImproperType(type);
219 improperTorsion.scaleFactor = 1.0 / 6.0;
220 improperTorsions.add(improperTorsion);
221 improperTorsion = new ImproperTorsion(atoms[3], atoms[0], atoms[2], atoms[1]);
222 improperTorsion.setImproperType(type);
223 improperTorsion.scaleFactor = 1.0 / 6.0;
224 improperTorsions.add(improperTorsion);
225 improperTorsion = new ImproperTorsion(atoms[3], atoms[1], atoms[2], atoms[0]);
226 improperTorsion.setImproperType(type);
227 improperTorsion.scaleFactor = 1.0 / 6.0;
228 improperTorsions.add(improperTorsion);
229 } else if (c0 == c1) {
230 improperTorsion.scaleFactor = 0.5;
231 improperTorsion = new ImproperTorsion(atoms[1], atoms[0], atoms[2], atoms[3]);
232 improperTorsion.setImproperType(type);
233 improperTorsion.scaleFactor = 0.5;
234 improperTorsions.add(improperTorsion);
235 } else if (c0 == c3) {
236 improperTorsion.scaleFactor = 0.5;
237 improperTorsion = new ImproperTorsion(atoms[3], atoms[1], atoms[2], atoms[0]);
238 improperTorsion.setImproperType(type);
239 improperTorsion.scaleFactor = 0.5;
240 improperTorsions.add(improperTorsion);
241 } else if (c1 == c3) {
242 improperTorsion.scaleFactor = 0.5;
243 improperTorsion = new ImproperTorsion(atoms[0], atoms[3], atoms[2], atoms[1]);
244 improperTorsion.setImproperType(type);
245 improperTorsion.scaleFactor = 0.5;
246 improperTorsions.add(improperTorsion);
247 }
248 }
249
250 if (done) {
251 break;
252 }
253 }
254
255
256 if (!done) {
257 for (ImproperTorsionType type : types) {
258 int[] classes = new int[4];
259 classes[0] = atoms[0].getAtomType().atomClass;
260 classes[1] = atoms[1].getAtomType().atomClass;
261 classes[2] = atoms[2].getAtomType().atomClass;
262 classes[3] = atoms[3].getAtomType().atomClass;
263 boolean assigned = type.assigned(classes, true, false);
264 if (assigned) {
265 done = true;
266 createWildCardImproperTorsion(atoms, classes, type, improperTorsions);
267 break;
268 }
269 }
270 }
271
272
273 if (!done) {
274 for (ImproperTorsionType type : types) {
275 int[] classes = new int[4];
276 classes[0] = atoms[0].getAtomType().atomClass;
277 classes[1] = atoms[1].getAtomType().atomClass;
278 classes[2] = atoms[2].getAtomType().atomClass;
279 classes[3] = atoms[3].getAtomType().atomClass;
280 boolean assigned = type.assigned(classes, true, true);
281 if (assigned) {
282 createWildCardImproperTorsion(atoms, classes, type, improperTorsions);
283 break;
284 }
285 }
286 }
287
288 if (improperTorsions.isEmpty()) {
289 return null;
290 }
291
292 return improperTorsions;
293 }
294
295
296 @Override
297 public int compareTo(BondedTerm o) {
298 if (o == null) {
299 throw new NullPointerException();
300 }
301 if (o == this) {
302 return 0;
303 }
304 if (!o.getClass().isInstance(this)) {
305 return super.compareTo(o);
306 }
307 int this1 = atoms[1].getIndex();
308 int a1 = o.atoms[1].getIndex();
309 if (this1 < a1) {
310 return -1;
311 }
312 if (this1 > a1) {
313 return 1;
314 }
315 int this3 = atoms[3].getIndex();
316 int a3 = o.atoms[3].getIndex();
317 return Integer.compare(this3, a3);
318 }
319
320
321
322
323
324
325 @Override
326 public double energy(boolean gradient, int threadID, AtomicDoubleArray3D grad, AtomicDoubleArray3D lambdaGrad) {
327 energy = 0.0;
328 value = 0.0;
329
330 if (!getUse()) {
331 return energy;
332 }
333 var atomA = atoms[0];
334 var atomB = atoms[1];
335 var atomC = atoms[2];
336 var atomD = atoms[3];
337 var va = atomA.getXYZ();
338 var vb = atomB.getXYZ();
339 var vc = atomC.getXYZ();
340 var vd = atomD.getXYZ();
341 var vba = vb.sub(va);
342 var vcb = vc.sub(vb);
343 var vdc = vd.sub(vc);
344 var vt = vba.X(vcb);
345 var vu = vcb.X(vdc);
346 var vtu = vt.X(vu);
347 var rt2 = vt.length2();
348 var ru2 = vu.length2();
349 var rtru = sqrt(rt2 * ru2);
350 if (rtru != 0.0) {
351
352 var v2 = improperType.k;
353 var c2 = improperType.cos;
354 var s2 = improperType.sin;
355
356 var rcb = vcb.length();
357 var cosine = vt.dot(vu) / rtru;
358 var sine = vcb.dot(vtu) / (rcb * rtru);
359 var cosine2 = cosine * cosine - sine * sine;
360 var sine2 = 2.0 * cosine * sine;
361 var phi2 = 1.0 + (cosine2 * c2 + sine2 * s2);
362 var dphi2 = 2.0 * (cosine2 * s2 - sine2 * c2);
363
364 value = toDegrees(acos(cosine));
365 var prefactor = improperType.impTorUnit * scaleFactor;
366 energy = prefactor * (v2 * phi2);
367 var dedphi = prefactor * (v2 * dphi2);
368 if (gradient) {
369
370 var vca = vc.sub(va);
371 var vdb = vd.sub(vb);
372 var dedt = vt.X(vcb).scaleI(dedphi / (rt2 * rcb));
373 var dedu = vu.X(vcb).scaleI(-dedphi / (ru2 * rcb));
374
375 var ia = atomA.getIndex() - 1;
376 var ib = atomB.getIndex() - 1;
377 var ic = atomC.getIndex() - 1;
378 var id = atomD.getIndex() - 1;
379 grad.add(threadID, ia, dedt.X(vcb));
380 grad.add(threadID, ib, dedu.X(vdc).addI(dedt.X(vca).scaleI(-1.0)));
381 grad.add(threadID, ic, dedu.X(vdb).scaleI(-1.0).addI(dedt.X(vba)));
382 grad.add(threadID, id, dedu.X(vcb));
383 }
384 }
385
386 return energy;
387 }
388
389
390 public void log() {
391 logger.info(
392 String.format(
393 " %s %6d-%s %6d-%s %6d-%s %6d-%s %6.4f %10.4f",
394 "Improper Torsion",
395 atoms[0].getIndex(),
396 atoms[0].getAtomType().name,
397 atoms[1].getIndex(),
398 atoms[1].getAtomType().name,
399 atoms[2].getIndex(),
400 atoms[2].getAtomType().name,
401 atoms[3].getIndex(),
402 atoms[3].getAtomType().name,
403 value,
404 energy));
405 }
406
407
408
409
410
411
412 @Override
413 public String toString() {
414 return String.format("%s (%7.1f,%7.2f)", id, value, energy);
415 }
416
417
418
419
420
421
422 private void setImproperType(ImproperTorsionType a) {
423 improperType = a;
424 }
425 }