nonbonded_nvidia.cl 16.4 KB
Newer Older
1
#pragma OPENCL EXTENSION cl_khr_global_int32_base_atomics : enable
2
3
4
#ifdef SUPPORTS_64_BIT_ATOMICS
#pragma OPENCL EXTENSION cl_khr_int64_base_atomics : enable
#endif
5
#define TILE_SIZE 32
6
#define WARPS_PER_GROUP (FORCE_WORK_GROUP_SIZE/TILE_SIZE)
7

8
typedef struct {
9
10
11
    float x, y, z;
    float q;
    float fx, fy, fz;
12
13
14
    ATOM_PARAMETER_DATA
} AtomData;

15
16
17
/**
 * Compute nonbonded interactions.
 */
18
19
__kernel void computeNonbonded(
#ifdef SUPPORTS_64_BIT_ATOMICS
20
        __global long* restrict forceBuffers,
21
#else
22
        __global float4* restrict forceBuffers,
23
#endif
24
        __global float* restrict energyBuffer, __global const float4* restrict posq, __global const unsigned int* restrict exclusions,
25
        __global const unsigned int* restrict exclusionIndices, __global const unsigned int* restrict exclusionRowIndices, __local AtomData* restrict localData,
26
        unsigned int startTileIndex, unsigned int endTileIndex,
27
#ifdef USE_CUTOFF
28
        __global const ushort2* restrict tiles, __global const unsigned int* restrict interactionCount, float4 periodicBoxSize, float4 invPeriodicBoxSize, unsigned int maxTiles, __global const unsigned int* restrict interactionFlags
29
30
#else
        unsigned int numTiles
31
#endif
32
        PARAMETER_ARGUMENTS) {
33
34
    unsigned int totalWarps = get_global_size(0)/TILE_SIZE;
    unsigned int warp = get_global_id(0)/TILE_SIZE;
35
36
#ifdef USE_CUTOFF
    unsigned int numTiles = interactionCount[0];
37
38
    unsigned int pos = (numTiles > maxTiles ? startTileIndex+warp*(endTileIndex-startTileIndex)/totalWarps : warp*numTiles/totalWarps);
    unsigned int end = (numTiles > maxTiles ? startTileIndex+(warp+1)*(endTileIndex-startTileIndex)/totalWarps : (warp+1)*numTiles/totalWarps);
39
#else
40
41
    unsigned int pos = startTileIndex+warp*numTiles/totalWarps;
    unsigned int end = startTileIndex+(warp+1)*numTiles/totalWarps;
42
#endif
43
    float energy = 0.0f;
44
    __local float tempBuffer[3*FORCE_WORK_GROUP_SIZE];
45
46
    __local unsigned int exclusionRange[2*WARPS_PER_GROUP];
    __local int exclusionIndex[WARPS_PER_GROUP];
47
48
49
    __local int2* reservedBlocks = (__local int2*) exclusionRange;
    
    do {
50
        // Extract the coordinates of this tile
51
52
53
        const unsigned int tgx = get_local_id(0) & (TILE_SIZE-1);
        const unsigned int tbx = get_local_id(0) - tgx;
        const unsigned int localGroupIndex = get_local_id(0)/TILE_SIZE;
54
        unsigned int x, y;
55
56
        float4 force = 0.0f;
        if (pos < end) {
57
#ifdef USE_CUTOFF
58
59
60
61
62
63
            if (numTiles <= maxTiles) {
                ushort2 tileIndices = tiles[pos];
                x = tileIndices.x;
                y = tileIndices.y;
            }
            else
64
#endif
65
66
            {
                y = (unsigned int) floor(NUM_BLOCKS+0.5f-SQRT((NUM_BLOCKS+0.5f)*(NUM_BLOCKS+0.5f)-2*pos));
67
                x = (pos-y*NUM_BLOCKS+y*(y+1)/2);
68
69
70
71
                if (x < y || x >= NUM_BLOCKS) { // Occasionally happens due to roundoff error.
                    y += (x < y ? -1 : 1);
                    x = (pos-y*NUM_BLOCKS+y*(y+1)/2);
                }
72
            }
73
74
75
            unsigned int atom1 = x*TILE_SIZE + tgx;
            float4 posq1 = posq[atom1];
            LOAD_ATOM1_PARAMETERS
76

77
            // Locate the exclusion data for this tile.
78
79

#ifdef USE_EXCLUSIONS
80
81
82
83
            if (tgx < 2)
                exclusionRange[2*localGroupIndex+tgx] = exclusionRowIndices[x+tgx];
            if (tgx == 0)
                exclusionIndex[localGroupIndex] = -1;
84
            for (unsigned int i = exclusionRange[2*localGroupIndex]+tgx; i < exclusionRange[2*localGroupIndex+1]; i += TILE_SIZE)
85
86
87
                if (exclusionIndices[i] == y)
                    exclusionIndex[localGroupIndex] = i*TILE_SIZE;
            bool hasExclusions = (exclusionIndex[localGroupIndex] > -1);
88
#else
89
            bool hasExclusions = false;
90
#endif
91
92
93
94
            if (pos >= end)
                ; // This warp is done.
            else if (x == y) {
                // This tile is on the diagonal.
95

96
97
98
99
100
101
                const unsigned int localAtomIndex = get_local_id(0);
                localData[localAtomIndex].x = posq1.x;
                localData[localAtomIndex].y = posq1.y;
                localData[localAtomIndex].z = posq1.z;
                localData[localAtomIndex].q = posq1.w;
                LOAD_LOCAL_PARAMETERS_FROM_1
102
#ifdef USE_EXCLUSIONS
103
                unsigned int excl = exclusions[exclusionIndex[localGroupIndex]+tgx];
104
#endif
105
                for (unsigned int j = 0; j < TILE_SIZE; j++) {
106
#ifdef USE_EXCLUSIONS
107
                    bool isExcluded = !(excl & 0x1);
108
#endif
109
110
111
                    int atom2 = tbx+j;
                    float4 posq2 = (float4) (localData[atom2].x, localData[atom2].y, localData[atom2].z, localData[atom2].q);
                    float4 delta = (float4) (posq2.xyz - posq1.xyz, 0.0f);
112
#ifdef USE_PERIODIC
113
114
115
                    delta.x -= floor(delta.x*invPeriodicBoxSize.x+0.5f)*periodicBoxSize.x;
                    delta.y -= floor(delta.y*invPeriodicBoxSize.y+0.5f)*periodicBoxSize.y;
                    delta.z -= floor(delta.z*invPeriodicBoxSize.z+0.5f)*periodicBoxSize.z;
116
#endif
117
118
119
120
121
                    float r2 = delta.x*delta.x + delta.y*delta.y + delta.z*delta.z;
                    float invR = RSQRT(r2);
                    float r = RECIP(invR);
                    LOAD_ATOM2_PARAMETERS
                    atom2 = y*TILE_SIZE+j;
122
#ifdef USE_SYMMETRIC
123
                    float dEdR = 0.0f;
124
#else
125
126
                    float4 dEdR1 = (float4) 0.0f;
                    float4 dEdR2 = (float4) 0.0f;
127
#endif
128
129
130
                    float tempEnergy = 0.0f;
                    COMPUTE_INTERACTION
                    energy += 0.5f*tempEnergy;
131
#ifdef USE_SYMMETRIC
132
                    force.xyz -= delta.xyz*dEdR;
133
#else
134
                    force.xyz -= dEdR1.xyz;
135
#endif
136
137
138
139
#ifdef USE_EXCLUSIONS
                    excl >>= 1;
#endif
                }
140
            }
141
142
            else {
                // This is an off-diagonal tile.
143

144
                const unsigned int localAtomIndex = get_local_id(0);
145
146
147
148
149
150
151
                unsigned int j = y*TILE_SIZE + tgx;
                float4 tempPosq = posq[j];
                localData[localAtomIndex].x = tempPosq.x;
                localData[localAtomIndex].y = tempPosq.y;
                localData[localAtomIndex].z = tempPosq.z;
                localData[localAtomIndex].q = tempPosq.w;
                LOAD_LOCAL_PARAMETERS_FROM_GLOBAL
152
153
154
155
156
157
158
159
160
161
162
                localData[localAtomIndex].fx = 0.0f;
                localData[localAtomIndex].fy = 0.0f;
                localData[localAtomIndex].fz = 0.0f;
#ifdef USE_CUTOFF
                unsigned int flags = (numTiles <= maxTiles ? interactionFlags[pos] : 0xFFFFFFFF);
                if (!hasExclusions && flags != 0xFFFFFFFF) {
                    if (flags == 0) {
                        // No interactions in this tile.
                    }
                    else {
                        // Compute only a subset of the interactions in this tile.
163

Peter Eastman's avatar
Peter Eastman committed
164
                        for (j = 0; j < TILE_SIZE; j++) {
165
166
167
                            if ((flags&(1<<j)) != 0) {
                                bool isExcluded = false;
                                int atom2 = tbx+j;
168
169
170
171
172
173
174
175
                                int bufferIndex = 3*get_local_id(0);
#ifdef USE_SYMMETRIC
                                float dEdR = 0.0f;
#else
                                float4 dEdR1 = (float4) 0.0f;
                                float4 dEdR2 = (float4) 0.0f;
#endif
                                float tempEnergy = 0.0f;
176
177
                                float4 posq2 = (float4) (localData[atom2].x, localData[atom2].y, localData[atom2].z, localData[atom2].q);
                                float4 delta = (float4) (posq2.xyz - posq1.xyz, 0.0f);
178
#ifdef USE_PERIODIC
179
180
181
                                delta.x -= floor(delta.x*invPeriodicBoxSize.x+0.5f)*periodicBoxSize.x;
                                delta.y -= floor(delta.y*invPeriodicBoxSize.y+0.5f)*periodicBoxSize.y;
                                delta.z -= floor(delta.z*invPeriodicBoxSize.z+0.5f)*periodicBoxSize.z;
182
#endif
183
                                float r2 = delta.x*delta.x + delta.y*delta.y + delta.z*delta.z;
184
185
186
187
188
189
190
191
192
193
194
#ifdef USE_CUTOFF
                                if (r2 < CUTOFF_SQUARED) {
#endif
                                    float invR = RSQRT(r2);
                                    float r = RECIP(invR);
                                    LOAD_ATOM2_PARAMETERS
                                    atom2 = y*TILE_SIZE+j;
                                    COMPUTE_INTERACTION
                                    energy += tempEnergy;
#ifdef USE_CUTOFF
                                }
195
196
#endif
#ifdef USE_SYMMETRIC
197
198
199
200
201
                                delta.xyz *= dEdR;
                                force.xyz -= delta.xyz;
                                tempBuffer[bufferIndex] = delta.x;
                                tempBuffer[bufferIndex+1] = delta.y;
                                tempBuffer[bufferIndex+2] = delta.z;
202
#else
203
204
205
206
                                force.xyz -= dEdR1.xyz;
                                tempBuffer[bufferIndex] = dEdR2.x;
                                tempBuffer[bufferIndex+1] = dEdR2.y;
                                tempBuffer[bufferIndex+2] = dEdR2.z;
207
#endif
208

209
                                // Sum the forces on atom2.
210

211
                                if (tgx % 4 == 0) {
212
213
214
                                    tempBuffer[bufferIndex] += tempBuffer[bufferIndex+3]+tempBuffer[bufferIndex+6]+tempBuffer[bufferIndex+9];
                                    tempBuffer[bufferIndex+1] += tempBuffer[bufferIndex+4]+tempBuffer[bufferIndex+7]+tempBuffer[bufferIndex+10];
                                    tempBuffer[bufferIndex+2] += tempBuffer[bufferIndex+5]+tempBuffer[bufferIndex+8]+tempBuffer[bufferIndex+11];
215
216
                                }
                                if (tgx == 0) {
217
218
219
                                    localData[tbx+j].fx += tempBuffer[bufferIndex]+tempBuffer[bufferIndex+12]+tempBuffer[bufferIndex+24]+tempBuffer[bufferIndex+36]+tempBuffer[bufferIndex+48]+tempBuffer[bufferIndex+60]+tempBuffer[bufferIndex+72]+tempBuffer[bufferIndex+84];
                                    localData[tbx+j].fy += tempBuffer[bufferIndex+1]+tempBuffer[bufferIndex+13]+tempBuffer[bufferIndex+25]+tempBuffer[bufferIndex+37]+tempBuffer[bufferIndex+49]+tempBuffer[bufferIndex+61]+tempBuffer[bufferIndex+73]+tempBuffer[bufferIndex+85];
                                    localData[tbx+j].fz += tempBuffer[bufferIndex+2]+tempBuffer[bufferIndex+14]+tempBuffer[bufferIndex+26]+tempBuffer[bufferIndex+38]+tempBuffer[bufferIndex+50]+tempBuffer[bufferIndex+62]+tempBuffer[bufferIndex+74]+tempBuffer[bufferIndex+86];
220
                                }
221
                            }
222
223
224
                        }
                    }
                }
225
                else
226
#endif
227
228
                {
                    // Compute the full set of interactions in this tile.
229

230
#ifdef USE_EXCLUSIONS
231
232
                    unsigned int excl = (hasExclusions ? exclusions[exclusionIndex[localGroupIndex]+tgx] : 0xFFFFFFFF);
                    excl = (excl >> tgx) | (excl << (TILE_SIZE - tgx));
233
#endif
234
                    unsigned int tj = tgx;
Peter Eastman's avatar
Peter Eastman committed
235
                    for (j = 0; j < TILE_SIZE; j++) {
236
#ifdef USE_EXCLUSIONS
237
                        bool isExcluded = !(excl & 0x1);
238
#endif
239
240
241
                        int atom2 = tbx+tj;
                        float4 posq2 = (float4) (localData[atom2].x, localData[atom2].y, localData[atom2].z, localData[atom2].q);
                        float4 delta = (float4) (posq2.xyz - posq1.xyz, 0.0f);
242
#ifdef USE_PERIODIC
243
244
245
                        delta.x -= floor(delta.x*invPeriodicBoxSize.x+0.5f)*periodicBoxSize.x;
                        delta.y -= floor(delta.y*invPeriodicBoxSize.y+0.5f)*periodicBoxSize.y;
                        delta.z -= floor(delta.z*invPeriodicBoxSize.z+0.5f)*periodicBoxSize.z;
246
#endif
247
                        float r2 = delta.x*delta.x + delta.y*delta.y + delta.z*delta.z;
248
249
250
251
252
253
254
#ifdef USE_CUTOFF
                        if (r2 < CUTOFF_SQUARED) {
#endif
                            float invR = RSQRT(r2);
                            float r = RECIP(invR);
                            LOAD_ATOM2_PARAMETERS
                            atom2 = y*TILE_SIZE+tj;
255
#ifdef USE_SYMMETRIC
256
                            float dEdR = 0.0f;
257
#else
258
259
                            float4 dEdR1 = (float4) 0.0f;
                            float4 dEdR2 = (float4) 0.0f;
260
#endif
261
262
263
                            float tempEnergy = 0.0f;
                            COMPUTE_INTERACTION
                            energy += tempEnergy;
264
#ifdef USE_SYMMETRIC
265
266
267
268
269
                            delta.xyz *= dEdR;
                            force.xyz -= delta.xyz;
                            localData[tbx+tj].fx += delta.x;
                            localData[tbx+tj].fy += delta.y;
                            localData[tbx+tj].fz += delta.z;
270
#else
271
272
273
274
275
276
277
                            force.xyz -= dEdR1.xyz;
                            localData[tbx+tj].fx += dEdR2.x;
                            localData[tbx+tj].fy += dEdR2.y;
                            localData[tbx+tj].fz += dEdR2.z;
#endif
#ifdef USE_CUTOFF
                        }
278
#endif
279
#ifdef USE_EXCLUSIONS
280
                        excl >>= 1;
Peter Eastman's avatar
Bug fix  
Peter Eastman committed
281
#endif
282
283
                        tj = (tj + 1) & (TILE_SIZE - 1);
                    }
284
285
                }
            }
286
287
288
289
290
        }
        
        // Write results.  We need to coordinate between warps to make sure no two of them
        // ever try to write to the same piece of memory at the same time.
        
291
292
293
294
295
296
297
298
299
300
301
302
303
304
#ifdef SUPPORTS_64_BIT_ATOMICS
        if (pos < end) {
            const unsigned int offset = x*TILE_SIZE + tgx;
            atom_add(&forceBuffers[offset], (long) (force.x*0xFFFFFFFF));
            atom_add(&forceBuffers[offset+PADDED_NUM_ATOMS], (long) (force.y*0xFFFFFFFF));
            atom_add(&forceBuffers[offset+2*PADDED_NUM_ATOMS], (long) (force.z*0xFFFFFFFF));
        }
        if (pos < end && x != y) {
            const unsigned int offset = y*TILE_SIZE + tgx;
            atom_add(&forceBuffers[offset], (long) (localData[get_local_id(0)].fx*0xFFFFFFFF));
            atom_add(&forceBuffers[offset+PADDED_NUM_ATOMS], (long) (localData[get_local_id(0)].fy*0xFFFFFFFF));
            atom_add(&forceBuffers[offset+2*PADDED_NUM_ATOMS], (long) (localData[get_local_id(0)].fz*0xFFFFFFFF));
        }
#else
305
306
307
308
309
310
311
312
313
        int writeX = (pos < end ? x : -1);
        int writeY = (pos < end && x != y ? y : -1);
        if (tgx == 0)
            reservedBlocks[localGroupIndex] = (int2)(writeX, writeY);
        bool done = false;
        int doneIndex = 0;
        int checkIndex = 0;
        while (true) {
            // See if any warp still needs to write its data.
314

315
316
317
318
319
320
321
322
323
324
325
326
327
            bool allDone = true;
            barrier(CLK_LOCAL_MEM_FENCE);
            while (doneIndex < WARPS_PER_GROUP && allDone) {
                if (reservedBlocks[doneIndex].x != -1)
                    allDone = false;
                else
                    doneIndex++;
            }
            if (allDone)
                break;
            if (!done) {
                // See whether this warp can write its data.  This requires that no previous warp
                // is trying to write to the same block of the buffer.
328

329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
                bool canWrite = (writeX != -1);
                while (checkIndex < localGroupIndex && canWrite) {
                    if ((reservedBlocks[checkIndex].x == x || reservedBlocks[checkIndex].y == x) ||
                            (writeY != -1 && (reservedBlocks[checkIndex].x == y || reservedBlocks[checkIndex].y == y)))
                        canWrite = false;
                    else
                        checkIndex++;
                }
                if (canWrite) {
                    // Write the data to global memory, then mark this warp as done.

                    if (writeX > -1) {
                        const unsigned int offset = x*TILE_SIZE + tgx + get_group_id(0)*PADDED_NUM_ATOMS;
                        forceBuffers[offset].xyz += force.xyz;
                    }
                    if (writeY > -1) {
                        const unsigned int offset = y*TILE_SIZE + tgx + get_group_id(0)*PADDED_NUM_ATOMS;
                        forceBuffers[offset] += (float4) (localData[get_local_id(0)].fx, localData[get_local_id(0)].fy, localData[get_local_id(0)].fz, 0.0f);
                    }
                    done = true;
                    if (tgx == 0)
                        reservedBlocks[localGroupIndex] = (int2)(-1, -1);
                }
            }
353
        }
354
#endif
355
        pos++;
356
    } while (pos < end);
357
358
    energyBuffer[get_global_id(0)] += energy;
}