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-2024.
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.numerics.atomic;
39  
40  import static ffx.numerics.atomic.AtomicDoubleArray.atomicDoubleArrayFactory;
41  import static java.lang.String.format;
42  
43  import edu.rit.pj.IntegerForLoop;
44  import edu.rit.pj.ParallelRegion;
45  import edu.rit.pj.ParallelTeam;
46  import ffx.numerics.atomic.AtomicDoubleArray.AtomicDoubleArrayImpl;
47  import ffx.numerics.math.Double3;
48  import ffx.numerics.math.DoubleMath;
49  import java.util.Objects;
50  
51  /**
52   * Implementation of maintaining a 3D double array that is operated on by multiple threads.
53   *
54   * @author Michael J. Schnieders
55   * @since 1.0
56   */
57  public class AtomicDoubleArray3D {
58  
59    /**
60     * Each dimension is stored in its own AtomicDoubleArray.
61     */
62    private final AtomicDoubleArray[] atomicDoubleArray;
63  
64    /**
65     * The implementation.
66     */
67    private final AtomicDoubleArrayImpl atomicDoubleArrayImpl;
68  
69    /**
70     * Private ParallelRegion3D for parallel reduction and resets.
71     */
72    private final ParallelRegion3D parallelRegion3D = new ParallelRegion3D();
73  
74    /**
75     * Construct an atomic 3D double array of the specified size using the specified implementation.
76     *
77     * @param atomicDoubleArrayImpl Implementation.
78     * @param size Size of each dimension.
79     */
80    public AtomicDoubleArray3D(AtomicDoubleArrayImpl atomicDoubleArrayImpl, int size) {
81      this(atomicDoubleArrayImpl, size, ParallelTeam.getDefaultThreadCount());
82    }
83  
84    /**
85     * Construct an atomic 3D double array of the specified size, using the specified implementation,
86     * and the requested number of threads.
87     *
88     * @param atomicDoubleArrayImpl Implementation.
89     * @param size Size of each dimension.
90     * @param nThreads Requested number of threads (only used by the MULTI implementation).
91     */
92    public AtomicDoubleArray3D(AtomicDoubleArrayImpl atomicDoubleArrayImpl, int size, int nThreads) {
93      atomicDoubleArray = new AtomicDoubleArray[3];
94      atomicDoubleArray[0] = atomicDoubleArrayFactory(atomicDoubleArrayImpl, nThreads, size);
95      atomicDoubleArray[1] = atomicDoubleArrayFactory(atomicDoubleArrayImpl, nThreads, size);
96      atomicDoubleArray[2] = atomicDoubleArrayFactory(atomicDoubleArrayImpl, nThreads, size);
97      this.atomicDoubleArrayImpl = atomicDoubleArrayImpl;
98    }
99  
100   public AtomicDoubleArray3D(AtomicDoubleArray x, AtomicDoubleArray y, AtomicDoubleArray z) {
101     atomicDoubleArray = new AtomicDoubleArray[3];
102     atomicDoubleArray[0] = x;
103     atomicDoubleArray[1] = y;
104     atomicDoubleArray[2] = z;
105     if (x instanceof MultiDoubleArray) {
106       this.atomicDoubleArrayImpl = AtomicDoubleArrayImpl.MULTI;
107     } else if (x instanceof AdderDoubleArray) {
108       this.atomicDoubleArrayImpl = AtomicDoubleArrayImpl.ADDER;
109     } else {
110       this.atomicDoubleArrayImpl = AtomicDoubleArrayImpl.PJ;
111     }
112   }
113 
114   /**
115    * Add to the double arrays at the specified index the given values.
116    *
117    * @param threadID the thread ID.
118    * @param index the index.
119    * @param x the x value.
120    * @param y the y value.
121    * @param z the z value.
122    */
123   public void add(int threadID, int index, double x, double y, double z) {
124     atomicDoubleArray[0].add(threadID, index, x);
125     atomicDoubleArray[1].add(threadID, index, y);
126     atomicDoubleArray[2].add(threadID, index, z);
127   }
128 
129   /**
130    * Add to the double arrays at the specified index the given Double3.
131    *
132    * @param threadID the thread ID.
133    * @param index the index.
134    * @param d3 the Double3 containing the values to add.
135    */
136   public void add(int threadID, int index, Double3 d3) {
137     atomicDoubleArray[0].add(threadID, index, d3.x());
138     atomicDoubleArray[1].add(threadID, index, d3.y());
139     atomicDoubleArray[2].add(threadID, index, d3.z());
140   }
141 
142   /**
143    * Ensure the AtomicDoubleArray3D instance is greater than or equal to size.
144    *
145    * @param size the size of the array.
146    */
147   public void alloc(int size) {
148     atomicDoubleArray[0].alloc(size);
149     atomicDoubleArray[1].alloc(size);
150     atomicDoubleArray[2].alloc(size);
151   }
152 
153   /**
154    * Get the value of the array at the specified index after calling the <code>reduce</code> method.
155    *
156    * @param dim Dimension [0, 1, 2]
157    * @param index the index.
158    * @return the value.
159    */
160   public double get(int dim, int index) {
161     return atomicDoubleArray[dim].get(index);
162   }
163 
164   /**
165    * Get the Double3 at the specified index. This is usually after calling the <code>reduce
166    * </code> method.
167    *
168    * @param index the index.
169    * @return a new Double3 with the values at the specified index.
170    */
171   public Double3 get(int index) {
172     return new Double3(
173         atomicDoubleArray[0].get(index),
174         atomicDoubleArray[1].get(index),
175         atomicDoubleArray[2].get(index));
176   }
177 
178   /**
179    * Get the X-value of the array at the specified index after calling the <code>reduce</code>
180    * method.
181    *
182    * @param index the index.
183    * @return the X value.
184    */
185   public double getX(int index) {
186     return atomicDoubleArray[0].get(index);
187   }
188 
189   /**
190    * Get the Y-value of the array at the specified index after calling the <code>reduce</code>
191    * method.
192    *
193    * @param index the index.
194    * @return the Y value.
195    */
196   public double getY(int index) {
197     return atomicDoubleArray[1].get(index);
198   }
199 
200   /**
201    * Get the Z-value of the array at the specified index after calling the <code>reduce</code>
202    * method.
203    *
204    * @param index the index.
205    * @return the Z value.
206    */
207   public double getZ(int index) {
208     return atomicDoubleArray[2].get(index);
209   }
210 
211   /**
212    * Perform reduction between the given lower bound (lb) and upper bound (up) if necessary.
213    *
214    * @param lb the lower bound.
215    * @param ub the upper bound.
216    */
217   public void reduce(int lb, int ub) {
218     // Nothing to do for PJ and Adder.
219     if (Objects.requireNonNull(atomicDoubleArrayImpl) == AtomicDoubleArrayImpl.MULTI) {
220       atomicDoubleArray[0].reduce(lb, ub);
221       atomicDoubleArray[1].reduce(lb, ub);
222       atomicDoubleArray[2].reduce(lb, ub);
223     }
224   }
225 
226   /**
227    * Perform a reduction on the entire array.
228    *
229    * @param parallelTeam ParallelTeam to use.
230    */
231   public void reduce(ParallelTeam parallelTeam) {
232     if (Objects.requireNonNull(atomicDoubleArrayImpl) == AtomicDoubleArrayImpl.MULTI) {
233       parallelRegion3D.setOperation(Operation.REDUCE);
234       try {
235         parallelTeam.execute(parallelRegion3D);
236       } catch (Exception e) {
237         throw new RuntimeException(e);
238       }
239     }
240   }
241 
242   /**
243    * Reset the double array to Zero.
244    *
245    * @param threadID the thread ID.
246    * @param lb the lower bound.
247    * @param ub the upper bound.
248    */
249   public void reset(int threadID, int lb, int ub) {
250     atomicDoubleArray[0].reset(threadID, lb, ub);
251     atomicDoubleArray[1].reset(threadID, lb, ub);
252     atomicDoubleArray[2].reset(threadID, lb, ub);
253   }
254 
255   /**
256    * Reset the double array to Zero.
257    *
258    * @param parallelTeam a ParallelTeam.
259    */
260   public void reset(ParallelTeam parallelTeam) {
261     parallelRegion3D.setOperation(Operation.RESET);
262     try {
263       parallelTeam.execute(parallelRegion3D);
264     } catch (Exception e) {
265       throw new RuntimeException(e);
266     }
267   }
268 
269   /**
270    * Scale the double arrays at the specified index to the given values.
271    *
272    * @param threadID the thread ID.
273    * @param index the index.
274    * @param scale The value to scale by.
275    */
276   public void scale(int threadID, int index, double scale) {
277     atomicDoubleArray[0].scale(threadID, index, scale);
278     atomicDoubleArray[1].scale(threadID, index, scale);
279     atomicDoubleArray[2].scale(threadID, index, scale);
280   }
281 
282   /**
283    * Set the double arrays at the specified index to the given values.
284    *
285    * @param threadID the thread ID.
286    * @param index the index.
287    * @param x the X value.
288    * @param y the Y value.
289    * @param z the Z value.
290    */
291   public void set(int threadID, int index, double x, double y, double z) {
292     atomicDoubleArray[0].set(threadID, index, x);
293     atomicDoubleArray[1].set(threadID, index, y);
294     atomicDoubleArray[2].set(threadID, index, z);
295   }
296 
297   /**
298    * Set the double arrays at the specified index to the given Double3.
299    *
300    * @param threadID the thread ID.
301    * @param index the index.
302    * @param d3 the Double3 containing the values.
303    */
304   public void set(int threadID, int index, Double3 d3) {
305     atomicDoubleArray[0].set(threadID, index, d3.x());
306     atomicDoubleArray[1].set(threadID, index, d3.y());
307     atomicDoubleArray[2].set(threadID, index, d3.z());
308   }
309 
310   /**
311    * Subtracts from the double arrays at the specified index the given values.
312    *
313    * @param threadID the thread ID.
314    * @param index the index.
315    * @param x the X value.
316    * @param y the Y value.
317    * @param z the Z value.
318    */
319   public void sub(int threadID, int index, double x, double y, double z) {
320     atomicDoubleArray[0].sub(threadID, index, x);
321     atomicDoubleArray[1].sub(threadID, index, y);
322     atomicDoubleArray[2].sub(threadID, index, z);
323   }
324 
325   /**
326    * Subtracts from the double arrays at the specified index the given Double3.
327    *
328    * @param threadID the thread ID.
329    * @param index the index.
330    * @param d3 the Double3 containing the values.
331    */
332   public void sub(int threadID, int index, Double3 d3) {
333     atomicDoubleArray[0].sub(threadID, index, d3.x());
334     atomicDoubleArray[1].sub(threadID, index, d3.y());
335     atomicDoubleArray[2].sub(threadID, index, d3.z());
336   }
337 
338   /**
339    * Return a string for given index.
340    *
341    * @param index Index.
342    * @return Returns a String for 3D vector at the given index.
343    */
344   public String toString(int index) {
345     String defaultLabel = " " + index + ": ";
346     return toString(index, defaultLabel);
347   }
348 
349   /**
350    * Return a string for given index.
351    *
352    * @param index Index.
353    * @param label Label to use for the String.
354    * @return Returns a String for 3D vector at the given index.
355    */
356   public String toString(int index, String label) {
357     var d = new double[] {getX(index), getY(index), getZ(index)};
358     return DoubleMath.toString(d, label);
359   }
360 
361   /**
362    * Return a String for entire Array, with one 3D vector per line.
363    *
364    * @return Returns a String for the 3D array.
365    */
366   public String toString() {
367     StringBuilder sb = new StringBuilder();
368     for (int i = 0; i < atomicDoubleArray.length; i++) {
369       sb.append(toString(i)).append("\n");
370     }
371     return sb.toString();
372   }
373 
374   /**
375    * Return a String for entire Array, with one 3D vector per line.
376    *
377    * @param label Label may include one "%d" format conversion to include for the index of each
378    *     entry.
379    * @return Returns a String for the 3D array.
380    */
381   public String toString(String label) {
382     StringBuilder sb = new StringBuilder();
383     if (label.contains("%d")) {
384       for (int i = 0; i < atomicDoubleArray[0].size(); i++) {
385         sb.append(toString(i, format(label, i))).append("\n");
386       }
387     } else {
388       for (int i = 0; i < atomicDoubleArray[0].size(); i++) {
389         sb.append(toString(i, label)).append("\n");
390       }
391     }
392     return sb.toString();
393   }
394 
395   /**
396    * Available parallel operations.
397    */
398   private enum Operation {
399     RESET,
400     REDUCE
401   }
402 
403   /**
404    * Private ParallelRegion for parallel reset and reduction operations.
405    */
406   private class ParallelRegion3D extends ParallelRegion {
407 
408     private Operation operation;
409     private IntegerForLoop3D[] integerForLoop3D;
410 
411     /**
412      * Instantiates a new parallel region.
413      */
414     public ParallelRegion3D() {
415       operation = Operation.RESET;
416     }
417 
418     /**
419      * Sets the operation.
420      *
421      * @param operation the new operation
422      */
423     public void setOperation(Operation operation) {
424       this.operation = operation;
425     }
426 
427     /** {@inheritDoc} */
428     @Override
429     public void start() {
430       int nThreads = getThreadCount();
431       if (integerForLoop3D == null) {
432         integerForLoop3D = new IntegerForLoop3D[nThreads];
433       }
434     }
435 
436     /** {@inheritDoc} */
437     @Override
438     public void run() throws Exception {
439       int threadID = getThreadIndex();
440       if (integerForLoop3D[threadID] == null) {
441         integerForLoop3D[threadID] = new IntegerForLoop3D();
442       }
443       int size = atomicDoubleArray[0].size();
444       execute(0, size - 1, integerForLoop3D[threadID]);
445     }
446 
447     /**
448      * Private class for parallel reset and reduction operations.
449      */
450     private class IntegerForLoop3D extends IntegerForLoop {
451 
452       /** {@inheritDoc} */
453       @Override
454       public void run(int lb, int ub) {
455         int threadID = getThreadIndex();
456         switch (operation) {
457           case RESET -> reset(threadID, lb, ub);
458           case REDUCE -> reduce(lb, ub);
459         }
460       }
461     }
462   }
463 
464 
465 }