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.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   * RealSpaceData class.
71   *
72   * @author Timothy D. Fenn
73   * @since 1.0
74   */
75  public class RealSpaceData implements DataContainer {
76  
77    private static final Logger logger = Logger.getLogger(RealSpaceData.class.getName());
78    /**
79     * Parallel Team instance.
80     */
81    private final ParallelTeam parallelTeam;
82    /**
83     * Calculate the real space target in parallel.
84     */
85    private final RealSpaceRegion realSpaceRegion;
86    /**
87     * The real space data files.
88     */
89    private final RealSpaceFile[] realSpaceFile;
90    /**
91     * The number of real space data sources to consider.
92     */
93    private final int nRealSpaceData;
94    /**
95     * The refinement model.
96     */
97    private final RefinementModel refinementModel;
98    /**
99     * A flag to indicate if the lambda term is active.
100    */
101   private final boolean lambdaTerm;
102   /**
103    * The collection of MolecularAssembly instances that model the real space data.
104    */
105   private final MolecularAssembly[] molecularAssemblies;
106   /**
107    * The collection of crystals that describe the PBC and symmetry of each data source.
108    */
109   private Crystal[] crystal;
110   /**
111    * The real space refinement data for each data source.
112    */
113   private RealSpaceRefinementData[] refinementData;
114   /**
115    * The total real space energy.
116    */
117   private double realSpaceEnergy = 0.0;
118   /**
119    * The partial derivative of the real space energy with respect to lambda.
120    */
121   private double realSpacedUdL = 0.0;
122   /**
123    * The real space gradient.
124    */
125   private double[] realSpaceGradient;
126   /**
127    * The partial derivative of the real space gradient with respect to lambda.
128    */
129   private double[] realSpacedUdXdL;
130   /**
131    * The weight of the data relative to the weight of the force field.
132    */
133   private double xweight;
134   /**
135    * The current value of the state variable lambda.
136    */
137   private double lambda = 1.0;
138 
139   /**
140    * Construct a real space data molecularAssemblies, assumes a real space map with a weight of 1.0
141    * using the same name as the molecular molecularAssemblies.
142    *
143    * @param molecularAssembly {@link ffx.potential.MolecularAssembly molecular molecularAssemblies}
144    *                          object, used as the atomic model for comparison against the data
145    * @param properties        system properties file
146    * @param parallelTeam      a {@link edu.rit.pj.ParallelTeam} object.
147    * @param diffractionData   {@link ffx.xray.DiffractionData diffraction data}
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    * Construct a real space data molecularAssemblies, assumes a real space map with a weight of 1.0
159    * using the same name as the molecularAssemblies.
160    *
161    * @param molecularAssemblies {@link ffx.potential.MolecularAssembly molecular
162    *                            molecularAssemblies} object, used as the atomic model for comparison against the data
163    * @param properties          system properties file
164    * @param parallelTeam        a {@link edu.rit.pj.ParallelTeam} object.
165    * @param diffractionData     {@link ffx.xray.DiffractionData diffraction data}
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     // get Fo-Fc difference density
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     // Initialize the refinement model.
222     refinementModel = new RefinementModel(molecularAssemblies);
223 
224     // Initialize the RealSpaceRegion.
225     int nAtoms = refinementModel.getTotalAtomArray().length;
226     realSpaceRegion =
227         new RealSpaceRegion(parallelTeam.getThreadCount(), nAtoms, refinementData.length);
228   }
229 
230   /**
231    * Construct a real space data molecularAssemblies, assumes a real space map with a weight of 1.0
232    * using the same name as the molecular molecularAssemblies.
233    *
234    * @param assembly     {@link ffx.potential.MolecularAssembly molecular molecularAssemblies} object,
235    *                     used as the atomic model for comparison against the data
236    * @param properties   system properties file
237    * @param parallelTeam a {@link edu.rit.pj.ParallelTeam} object.
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    * Construct a real space data molecularAssemblies.
246    *
247    * @param assembly     {@link ffx.potential.MolecularAssembly molecular molecularAssemblies} object,
248    *                     used as the atomic model for comparison against the data
249    * @param properties   system properties file
250    * @param parallelTeam a {@link edu.rit.pj.ParallelTeam} object.
251    * @param datafile     one or more {@link ffx.realspace.parsers.RealSpaceFile} to be refined against
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    * Construct a real space data molecularAssemblies.
263    *
264    * @param molecularAssemblies {@link ffx.potential.MolecularAssembly molecular
265    *                            molecularAssemblies} object array (typically containing alternate conformer assemblies),
266    *                            used as the atomic model for comparison against the data
267    * @param properties          system properties file
268    * @param parallelTeam        a {@link edu.rit.pj.ParallelTeam} object.
269    * @param dataFile            one or more {@link ffx.realspace.parsers.RealSpaceFile} to be refined against
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     // now set up the refinement model
318     refinementModel = new RefinementModel(molecularAssemblies);
319 
320     // Initialize the RealSpaceRegion.
321     int nAtoms = refinementModel.getTotalAtomArray().length;
322     realSpaceRegion =
323         new RealSpaceRegion(parallelTeam.getThreadCount(), nAtoms, refinementData.length);
324   }
325 
326   /**
327    * Similar to Potential.destroy(), frees up resources associated with this RealSpaceData.
328    *
329    * @return If assets successfully freed.
330    */
331   public boolean destroy() {
332     try {
333       boolean underlyingShutdown = true;
334       for (MolecularAssembly assembly : molecularAssemblies) {
335         // Continue trying to shut assemblies down even if one fails to shut down.
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    * {@inheritDoc}
350    */
351   @Override
352   public Atom[] getActiveAtomArray() {
353     return refinementModel.getActiveAtomArray();
354   }
355 
356   /**
357    * {@inheritDoc}
358    */
359   @Override
360   public List<List<Molecule>> getAltMolecules() {
361     return refinementModel.getAltMolecules();
362   }
363 
364   /**
365    * {@inheritDoc}
366    */
367   @Override
368   public List<List<Residue>> getAltResidues() {
369     return refinementModel.getAltResidues();
370   }
371 
372   /**
373    * {@inheritDoc}
374    */
375   @Override
376   public Atom[] getAtomArray() {
377     return refinementModel.getTotalAtomArray();
378   }
379 
380   /**
381    * Getter for the field <code>crystal</code>.
382    *
383    * @return the crystal
384    */
385   public Crystal[] getCrystal() {
386     return crystal;
387   }
388 
389   /**
390    * Setter for the field <code>crystal</code>.
391    *
392    * @param crystal the crystal to set
393    */
394   public void setCrystal(Crystal[] crystal) {
395     this.crystal = crystal;
396   }
397 
398   /**
399    * Getter for the field <code>lambda</code>.
400    *
401    * @return the lambda
402    */
403   public double getLambda() {
404     return lambda;
405   }
406 
407   /**
408    * Set the current value of the state variable.
409    *
410    * @param lambda a double.
411    */
412   public void setLambda(double lambda) {
413     this.lambda = lambda;
414   }
415 
416   /**
417    * {@inheritDoc}
418    */
419   @Override
420   public MolecularAssembly[] getMolecularAssemblies() {
421     return molecularAssemblies;
422   }
423 
424   /**
425    * Getter for the field <code>realSpaceGradient</code>.
426    *
427    * @param gradient the gradient array.
428    * @return the real space gradient added to the input gradient array, or a new array if the
429    * gradient is null or too small.
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    * Getter for the field <code>refinementData</code>.
451    *
452    * @return the refinementData
453    */
454   public RealSpaceRefinementData[] getRefinementData() {
455     return refinementData;
456   }
457 
458   /**
459    * Setter for the field <code>refinementData</code>.
460    *
461    * @param refinementData the refinementData to set
462    */
463   public void setRefinementData(RealSpaceRefinementData[] refinementData) {
464     this.refinementData = refinementData;
465   }
466 
467   /**
468    * {@inheritDoc}
469    */
470   @Override
471   public RefinementModel getRefinementModel() {
472     return refinementModel;
473   }
474 
475   /**
476    * {@inheritDoc}
477    */
478   @Override
479   public double getWeight() {
480     return xweight;
481   }
482 
483   /**
484    * {@inheritDoc}
485    */
486   @Override
487   public void setWeight(double weight) {
488     this.xweight = weight;
489   }
490 
491   /**
492    * {@inheritDoc}
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    * {@inheritDoc}
511    */
512   @Override
513   public String printOptimizationHeader() {
514     return "Density score";
515   }
516 
517   /**
518    * {@inheritDoc}
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    * compute real space target value, also fills in atomic derivatives
531    *
532    * @return target value sum over all data sets.
533    */
534   double computeRealSpaceTarget() {
535 
536     long time = -System.nanoTime();
537     // Zero out the realSpaceTarget energy.
538     realSpaceEnergy = 0.0;
539     // Zero out the realSpacedUdL energy.
540     realSpacedUdL = 0.0;
541     // Initialize gradient to zero; allocate space if necessary.
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     // Initialize dUdXdL to zero; allocate space if necessary.
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    * getdEdL.
581    *
582    * @return a double.
583    */
584   double getdEdL() {
585     return realSpacedUdL;
586   }
587 
588   /**
589    * getdEdXdL.
590    *
591    * @param gradient an array of {@link double} objects.
592    * @return an array of {@link double} objects.
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    * Getter for the field <code>realSpaceFile</code>.
615    *
616    * @return the realSpaceFile
617    */
618   private RealSpaceFile[] getRealSpaceFile() {
619     return realSpaceFile;
620   }
621 
622   /**
623    * Getter for the field <code>nRealSpaceData</code>.
624    *
625    * @return the nRealSpaceData
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       // Load final values.
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      * Initialize gradient and lambda gradient arrays.
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           // Define the extent of this real space data sources.
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             // Only include atoms in the target function that have
771             // their use flag set to true and are Active.
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             // Logic to find atom in 3d scalar field box.
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             // Fill in scalar 4x4 array for interpolation.
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             // Scale and interpolate.
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               // transpose of toFractional
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 }