OpenCLNonbondedUtilities.h 14.6 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
#ifndef OPENMM_OPENCLNONBONDEDUTILITIES_H_
#define OPENMM_OPENCLNONBONDEDUTILITIES_H_

/* -------------------------------------------------------------------------- *
 *                                   OpenMM                                   *
 * -------------------------------------------------------------------------- *
 * This is part of the OpenMM molecular simulation toolkit originating from   *
 * Simbios, the NIH National Center for Physics-Based Simulation of           *
 * Biological Structures at Stanford, funded under the NIH Roadmap for        *
 * Medical Research, grant U54 GM072970. See https://simtk.org.               *
 *                                                                            *
Peter Eastman's avatar
Peter Eastman committed
12
 * Portions copyright (c) 2009-2025 Stanford University and the Authors.      *
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
 * Authors: Peter Eastman                                                     *
 * Contributors:                                                              *
 *                                                                            *
 * This program is free software: you can redistribute it and/or modify       *
 * it under the terms of the GNU Lesser General Public License as published   *
 * by the Free Software Foundation, either version 3 of the License, or       *
 * (at your option) any later version.                                        *
 *                                                                            *
 * This program is distributed in the hope that it will be useful,            *
 * but WITHOUT ANY WARRANTY; without even the implied warranty of             *
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the              *
 * GNU Lesser General Public License for more details.                        *
 *                                                                            *
 * You should have received a copy of the GNU Lesser General Public License   *
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.      *
 * -------------------------------------------------------------------------- */

#include "openmm/System.h"
31
#include "OpenCLArray.h"
32
#include "OpenCLExpressionUtilities.h"
33
#include "openmm/common/ComputeSort.h"
34
#include "openmm/common/NonbondedUtilities.h"
35
#include <sstream>
36
37
38
39
#include <string>
#include <vector>

