Commit c0ccf9e3 authored by peastman's avatar peastman
Browse files

Created API and reference implementation for ring polymer contraction

parent 178aa003
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,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: *
* * * *
...@@ -37,6 +37,7 @@ ...@@ -37,6 +37,7 @@
#include "openmm/State.h" #include "openmm/State.h"
#include "openmm/Vec3.h" #include "openmm/Vec3.h"
#include "openmm/internal/windowsExportRpmd.h" #include "openmm/internal/windowsExportRpmd.h"
#include <map>
namespace OpenMM { namespace OpenMM {
...@@ -52,6 +53,14 @@ namespace OpenMM { ...@@ -52,6 +53,14 @@ namespace OpenMM {
* to set them for specific copies of the System. Similarly, you should retrieve state information * to set them for specific copies of the System. Similarly, you should retrieve state information
* for particular copies by calling getState() on the Integrator. Do not query the Context for * for particular copies by calling getState() on the Integrator. Do not query the Context for
* state information. * state information.
*
* You can optionally specify a set of "ring polymer contractions", by which different force
* groups are evaluated on different numbers of copies, instead of computing every force on
* every copy. This can be much more efficient, since different forces may vary widely in
* how many times they must be evaluated to produce sufficient accuracy. For example, you
* might simulate a 32 copy ring polymer and evaluate bonded forces on every copy, but contract
* it down to only 6 copies for computing nonbonded interactions, and down to only a single
* copy (the centroid) for computing the reciprocal space part of PME.
*/ */
class OPENMM_EXPORT_RPMD RPMDIntegrator : public Integrator { class OPENMM_EXPORT_RPMD RPMDIntegrator : public Integrator {
...@@ -65,6 +74,19 @@ public: ...@@ -65,6 +74,19 @@ public:
* @param stepSize the step size with which to integrator the system (in picoseconds) * @param stepSize the step size with which to integrator the system (in picoseconds)
*/ */
RPMDIntegrator(int numCopies, double temperature, double frictionCoeff, double stepSize); RPMDIntegrator(int numCopies, double temperature, double frictionCoeff, double stepSize);
/**
* Create a RPMDIntegrator.
*
* @param numCopies the number of copies of the system that should be simulated
* @param temperature the temperature of the heat bath (in Kelvin)
* @param frictionCoeff the friction coefficient which couples the system to the heat bath (in inverse picoseconds)
* @param stepSize the step size with which to integrator the system (in picoseconds)
* @param contractions the ring polymer contractions to use for evaluating different force groups. Each key in the
* map is the index of a force group, and the corresponding value is the number of copies to evaluate
* that force group on. If no entry is provided for a force group (the default), it is evaluated
* independently on every copy.
*/
RPMDIntegrator(int numCopies, double temperature, double frictionCoeff, double stepSize, const std::map<int, int>& contractions);
/** /**
* Get the number of copies of the system being simulated. * Get the number of copies of the system being simulated.
*/ */
...@@ -122,6 +144,15 @@ public: ...@@ -122,6 +144,15 @@ public:
void setRandomNumberSeed(int seed) { void setRandomNumberSeed(int seed) {
randomNumberSeed = seed; randomNumberSeed = seed;
} }
/**
* Get the ring polymer contractions to use for evaluating different force groups. Each key in the
* map is the index of a force group, and the corresponding value is the number of copies to evaluate
* that force group on. If no entry is provided for a force group, it is evaluated independently on
* every copy.
*/
const std::map<int, int>& getContractions() const {
return contractions;
}
/** /**
* Set the positions of all particles in one copy of the system. * Set the positions of all particles in one copy of the system.
* *
...@@ -182,6 +213,7 @@ protected: ...@@ -182,6 +213,7 @@ protected:
private: private:
double temperature, friction; double temperature, friction;
int numCopies, randomNumberSeed; int numCopies, randomNumberSeed;
std::map<int, int> contractions;
bool forcesAreValid, hasSetPosition, hasSetVelocity, isFirstStep; bool forcesAreValid, hasSetPosition, hasSetVelocity, isFirstStep;
Kernel kernel; Kernel kernel;
}; };
......
...@@ -39,15 +39,23 @@ ...@@ -39,15 +39,23 @@
#include <string> #include <string>
using namespace OpenMM; using namespace OpenMM;
using std::string; using namespace std;
using std::vector;
RPMDIntegrator::RPMDIntegrator(int numCopies, double temperature, double frictionCoeff, double stepSize, const map<int, int>& contractions) :
numCopies(numCopies), contractions(contractions), forcesAreValid(false), hasSetPosition(false), hasSetVelocity(false), isFirstStep(true) {
setTemperature(temperature);
setFriction(frictionCoeff);
setStepSize(stepSize);
setConstraintTolerance(1e-5);
setRandomNumberSeed((int) time(NULL));
}
RPMDIntegrator::RPMDIntegrator(int numCopies, double temperature, double frictionCoeff, double stepSize) : RPMDIntegrator::RPMDIntegrator(int numCopies, double temperature, double frictionCoeff, double stepSize) :
numCopies(numCopies), forcesAreValid(false), hasSetPosition(false), hasSetVelocity(false), isFirstStep(true) { numCopies(numCopies), forcesAreValid(false), hasSetPosition(false), hasSetVelocity(false), isFirstStep(true) {
setTemperature(temperature); setTemperature(temperature);
setFriction(frictionCoeff); setFriction(frictionCoeff);
setStepSize(stepSize); setStepSize(stepSize);
setConstraintTolerance(1e-4); setConstraintTolerance(1e-5);
setRandomNumberSeed((int) time(NULL)); setRandomNumberSeed((int) time(NULL));
} }
......
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
* -------------------------------------------------------------------------- */ * -------------------------------------------------------------------------- */
#include "ReferenceRpmdKernels.h" #include "ReferenceRpmdKernels.h"
#include "openmm/OpenMMException.h"
#include "openmm/internal/ContextImpl.h" #include "openmm/internal/ContextImpl.h"
#include "SimTKUtilities/SimTKOpenMMUtilities.h" #include "SimTKUtilities/SimTKOpenMMUtilities.h"
...@@ -54,7 +55,11 @@ static vector<RealVec>& extractForces(ContextImpl& context) { ...@@ -54,7 +55,11 @@ static vector<RealVec>& extractForces(ContextImpl& context) {
ReferenceIntegrateRPMDStepKernel::~ReferenceIntegrateRPMDStepKernel() { ReferenceIntegrateRPMDStepKernel::~ReferenceIntegrateRPMDStepKernel() {
if (fft != NULL) if (fft != NULL)
fftpack_destroy(fft); fftpack_destroy(fft);
for (map<int, fftpack*>::const_iterator iter = contractionFFT.begin(); iter != contractionFFT.end(); ++iter)
if (iter->second != NULL)
fftpack_destroy(iter->second);
} }
void ReferenceIntegrateRPMDStepKernel::initialize(const System& system, const RPMDIntegrator& integrator) { void ReferenceIntegrateRPMDStepKernel::initialize(const System& system, const RPMDIntegrator& integrator) {
int numCopies = integrator.getNumCopies(); int numCopies = integrator.getNumCopies();
int numParticles = system.getNumParticles(); int numParticles = system.getNumParticles();
...@@ -68,6 +73,41 @@ void ReferenceIntegrateRPMDStepKernel::initialize(const System& system, const RP ...@@ -68,6 +73,41 @@ void ReferenceIntegrateRPMDStepKernel::initialize(const System& system, const RP
} }
fftpack_init_1d(&fft, numCopies); fftpack_init_1d(&fft, numCopies);
SimTKOpenMMUtilities::setRandomNumberSeed((unsigned int) integrator.getRandomNumberSeed()); SimTKOpenMMUtilities::setRandomNumberSeed((unsigned int) integrator.getRandomNumberSeed());
// Build a list of contractions.
groupsNotContracted = -1;
const map<int, int>& contractions = integrator.getContractions();
int maxContractedCopies = 0;
for (map<int, int>::const_iterator iter = contractions.begin(); iter != contractions.end(); ++iter) {
int group = iter->first;
int copies = iter->second;
if (group < 0 || group > 31)
throw OpenMMException("RPMDIntegrator: Force group must be between 0 and 31");
if (copies < 0 || copies > numCopies)
throw OpenMMException("RPMDIntegrator: Number of copies for contraction cannot be greater than the total number of copies being simulated");
if (copies != numCopies) {
if (groupsByCopies.find(copies) == groupsByCopies.end()) {
groupsByCopies[copies] = 1<<group;
groupsNotContracted -= 1<<group;
contractionFFT[copies] = NULL;
fftpack_init_1d(&contractionFFT[copies], copies);
if (copies > maxContractedCopies)
maxContractedCopies = copies;
}
else
groupsByCopies[copies] |= 1<<group;
}
}
// Create workspace for doing contractions.
contractedPositions.resize(maxContractedCopies);
contractedForces.resize(maxContractedCopies);
for (int i = 0; i < maxContractedCopies; i++) {
contractedPositions[i].resize(numParticles);
contractedForces[i].resize(numParticles);
}
} }
void ReferenceIntegrateRPMDStepKernel::execute(ContextImpl& context, const RPMDIntegrator& integrator, bool forcesAreValid) { void ReferenceIntegrateRPMDStepKernel::execute(ContextImpl& context, const RPMDIntegrator& integrator, bool forcesAreValid) {
...@@ -82,15 +122,8 @@ void ReferenceIntegrateRPMDStepKernel::execute(ContextImpl& context, const RPMDI ...@@ -82,15 +122,8 @@ void ReferenceIntegrateRPMDStepKernel::execute(ContextImpl& context, const RPMDI
// Loop over copies and compute the force on each one. // Loop over copies and compute the force on each one.
if (!forcesAreValid) { if (!forcesAreValid)
for (int i = 0; i < numCopies; i++) { computeForces(context, integrator);
pos = positions[i];
vel = velocities[i];
context.computeVirtualSites();
context.calcForcesAndEnergy(true, false);
forces[i] = f;
}
}
// Apply the PILE-L thermostat. // Apply the PILE-L thermostat.
...@@ -176,16 +209,7 @@ void ReferenceIntegrateRPMDStepKernel::execute(ContextImpl& context, const RPMDI ...@@ -176,16 +209,7 @@ void ReferenceIntegrateRPMDStepKernel::execute(ContextImpl& context, const RPMDI
// Calculate forces based on the updated positions. // Calculate forces based on the updated positions.
for (int i = 0; i < numCopies; i++) { computeForces(context, integrator);
pos = positions[i];
vel = velocities[i];
context.computeVirtualSites();
context.updateContextState();
positions[i] = pos;
velocities[i] = vel;
context.calcForcesAndEnergy(true, false);
forces[i] = f;
}
// Update velocities. // Update velocities.
...@@ -234,6 +258,90 @@ void ReferenceIntegrateRPMDStepKernel::execute(ContextImpl& context, const RPMDI ...@@ -234,6 +258,90 @@ void ReferenceIntegrateRPMDStepKernel::execute(ContextImpl& context, const RPMDI
context.setTime(context.getTime()+dt); context.setTime(context.getTime()+dt);
} }
void ReferenceIntegrateRPMDStepKernel::computeForces(ContextImpl& context, const RPMDIntegrator& integrator) {
const int totalCopies = positions.size();
const int numParticles = positions[0].size();
vector<RealVec>& pos = extractPositions(context);
vector<RealVec>& vel = extractVelocities(context);
vector<RealVec>& f = extractForces(context);
// Compute forces from all groups that didn't have a specified contraction.
for (int i = 0; i < totalCopies; i++) {
pos = positions[i];
vel = velocities[i];
context.computeVirtualSites();
context.updateContextState();
positions[i] = pos;
velocities[i] = vel;
context.calcForcesAndEnergy(true, false, groupsNotContracted);
forces[i] = f;
}
// Now loop over contractions and compute forces from them.
for (map<int, int>::const_iterator iter = groupsByCopies.begin(); iter != groupsByCopies.end(); ++iter) {
int copies = iter->first;
int groupFlags = iter->second;
fftpack* shortFFT = contractionFFT[copies];
// Find the contracted positions.
vector<t_complex> q(totalCopies);
const RealOpenMM scale1 = 1.0/totalCopies;
for (int particle = 0; particle < numParticles; particle++) {
for (int component = 0; component < 3; component++) {
// Transform to the frequency domain, set high frequency components to zero, and transform back.
for (int k = 0; k < totalCopies; k++)
q[k] = t_complex(positions[k][particle][component], 0.0);
fftpack_exec_1d(fft, FFTPACK_FORWARD, &q[0], &q[0]);
if (copies > 1) {
int start = (copies+1)/2;
int end = totalCopies-copies+start;
for (int k = end; k < totalCopies; k++)
q[k-(totalCopies-copies)] = q[k];
fftpack_exec_1d(shortFFT, FFTPACK_BACKWARD, &q[0], &q[0]);
}
for (int k = 0; k < copies; k++)
contractedPositions[k][particle][component] = scale1*q[k].re;
}
}
// Compute forces.
for (int i = 0; i < copies; i++) {
pos = contractedPositions[i];
context.computeVirtualSites();
context.calcForcesAndEnergy(true, false, groupFlags);
contractedForces[i] = f;
}
// Apply the forces to the original copies.
const RealOpenMM scale2 = 1.0/copies;
for (int particle = 0; particle < numParticles; particle++) {
for (int component = 0; component < 3; component++) {
// Transform to the frequency domain, pad with zeros, and transform back.
for (int k = 0; k < copies; k++)
q[k] = t_complex(contractedForces[k][particle][component], 0.0);
if (copies > 1)
fftpack_exec_1d(shortFFT, FFTPACK_FORWARD, &q[0], &q[0]);
int start = (copies+1)/2;
int end = totalCopies-copies+start;
for (int k = end; k < totalCopies; k++)
q[k] = q[k-(totalCopies-copies)];
for (int k = start; k < end; k++)
q[k] = t_complex(0, 0);
fftpack_exec_1d(fft, FFTPACK_BACKWARD, &q[0], &q[0]);
for (int k = 0; k < totalCopies; k++)
forces[k][particle][component] = scale2*q[k].re;
}
}
}
}
double ReferenceIntegrateRPMDStepKernel::computeKineticEnergy(ContextImpl& context, const RPMDIntegrator& integrator) { double ReferenceIntegrateRPMDStepKernel::computeKineticEnergy(ContextImpl& context, const RPMDIntegrator& integrator) {
const System& system = context.getSystem(); const System& system = context.getSystem();
int numParticles = system.getNumParticles(); int numParticles = system.getNumParticles();
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,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) 2011 Stanford University and the Authors. * * Portions copyright (c) 2011-2013 Stanford University and the Authors. *
* Authors: Peter Eastman * * Authors: Peter Eastman *
* Contributors: * * Contributors: *
* * * *
...@@ -85,10 +85,16 @@ public: ...@@ -85,10 +85,16 @@ public:
*/ */
void copyToContext(int copy, ContextImpl& context); void copyToContext(int copy, ContextImpl& context);
private: private:
void computeForces(ContextImpl& context, const RPMDIntegrator& integrator);
std::vector<std::vector<RealVec> > positions; std::vector<std::vector<RealVec> > positions;
std::vector<std::vector<RealVec> > velocities; std::vector<std::vector<RealVec> > velocities;
std::vector<std::vector<RealVec> > forces; std::vector<std::vector<RealVec> > forces;
std::vector<std::vector<RealVec> > contractedPositions;
std::vector<std::vector<RealVec> > contractedForces;
std::map<int, int> groupsByCopies;
int groupsNotContracted;
fftpack* fft; fftpack* fft;
std::map<int, fftpack*> contractionFFT;
}; };
} // namespace OpenMM } // namespace OpenMM
......
...@@ -237,11 +237,88 @@ void testVirtualSites() { ...@@ -237,11 +237,88 @@ void testVirtualSites() {
ASSERT_USUALLY_EQUAL_TOL(expectedKE, meanKE, 1e-2); ASSERT_USUALLY_EQUAL_TOL(expectedKE, meanKE, 1e-2);
} }
void testContractions() {
const int gridSize = 3;
const int numMolecules = gridSize*gridSize*gridSize;
const int numParticles = numMolecules*2;
const int numCopies = 10;
const double spacing = 2.0;
const double cutoff = 3.0;
const double boxSize = spacing*(gridSize+1);
const double temperature = 300.0;
System system;
system.setDefaultPeriodicBoxVectors(Vec3(boxSize, 0, 0), Vec3(0, boxSize, 0), Vec3(0, 0, boxSize));
HarmonicBondForce* bonds = new HarmonicBondForce();
system.addForce(bonds);
NonbondedForce* nonbonded = new NonbondedForce();
nonbonded->setCutoffDistance(cutoff);
nonbonded->setNonbondedMethod(NonbondedForce::PME);
nonbonded->setForceGroup(1);
nonbonded->setReciprocalSpaceForceGroup(2);
system.addForce(nonbonded);
// Create a cloud of molecules.
OpenMM_SFMT::SFMT sfmt;
init_gen_rand(0, sfmt);
vector<Vec3> positions(numParticles);
for (int i = 0; i < numMolecules; i++) {
system.addParticle(1.0);
system.addParticle(1.0);
nonbonded->addParticle(-0.2, 0.2, 0.2);
nonbonded->addParticle(0.2, 0.2, 0.2);
nonbonded->addException(2*i, 2*i+1, 0, 1, 0);
bonds->addBond(2*i, 2*i+1, 1.0, 10000.0);
}
map<int, int> contractions;
contractions[1] = 3;
contractions[2] = 1;
RPMDIntegrator integ(numCopies, temperature, 10.0, 0.001, contractions);
Platform& platform = Platform::getPlatformByName("Reference");
Context context(system, integ, platform);
for (int copy = 0; copy < numCopies; copy++) {
for (int i = 0; i < gridSize; i++)
for (int j = 0; j < gridSize; j++)
for (int k = 0; k < gridSize; k++) {
Vec3 pos = Vec3(spacing*(i+0.02*genrand_real2(sfmt)), spacing*(j+0.02*genrand_real2(sfmt)), spacing*(k+0.02*genrand_real2(sfmt)));
int index = k+gridSize*(j+gridSize*i);
positions[2*index] = pos;
positions[2*index+1] = Vec3(pos[0]+1.0, pos[1], pos[2]);
}
integ.setPositions(copy, positions);
}
// Check the temperature.
const int numSteps = 1000;
integ.step(1000);
vector<double> ke(numCopies, 0.0);
for (int i = 0; i < numSteps; i++) {
integ.step(1);
vector<State> state(numCopies);
for (int j = 0; j < numCopies; j++)
state[j] = integ.getState(j, State::Velocities, true);
for (int j = 0; j < numParticles; j++) {
for (int k = 0; k < numCopies; k++) {
Vec3 v = state[k].getVelocities()[j];
ke[k] += 0.5*system.getParticleMass(j)*v.dot(v);
}
}
}
double meanKE = 0.0;
for (int i = 0; i < numCopies; i++)
meanKE += ke[i];
meanKE /= numSteps*numCopies;
double expectedKE = 0.5*numCopies*numParticles*3*BOLTZ*temperature;
ASSERT_USUALLY_EQUAL_TOL(expectedKE, meanKE, 1e-2);
}
int main() { int main() {
try { try {
testFreeParticles(); testFreeParticles();
testCMMotionRemoval(); testCMMotionRemoval();
testVirtualSites(); testVirtualSites();
testContractions();
} }
catch(const std::exception& e) { catch(const std::exception& e) {
std::cout << "exception: " << e.what() << std::endl; std::cout << "exception: " << e.what() << std::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