View Javadoc
1   // ******************************************************************************
2   //
3   // Title:       Force Field X.
4   // Description: Force Field X - Software for Molecular Biophysics.
5   // Copyright:   Copyright (c) Michael J. Schnieders 2001-2025.
6   //
7   // This file is part of Force Field X.
8   //
9   // Force Field X is free software; you can redistribute it and/or modify it
10  // under the terms of the GNU General Public License version 3 as published by
11  // the Free Software Foundation.
12  //
13  // Force Field X is distributed in the hope that it will be useful, but WITHOUT
14  // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15  // FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
16  // details.
17  //
18  // You should have received a copy of the GNU General Public License along with
19  // Force Field X; if not, write to the Free Software Foundation, Inc., 59 Temple
20  // Place, Suite 330, Boston, MA 02111-1307 USA
21  //
22  // Linking this library statically or dynamically with other modules is making a
23  // combined work based on this library. Thus, the terms and conditions of the
24  // GNU General Public License cover the whole combination.
25  //
26  // As a special exception, the copyright holders of this library give you
27  // permission to link this library with independent modules to produce an
28  // executable, regardless of the license terms of these independent modules, and
29  // to copy and distribute the resulting executable under terms of your choice,
30  // provided that you also meet, for each linked independent module, the terms
31  // and conditions of the license of that module. An independent module is a
32  // module which is not derived from or based on this library. If you modify this
33  // library, you may extend this exception to your version of the library, but
34  // you are not obligated to do so. If you do not wish to do so, delete this
35  // exception statement from your version.
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   * RealSpaceData class.
68   *
69   * @author Timothy D. Fenn
70   * @since 1.0
71   */
72  public class RealSpaceData implements DataContainer {
73  
74    private static final Logger logger = Logger.getLogger(RealSpaceData.class.getName());
75    /**
76     * Parallel Team instance.
77     */
78    private final ParallelTeam parallelTeam;
79    /**
80     * Calculate the real space target in parallel.
81     */
82    private final RealSpaceRegion realSpaceRegion;
83    /**
84     * The real space data files.
85     */
86    private final RealSpaceFile[] realSpaceFile;
87    /**
88     * The number of real space data sources to consider.
89     */
90    private final int nRealSpaceData;
91    /**
92     * The refinement model.
93     */
94    private final RefinementModel refinementModel;
95    /**
96     * A flag to indicate if the lambda term is active.
97     */
98    private final boolean lambdaTerm;
99    /**
100    * The collection of MolecularAssembly instances that model the real space data.
101    */
102   private final MolecularAssembly[] molecularAssemblies;
103   /**
104    * The collection of crystals that describe the PBC and symmetry of each data source.
105    */
106   private Crystal[] crystal;
107   /**
108    * The real space refinement data for each data source.
109    */
110   private RealSpaceRefinementData[] refinementData;
111   /**
112    * The total real space energy.
113    */
114   private double realSpaceEnergy = 0.0;
115   /**
116    * The partial derivative of the real space energy with respect to lambda.
117    */
118   private double realSpacedUdL = 0.0;
119   /**
120    * The real space gradient.
121    */
122   private double[] realSpaceGradient;
123   /**
124    * The partial derivative of the real space gradient with respect to lambda.
125    */
126   private double[] realSpacedUdXdL;
127   /**
128    * The weight of the data relative to the weight of the force field.
129    */
130   private double xweight;
131   /**
132    * The current value of the state variable lambda.
133    */
134   private double lambda = 1.0;
135 
136   /**
137    * Construct a real space data molecularAssemblies, assumes a real space map with a weight of 1.0
138    * using the same name as the molecular molecularAssemblies.
139    *
140    * @param molecularAssembly {@link ffx.potential.MolecularAssembly molecular molecularAssemblies}
141    *                          object, used as the atomic model for comparison against the data
142    * @param properties        system properties file
143    * @param parallelTeam      a {@link edu.rit.pj.ParallelTeam} object.
144    * @param diffractionData   {@link ffx.xray.DiffractionData diffraction data}
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    * Construct a real space data molecularAssemblies, assumes a real space map with a weight of 1.0
156    * using the same name as the molecularAssemblies.
157    *
158    * @param molecularAssemblies {@link ffx.potential.MolecularAssembly molecular
159    *                            molecularAssemblies} object, used as the atomic model for comparison against the data
160    * @param properties          system properties file
161    * @param parallelTeam        a {@link edu.rit.pj.ParallelTeam} object.
162    * @param diffractionData     {@link ffx.xray.DiffractionData diffraction data}
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     // get Fo-Fc difference density
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     // Initialize the refinement model.
219     refinementModel = new RefinementModel(molecularAssemblies);
220 
221     // Initialize the RealSpaceRegion.
222     int nAtoms = refinementModel.getScatteringAtoms().length;
223     realSpaceRegion =
224         new RealSpaceRegion(parallelTeam.getThreadCount(), nAtoms, refinementData.length);
225   }
226 
227   /**
228    * Construct a real space data molecularAssemblies, assumes a real space map with a weight of 1.0
229    * using the same name as the molecular molecularAssemblies.
230    *
231    * @param assembly     {@link ffx.potential.MolecularAssembly molecular molecularAssemblies} object,
232    *                     used as the atomic model for comparison against the data
233    * @param properties   system properties file
234    * @param parallelTeam a {@link edu.rit.pj.ParallelTeam} object.
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    * Construct a real space data molecularAssemblies.
243    *
244    * @param assembly     {@link ffx.potential.MolecularAssembly molecular molecularAssemblies} object,
245    *                     used as the atomic model for comparison against the data
246    * @param properties   system properties file
247    * @param parallelTeam a {@link edu.rit.pj.ParallelTeam} object.
248    * @param datafile     one or more {@link ffx.realspace.parsers.RealSpaceFile} to be refined against
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    * Construct a real space data molecularAssemblies.
260    *
261    * @param molecularAssemblies {@link ffx.potential.MolecularAssembly molecular
262    *                            molecularAssemblies} object array (typically containing alternate conformer assemblies),
263    *                            used as the atomic model for comparison against the data
264    * @param properties          system properties file
265    * @param parallelTeam        a {@link edu.rit.pj.ParallelTeam} object.
266    * @param dataFile            one or more {@link ffx.realspace.parsers.RealSpaceFile} to be refined against
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     // Set up the refinement model
315     refinementModel = new RefinementModel(RefinementMode.COORDINATES, molecularAssemblies);
316 
317     // Initialize the RealSpaceRegion.
318     int nAtoms = refinementModel.getScatteringAtoms().length;
319     realSpaceRegion =
320         new RealSpaceRegion(parallelTeam.getThreadCount(), nAtoms, refinementData.length);
321   }
322 
323   /**
324    * Similar to Potential.destroy(), frees up resources associated with this RealSpaceData.
325    *
326    * @return If assets successfully freed.
327    */
328   public boolean destroy() {
329     try {
330       boolean underlyingShutdown = true;
331       for (MolecularAssembly assembly : molecularAssemblies) {
332         // Continue trying to shut assemblies down even if one fails to shut down.
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    * Getter for the field <code>crystal</code>.
347    *
348    * @return the crystal
349    */
350   public Crystal[] getCrystal() {
351     return crystal;
352   }
353 
354   /**
355    * Setter for the field <code>crystal</code>.
356    *
357    * @param crystal the crystal to set
358    */
359   public void setCrystal(Crystal[] crystal) {
360     this.crystal = crystal;
361   }
362 
363   /**
364    * Getter for the field <code>lambda</code>.
365    *
366    * @return the lambda
367    */
368   public double getLambda() {
369     return lambda;
370   }
371 
372   /**
373    * Set the current value of the state variable.
374    *
375    * @param lambda a double.
376    */
377   public void setLambda(double lambda) {
378     this.lambda = lambda;
379   }
380 
381   /**
382    * Getter for the field <code>realSpaceGradient</code>.
383    *
384    * @param gradient the gradient array.
385    * @return the real space gradient added to the input gradient array, or a new array if the
386    * gradient is null or too small.
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    * Getter for the field <code>refinementData</code>.
408    *
409    * @return the refinementData
410    */
411   public RealSpaceRefinementData[] getRefinementData() {
412     return refinementData;
413   }
414 
415   /**
416    * Setter for the field <code>refinementData</code>.
417    *
418    * @param refinementData the refinementData to set
419    */
420   public void setRefinementData(RealSpaceRefinementData[] refinementData) {
421     this.refinementData = refinementData;
422   }
423 
424   /**
425    * {@inheritDoc}
426    */
427   @Override
428   public RefinementModel getRefinementModel() {
429     return refinementModel;
430   }
431 
432   /**
433    * {@inheritDoc}
434    */
435   @Override
436   public double getWeight() {
437     return xweight;
438   }
439 
440   /**
441    * {@inheritDoc}
442    */
443   @Override
444   public void setWeight(double weight) {
445     this.xweight = weight;
446   }
447 
448   /**
449    * {@inheritDoc}
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    * {@inheritDoc}
468    */
469   @Override
470   public String printOptimizationHeader() {
471     return "Density score";
472   }
473 
474   /**
475    * {@inheritDoc}
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    * compute real space target value, also fills in atomic derivatives
488    *
489    * @return target value sum over all data sets.
490    */
491   double computeRealSpaceTarget() {
492 
493     long time = -System.nanoTime();
494     // Zero out the realSpaceTarget energy.
495     realSpaceEnergy = 0.0;
496     // Zero out the realSpacedUdL energy.
497     realSpacedUdL = 0.0;
498     // Initialize gradient to zero; allocate space if necessary.
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     // Initialize dUdXdL to zero; allocate space if necessary.
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    * getdEdL.
538    *
539    * @return a double.
540    */
541   double getdEdL() {
542     return realSpacedUdL;
543   }
544 
545   /**
546    * getdEdXdL.
547    *
548    * @param gradient an array of {@link double} objects.
549    * @return an array of {@link double} objects.
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    * Getter for the field <code>realSpaceFile</code>.
572    *
573    * @return the realSpaceFile
574    */
575   private RealSpaceFile[] getRealSpaceFile() {
576     return realSpaceFile;
577   }
578 
579   /**
580    * Getter for the field <code>nRealSpaceData</code>.
581    *
582    * @return the nRealSpaceData
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       // Load final values.
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      * Initialize gradient and lambda gradient arrays.
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           // Define the extent of this real space data sources.
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             // Only include atoms in the target function that have
728             // their use flag set to true and are Active.
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             // Logic to find atom in 3d scalar field box.
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             // Fill in scalar 4x4 array for interpolation.
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             // Scale and interpolate.
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               // transpose of toFractional
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 }