namespace OpenMM {
40
    
41
class OpenCLContext;
42
43

/**
44
 * This class provides a generic interface for calculating nonbonded interactions.  It does this in two
45
 * ways.  First, it can be used to create Kernels that evaluate nonbonded interactions.  Clients
46
47
48
49
50
51
52
53
54
55
56
57
58
 * only need to provide the code for evaluating a single interaction and the list of parameters it depends on.
 * A complete kernel is then synthesized using an appropriate algorithm to evaluate all interactions on all
 * atoms.
 *
 * Second, this class itself creates and invokes a single "default" interaction kernel, allowing several
 * different forces to be evaluated at once for greater efficiency.  Call addInteraction() and addParameter()
 * to add interactions to this default kernel.
 *
 * During each force or energy evaluation, the following sequence of steps takes place:
 *
 * 1. Data structures (e.g. neighbor lists) are calculated to allow nonbonded interactions to be evaluated
 * quickly.
 *
59
 * 2. calcForcesAndEnergy() is called on each ForceImpl in the System.
60
61
62
63
64
 *
 * 3. Finally, the default interaction kernel is invoked to calculate all interactions that were added
 * to it.
 *
 * This sequence means that the default interaction kernel may depend on quantities that were calculated
65
 * by ForceImpls during calcForcesAndEnergy().
66
67
 */

68
class OPENMM_EXPORT_COMMON OpenCLNonbondedUtilities : public NonbondedUtilities {
69
70
71
72
public:
    OpenCLNonbondedUtilities(OpenCLContext& context);
    ~OpenCLNonbondedUtilities();
    /**
73
     * Add a nonbonded interaction to be evaluated by the default interaction kernel.
74
75
76
     *
     * @param usesCutoff     specifies whether a cutoff should be applied to this interaction
     * @param usesPeriodic   specifies whether periodic boundary conditions should be applied to this interaction
77
     * @param usesExclusions specifies whether this interaction uses exclusions.  If this is true, it must have identical exclusions to every other interaction.
78
79
     * @param cutoffDistance the cutoff distance for this interaction (ignored if usesCutoff is false)
     * @param exclusionList  for each atom, specifies the list of other atoms whose interactions should be excluded
80
     * @param kernel         the code to evaluate the interaction
81
     * @param forceGroup     the force group in which the interaction should be calculated
82
83
     * @param useNeighborList  specifies whether a neighbor list should be used to optimize this interaction.  This should
     *                         be viewed as only a suggestion.  Even when it is false, a neighbor list may be used anyway.
84
     * @param supportsPairList specifies whether this interaction can work with a neighbor list that uses a separate pair list
85
     */
86
87
88
    void addInteraction(bool usesCutoff, bool usesPeriodic, bool usesExclusions, double cutoffDistance,
                        const std::vector<std::vector<int> >& exclusionList, const std::string& kernel,
                        int forceGroup, bool useNeighborList=true, bool supportsPairList=false);
89
    /**
90
     * Add a per-atom parameter that the default interaction kernel may depend on.
91
     */
92
    void addParameter(ComputeParameterInfo parameter);
93
94
95
    /**
     * Add an array (other than a per-atom parameter) that should be passed as an argument to the default interaction kernel.
     */
96
    void addArgument(ComputeParameterInfo parameter);
97
98
99
100
101
102
103
104
105
    /**
     * Register that the interaction kernel will be computing the derivative of the potential energy
     * with respect to a parameter.
     * 
     * @param param   the name of the parameter
     * @return the variable that will be used to accumulate the derivative.  Any code you pass to addInteraction() should
     * add its contributions to this variable.
     */
    std::string addEnergyParameterDerivative(const std::string& param);
106
107
108
109
110
111
    /**
     * Specify the list of exclusions that an interaction outside the default kernel will depend on.
     * 
     * @param exclusionList  for each atom, specifies the list of other atoms whose interactions should be excluded
     */
    void requestExclusions(const std::vector<std::vector<int> >& exclusionList);
112
113
114
115
116
117
118
    /**
     * Initialize this object in preparation for a simulation.
     */
    void initialize(const System& system);
    /**
     * Get the number of force buffers required for nonbonded forces.
     */
119
    int getNumForceBuffers() const {
120
        return 1;
121
    }
122
123
124
125
126
127
    /**
     * Get the number of energy buffers required for nonbonded forces.
     */
    int getNumEnergyBuffers() {
        return numForceThreadBlocks*forceThreadBlockSize;
    }
128
129
130
131
132
133
134
135
136
137
138
139
    /**
     * Get whether a cutoff is being used.
     */
    bool getUseCutoff() {
        return useCutoff;
    }
    /**
     * Get whether periodic boundary conditions are being used.
     */
    bool getUsePeriodic() {
        return usePeriodic;
    }
140
141
142
143
144
145
146
147
148
149
150
151
    /**
     * Get the number of work groups used for computing nonbonded forces.
     */
    int getNumForceThreadBlocks() {
        return numForceThreadBlocks;
    }
    /**
     * Get the size of each work group used for computing nonbonded forces.
     */
    int getForceThreadBlockSize() {
        return forceThreadBlockSize;
    }
152
    /**
153
     * Get the maximum cutoff distance used by any force group.
154
     */
155
    double getMaxCutoffDistance();
Peter Eastman's avatar
Peter Eastman committed
156
157
158
159
    /**
     * Get whether any interactions have been added.
     */
    bool getHasInteractions() {
160
        return (groupCutoff.size() > 0);
161
    }
162
163
164
165
166
    /**
     * Given a nonbonded cutoff, get the padded cutoff distance used in computing
     * the neighbor list.
     */
    double padCutoff(double cutoff);
167
168
169
    /**
     * Prepare to compute interactions.  This updates the neighbor list.
     */
170
    void prepareInteractions(int forceGroups);
171
    /**
172
     * Compute the nonbonded interactions.
173
174
175
176
     * 
     * @param forceGroups    the flags specifying which force groups to include
     * @param includeForces  whether to compute forces
     * @param includeEnergy  whether to compute the potential energy
177
     */
178
    void computeInteractions(int forceGroups, bool includeForces, bool includeEnergy);
179
180
    /**
     * Check to see if the neighbor list arrays are large enough, and make them bigger if necessary.
181
182
     *
     * @return true if the neighbor list needed to be enlarged.
183
     */
184
    bool updateNeighborListSize();
185
186
187
    /**
     * Get the array containing the center of each atom block.
     */
188
    OpenCLArray& getBlockCenters() {
peastman's avatar
peastman committed
189
        return blockCenter;
190
191
192
193
    }
    /**
     * Get the array containing the dimensions of each atom block.
     */
194
    OpenCLArray& getBlockBoundingBoxes() {
peastman's avatar
peastman committed
195
        return blockBoundingBox;
196
197
198
199
    }
    /**
     * Get the array whose first element contains the number of tiles with interactions.
     */
200
    OpenCLArray& getInteractionCount() {
peastman's avatar
peastman committed
201
        return interactionCount;
202
203
204
205
    }
    /**
     * Get the array containing tiles with interactions.
     */
206
    OpenCLArray& getInteractingTiles() {
peastman's avatar
peastman committed
207
        return interactingTiles;
208
209
    }
    /**
210
     * Get the array containing the atoms in each tile with interactions.
211
     */
212
    OpenCLArray& getInteractingAtoms() {
peastman's avatar
peastman committed
213
        return interactingAtoms;
214
    }
215
216
217
    /**
     * Get the array containing exclusion flags.
     */
218
    OpenCLArray& getExclusions() {
peastman's avatar
peastman committed
219
        return exclusions;
220
    }
221
222
223
224
    /**
     * Get the array containing tiles with exclusions.
     */
    OpenCLArray& getExclusionTiles() {
peastman's avatar
peastman committed
225
        return exclusionTiles;
226
    }
227
228
229
    /**
     * Get the array containing the index into the exclusion array for each tile.
     */
230
    OpenCLArray& getExclusionIndices() {
peastman's avatar
peastman committed
231
        return exclusionIndices;
232
233
234
235
    }
    /**
     * Get the array listing where the exclusion data starts for each row.
     */
236
    OpenCLArray& getExclusionRowIndices() {
peastman's avatar
peastman committed
237
        return exclusionRowIndices;
238
    }
239
240
241
242
243
244
245
    /**
     * Get the array containing a flag for whether the neighbor list was rebuilt
     * on the most recent call to prepareInteractions().
     */
    OpenCLArray& getRebuildNeighborList() {
        return rebuildNeighborList;
    }
246
247
248
249
250
251
252
253
254
255
256
257
    /**
     * Get the index of the first tile this context is responsible for processing.
     */
    int getStartTileIndex() const {
        return startTileIndex;
    }
    /**
     * Get the total number of tiles this context is responsible for processing.
     */
    int getNumTiles() const {
        return numTiles;
    }
258
    /**
259
260
261
262
263
264
265
266
267
     * Set whether to add padding to the cutoff distance when building the neighbor list.
     * This increases the size of the neighbor list (and thus the cost of computing interactions),
     * but also means we don't need to rebuild it every time step.  The default value is true,
     * since usually this improves performance.  For very expensive interactions, however,
     * it may be better to set this to false.
     */
    void setUsePadding(bool padding);
    /**
     * Set the range of atom blocks and tiles that should be processed by this context.
268
     */
269
    void setAtomBlockRange(double startFraction, double endFraction);
270
271
272
273
274
275
276
    /**
     * Create a Kernel for evaluating a nonbonded interaction.  Cutoffs and periodic boundary conditions
     * are assumed to be the same as those for the default interaction Kernel, since this kernel will use
     * the same neighbor list.
     * 
     * @param source        the source code for evaluating the force and energy
     * @param params        the per-atom parameters this kernel may depend on
277
     * @param arguments     arrays (other than per-atom parameters) that should be passed as arguments to the kernel
278
     * @param useExclusions specifies whether exclusions are applied to this interaction
279
     * @param isSymmetric   specifies whether the interaction is symmetric
280
     * @param groups        the set of force groups this kernel is for
281
282
     * @param includeForces whether this kernel should compute forces
     * @param includeEnergy whether this kernel should compute potential energy
283
     */
284
    cl::Kernel createInteractionKernel(const std::string& source, std::vector<ComputeParameterInfo>& params, std::vector<ComputeParameterInfo>& arguments, bool useExclusions, bool isSymmetric, int groups, bool includeForces, bool includeEnergy);
285
286
287
288
    /**
     * Create the set of kernels that will be needed for a particular combination of force groups.
     * 
     * @param groups    the set of force groups
289
     */
290
    void createKernelsForGroups(int groups);
291
292
293
294
    /**
     * Set the source code for the main kernel.  It only needs to be changed in very unusual circumstances.
     */
    void setKernelSource(const std::string& source);
295
private:
296
    class KernelSet;
297
    class BlockSortTrait;
298
    OpenCLContext& context;
299
    std::map<int, KernelSet> groupKernels;
peastman's avatar
peastman committed
300
301
302
303
304
305
306
307
308
309
310
311
    OpenCLArray exclusionTiles;
    OpenCLArray exclusions;
    OpenCLArray exclusionIndices;
    OpenCLArray exclusionRowIndices;
    OpenCLArray interactingTiles;
    OpenCLArray interactingAtoms;
    OpenCLArray interactionCount;
    OpenCLArray blockCenter;
    OpenCLArray blockBoundingBox;
    OpenCLArray sortedBlocks;
    OpenCLArray sortedBlockCenter;
    OpenCLArray sortedBlockBoundingBox;
312
    OpenCLArray blockSizeRange;
313
314
    OpenCLArray largeBlockCenter;
    OpenCLArray largeBlockBoundingBox;
peastman's avatar
peastman committed
315
316
    OpenCLArray oldPositions;
    OpenCLArray rebuildNeighborList;
317
    ComputeSort blockSorter;
318
319
    cl::Event downloadCountEvent;
    cl::Buffer* pinnedCountBuffer;
320
    unsigned int* pinnedCountMemory;
321
    std::vector<std::vector<int> > atomExclusions;
322
323
    std::vector<ComputeParameterInfo> parameters;
    std::vector<ComputeParameterInfo> arguments;
324
    std::vector<std::string> energyParameterDerivatives;
325
326
    std::map<int, double> groupCutoff;
    std::map<int, std::string> groupKernelSource;
Peter Eastman's avatar
Peter Eastman committed
327
    double maxCutoff;
328
    bool useCutoff, usePeriodic, deviceIsCpu, anyExclusions, usePadding, useNeighborList, forceRebuildNeighborList, useLargeBlocks, isAMD;
329
    int startTileIndex, startBlockIndex, numBlocks, maxExclusions, numForceThreadBlocks;
330
    int forceThreadBlockSize, interactingBlocksThreadBlockSize, groupFlags, numBlockSizes;
331
    unsigned int tilesAfterReorder;
332
    long long numTiles;
333
    std::string kernelSource;
334
335
336
337
338
339
340
341
342
};

/**
 * This class stores the kernels to execute for a set of force groups.
 */

class OpenCLNonbondedUtilities::KernelSet {
public:
    bool hasForces;
343
344
    std::string source;
    cl::Kernel forceKernel, energyKernel, forceEnergyKernel;
345
    cl::Kernel findBlockBoundsKernel;
346
    cl::Kernel computeSortKeysKernel;
347
348
349
    cl::Kernel sortBoxDataKernel;
    cl::Kernel findInteractingBlocksKernel;
    cl::Kernel findInteractionsWithinBlocksKernel;
350
351
352
353
354
};

} // namespace OpenMM

#endif /*OPENMM_OPENCLNONBONDEDUTILITIES_H_*/