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 static java.lang.String.format;
41 import static org.apache.commons.math3.util.FastMath.PI;
42 import static org.apache.commons.math3.util.FastMath.cbrt;
43 import static org.apache.commons.math3.util.FastMath.pow;
44 import static org.apache.commons.math3.util.FastMath.sqrt;
45
46 import ffx.numerics.atomic.AtomicDoubleArray3D;
47 import ffx.numerics.switching.MultiplicativeSwitch;
48 import ffx.potential.bonded.Atom;
49 import ffx.potential.nonbonded.GeneralizedKirkwood;
50 import ffx.potential.parameters.ForceField;
51 import java.util.logging.Level;
52 import java.util.logging.Logger;
53
54
55
56
57
58
59
60
61 public class ChandlerCavitation {
62
63 private static final Logger logger = Logger.getLogger(ChandlerCavitation.class.getName());
64
65 private final double HALF_SWITCH_RANGE = 3.5;
66
67
68
69
70
71 private final double SA_SWITCH_OFFSET = 0.2;
72
73 private final boolean doVolume;
74 private final boolean doSurfaceArea;
75
76 private final GaussVol gaussVol;
77 private final ConnollyRegion connollyRegion;
78
79 private double surfaceArea;
80
81 private double surfaceAreaEnergy;
82
83 private double volume;
84
85 private double volumeEnergy;
86
87 private double cavitationEnergy;
88
89
90
91
92
93
94
95
96
97
98 private double effectiveRadius;
99
100
101
102
103 private double solventPressure = GeneralizedKirkwood.DEFAULT_SOLVENT_PRESSURE;
104
105 private double surfaceTension = GeneralizedKirkwood.DEFAULT_CAVDISP_SURFACE_TENSION;
106
107
108
109 private double crossOver = GeneralizedKirkwood.DEFAULT_CROSSOVER;
110
111 private double beginVolumeOff = crossOver - HALF_SWITCH_RANGE;
112
113 private double endVolumeOff = beginVolumeOff + 2.0 * HALF_SWITCH_RANGE;
114
115 private double beginSurfaceAreaOff = crossOver + SA_SWITCH_OFFSET + HALF_SWITCH_RANGE;
116
117 private double endSurfaceAreaOff = beginSurfaceAreaOff - 2.0 * HALF_SWITCH_RANGE;
118
119 private MultiplicativeSwitch volumeSwitch =
120 new MultiplicativeSwitch(beginVolumeOff, endVolumeOff);
121
122 private MultiplicativeSwitch surfaceAreaSwitch =
123 new MultiplicativeSwitch(beginSurfaceAreaOff, endSurfaceAreaOff);
124
125 private final int nAtoms;
126 private final Atom[] atoms;
127
128 public ChandlerCavitation(Atom[] atoms, GaussVol gaussVol, ForceField forceField) {
129 this.atoms = atoms;
130 this.nAtoms = atoms.length;
131 this.gaussVol = gaussVol;
132 this.connollyRegion = null;
133
134 doVolume = forceField.getBoolean("VOLUMETERM", true);
135 doSurfaceArea = forceField.getBoolean("AREATERM", true);
136 }
137
138 public ChandlerCavitation(Atom[] atoms, ConnollyRegion connollyRegion, ForceField forceField) {
139 this.atoms = atoms;
140 this.nAtoms = atoms.length;
141 this.connollyRegion = connollyRegion;
142 this.gaussVol = null;
143
144 doVolume = forceField.getBoolean("VOLUMETERM", true);
145 doSurfaceArea = forceField.getBoolean("AREATERM", true);
146 }
147
148
149
150
151
152
153
154
155 public double energyAndGradient(double[][] positions, AtomicDoubleArray3D gradient) {
156 if (gaussVol != null) {
157 return energyAndGradientGausVol(positions, gradient);
158 } else {
159 return energyAndGradientConnolly(gradient);
160 }
161 }
162
163
164
165
166
167
168
169 public double energyAndGradientConnolly(AtomicDoubleArray3D gradient) {
170
171
172 cavitationEnergy = 0.0;
173
174 connollyRegion.init(atoms, true);
175 connollyRegion.runVolume();
176
177
178 surfaceArea = connollyRegion.getSurfaceArea();
179 surfaceAreaEnergy = surfaceArea * surfaceTension;
180
181
182 volume = connollyRegion.getVolume();
183 volumeEnergy = volume * solventPressure;
184
185
186
187
188
189
190
191
192
193
194 effectiveRadius = cbrt(3.0 * volume / (4.0 * PI));
195 double reff = effectiveRadius;
196 double reff2 = reff * reff;
197 double reff3 = reff2 * reff;
198 double reff4 = reff3 * reff;
199 double reff5 = reff4 * reff;
200 double vdWVolPI23 = pow(volume / PI, 2.0 / 3.0);
201 double dReffdvdW = 1.0 / (pow(6.0, 2.0 / 3.0) * PI * vdWVolPI23);
202
203
204 if (doVolume) {
205 if (!doSurfaceArea || reff < beginVolumeOff) {
206
207 cavitationEnergy = volumeEnergy;
208 double[][] volumeGradient = connollyRegion.getVolumeGradient();
209 for (int i = 0; i < nAtoms; i++) {
210 double dx = solventPressure * volumeGradient[0][i];
211 double dy = solventPressure * volumeGradient[1][i];
212 double dz = solventPressure * volumeGradient[2][i];
213 gradient.add(0, i, dx, dy, dz);
214 }
215 } else if (reff <= endVolumeOff) {
216
217 double taper = volumeSwitch.taper(reff, reff2, reff3, reff4, reff5);
218 cavitationEnergy = taper * volumeEnergy;
219 double dtaper = volumeSwitch.dtaper(reff, reff2, reff3, reff4) * dReffdvdW;
220 double factor = dtaper * volumeEnergy + taper * solventPressure;
221 double[][] volumeGradient = connollyRegion.getVolumeGradient();
222 for (int i = 0; i < nAtoms; i++) {
223 double dx = factor * volumeGradient[0][i];
224 double dy = factor * volumeGradient[1][i];
225 double dz = factor * volumeGradient[2][i];
226 gradient.add(0, i, dx, dy, dz);
227 }
228 }
229 }
230
231
232 if (doSurfaceArea) {
233 if (!doVolume || reff > beginSurfaceAreaOff) {
234
235 cavitationEnergy = surfaceAreaEnergy;
236
237 } else if (reff >= endSurfaceAreaOff) {
238
239 double taperSA = surfaceAreaSwitch.taper(reff, reff2, reff3, reff4, reff5);
240 cavitationEnergy += taperSA * surfaceAreaEnergy;
241
242
243 }
244 }
245
246 if (logger.isLoggable(Level.FINE)) {
247 logger.fine("\n Connolly");
248 logger.fine(format(" Volume: %8.3f (Ang^3)", volume));
249 logger.fine(format(" Volume Energy: %8.3f (kcal/mol)", volumeEnergy));
250 logger.fine(format(" Surface Area: %8.3f (Ang^2)", surfaceArea));
251 logger.fine(format(" Surface Area Energy: %8.3f (kcal/mol)", surfaceAreaEnergy));
252 logger.fine(format(" Volume + SA Energy: %8.3f (kcal/mol)", cavitationEnergy));
253 logger.fine(format(" Effective Radius: %8.3f (Ang)", reff));
254 }
255
256 return cavitationEnergy;
257 }
258
259
260
261
262
263
264
265
266 public double energyAndGradientGausVol(double[][] positions, AtomicDoubleArray3D gradient) {
267
268
269 cavitationEnergy = 0.0;
270
271 gaussVol.computeVolumeAndSA(positions);
272
273
274 volume = gaussVol.getVolume();
275 double[] volumeGradient = gaussVol.getVolumeGradient();
276
277
278 volumeEnergy = volume * solventPressure;
279
280
281 surfaceArea = gaussVol.getSurfaceArea();
282 double[] surfaceAreaGradient = gaussVol.getSurfaceAreaGradient();
283
284
285 surfaceAreaEnergy = surfaceArea * surfaceTension;
286
287
288 effectiveRadius = 0.5 * sqrt(surfaceArea / PI);
289 double reff = effectiveRadius;
290 double reff2 = reff * reff;
291 double reff3 = reff2 * reff;
292 double reff4 = reff3 * reff;
293 double reff5 = reff4 * reff;
294 double dRdSA = reff / (2.0 * surfaceArea);
295
296
297
298
299
300
301
302
303
304
305
306
307 if (doVolume) {
308 if (!doSurfaceArea || reff < beginVolumeOff) {
309
310 cavitationEnergy = volumeEnergy;
311 addVolumeGradient(0.0, solventPressure, surfaceAreaGradient, volumeGradient, gradient);
312 } else if (reff <= endVolumeOff) {
313
314 double taper = volumeSwitch.taper(reff, reff2, reff3, reff4, reff5);
315 double dtaper = volumeSwitch.dtaper(reff, reff2, reff3, reff4) * dRdSA;
316 cavitationEnergy = taper * volumeEnergy;
317 double factorSA = dtaper * volumeEnergy;
318 double factorVol = taper * solventPressure;
319 addVolumeGradient(factorSA, factorVol, surfaceAreaGradient, volumeGradient, gradient);
320 }
321 }
322
323 if (doSurfaceArea) {
324 if (!doVolume || reff > beginSurfaceAreaOff) {
325
326 cavitationEnergy = surfaceAreaEnergy;
327 addSurfaceAreaGradient(surfaceTension, surfaceAreaGradient, gradient);
328 } else if (reff >= endSurfaceAreaOff) {
329
330 double taperSA = surfaceAreaSwitch.taper(reff, reff2, reff3, reff4, reff5);
331 double dtaperSA = surfaceAreaSwitch.dtaper(reff, reff2, reff3, reff4) * dRdSA;
332 cavitationEnergy += taperSA * surfaceAreaEnergy;
333 double factor = dtaperSA * surfaceAreaEnergy + taperSA * surfaceTension;
334 addSurfaceAreaGradient(factor, surfaceAreaGradient, gradient);
335 }
336 }
337
338 if (logger.isLoggable(Level.FINE)) {
339 logger.fine("\n GaussVol");
340 logger.fine(format(" Volume: %8.3f (Ang^3)", volume));
341 logger.fine(format(" Volume Energy: %8.3f (kcal/mol)", volumeEnergy));
342 logger.fine(format(" Surface Area: %8.3f (Ang^2)", surfaceArea));
343 logger.fine(format(" Surface Area Energy: %8.3f (kcal/mol)", surfaceAreaEnergy));
344 logger.fine(format(" Volume + SA Energy: %8.3f (kcal/mol)", cavitationEnergy));
345 logger.fine(format(" Effective Radius: %8.3f (Ang)", reff));
346 }
347
348 return cavitationEnergy;
349 }
350
351 public ConnollyRegion getConnollyRegion() {
352 return connollyRegion;
353 }
354
355 public double getCrossOver() {
356 return crossOver;
357 }
358
359 public void setCrossOver(double crossOver) {
360 if (crossOver < 3.5) {
361 logger.severe(
362 format(" The cross-over point (%8.6f A) must be greater than 3.5 A", crossOver));
363 return;
364 }
365 this.crossOver = crossOver;
366 beginVolumeOff = crossOver - HALF_SWITCH_RANGE;
367 endVolumeOff = beginVolumeOff + 2.0 * HALF_SWITCH_RANGE;
368 beginSurfaceAreaOff = crossOver + SA_SWITCH_OFFSET + HALF_SWITCH_RANGE;
369 endSurfaceAreaOff = beginSurfaceAreaOff - 2.0 * HALF_SWITCH_RANGE;
370 volumeSwitch = new MultiplicativeSwitch(beginVolumeOff, endVolumeOff);
371 surfaceAreaSwitch = new MultiplicativeSwitch(beginSurfaceAreaOff, endSurfaceAreaOff);
372 }
373
374 public double getEffectiveRadius() {
375 return effectiveRadius;
376 }
377
378 public double getEnergy() {
379 return cavitationEnergy;
380 }
381
382 public GaussVol getGaussVol() {
383 return gaussVol;
384 }
385
386 public double getSolventPressure() {
387 return solventPressure;
388 }
389
390 public void setSolventPressure(double solventPressure) {
391 double newCrossOver = 3.0 * surfaceTension / solventPressure;
392 if (newCrossOver < 3.5) {
393 logger.severe(
394 format(
395 " The solvent pressure (%8.6f kcal/mol/A^3)"
396 + " and surface tension (%8.6f kcal/mol/A^2) combination is not supported.",
397 solventPressure, surfaceTension));
398 return;
399 }
400 this.solventPressure = solventPressure;
401 this.setCrossOver(newCrossOver);
402 }
403
404
405
406
407
408
409 public double getSurfaceArea() {
410 return surfaceArea;
411 }
412
413
414
415
416
417
418 public double getSurfaceAreaEnergy() {
419 return surfaceAreaEnergy;
420 }
421
422 public double getSurfaceTension() {
423 return surfaceTension;
424 }
425
426 public void setSurfaceTension(double surfaceTension) {
427 double newCrossOver = 3.0 * surfaceTension / solventPressure;
428 if (newCrossOver < 3.5) {
429 logger.severe(
430 format(
431 " The solvent pressure (%8.6f kcal/mol/A^3)"
432 + " and surface tension (%8.6f kcal/mol/A^2) combination is not supported.",
433 solventPressure, surfaceTension));
434 return;
435 }
436 this.surfaceTension = surfaceTension;
437 this.setCrossOver(newCrossOver);
438 }
439
440
441
442
443
444
445 public double getVolume() {
446 return volume;
447 }
448
449
450
451
452
453
454 public double getVolumeEnergy() {
455 return volumeEnergy;
456 }
457
458
459
460
461
462
463
464
465
466
467 private void addVolumeGradient(
468 double factorSA,
469 double factorVol,
470 double[] surfaceAreaGradient,
471 double[] volumeGradient,
472 AtomicDoubleArray3D gradient) {
473 for (int i = 0; i < nAtoms; i++) {
474 int index = i * 3;
475 double gx = factorSA * surfaceAreaGradient[index] + factorVol * volumeGradient[index++];
476 double gy = factorSA * surfaceAreaGradient[index] + factorVol * volumeGradient[index++];
477 double gz = factorSA * surfaceAreaGradient[index] + factorVol * volumeGradient[index];
478 gradient.add(0, i, gx, gy, gz);
479 }
480 }
481
482
483
484
485
486
487
488
489 private void addSurfaceAreaGradient(
490 double factor, double[] surfaceAreaGradient, AtomicDoubleArray3D gradient) {
491 for (int i = 0; i < nAtoms; i++) {
492 int index = i * 3;
493 double gx = factor * surfaceAreaGradient[index++];
494 double gy = factor * surfaceAreaGradient[index++];
495 double gz = factor * surfaceAreaGradient[index];
496 gradient.add(0, i, gx, gy, gz);
497 }
498 }
499 }