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.realspace;
39
40 import edu.rit.pj.IntegerForLoop;
41 import edu.rit.pj.ParallelRegion;
42 import edu.rit.pj.ParallelTeam;
43 import edu.rit.pj.reduction.SharedDouble;
44 import ffx.crystal.Crystal;
45 import ffx.numerics.atomic.AtomicDoubleArray.AtomicDoubleArrayImpl;
46 import ffx.numerics.atomic.AtomicDoubleArray3D;
47 import ffx.numerics.spline.TriCubicSpline;
48 import ffx.potential.MolecularAssembly;
49 import ffx.potential.Utilities;
50 import ffx.potential.bonded.Atom;
51 import ffx.realspace.parsers.RealSpaceFile;
52 import ffx.xray.DataContainer;
53 import ffx.xray.DiffractionData;
54 import ffx.xray.refine.RefinementMode;
55 import ffx.xray.refine.RefinementModel;
56 import org.apache.commons.configuration2.CompositeConfiguration;
57
58 import java.util.logging.Level;
59 import java.util.logging.Logger;
60
61 import static ffx.numerics.math.ScalarMath.mod;
62 import static java.lang.String.format;
63 import static java.util.Arrays.fill;
64 import static org.apache.commons.math3.util.FastMath.floor;
65
66
67
68
69
70
71
72 public class RealSpaceData implements DataContainer {
73
74 private static final Logger logger = Logger.getLogger(RealSpaceData.class.getName());
75
76
77
78 private final ParallelTeam parallelTeam;
79
80
81
82 private final RealSpaceRegion realSpaceRegion;
83
84
85
86 private final RealSpaceFile[] realSpaceFile;
87
88
89
90 private final int nRealSpaceData;
91
92
93
94 private final RefinementModel refinementModel;
95
96
97
98 private final boolean lambdaTerm;
99
100
101
102 private final MolecularAssembly[] molecularAssemblies;
103
104
105
106 private Crystal[] crystal;
107
108
109
110 private RealSpaceRefinementData[] refinementData;
111
112
113
114 private double realSpaceEnergy = 0.0;
115
116
117
118 private double realSpacedUdL = 0.0;
119
120
121
122 private double[] realSpaceGradient;
123
124
125
126 private double[] realSpacedUdXdL;
127
128
129
130 private double xweight;
131
132
133
134 private double lambda = 1.0;
135
136
137
138
139
140
141
142
143
144
145
146 public RealSpaceData(
147 MolecularAssembly molecularAssembly,
148 CompositeConfiguration properties,
149 ParallelTeam parallelTeam,
150 DiffractionData diffractionData) {
151 this(new MolecularAssembly[]{molecularAssembly}, properties, parallelTeam, diffractionData);
152 }
153
154
155
156
157
158
159
160
161
162
163
164 public RealSpaceData(
165 MolecularAssembly[] molecularAssemblies,
166 CompositeConfiguration properties,
167 ParallelTeam parallelTeam,
168 DiffractionData diffractionData) {
169 this.molecularAssemblies = molecularAssemblies;
170 this.parallelTeam = parallelTeam;
171 this.realSpaceFile = null;
172 this.nRealSpaceData = 1;
173 crystal = new Crystal[nRealSpaceData];
174 crystal[0] = diffractionData.getCrystal()[0];
175 refinementData = new RealSpaceRefinementData[nRealSpaceData];
176 refinementData[0] = new RealSpaceRefinementData();
177 refinementData[0].setPeriodic(true);
178
179 xweight = properties.getDouble("xweight", 1.0);
180 lambdaTerm = properties.getBoolean("lambdaterm", false);
181
182 if (logger.isLoggable(Level.INFO)) {
183 StringBuilder sb = new StringBuilder(" Real Space Refinement Settings\n");
184 sb.append(format(" Refinement weight (xweight): %5.3f", xweight));
185 logger.info(sb.toString());
186 }
187
188 if (!diffractionData.getScaled()[0]) {
189 diffractionData.scaleBulkFit();
190 diffractionData.printStats();
191 }
192
193
194 diffractionData.getCrystalReciprocalSpacesFc()[0].computeAtomicGradients(
195 diffractionData.getRefinementData()[0].foFc1,
196 diffractionData.getRefinementData()[0].freeR,
197 diffractionData.getRefinementData()[0].rFreeFlag,
198 RefinementMode.COORDINATES);
199
200 refinementData[0].setOrigin(0, 0, 0);
201 int extx = (int) diffractionData.getCrystalReciprocalSpacesFc()[0].getXDim();
202 int exty = (int) diffractionData.getCrystalReciprocalSpacesFc()[0].getYDim();
203 int extz = (int) diffractionData.getCrystalReciprocalSpacesFc()[0].getZDim();
204 refinementData[0].setExtent(extx, exty, extz);
205 refinementData[0].setNI(extx, exty, extz);
206 refinementData[0].setData(new double[extx * exty * extz]);
207 for (int k = 0; k < extz; k++) {
208 for (int j = 0; j < exty; j++) {
209 for (int i = 0; i < extx; i++) {
210 int index1 = (i + extx * (j + exty * k));
211 int index2 = 2 * index1;
212 refinementData[0].getData()[index1] =
213 diffractionData.getCrystalReciprocalSpacesFc()[0].getDensityGrid()[index2];
214 }
215 }
216 }
217
218
219 refinementModel = new RefinementModel(molecularAssemblies);
220
221
222 int nAtoms = refinementModel.getScatteringAtoms().length;
223 realSpaceRegion =
224 new RealSpaceRegion(parallelTeam.getThreadCount(), nAtoms, refinementData.length);
225 }
226
227
228
229
230
231
232
233
234
235
236 public RealSpaceData(
237 MolecularAssembly assembly, CompositeConfiguration properties, ParallelTeam parallelTeam) {
238 this(new MolecularAssembly[]{assembly}, properties, parallelTeam, new RealSpaceFile(assembly));
239 }
240
241
242
243
244
245
246
247
248
249
250 public RealSpaceData(
251 MolecularAssembly assembly,
252 CompositeConfiguration properties,
253 ParallelTeam parallelTeam,
254 RealSpaceFile... datafile) {
255 this(new MolecularAssembly[]{assembly}, properties, parallelTeam, datafile);
256 }
257
258
259
260
261
262
263
264
265
266
267
268 public RealSpaceData(
269 MolecularAssembly[] molecularAssemblies,
270 CompositeConfiguration properties,
271 ParallelTeam parallelTeam,
272 RealSpaceFile... dataFile) {
273
274 this.molecularAssemblies = molecularAssemblies;
275 this.parallelTeam = parallelTeam;
276 this.realSpaceFile = dataFile;
277 this.nRealSpaceData = dataFile.length;
278 crystal = new Crystal[nRealSpaceData];
279 refinementData = new RealSpaceRefinementData[nRealSpaceData];
280
281 xweight = properties.getDouble("xweight", 1.0);
282 lambdaTerm = properties.getBoolean("lambdaterm", false);
283
284 for (int i = 0; i < nRealSpaceData; i++) {
285 crystal[i] =
286 dataFile[i].getRealSpaceFileFilter().getCrystal(dataFile[i].getFilename(), properties);
287 if (crystal[i] == null) {
288 logger.severe(" CCP4 map file does not contain full crystal information!");
289 }
290 }
291
292 for (int i = 0; i < nRealSpaceData; i++) {
293 refinementData[i] = new RealSpaceRefinementData();
294 dataFile[i]
295 .getRealSpaceFileFilter()
296 .readFile(dataFile[i].getFilename(), refinementData[i], properties);
297
298 if (refinementData[i].getOrigin()[0] == 0
299 && refinementData[i].getOrigin()[1] == 0
300 && refinementData[i].getOrigin()[2] == 0
301 && refinementData[i].getExtent()[0] == refinementData[i].getNi()[0]
302 && refinementData[i].getExtent()[1] == refinementData[i].getNi()[1]
303 && refinementData[i].getExtent()[2] == refinementData[i].getNi()[2]) {
304 refinementData[i].setPeriodic(true);
305 }
306 }
307
308 if (logger.isLoggable(Level.INFO)) {
309 StringBuilder sb = new StringBuilder(" Real Space Refinement Settings\n");
310 sb.append(format(" Refinement weight (xweight): %5.3f", xweight));
311 logger.info(sb.toString());
312 }
313
314
315 refinementModel = new RefinementModel(RefinementMode.COORDINATES, molecularAssemblies);
316
317
318 int nAtoms = refinementModel.getScatteringAtoms().length;
319 realSpaceRegion =
320 new RealSpaceRegion(parallelTeam.getThreadCount(), nAtoms, refinementData.length);
321 }
322
323
324
325
326
327
328 public boolean destroy() {
329 try {
330 boolean underlyingShutdown = true;
331 for (MolecularAssembly assembly : molecularAssemblies) {
332
333 boolean thisShutdown = assembly.destroy();
334 underlyingShutdown = underlyingShutdown && thisShutdown;
335 }
336 parallelTeam.shutdown();
337 return underlyingShutdown;
338 } catch (Exception ex) {
339 logger.warning(format(" Exception in shutting down a RealSpaceData: %s", ex));
340 logger.info(Utilities.stackTraceToString(ex));
341 return false;
342 }
343 }
344
345
346
347
348
349
350 public Crystal[] getCrystal() {
351 return crystal;
352 }
353
354
355
356
357
358
359 public void setCrystal(Crystal[] crystal) {
360 this.crystal = crystal;
361 }
362
363
364
365
366
367
368 public double getLambda() {
369 return lambda;
370 }
371
372
373
374
375
376
377 public void setLambda(double lambda) {
378 this.lambda = lambda;
379 }
380
381
382
383
384
385
386
387
388 public double[] getRealSpaceGradient(double[] gradient) {
389 int nAtoms = refinementModel.getScatteringAtoms().length;
390 int nActiveAtoms = 0;
391 for (int i = 0; i < nAtoms; i++) {
392 if (refinementModel.getScatteringAtoms()[i].isActive()) {
393 nActiveAtoms++;
394 }
395 }
396
397 if (gradient == null || gradient.length < nActiveAtoms * 3) {
398 gradient = new double[nActiveAtoms * 3];
399 }
400 for (int i = 0; i < nActiveAtoms * 3; i++) {
401 gradient[i] += realSpaceGradient[i];
402 }
403 return gradient;
404 }
405
406
407
408
409
410
411 public RealSpaceRefinementData[] getRefinementData() {
412 return refinementData;
413 }
414
415
416
417
418
419
420 public void setRefinementData(RealSpaceRefinementData[] refinementData) {
421 this.refinementData = refinementData;
422 }
423
424
425
426
427 @Override
428 public RefinementModel getRefinementModel() {
429 return refinementModel;
430 }
431
432
433
434
435 @Override
436 public double getWeight() {
437 return xweight;
438 }
439
440
441
442
443 @Override
444 public void setWeight(double weight) {
445 this.xweight = weight;
446 }
447
448
449
450
451 @Override
452 public String printEnergyUpdate() {
453 StringBuilder sb = new StringBuilder();
454 for (int i = 0; i < nRealSpaceData; i++) {
455 sb.append(
456 format(
457 " dataset %d (weight: %5.1f): chemical energy: %8.2f density score: %8.2f\n",
458 i + 1,
459 realSpaceFile[i].getWeight(),
460 molecularAssemblies[0].getPotentialEnergy().getTotalEnergy(),
461 realSpaceFile[i].getWeight() * refinementData[i].getDensityScore()));
462 }
463 return sb.toString();
464 }
465
466
467
468
469 @Override
470 public String printOptimizationHeader() {
471 return "Density score";
472 }
473
474
475
476
477 @Override
478 public String printOptimizationUpdate() {
479 StringBuilder sb = new StringBuilder();
480 for (int i = 0; i < nRealSpaceData; i++) {
481 sb.append(format("%6.2f ", refinementData[i].getDensityScore()));
482 }
483 return sb.toString();
484 }
485
486
487
488
489
490
491 double computeRealSpaceTarget() {
492
493 long time = -System.nanoTime();
494
495 realSpaceEnergy = 0.0;
496
497 realSpacedUdL = 0.0;
498
499 int nActive = 0;
500 int nAtoms = refinementModel.getScatteringAtoms().length;
501 for (int i = 0; i < nAtoms; i++) {
502 if (refinementModel.getScatteringAtoms()[i].isActive()) {
503 nActive++;
504 }
505 }
506
507 int nGrad = nActive * 3;
508 if (realSpaceGradient == null || realSpaceGradient.length < nGrad) {
509 realSpaceGradient = new double[nGrad];
510 } else {
511 fill(realSpaceGradient, 0.0);
512 }
513
514
515 if (realSpacedUdXdL == null || realSpacedUdXdL.length < nGrad) {
516 realSpacedUdXdL = new double[nGrad];
517 } else {
518 fill(realSpacedUdXdL, 0.0);
519 }
520
521 try {
522 parallelTeam.execute(realSpaceRegion);
523 } catch (Exception e) {
524 String message = " Exception computing real space energy";
525 logger.log(Level.SEVERE, message, e);
526 }
527
528 time += System.nanoTime();
529 if (logger.isLoggable(Level.FINE)) {
530 logger.fine(format(" Real space energy time: %16.8f (sec).", time * 1.0e-9));
531 }
532
533 return realSpaceEnergy;
534 }
535
536
537
538
539
540
541 double getdEdL() {
542 return realSpacedUdL;
543 }
544
545
546
547
548
549
550
551 double[] getdEdXdL(double[] gradient) {
552 int nAtoms = refinementModel.getScatteringAtoms().length;
553 int nActiveAtoms = 0;
554 for (int i = 0; i < nAtoms; i++) {
555 if (refinementModel.getScatteringAtoms()[i].isActive()) {
556 nActiveAtoms++;
557 }
558 }
559
560 if (gradient == null || gradient.length < nActiveAtoms * 3) {
561 gradient = new double[nActiveAtoms * 3];
562 }
563
564 for (int i = 0; i < nActiveAtoms * 3; i++) {
565 gradient[i] += realSpacedUdXdL[i];
566 }
567 return gradient;
568 }
569
570
571
572
573
574
575 private RealSpaceFile[] getRealSpaceFile() {
576 return realSpaceFile;
577 }
578
579
580
581
582
583
584 private int getnRealSpaceData() {
585 return nRealSpaceData;
586 }
587
588 private class RealSpaceRegion extends ParallelRegion {
589
590 private final AtomicDoubleArray3D gradient;
591 private final AtomicDoubleArray3D lambdaGrad;
592 private final InitializationLoop[] initializationLoops;
593 private final RealSpaceLoop[] realSpaceLoops;
594 private final SharedDouble[] sharedTarget;
595 private final SharedDouble shareddUdL;
596 private final int nAtoms;
597 private final int nData;
598
599 RealSpaceRegion(int nThreads, int nAtoms, int nData) {
600 this.nAtoms = nAtoms;
601 this.nData = nData;
602 initializationLoops = new InitializationLoop[nThreads];
603 realSpaceLoops = new RealSpaceLoop[nThreads];
604 sharedTarget = new SharedDouble[nData];
605 for (int i = 0; i < nData; i++) {
606 sharedTarget[i] = new SharedDouble();
607 }
608 shareddUdL = new SharedDouble();
609 gradient = new AtomicDoubleArray3D(AtomicDoubleArrayImpl.MULTI, nThreads, nAtoms);
610 lambdaGrad = new AtomicDoubleArray3D(AtomicDoubleArrayImpl.MULTI, nThreads, nAtoms);
611 }
612
613 @Override
614 public void finish() {
615
616 realSpaceEnergy = 0;
617 for (int i = 0; i < nData; i++) {
618 refinementData[i].setDensityScore(sharedTarget[i].get());
619 realSpaceEnergy += getRefinementData()[i].getDensityScore();
620 }
621 realSpacedUdL = shareddUdL.get();
622 int index = 0;
623 for (int i = 0; i < nAtoms; i++) {
624 Atom atom = refinementModel.getScatteringAtoms()[i];
625 if (atom.isActive()) {
626 int ii = index * 3;
627 double gx = gradient.getX(i);
628 double gy = gradient.getY(i);
629 double gz = gradient.getZ(i);
630 realSpaceGradient[ii] = gx;
631 realSpaceGradient[ii + 1] = gy;
632 realSpaceGradient[ii + 2] = gz;
633 atom.setXYZGradient(gx, gy, gz);
634 gx = lambdaGrad.getX(i);
635 gy = lambdaGrad.getY(i);
636 gz = lambdaGrad.getZ(i);
637 realSpacedUdXdL[ii] = gx;
638 realSpacedUdXdL[ii + 1] = gy;
639 realSpacedUdXdL[ii + 2] = gz;
640 atom.setLambdaXYZGradient(gx, gy, gz);
641 index++;
642 }
643 }
644 }
645
646 @Override
647 public void run() throws Exception {
648 int threadID = getThreadIndex();
649
650 if (initializationLoops[threadID] == null) {
651 initializationLoops[threadID] = new InitializationLoop();
652 }
653 execute(0, nAtoms - 1, initializationLoops[threadID]);
654
655 if (realSpaceLoops[threadID] == null) {
656 realSpaceLoops[threadID] = new RealSpaceLoop();
657 }
658 execute(0, nAtoms - 1, realSpaceLoops[threadID]);
659 }
660
661 @Override
662 public void start() {
663 for (int i = 0; i < nData; i++) {
664 sharedTarget[i].set(0.0);
665 }
666 shareddUdL.set(0.0);
667 gradient.alloc(nAtoms);
668 lambdaGrad.alloc(nAtoms);
669 }
670
671
672
673
674 private class InitializationLoop extends IntegerForLoop {
675
676 @Override
677 public void run(int lb, int ub) {
678 int threadID = getThreadIndex();
679 gradient.reset(threadID, lb, ub);
680 lambdaGrad.reset(threadID, lb, ub);
681 for (int i = lb; i <= ub; i++) {
682 Atom a = refinementModel.getScatteringAtoms()[i];
683 a.setXYZGradient(0.0, 0.0, 0.0);
684 a.setLambdaXYZGradient(0.0, 0.0, 0.0);
685 }
686 }
687 }
688
689 private class RealSpaceLoop extends IntegerForLoop {
690
691 double[] target = new double[nData];
692 double localdUdL;
693
694 @Override
695 public void finish() {
696 for (int i = 0; i < nData; i++) {
697 sharedTarget[i].addAndGet(target[i]);
698 }
699 shareddUdL.addAndGet(localdUdL);
700 }
701
702 @Override
703 public void run(int first, int last) throws Exception {
704
705 int threadID = getThreadIndex();
706 double[] xyz = new double[3];
707 double[] uvw = new double[3];
708 double[] grad = new double[3];
709 double[][][] scalar = new double[4][4][4];
710 TriCubicSpline spline = new TriCubicSpline();
711
712 for (int i = 0; i < getnRealSpaceData(); i++) {
713
714
715 int extX = getRefinementData()[i].getExtent()[0];
716 int extY = getRefinementData()[i].getExtent()[1];
717 int extZ = getRefinementData()[i].getExtent()[2];
718 int nX = getRefinementData()[i].getNi()[0];
719 int nY = getRefinementData()[i].getNi()[1];
720 int nZ = getRefinementData()[i].getNi()[2];
721 int originX = getRefinementData()[i].getOrigin()[0];
722 int originY = getRefinementData()[i].getOrigin()[1];
723 int originZ = getRefinementData()[i].getOrigin()[2];
724
725 for (int ia = first; ia <= last; ia++) {
726 Atom a = refinementModel.getScatteringAtoms()[ia];
727
728
729 if (!a.getUse()) {
730 continue;
731 }
732
733 double lambdai = 1.0;
734 double dUdL = 0.0;
735 if (lambdaTerm && a.applyLambda()) {
736 lambdai = getLambda();
737 dUdL = 1.0;
738 }
739 a.getXYZ(xyz);
740 getCrystal()[i].toFractionalCoordinates(xyz, uvw);
741
742
743 final double frx = nX * uvw[0];
744 final int ifrx = ((int) floor(frx)) - originX;
745 final double dfrx = frx - floor(frx);
746
747 final double fry = nY * uvw[1];
748 final int ifry = ((int) floor(fry)) - originY;
749 final double dfry = fry - floor(fry);
750
751 final double frz = nZ * uvw[2];
752 final int ifrz = ((int) floor(frz)) - originZ;
753 final double dfrz = frz - floor(frz);
754
755 if (!refinementData[i].isPeriodic()) {
756 if (ifrx - 1 < 0
757 || ifrx + 2 > extX
758 || ifry - 1 < 0
759 || ifry + 2 > extY
760 || ifrz - 1 < 0
761 || ifrz + 2 > extZ) {
762 String message = format(" Atom %s is outside the density will be ignored.", a);
763 logger.warning(message);
764 continue;
765 }
766 }
767
768
769 if (getRefinementData()[i].isPeriodic()) {
770 for (int ui = ifrx - 1; ui < ifrx + 3; ui++) {
771 int uii = ui - (ifrx - 1);
772 int pui = mod(ui, extX);
773 for (int vi = ifry - 1; vi < ifry + 3; vi++) {
774 int vii = vi - (ifry - 1);
775 int pvi = mod(vi, extY);
776 for (int wi = ifrz - 1; wi < ifrz + 3; wi++) {
777 int wii = wi - (ifrz - 1);
778 int pwi = mod(wi, extZ);
779 scalar[uii][vii][wii] = getRefinementData()[i].getDataIndex(pui, pvi, pwi);
780 }
781 }
782 }
783 } else {
784 for (int ui = ifrx - 1; ui < ifrx + 3; ui++) {
785 int uii = ui - (ifrx - 1);
786 for (int vi = ifry - 1; vi < ifry + 3; vi++) {
787 int vii = vi - (ifry - 1);
788 for (int wi = ifrz - 1; wi < ifrz + 3; wi++) {
789 int wii = wi - (ifrz - 1);
790 scalar[uii][vii][wii] = getRefinementData()[i].getDataIndex(ui, vi, wi);
791 }
792 }
793 }
794 }
795
796
797 double scale;
798 double scaledUdL;
799 double atomicWeight = a.getAtomType().atomicWeight;
800 if (getRealSpaceFile() != null) {
801 atomicWeight *= getRealSpaceFile()[i].getWeight();
802 }
803 scale = -1.0 * lambdai * atomicWeight;
804 scaledUdL = -1.0 * dUdL * atomicWeight;
805
806 double val = spline.spline(dfrx, dfry, dfrz, scalar, grad);
807 target[i] += scale * val;
808 localdUdL += scaledUdL * val;
809
810 if (a.isActive()) {
811 grad[0] = grad[0] * nX;
812 grad[1] = grad[1] * nY;
813 grad[2] = grad[2] * nZ;
814
815 xyz[0] = grad[0] * getCrystal()[i].A00;
816 xyz[1] = grad[0] * getCrystal()[i].A10
817 + grad[1] * getCrystal()[i].A11;
818 xyz[2] = grad[0] * getCrystal()[i].A20
819 + grad[1] * getCrystal()[i].A21
820 + grad[2] * getCrystal()[i].A22;
821 gradient.add(threadID, ia, scale * xyz[0], scale * xyz[1], scale * xyz[2]);
822 lambdaGrad.add(threadID, ia, scaledUdL * xyz[0], scaledUdL * xyz[1], scaledUdL * xyz[2]);
823 }
824 }
825 }
826 gradient.reduce(first, last);
827 lambdaGrad.reduce(first, last);
828 }
829
830 @Override
831 public void start() {
832 for (int i = 0; i < nData; i++) {
833 target[i] = 0;
834 }
835 localdUdL = 0;
836 }
837 }
838 }
839 }