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.nonbonded.implicit;
39
40 import edu.rit.pj.IntegerForLoop;
41 import edu.rit.pj.ParallelRegion;
42 import edu.rit.pj.reduction.SharedDouble;
43 import ffx.potential.bonded.Angle;
44 import ffx.potential.bonded.Atom;
45 import ffx.potential.bonded.Bond;
46 import ffx.potential.bonded.Torsion;
47
48 import java.util.List;
49 import java.util.logging.Level;
50 import java.util.logging.Logger;
51
52 import static java.lang.String.format;
53 import static org.apache.commons.math3.util.FastMath.PI;
54 import static org.apache.commons.math3.util.FastMath.exp;
55 import static org.apache.commons.math3.util.FastMath.sqrt;
56 import static org.apache.commons.math3.util.FastMath.tanh;
57
58
59
60
61
62
63 public class HydrophobicPMFRegion extends ParallelRegion {
64
65 private static final Logger logger = Logger.getLogger(HydrophobicPMFRegion.class.getName());
66
67 private final double rCarbon = 1.7;
68
69 private final double rWater = 1.4;
70
71 private final double safact = 0.3516;
72
73 private final double acSurf = 120.7628;
74
75 private final double tSlope = 100.0;
76
77 private final double tOffset = 6.0;
78
79 private final double hpmfCut = 11.0;
80
81 private final double hpmfCut2 = hpmfCut * hpmfCut;
82
83 private final double h1 = -0.7308004860404441194;
84 private final double h2 = 0.2001645051578760659;
85 private final double h3 = -0.0905499953418473502;
86
87 private final double c1 = 3.8167879266271396155;
88 private final double c2 = 5.4669162286016419472;
89 private final double c3 = 7.1167694861385353278;
90
91 private final double w1 = 1.6858993102248638341;
92 private final double w2 = 1.3906405621629980285;
93 private final double w3 = 1.5741657341338335385;
94 private final double rSurf = rCarbon + 2.0 * rWater;
95 private final double piSurf = PI * (rCarbon + rWater);
96
97 private final double[] rPMF;
98
99 private final int nCarbon;
100
101 private final int[] iCarbon;
102
103 private final double[] carbonSASA;
104
105 private final double[] tanhSA;
106
107 private final CarbonSASALoop[] carbonSASALoop;
108
109 private final HydrophobicPMFLoop[] hydrophobicPMFLoop;
110
111 private final CarbonSASACRLoop[] carbonSASACRLoop;
112
113 private final SharedDouble sharedEnergy;
114 private final double[] dtanhSA;
115 private final double[] sasa;
116 private final double[] carbonSASACR;
117 private Atom[] atoms;
118 private int nAtoms;
119 private double[] x, y, z;
120 private boolean[] use;
121 private double[][][] grad;
122 private boolean gradient;
123
124 public HydrophobicPMFRegion(
125 Atom[] atoms, double[] x, double[] y, double[] z, boolean[] use, double[][][] grad, int nt) {
126 logger.info(format(" Hydrophobic PMF cut-off: %8.2f (A)", hpmfCut));
127
128 this.atoms = atoms;
129 nAtoms = atoms.length;
130 this.x = x;
131 this.y = y;
132 this.z = z;
133 this.use = use;
134 this.grad = grad;
135
136
137 int count = 0;
138 for (int i = 0; i < nAtoms; i++) {
139 Atom atom = atoms[i];
140 int atomicNumber = atom.getAtomicNumber();
141 if (atomicNumber == 6) {
142 List<Bond> bonds = atom.getBonds();
143 int bondCount = bonds.size();
144 if (bondCount <= 2) {
145 continue;
146 }
147 boolean keep = true;
148 for (int j = 0; j < bondCount; j++) {
149 Atom atom2 = bonds.get(j).get1_2(atom);
150 int atomicNumber2 = atom2.getAtomicNumber();
151 if (bondCount == 3 && atomicNumber2 == 8) {
152 keep = false;
153 break;
154 }
155 }
156 if (keep) {
157 count++;
158 }
159 }
160 }
161
162 rPMF = new double[nAtoms];
163 nCarbon = count;
164 iCarbon = new int[nCarbon];
165 carbonSASA = new double[nCarbon];
166 carbonSASACR = new double[nCarbon];
167 tanhSA = new double[nCarbon];
168 dtanhSA = new double[nCarbon];
169 sasa = new double[nCarbon];
170
171 carbonSASALoop = new CarbonSASALoop[nt];
172 hydrophobicPMFLoop = new HydrophobicPMFLoop[nt];
173 carbonSASACRLoop = new CarbonSASACRLoop[nt];
174 for (int i = 0; i < nt; i++) {
175 carbonSASALoop[i] = new CarbonSASALoop();
176 hydrophobicPMFLoop[i] = new HydrophobicPMFLoop();
177 carbonSASACRLoop[i] = new CarbonSASACRLoop();
178 }
179 sharedEnergy = new SharedDouble();
180
181
182 int index = 0;
183 for (int i = 0; i < nAtoms; i++) {
184 Atom atom = atoms[i];
185 int atomicNumber = atom.getAtomicNumber();
186 if (atomicNumber == 6) {
187 int nh = 0;
188 List<Bond> bonds = atom.getBonds();
189 int bondCount = bonds.size();
190 if (bondCount <= 2) {
191 continue;
192 }
193 boolean keep = true;
194 for (int j = 0; j < bondCount; j++) {
195 Atom atom2 = bonds.get(j).get1_2(atom);
196 int atomicNumber2 = atom2.getAtomicNumber();
197 if (atomicNumber2 == 1) {
198 nh++;
199 }
200 if (bondCount == 3 && atomicNumber2 == 8) {
201 keep = false;
202 }
203 }
204 if (keep) {
205 iCarbon[index] = i;
206 carbonSASA[index] = 1.0;
207 if (bondCount == 3 && nh == 0) {
208 carbonSASA[index] = 1.554;
209 } else if (bondCount == 3 && nh == 1) {
210 carbonSASA[index] = 1.073;
211 } else if (bondCount == 4 && nh == 1) {
212 carbonSASA[index] = 1.276;
213 } else if (bondCount == 4 && nh == 2) {
214 carbonSASA[index] = 1.045;
215 } else if (bondCount == 4 && nh == 3) {
216 carbonSASA[index] = 0.880;
217 }
218 carbonSASA[index] = carbonSASA[index] * safact / acSurf;
219 if (logger.isLoggable(Level.FINEST)) {
220 logger.finest(
221 format(
222 " %d Base HPMF SASA for atom %d: %10.8f", index + 1, i + 1, carbonSASA[index]));
223 }
224 index++;
225 }
226 }
227 }
228
229
230 for (int i = 0; i < nAtoms; i++) {
231 rPMF[i] = 2.0;
232 int atmnum = atoms[i].getAtomicNumber();
233 switch (atmnum) {
234 case 0:
235 rPMF[i] = 0.0;
236 break;
237 case 1:
238 rPMF[i] = 1.20;
239 break;
240 case 2:
241 rPMF[i] = 1.40;
242 break;
243 case 5:
244 rPMF[i] = 1.80;
245 break;
246 case 6:
247 rPMF[i] = 1.70;
248 break;
249 case 7:
250 rPMF[i] = 1.55;
251 break;
252 case 8:
253 rPMF[i] = 1.50;
254 break;
255 case 9:
256 rPMF[i] = 1.47;
257 break;
258 case 10:
259 rPMF[i] = 1.54;
260 break;
261 case 14:
262 rPMF[i] = 2.10;
263 break;
264 case 15:
265 rPMF[i] = 1.80;
266 break;
267 case 16:
268 rPMF[i] = 1.80;
269 break;
270 case 17:
271 rPMF[i] = 1.75;
272 break;
273 case 18:
274 rPMF[i] = 1.88;
275 break;
276 case 34:
277 rPMF[i] = 1.90;
278 break;
279 case 35:
280 rPMF[i] = 1.85;
281 break;
282 case 36:
283 rPMF[i] = 2.02;
284 break;
285 case 53:
286 rPMF[i] = 1.98;
287 break;
288 case 54:
289 rPMF[i] = 2.16;
290 break;
291 }
292 }
293 }
294
295 public double getEnergy() {
296 return sharedEnergy.get();
297 }
298
299 @Override
300 public void run() {
301 int ti = getThreadIndex();
302 try {
303 execute(0, nCarbon - 1, carbonSASALoop[ti]);
304 execute(0, nCarbon - 2, hydrophobicPMFLoop[ti]);
305 if (gradient) {
306 execute(0, nCarbon - 1, carbonSASACRLoop[ti]);
307 }
308 } catch (Exception e) {
309 String message = "Fatal exception computing Born radii in thread " + ti + "\n";
310 logger.log(Level.SEVERE, message, e);
311 }
312 }
313
314 public void setGradient(boolean gradient) {
315 this.gradient = gradient;
316 }
317
318 @Override
319 public void start() {
320 sharedEnergy.set(0);
321 }
322
323
324
325
326
327
328 private class CarbonSASALoop extends IntegerForLoop {
329
330 @Override
331 public void run(int lb, int ub) {
332
333 for (int ii = lb; ii <= ub; ii++) {
334 final int i = iCarbon[ii];
335 if (!use[i]) {
336 continue;
337 }
338 final double carbonSA = carbonSASA[ii];
339 double sa = acSurf;
340 final double xi = x[i];
341 final double yi = y[i];
342 final double zi = z[i];
343 int count = 0;
344 for (int k = 0; k < nAtoms; k++) {
345 if (i != k && use[k]) {
346 final double xr = x[k] - xi;
347 final double yr = y[k] - yi;
348 final double zr = z[k] - zi;
349 final double r2 = xr * xr + yr * yr + zr * zr;
350 double rk = rPMF[k];
351 double rBig = rk + rSurf;
352 if (r2 < rBig * rBig) {
353 final double r = sqrt(r2);
354 final double rSmall = rk - rCarbon;
355 final double part = piSurf * (rBig - r) * (1.0 + rSmall / r);
356 sa *= (1.0 - carbonSA * part);
357 count++;
358 }
359 }
360 }
361 sasa[ii] = sa;
362
363 double tSA = tanh(tSlope * (sa - tOffset));
364 tanhSA[ii] = 0.5 * (1.0 + tSA);
365 dtanhSA[ii] = 0.5 * tSlope * (1.0 - tSA * tSA);
366 }
367 }
368 }
369
370
371
372
373
374
375 private class HydrophobicPMFLoop extends IntegerForLoop {
376
377
378 private final int[] omit;
379 private double energy;
380 private double[] gX;
381 private double[] gY;
382 private double[] gZ;
383
384 public HydrophobicPMFLoop() {
385 omit = new int[nAtoms];
386 }
387
388 @Override
389 public void finish() {
390 sharedEnergy.addAndGet(energy);
391 }
392
393 @Override
394 public void run(int lb, int ub) {
395
396 for (int ii = lb; ii <= ub; ii++) {
397 final int i = iCarbon[ii];
398 if (!use[i]) {
399 continue;
400 }
401 double tanhSAi = tanhSA[ii];
402 Atom ssAtom = null;
403 Atom atom = atoms[i];
404 List<Bond> bonds = atom.getBonds();
405 for (Bond bond : bonds) {
406 Atom atom2 = bond.get1_2(atom);
407 int k = atom2.getIndex() - 1;
408 if (!use[k]) {
409 continue;
410 }
411 omit[k] = i;
412 if (atom2.getAtomicNumber() == 16) {
413 ssAtom = atom2;
414 }
415 }
416 List<Angle> angles = atom.getAngles();
417 for (Angle angle : angles) {
418 Atom atom2 = angle.get1_3(atom);
419 if (atom2 != null) {
420 int k = atom2.getIndex() - 1;
421 if (!use[k]) {
422 continue;
423 }
424 omit[k] = i;
425 }
426 }
427 List<Torsion> torsions = atom.getTorsions();
428 for (Torsion torsion : torsions) {
429 Atom atom2 = torsion.get1_4(atom);
430 if (atom2 != null) {
431 int k = atom2.getIndex() - 1;
432 if (!use[k]) {
433 continue;
434 }
435 omit[k] = i;
436 if (ssAtom != null) {
437 List<Bond> bonds2 = atom2.getBonds();
438 for (Bond bond : bonds2) {
439 Atom s = bond.get1_2(atom2);
440 if (s.getAtomicNumber() == 16) {
441 List<Bond> sBonds = s.getBonds();
442 for (Bond sBond : sBonds) {
443 Atom s2 = sBond.get1_2(s);
444 if (s2 == ssAtom) {
445 omit[k] = -1;
446 }
447 }
448 }
449 }
450 }
451 }
452 }
453 final double xi = x[i];
454 final double yi = y[i];
455 final double zi = z[i];
456 double e = 0.0;
457 for (int kk = ii + 1; kk < nCarbon; kk++) {
458 int k = iCarbon[kk];
459 if (!use[k]) {
460 continue;
461 }
462 if (omit[k] != i) {
463 final double xr = xi - x[k];
464 final double yr = yi - y[k];
465 final double zr = zi - z[k];
466 final double r2 = xr * xr + yr * yr + zr * zr;
467 if (r2 < hpmfCut2) {
468 final double r = sqrt(r2);
469 final double a1 = (r - c1) * w1;
470 final double a2 = (r - c2) * w2;
471 final double a3 = (r - c3) * w3;
472 final double e1 = h1 * exp(-a1 * a1);
473 final double e2 = h2 * exp(-a2 * a2);
474 final double e3 = h3 * exp(-a3 * a3);
475 final double t1t2 = tanhSAi * tanhSA[kk];
476 final double sum = (e1 + e2 + e3);
477 e += sum * t1t2;
478 if (gradient) {
479
480 double de1 = -2.0 * e1 * a1 * w1;
481 double de2 = -2.0 * e2 * a2 * w2;
482 double de3 = -2.0 * e3 * a3 * w3;
483 double dsum = (de1 + de2 + de3) * t1t2 / r;
484 double dedx = dsum * xr;
485 double dedy = dsum * yr;
486 double dedz = dsum * zr;
487 gX[i] += dedx;
488 gY[i] += dedy;
489 gZ[i] += dedz;
490 gX[k] -= dedx;
491 gY[k] -= dedy;
492 gZ[k] -= dedz;
493
494 carbonSASACR[ii] += sum * tanhSA[kk] * dtanhSA[ii];
495 carbonSASACR[kk] += sum * tanhSA[ii] * dtanhSA[kk];
496 }
497 }
498 }
499 }
500 energy += e;
501 }
502 }
503
504 @Override
505 public void start() {
506 energy = 0.0;
507 for (int i = 0; i < nAtoms; i++) {
508 omit[i] = -1;
509 }
510 int threadID = getThreadIndex();
511 gX = grad[threadID][0];
512 gY = grad[threadID][1];
513 gZ = grad[threadID][2];
514 if (gradient) {
515 for (int i = 0; i < nCarbon; i++) {
516 carbonSASACR[i] = 0.0;
517 }
518 }
519 }
520 }
521
522
523
524
525
526
527 private class CarbonSASACRLoop extends IntegerForLoop {
528
529 private double[] gX;
530 private double[] gY;
531 private double[] gZ;
532
533 public CarbonSASACRLoop() {}
534
535 @Override
536 public void run(int lb, int ub) {
537 for (int ii = lb; ii <= ub; ii++) {
538 final int i = iCarbon[ii];
539 if (!use[i]) {
540 continue;
541 }
542 final double carbonSA = carbonSASA[ii];
543 final double xi = x[i];
544 final double yi = y[i];
545 final double zi = z[i];
546 for (int k = 0; k < nAtoms; k++) {
547 if (i != k && use[k]) {
548 final double xr = xi - x[k];
549 final double yr = yi - y[k];
550 final double zr = zi - z[k];
551 final double r2 = xr * xr + yr * yr + zr * zr;
552 double rk = rPMF[k];
553 double rBig = rk + rSurf;
554 if (r2 <= rBig * rBig) {
555 final double r = sqrt(r2);
556 final double rSmall = rk - rCarbon;
557 final double rr = 1.0 / r;
558 final double rr2 = rr * rr;
559 final double part = piSurf * (rBig - r) * (1.0 + rSmall * rr);
560 double t1b = -piSurf * (1.0 + rBig * rSmall * rr2);
561 double t1a = -sasa[ii] / (1.0 / carbonSA - part);
562 double de = t1a * t1b * rr * carbonSASACR[ii];
563 double dedx = de * xr;
564 double dedy = de * yr;
565 double dedz = de * zr;
566 gX[i] += dedx;
567 gY[i] += dedy;
568 gZ[i] += dedz;
569 gX[k] -= dedx;
570 gY[k] -= dedy;
571 gZ[k] -= dedz;
572 }
573 }
574 }
575 }
576 }
577
578 @Override
579 public void start() {
580 int threadID = getThreadIndex();
581 gX = grad[threadID][0];
582 gY = grad[threadID][1];
583 gZ = grad[threadID][2];
584 }
585 }
586 }