customHbondForce.cc 9.04 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
DEVICE void findBoundingBox(GLOBAL const real4* RESTRICT posq, GLOBAL const int4* RESTRICT atoms, int numGroups,
        real4 periodicBoxSize, real4 invPeriodicBoxSize, real4 periodicBoxVecX, real4 periodicBoxVecY, real4 periodicBoxVecZ,
        GLOBAL real4* center, GLOBAL real4* blockSize) {
    real4 pos = posq[atoms[0].x];
#ifdef USE_PERIODIC
    APPLY_PERIODIC_TO_POS(pos)
#endif
    real4 minPos = pos;
    real4 maxPos = pos;
    for (int i = 1; i < numGroups; i++) {
        pos = posq[atoms[i].x];
#ifdef USE_PERIODIC
        real4 center = 0.5f*(maxPos+minPos);
        APPLY_PERIODIC_TO_POS_WITH_CENTER(pos, center)
#endif
        minPos = make_real4(min(minPos.x,pos.x), min(minPos.y,pos.y), min(minPos.z,pos.z), 0);
        maxPos = make_real4(max(maxPos.x,pos.x), max(maxPos.y,pos.y), max(maxPos.z,pos.z), 0);
    }
    *blockSize = 0.5f*(maxPos-minPos);
    *center = 0.5f*(maxPos+minPos);
}

KERNEL void findBlockBounds(GLOBAL const int4* RESTRICT donorAtoms, GLOBAL const int4* RESTRICT acceptorAtoms,
        real4 periodicBoxSize, real4 invPeriodicBoxSize, real4 periodicBoxVecX, real4 periodicBoxVecY, real4 periodicBoxVecZ,
        GLOBAL const real4* RESTRICT posq, GLOBAL real4* RESTRICT donorBlockCenter, GLOBAL real4* RESTRICT donorBlockSize,
        GLOBAL real4* RESTRICT acceptorBlockCenter, GLOBAL real4* RESTRICT acceptorBlockSize) {
    for (int index = GLOBAL_ID; index < NUM_DONOR_BLOCKS; index += GLOBAL_SIZE) {
        findBoundingBox(posq, donorAtoms+index*32, min(32, NUM_DONORS-index*32), periodicBoxSize,
                invPeriodicBoxSize, periodicBoxVecX, periodicBoxVecY, periodicBoxVecZ, donorBlockCenter+index,
                donorBlockSize+index);
    }
    for (int index = GLOBAL_ID; index < NUM_ACCEPTOR_BLOCKS; index += GLOBAL_SIZE) {
        findBoundingBox(posq, acceptorAtoms+index*32, min(32, NUM_ACCEPTORS-index*32), periodicBoxSize,
                invPeriodicBoxSize, periodicBoxVecX, periodicBoxVecY, periodicBoxVecZ, acceptorBlockCenter+index,
                acceptorBlockSize+index);
    }
}

39
/**
Peter Eastman's avatar
Peter Eastman committed
40
 * Compute the difference between two vectors, optionally taking periodic boundary conditions into account
41
42
 * and setting the fourth component to the squared magnitude.
 */
43
inline DEVICE real4 delta(real3 vec1, real3 vec2, real4 periodicBoxSize, real4 invPeriodicBoxSize, real4 periodicBoxVecX, real4 periodicBoxVecY, real4 periodicBoxVecZ) {
44
    real4 result = make_real4(vec1.x-vec2.x, vec1.y-vec2.y, vec1.z-vec2.z, 0);
45
#ifdef USE_PERIODIC
46
    APPLY_PERIODIC_TO_DELTA(result)
47
48
49
50
51
52
53
54
#endif
    result.w = result.x*result.x + result.y*result.y + result.z*result.z;
    return result;
}

/**
 * Compute the angle between two vectors.  The w component of each vector should contain the squared magnitude.
 */
55
inline DEVICE real computeAngle(real4 vec1, real4 vec2) {
56
57
58
    real dotProduct = vec1.x*vec2.x + vec1.y*vec2.y + vec1.z*vec2.z;
    real cosine = dotProduct*RSQRT(vec1.w*vec2.w);
    real angle;
59
60
61
    if (cosine > 0.99f || cosine < -0.99f) {
        // We're close to the singularity in acos(), so take the cross product and use asin() instead.

62
        real3 crossProduct = cross(trimTo3(vec1), trimTo3(vec2));
63
        real scale = vec1.w*vec2.w;
64
65
66
        angle = ASIN(SQRT(dot(crossProduct, crossProduct)/scale));
        if (cosine < 0)
            angle = M_PI-angle;
67
68
    }
    else
69
       angle = ACOS(cosine);
70
71
72
73
    return angle;
}

