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.potential.nonbonded;
39
40 import static ffx.potential.nonbonded.SpatialDensityRegion.logger;
41 import static java.util.Arrays.fill;
42
43 import edu.rit.pj.IntegerForLoop;
44 import edu.rit.pj.IntegerSchedule;
45 import edu.rit.pj.ParallelRegion;
46 import ffx.crystal.Crystal;
47 import ffx.potential.bonded.Atom;
48 import java.nio.DoubleBuffer;
49 import java.util.logging.Level;
50
51 /**
52 * The SliceLoop class is used to parallelize placing onto a 3D grid
53 *
54 * <p>1) Multipoles using B-splines or
55 *
56 * <p>2) Diffraction form factors.
57 *
58 * <p>Each "slice" of the grid (i.e. a fixed value of the z-coordinate) is operated on by only a
59 * single thread to logically enforce atomic updates of grid magnitudes.
60 *
61 * @author Armin Avdic
62 */
63 public class SliceRegion extends ParallelRegion {
64
65 public int buff = 3;
66 public boolean[][] select;
67 protected SliceLoop[] sliceLoop;
68 protected double[][][] coordinates;
69 int nAtoms;
70 int nSymm;
71 private int gX, gY, gZ;
72 private DoubleBuffer gridBuffer;
73 private GridInitLoop[] gridInitLoop;
74 private double initValue = 0.0;
75 private int gridSize;
76 private double[] grid;
77 private boolean rebuildList;
78 private int[][] zAtListBuild;
79
80 /**
81 * Constructor for SliceRegion.
82 *
83 * @param gX a int.
84 * @param gY a int.
85 * @param gZ a int.
86 * @param grid the grid array.
87 * @param nSymm a int.
88 * @param threadCount a int.
89 * @param atoms an array of {@link ffx.potential.bonded.Atom} objects.
90 * @param coordinates the atomic coordinate array.
91 */
92 public SliceRegion(int gX, int gY, int gZ, double[] grid, int nSymm,
93 int threadCount, Atom[] atoms, double[][][] coordinates) {
94 this.nAtoms = atoms.length;
95 this.gX = gX;
96 this.gY = gY;
97 this.gZ = gZ;
98 gridSize = gX * gY * gZ * 2;
99 this.nSymm = nSymm;
100 this.coordinates = coordinates;
101 this.grid = grid;
102 if (grid != null) {
103 gridBuffer = DoubleBuffer.wrap(grid);
104 }
105 sliceLoop = new SliceLoop[threadCount];
106 gridInitLoop = new GridInitLoop[threadCount];
107 for (int i = 0; i < threadCount; i++) {
108 gridInitLoop[i] = new GridInitLoop();
109 }
110 select = new boolean[nSymm][nAtoms];
111 for (int i = 0; i < nSymm; i++) {
112 fill(select[i], true);
113 }
114 zAtListBuild = new int[nSymm][nAtoms];
115 rebuildList = true;
116 }
117
118 /** {@inheritDoc} */
119 @Override
120 public void finish() {
121 if (rebuildList) {
122 sliceLoop[0].saveZValues(zAtListBuild);
123 }
124 rebuildList = false;
125 }
126
127 /**
128 * Getter for the field <code>grid</code>.
129 *
130 * @return return the grid array.
131 */
132 public double[] getGrid() {
133 return grid;
134 }
135
136 /**
137 * getNatoms.
138 *
139 * @return a int.
140 */
141 public int getNatoms() {
142 return nAtoms;
143 }
144
145 /**
146 * getNsymm.
147 *
148 * @return a int.
149 */
150 public int getNsymm() {
151 return nSymm;
152 }
153
154 /** {@inheritDoc} */
155 @Override
156 public void run() throws Exception {
157 int threadIndex = getThreadIndex();
158 SliceLoop loop = sliceLoop[threadIndex];
159 // This lets the same SpatialDensityLoops be used with different SpatialDensityRegions.
160 loop.setNsymm(nSymm);
161 loop.setRebuildList(rebuildList);
162 try {
163 execute(0, gridSize - 1, gridInitLoop[threadIndex]);
164 execute(0, gZ - 1, loop);
165 } catch (Exception e) {
166 String message = " Exception in SliceRegion.";
167 logger.log(Level.SEVERE, message, e);
168 }
169 }
170
171 /**
172 * Select atoms that should be included. The default is to include all atoms, which is set up in
173 * the constructor. This function should be over-ridden by subclasses that want finer control.
174 */
175 public void selectAtoms() {
176 for (int i = 0; i < nSymm; i++) {
177 fill(select[i], true);
178 }
179 }
180
181 /**
182 * Setter for the field <code>atoms</code>.
183 *
184 * @param atoms an array of {@link ffx.potential.bonded.Atom} objects.
185 */
186 public void setAtoms(Atom[] atoms) {
187 nAtoms = atoms.length;
188 select = new boolean[nSymm][nAtoms];
189 zAtListBuild = new int[nSymm][nAtoms];
190 for (int i = 0; i < nSymm; i++) {
191 fill(select[i], true);
192 }
193 rebuildList = true;
194 }
195
196 /**
197 * Setter for the field <code>crystal</code>.
198 *
199 * @param crystal a {@link ffx.crystal.Crystal} object.
200 * @param gX a int.
201 * @param gY a int.
202 * @param gZ a int.
203 */
204 public final void setCrystal(Crystal crystal, int gX, int gY, int gZ) {
205 // this.crystal = crystal.getUnitCell();
206 this.gX = gX;
207 this.gY = gY;
208 this.gZ = gZ;
209 gridSize = gX * gY * gZ * 2;
210 }
211
212 /**
213 * setDensityLoop.
214 *
215 * @param loops an array of {@link ffx.potential.nonbonded.SliceLoop} objects.
216 */
217 public void setDensityLoop(SliceLoop[] loops) {
218 sliceLoop = loops;
219 }
220
221 /**
222 * Setter for the field <code>initValue</code>.
223 *
224 * @param initValue a double.
225 */
226 public void setInitValue(double initValue) {
227 this.initValue = initValue;
228 }
229
230 /** {@inheritDoc} */
231 @Override
232 public void start() {
233 selectAtoms();
234 rebuildList = (rebuildList || sliceLoop[0].checkList(zAtListBuild, buff));
235 }
236
237 /**
238 * Setter for the field <code>gridBuffer</code>.
239 *
240 * @param grid a {@link java.nio.DoubleBuffer} object.
241 */
242 void setGridBuffer(DoubleBuffer grid) {
243 gridBuffer = grid;
244 }
245
246 private class GridInitLoop extends IntegerForLoop {
247
248 private final IntegerSchedule schedule = IntegerSchedule.fixed();
249
250 @Override
251 public void run(int lb, int ub) {
252 if (gridBuffer != null) {
253 // if (grid != null) {
254 for (int i = lb; i <= ub; i++) {
255 // grid[i] = initValue;
256 gridBuffer.put(i, initValue);
257 }
258 }
259 }
260
261 @Override
262 public IntegerSchedule schedule() {
263 return schedule;
264 }
265 }
266 }