Commit 8e01339d authored by peastman's avatar peastman
Browse files

Continuing to implement derivatives with respect to parameters

parent cd566c63
......@@ -38,20 +38,19 @@ using namespace std;
--------------------------------------------------------------------------------------- */
ReferenceCustomAngleIxn::ReferenceCustomAngleIxn(const Lepton::CompiledExpression& energyExpression,
const Lepton::CompiledExpression& forceExpression, const vector<string>& parameterNames, map<string, double> globalParameters) :
energyExpression(energyExpression), forceExpression(forceExpression), usePeriodic(false) {
energyTheta = ReferenceForce::getVariablePointer(this->energyExpression, "theta");
forceTheta = ReferenceForce::getVariablePointer(this->forceExpression, "theta");
const Lepton::CompiledExpression& forceExpression, const vector<string>& parameterNames, map<string, double> globalParameters,
const vector<Lepton::CompiledExpression> energyParamDerivExpressions) :
energyExpression(energyExpression), forceExpression(forceExpression), usePeriodic(false), energyParamDerivExpressions(energyParamDerivExpressions) {
expressionSet.registerExpression(this->energyExpression);
expressionSet.registerExpression(this->forceExpression);
for (int i = 0; i < this->energyParamDerivExpressions.size(); i++)
expressionSet.registerExpression(this->energyParamDerivExpressions[i]);
thetaIndex = expressionSet.getVariableIndex("theta");
numParameters = parameterNames.size();
for (int i = 0; i < (int) numParameters; i++) {
energyParams.push_back(ReferenceForce::getVariablePointer(this->energyExpression, parameterNames[i]));
forceParams.push_back(ReferenceForce::getVariablePointer(this->forceExpression, parameterNames[i]));
}
for (map<string, double>::const_iterator iter = globalParameters.begin(); iter != globalParameters.end(); ++iter) {
ReferenceForce::setVariable(ReferenceForce::getVariablePointer(this->energyExpression, iter->first), iter->second);
ReferenceForce::setVariable(ReferenceForce::getVariablePointer(this->forceExpression, iter->first), iter->second);
}
for (int i = 0; i < (int) numParameters; i++)
angleParamIndex.push_back(expressionSet.getVariableIndex(parameterNames[i]));
for (map<string, double>::const_iterator iter = globalParameters.begin(); iter != globalParameters.end(); ++iter)
expressionSet.setVariable(expressionSet.getVariableIndex(iter->first), iter->second);
}
/**---------------------------------------------------------------------------------------
......@@ -87,17 +86,9 @@ void ReferenceCustomAngleIxn::calculateBondIxn(int* atomIndices,
RealOpenMM* parameters,
vector<RealVec>& forces,
RealOpenMM* totalEnergy, double* energyParamDerivs) {
static const std::string methodName = "\nReferenceCustomAngleIxn::calculateAngleIxn";
static const RealOpenMM zero = 0.0;
static const RealOpenMM one = 1.0;
RealOpenMM deltaR[2][ReferenceForce::LastDeltaRIndex];
for (int i = 0; i < numParameters; i++) {
ReferenceForce::setVariable(energyParams[i], parameters[i]);
ReferenceForce::setVariable(forceParams[i], parameters[i]);
}
for (int i = 0; i < numParameters; i++)
expressionSet.setVariable(angleParamIndex[i], parameters[i]);
// ---------------------------------------------------------------------------------------
......@@ -122,14 +113,13 @@ void ReferenceCustomAngleIxn::calculateBondIxn(int* atomIndices,
RealOpenMM dot = DOT3(deltaR[0], deltaR[1]);
RealOpenMM cosine = dot/SQRT((deltaR[0][ReferenceForce::R2Index]*deltaR[1][ReferenceForce::R2Index]));
RealOpenMM angle;
if (cosine >= one)
angle = zero;
else if (cosine <= -one)
if (cosine >= 1.0)
angle = 0.0;
else if (cosine <= -1.0)
angle = PI_M;
else
angle = ACOS(cosine);
ReferenceForce::setVariable(energyTheta, angle);
ReferenceForce::setVariable(forceTheta, angle);
expressionSet.setVariable(thetaIndex, angle);
// Compute the force and energy, and apply them to the atoms.
......@@ -156,6 +146,11 @@ void ReferenceCustomAngleIxn::calculateBondIxn(int* atomIndices,
}
}
// Record parameter derivatives.
for (int i = 0; i < energyParamDerivExpressions.size(); i++)
energyParamDerivs[i] += energyParamDerivExpressions[i].evaluate();
// accumulate energies
if (totalEnergy != NULL)
......
/* Portions copyright (c) 2009-2013 Stanford University and Simbios.
/* Portions copyright (c) 2009-2016 Stanford University and Simbios.
* Contributors: Peter Eastman
*
* Permission is hereby granted, free of charge, to any person obtaining
......@@ -44,23 +44,20 @@ using namespace OpenMM;
--------------------------------------------------------------------------------------- */
ReferenceCustomNonbondedIxn::ReferenceCustomNonbondedIxn(const Lepton::CompiledExpression& energyExpression,
const Lepton::CompiledExpression& forceExpression, const vector<string>& parameterNames) :
cutoff(false), useSwitch(false), periodic(false), energyExpression(energyExpression), forceExpression(forceExpression), paramNames(parameterNames) {
// ---------------------------------------------------------------------------------------
// static const char* methodName = "\nReferenceCustomNonbondedIxn::ReferenceCustomNonbondedIxn";
// ---------------------------------------------------------------------------------------
energyR = ReferenceForce::getVariablePointer(this->energyExpression, "r");
forceR = ReferenceForce::getVariablePointer(this->forceExpression, "r");
const Lepton::CompiledExpression& forceExpression, const vector<string>& parameterNames,
const vector<Lepton::CompiledExpression> energyParamDerivExpressions) :
cutoff(false), useSwitch(false), periodic(false), energyExpression(energyExpression), forceExpression(forceExpression),
paramNames(parameterNames), energyParamDerivExpressions(energyParamDerivExpressions) {
expressionSet.registerExpression(this->energyExpression);
expressionSet.registerExpression(this->forceExpression);
for (int i = 0; i < this->energyParamDerivExpressions.size(); i++)
expressionSet.registerExpression(this->energyParamDerivExpressions[i]);
rIndex = expressionSet.getVariableIndex("r");
for (int i = 0; i < (int) paramNames.size(); i++) {
for (int j = 1; j < 3; j++) {
stringstream name;
name << paramNames[i] << j;
energyParticleParams.push_back(ReferenceForce::getVariablePointer(this->energyExpression, name.str()));
forceParticleParams.push_back(ReferenceForce::getVariablePointer(this->forceExpression, name.str()));
particleParamIndex.push_back(expressionSet.getVariableIndex(name.str()));
}
}
}
......@@ -167,12 +164,10 @@ void ReferenceCustomNonbondedIxn::setUseSwitchingFunction(RealOpenMM distance) {
void ReferenceCustomNonbondedIxn::calculatePairIxn(int numberOfAtoms, vector<RealVec>& atomCoordinates,
RealOpenMM** atomParameters, vector<set<int> >& exclusions,
RealOpenMM* fixedParameters, const map<string, double>& globalParameters, vector<RealVec>& forces,
RealOpenMM* energyByAtom, RealOpenMM* totalEnergy) {
RealOpenMM* energyByAtom, RealOpenMM* totalEnergy, double* energyParamDerivs) {
for (map<string, double>::const_iterator iter = globalParameters.begin(); iter != globalParameters.end(); ++iter) {
ReferenceForce::setVariable(ReferenceForce::getVariablePointer(energyExpression, iter->first), iter->second);
ReferenceForce::setVariable(ReferenceForce::getVariablePointer(forceExpression, iter->first), iter->second);
}
for (map<string, double>::const_iterator iter = globalParameters.begin(); iter != globalParameters.end(); ++iter)
expressionSet.setVariable(expressionSet.getVariableIndex(iter->first), iter->second);
if (interactionGroups.size() > 0) {
// The user has specified interaction groups, so compute only the requested interactions.
......@@ -186,12 +181,10 @@ void ReferenceCustomNonbondedIxn::calculatePairIxn(int numberOfAtoms, vector<Rea
if (*atom1 > *atom2 && set1.find(*atom2) != set1.end() && set2.find(*atom1) != set2.end())
continue; // Both atoms are in both sets, so skip duplicate interactions.
for (int j = 0; j < (int) paramNames.size(); j++) {
ReferenceForce::setVariable(energyParticleParams[j*2], atomParameters[*atom1][j]);
ReferenceForce::setVariable(energyParticleParams[j*2+1], atomParameters[*atom2][j]);
ReferenceForce::setVariable(forceParticleParams[j*2], atomParameters[*atom1][j]);
ReferenceForce::setVariable(forceParticleParams[j*2+1], atomParameters[*atom2][j]);
expressionSet.setVariable(particleParamIndex[j*2], atomParameters[*atom1][j]);
expressionSet.setVariable(particleParamIndex[j*2+1], atomParameters[*atom2][j]);
}
calculateOneIxn(*atom1, *atom2, atomCoordinates, forces, energyByAtom, totalEnergy);
calculateOneIxn(*atom1, *atom2, atomCoordinates, forces, energyByAtom, totalEnergy, energyParamDerivs);
}
}
}
......@@ -202,12 +195,10 @@ void ReferenceCustomNonbondedIxn::calculatePairIxn(int numberOfAtoms, vector<Rea
for (int i = 0; i < (int) neighborList->size(); i++) {
OpenMM::AtomPair pair = (*neighborList)[i];
for (int j = 0; j < (int) paramNames.size(); j++) {
ReferenceForce::setVariable(energyParticleParams[j*2], atomParameters[pair.first][j]);
ReferenceForce::setVariable(energyParticleParams[j*2+1], atomParameters[pair.second][j]);
ReferenceForce::setVariable(forceParticleParams[j*2], atomParameters[pair.first][j]);
ReferenceForce::setVariable(forceParticleParams[j*2+1], atomParameters[pair.second][j]);
expressionSet.setVariable(particleParamIndex[j*2], atomParameters[pair.first][j]);
expressionSet.setVariable(particleParamIndex[j*2+1], atomParameters[pair.second][j]);
}
calculateOneIxn(pair.first, pair.second, atomCoordinates, forces, energyByAtom, totalEnergy);
calculateOneIxn(pair.first, pair.second, atomCoordinates, forces, energyByAtom, totalEnergy, energyParamDerivs);
}
}
else {
......@@ -217,12 +208,10 @@ void ReferenceCustomNonbondedIxn::calculatePairIxn(int numberOfAtoms, vector<Rea
for (int jj = ii+1; jj < numberOfAtoms; jj++) {
if (exclusions[jj].find(ii) == exclusions[jj].end()) {
for (int j = 0; j < (int) paramNames.size(); j++) {
ReferenceForce::setVariable(energyParticleParams[j*2], atomParameters[ii][j]);
ReferenceForce::setVariable(energyParticleParams[j*2+1], atomParameters[jj][j]);
ReferenceForce::setVariable(forceParticleParams[j*2], atomParameters[ii][j]);
ReferenceForce::setVariable(forceParticleParams[j*2+1], atomParameters[jj][j]);
expressionSet.setVariable(particleParamIndex[j*2], atomParameters[ii][j]);
expressionSet.setVariable(particleParamIndex[j*2+1], atomParameters[jj][j]);
}
calculateOneIxn(ii, jj, atomCoordinates, forces, energyByAtom, totalEnergy);
calculateOneIxn(ii, jj, atomCoordinates, forces, energyByAtom, totalEnergy, energyParamDerivs);
}
}
}
......@@ -244,24 +233,7 @@ void ReferenceCustomNonbondedIxn::calculatePairIxn(int numberOfAtoms, vector<Rea
--------------------------------------------------------------------------------------- */
void ReferenceCustomNonbondedIxn::calculateOneIxn(int ii, int jj, vector<RealVec>& atomCoordinates, vector<RealVec>& forces,
RealOpenMM* energyByAtom, RealOpenMM* totalEnergy) {
// ---------------------------------------------------------------------------------------
static const std::string methodName = "\nReferenceCustomNonbondedIxn::calculateOneIxn";
// ---------------------------------------------------------------------------------------
// constants -- reduce Visual Studio warnings regarding conversions between float & double
static const RealOpenMM zero = 0.0;
static const RealOpenMM one = 1.0;
static const RealOpenMM two = 2.0;
static const RealOpenMM three = 3.0;
static const RealOpenMM six = 6.0;
static const RealOpenMM twelve = 12.0;
static const RealOpenMM oneM = -1.0;
RealOpenMM* energyByAtom, RealOpenMM* totalEnergy, double* energyParamDerivs) {
// get deltaR, R2, and R between 2 atoms
RealOpenMM deltaR[ReferenceForce::LastDeltaRIndex];
......@@ -275,14 +247,14 @@ void ReferenceCustomNonbondedIxn::calculateOneIxn(int ii, int jj, vector<RealVec
// accumulate forces
ReferenceForce::setVariable(energyR, r);
ReferenceForce::setVariable(forceR, r);
expressionSet.setVariable(rIndex, r);
RealOpenMM dEdR = (RealOpenMM) (forceExpression.evaluate()/(deltaR[ReferenceForce::RIndex]));
RealOpenMM energy = (RealOpenMM) energyExpression.evaluate();
RealOpenMM switchValue = 1.0;
if (useSwitch) {
if (r > switchingDistance) {
RealOpenMM t = (r-switchingDistance)/(cutoffDistance-switchingDistance);
RealOpenMM switchValue = 1+t*t*t*(-10+t*(15-t*6));
switchValue = 1+t*t*t*(-10+t*(15-t*6));
RealOpenMM switchDeriv = t*t*(-30+t*(60-t*30))/(cutoffDistance-switchingDistance);
dEdR = switchValue*dEdR + energy*switchDeriv/r;
energy *= switchValue;
......@@ -293,6 +265,8 @@ void ReferenceCustomNonbondedIxn::calculateOneIxn(int ii, int jj, vector<RealVec
forces[ii][kk] += force;
forces[jj][kk] -= force;
}
for (int i = 0; i < energyParamDerivExpressions.size(); i++)
energyParamDerivs[i] += switchValue*energyParamDerivExpressions[i].evaluate();
// accumulate energies
......
......@@ -38,20 +38,19 @@ using namespace OpenMM;
--------------------------------------------------------------------------------------- */
ReferenceCustomTorsionIxn::ReferenceCustomTorsionIxn(const Lepton::CompiledExpression& energyExpression,
const Lepton::CompiledExpression& forceExpression, const vector<string>& parameterNames, map<string, double> globalParameters) :
energyExpression(energyExpression), forceExpression(forceExpression), usePeriodic(false) {
energyTheta = ReferenceForce::getVariablePointer(this->energyExpression, "theta");
forceTheta = ReferenceForce::getVariablePointer(this->forceExpression, "theta");
const Lepton::CompiledExpression& forceExpression, const vector<string>& parameterNames, map<string, double> globalParameters,
const vector<Lepton::CompiledExpression> energyParamDerivExpressions) :
energyExpression(energyExpression), forceExpression(forceExpression), usePeriodic(false), energyParamDerivExpressions(energyParamDerivExpressions) {
expressionSet.registerExpression(this->energyExpression);
expressionSet.registerExpression(this->forceExpression);
for (int i = 0; i < this->energyParamDerivExpressions.size(); i++)
expressionSet.registerExpression(this->energyParamDerivExpressions[i]);
thetaIndex = expressionSet.getVariableIndex("theta");
numParameters = parameterNames.size();
for (int i = 0; i < (int) numParameters; i++) {
energyParams.push_back(ReferenceForce::getVariablePointer(this->energyExpression, parameterNames[i]));
forceParams.push_back(ReferenceForce::getVariablePointer(this->forceExpression, parameterNames[i]));
}
for (map<string, double>::const_iterator iter = globalParameters.begin(); iter != globalParameters.end(); ++iter) {
ReferenceForce::setVariable(ReferenceForce::getVariablePointer(this->energyExpression, iter->first), iter->second);
ReferenceForce::setVariable(ReferenceForce::getVariablePointer(this->forceExpression, iter->first), iter->second);
}
for (int i = 0; i < (int) numParameters; i++)
torsionParamIndex.push_back(expressionSet.getVariableIndex(parameterNames[i]));
for (map<string, double>::const_iterator iter = globalParameters.begin(); iter != globalParameters.end(); ++iter)
expressionSet.setVariable(expressionSet.getVariableIndex(iter->first), iter->second);
}
/**---------------------------------------------------------------------------------------
......@@ -87,17 +86,9 @@ void ReferenceCustomTorsionIxn::calculateBondIxn(int* atomIndices,
RealOpenMM* parameters,
vector<RealVec>& forces,
RealOpenMM* totalEnergy, double* energyParamDerivs) {
static const std::string methodName = "\nReferenceCustomTorsionIxn::calculateTorsionIxn";
static const RealOpenMM zero = 0.0;
static const RealOpenMM one = 1.0;
RealOpenMM deltaR[3][ReferenceForce::LastDeltaRIndex];
for (int i = 0; i < numParameters; i++) {
ReferenceForce::setVariable(energyParams[i], parameters[i]);
ReferenceForce::setVariable(forceParams[i], parameters[i]);
}
for (int i = 0; i < numParameters; i++)
expressionSet.setVariable(torsionParamIndex[i], parameters[i]);
// ---------------------------------------------------------------------------------------
......@@ -130,8 +121,7 @@ void ReferenceCustomTorsionIxn::calculateBondIxn(int* atomIndices,
RealOpenMM dotDihedral;
RealOpenMM signOfAngle;
RealOpenMM angle = getDihedralAngleBetweenThreeVectors(deltaR[0], deltaR[1], deltaR[2], crossProduct, &dotDihedral, deltaR[0], &signOfAngle, 1);
ReferenceForce::setVariable(energyTheta, angle);
ReferenceForce::setVariable(forceTheta, angle);
expressionSet.setVariable(thetaIndex, angle);
// evaluate delta angle, dE/d(angle)
......@@ -174,6 +164,11 @@ void ReferenceCustomTorsionIxn::calculateBondIxn(int* atomIndices,
forces[atomDIndex][ii] += internalF[3][ii];
}
// Record parameter derivatives.
for (int i = 0; i < energyParamDerivExpressions.size(); i++)
energyParamDerivs[i] += energyParamDerivExpressions[i].evaluate();
// accumulate energies
if (totalEnergy != NULL)
......
......@@ -6,7 +6,7 @@
* Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org. *
* *
* Portions copyright (c) 2010-2014 Stanford University and the Authors. *
* Portions copyright (c) 2010-2016 Stanford University and the Authors. *
* Authors: Peter Eastman *
* Contributors: *
* *
......@@ -42,7 +42,7 @@ CustomNonbondedForceProxy::CustomNonbondedForceProxy() : SerializationProxy("Cus
}
void CustomNonbondedForceProxy::serialize(const void* object, SerializationNode& node) const {
node.setIntProperty("version", 1);
node.setIntProperty("version", 2);
const CustomNonbondedForce& force = *reinterpret_cast<const CustomNonbondedForce*>(object);
node.setIntProperty("forceGroup", force.getForceGroup());
node.setStringProperty("energy", force.getEnergyFunction());
......@@ -59,6 +59,10 @@ void CustomNonbondedForceProxy::serialize(const void* object, SerializationNode&
for (int i = 0; i < force.getNumGlobalParameters(); i++) {
globalParams.createChildNode("Parameter").setStringProperty("name", force.getGlobalParameterName(i)).setDoubleProperty("default", force.getGlobalParameterDefaultValue(i));
}
SerializationNode& energyDerivs = node.createChildNode("EnergyParameterDerivatives");
for (int i = 0; i < force.getNumEnergyParameterDerivatives(); i++) {
energyDerivs.createChildNode("Parameter").setStringProperty("name", force.getEnergyParameterDerivativeName(i));
}
SerializationNode& particles = node.createChildNode("Particles");
for (int i = 0; i < force.getNumParticles(); i++) {
vector<double> params;
......@@ -97,7 +101,8 @@ void CustomNonbondedForceProxy::serialize(const void* object, SerializationNode&
}
void* CustomNonbondedForceProxy::deserialize(const SerializationNode& node) const {
if (node.getIntProperty("version") != 1)
int version = node.getIntProperty("version");
if (version < 1 || version > 2)
throw OpenMMException("Unsupported version number");
CustomNonbondedForce* force = NULL;
try {
......@@ -118,6 +123,13 @@ void* CustomNonbondedForceProxy::deserialize(const SerializationNode& node) cons
const SerializationNode& parameter = globalParams.getChildren()[i];
force->addGlobalParameter(parameter.getStringProperty("name"), parameter.getDoubleProperty("default"));
}
if (version > 1) {
const SerializationNode& energyDerivs = node.getChildNode("EnergyParameterDerivatives");
for (int i = 0; i < (int) energyDerivs.getChildren().size(); i++) {
const SerializationNode& parameter = energyDerivs.getChildren()[i];
force->addEnergyParameterDerivative(parameter.getStringProperty("name"));
}
}
const SerializationNode& particles = node.getChildNode("Particles");
vector<double> params(force->getNumPerParticleParameters());
for (int i = 0; i < (int) particles.getChildren().size(); i++) {
......
......@@ -6,7 +6,7 @@
* Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org. *
* *
* Portions copyright (c) 2010-2014 Stanford University and the Authors. *
* Portions copyright (c) 2010-2016 Stanford University and the Authors. *
* Authors: Peter Eastman *
* Contributors: *
* *
......@@ -51,6 +51,7 @@ void testSerialization() {
force.addGlobalParameter("x", 1.3);
force.addGlobalParameter("y", 2.221);
force.addPerParticleParameter("z");
force.addEnergyParameterDerivative("y");
vector<double> params(1);
params[0] = 1.0;
force.addParticle(params);
......@@ -94,6 +95,9 @@ void testSerialization() {
ASSERT_EQUAL(force.getGlobalParameterName(i), force2.getGlobalParameterName(i));
ASSERT_EQUAL(force.getGlobalParameterDefaultValue(i), force2.getGlobalParameterDefaultValue(i));
}
ASSERT_EQUAL(force.getNumEnergyParameterDerivatives(), force2.getNumEnergyParameterDerivatives());
for (int i = 0; i < force.getNumEnergyParameterDerivatives(); i++)
ASSERT_EQUAL(force.getEnergyParameterDerivativeName(i), force2.getEnergyParameterDerivativeName(i));
ASSERT_EQUAL(force.getNumParticles(), force2.getNumParticles());
for (int i = 0; i < force.getNumParticles(); i++) {
vector<double> params1, params2;
......
......@@ -181,6 +181,41 @@ void testPeriodic() {
ASSERT_EQUAL_TOL(0.5*1.1*(M_PI/6)*(M_PI/6), state.getPotentialEnergy(), TOL);
}
void testEnergyParameterDerivatives() {
System system;
system.addParticle(1.0);
system.addParticle(1.0);
system.addParticle(1.0);
VerletIntegrator integrator(0.01);
CustomAngleForce* angles = new CustomAngleForce("k*(theta-theta0)^2");
angles->addGlobalParameter("theta0", 0.0);
angles->addGlobalParameter("k", 0.0);
angles->addEnergyParameterDerivative("theta0");
angles->addEnergyParameterDerivative("k");
vector<double> parameters;
angles->addAngle(0, 1, 2, parameters);
system.addForce(angles);
Context context(system, integrator, platform);
vector<Vec3> positions(3);
positions[0] = Vec3(0, 2, 0);
positions[1] = Vec3(0, 0, 0);
positions[2] = Vec3(1, 1, 0);
context.setPositions(positions);
double theta = M_PI/4;
for (int i = 0; i < 10; i++) {
double theta0 = 0.1*i;
double k = 10-i;
context.setParameter("theta0", theta0);
context.setParameter("k", k);
State state = context.getState(State::ParameterDerivatives);
map<string, double> derivs = state.getEnergyParameterDerivatives();
double dEdtheta0 = -2*k*(theta-theta0);
double dEdk = (theta-theta0)*(theta-theta0);
ASSERT_EQUAL_TOL(dEdtheta0, derivs["theta0"], 1e-5);
ASSERT_EQUAL_TOL(dEdk, derivs["k"], 1e-5);
}
}
void runPlatformTests();
int main(int argc, char* argv[]) {
......@@ -189,6 +224,7 @@ int main(int argc, char* argv[]) {
testAngles();
testIllegalVariable();
testPeriodic();
testEnergyParameterDerivatives();
runPlatformTests();
}
catch(const exception& e) {
......
......@@ -212,6 +212,7 @@ void testEnergyParameterDerivatives() {
ASSERT_EQUAL_TOL(dEdk, derivs["k"], 1e-5);
}
}
void runPlatformTests();
int main(int argc, char* argv[]) {
......
......@@ -7,7 +7,7 @@
* Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org. *
* *
* Portions copyright (c) 2008-2015 Stanford University and the Authors. *
* Portions copyright (c) 2008-2016 Stanford University and the Authors. *
* Authors: Peter Eastman *
* Contributors: *
* *
......@@ -1041,6 +1041,84 @@ void testIllegalVariable() {
ASSERT(threwException);
}
void testEnergyParameterDerivatives() {
System system;
system.addParticle(1.0);
system.addParticle(1.0);
system.addParticle(1.0);
VerletIntegrator integrator(0.01);
CustomNonbondedForce* nonbonded = new CustomNonbondedForce("k*(r-r0)^2");
nonbonded->addGlobalParameter("r0", 0.0);
nonbonded->addGlobalParameter("k", 0.0);
nonbonded->addEnergyParameterDerivative("r0");
nonbonded->addEnergyParameterDerivative("k");
vector<double> parameters;
nonbonded->addParticle(parameters);
nonbonded->addParticle(parameters);
nonbonded->addParticle(parameters);
nonbonded->addExclusion(0, 2);
system.addForce(nonbonded);
Context context(system, integrator, platform);
vector<Vec3> positions(3);
positions[0] = Vec3(0, 2, 0);
positions[1] = Vec3(0, 0, 0);
positions[2] = Vec3(1, 0, 0);
context.setPositions(positions);
for (int i = 0; i < 10; i++) {
double r0 = 0.1*i;
double k = 10-i;
context.setParameter("r0", r0);
context.setParameter("k", k);
State state = context.getState(State::ParameterDerivatives);
map<string, double> derivs = state.getEnergyParameterDerivatives();
double dEdr0 = -2*k*((2-r0)+(1-r0));
double dEdk = (2-r0)*(2-r0) + (1-r0)*(1-r0);
ASSERT_EQUAL_TOL(dEdr0, derivs["r0"], 1e-5);
ASSERT_EQUAL_TOL(dEdk, derivs["k"], 1e-5);
}
}
void testEnergyParameterDerivatives2() {
// Create a box of particles.
const int numParticles = 30;
const double boxSize = 2.0;
const double a = 1.0;
const double delta = 1e-3;
System system;
system.setDefaultPeriodicBoxVectors(Vec3(boxSize, 0, 0), Vec3(0, boxSize, 0), Vec3(0, 0, boxSize));
CustomNonbondedForce* nonbonded = new CustomNonbondedForce("(r+a)^-4");
system.addForce(nonbonded);
nonbonded->addGlobalParameter("a", a);
nonbonded->addEnergyParameterDerivative("a");
nonbonded->setNonbondedMethod(CustomNonbondedForce::CutoffPeriodic);
nonbonded->setCutoffDistance(1.0);
nonbonded->setSwitchingDistance(0.9);
nonbonded->setUseSwitchingFunction(true);
nonbonded->setUseLongRangeCorrection(true);
vector<Vec3> positions;
vector<double> parameters;
OpenMM_SFMT::SFMT sfmt;
init_gen_rand(0, sfmt);
for (int i = 0; i < numParticles; i++) {
system.addParticle(1.0);
nonbonded->addParticle(parameters);
positions.push_back(Vec3(genrand_real2(sfmt), genrand_real2(sfmt), genrand_real2(sfmt))*boxSize);
}
// Compute the energy derivative and compare it to a finite difference approximation.
VerletIntegrator integrator(0.01);
Context context(system, integrator, platform);
context.setPositions(positions);
map<string, double> derivs = context.getState(State::ParameterDerivatives).getEnergyParameterDerivatives();
context.setParameter("a", a+delta);
double energy1 = context.getState(State::Energy).getPotentialEnergy();
context.setParameter("a", a-delta);
double energy2 = context.getState(State::Energy).getPotentialEnergy();
ASSERT_EQUAL_TOL((energy1-energy2)/(2*delta), derivs["a"], 1e-4);
}
void runPlatformTests();
int main(int argc, char* argv[]) {
......@@ -1067,6 +1145,8 @@ int main(int argc, char* argv[]) {
testInteractionGroupTabulatedFunction();
testMultipleCutoffs();
testIllegalVariable();
testEnergyParameterDerivatives();
testEnergyParameterDerivatives2();
runPlatformTests();
}
catch(const exception& e) {
......
......@@ -222,6 +222,43 @@ void testPeriodic() {
ASSERT_EQUAL_TOL(1.1*(1+std::cos(2*M_PI/3)), state.getPotentialEnergy(), TOL);
}
void testEnergyParameterDerivatives() {
System system;
system.addParticle(1.0);
system.addParticle(1.0);
system.addParticle(1.0);
system.addParticle(1.0);
VerletIntegrator integrator(0.01);
CustomTorsionForce* torsions = new CustomTorsionForce("k*(theta-theta0)^2");
torsions->addGlobalParameter("theta0", 0.0);
torsions->addGlobalParameter("k", 0.0);
torsions->addEnergyParameterDerivative("theta0");
torsions->addEnergyParameterDerivative("k");
vector<double> parameters;
torsions->addTorsion(0, 1, 2, 3, parameters);
system.addForce(torsions);
Context context(system, integrator, platform);
vector<Vec3> positions(4);
positions[0] = Vec3(0, 2, 0);
positions[1] = Vec3(0, 0, 0);
positions[2] = Vec3(1, 0, 0);
positions[3] = Vec3(1, 1, 1);
context.setPositions(positions);
double theta = M_PI/4;
for (int i = 0; i < 10; i++) {
double theta0 = 0.1*i;
double k = 10-i;
context.setParameter("theta0", theta0);
context.setParameter("k", k);
State state = context.getState(State::ParameterDerivatives);
map<string, double> derivs = state.getEnergyParameterDerivatives();
double dEdtheta0 = -2*k*(theta-theta0);
double dEdk = (theta-theta0)*(theta-theta0);
ASSERT_EQUAL_TOL(dEdtheta0, derivs["theta0"], 1e-5);
ASSERT_EQUAL_TOL(dEdk, derivs["k"], 1e-5);
}
}
void runPlatformTests();
int main(int argc, char* argv[]) {
......@@ -231,6 +268,7 @@ int main(int argc, char* argv[]) {
testRange();
testIllegalVariable();
testPeriodic();
testEnergyParameterDerivatives();
runPlatformTests();
}
catch(const exception& e) {
......
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