"vscode:/vscode.git/clone" did not exist on "f2bdcc7579668f767206b1bebedef0d12a870c6d"
Commit 4af689c5 authored by Peter Eastman's avatar Peter Eastman
Browse files

Created OpenCL version of CustomAngleForce

parent eac50971
......@@ -44,6 +44,8 @@ KernelImpl* OpenCLKernelFactory::createKernelImpl(std::string name, const Platfo
return new OpenCLCalcCustomBondForceKernel(name, platform, cl, context.getSystem());
if (name == CalcHarmonicAngleForceKernel::Name())
return new OpenCLCalcHarmonicAngleForceKernel(name, platform, cl, context.getSystem());
if (name == CalcCustomAngleForceKernel::Name())
return new OpenCLCalcCustomAngleForceKernel(name, platform, cl, context.getSystem());
if (name == CalcPeriodicTorsionForceKernel::Name())
return new OpenCLCalcPeriodicTorsionForceKernel(name, platform, cl, context.getSystem());
if (name == CalcRBTorsionForceKernel::Name())
......
......@@ -484,6 +484,161 @@ double OpenCLCalcHarmonicAngleForceKernel::executeEnergy(ContextImpl& context) {
return 0.0;
}
class OpenCLCustomAngleForceInfo : public OpenCLForceInfo {
public:
OpenCLCustomAngleForceInfo(int requiredBuffers, const CustomAngleForce& force) : OpenCLForceInfo(requiredBuffers), force(force) {
}
int getNumParticleGroups() {
return force.getNumAngles();
}
void getParticlesInGroup(int index, std::vector<int>& particles) {
int particle1, particle2, particle3;
vector<double> parameters;
force.getAngleParameters(index, particle1, particle2, particle3, parameters);
particles.resize(3);
particles[0] = particle1;
particles[1] = particle2;
particles[2] = particle3;
}
bool areGroupsIdentical(int group1, int group2) {
int particle1, particle2, particle3;
vector<double> parameters1, parameters2;
force.getAngleParameters(group1, particle1, particle2, particle3, parameters1);
force.getAngleParameters(group2, particle1, particle2, particle3, parameters2);
for (int i = 0; i < (int) parameters1.size(); i++)
if (parameters1[i] != parameters2[i])
return false;
return true;
}
private:
const CustomAngleForce& force;
};
OpenCLCalcCustomAngleForceKernel::~OpenCLCalcCustomAngleForceKernel() {
if (params != NULL)
delete params;
if (indices != NULL)
delete indices;
if (globals != NULL)
delete globals;
}
void OpenCLCalcCustomAngleForceKernel::initialize(const System& system, const CustomAngleForce& force) {
numAngles = force.getNumAngles();
if (numAngles == 0)
return;
params = new OpenCLParameterSet(cl, force.getNumPerAngleParameters(), numAngles, "customAngleParams");
indices = new OpenCLArray<mm_int8>(cl, numAngles, "customAngleIndices");
string extraArguments;
if (force.getNumGlobalParameters() > 0) {
globals = new OpenCLArray<cl_float>(cl, force.getNumGlobalParameters(), "customAngleGlobals", false, CL_MEM_READ_ONLY);
extraArguments += ", __constant float* globals";
}
vector<int> forceBufferCounter(system.getNumParticles(), 0);
vector<vector<cl_float> > paramVector(numAngles);
vector<mm_int8> indicesVector(numAngles);
for (int i = 0; i < numAngles; i++) {
int particle1, particle2, particle3;
vector<double> parameters;
force.getAngleParameters(i, particle1, particle2, particle3, parameters);
paramVector[i].resize(parameters.size());
for (int j = 0; j < (int) parameters.size(); j++)
paramVector[i][j] = (cl_float) parameters[j];
indicesVector[i] = mm_int8(particle1, particle2, particle3, forceBufferCounter[particle1]++,
forceBufferCounter[particle2]++, forceBufferCounter[particle3]++, 0, 0);
}
params->setParameterValues(paramVector);
indices->upload(indicesVector);
int maxBuffers = 1;
for (int i = 0; i < (int) forceBufferCounter.size(); i++)
maxBuffers = max(maxBuffers, forceBufferCounter[i]);
cl.addForce(new OpenCLCustomAngleForceInfo(maxBuffers, force));
// Record information for the expressions.
vector<string> paramNames;
for (int i = 0; i < force.getNumPerAngleParameters(); i++)
paramNames.push_back(force.getPerAngleParameterName(i));
globalParamNames.resize(force.getNumGlobalParameters());
globalParamValues.resize(force.getNumGlobalParameters());
for (int i = 0; i < force.getNumGlobalParameters(); i++) {
globalParamNames[i] = force.getGlobalParameterName(i);
globalParamValues[i] = (cl_float) force.getGlobalParameterDefaultValue(i);
}
if (globals != NULL)
globals->upload(globalParamValues);
Lepton::ParsedExpression energyExpression = Lepton::Parser::parse(force.getEnergyFunction()).optimize();
Lepton::ParsedExpression forceExpression = energyExpression.differentiate("theta").optimize();
map<string, Lepton::ParsedExpression> expressions;
expressions["energy += "] = energyExpression;
expressions["float dEdR = "] = forceExpression;
// Create the kernels.
map<string, string> variables;
variables["theta"] = "theta";
for (int i = 0; i < force.getNumPerAngleParameters(); i++) {
const string& name = force.getPerAngleParameterName(i);
variables[name] = "angleParams"+params->getParameterSuffix(i);
}
for (int i = 0; i < force.getNumGlobalParameters(); i++) {
const string& name = force.getGlobalParameterName(i);
string value = "globals["+intToString(i)+"]";
variables[name] = value;
}
stringstream compute;
for (int i = 0; i < (int) params->getBuffers().size(); i++) {
const OpenCLNonbondedUtilities::ParameterInfo& buffer = params->getBuffers()[i];
extraArguments += ", __global "+buffer.getType()+"* "+buffer.getName();
compute<<buffer.getType()<<" angleParams"<<(i+1)<<" = "<<buffer.getName()<<"[index];\n";
}
vector<pair<string, string> > functions;
compute << OpenCLExpressionUtilities::createExpressions(expressions, variables, functions, "temp", "");
map<string, string> replacements;
replacements["COMPUTE_FORCE"] = compute.str();
replacements["EXTRA_ARGUMENTS"] = extraArguments;
cl::Program program = cl.createProgram(cl.replaceStrings(OpenCLKernelSources::customAngleForce, replacements));
kernel = cl::Kernel(program, "computeCustomAngleForces");
}
void OpenCLCalcCustomAngleForceKernel::executeForces(ContextImpl& context) {
if (numAngles == 0)
return;
if (globals != NULL) {
bool changed = false;
for (int i = 0; i < (int) globalParamNames.size(); i++) {
cl_float value = (cl_float) context.getParameter(globalParamNames[i]);
if (value != globalParamValues[i])
changed = true;
globalParamValues[i] = value;
}
if (changed)
globals->upload(globalParamValues);
}
if (!hasInitializedKernel) {
hasInitializedKernel = true;
kernel.setArg<cl_int>(0, cl.getPaddedNumAtoms());
kernel.setArg<cl_int>(1, numAngles);
kernel.setArg<cl::Buffer>(2, cl.getForceBuffers().getDeviceBuffer());
kernel.setArg<cl::Buffer>(3, cl.getEnergyBuffer().getDeviceBuffer());
kernel.setArg<cl::Buffer>(4, cl.getPosq().getDeviceBuffer());
kernel.setArg<cl::Buffer>(5, indices->getDeviceBuffer());
int nextIndex = 6;
if (globals != NULL)
kernel.setArg<cl::Buffer>(nextIndex++, globals->getDeviceBuffer());
for (int i = 0; i < (int) params->getBuffers().size(); i++) {
const OpenCLNonbondedUtilities::ParameterInfo& buffer = params->getBuffers()[i];
kernel.setArg<cl::Buffer>(nextIndex++, buffer.getBuffer());
}
}
cl.executeKernel(kernel, numAngles);
}
double OpenCLCalcCustomAngleForceKernel::executeEnergy(ContextImpl& context) {
executeForces(context);
return 0.0;
}
class OpenCLPeriodicTorsionForceInfo : public OpenCLForceInfo {
public:
OpenCLPeriodicTorsionForceInfo(int requiredBuffers, const PeriodicTorsionForce& force) : OpenCLForceInfo(requiredBuffers), force(force) {
......
......@@ -268,6 +268,48 @@ private:
cl::Kernel kernel;
};
/**
* This kernel is invoked by CustomAngleForce to calculate the forces acting on the system and the energy of the system.
*/
class OpenCLCalcCustomAngleForceKernel : public CalcCustomAngleForceKernel {
public:
OpenCLCalcCustomAngleForceKernel(std::string name, const Platform& platform, OpenCLContext& cl, System& system) : CalcCustomAngleForceKernel(name, platform),
hasInitializedKernel(false), cl(cl), system(system), params(NULL), indices(NULL), globals(NULL) {
}
~OpenCLCalcCustomAngleForceKernel();
/**
* Initialize the kernel.
*
* @param system the System this kernel will be applied to
* @param force the CustomAngleForce this kernel will be used for
*/
void initialize(const System& system, const CustomAngleForce& 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 CustomAngleForce
*/
double executeEnergy(ContextImpl& context);
private:
int numAngles;
bool hasInitializedKernel;
OpenCLContext& cl;
System& system;
OpenCLParameterSet* params;
OpenCLArray<mm_int8>* indices;
OpenCLArray<cl_float>* globals;
std::vector<std::string> globalParamNames;
std::vector<cl_float> globalParamValues;
cl::Kernel kernel;
};
/**
* This kernel is invoked by PeriodicTorsionForce to calculate the forces acting on the system and the energy of the system.
*/
......
......@@ -49,6 +49,7 @@ OpenCLPlatform::OpenCLPlatform() {
registerKernelFactory(CalcHarmonicBondForceKernel::Name(), factory);
registerKernelFactory(CalcCustomBondForceKernel::Name(), factory);
registerKernelFactory(CalcHarmonicAngleForceKernel::Name(), factory);
registerKernelFactory(CalcCustomAngleForceKernel::Name(), factory);
registerKernelFactory(CalcPeriodicTorsionForceKernel::Name(), factory);
registerKernelFactory(CalcRBTorsionForceKernel::Name(), factory);
registerKernelFactory(CalcNonbondedForceKernel::Name(), factory);
......
/**
* Compute custom angle forces.
*/
__kernel void computeCustomAngleForces(int numAtoms, int numAngles, __global float4* forceBuffers, __global float* energyBuffer,
__global float4* posq, __global int8* indices
EXTRA_ARGUMENTS) {
float energy = 0.0f;
for (int index = get_global_id(0); index < numAngles; index += get_global_size(0)) {
int8 atoms = indices[index];
float4 a1 = posq[atoms.s0];
float4 a2 = posq[atoms.s1];
float4 a3 = posq[atoms.s2];
// Compute the force.
float4 v0 = a2-a1;
float4 v1 = a2-a3;
float4 cp = cross(v0, v1);
float rp = cp.x*cp.x + cp.y*cp.y + cp.z*cp.z;
rp = max(sqrt(rp), 1.0e-06f);
float r21 = v0.x*v0.x + v0.y*v0.y + v0.z*v0.z;
float r23 = v1.x*v1.x + v1.y*v1.y + v1.z*v1.z;
float dot = v0.x*v1.x + v0.y*v1.y + v0.z*v1.z;
float cosine = dot/sqrt(r21*r23);
float theta = acos(cosine);
COMPUTE_FORCE
float4 c21 = cross(v0, cp)*(dEdR/(r21*rp));
float4 c23 = cross(cp, v1)*(dEdR/(r23*rp));
// Record the force on each of the three atoms.
unsigned int offsetA = atoms.s0+atoms.s3*numAtoms;
unsigned int offsetB = atoms.s1+atoms.s4*numAtoms;
unsigned int offsetC = atoms.s2+atoms.s5*numAtoms;
float4 forceA = forceBuffers[offsetA];
float4 forceB = forceBuffers[offsetB];
float4 forceC = forceBuffers[offsetC];
forceA.xyz += c21.xyz;
forceB.xyz -= c21.xyz+c23.xyz;
forceC.xyz += c23.xyz;
forceBuffers[offsetA] = forceA;
forceBuffers[offsetB] = forceB;
forceBuffers[offsetC] = forceC;
}
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-2010 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 CustomAngleForce.
*/
#include "../../../tests/AssertionUtilities.h"
#include "openmm/Context.h"
#include "OpenCLPlatform.h"
#include "openmm/CustomAngleForce.h"
#include "openmm/HarmonicAngleForce.h"
#include "openmm/System.h"
#include "openmm/VerletIntegrator.h"
#include "../src/sfmt/SFMT.h"
#include <iostream>
#include <vector>
using namespace OpenMM;
using namespace std;
const double TOL = 1e-5;
void testAngles() {
OpenCLPlatform platform;
// Create a system using a CustomAngleForce.
System customSystem;
customSystem.addParticle(1.0);
customSystem.addParticle(1.0);
customSystem.addParticle(1.0);
customSystem.addParticle(1.0);
CustomAngleForce* custom = new CustomAngleForce("scale*k*(theta-theta0)^2");
custom->addPerAngleParameter("theta0");
custom->addPerAngleParameter("k");
custom->addGlobalParameter("scale", 0.5);
vector<double> parameters(2);
parameters[0] = 1.5;
parameters[1] = 0.8;
custom->addAngle(0, 1, 2, parameters);
parameters[0] = 2.0;
parameters[1] = 0.5;
custom->addAngle(1, 2, 3, parameters);
customSystem.addForce(custom);
// Create an identical system using a HarmonicAngleForce.
System harmonicSystem;
harmonicSystem.addParticle(1.0);
harmonicSystem.addParticle(1.0);
harmonicSystem.addParticle(1.0);
harmonicSystem.addParticle(1.0);
VerletIntegrator integrator(0.01);
HarmonicAngleForce* harmonic = new HarmonicAngleForce();
harmonic->addAngle(0, 1, 2, 1.5, 0.8);
harmonic->addAngle(1, 2, 3, 2.0, 0.5);
harmonicSystem.addForce(harmonic);
// Set the atoms in various positions, and verify that both systems give identical forces and energy.
init_gen_rand(0);
vector<Vec3> positions(4);
VerletIntegrator integrator1(0.01);
VerletIntegrator integrator2(0.01);
for (int i = 0; i < 10; i++) {
Context c1(customSystem, integrator1, platform);
Context c2(harmonicSystem, integrator2, platform);
for (int j = 0; j < (int) positions.size(); j++)
positions[j] = Vec3(5.0*genrand_real2(), 5.0*genrand_real2(), 5.0*genrand_real2());
c1.setPositions(positions);
c2.setPositions(positions);
State s1 = c1.getState(State::Forces | State::Energy);
State s2 = c2.getState(State::Forces | State::Energy);
const vector<Vec3>& forces = s1.getForces();
ASSERT_EQUAL_VEC(s1.getForces()[0], s2.getForces()[0], TOL);
ASSERT_EQUAL_VEC(s1.getForces()[1], s2.getForces()[1], TOL);
ASSERT_EQUAL_VEC(s1.getForces()[2], s2.getForces()[2], TOL);
ASSERT_EQUAL_TOL(s1.getPotentialEnergy(), s2.getPotentialEnergy(), TOL);
}
}
int main() {
try {
testAngles();
}
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