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 edu.rit.pj.IntegerSchedule; 41 import edu.rit.util.Range; 42 import java.util.logging.Logger; 43 44 /** 45 * A fixed schedule that balances pairwise work across threads. 46 * 47 * @author Michael J. Schnieders 48 * @since 1.0 49 */ 50 public class PairwiseSchedule extends IntegerSchedule { 51 52 private static final Logger logger = Logger.getLogger(PairwiseSchedule.class.getName()); 53 private final int nThreads; 54 private final Range[] ranges; 55 private final boolean[] threadDone; 56 private int nAtoms; 57 private int threadOffset; 58 59 /** 60 * Constructor for PairwiseSchedule. 61 * 62 * @param nThreads a int. 63 * @param nAtoms a int. 64 * @param ranges an array of {@link edu.rit.util.Range} objects. 65 */ 66 public PairwiseSchedule(int nThreads, int nAtoms, Range[] ranges) { 67 this.nAtoms = nAtoms; 68 this.nThreads = nThreads; 69 threadOffset = 0; 70 this.ranges = ranges; 71 threadDone = new boolean[nThreads]; 72 } 73 74 /** 75 * {@inheritDoc} 76 * 77 * <p>This is a fixed schedule. 78 */ 79 @Override 80 public boolean isFixedSchedule() { 81 return true; 82 } 83 84 /** {@inheritDoc} */ 85 @Override 86 public Range next(int threadID) { 87 if (!threadDone[threadID]) { 88 threadDone[threadID] = true; 89 return ranges[threadID]; 90 } 91 return null; 92 } 93 94 /** 95 * setAtoms. 96 * 97 * @param nAtoms a int. 98 */ 99 public void setAtoms(int nAtoms) { 100 this.nAtoms = nAtoms; 101 } 102 103 /** {@inheritDoc} */ 104 @Override 105 public void start(int nThreads, Range chunkRange) { 106 assert (nThreads == this.nThreads); 107 assert (chunkRange.lb() == 0); 108 assert (chunkRange.ub() == nAtoms - 1); 109 110 for (int i = 0; i < nThreads; i++) { 111 threadDone[i] = false; 112 } 113 } 114 115 /** 116 * updateRanges 117 * 118 * @param totalInteractions a int. 119 * @param atomsWithInteractions the number of chunks of interactions. 120 * @param listCount an array of int. 121 */ 122 void updateRanges(int totalInteractions, int atomsWithInteractions, int[] listCount) { 123 int id = 0; 124 int goal = totalInteractions / (nThreads + threadOffset); 125 int num = 0; 126 int start = 0; 127 for (int i = 0; i < nAtoms; i++) { 128 num += listCount[i]; 129 if (num >= goal) { 130 131 // Last thread gets the remaining atoms. 132 if (id == nThreads - 1) { 133 ranges[id] = new Range(start, nAtoms - 1); 134 break; 135 } 136 137 ranges[id] = new Range(start, i); 138 139 // Zero out the interaction counter. 140 num = 0; 141 // Next thread. 142 id++; 143 // Next range starts at i+1. 144 start = i + 1; 145 146 // Out of atoms. Threads remaining get a null range. 147 if (start == nAtoms) { 148 if (atomsWithInteractions > nThreads + threadOffset + 1) { 149 threadOffset++; 150 updateRanges(totalInteractions, atomsWithInteractions, listCount); 151 break; 152 } 153 for (int j = id; j < nThreads; j++) { 154 ranges[j] = null; 155 } 156 break; 157 } 158 } else if (i == nAtoms - 1) { 159 160 // Last atom without reaching goal for current thread. 161 if (id < nThreads - 1 && atomsWithInteractions > nThreads + threadOffset + 1) { 162 threadOffset++; 163 updateRanges(totalInteractions, atomsWithInteractions, listCount); 164 break; 165 } 166 ranges[id] = new Range(start, nAtoms - 1); 167 for (int j = id + 1; j < nThreads; j++) { 168 ranges[j] = null; 169 } 170 } 171 } 172 threadOffset = 0; 173 } 174 }