/**
74
 * Compute the cross product of two vectors, setting the fourth component to the squared magnitude.
75
 */
76
77
78
inline DEVICE real4 computeCross(real4 vec1, real4 vec2) {
    real3 cp = cross(trimTo3(vec1), trimTo3(vec2));
    return make_real4(cp.x, cp.y, cp.z, cp.x*cp.x+cp.y*cp.y+cp.z*cp.z);
79
}
80

81
/**
82
 * Write the force on an atom to global memory.
83
 */
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
inline DEVICE void applyForce(int atom, real3 f, GLOBAL mm_ulong* force) {
    if (atom > -1) {
        if (f.x != 0)
            ATOMIC_ADD(&force[atom], (mm_ulong) realToFixedPoint(f.x));
        if (f.y != 0)
            ATOMIC_ADD(&force[atom+PADDED_NUM_ATOMS], (mm_ulong) realToFixedPoint(f.y));
        if (f.z != 0)
            ATOMIC_ADD(&force[atom+2*PADDED_NUM_ATOMS], (mm_ulong) realToFixedPoint(f.z));
        MEM_FENCE;
    }
}

typedef struct {
    real3 pos1, pos2, pos3;
    real3 f1, f2, f3;
} AcceptorData;

/**
 * Compute forces on donors and acceptors.
 */
