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 ffx.numerics.atomic.AtomicDoubleArray3D;
41 import ffx.potential.parameters.ForceField;
42 import ffx.potential.parameters.PiOrbitalTorsionType;
43
44 import java.io.Serial;
45 import java.util.logging.Logger;
46
47 import static java.lang.String.format;
48 import static org.apache.commons.math3.util.FastMath.acos;
49 import static org.apache.commons.math3.util.FastMath.max;
50 import static org.apache.commons.math3.util.FastMath.min;
51 import static org.apache.commons.math3.util.FastMath.sqrt;
52 import static org.apache.commons.math3.util.FastMath.toDegrees;
53
54
55
56
57
58
59
60 public class PiOrbitalTorsion extends BondedTerm implements LambdaInterface {
61
62 @Serial
63 private static final long serialVersionUID = 1L;
64
65 private static final Logger logger = Logger.getLogger(PiOrbitalTorsion.class.getName());
66
67
68
69
70 public PiOrbitalTorsionType piOrbitalTorsionType = null;
71
72
73
74 private double lambda = 1.0;
75
76
77
78 private double dEdL = 0.0;
79
80
81
82 private boolean lambdaTerm = false;
83
84
85
86
87
88
89 public PiOrbitalTorsion(Bond middleBond) {
90 super();
91 atoms = new Atom[6];
92 atoms[2] = middleBond.atoms[0];
93 atoms[3] = middleBond.atoms[1];
94 bonds = new Bond[5];
95 bonds[2] = middleBond;
96 int i = 0;
97 for (Bond b : atoms[2].getBonds()) {
98 if (b != middleBond) {
99 atoms[i] = b.get1_2(atoms[2]);
100 bonds[i] = b;
101 i++;
102 }
103 }
104 i = 4;
105 for (Bond b : atoms[3].getBonds()) {
106 if (b != middleBond) {
107 atoms[i] = b.get1_2(atoms[3]);
108 bonds[i - 1] = b;
109 i++;
110 }
111 }
112 setID_Key(false);
113 }
114
115
116
117
118
119
120 public Bond getMiddleBond() {
121 return bonds[2];
122 }
123
124
125
126
127
128
129 public void setPiOrbitalTorsionType(PiOrbitalTorsionType piOrbitalTorsionType) {
130 this.piOrbitalTorsionType = piOrbitalTorsionType;
131 }
132
133
134
135
136
137
138
139
140 public static PiOrbitalTorsion piOrbitalTorsionFactory(Bond bond, ForceField forceField) {
141 Atom atom1 = bond.getAtom(0);
142 Atom atom2 = bond.getAtom(1);
143 if (!atom1.isTrigonal() || !atom2.isTrigonal()) {
144 return null;
145 }
146
147 PiOrbitalTorsionType piOrbitalTorsionType = forceField.getPiOrbitalTorsionType(
148 atom1.getAtomType(), atom2.getAtomType());
149 if (piOrbitalTorsionType == null) {
150 return null;
151 }
152
153 PiOrbitalTorsion piOrbitalTorsion = new PiOrbitalTorsion(bond);
154 piOrbitalTorsion.setPiOrbitalTorsionType(piOrbitalTorsionType);
155 return piOrbitalTorsion;
156 }
157
158
159
160
161
162
163 @Override
164 public double energy(boolean gradient, int threadID, AtomicDoubleArray3D grad, AtomicDoubleArray3D lambdaGrad) {
165 energy = 0.0;
166 value = 0.0;
167 dEdL = 0.0;
168
169 if (!getUse()) {
170 return energy;
171 }
172 var atomA = atoms[0];
173 var atomB = atoms[1];
174 var atomC = atoms[2];
175 var atomD = atoms[3];
176 var atomE = atoms[4];
177 var atomF = atoms[5];
178 var va = atomA.getXYZ();
179 var vb = atomB.getXYZ();
180 var vc = atomC.getXYZ();
181 var vd = atomD.getXYZ();
182 var ve = atomE.getXYZ();
183 var vf = atomF.getXYZ();
184 var vad = va.sub(vd);
185 var vbd = vb.sub(vd);
186 var vdc = vd.sub(vc);
187 var vec = ve.sub(vc);
188 var vfc = vf.sub(vc);
189 var vp = vad.X(vbd).addI(vc);
190 var vq = vec.X(vfc).addI(vd);
191 var vpc = vc.sub(vp);
192 var vdq = vq.sub(vd);
193 var vt = vpc.X(vdc);
194 var vu = vdc.X(vdq);
195 var rt2 = vt.length2();
196 var ru2 = vu.length2();
197 var rtru2 = rt2 * ru2;
198 if (rtru2 != 0.0) {
199 var rr = sqrt(rtru2);
200 var rdc = vdc.length();
201 var sine = vdc.dot(vt.X(vu)) / (rdc * rr);
202 var cosine = min(1.0, max(-1.0, vt.dot(vu) / rr));
203 value = toDegrees(acos(cosine));
204 if (sine < 0.0) {
205 value = -value;
206 }
207 if (value > 90.0) {
208 value -= 180.0;
209 }
210 if (value < -90.0) {
211 value += 180.0;
212 }
213 var cosine2 = cosine * cosine - sine * sine;
214 var phi2 = 1.0 - cosine2;
215 energy = piOrbitalTorsionType.piTorsUnit * piOrbitalTorsionType.forceConstant * phi2;
216 dEdL = energy;
217 energy = lambda * energy;
218 if (gradient || lambdaTerm) {
219 var sine2 = 2.0 * cosine * sine;
220 var dphi2 = 2.0 * sine2;
221 var dedphi = piOrbitalTorsionType.piTorsUnit * piOrbitalTorsionType.forceConstant * dphi2;
222
223
224 var vdp = vd.sub(vp);
225 var vqc = vq.sub(vc);
226 var vdt = vt.X(vdc).scaleI(dedphi / (rt2 * rdc));
227 var vdu = vu.X(vdc).scaleI(-dedphi / (ru2 * rdc));
228 var dedp = vdt.X(vdc);
229 var dedc = vdp.X(vdt).addI(vdu.X(vdq));
230 var dedd = vdt.X(vpc).addI(vqc.X(vdu));
231 var dedq = vdu.X(vdc);
232
233
234 var ga = vbd.X(dedp);
235 var gb = dedp.X(vad);
236 var ge = vfc.X(dedq);
237 var gf = dedq.X(vec);
238 var gc = dedc.add(dedp).subI(ge).subI(gf);
239 var gd = dedd.add(dedq).subI(ga).subI(gb);
240 var iA = atomA.getIndex() - 1;
241 var iB = atomB.getIndex() - 1;
242 var iC = atomC.getIndex() - 1;
243 var iD = atomD.getIndex() - 1;
244 var iE = atomE.getIndex() - 1;
245 var iF = atomF.getIndex() - 1;
246 if (lambdaTerm) {
247 lambdaGrad.add(threadID, iA, ga);
248 lambdaGrad.add(threadID, iB, gb);
249 lambdaGrad.add(threadID, iC, gc);
250 lambdaGrad.add(threadID, iD, gd);
251 lambdaGrad.add(threadID, iE, ge);
252 lambdaGrad.add(threadID, iF, gf);
253 }
254 if (gradient) {
255 grad.add(threadID, iA, ga.scaleI(lambda));
256 grad.add(threadID, iB, gb.scaleI(lambda));
257 grad.add(threadID, iC, gc.scaleI(lambda));
258 grad.add(threadID, iD, gd.scaleI(lambda));
259 grad.add(threadID, iE, ge.scaleI(lambda));
260 grad.add(threadID, iF, gf.scaleI(lambda));
261 }
262 }
263 }
264 return energy;
265 }
266
267
268
269
270 @Override
271 public double getLambda() {
272 return lambda;
273 }
274
275
276
277
278 @Override
279 public void setLambda(double lambda) {
280 if (applyAllLambda()) {
281 this.lambda = lambda;
282 lambdaTerm = true;
283 } else {
284 this.lambda = 1.0;
285 }
286 }
287
288
289
290
291 @Override
292 public double getd2EdL2() {
293 return 0.0;
294 }
295
296
297
298
299 @Override
300 public double getdEdL() {
301 if (lambdaTerm) {
302 return dEdL;
303 } else {
304 return 0.0;
305 }
306 }
307
308
309
310
311 @Override
312 public void getdEdXdL(double[] gradient) {
313
314 }
315
316
317
318
319 public void log() {
320 logger.info(format(" %s %6d-%s %6d-%s %10.4f %10.4f", "Pi-Orbital Torsion", atoms[2].getIndex(),
321 atoms[2].getAtomType().name, atoms[3].getIndex(), atoms[3].getAtomType().name, value, energy));
322 }
323
324
325
326
327
328
329 @Override
330 public String toString() {
331 return format("%s (%7.1f,%7.2f)", id, value, energy);
332 }
333 }