Commit 925b00ec authored by Peter Eastman's avatar Peter Eastman
Browse files

Kinetic energy is computed by the Integrator so it can adjust for leapfrog displacements

parent d0432e70
...@@ -59,7 +59,14 @@ ReferenceCustomDynamics::ReferenceCustomDynamics(int numberOfAtoms, const Custom ...@@ -59,7 +59,14 @@ ReferenceCustomDynamics::ReferenceCustomDynamics(int numberOfAtoms, const Custom
string expression; string expression;
integrator.getComputationStep(i, stepType[i], stepVariable[i], expression); integrator.getComputationStep(i, stepType[i], stepVariable[i], expression);
if (expression.length() > 0) if (expression.length() > 0)
stepExpression[i] = Lepton::Parser::parse(expression).createProgram(); stepExpression[i] = Lepton::Parser::parse(expression).optimize().createProgram();
}
kineticEnergyExpression = Lepton::Parser::parse(integrator.getKineticEnergyExpression()).optimize().createProgram();
kineticEnergyNeedsForce = false;
for (int i = 0; i < kineticEnergyExpression.getNumOperations(); i++) {
const Lepton::Operation& op = kineticEnergyExpression.getOperation(i);
if (op.getId() == Lepton::Operation::VARIABLE && op.getName() == "f")
kineticEnergyNeedsForce = true;
} }
} }
...@@ -304,3 +311,35 @@ void ReferenceCustomDynamics::recordChangedParameters(OpenMM::ContextImpl& conte ...@@ -304,3 +311,35 @@ void ReferenceCustomDynamics::recordChangedParameters(OpenMM::ContextImpl& conte
context.setParameter(name, globals[name]); context.setParameter(name, globals[name]);
} }
} }
/**---------------------------------------------------------------------------------------
Compute the kinetic energy of the system.
@param context the context this integrator is updating
@param numberOfAtoms number of atoms
@param atomCoordinates atom coordinates
@param velocities velocities
@param forces forces
@param masses atom masses
@param globals a map containing values of global variables
@param perDof the values of per-DOF variables
@param forcesAreValid whether the current forces are valid or need to be recomputed
--------------------------------------------------------------------------------------- */
double ReferenceCustomDynamics::computeKineticEnergy(OpenMM::ContextImpl& context, int numberOfAtoms, std::vector<OpenMM::RealVec>& atomCoordinates,
std::vector<OpenMM::RealVec>& velocities, std::vector<OpenMM::RealVec>& forces, std::vector<RealOpenMM>& masses,
std::map<std::string, RealOpenMM>& globals, std::vector<std::vector<OpenMM::RealVec> >& perDof, bool& forcesAreValid) {
globals.insert(context.getParameters().begin(), context.getParameters().end());
if (kineticEnergyNeedsForce) {
energy = context.calcForcesAndEnergy(true, true, -1);
forcesAreValid = true;
}
computePerDof(numberOfAtoms, sumBuffer, atomCoordinates, velocities, forces, masses, globals, perDof, kineticEnergyExpression, "f");
RealOpenMM sum = 0.0;
for (int j = 0; j < numberOfAtoms; j++)
if (masses[j] != 0.0)
sum += sumBuffer[j][0]+sumBuffer[j][1]+sumBuffer[j][2];
return sum;
}
...@@ -48,6 +48,8 @@ private: ...@@ -48,6 +48,8 @@ private:
std::vector<bool> invalidatesForces, needsForces, needsEnergy; std::vector<bool> invalidatesForces, needsForces, needsEnergy;
std::vector<int> forceGroup; std::vector<int> forceGroup;
RealOpenMM energy; RealOpenMM energy;
Lepton::ExpressionProgram kineticEnergyExpression;
bool kineticEnergyNeedsForce;
void computePerDof(int numberOfAtoms, std::vector<OpenMM::RealVec>& results, const std::vector<OpenMM::RealVec>& atomCoordinates, void computePerDof(int numberOfAtoms, std::vector<OpenMM::RealVec>& results, const std::vector<OpenMM::RealVec>& atomCoordinates,
const std::vector<OpenMM::RealVec>& velocities, const std::vector<OpenMM::RealVec>& forces, const std::vector<RealOpenMM>& masses, const std::vector<OpenMM::RealVec>& velocities, const std::vector<OpenMM::RealVec>& forces, const std::vector<RealOpenMM>& masses,
...@@ -97,6 +99,25 @@ public: ...@@ -97,6 +99,25 @@ public:
std::vector<OpenMM::RealVec>& velocities, std::vector<OpenMM::RealVec>& forces, std::vector<RealOpenMM>& masses, std::vector<OpenMM::RealVec>& velocities, std::vector<OpenMM::RealVec>& forces, std::vector<RealOpenMM>& masses,
std::map<std::string, RealOpenMM>& globals, std::vector<std::vector<OpenMM::RealVec> >& perDof, bool& forcesAreValid); std::map<std::string, RealOpenMM>& globals, std::vector<std::vector<OpenMM::RealVec> >& perDof, bool& forcesAreValid);
/**---------------------------------------------------------------------------------------
Compute the kinetic energy of the system.
@param context the context this integrator is updating
@param numberOfAtoms number of atoms
@param atomCoordinates atom coordinates
@param velocities velocities
@param forces forces
@param masses atom masses
@param globals a map containing values of global variables
@param perDof the values of per-DOF variables
@param forcesAreValid whether the current forces are valid or need to be recomputed
--------------------------------------------------------------------------------------- */
double computeKineticEnergy(OpenMM::ContextImpl& context, int numberOfAtoms, std::vector<OpenMM::RealVec>& atomCoordinates,
std::vector<OpenMM::RealVec>& velocities, std::vector<OpenMM::RealVec>& forces, std::vector<RealOpenMM>& masses,
std::map<std::string, RealOpenMM>& globals, std::vector<std::vector<OpenMM::RealVec> >& perDof, bool& forcesAreValid);
}; };
// --------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------
......
...@@ -59,9 +59,11 @@ void testSingleBond() { ...@@ -59,9 +59,11 @@ void testSingleBond() {
System system; System system;
system.addParticle(2.0); system.addParticle(2.0);
system.addParticle(2.0); system.addParticle(2.0);
CustomIntegrator integrator(0.01); const double dt = 0.01;
CustomIntegrator integrator(dt);
integrator.addComputePerDof("v", "v+dt*f/m"); integrator.addComputePerDof("v", "v+dt*f/m");
integrator.addComputePerDof("x", "x+dt*v"); integrator.addComputePerDof("x", "x+dt*v");
integrator.setKineticEnergyExpression("m*v1*v1/2; v1=v+0.5*dt*f/m");
HarmonicBondForce* forceField = new HarmonicBondForce(); HarmonicBondForce* forceField = new HarmonicBondForce();
forceField->addBond(0, 1, 1.5, 1); forceField->addBond(0, 1, 1.5, 1);
system.addForce(forceField); system.addForce(forceField);
...@@ -70,23 +72,25 @@ void testSingleBond() { ...@@ -70,23 +72,25 @@ void testSingleBond() {
positions[0] = Vec3(-1, 0, 0); positions[0] = Vec3(-1, 0, 0);
positions[1] = Vec3(1, 0, 0); positions[1] = Vec3(1, 0, 0);
context.setPositions(positions); context.setPositions(positions);
vector<Vec3> velocities(2);
velocities[0] = Vec3(-0.5*dt*0.5*0.5, 0, 0);
velocities[1] = Vec3(0.5*dt*0.5*0.5, 0, 0);
context.setVelocities(velocities);
// This is simply a harmonic oscillator, so compare it to the analytical solution. // This is simply a harmonic oscillator, so compare it to the analytical solution.
const double freq = 1.0;; const double freq = 1.0;;
State state = context.getState(State::Energy);
const double initialEnergy = state.getKineticEnergy()+state.getPotentialEnergy();
for (int i = 0; i < 1000; ++i) { for (int i = 0; i < 1000; ++i) {
state = context.getState(State::Positions | State::Velocities | State::Energy); State state = context.getState(State::Positions | State::Velocities | State::Energy);
double time = state.getTime(); double time = state.getTime();
double expectedDist = 1.5+0.5*std::cos(freq*time); double expectedDist = 1.5+0.5*std::cos(freq*time);
ASSERT_EQUAL_VEC(Vec3(-0.5*expectedDist, 0, 0), state.getPositions()[0], 0.02); ASSERT_EQUAL_VEC(Vec3(-0.5*expectedDist, 0, 0), state.getPositions()[0], 1e-4);
ASSERT_EQUAL_VEC(Vec3(0.5*expectedDist, 0, 0), state.getPositions()[1], 0.02); ASSERT_EQUAL_VEC(Vec3(0.5*expectedDist, 0, 0), state.getPositions()[1], 1e-4);
double expectedSpeed = -0.5*freq*std::sin(freq*time); double expectedSpeed = -0.5*freq*std::sin(freq*(time-dt/2));
ASSERT_EQUAL_VEC(Vec3(-0.5*expectedSpeed, 0, 0), state.getVelocities()[0], 0.02); ASSERT_EQUAL_VEC(Vec3(-0.5*expectedSpeed, 0, 0), state.getVelocities()[0], 1e-4);
ASSERT_EQUAL_VEC(Vec3(0.5*expectedSpeed, 0, 0), state.getVelocities()[1], 0.02); ASSERT_EQUAL_VEC(Vec3(0.5*expectedSpeed, 0, 0), state.getVelocities()[1], 1e-4);
double energy = state.getKineticEnergy()+state.getPotentialEnergy(); double energy = state.getKineticEnergy()+state.getPotentialEnergy();
ASSERT_EQUAL_TOL(initialEnergy, energy, 0.01); ASSERT_EQUAL_TOL(0.5*0.5*0.5, energy, 1e-4);
integrator.step(1); integrator.step(1);
} }
} }
......
...@@ -50,23 +50,6 @@ using namespace std; ...@@ -50,23 +50,6 @@ using namespace std;
const double TOL = 1e-5; const double TOL = 1e-5;
/**
* Compute the energy of a state, taking into account the half step offset between
* positions and velocities.
*/
static double computeEnergy(const State& state, const System& system, double dt) {
const vector<Vec3>& v = state.getVelocities();
const vector<Vec3>& f = state.getForces();
double energy = 0.0;
for (int i = 0; i < system.getNumParticles(); i++) {
double m = system.getParticleMass(i);
Vec3 vel = v[i]+f[i]*(0.5*dt/m);
energy += 0.5*m*vel.dot(vel);
}
return energy+state.getPotentialEnergy();
}
void testSingleBond() { void testSingleBond() {
ReferencePlatform platform; ReferencePlatform platform;
System system; System system;
...@@ -145,7 +128,7 @@ void testConstraints() { ...@@ -145,7 +128,7 @@ void testConstraints() {
double dist = std::sqrt((p1[0]-p2[0])*(p1[0]-p2[0])+(p1[1]-p2[1])*(p1[1]-p2[1])+(p1[2]-p2[2])*(p1[2]-p2[2])); double dist = std::sqrt((p1[0]-p2[0])*(p1[0]-p2[0])+(p1[1]-p2[1])*(p1[1]-p2[1])+(p1[2]-p2[2])*(p1[2]-p2[2]));
ASSERT_EQUAL_TOL(distance, dist, 2e-5); ASSERT_EQUAL_TOL(distance, dist, 2e-5);
} }
double energy = computeEnergy(state, system, integrator.getStepSize()); double energy = state.getKineticEnergy()+state.getPotentialEnergy();
if (i == 1) if (i == 1)
initialEnergy = energy; initialEnergy = energy;
else if (i > 1) else if (i > 1)
...@@ -217,7 +200,7 @@ void testConstrainedClusters() { ...@@ -217,7 +200,7 @@ void testConstrainedClusters() {
double dist = std::sqrt((p1[0]-p2[0])*(p1[0]-p2[0])+(p1[1]-p2[1])*(p1[1]-p2[1])+(p1[2]-p2[2])*(p1[2]-p2[2])); double dist = std::sqrt((p1[0]-p2[0])*(p1[0]-p2[0])+(p1[1]-p2[1])*(p1[1]-p2[1])+(p1[2]-p2[2])*(p1[2]-p2[2]));
ASSERT_EQUAL_TOL(distance, dist, 2e-5); ASSERT_EQUAL_TOL(distance, dist, 2e-5);
} }
double energy = computeEnergy(state, system, integrator.getStepSize()); double energy = state.getKineticEnergy()+state.getPotentialEnergy();
if (i == 1) if (i == 1)
initialEnergy = energy; initialEnergy = energy;
else if (i > 1) else if (i > 1)
......
...@@ -50,23 +50,6 @@ using namespace std; ...@@ -50,23 +50,6 @@ using namespace std;
const double TOL = 1e-5; const double TOL = 1e-5;
/**
* Compute the energy of a state, taking into account the half step offset between
* positions and velocities.
*/
static double computeEnergy(const State& state, const System& system, double dt) {
const vector<Vec3>& v = state.getVelocities();
const vector<Vec3>& f = state.getForces();
double energy = 0.0;
for (int i = 0; i < system.getNumParticles(); i++) {
double m = system.getParticleMass(i);
Vec3 vel = v[i]+f[i]*(0.5*dt/m);
energy += 0.5*m*vel.dot(vel);
}
return energy+state.getPotentialEnergy();
}
void testSingleBond() { void testSingleBond() {
ReferencePlatform platform; ReferencePlatform platform;
System system; System system;
...@@ -144,7 +127,7 @@ void testConstraints() { ...@@ -144,7 +127,7 @@ void testConstraints() {
double dist = std::sqrt((p1[0]-p2[0])*(p1[0]-p2[0])+(p1[1]-p2[1])*(p1[1]-p2[1])+(p1[2]-p2[2])*(p1[2]-p2[2])); double dist = std::sqrt((p1[0]-p2[0])*(p1[0]-p2[0])+(p1[1]-p2[1])*(p1[1]-p2[1])+(p1[2]-p2[2])*(p1[2]-p2[2]));
ASSERT_EQUAL_TOL(distance, dist, 2e-5); ASSERT_EQUAL_TOL(distance, dist, 2e-5);
} }
double energy = computeEnergy(state, system, integrator.getStepSize()); double energy = state.getPotentialEnergy()+state.getKineticEnergy();
if (i == 1) if (i == 1)
initialEnergy = energy; initialEnergy = energy;
else if (i > 1) else if (i > 1)
...@@ -208,7 +191,7 @@ void testConstrainedClusters() { ...@@ -208,7 +191,7 @@ void testConstrainedClusters() {
double dist = std::sqrt((p1[0]-p2[0])*(p1[0]-p2[0])+(p1[1]-p2[1])*(p1[1]-p2[1])+(p1[2]-p2[2])*(p1[2]-p2[2])); double dist = std::sqrt((p1[0]-p2[0])*(p1[0]-p2[0])+(p1[1]-p2[1])*(p1[1]-p2[1])+(p1[2]-p2[2])*(p1[2]-p2[2]));
ASSERT_EQUAL_TOL(distance, dist, 2e-5); ASSERT_EQUAL_TOL(distance, dist, 2e-5);
} }
double energy = computeEnergy(state, system, integrator.getStepSize()); double energy = state.getPotentialEnergy()+state.getKineticEnergy();
if (i == 1) if (i == 1)
initialEnergy = energy; initialEnergy = energy;
else if (i > 1) else if (i > 1)
......
...@@ -175,6 +175,10 @@ protected: ...@@ -175,6 +175,10 @@ protected:
* Get the names of all Kernels used by this Integrator. * Get the names of all Kernels used by this Integrator.
*/ */
std::vector<std::string> getKernelNames(); std::vector<std::string> getKernelNames();
/**
* Compute the kinetic energy of the system at the current time.
*/
double computeKineticEnergy();
private: private:
double temperature, friction; double temperature, friction;
int numCopies, randomNumberSeed; int numCopies, randomNumberSeed;
......
...@@ -80,6 +80,10 @@ public: ...@@ -80,6 +80,10 @@ public:
* Copy positions and velocities for one copy into the context. * Copy positions and velocities for one copy into the context.
*/ */
virtual void copyToContext(int copy, ContextImpl& context) = 0; virtual void copyToContext(int copy, ContextImpl& context) = 0;
/**
* Compute the kinetic energy.
*/
virtual double computeKineticEnergy(ContextImpl& context, const RPMDIntegrator& integrator) = 0;
}; };
} // namespace OpenMM } // namespace OpenMM
......
...@@ -90,6 +90,10 @@ State RPMDIntegrator::getState(int copy, int types, bool enforcePeriodicBox, int ...@@ -90,6 +90,10 @@ State RPMDIntegrator::getState(int copy, int types, bool enforcePeriodicBox, int
return context->getOwner().getState(types, enforcePeriodicBox, groups); return context->getOwner().getState(types, enforcePeriodicBox, groups);
} }
double RPMDIntegrator::computeKineticEnergy() {
kernel.getAs<IntegrateRPMDStepKernel>().computeKineticEnergy(*context, *this);
}
void RPMDIntegrator::step(int steps) { void RPMDIntegrator::step(int steps) {
if (!hasSetPosition) { if (!hasSetPosition) {
// Initialize the positions from the context. // Initialize the positions from the context.
......
...@@ -185,6 +185,10 @@ void OpenCLIntegrateRPMDStepKernel::computeForces(ContextImpl& context) { ...@@ -185,6 +185,10 @@ void OpenCLIntegrateRPMDStepKernel::computeForces(ContextImpl& context) {
} }
} }
double OpenCLIntegrateRPMDStepKernel::computeKineticEnergy(ContextImpl& context, const RPMDIntegrator& integrator) {
return cl.getIntegrationUtilities().computeKineticEnergy(0);
}
void OpenCLIntegrateRPMDStepKernel::setPositions(int copy, const vector<Vec3>& pos) { void OpenCLIntegrateRPMDStepKernel::setPositions(int copy, const vector<Vec3>& pos) {
if (positions == NULL) if (positions == NULL)
throw OpenMMException("RPMDIntegrator: Cannot set positions before the integrator is added to a Context"); throw OpenMMException("RPMDIntegrator: Cannot set positions before the integrator is added to a Context");
......
...@@ -64,6 +64,13 @@ public: ...@@ -64,6 +64,13 @@ public:
* false to show that cached forces are invalid and must be recalculated * false to show that cached forces are invalid and must be recalculated
*/ */
void execute(ContextImpl& context, const RPMDIntegrator& integrator, bool forcesAreValid); void execute(ContextImpl& context, const RPMDIntegrator& integrator, bool forcesAreValid);
/**
* Compute the kinetic energy.
*
* @param context the context in which to execute this kernel
* @param integrator the RPMDIntegrator this kernel is being used for
*/
double computeKineticEnergy(ContextImpl& context, const RPMDIntegrator& integrator);
/** /**
* Get the positions of all particles in one copy of the system. * Get the positions of all particles in one copy of the system.
*/ */
......
...@@ -320,6 +320,21 @@ void ReferenceIntegrateRPMDStepKernel::execute(ContextImpl& context, const RPMDI ...@@ -320,6 +320,21 @@ void ReferenceIntegrateRPMDStepKernel::execute(ContextImpl& context, const RPMDI
// } // }
//} //}
double ReferenceIntegrateRPMDStepKernel::computeKineticEnergy(ContextImpl& context, const RPMDIntegrator& integrator) {
System& system = context.getSystem();
int numParticles = system.getNumParticles();
vector<RealVec>& velData = extractVelocities(context);
double energy = 0.0;
for (int i = 0; i < numParticles; ++i) {
double mass = system.getParticleMass(i);
if (mass > 0) {
RealVec v = velData[i];
energy += mass*(v.dot(v));
}
}
return 0.5*energy;
}
void ReferenceIntegrateRPMDStepKernel::setPositions(int copy, const vector<Vec3>& pos) { void ReferenceIntegrateRPMDStepKernel::setPositions(int copy, const vector<Vec3>& pos) {
int numParticles = positions[copy].size(); int numParticles = positions[copy].size();
for (int i = 0; i < numParticles; i++) for (int i = 0; i < numParticles; i++)
......
...@@ -65,6 +65,13 @@ public: ...@@ -65,6 +65,13 @@ public:
* false to show that cached forces are invalid and must be recalculated * false to show that cached forces are invalid and must be recalculated
*/ */
void execute(ContextImpl& context, const RPMDIntegrator& integrator, bool forcesAreValid); void execute(ContextImpl& context, const RPMDIntegrator& integrator, bool forcesAreValid);
/**
* Compute the kinetic energy.
*
* @param context the context in which to execute this kernel
* @param integrator the RPMDIntegrator this kernel is being used for
*/
double computeKineticEnergy(ContextImpl& context, const RPMDIntegrator& integrator);
/** /**
* Get the positions of all particles in one copy of the system. * Get the positions of all particles in one copy of the system.
*/ */
......
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