KERNEL void computeHbondForces(
105
106
107
	GLOBAL mm_ulong* RESTRICT force,
	GLOBAL mixed* RESTRICT energyBuffer, GLOBAL const real4* RESTRICT posq, GLOBAL const int4* RESTRICT exclusions,
        GLOBAL const int4* RESTRICT donorAtoms, GLOBAL const int4* RESTRICT acceptorAtoms, real4 periodicBoxSize, real4 invPeriodicBoxSize,
108
        real4 periodicBoxVecX, real4 periodicBoxVecY, real4 periodicBoxVecZ
109
110
111
112
#ifdef USE_BOUNDING_BOXES
        , GLOBAL real4* RESTRICT donorBlockCenter, GLOBAL real4* RESTRICT donorBlockSize,
        GLOBAL real4* RESTRICT acceptorBlockCenter, GLOBAL real4* RESTRICT acceptorBlockSize
#endif
113
        PARAMETER_ARGUMENTS) {
114
115
116
117
118
    const unsigned int totalWarps = GLOBAL_SIZE/32;
    const unsigned int warp = GLOBAL_ID/32;
    const int indexInWarp = GLOBAL_ID%32;
    const int tbx = LOCAL_ID-indexInWarp;
    LOCAL AcceptorData localData[THREAD_BLOCK_SIZE];
119
    mixed energy = 0;
120
    for (int tile = warp; tile < NUM_DONOR_BLOCKS*NUM_ACCEPTOR_BLOCKS; tile += totalWarps) {
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
        int donorBlock = tile/NUM_ACCEPTOR_BLOCKS;
        int acceptorBlock = tile%NUM_ACCEPTOR_BLOCKS;
#ifdef USE_BOUNDING_BOXES
        real4 blockDelta = donorBlockCenter[donorBlock]-acceptorBlockCenter[acceptorBlock];
#ifdef USE_PERIODIC
        APPLY_PERIODIC_TO_DELTA(blockDelta)
#endif
        real4 donorSize = donorBlockSize[donorBlock];
        real4 acceptorSize = acceptorBlockSize[acceptorBlock];
        blockDelta.x = max((real) 0, fabs(blockDelta.x)-donorSize.x-acceptorSize.x);
        blockDelta.y = max((real) 0, fabs(blockDelta.y)-donorSize.y-acceptorSize.y);
        blockDelta.z = max((real) 0, fabs(blockDelta.z)-donorSize.z-acceptorSize.z);
        if (blockDelta.x*blockDelta.x+blockDelta.y*blockDelta.y+blockDelta.z*blockDelta.z >= CUTOFF_SQUARED)
            continue;
#endif
136

137
138
        // Load information about the donor this thread will compute forces on.

139
140
141
        real3 f1 = make_real3(0);
        real3 f2 = make_real3(0);
        real3 f3 = make_real3(0);
142
        int donorIndex = donorBlock*32 + indexInWarp;
143
        int4 atoms, exclusionIndices;
144
        real3 d1, d2, d3;
145
146
        if (donorIndex < NUM_DONORS) {
            atoms = donorAtoms[donorIndex];
147
148
149
            d1 = (atoms.x > -1 ? trimTo3(posq[atoms.x]) : make_real3(0));
            d2 = (atoms.y > -1 ? trimTo3(posq[atoms.y]) : make_real3(0));
            d3 = (atoms.z > -1 ? trimTo3(posq[atoms.z]) : make_real3(0));
150
151
152
#ifdef USE_EXCLUSIONS
            exclusionIndices = exclusions[donorIndex];
#endif
153
154
        }
        else
155
            atoms = make_int4(-1, -1, -1, -1);
156

157
        // Load information about the acceptors into local memory.
158

159
160
161
162
        SYNC_WARPS;
        localData[LOCAL_ID].f1 = make_real3(0);
        localData[LOCAL_ID].f2 = make_real3(0);
        localData[LOCAL_ID].f3 = make_real3(0);
163
        int acceptorStart = acceptorBlock*32;
164
165
166
167
168
169
        int blockSize = min(32, NUM_ACCEPTORS-acceptorStart);
        int4 atoms2 = (indexInWarp < blockSize ? acceptorAtoms[acceptorStart+indexInWarp] : make_int4(-1));
        if (indexInWarp < blockSize) {
            localData[LOCAL_ID].pos1 = (atoms2.x > -1 ? trimTo3(posq[atoms2.x]) : make_real3(0));
            localData[LOCAL_ID].pos2 = (atoms2.y > -1 ? trimTo3(posq[atoms2.y]) : make_real3(0));
            localData[LOCAL_ID].pos3 = (atoms2.z > -1 ? trimTo3(posq[atoms2.z]) : make_real3(0));
170
        }
171
        SYNC_WARPS;
172
        if (donorIndex < NUM_DONORS) {
173
174
175
            int index = indexInWarp;
            for (int j = 0; j < 32; j++) {
                int acceptorIndex = acceptorStart+index;
176
#ifdef USE_EXCLUSIONS
177
178
179
                if (acceptorIndex < NUM_ACCEPTORS && acceptorIndex != exclusionIndices.x && acceptorIndex != exclusionIndices.y && acceptorIndex != exclusionIndices.z && acceptorIndex != exclusionIndices.w) {
#else
                if (acceptorIndex < NUM_ACCEPTORS) {
180
#endif
181
182
                    // Compute the interaction between a donor and an acceptor.

183
184
185
                    real3 a1 = localData[tbx+index].pos1;
                    real3 a2 = localData[tbx+index].pos2;
                    real3 a3 = localData[tbx+index].pos3;
Peter Eastman's avatar
Peter Eastman committed
186
                    real4 deltaD1A1 = delta(d1, a1, periodicBoxSize, invPeriodicBoxSize, periodicBoxVecX, periodicBoxVecY, periodicBoxVecZ);
187
188
#ifdef USE_CUTOFF
                    if (deltaD1A1.w < CUTOFF_SQUARED) {
189
#endif
190
                        COMPUTE_FORCE
191
#ifdef USE_CUTOFF
192
                    }
193
#endif
194
                }
195
                index = (index+1)%32;
196
197
198
199
200
            }
        }

        // Write results

201
202
203
204
        if (donorIndex < NUM_DONORS) {
            applyForce(atoms.x, f1, force);
            applyForce(atoms.y, f2, force);
            applyForce(atoms.z, f3, force);
205
        }
206
207
208
209
        SYNC_WARPS;
        applyForce(atoms2.x, localData[LOCAL_ID].f1, force);
        applyForce(atoms2.y, localData[LOCAL_ID].f2, force);
        applyForce(atoms2.z, localData[LOCAL_ID].f3, force);
210
    }
211
    energyBuffer[GLOBAL_ID] += energy;
212
}