Commit 695c066f authored by Peter Eastman's avatar Peter Eastman
Browse files

Created OpenCL implementation of RBTorsionForce.

parent 3d6e98d2
......@@ -49,6 +49,9 @@ typedef struct {
typedef struct {
cl_float x, y, z, w;
} mm_float4;
typedef struct {
cl_float s0, s1, s2, s3, s4, s5, s6, s7;
} mm_float8;
typedef struct {
cl_int x, y;
} mm_int2;
......
......@@ -43,8 +43,8 @@ KernelImpl* OpenCLKernelFactory::createKernelImpl(std::string name, const Platfo
return new OpenCLCalcHarmonicAngleForceKernel(name, platform, data, context.getSystem());
if (name == CalcPeriodicTorsionForceKernel::Name())
return new OpenCLCalcPeriodicTorsionForceKernel(name, platform, data, context.getSystem());
// if (name == CalcRBTorsionForceKernel::Name())
// return new OpenCLCalcRBTorsionForceKernel(name, platform, data, context.getSystem());
if (name == CalcRBTorsionForceKernel::Name())
return new OpenCLCalcRBTorsionForceKernel(name, platform, data, context.getSystem());
// if (name == CalcNonbondedForceKernel::Name())
// return new OpenCLCalcNonbondedForceKernel(name, platform, data, context.getSystem());
// if (name == CalcCustomNonbondedForceKernel::Name())
......
......@@ -367,48 +367,84 @@ double OpenCLCalcPeriodicTorsionForceKernel::executeEnergy(ContextImpl& context)
return 0.0;
}
//OpenCLCalcRBTorsionForceKernel::~OpenCLCalcRBTorsionForceKernel() {
//}
//
//void OpenCLCalcRBTorsionForceKernel::initialize(const System& system, const RBTorsionForce& force) {
// if (data.primaryKernel == NULL)
// data.primaryKernel = this;
// data.hasRB = true;
// numTorsions = force.getNumTorsions();
// vector<int> particle1(numTorsions);
// vector<int> particle2(numTorsions);
// vector<int> particle3(numTorsions);
// vector<int> particle4(numTorsions);
// vector<float> c0(numTorsions);
// vector<float> c1(numTorsions);
// vector<float> c2(numTorsions);
// vector<float> c3(numTorsions);
// vector<float> c4(numTorsions);
// vector<float> c5(numTorsions);
// for (int i = 0; i < numTorsions; i++) {
// double c[6];
// force.getTorsionParameters(i, particle1[i], particle2[i], particle3[i], particle4[i], c[0], c[1], c[2], c[3], c[4], c[5]);
// c0[i] = (float) c[0];
// c1[i] = (float) c[1];
// c2[i] = (float) c[2];
// c3[i] = (float) c[3];
// c4[i] = (float) c[4];
// c5[i] = (float) c[5];
// }
// gpuSetRbDihedralParameters(data.gpu, particle1, particle2, particle3, particle4, c0, c1, c2, c3, c4, c5);
//}
//
//void OpenCLCalcRBTorsionForceKernel::executeForces(ContextImpl& context) {
// if (data.primaryKernel == this)
// calcForces(context, data);
//}
//
//double OpenCLCalcRBTorsionForceKernel::executeEnergy(ContextImpl& context) {
// if (data.primaryKernel == this)
// return calcEnergy(context, data, system);
// return 0.0;
//}
//
class OpenCLRBTorsionForceInfo : public OpenCLForceInfo {
public:
OpenCLRBTorsionForceInfo(int requiredBuffers, const RBTorsionForce& force) : OpenCLForceInfo(requiredBuffers, false, 0.0), force(force) {
}
int getNumParticleGroups() {
return force.getNumTorsions();
}
void getParticlesInGroup(int index, std::vector<int>& particles) {
int particle1, particle2, particle3, particle4;
double c0, c1, c2, c3, c4, c5;
force.getTorsionParameters(index, particle1, particle2, particle3, particle4, c0, c1, c2, c3, c4, c5);
particles.resize(4);
particles[0] = particle1;
particles[1] = particle2;
particles[2] = particle3;
particles[3] = particle4;
}
bool areGroupsIdentical(int group1, int group2) {
int particle1, particle2, particle3, particle4;
double c0a, c0b, c1a, c1b, c2a, c2b, c3a, c3b, c4a, c4b, c5a, c5b;
force.getTorsionParameters(group1, particle1, particle2, particle3, particle4, c0a, c1a, c2a, c3a, c4a, c5a);
force.getTorsionParameters(group1, particle1, particle2, particle3, particle4, c0b, c1b, c2b, c3b, c4b, c5b);
return (c0a == c0b && c1a == c1b && c2a == c2b && c3a == c3b && c4a == c4b && c5a == c5b);
}
private:
const RBTorsionForce& force;
};
OpenCLCalcRBTorsionForceKernel::~OpenCLCalcRBTorsionForceKernel() {
if (params != NULL)
delete params;
if (indices != NULL)
delete indices;
}
void OpenCLCalcRBTorsionForceKernel::initialize(const System& system, const RBTorsionForce& force) {
numTorsions = force.getNumTorsions();
params = new OpenCLArray<mm_float8>(*data.context, numTorsions, "rbTorsionParams");
indices = new OpenCLArray<mm_int8>(*data.context, numTorsions, "rbTorsionIndices");
vector<int> forceBufferCounter(system.getNumParticles(), 0);
vector<mm_float8> paramVector(numTorsions);
vector<mm_int8> indicesVector(numTorsions);
for (int i = 0; i < numTorsions; i++) {
int particle1, particle2, particle3, particle4;
double c0, c1, c2, c3, c4, c5;
force.getTorsionParameters(i, particle1, particle2, particle3, particle4, c0, c1, c2, c3, c4, c5);
paramVector[i] = (mm_float8) {c0, c1, c2, c3, c4, c5};
indicesVector[i] = (mm_int8) {particle1, particle2, particle3, particle4,
forceBufferCounter[particle1]++, forceBufferCounter[particle2]++, forceBufferCounter[particle3]++, forceBufferCounter[particle4]++};
}
params->upload(paramVector);
indices->upload(indicesVector);
int maxBuffers = 1;
for (int i = 0; i < forceBufferCounter.size(); i++) {
maxBuffers = max(maxBuffers, forceBufferCounter[i]);
}
data.context->addForce(new OpenCLRBTorsionForceInfo(maxBuffers, force));
cl::Program program = data.context->createProgram(data.context->loadSourceFromFile("rbTorsionForce.cl"));
kernel = cl::Kernel(program, "calcRBTorsionForce");
}
void OpenCLCalcRBTorsionForceKernel::executeForces(ContextImpl& context) {
kernel.setArg<cl_int>(0, data.context->getPaddedNumAtoms());
kernel.setArg<cl_int>(1, numTorsions);
kernel.setArg<cl::Buffer>(2, data.context->getForceBuffers().getDeviceBuffer());
kernel.setArg<cl::Buffer>(3, data.context->getEnergyBuffer().getDeviceBuffer());
kernel.setArg<cl::Buffer>(4, data.context->getPosq().getDeviceBuffer());
kernel.setArg<cl::Buffer>(5, params->getDeviceBuffer());
kernel.setArg<cl::Buffer>(6, indices->getDeviceBuffer());
data.context->executeKernel(kernel, numTorsions);
}
double OpenCLCalcRBTorsionForceKernel::executeEnergy(ContextImpl& context) {
executeForces(context);
return 0.0;
}
//OpenCLCalcNonbondedForceKernel::~OpenCLCalcNonbondedForceKernel() {
//}
//
......
......@@ -257,40 +257,43 @@ private:
cl::Kernel kernel;
};
///**
// * This kernel is invoked by RBTorsionForce to calculate the forces acting on the system and the energy of the system.
// */
//class OpenCLCalcRBTorsionForceKernel : public CalcRBTorsionForceKernel {
//public:
// OpenCLCalcRBTorsionForceKernel(std::string name, const Platform& platform, OpenCLPlatform::PlatformData& data, System& system) : CalcRBTorsionForceKernel(name, platform), data(data), system(system) {
// }
// ~OpenCLCalcRBTorsionForceKernel();
// /**
// * Initialize the kernel.
// *
// * @param system the System this kernel will be applied to
// * @param force the RBTorsionForce this kernel will be used for
// */
// void initialize(const System& system, const RBTorsionForce& force);
// /**
// * Execute the kernel to calculate the forces.
// *
// * @param context the context in which to execute this kernel
// */
// void executeForces(ContextImpl& context);
// /**
// * Execute the kernel to calculate the energy.
// *
// * @param context the context in which to execute this kernel
// * @return the potential energy due to the RBTorsionForce
// */
// double executeEnergy(ContextImpl& context);
//private:
// int numTorsions;
// OpenCLPlatform::PlatformData& data;
// System& system;
//};
//
/**
* This kernel is invoked by RBTorsionForce to calculate the forces acting on the system and the energy of the system.
*/
class OpenCLCalcRBTorsionForceKernel : public CalcRBTorsionForceKernel {
public:
OpenCLCalcRBTorsionForceKernel(std::string name, const Platform& platform, OpenCLPlatform::PlatformData& data, System& system) : CalcRBTorsionForceKernel(name, platform), data(data), system(system) {
}
~OpenCLCalcRBTorsionForceKernel();
/**
* Initialize the kernel.
*
* @param system the System this kernel will be applied to
* @param force the RBTorsionForce this kernel will be used for
*/
void initialize(const System& system, const RBTorsionForce& force);
/**
* Execute the kernel to calculate the forces.
*
* @param context the context in which to execute this kernel
*/
void executeForces(ContextImpl& context);
/**
* Execute the kernel to calculate the energy.
*
* @param context the context in which to execute this kernel
* @return the potential energy due to the RBTorsionForce
*/
double executeEnergy(ContextImpl& context);
private:
int numTorsions;
OpenCLPlatform::PlatformData& data;
System& system;
OpenCLArray<mm_float8>* params;
OpenCLArray<mm_int8>* indices;
cl::Kernel kernel;
};
///**
// * This kernel is invoked by NonbondedForce to calculate the forces acting on the system.
// */
......
......@@ -50,7 +50,7 @@ OpenCLPlatform::OpenCLPlatform() {
registerKernelFactory(CalcHarmonicBondForceKernel::Name(), factory);
registerKernelFactory(CalcHarmonicAngleForceKernel::Name(), factory);
registerKernelFactory(CalcPeriodicTorsionForceKernel::Name(), factory);
// registerKernelFactory(CalcRBTorsionForceKernel::Name(), factory);
registerKernelFactory(CalcRBTorsionForceKernel::Name(), factory);
// registerKernelFactory(CalcNonbondedForceKernel::Name(), factory);
// registerKernelFactory(CalcCustomNonbondedForceKernel::Name(), factory);
// registerKernelFactory(CalcGBSAOBCForceKernel::Name(), factory);
......
......@@ -25,6 +25,7 @@ __kernel void calcPeriodicTorsionForce(int numAtoms, int numTorsions, __global f
float cosangle = dot(normalize(cp0), normalize(cp1));
cosangle = clamp(cosangle, -1.0f, 1.0f);
float dihedralAngle = acos(cosangle);
dihedralAngle = (dot(v0, cp1) >= 0 ? dihedralAngle : -dihedralAngle);
float deltaAngle = torsionParams.z*dihedralAngle-torsionParams.y;
energy += torsionParams.x*(1.0f+cos(deltaAngle));
float sinDeltaAngle = sin(deltaAngle);
......
/**
* Evaluate the forces due to Ryckaert-Bellemans torsions.
*/
__kernel void calcRBTorsionForce(int numAtoms, int numTorsions, __global float4* forceBuffers, __global float* energyBuffer, __global float4* posq, __global float8* params, __global int8* indices) {
int index = get_global_id(0);
float energy = 0.0f;
const float PI = 3.14159265358979323846f;
while (index < numTorsions) {
// Look up the data for this torsion.
int8 atoms = indices[index];
float8 torsionParams = params[index];
float4 a1 = posq[atoms.s0];
float4 a2 = posq[atoms.s1];
float4 a3 = posq[atoms.s2];
float4 a4 = posq[atoms.s3];
// Compute the force.
float4 v0 = (float4) (a1.xyz-a2.xyz, 0.0f);
float4 v1 = (float4) (a3.xyz-a2.xyz, 0.0f);
float4 v2 = (float4) (a3.xyz-a4.xyz, 0.0f);
float4 cp0 = cross(v0, v1);
float4 cp1 = cross(v1, v2);
float cosangle = dot(normalize(cp0), normalize(cp1));
cosangle = clamp(cosangle, -1.0f, 1.0f);
float dihedralAngle = acos(cosangle);
dihedralAngle = (dot(v0, cp1) >= 0 ? dihedralAngle : -dihedralAngle);
if (dihedralAngle < 0.0f)
dihedralAngle += PI;
else
dihedralAngle -= PI;
cosangle = -cosangle;
float cosFactor = cosangle;
float dEdAngle = -torsionParams.s1;
float rbEnergy = torsionParams.s0;
rbEnergy += torsionParams.s1*cosFactor;
dEdAngle -= 2.0f*torsionParams.s2*cosFactor;
cosFactor *= cosangle;
dEdAngle -= 3.0f*torsionParams.s3*cosFactor;
rbEnergy += torsionParams.s2*cosFactor;
cosFactor *= cosangle;
dEdAngle -= 4.0f*torsionParams.s4*cosFactor;
rbEnergy += torsionParams.s3*cosFactor;
cosFactor *= cosangle;
dEdAngle -= 5.0f*torsionParams.s5*cosFactor;
rbEnergy += torsionParams.s4*cosFactor;
rbEnergy += torsionParams.s5*cosFactor*cosangle;
energy += rbEnergy;
dEdAngle *= sin(dihedralAngle);
float normCross1 = dot(cp0, cp0);
float normBC = sqrt(dot(v1, v1));
float normCross2 = dot(cp1, cp1);
float dp = 1.0f/normBC;
float4 ff = (float4) ((-dEdAngle*normBC)/normCross1, dot(v0, v1)*dp, dot(v2, v1)*dp, (dEdAngle*normBC)/normCross2);
float4 internalF0 = ff.x*cp0;
float4 internalF3 = ff.w*cp1;
float4 s = ff.y*internalF0 - ff.z*internalF3;
// Record the force on each of the four atoms.
unsigned int offsetA = atoms.s0+atoms.s4*numAtoms;
unsigned int offsetB = atoms.s1+atoms.s5*numAtoms;
unsigned int offsetC = atoms.s2+atoms.s6*numAtoms;
unsigned int offsetD = atoms.s3+atoms.s7*numAtoms;
float4 forceA = forceBuffers[offsetA];
float4 forceB = forceBuffers[offsetB];
float4 forceC = forceBuffers[offsetC];
float4 forceD = forceBuffers[offsetD];
forceA.xyz += internalF0.xyz;
forceB.xyz += s.xyz-internalF0.xyz;
forceC.xyz += -s.xyz-internalF3.xyz;
forceD.xyz += internalF3.xyz;
forceBuffers[offsetA] = forceA;
forceBuffers[offsetB] = forceB;
forceBuffers[offsetC] = forceC;
forceBuffers[offsetD] = forceD;
index += get_global_size(0);
}
energyBuffer[get_global_id(0)] += energy;
}
/* -------------------------------------------------------------------------- *
* 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. *
* *
* Portions copyright (c) 2008-2009 Stanford University and the Authors. *
* Authors: Peter Eastman *
* Contributors: *
* *
* Permission is hereby granted, free of charge, to any person obtaining a *
* copy of this software and associated documentation files (the "Software"), *
* to deal in the Software without restriction, including without limitation *
* the rights to use, copy, modify, merge, publish, distribute, sublicense, *
* and/or sell copies of the Software, and to permit persons to whom the *
* Software is furnished to do so, subject to the following conditions: *
* *
* The above copyright notice and this permission notice shall be included in *
* all copies or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
* THE AUTHORS, CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, *
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR *
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE *
* USE OR OTHER DEALINGS IN THE SOFTWARE. *
* -------------------------------------------------------------------------- */
/**
* This tests the OpenCL implementation of RBTorsionForce.
*/
#include "../../../tests/AssertionUtilities.h"
#include "openmm/Context.h"
#include "OpenCLPlatform.h"
#include "openmm/RBTorsionForce.h"
#include "openmm/System.h"
#include "openmm/VerletIntegrator.h"
#include "../src/SimTKUtilities/SimTKOpenMMRealType.h"
#include <iostream>
#include <vector>
using namespace OpenMM;
using namespace std;
const double TOL = 1e-5;
void testRBTorsions() {
OpenCLPlatform platform;
System system;
system.addParticle(1.0);
system.addParticle(1.0);
system.addParticle(1.0);
system.addParticle(1.0);
VerletIntegrator integrator(0.01);
RBTorsionForce* forceField = new RBTorsionForce();
forceField->addTorsion(0, 1, 2, 3, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6);
system.addForce(forceField);
Context context(system, integrator, platform);
vector<Vec3> positions(4);
positions[0] = Vec3(0, 1, 0);
positions[1] = Vec3(0, 0, 0);
positions[2] = Vec3(1, 0, 0);
positions[3] = Vec3(1, 1, 1);
context.setPositions(positions);
State state = context.getState(State::Forces | State::Energy);
const vector<Vec3>& forces = state.getForces();
double psi = 0.25*PI_M - PI_M;
double torque = 0.0;
for (int i = 1; i < 6; ++i) {
double c = 0.1*(i+1);
torque += -c*i*std::pow(std::cos(psi), i-1)*std::sin(psi);
}
ASSERT_EQUAL_VEC(Vec3(0, 0, torque), forces[0], TOL);
ASSERT_EQUAL_VEC(Vec3(0, 0.5*torque, -0.5*torque), forces[3], TOL);
ASSERT_EQUAL_VEC(Vec3(forces[0][0]+forces[1][0]+forces[2][0]+forces[3][0], forces[0][1]+forces[1][1]+forces[2][1]+forces[3][1], forces[0][2]+forces[1][2]+forces[2][2]+forces[3][2]), Vec3(0, 0, 0), TOL);
double energy = 0.0;
for (int i = 0; i < 6; ++i) {
double c = 0.1*(i+1);
energy += c*std::pow(std::cos(psi), i);
}
ASSERT_EQUAL_TOL(energy, state.getPotentialEnergy(), TOL);
}
int main() {
try {
testRBTorsions();
}
catch(const exception& e) {
cout << "exception: " << e.what() << endl;
return 1;
}
cout << "Done" << endl;
return 0;
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment