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 ffx.numerics.math.DoubleMath.add;
41 import static ffx.numerics.math.DoubleMath.length2;
42 import static ffx.numerics.math.DoubleMath.scale;
43 import static ffx.numerics.math.DoubleMath.sub;
44 import static java.lang.Double.compare;
45 import static java.lang.String.format;
46 import static org.apache.commons.math3.util.FastMath.PI;
47 import static org.apache.commons.math3.util.FastMath.cbrt;
48 import static org.apache.commons.math3.util.FastMath.exp;
49 import static org.apache.commons.math3.util.FastMath.pow;
50
51 import edu.rit.pj.IntegerForLoop;
52 import edu.rit.pj.ParallelRegion;
53 import edu.rit.pj.ParallelTeam;
54 import edu.rit.pj.reduction.SharedDouble;
55 import ffx.numerics.atomic.AtomicDoubleArray;
56 import ffx.numerics.atomic.AtomicDoubleArray.AtomicDoubleArrayImpl;
57 import ffx.numerics.atomic.AtomicDoubleArray3D;
58 import ffx.potential.bonded.Atom;
59 import ffx.potential.parameters.ForceField;
60
61 import java.util.ArrayList;
62 import java.util.Collections;
63 import java.util.List;
64 import java.util.logging.Level;
65 import java.util.logging.Logger;
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81 public class GaussVol {
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109 private static final Logger logger = Logger.getLogger(GaussVol.class.getName());
110
111
112
113 private static final double offset = 0.005;
114
115
116
117 private static final double KFC = 2.2269859253;
118
119 private static final double PFC = 2.5;
120
121
122
123 private static final double sphereConversion = KFC;
124
125
126
127 private static final int MAX_ORDER = 16;
128
129
130
131 private static final double ANG3 = 1.0;
132
133
134
135 private static final double VOLMINA = 0.01 * ANG3;
136
137
138
139 private static final double VOLMINB = 0.1 * ANG3;
140
141
142
143 private static final double MIN_GVOL = Double.MIN_VALUE;
144
145
146
147
148
149 public static final double DEFAULT_GAUSSVOL_RADII_OFFSET = 0.0;
150
151
152
153
154
155
156
157
158 public static final double DEFAULT_GAUSSVOL_RADII_SCALE = 1.15;
159
160 private static final double RMIN_TO_SIGMA = 1.0 / pow(2.0, 1.0 / 6.0);
161
162
163 private final GaussVolRegion gaussVolRegion;
164
165 private enum GAUSSVOL_MODE {COMPUTE_TREE, RESCAN_TREE}
166
167 private final AtomicDoubleArray3D grad;
168 private final SharedDouble totalVolume;
169 private final SharedDouble energy;
170 private final AtomicDoubleArray gradV;
171 private final AtomicDoubleArray freeVolume;
172 private final AtomicDoubleArray selfVolume;
173 private final ParallelTeam parallelTeam;
174
175 private final double vdwRadiiOffset;
176 private final double vdwRadiiScale;
177 private final boolean includeHydrogen;
178 private final boolean useSigma;
179 private static final double FOUR_THIRDS_PI = 4.0 / 3.0 * PI;
180
181
182
183
184 private final Atom[] atoms;
185
186
187
188 private final int nAtoms;
189
190
191
192 private double[] radii;
193
194
195
196 private double[] volumes;
197
198
199
200
201 private final double[] radiiOffset;
202
203
204
205 private final double[] volumeOffset;
206
207
208
209 private final double[] gammas;
210
211
212
213
214 private final boolean[] ishydrogen;
215
216
217
218 private final double[] selfVolumeFraction;
219
220
221
222 private final GaussianOverlapTree tree;
223
224
225
226 private int maximumDepth = 0;
227
228
229
230 private int totalNumberOfOverlaps = 0;
231
232
233
234 private double surfaceArea;
235
236
237
238 private double volume;
239
240
241
242 private double[] volumeGradient;
243
244
245
246 private double[] surfaceAreaGradient;
247
248
249
250
251
252
253
254
255 public GaussVol(Atom[] atoms, ForceField forceField, ParallelTeam parallelTeam) {
256 this.atoms = atoms;
257 nAtoms = atoms.length;
258 tree = new GaussianOverlapTree(nAtoms);
259 radii = new double[nAtoms];
260 volumes = new double[nAtoms];
261 gammas = new double[nAtoms];
262 ishydrogen = new boolean[nAtoms];
263 radiiOffset = new double[nAtoms];
264 volumeOffset = new double[nAtoms];
265 selfVolumeFraction = new double[nAtoms];
266
267 vdwRadiiOffset = forceField.getDouble("GAUSSVOL_RADII_OFFSET", DEFAULT_GAUSSVOL_RADII_OFFSET);
268 vdwRadiiScale = forceField.getDouble("GAUSSVOL_RADII_SCALE", DEFAULT_GAUSSVOL_RADII_SCALE);
269 includeHydrogen = forceField.getBoolean("GAUSSVOL_HYDROGEN", false);
270 useSigma = forceField.getBoolean("GAUSSVOL_USE_SIGMA", false);
271
272 for (int i = 0; i < nAtoms; i++) {
273 updateAtom(i);
274 }
275
276 this.parallelTeam = parallelTeam;
277 int nThreads = 1;
278 if (parallelTeam != null) {
279 nThreads = parallelTeam.getThreadCount();
280 gaussVolRegion = new GaussVolRegion(nThreads);
281 } else {
282 gaussVolRegion = null;
283 }
284
285 totalVolume = new SharedDouble();
286 energy = new SharedDouble();
287 AtomicDoubleArrayImpl atomicDoubleArrayImpl = AtomicDoubleArrayImpl.MULTI;
288 grad = new AtomicDoubleArray3D(atomicDoubleArrayImpl, nAtoms, nThreads);
289 gradV = atomicDoubleArrayImpl.createInstance(nThreads, nAtoms);
290 freeVolume = atomicDoubleArrayImpl.createInstance(nThreads, nAtoms);
291 selfVolume = atomicDoubleArrayImpl.createInstance(nThreads, nAtoms);
292 }
293
294
295
296
297
298
299 public double[] getRadii() {
300 return radii;
301 }
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323 private static double overlapGaussianAlpha(
324 GaussianVca g1, GaussianVca g2, GaussianVca g12, double[] dVdr, double[] dVdV, double[] sfp) {
325 double[] c1 = g1.c;
326 double[] c2 = g2.c;
327 double[] dist = new double[3];
328
329 sub(c2, c1, dist);
330 double d2 = length2(dist);
331 double a12 = g1.a + g2.a;
332 double deltai = 1.0 / a12;
333
334
335 double df = (g1.a) * (g2.a) * deltai;
336 double ef = exp(-df * d2);
337 double gvol = ((g1.v * g2.v) / pow(PI / df, 1.5)) * ef;
338
339
340 double dgvol = -2.0 * df * gvol;
341 dVdr[0] = dgvol;
342
343
344 double dgvolv = g1.v > 0.0 ? gvol / g1.v : 0.0;
345 dVdV[0] = dgvolv;
346
347
348
349
350
351
352 double[] c1a = new double[3];
353 double[] c2a = new double[3];
354 scale(c1, g1.a * deltai, c1a);
355 scale(c2, g2.a * deltai, c2a);
356 add(c1a, c2a, g12.c);
357 g12.a = a12;
358 g12.v = gvol;
359
360
361 double[] sp = new double[1];
362 double s = switchingFunction(gvol, VOLMINA, VOLMINB, sp);
363 sfp[0] = sp[0] * gvol + s;
364 return s * gvol;
365 }
366
367
368
369
370
371
372
373
374
375
376 private static double switchingFunction(
377 double gvol, double volmina, double volminb, double[] sp) {
378 if (gvol > volminb) {
379 sp[0] = 0.0;
380 return 1.0;
381 } else if (gvol < volmina) {
382 sp[0] = 0.0;
383 return 0.0;
384 }
385 double swd = 1.0 / (volminb - volmina);
386 double swu = (gvol - volmina) * swd;
387 double swu2 = swu * swu;
388 double swu3 = swu * swu2;
389 sp[0] = swd * 30.0 * swu2 * (1.0 - 2.0 * swu + swu2);
390 return swu3 * (10.0 - 15.0 * swu + 6.0 * swu2);
391 }
392
393
394
395
396
397
398 public double[] getSelfVolumeFractions() {
399 return selfVolumeFraction;
400 }
401
402
403
404
405
406
407
408 public double computeVolumeAndSA(double[][] positions) {
409
410 if (parallelTeam == null || parallelTeam.getThreadCount() == 1) {
411
412 for (int i = 0; i < nAtoms - 1; i++) {
413 updateAtom(i);
414 }
415
416
417 computeTree(positions);
418
419
420 computeVolume(totalVolume, energy, grad, gradV, freeVolume, selfVolume);
421 } else {
422
423 try {
424 gaussVolRegion.init(GAUSSVOL_MODE.COMPUTE_TREE, positions);
425 parallelTeam.execute(gaussVolRegion);
426 } catch (Exception e) {
427 logger.severe(" Exception evaluating GaussVol " + e);
428 }
429 }
430
431 if (volumeGradient == null || volumeGradient.length != nAtoms * 3) {
432 volumeGradient = new double[nAtoms * 3];
433 surfaceAreaGradient = new double[nAtoms * 3];
434 }
435
436 double selfVolumeSum = 0.0;
437 double noOverlapVolume = 0.0;
438 for (int i = 0; i < nAtoms; i++) {
439 double si = selfVolume.get(i);
440 selfVolumeSum += si;
441 double x = grad.getX(i);
442 double y = grad.getY(i);
443 double z = grad.getZ(i);
444 if (ishydrogen[i]) {
445 x = 0.0;
446 y = 0.0;
447 z = 0.0;
448 selfVolumeFraction[i] = 0.0;
449 } else {
450 double vi = volumes[i];
451 noOverlapVolume += vi;
452 selfVolumeFraction[i] = si / vi;
453 }
454
455 int index = i * 3;
456 volumeGradient[index++] = x;
457 volumeGradient[index++] = y;
458 volumeGradient[index] = z;
459
460
461 index = i * 3;
462 surfaceAreaGradient[index++] = x;
463 surfaceAreaGradient[index++] = y;
464 surfaceAreaGradient[index] = z;
465 }
466
467
468 if (logger.isLoggable(Level.FINE)) {
469 double meanHCT = selfVolumeSum / noOverlapVolume;
470 logger.fine(format("\n Mean Self-Volume Fraction: %16.8f", meanHCT));
471 logger.fine(format(" Cube Root of the Fraction: %16.8f", cbrt(meanHCT)));
472 if (logger.isLoggable(Level.FINEST)) {
473 logger.finest(" Atomic Self-volume Fractions");
474 for (int i = 0; i < nAtoms; i++) {
475 logger.finest(format(" %6d %16.8f", i + 1, selfVolumeFraction[i]));
476 }
477 }
478 }
479
480
481 volume = totalVolume.get();
482
483
484 double[] radiiBak = this.radii;
485 double[] volumeBak = this.volumes;
486
487
488 this.radii = radiiOffset;
489 this.volumes = volumeOffset;
490
491
492
493
494 if (parallelTeam == null || parallelTeam.getThreadCount() == 1) {
495
496
497 rescanTreeVolumes(positions);
498
499 computeVolume(totalVolume, energy, grad, gradV, freeVolume, selfVolume);
500 } else {
501
502 try {
503 gaussVolRegion.init(GAUSSVOL_MODE.RESCAN_TREE, positions);
504 parallelTeam.execute(gaussVolRegion);
505 } catch (Exception e) {
506 logger.severe(" Exception evaluating GaussVol " + e);
507 }
508 }
509
510
511 this.radii = radiiBak;
512 this.volumes = volumeBak;
513
514 double selfVolumeOffsetSum = 0;
515 for (int i = 0; i < nAtoms; i++) {
516 selfVolumeOffsetSum += selfVolume.get(i);
517 double x = grad.getX(i);
518 double y = grad.getY(i);
519 double z = grad.getZ(i);
520 if (ishydrogen[i]) {
521 x = 0.0;
522 y = 0.0;
523 z = 0.0;
524 }
525
526 int index = i * 3;
527 surfaceAreaGradient[index] = (x - surfaceAreaGradient[index++]) / offset;
528 surfaceAreaGradient[index] = (y - surfaceAreaGradient[index++]) / offset;
529 surfaceAreaGradient[index] = (z - surfaceAreaGradient[index]) / offset;
530 }
531
532
533 surfaceArea = (selfVolumeOffsetSum - selfVolumeSum) / offset;
534
535 return volume;
536 }
537
538
539
540
541
542
543 public int getMaximumDepth() {
544 return maximumDepth;
545 }
546
547
548
549
550
551
552 public double getSurfaceArea() {
553 return surfaceArea;
554 }
555
556 public double[] getSurfaceAreaGradient() {
557 return surfaceAreaGradient;
558 }
559
560
561
562
563
564
565 public int getTotalNumberOfOverlaps() {
566 return totalNumberOfOverlaps;
567 }
568
569
570
571
572
573
574 public double getVolume() {
575 return volume;
576 }
577
578 public double[] getVolumeGradient() {
579 return volumeGradient;
580 }
581
582 public void allocate(Atom[] atoms) throws Exception {
583 if (atoms.length != this.atoms.length) {
584 throw new Exception(" allocate: number of atoms does not match");
585 }
586 }
587
588
589
590
591
592
593 private void computeTree(double[][] positions) {
594 tree.computeOverlapTreeR(positions, radii, volumes, gammas, ishydrogen);
595 }
596
597
598
599
600
601
602
603
604
605
606
607
608 private void computeVolume(
609 SharedDouble totalVolume,
610 SharedDouble totalEnergy,
611 AtomicDoubleArray3D grad,
612 AtomicDoubleArray gradV,
613 AtomicDoubleArray freeVolume,
614 AtomicDoubleArray selfVolume) {
615
616 totalVolume.set(0.0);
617 totalEnergy.set(0.0);
618 grad.reset(0, 0, nAtoms - 1);
619 gradV.reset(0, 0, nAtoms - 1);
620 freeVolume.reset(0, 0, nAtoms - 1);
621 selfVolume.reset(0, 0, nAtoms - 1);
622
623
624 tree.computeVolume2R(totalVolume, totalEnergy, grad, gradV, freeVolume, selfVolume);
625
626
627 grad.reduce(0, nAtoms - 1);
628 gradV.reduce(0, nAtoms - 1);
629 freeVolume.reduce(0, nAtoms - 1);
630 selfVolume.reduce(0, nAtoms - 1);
631
632 for (int i = 0; i < nAtoms; ++i) {
633 if (volumes[i] > 0) {
634 gradV.set(0, i, gradV.get(i) / volumes[i]);
635 }
636 }
637 }
638
639
640
641
642
643
644 private void rescanTreeVolumes(double[][] positions) {
645 tree.rescanTreeV(positions, radii, volumes, gammas, ishydrogen);
646 }
647
648
649
650
651 private void rescanTreeGammas() {
652 tree.rescanTreeG(gammas);
653 }
654
655
656
657
658
659
660 private void getStats(int[] nov) {
661 if (nov.length != nAtoms) {
662 return;
663 }
664
665 for (int i = 0; i < nAtoms; i++) {
666 nov[i] = 0;
667 }
668 for (int atom = 0; atom < nAtoms; atom++) {
669 int slot = atom + 1;
670 nov[atom] = tree.nChildrenUnderSlotR(slot);
671 }
672 }
673
674
675
676
677 private void printTree() {
678 tree.printTree();
679 }
680
681
682
683
684 private static class GaussianVca {
685
686
687 public double v;
688
689 public double a;
690
691 public double[] c = new double[3];
692 }
693
694
695
696
697
698
699
700
701
702
703
704 private static class GaussianOverlap implements Comparable<GaussianOverlap> {
705
706
707
708
709 public int level;
710
711
712
713 public GaussianVca g;
714
715
716
717 public double volume;
718
719
720
721 public int atom;
722
723
724
725 double dvv1;
726
727
728
729 double[] dv1;
730
731
732
733 double gamma1i;
734
735
736
737 double selfVolume;
738
739
740
741 double sfp;
742
743
744
745 int parentIndex;
746
747
748
749 int childrenStartIndex;
750
751
752
753 int childrenCount;
754
755 public GaussianOverlap() {
756 g = new GaussianVca();
757 dv1 = new double[3];
758 }
759
760 public GaussianOverlap(GaussianVca g, double volume, double selfVolume, int atom) {
761 this.g = g;
762 this.volume = volume;
763 this.selfVolume = selfVolume;
764 this.atom = atom;
765
766 dv1 = new double[3];
767 }
768
769
770
771
772
773
774
775 @Override
776 public int compareTo(GaussianOverlap o) {
777 return compare(volume, o.volume);
778 }
779
780
781
782
783 public void printOverlap() {
784 logger.info(format(" Gaussian Overlap %d: Atom: %d, Parent: %d, ChildrenStartIndex: %d, ChildrenCount: %d,"
785 + "Volume: %6.3f, Gamma: %6.3f, Gauss.a: %6.3f, Gauss.v: %6.3f, Gauss.center (%6.3f,%6.3f,%6.3f),"
786 + "dedx: %6.3f, dedy: %6.3f, dedz: %6.3f, sfp: %6.3f",
787 level, atom, parentIndex, childrenStartIndex, childrenCount, volume, gamma1i,
788 g.a, g.v, g.c[0], g.c[1], g.c[2], dv1[0], dv1[1], dv1[2], sfp));
789 }
790 }
791
792 public void updateAtom(int i) {
793 Atom atom = atoms[i];
794 ishydrogen[i] = atom.isHydrogen();
795 if (includeHydrogen) {
796 ishydrogen[i] = false;
797 }
798 radii[i] = atom.getVDWType().radius / 2.0;
799 if (useSigma) {
800 radii[i] *= RMIN_TO_SIGMA;
801 }
802 radii[i] += vdwRadiiOffset;
803 radii[i] *= vdwRadiiScale;
804 volumes[i] = FOUR_THIRDS_PI * pow(radii[i], 3);
805 gammas[i] = 1.0;
806 radiiOffset[i] = radii[i] + offset;
807 volumeOffset[i] = FOUR_THIRDS_PI * pow(radiiOffset[i], 3);
808 }
809
810
811
812
813 private class GaussianOverlapTree {
814
815
816
817
818 int nAtoms;
819
820
821
822 List<GaussianOverlap> overlaps;
823
824
825
826
827
828
829 GaussianOverlapTree(int nAtoms) {
830 this.nAtoms = nAtoms;
831 overlaps = Collections.synchronizedList(new ArrayList<>(nAtoms + 1));
832 }
833
834
835
836
837
838
839
840
841
842
843 void initOverlapTree(
844 double[][] pos, double[] radii, double[] volumes, double[] gammas, boolean[] ishydrogen) {
845
846
847 overlaps = Collections.synchronizedList(new ArrayList<>(nAtoms + 1));
848
849
850 GaussianOverlap overlap = new GaussianOverlap();
851 overlap.level = 0;
852 overlap.volume = 0;
853 overlap.dvv1 = 0.;
854 overlap.selfVolume = 0;
855 overlap.sfp = 1.;
856 overlap.gamma1i = 0.;
857 overlap.parentIndex = -1;
858 overlap.atom = -1;
859 overlap.childrenStartIndex = 1;
860 overlap.childrenCount = nAtoms;
861 overlaps.add(0, overlap);
862
863
864 for (int iat = 0; iat < nAtoms; iat++) {
865 overlap = new GaussianOverlap();
866 double a = sphereConversion / (radii[iat] * radii[iat]);
867 double vol = ishydrogen[iat] ? 0 : volumes[iat];
868 overlap.level = 1;
869 overlap.g.v = vol;
870 overlap.g.a = a;
871 overlap.g.c = pos[iat];
872 overlap.volume = vol;
873 overlap.dvv1 = 1;
874 overlap.selfVolume = 0;
875 overlap.sfp = 1;
876 overlap.gamma1i = gammas[iat];
877 overlap.parentIndex = 0;
878 overlap.atom = iat;
879 overlap.childrenStartIndex = -1;
880 overlap.childrenCount = -1;
881 overlaps.add(iat + 1, overlap);
882 }
883 }
884
885
886
887
888
889
890
891
892 int addChildren(int parentIndex, List<GaussianOverlap> childrenOverlaps) {
893
894
895 int startIndex = overlaps.size();
896
897 int noverlaps = childrenOverlaps.size();
898
899
900 GaussianOverlap root = overlaps.get(parentIndex);
901
902
903 root.childrenStartIndex = startIndex;
904 root.childrenCount = noverlaps;
905
906
907 Collections.sort(childrenOverlaps);
908
909 int rootLevel = root.level;
910 int nextLevel = rootLevel + 1;
911
912
913 for (GaussianOverlap child : childrenOverlaps) {
914 child.level = nextLevel;
915
916
917 child.parentIndex = parentIndex;
918
919
920 child.childrenStartIndex = -1;
921 child.childrenCount = -1;
922
923
924 overlaps.add(child);
925 }
926
927 return startIndex;
928 }
929
930
931
932
933
934
935
936
937 void computeChildren(int rootIndex, List<GaussianOverlap> childrenOverlaps) {
938 int parentIndex;
939 int siblingStart, siblingCount;
940
941
942 childrenOverlaps.clear();
943
944
945 GaussianOverlap root = overlaps.get(rootIndex);
946
947
948 parentIndex = root.parentIndex;
949
950
951 if (parentIndex < 0) {
952 throw new IllegalArgumentException(" Cannot compute children of master node!");
953 }
954
955 if (root.level >= MAX_ORDER) {
956 return;
957 }
958
959 GaussianOverlap parent = overlaps.get(parentIndex);
960
961
962 siblingStart = parent.childrenStartIndex;
963 siblingCount = parent.childrenCount;
964
965
966 if (siblingStart < 0 || siblingCount < 0) {
967 throw new IllegalArgumentException(
968 format(" Parent %s of overlap %s has no sibilings.", parent, root));
969 }
970
971
972 if (rootIndex < siblingStart && rootIndex > siblingStart + siblingCount - 1) {
973 throw new IllegalArgumentException(
974 format(" Node %s is somehow not the child of its parent %s", root, parent));
975 }
976
977
978
979 for (int slotj = rootIndex + 1; slotj < siblingStart + siblingCount; slotj++) {
980
981 GaussianVca g12 = new GaussianVca();
982
983 GaussianOverlap sibling = overlaps.get(slotj);
984 double gvol;
985 double[] dVdr = new double[1];
986 double[] dVdV = new double[1];
987 double[] sfp = new double[1];
988
989
990 int atom2 = sibling.atom;
991 GaussianVca g1 = root.g;
992
993
994 GaussianVca g2 = overlaps.get(atom2 + 1).g;
995 gvol = overlapGaussianAlpha(g1, g2, g12, dVdr, dVdV, sfp);
996
997
998
999
1000
1001 if (gvol > MIN_GVOL) {
1002 GaussianOverlap ov = new GaussianOverlap(g12, gvol, 0.0, atom2);
1003
1004
1005 sub(g2.c, g1.c, ov.dv1);
1006 scale(ov.dv1, -dVdr[0], ov.dv1);
1007
1008
1009 ov.dvv1 = dVdV[0];
1010 ov.sfp = sfp[0];
1011 ov.gamma1i = root.gamma1i + overlaps.get(atom2 + 1).gamma1i;
1012 childrenOverlaps.add(ov);
1013 }
1014 }
1015 }
1016
1017
1018
1019
1020
1021
1022 private void computeAndAddChildrenR(int root) {
1023 List<GaussianOverlap> childrenOverlaps = Collections.synchronizedList(new ArrayList<>());
1024 computeChildren(root, childrenOverlaps);
1025 int nOverlaps = childrenOverlaps.size();
1026 if (nOverlaps > 0) {
1027 int startSlot = addChildren(root, childrenOverlaps);
1028 for (int ichild = startSlot; ichild < startSlot + nOverlaps; ichild++) {
1029 computeAndAddChildrenR(ichild);
1030 totalNumberOfOverlaps++;
1031 }
1032 }
1033 }
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044 void computeOverlapTreeR(
1045 double[][] pos, double[] radii, double[] volumes, double[] gammas, boolean[] ishydrogen) {
1046 initOverlapTree(pos, radii, volumes, gammas, ishydrogen);
1047 for (int slot = 1; slot <= nAtoms; slot++) {
1048 computeAndAddChildrenR(slot);
1049 totalNumberOfOverlaps++;
1050 }
1051 }
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073 void computeVolumeUnderSlot2R(
1074 int slot,
1075 double[] psi1i,
1076 double[] f1i,
1077 double[] p1i,
1078 double[] psip1i,
1079 double[] fp1i,
1080 double[] pp1i,
1081 double[] energy1i,
1082 double[] fenergy1i,
1083 double[] penergy1i,
1084 int threadID,
1085 AtomicDoubleArray3D dr,
1086 AtomicDoubleArray dv,
1087 AtomicDoubleArray freeVolume,
1088 AtomicDoubleArray selfVolume) {
1089
1090 GaussianOverlap ov = overlaps.get(slot);
1091
1092
1093 if (ov.level >= maximumDepth) {
1094 maximumDepth = ov.level;
1095 }
1096
1097
1098
1099 double cf = ov.level % 2 == 0 ? -1.0 : 1.0;
1100
1101 double volcoeff = ov.level > 0 ? cf : 0;
1102
1103 double volcoeffp = ov.level > 0 ? volcoeff / (double) ov.level : 0;
1104
1105 int atom = ov.atom;
1106 double ai = overlaps.get(atom + 1).g.a;
1107 double a1i = ov.g.a;
1108 double a1 = a1i - ai;
1109
1110
1111 psi1i[0] = volcoeff * ov.volume;
1112 f1i[0] = volcoeff * ov.sfp;
1113
1114
1115 psip1i[0] = volcoeffp * ov.volume;
1116 fp1i[0] = volcoeffp * ov.sfp;
1117
1118
1119 energy1i[0] = volcoeffp * ov.gamma1i * ov.volume;
1120 fenergy1i[0] = volcoeffp * ov.sfp * ov.gamma1i;
1121
1122
1123 if (ov.childrenStartIndex >= 0) {
1124 for (int sloti = ov.childrenStartIndex;
1125 sloti < ov.childrenStartIndex + ov.childrenCount;
1126 sloti++) {
1127 double[] psi1it = new double[1];
1128 double[] f1it = new double[1];
1129 double[] p1it = new double[3];
1130 double[] psip1it = new double[1];
1131 double[] fp1it = new double[1];
1132 double[] pp1it = new double[3];
1133 double[] energy1it = new double[1];
1134 double[] fenergy1it = new double[1];
1135 double[] penergy1it = new double[3];
1136 computeVolumeUnderSlot2R(
1137 sloti,
1138 psi1it,
1139 f1it,
1140 p1it,
1141 psip1it,
1142 fp1it,
1143 pp1it,
1144 energy1it,
1145 fenergy1it,
1146 penergy1it,
1147 threadID,
1148 dr,
1149 dv,
1150 freeVolume,
1151 selfVolume);
1152 psi1i[0] += psi1it[0];
1153 f1i[0] += f1it[0];
1154 add(p1i, p1it, p1i);
1155
1156 psip1i[0] += psip1it[0];
1157 fp1i[0] += fp1it[0];
1158 add(pp1i, pp1it, pp1i);
1159
1160 energy1i[0] += energy1it[0];
1161 fenergy1i[0] += fenergy1it[0];
1162 add(penergy1i, penergy1it, penergy1i);
1163 }
1164 }
1165
1166
1167 if (ov.level > 0) {
1168
1169 freeVolume.add(threadID, atom, psi1i[0]);
1170 selfVolume.add(threadID, atom, psip1i[0]);
1171
1172
1173 double c2 = ai / a1i;
1174
1175
1176 double[] work1 = new double[3];
1177 double[] work2 = new double[3];
1178 double[] work3 = new double[3];
1179 scale(penergy1i, c2, work1);
1180 scale(ov.dv1, -fenergy1i[0], work2);
1181 add(work1, work2, work3);
1182 dr.add(threadID, atom, work3[0], work3[1], work3[2]);
1183
1184
1185 dv.add(threadID, atom, ov.g.v * fenergy1i[0]);
1186
1187
1188 c2 = a1 / a1i;
1189
1190
1191 scale(ov.dv1, f1i[0], work1);
1192 scale(pp1i, c2, work2);
1193 add(work1, work2, p1i);
1194
1195
1196 scale(ov.dv1, fp1i[0], work1);
1197 scale(pp1i, c2, work2);
1198 add(work1, work2, pp1i);
1199
1200
1201 scale(ov.dv1, fenergy1i[0], work1);
1202 scale(penergy1i, c2, work2);
1203 add(work1, work2, penergy1i);
1204
1205
1206 f1i[0] = ov.dvv1 * f1i[0];
1207 fp1i[0] = ov.dvv1 * fp1i[0];
1208 fenergy1i[0] = ov.dvv1 * fenergy1i[0];
1209 }
1210 }
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222 void computeVolume2R(
1223 SharedDouble volume,
1224 SharedDouble energy,
1225 AtomicDoubleArray3D dr,
1226 AtomicDoubleArray dv,
1227 AtomicDoubleArray freeVolume,
1228 AtomicDoubleArray selfvolume) {
1229
1230 double[] psi1i = new double[1];
1231 double[] f1i = new double[1];
1232 double[] p1i = new double[3];
1233 double[] psip1i = new double[1];
1234 double[] fp1i = new double[1];
1235 double[] pp1i = new double[3];
1236 double[] energy1i = new double[1];
1237 double[] fenergy1i = new double[1];
1238 double[] penergy1i = new double[3];
1239
1240 int threadID = 0;
1241
1242 computeVolumeUnderSlot2R(
1243 0,
1244 psi1i,
1245 f1i,
1246 p1i,
1247 psip1i,
1248 fp1i,
1249 pp1i,
1250 energy1i,
1251 fenergy1i,
1252 penergy1i,
1253 threadID,
1254 dr,
1255 dv,
1256 freeVolume,
1257 selfvolume);
1258
1259 volume.addAndGet(psi1i[0]);
1260 energy.addAndGet(energy1i[0]);
1261 }
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272 void rescanTreeV(
1273 double[][] pos, double[] radii, double[] volumes, double[] gammas, boolean[] ishydrogen) {
1274 initRescanTreeV(pos, radii, volumes, gammas, ishydrogen);
1275 rescanR(0);
1276 }
1277
1278
1279
1280
1281
1282
1283 void rescanR(int slot) {
1284 int parentIndex;
1285
1286
1287 GaussianOverlap ov = overlaps.get(slot);
1288
1289
1290 parentIndex = ov.parentIndex;
1291 if (parentIndex > 0) {
1292 GaussianVca g12 = new GaussianVca();
1293 double[] dVdr = new double[1];
1294 double[] dVdV = new double[1];
1295 double[] sfp = new double[1];
1296
1297 int atom = ov.atom;
1298 GaussianOverlap parent = overlaps.get(parentIndex);
1299 GaussianVca g1 = parent.g;
1300
1301
1302 GaussianVca g2 = overlaps.get(atom + 1).g;
1303 double gvol = overlapGaussianAlpha(g1, g2, g12, dVdr, dVdV, sfp);
1304 ov.g = g12;
1305 ov.volume = gvol;
1306
1307
1308
1309 sub(g2.c, g1.c, ov.dv1);
1310 scale(ov.dv1, -dVdr[0], ov.dv1);
1311
1312
1313 ov.dvv1 = dVdV[0];
1314 ov.sfp = sfp[0];
1315 ov.gamma1i = parent.gamma1i + overlaps.get(atom + 1).gamma1i;
1316 }
1317
1318
1319 for (int slotChild = ov.childrenStartIndex;
1320 slotChild < ov.childrenStartIndex + ov.childrenCount;
1321 slotChild++) {
1322 rescanR(slotChild);
1323 }
1324 }
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335 void initRescanTreeV(
1336 double[][] pos, double[] radii, double[] volumes, double[] gammas, boolean[] ishydrogen) {
1337 int slot = 0;
1338 GaussianOverlap ov = overlaps.get(slot);
1339 ov.level = 0;
1340 ov.volume = 0.0;
1341 ov.dv1 = new double[3];
1342 ov.dvv1 = 0.0;
1343 ov.selfVolume = 0.0;
1344 ov.sfp = 1.0;
1345 ov.gamma1i = 0.0;
1346
1347 slot = 1;
1348 for (int iat = 0; iat < nAtoms; iat++, slot++) {
1349 double a = sphereConversion / (radii[iat] * radii[iat]);
1350 double vol = ishydrogen[iat] ? 0.0 : volumes[iat];
1351 ov = overlaps.get(slot);
1352 ov.level = 1;
1353 ov.g.v = vol;
1354 ov.g.a = a;
1355 ov.g.c = pos[iat];
1356 ov.volume = vol;
1357 ov.dv1 = new double[3];
1358 ov.dvv1 = 1.0;
1359 ov.selfVolume = 0.0;
1360 ov.sfp = 1.0;
1361 ov.gamma1i = gammas[iat];
1362 }
1363 }
1364
1365
1366
1367
1368
1369
1370 void rescanGammaR(int slot) {
1371
1372
1373 GaussianOverlap go = overlaps.get(slot);
1374
1375
1376 int parentIndex = go.parentIndex;
1377 if (parentIndex > 0) {
1378 int atom = go.atom;
1379 GaussianOverlap parent = overlaps.get(parentIndex);
1380 go.gamma1i = parent.gamma1i + overlaps.get(atom + 1).gamma1i;
1381 }
1382
1383
1384 for (int slotChild = go.childrenStartIndex;
1385 slotChild < go.childrenStartIndex + go.childrenCount;
1386 slotChild++) {
1387 rescanGammaR(slotChild);
1388 }
1389 }
1390
1391
1392
1393
1394
1395
1396 void rescanTreeG(double[] gammas) {
1397
1398 int slot = 0;
1399 GaussianOverlap ov = overlaps.get(slot);
1400 ov.gamma1i = 0.;
1401
1402 slot = 1;
1403 for (int iat = 0; iat < nAtoms; iat++, slot++) {
1404 ov = overlaps.get(slot);
1405 ov.gamma1i = gammas[iat];
1406 }
1407
1408 rescanGammaR(0);
1409 }
1410
1411
1412
1413
1414 void printTree() {
1415
1416
1417 for (int i = 1; i <= nAtoms; i++) {
1418 printTreeR(i);
1419 }
1420 }
1421
1422
1423
1424
1425
1426
1427 void printTreeR(int slot) {
1428 GaussianOverlap ov = overlaps.get(slot);
1429 logger.info(format("tg: %d ", slot));
1430 ov.printOverlap();
1431 for (int i = ov.childrenStartIndex; i < ov.childrenStartIndex + ov.childrenCount; i++) {
1432 printTreeR(i);
1433 }
1434 }
1435
1436
1437
1438
1439
1440
1441
1442 int nChildrenUnderSlotR(int slot) {
1443 int n = 0;
1444 if (overlaps.get(slot).childrenCount > 0) {
1445 n += overlaps.get(slot).childrenCount;
1446
1447 for (int i = 0; i < overlaps.get(slot).childrenCount; i++) {
1448 n += nChildrenUnderSlotR(overlaps.get(slot).childrenStartIndex + i);
1449 }
1450 }
1451 return n;
1452 }
1453 }
1454
1455
1456
1457
1458 private class GaussVolRegion extends ParallelRegion {
1459
1460 private final InitGaussVol[] initGaussVol;
1461 private final GaussianOverlapTree[] localTree;
1462 private final ComputeTreeLoop[] computeTreeLoops;
1463 private final ComputeVolumeLoop[] computeVolumeLoops;
1464 private final RescanTreeLoop[] rescanTreeLoops;
1465 private final ReductionLoop[] reductionLoops;
1466 private GAUSSVOL_MODE mode = GAUSSVOL_MODE.COMPUTE_TREE;
1467 private double[][] coordinates = null;
1468
1469
1470
1471
1472
1473
1474 public GaussVolRegion(int nThreads) {
1475 initGaussVol = new InitGaussVol[nThreads];
1476 localTree = new GaussianOverlapTree[nThreads];
1477 computeTreeLoops = new ComputeTreeLoop[nThreads];
1478 computeVolumeLoops = new ComputeVolumeLoop[nThreads];
1479 rescanTreeLoops = new RescanTreeLoop[nThreads];
1480 reductionLoops = new ReductionLoop[nThreads];
1481 }
1482
1483 public void init(GAUSSVOL_MODE mode, double[][] coordinates) {
1484 this.mode = mode;
1485 this.coordinates = coordinates;
1486
1487
1488 totalVolume.set(0.0);
1489 energy.set(0.0);
1490 grad.reset(parallelTeam);
1491 gradV.reset(parallelTeam, 0, nAtoms - 1);
1492 freeVolume.reset(parallelTeam, 0, nAtoms - 1);
1493 selfVolume.reset(parallelTeam, 0, nAtoms - 1);
1494 }
1495
1496 @Override
1497 public void run() throws Exception {
1498 int threadIndex = getThreadIndex();
1499 if (computeTreeLoops[threadIndex] == null) {
1500 initGaussVol[threadIndex] = new InitGaussVol();
1501 localTree[threadIndex] = new GaussianOverlapTree(nAtoms);
1502 computeTreeLoops[threadIndex] = new ComputeTreeLoop();
1503 computeVolumeLoops[threadIndex] = new ComputeVolumeLoop();
1504 rescanTreeLoops[threadIndex] = new RescanTreeLoop();
1505 reductionLoops[threadIndex] = new ReductionLoop();
1506 }
1507 try {
1508 if (mode == GAUSSVOL_MODE.COMPUTE_TREE) {
1509 execute(0, nAtoms - 1, initGaussVol[threadIndex]);
1510 execute(1, nAtoms, computeTreeLoops[threadIndex]);
1511 execute(1, nAtoms, computeVolumeLoops[threadIndex]);
1512 execute(0, nAtoms - 1, reductionLoops[threadIndex]);
1513 } else {
1514 execute(1, nAtoms, rescanTreeLoops[threadIndex]);
1515 execute(1, nAtoms, computeVolumeLoops[threadIndex]);
1516 execute(0, nAtoms - 1, reductionLoops[threadIndex]);
1517 }
1518 } catch (RuntimeException ex) {
1519 logger.warning("Runtime exception computing tree in thread: " + threadIndex);
1520 throw ex;
1521 } catch (Exception e) {
1522 String message = "Fatal exception computing tree in thread: " + threadIndex + "\n";
1523 logger.log(Level.SEVERE, message, e);
1524 }
1525 }
1526
1527 private class InitGaussVol extends IntegerForLoop {
1528
1529 @Override
1530 public void run(int first, int last) throws Exception {
1531 for (int i = first; i <= last; i++) {
1532
1533 updateAtom(i);
1534 }
1535 }
1536 }
1537
1538
1539
1540
1541 private class ComputeTreeLoop extends IntegerForLoop {
1542
1543 @Override
1544 public void run(int first, int last) throws Exception {
1545
1546 int threadIndex = getThreadIndex();
1547 for (int slot = first; slot <= last; slot++) {
1548 localTree[threadIndex].computeAndAddChildrenR(slot);
1549 }
1550 }
1551
1552 @Override
1553 public void start() {
1554 localTree[getThreadIndex()].initOverlapTree(
1555 coordinates, radii, volumes, gammas, ishydrogen);
1556 }
1557 }
1558
1559
1560
1561
1562 private class ComputeVolumeLoop extends IntegerForLoop {
1563
1564 @Override
1565 public void run(int first, int last) throws Exception {
1566 int threadIndex = getThreadIndex();
1567 for (int slot = first; slot <= last; slot++) {
1568 double[] psi1i = new double[1];
1569 double[] f1i = new double[1];
1570 double[] p1i = new double[3];
1571 double[] psip1i = new double[1];
1572 double[] fp1i = new double[1];
1573 double[] pp1i = new double[3];
1574 double[] energy1i = new double[1];
1575 double[] fenergy1i = new double[1];
1576 double[] penergy1i = new double[3];
1577 localTree[threadIndex].computeVolumeUnderSlot2R(
1578 slot,
1579 psi1i,
1580 f1i,
1581 p1i,
1582 psip1i,
1583 fp1i,
1584 pp1i,
1585 energy1i,
1586 fenergy1i,
1587 penergy1i,
1588 threadIndex,
1589 grad,
1590 gradV,
1591 freeVolume,
1592 selfVolume);
1593 totalVolume.addAndGet(psi1i[0]);
1594 energy.addAndGet(energy1i[0]);
1595 }
1596 }
1597 }
1598
1599
1600
1601
1602 private class RescanTreeLoop extends IntegerForLoop {
1603
1604 @Override
1605 public void run(int first, int last) throws Exception {
1606
1607 int threadIndex = getThreadIndex();
1608 for (int slot = first; slot <= last; slot++) {
1609 localTree[threadIndex].rescanR(slot);
1610 }
1611 }
1612
1613 @Override
1614 public void start() {
1615 localTree[getThreadIndex()].initRescanTreeV(
1616 coordinates, radii, volumes, gammas, ishydrogen);
1617 }
1618 }
1619
1620
1621
1622
1623 private class ReductionLoop extends IntegerForLoop {
1624
1625 int threadID;
1626
1627 @Override
1628 public void run(int lb, int ub) {
1629 grad.reduce(lb, ub);
1630 gradV.reduce(lb, ub);
1631 freeVolume.reduce(lb, ub);
1632 selfVolume.reduce(lb, ub);
1633 for (int i = lb; i <= ub; i++) {
1634 if (volumes[i] > 0) {
1635 gradV.set(0, i, gradV.get(i) / volumes[i]);
1636 }
1637 }
1638 }
1639
1640 @Override
1641 public void start() {
1642 threadID = getThreadIndex();
1643 }
1644 }
1645 }
1646 }