Commit 299b07a0 authored by Peter Eastman's avatar Peter Eastman
Browse files

Began implementing long range correction for CustomNonbondedForce

parent 6a951ba2
...@@ -170,7 +170,7 @@ public: ...@@ -170,7 +170,7 @@ public:
* *
* @param name the name of the parameter to get * @param name the name of the parameter to get
*/ */
double getParameter(const std::string& name); double getParameter(const std::string& name) const;
/** /**
* Set the value of an adjustable parameter defined by a Force object in the System. * Set the value of an adjustable parameter defined by a Force object in the System.
* *
......
...@@ -178,6 +178,34 @@ public: ...@@ -178,6 +178,34 @@ public:
* @param distance the cutoff distance, measured in nm * @param distance the cutoff distance, measured in nm
*/ */
void setCutoffDistance(double distance); void setCutoffDistance(double distance);
/**
* Get whether a switching is applied to the interaction.
*/
bool getUseSwitchingFunction() const;
/**
* Set whether a switching is applied to the interaction.
*/
void setUseSwitchingFunction(bool use);
/**
* Get the distance at which the switching function begins to reduce the interaction. This must be
* less than the cutoff distance.
*/
double getSwitchingDistance() const;
/**
* Set the distance at which the switching function begins to reduce the interaction. This must be
* less than the cutoff distance.
*/
void setSwitchingDistance(double distance);
/**
* Get whether to add a correction to the energy to compensate for the cutoff and switching function.
* This has no effect if periodic boundary conditions are not used.
*/
bool getUseLongRangeCorrection() const;
/**
* Set whether to add a correction to the energy to compensate for the cutoff and switching function.
* This has no effect if periodic boundary conditions are not used.
*/
void setUseLongRangeCorrection(bool use);
/** /**
* Add a new per-particle parameter that the interaction may depend on. * Add a new per-particle parameter that the interaction may depend on.
* *
...@@ -335,7 +363,8 @@ private: ...@@ -335,7 +363,8 @@ private:
class ExclusionInfo; class ExclusionInfo;
class FunctionInfo; class FunctionInfo;
NonbondedMethod nonbondedMethod; NonbondedMethod nonbondedMethod;
double cutoffDistance; double cutoffDistance, switchingDistance;
bool useSwitchingFunction, useLongRangeCorrection;
std::string energyExpression; std::string energyExpression;
std::vector<PerParticleParameterInfo> parameters; std::vector<PerParticleParameterInfo> parameters;
std::vector<GlobalParameterInfo> globalParameters; std::vector<GlobalParameterInfo> globalParameters;
......
...@@ -35,6 +35,7 @@ ...@@ -35,6 +35,7 @@
#include "ForceImpl.h" #include "ForceImpl.h"
#include "openmm/CustomNonbondedForce.h" #include "openmm/CustomNonbondedForce.h"
#include "openmm/Kernel.h" #include "openmm/Kernel.h"
#include "lepton/ExpressionProgram.h"
#include <utility> #include <utility>
#include <map> #include <map>
#include <string> #include <string>
...@@ -60,7 +61,15 @@ public: ...@@ -60,7 +61,15 @@ public:
std::map<std::string, double> getDefaultParameters(); std::map<std::string, double> getDefaultParameters();
std::vector<std::string> getKernelNames(); std::vector<std::string> getKernelNames();
void updateParametersInContext(ContextImpl& context); void updateParametersInContext(ContextImpl& context);
/**
* Compute the coefficient which, when divided by the periodic box volume, gives the
* long range correction to the energy.
*/
static double calcLongRangeCorrection(const CustomNonbondedForce& force, const Context& context);
private: private:
class TabulatedFunction;
static double integrateInteraction(const Lepton::ExpressionProgram& expression, const std::vector<double>& params1, const std::vector<double>& params2,
const CustomNonbondedForce& force, const Context& context);
const CustomNonbondedForce& owner; const CustomNonbondedForce& owner;
Kernel kernel; Kernel kernel;
}; };
......
...@@ -234,7 +234,7 @@ void Context::setVelocitiesToTemperature(double temperature, int randomSeed) { ...@@ -234,7 +234,7 @@ void Context::setVelocitiesToTemperature(double temperature, int randomSeed) {
impl->applyVelocityConstraints(1e-5); impl->applyVelocityConstraints(1e-5);
} }
double Context::getParameter(const string& name) { double Context::getParameter(const string& name) const {
return impl->getParameter(name); return impl->getParameter(name);
} }
......
...@@ -47,7 +47,8 @@ using std::string; ...@@ -47,7 +47,8 @@ using std::string;
using std::stringstream; using std::stringstream;
using std::vector; using std::vector;
CustomNonbondedForce::CustomNonbondedForce(const string& energy) : energyExpression(energy), nonbondedMethod(NoCutoff), cutoffDistance(1.0) { CustomNonbondedForce::CustomNonbondedForce(const string& energy) : energyExpression(energy), nonbondedMethod(NoCutoff), cutoffDistance(1.0),
switchingDistance(-1.0), useSwitchingFunction(false), useLongRangeCorrection(false) {
} }
const string& CustomNonbondedForce::getEnergyFunction() const { const string& CustomNonbondedForce::getEnergyFunction() const {
...@@ -74,6 +75,30 @@ void CustomNonbondedForce::setCutoffDistance(double distance) { ...@@ -74,6 +75,30 @@ void CustomNonbondedForce::setCutoffDistance(double distance) {
cutoffDistance = distance; cutoffDistance = distance;
} }
bool CustomNonbondedForce::getUseSwitchingFunction() const {
return useSwitchingFunction;
}
void CustomNonbondedForce::setUseSwitchingFunction(bool use) {
useSwitchingFunction = use;
}
double CustomNonbondedForce::getSwitchingDistance() const {
return switchingDistance;
}
void CustomNonbondedForce::setSwitchingDistance(double distance) {
switchingDistance = distance;
}
bool CustomNonbondedForce::getUseLongRangeCorrection() const {
return useLongRangeCorrection;
}
void CustomNonbondedForce::setUseLongRangeCorrection(bool use) {
useLongRangeCorrection = use;
}
int CustomNonbondedForce::addPerParticleParameter(const string& name) { int CustomNonbondedForce::addPerParticleParameter(const string& name) {
parameters.push_back(PerParticleParameterInfo(name)); parameters.push_back(PerParticleParameterInfo(name));
return parameters.size()-1; return parameters.size()-1;
......
...@@ -32,7 +32,12 @@ ...@@ -32,7 +32,12 @@
#include "openmm/OpenMMException.h" #include "openmm/OpenMMException.h"
#include "openmm/internal/ContextImpl.h" #include "openmm/internal/ContextImpl.h"
#include "openmm/internal/CustomNonbondedForceImpl.h" #include "openmm/internal/CustomNonbondedForceImpl.h"
#include "openmm/internal/SplineFitter.h"
#include "openmm/kernels.h" #include "openmm/kernels.h"
#include "lepton/CustomFunction.h"
#include "lepton/ParsedExpression.h"
#include "lepton/Parser.h"
#include <cmath>
#include <sstream> #include <sstream>
using namespace OpenMM; using namespace OpenMM;
...@@ -57,6 +62,10 @@ void CustomNonbondedForceImpl::initialize(ContextImpl& context) { ...@@ -57,6 +62,10 @@ void CustomNonbondedForceImpl::initialize(ContextImpl& context) {
const System& system = context.getSystem(); const System& system = context.getSystem();
if (owner.getNumParticles() != system.getNumParticles()) if (owner.getNumParticles() != system.getNumParticles())
throw OpenMMException("CustomNonbondedForce must have exactly as many particles as the System it belongs to."); throw OpenMMException("CustomNonbondedForce must have exactly as many particles as the System it belongs to.");
if (owner.getUseSwitchingFunction()) {
if (owner.getSwitchingDistance() < 0 || owner.getSwitchingDistance() >= owner.getCutoffDistance())
throw OpenMMException("CustomNonbondedForce: Switching distance must satisfy 0 <= r_switch < r_cutoff");
}
vector<set<int> > exclusions(owner.getNumParticles()); vector<set<int> > exclusions(owner.getNumParticles());
vector<double> parameters; vector<double> parameters;
int numParameters = owner.getNumPerParticleParameters(); int numParameters = owner.getNumPerParticleParameters();
...@@ -127,3 +136,126 @@ map<string, double> CustomNonbondedForceImpl::getDefaultParameters() { ...@@ -127,3 +136,126 @@ map<string, double> CustomNonbondedForceImpl::getDefaultParameters() {
void CustomNonbondedForceImpl::updateParametersInContext(ContextImpl& context) { void CustomNonbondedForceImpl::updateParametersInContext(ContextImpl& context) {
kernel.getAs<CalcCustomNonbondedForceKernel>().copyParametersToContext(context, owner); kernel.getAs<CalcCustomNonbondedForceKernel>().copyParametersToContext(context, owner);
} }
class CustomNonbondedForceImpl::TabulatedFunction : public Lepton::CustomFunction {
public:
TabulatedFunction(double min, double max, const vector<double>& values) :
min(min), max(max), values(values) {
int numValues = values.size();
x.resize(numValues);
for (int i = 0; i < numValues; i++)
x[i] = min+i*(max-min)/(numValues-1);
SplineFitter::createNaturalSpline(x, values, derivs);
}
int getNumArguments() const {
return 1;
}
double evaluate(const double* arguments) const {
double t = arguments[0];
if (t < min || t > max)
return 0.0;
return SplineFitter::evaluateSpline(x, values, derivs, t);
}
double evaluateDerivative(const double* arguments, const int* derivOrder) const {
double t = arguments[0];
if (t < min || t > max)
return 0.0;
return SplineFitter::evaluateSplineDerivative(x, values, derivs, t);
}
CustomFunction* clone() const {
return new TabulatedFunction(min, max, values);
}
double min, max;
vector<double> x, values, derivs;
};
double CustomNonbondedForceImpl::calcLongRangeCorrection(const CustomNonbondedForce& force, const Context& context) {
if (force.getNonbondedMethod() == CustomNonbondedForce::NoCutoff || force.getNonbondedMethod() == CustomNonbondedForce::CutoffNonPeriodic)
return 0.0;
// Identify all particle classes (defined by parameters), and count the number of
// particles in each class.
map<vector<double>, int> classCounts;
for (int i = 0; i < force.getNumParticles(); i++) {
vector<double> parameters;
force.getParticleParameters(i, parameters);
map<vector<double>, int>::iterator entry = classCounts.find(parameters);
if (entry == classCounts.end())
classCounts[parameters] = 1;
else
entry->second++;
}
// Parse the energy expression.
map<string, Lepton::CustomFunction*> functions;
for (int i = 0; i < force.getNumFunctions(); i++) {
string name;
vector<double> values;
double min, max;
force.getFunctionParameters(i, name, values, min, max);
functions[name] = new TabulatedFunction(min, max, values);
}
Lepton::ExpressionProgram expression = Lepton::Parser::parse(force.getEnergyFunction(), functions).optimize().createProgram();
// Loop over all pairs of classes to compute the coefficient.
double sum = 0;
for (map<vector<double>, int>::const_iterator entry = classCounts.begin(); entry != classCounts.end(); ++entry) {
int count = (entry->second*(entry->second+1))/2;
sum += count*integrateInteraction(expression, entry->first, entry->first, force, context);
}
for (map<vector<double>, int>::const_iterator class1 = classCounts.begin(); class1 != classCounts.end(); ++class1)
for (map<vector<double>, int>::const_iterator class2 = classCounts.begin(); class2 != class1; ++class2) {
int count = class1->second*class2->second;
sum += count*integrateInteraction(expression, class1->first, class2->first, force, context);
}
int numParticles = force.getNumParticles();
int numInteractions = (numParticles*(numParticles+1))/2;
sum /= numInteractions;
return 2*M_PI*numParticles*numParticles*sum;
}
double CustomNonbondedForceImpl::integrateInteraction(const Lepton::ExpressionProgram& expression, const vector<double>& params1, const vector<double>& params2,
const CustomNonbondedForce& force, const Context& context) {
map<std::string, double> variables;
for (int i = 0; i < force.getNumPerParticleParameters(); i++) {
stringstream name1, name2;
name1 << force.getPerParticleParameterName(i) << 1;
name2 << force.getPerParticleParameterName(i) << 2;
variables[name1.str()] = params1[i];
variables[name2.str()] = params2[i];
}
for (int i = 0; i < force.getNumGlobalParameters(); i++) {
const string& name = force.getGlobalParameterName(i);
variables[name] = context.getParameter(name);
}
// To integrate from r_cutoff to infinity, make the change of variables x=r_cutoff/r and integrate from 0 to 1.
// This introduces another r^2 into the integral, which along with the r^2 in the formula for the correction
// means we multiply the function by r^4. Use the midpoint method.
double cutoff = force.getCutoffDistance();
variables["r"] = 2*cutoff;
double sum = expression.evaluate(variables);
int numPoints = 1;
for (int iteration = 0; iteration < 10; iteration++) {
double oldSum = sum;
double newSum = 0;
numPoints *= 3;
for (int i = 0; i < numPoints; i++) {
if (i%3 == 1)
continue;
double x = (i+0.5)/numPoints;
double r = cutoff/x;
variables["r"] = r;
double r2 = r*r;
newSum += expression.evaluate(variables)*r2*r2;
}
sum = newSum/numPoints + oldSum/3;
if (iteration > 2 && (fabs((sum-oldSum)/sum) < 1e-5 || sum == 0))
break;
}
return sum/cutoff;
}
...@@ -33,6 +33,7 @@ ...@@ -33,6 +33,7 @@
#include "openmm/internal/ContextImpl.h" #include "openmm/internal/ContextImpl.h"
#include "openmm/internal/CustomCompoundBondForceImpl.h" #include "openmm/internal/CustomCompoundBondForceImpl.h"
#include "openmm/internal/CustomHbondForceImpl.h" #include "openmm/internal/CustomHbondForceImpl.h"
#include "openmm/internal/CustomNonbondedForceImpl.h"
#include "openmm/internal/NonbondedForceImpl.h" #include "openmm/internal/NonbondedForceImpl.h"
#include "CudaBondedUtilities.h" #include "CudaBondedUtilities.h"
#include "CudaExpressionUtilities.h" #include "CudaExpressionUtilities.h"
...@@ -1818,6 +1819,8 @@ CudaCalcCustomNonbondedForceKernel::~CudaCalcCustomNonbondedForceKernel() { ...@@ -1818,6 +1819,8 @@ CudaCalcCustomNonbondedForceKernel::~CudaCalcCustomNonbondedForceKernel() {
delete tabulatedFunctionParams; delete tabulatedFunctionParams;
for (int i = 0; i < (int) tabulatedFunctions.size(); i++) for (int i = 0; i < (int) tabulatedFunctions.size(); i++)
delete tabulatedFunctions[i]; delete tabulatedFunctions[i];
if (forceCopy != NULL)
delete forceCopy;
} }
void CudaCalcCustomNonbondedForceKernel::initialize(const System& system, const CustomNonbondedForce& force) { void CudaCalcCustomNonbondedForceKernel::initialize(const System& system, const CustomNonbondedForce& force) {
...@@ -1927,6 +1930,17 @@ void CudaCalcCustomNonbondedForceKernel::initialize(const System& system, const ...@@ -1927,6 +1930,17 @@ void CudaCalcCustomNonbondedForceKernel::initialize(const System& system, const
cu.getNonbondedUtilities().addArgument(CudaNonbondedUtilities::ParameterInfo(prefix+"globals", "float", 1, sizeof(float), globals->getDevicePointer())); cu.getNonbondedUtilities().addArgument(CudaNonbondedUtilities::ParameterInfo(prefix+"globals", "float", 1, sizeof(float), globals->getDevicePointer()));
} }
cu.addForce(new CudaCustomNonbondedForceInfo(force)); cu.addForce(new CudaCustomNonbondedForceInfo(force));
// Record information for the long range correction.
if (force.getNonbondedMethod() == CustomNonbondedForce::CutoffPeriodic && force.getUseLongRangeCorrection() && cu.getContextIndex() == 0) {
forceCopy = new CustomNonbondedForce(force);
hasInitializedLongRangeCorrection = false;
}
else {
longRangeCoefficient = 0.0;
hasInitializedLongRangeCorrection = true;
}
} }
double CudaCalcCustomNonbondedForceKernel::execute(ContextImpl& context, bool includeForces, bool includeEnergy) { double CudaCalcCustomNonbondedForceKernel::execute(ContextImpl& context, bool includeForces, bool includeEnergy) {
...@@ -1938,10 +1952,20 @@ double CudaCalcCustomNonbondedForceKernel::execute(ContextImpl& context, bool in ...@@ -1938,10 +1952,20 @@ double CudaCalcCustomNonbondedForceKernel::execute(ContextImpl& context, bool in
changed = true; changed = true;
globalParamValues[i] = value; globalParamValues[i] = value;
} }
if (changed) if (changed) {
globals->upload(globalParamValues); globals->upload(globalParamValues);
if (forceCopy != NULL) {
longRangeCoefficient = CustomNonbondedForceImpl::calcLongRangeCorrection(*forceCopy, context.getOwner());
hasInitializedLongRangeCorrection = true;
} }
return 0.0; }
}
if (!hasInitializedLongRangeCorrection) {
longRangeCoefficient = CustomNonbondedForceImpl::calcLongRangeCorrection(*forceCopy, context.getOwner());
hasInitializedLongRangeCorrection = true;
}
double4 boxSize = cu.getPeriodicBoxSize();
return longRangeCoefficient/(boxSize.x*boxSize.y*boxSize.z);
} }
void CudaCalcCustomNonbondedForceKernel::copyParametersToContext(ContextImpl& context, const CustomNonbondedForce& force) { void CudaCalcCustomNonbondedForceKernel::copyParametersToContext(ContextImpl& context, const CustomNonbondedForce& force) {
...@@ -1962,6 +1986,14 @@ void CudaCalcCustomNonbondedForceKernel::copyParametersToContext(ContextImpl& co ...@@ -1962,6 +1986,14 @@ void CudaCalcCustomNonbondedForceKernel::copyParametersToContext(ContextImpl& co
} }
params->setParameterValues(paramVector); params->setParameterValues(paramVector);
// If necessary, recompute the long range correction.
if (forceCopy != NULL) {
longRangeCoefficient = CustomNonbondedForceImpl::calcLongRangeCorrection(force, context.getOwner());
hasInitializedLongRangeCorrection = true;
*forceCopy = force;
}
// Mark that the current reordering may be invalid. // Mark that the current reordering may be invalid.
cu.invalidateMolecules(); cu.invalidateMolecules();
......
...@@ -633,7 +633,7 @@ private: ...@@ -633,7 +633,7 @@ private:
class CudaCalcCustomNonbondedForceKernel : public CalcCustomNonbondedForceKernel { class CudaCalcCustomNonbondedForceKernel : public CalcCustomNonbondedForceKernel {
public: public:
CudaCalcCustomNonbondedForceKernel(std::string name, const Platform& platform, CudaContext& cu, const System& system) : CalcCustomNonbondedForceKernel(name, platform), CudaCalcCustomNonbondedForceKernel(std::string name, const Platform& platform, CudaContext& cu, const System& system) : CalcCustomNonbondedForceKernel(name, platform),
cu(cu), params(NULL), globals(NULL), tabulatedFunctionParams(NULL), system(system) { cu(cu), params(NULL), globals(NULL), tabulatedFunctionParams(NULL), forceCopy(NULL), system(system) {
} }
~CudaCalcCustomNonbondedForceKernel(); ~CudaCalcCustomNonbondedForceKernel();
/** /**
...@@ -667,6 +667,9 @@ private: ...@@ -667,6 +667,9 @@ private:
std::vector<std::string> globalParamNames; std::vector<std::string> globalParamNames;
std::vector<float> globalParamValues; std::vector<float> globalParamValues;
std::vector<CudaArray*> tabulatedFunctions; std::vector<CudaArray*> tabulatedFunctions;
double longRangeCoefficient;
bool hasInitializedLongRangeCorrection;
CustomNonbondedForce* forceCopy;
const System& system; const System& system;
}; };
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Biological Structures at Stanford, funded under the NIH Roadmap for * * Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org. * * Medical Research, grant U54 GM072970. See https://simtk.org. *
* * * *
* Portions copyright (c) 2008-2012 Stanford University and the Authors. * * Portions copyright (c) 2008-2013 Stanford University and the Authors. *
* Authors: Peter Eastman * * Authors: Peter Eastman *
* Contributors: * * Contributors: *
* * * *
...@@ -407,6 +407,81 @@ void testParallelComputation() { ...@@ -407,6 +407,81 @@ void testParallelComputation() {
ASSERT_EQUAL_VEC(state1.getForces()[i], state2.getForces()[i], 1e-5); ASSERT_EQUAL_VEC(state1.getForces()[i], state2.getForces()[i], 1e-5);
} }
void testLongRangeCorrection() {
// Create a box of particles.
int gridSize = 5;
int numParticles = gridSize*gridSize*gridSize;
double boxSize = gridSize*0.7;
double cutoff = boxSize/3;
System standardSystem;
System customSystem;
VerletIntegrator integrator1(0.01);
VerletIntegrator integrator2(0.01);
NonbondedForce* standardNonbonded = new NonbondedForce();
CustomNonbondedForce* customNonbonded = new CustomNonbondedForce("4*eps*((sigma/r)^12-(sigma/r)^6); sigma=0.5*(sigma1+sigma2); eps=sqrt(eps1*eps2)");
customNonbonded->addPerParticleParameter("sigma");
customNonbonded->addPerParticleParameter("eps");
vector<Vec3> positions(numParticles);
int index = 0;
vector<double> params1(2);
params1[0] = 1.1;
params1[1] = 0.5;
vector<double> params2(2);
params2[0] = 1;
params2[1] = 1;
for (int i = 0; i < gridSize; i++)
for (int j = 0; j < gridSize; j++)
for (int k = 0; k < gridSize; k++) {
standardSystem.addParticle(1.0);
customSystem.addParticle(1.0);
if (index%2 == 0) {
standardNonbonded->addParticle(0, params1[0], params1[1]);
customNonbonded->addParticle(params1);
}
else {
standardNonbonded->addParticle(0, params2[0], params2[1]);
customNonbonded->addParticle(params2);
}
positions[index] = Vec3(i*boxSize/gridSize, j*boxSize/gridSize, k*boxSize/gridSize);
index++;
}
standardNonbonded->setNonbondedMethod(NonbondedForce::CutoffPeriodic);
customNonbonded->setNonbondedMethod(CustomNonbondedForce::CutoffPeriodic);
standardNonbonded->setCutoffDistance(cutoff);
customNonbonded->setCutoffDistance(cutoff);
standardSystem.setDefaultPeriodicBoxVectors(Vec3(boxSize, 0, 0), Vec3(0, boxSize, 0), Vec3(0, 0, boxSize));
customSystem.setDefaultPeriodicBoxVectors(Vec3(boxSize, 0, 0), Vec3(0, boxSize, 0), Vec3(0, 0, boxSize));
standardNonbonded->setUseDispersionCorrection(true);
customNonbonded->setUseLongRangeCorrection(true);
standardSystem.addForce(standardNonbonded);
customSystem.addForce(customNonbonded);
// Compute the correction for the standard force.
Context context1(standardSystem, integrator1, platform);
context1.setPositions(positions);
double standardEnergy1 = context1.getState(State::Energy).getPotentialEnergy();
standardNonbonded->setUseDispersionCorrection(false);
context1.reinitialize();
context1.setPositions(positions);
double standardEnergy2 = context1.getState(State::Energy).getPotentialEnergy();
// Compute the correction for the custom force.
Context context2(customSystem, integrator2, platform);
context2.setPositions(positions);
double customEnergy1 = context2.getState(State::Energy).getPotentialEnergy();
customNonbonded->setUseLongRangeCorrection(false);
context2.reinitialize();
context2.setPositions(positions);
double customEnergy2 = context2.getState(State::Energy).getPotentialEnergy();
// See if they agree.
ASSERT_EQUAL_TOL(standardEnergy1-standardEnergy2, customEnergy1-customEnergy2, 1e-4);
}
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {
try { try {
if (argc > 1) if (argc > 1)
...@@ -420,6 +495,7 @@ int main(int argc, char* argv[]) { ...@@ -420,6 +495,7 @@ int main(int argc, char* argv[]) {
testTabulatedFunction(); testTabulatedFunction();
testCoulombLennardJones(); testCoulombLennardJones();
testParallelComputation(); testParallelComputation();
testLongRangeCorrection();
} }
catch(const exception& e) { catch(const exception& e) {
cout << "exception: " << e.what() << endl; cout << "exception: " << e.what() << endl;
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Biological Structures at Stanford, funded under the NIH Roadmap for * * Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org. * * Medical Research, grant U54 GM072970. See https://simtk.org. *
* * * *
* Portions copyright (c) 2008-2012 Stanford University and the Authors. * * Portions copyright (c) 2008-2013 Stanford University and the Authors. *
* Authors: Peter Eastman * * Authors: Peter Eastman *
* Contributors: * * Contributors: *
* * * *
...@@ -65,6 +65,7 @@ ...@@ -65,6 +65,7 @@
#include "openmm/internal/ContextImpl.h" #include "openmm/internal/ContextImpl.h"
#include "openmm/internal/CustomCompoundBondForceImpl.h" #include "openmm/internal/CustomCompoundBondForceImpl.h"
#include "openmm/internal/CustomHbondForceImpl.h" #include "openmm/internal/CustomHbondForceImpl.h"
#include "openmm/internal/CustomNonbondedForceImpl.h"
#include "openmm/internal/CMAPTorsionForceImpl.h" #include "openmm/internal/CMAPTorsionForceImpl.h"
#include "openmm/internal/NonbondedForceImpl.h" #include "openmm/internal/NonbondedForceImpl.h"
#include "openmm/internal/SplineFitter.h" #include "openmm/internal/SplineFitter.h"
...@@ -1001,6 +1002,8 @@ ReferenceCalcCustomNonbondedForceKernel::~ReferenceCalcCustomNonbondedForceKerne ...@@ -1001,6 +1002,8 @@ ReferenceCalcCustomNonbondedForceKernel::~ReferenceCalcCustomNonbondedForceKerne
disposeIntArray(exclusionArray, numParticles); disposeIntArray(exclusionArray, numParticles);
if (neighborList != NULL) if (neighborList != NULL)
delete neighborList; delete neighborList;
if (forceCopy != NULL)
delete forceCopy;
} }
void ReferenceCalcCustomNonbondedForceKernel::initialize(const System& system, const CustomNonbondedForce& force) { void ReferenceCalcCustomNonbondedForceKernel::initialize(const System& system, const CustomNonbondedForce& force) {
...@@ -1059,18 +1062,32 @@ void ReferenceCalcCustomNonbondedForceKernel::initialize(const System& system, c ...@@ -1059,18 +1062,32 @@ void ReferenceCalcCustomNonbondedForceKernel::initialize(const System& system, c
forceExpression = expression.differentiate("r").optimize().createProgram(); forceExpression = expression.differentiate("r").optimize().createProgram();
for (int i = 0; i < numParameters; i++) for (int i = 0; i < numParameters; i++)
parameterNames.push_back(force.getPerParticleParameterName(i)); parameterNames.push_back(force.getPerParticleParameterName(i));
for (int i = 0; i < force.getNumGlobalParameters(); i++) for (int i = 0; i < force.getNumGlobalParameters(); i++) {
globalParameterNames.push_back(force.getGlobalParameterName(i)); globalParameterNames.push_back(force.getGlobalParameterName(i));
globalParamValues[force.getGlobalParameterName(i)] = force.getGlobalParameterDefaultValue(i);
}
// Delete the custom functions. // Delete the custom functions.
for (map<string, Lepton::CustomFunction*>::iterator iter = functions.begin(); iter != functions.end(); iter++) for (map<string, Lepton::CustomFunction*>::iterator iter = functions.begin(); iter != functions.end(); iter++)
delete iter->second; delete iter->second;
// Record information for the long range correction.
if (force.getNonbondedMethod() == CustomNonbondedForce::CutoffPeriodic && force.getUseLongRangeCorrection()) {
forceCopy = new CustomNonbondedForce(force);
hasInitializedLongRangeCorrection = false;
}
else {
longRangeCoefficient = 0.0;
hasInitializedLongRangeCorrection = true;
}
} }
double ReferenceCalcCustomNonbondedForceKernel::execute(ContextImpl& context, bool includeForces, bool includeEnergy) { double ReferenceCalcCustomNonbondedForceKernel::execute(ContextImpl& context, bool includeForces, bool includeEnergy) {
vector<RealVec>& posData = extractPositions(context); vector<RealVec>& posData = extractPositions(context);
vector<RealVec>& forceData = extractForces(context); vector<RealVec>& forceData = extractForces(context);
RealVec& box = extractBoxSize(context);
RealOpenMM energy = 0; RealOpenMM energy = 0;
ReferenceCustomNonbondedIxn ixn(energyExpression, forceExpression, parameterNames); ReferenceCustomNonbondedIxn ixn(energyExpression, forceExpression, parameterNames);
bool periodic = (nonbondedMethod == CutoffPeriodic); bool periodic = (nonbondedMethod == CutoffPeriodic);
...@@ -1079,16 +1096,27 @@ double ReferenceCalcCustomNonbondedForceKernel::execute(ContextImpl& context, bo ...@@ -1079,16 +1096,27 @@ double ReferenceCalcCustomNonbondedForceKernel::execute(ContextImpl& context, bo
ixn.setUseCutoff(nonbondedCutoff, *neighborList); ixn.setUseCutoff(nonbondedCutoff, *neighborList);
} }
if (periodic) { if (periodic) {
RealVec& box = extractBoxSize(context);
double minAllowedSize = 2*nonbondedCutoff; double minAllowedSize = 2*nonbondedCutoff;
if (box[0] < minAllowedSize || box[1] < minAllowedSize || box[2] < minAllowedSize) if (box[0] < minAllowedSize || box[1] < minAllowedSize || box[2] < minAllowedSize)
throw OpenMMException("The periodic box size has decreased to less than twice the nonbonded cutoff."); throw OpenMMException("The periodic box size has decreased to less than twice the nonbonded cutoff.");
ixn.setPeriodic(box); ixn.setPeriodic(box);
} }
map<string, double> globalParameters; bool globalParamsChanged = false;
for (int i = 0; i < (int) globalParameterNames.size(); i++) for (int i = 0; i < (int) globalParameterNames.size(); i++) {
globalParameters[globalParameterNames[i]] = context.getParameter(globalParameterNames[i]); double value = context.getParameter(globalParameterNames[i]);
ixn.calculatePairIxn(numParticles, posData, particleParamArray, exclusionArray, 0, globalParameters, forceData, 0, includeEnergy ? &energy : NULL); if (globalParamValues[globalParameterNames[i]] != value)
globalParamsChanged = true;
globalParamValues[globalParameterNames[i]] = value;
}
ixn.calculatePairIxn(numParticles, posData, particleParamArray, exclusionArray, 0, globalParamValues, forceData, 0, includeEnergy ? &energy : NULL);
// Add in the long range correction.
if (!hasInitializedLongRangeCorrection || (globalParamsChanged && forceCopy != NULL)) {
longRangeCoefficient = CustomNonbondedForceImpl::calcLongRangeCorrection(*forceCopy, context.getOwner());
hasInitializedLongRangeCorrection = true;
}
energy += longRangeCoefficient/(box[0]*box[1]*box[2]);
return energy; return energy;
} }
...@@ -1106,6 +1134,14 @@ void ReferenceCalcCustomNonbondedForceKernel::copyParametersToContext(ContextImp ...@@ -1106,6 +1134,14 @@ void ReferenceCalcCustomNonbondedForceKernel::copyParametersToContext(ContextImp
for (int j = 0; j < numParameters; j++) for (int j = 0; j < numParameters; j++)
particleParamArray[i][j] = static_cast<RealOpenMM>(parameters[j]); particleParamArray[i][j] = static_cast<RealOpenMM>(parameters[j]);
} }
// If necessary, recompute the long range correction.
if (forceCopy != NULL) {
longRangeCoefficient = CustomNonbondedForceImpl::calcLongRangeCorrection(force, context.getOwner());
hasInitializedLongRangeCorrection = true;
*forceCopy = force;
}
} }
ReferenceCalcGBSAOBCForceKernel::~ReferenceCalcGBSAOBCForceKernel() { ReferenceCalcGBSAOBCForceKernel::~ReferenceCalcGBSAOBCForceKernel() {
......
...@@ -586,7 +586,7 @@ private: ...@@ -586,7 +586,7 @@ private:
*/ */
class ReferenceCalcCustomNonbondedForceKernel : public CalcCustomNonbondedForceKernel { class ReferenceCalcCustomNonbondedForceKernel : public CalcCustomNonbondedForceKernel {
public: public:
ReferenceCalcCustomNonbondedForceKernel(std::string name, const Platform& platform) : CalcCustomNonbondedForceKernel(name, platform) { ReferenceCalcCustomNonbondedForceKernel(std::string name, const Platform& platform) : CalcCustomNonbondedForceKernel(name, platform), forceCopy(NULL) {
} }
~ReferenceCalcCustomNonbondedForceKernel(); ~ReferenceCalcCustomNonbondedForceKernel();
/** /**
...@@ -616,7 +616,10 @@ private: ...@@ -616,7 +616,10 @@ private:
int numParticles; int numParticles;
int **exclusionArray; int **exclusionArray;
RealOpenMM **particleParamArray; RealOpenMM **particleParamArray;
RealOpenMM nonbondedCutoff, periodicBoxSize[3]; RealOpenMM nonbondedCutoff, periodicBoxSize[3], longRangeCoefficient;
bool hasInitializedLongRangeCorrection;
CustomNonbondedForce* forceCopy;
std::map<std::string, double> globalParamValues;
std::vector<std::set<int> > exclusions; std::vector<std::set<int> > exclusions;
Lepton::ExpressionProgram energyExpression, forceExpression; Lepton::ExpressionProgram energyExpression, forceExpression;
std::vector<std::string> parameterNames, globalParameterNames; std::vector<std::string> parameterNames, globalParameterNames;
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Biological Structures at Stanford, funded under the NIH Roadmap for * * Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org. * * Medical Research, grant U54 GM072970. See https://simtk.org. *
* * * *
* Portions copyright (c) 2008-2009 Stanford University and the Authors. * * Portions copyright (c) 2008-2013 Stanford University and the Authors. *
* Authors: Peter Eastman * * Authors: Peter Eastman *
* Contributors: * * Contributors: *
* * * *
...@@ -337,6 +337,82 @@ void testCoulombLennardJones() { ...@@ -337,6 +337,82 @@ void testCoulombLennardJones() {
} }
} }
void testLongRangeCorrection() {
// Create a box of particles.
int gridSize = 5;
int numParticles = gridSize*gridSize*gridSize;
double boxSize = gridSize*0.7;
double cutoff = boxSize/3;
ReferencePlatform platform;
System standardSystem;
System customSystem;
VerletIntegrator integrator1(0.01);
VerletIntegrator integrator2(0.01);
NonbondedForce* standardNonbonded = new NonbondedForce();
CustomNonbondedForce* customNonbonded = new CustomNonbondedForce("4*eps*((sigma/r)^12-(sigma/r)^6); sigma=0.5*(sigma1+sigma2); eps=sqrt(eps1*eps2)");
customNonbonded->addPerParticleParameter("sigma");
customNonbonded->addPerParticleParameter("eps");
vector<Vec3> positions(numParticles);
int index = 0;
vector<double> params1(2);
params1[0] = 1.1;
params1[1] = 0.5;
vector<double> params2(2);
params2[0] = 1;
params2[1] = 1;
for (int i = 0; i < gridSize; i++)
for (int j = 0; j < gridSize; j++)
for (int k = 0; k < gridSize; k++) {
standardSystem.addParticle(1.0);
customSystem.addParticle(1.0);
if (index%2 == 0) {
standardNonbonded->addParticle(0, params1[0], params1[1]);
customNonbonded->addParticle(params1);
}
else {
standardNonbonded->addParticle(0, params2[0], params2[1]);
customNonbonded->addParticle(params2);
}
positions[index] = Vec3(i*boxSize/gridSize, j*boxSize/gridSize, k*boxSize/gridSize);
index++;
}
standardNonbonded->setNonbondedMethod(NonbondedForce::CutoffPeriodic);
customNonbonded->setNonbondedMethod(CustomNonbondedForce::CutoffPeriodic);
standardNonbonded->setCutoffDistance(cutoff);
customNonbonded->setCutoffDistance(cutoff);
standardSystem.setDefaultPeriodicBoxVectors(Vec3(boxSize, 0, 0), Vec3(0, boxSize, 0), Vec3(0, 0, boxSize));
customSystem.setDefaultPeriodicBoxVectors(Vec3(boxSize, 0, 0), Vec3(0, boxSize, 0), Vec3(0, 0, boxSize));
standardNonbonded->setUseDispersionCorrection(true);
customNonbonded->setUseLongRangeCorrection(true);
standardSystem.addForce(standardNonbonded);
customSystem.addForce(customNonbonded);
// Compute the correction for the standard force.
Context context1(standardSystem, integrator1, platform);
context1.setPositions(positions);
double standardEnergy1 = context1.getState(State::Energy).getPotentialEnergy();
standardNonbonded->setUseDispersionCorrection(false);
context1.reinitialize();
context1.setPositions(positions);
double standardEnergy2 = context1.getState(State::Energy).getPotentialEnergy();
// Compute the correction for the custom force.
Context context2(customSystem, integrator2, platform);
context2.setPositions(positions);
double customEnergy1 = context2.getState(State::Energy).getPotentialEnergy();
customNonbonded->setUseLongRangeCorrection(false);
context2.reinitialize();
context2.setPositions(positions);
double customEnergy2 = context2.getState(State::Energy).getPotentialEnergy();
// See if they agree.
ASSERT_EQUAL_TOL(standardEnergy1-standardEnergy2, customEnergy1-customEnergy2, 1e-4);
}
int main() { int main() {
try { try {
testSimpleExpression(); testSimpleExpression();
...@@ -346,6 +422,7 @@ int main() { ...@@ -346,6 +422,7 @@ int main() {
testPeriodic(); testPeriodic();
testTabulatedFunction(); testTabulatedFunction();
testCoulombLennardJones(); testCoulombLennardJones();
testLongRangeCorrection();
} }
catch(const exception& e) { catch(const exception& e) {
cout << "exception: " << e.what() << endl; cout << "exception: " << e.what() << endl;
......
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