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