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