Commit 3a2f4643 authored by Peter Eastman's avatar Peter Eastman
Browse files

Cleanup and bugfixes to MonteCarloAnisotropicBarostat

parent 7a55f02c
...@@ -9,8 +9,8 @@ ...@@ -9,8 +9,8 @@
* 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) 2010 Stanford University and the Authors. * * Portions copyright (c) 2010-2013 Stanford University and the Authors. *
* Authors: Peter Eastman * * Authors: Peter Eastman, Lee-Ping Wang *
* Contributors: * * Contributors: *
* * * *
* Permission is hereby granted, free of charge, to any person obtaining a * * Permission is hereby granted, free of charge, to any person obtaining a *
...@@ -43,9 +43,12 @@ namespace OpenMM { ...@@ -43,9 +43,12 @@ namespace OpenMM {
* This class uses a Monte Carlo algorithm to adjust the size of the periodic box, simulating the * This class uses a Monte Carlo algorithm to adjust the size of the periodic box, simulating the
* effect of constant pressure. * effect of constant pressure.
* *
* Compared to MonteCarloBarostat, this class scales the three axes of the simulation cell independently. * This class is similar to MonteCarloBarostat, but each Monte Carlo move is applied to only one axis
* The user supplies three doubles to specify the pressure along each axis. * of the periodic box (unlike MonteCarloBarostat, which scales the entire box isotropically). This
* * means that the box may change shape as well as size over the course of the simulation. It also
* allows you to specify a different pressure for each axis of the box, or to keep the box size fixed
* along certain axes while still allowing it to change along others.
*
* This class assumes the simulation is also being run at constant temperature, and requires you * This class assumes the simulation is also being run at constant temperature, and requires you
* to specify the system temperature (since it affects the acceptance probability for Monte Carlo * to specify the system temperature (since it affects the acceptance probability for Monte Carlo
* moves). It does not actually perform temperature regulation, however. You must use another * moves). It does not actually perform temperature regulation, however. You must use another
...@@ -84,39 +87,33 @@ public: ...@@ -84,39 +87,33 @@ public:
* @param defaultPressure The default pressure acting on each axis (in bar) * @param defaultPressure The default pressure acting on each axis (in bar)
* @param temperature the temperature at which the system is being maintained (in Kelvin) * @param temperature the temperature at which the system is being maintained (in Kelvin)
* @param frequency the frequency at which Monte Carlo pressure changes should be attempted (in time steps) * @param frequency the frequency at which Monte Carlo pressure changes should be attempted (in time steps)
* @param scaleX on/off switch for whether to scale the X axis * @param scaleX whether to allow the X dimension of the periodic box to change size
* @param scaleY on/off switch for whether to scale the Y axis * @param scaleY whether to allow the Y dimension of the periodic box to change size
* @param scaleZ on/off switch for whether to scale the Z axis * @param scaleZ whether to allow the Z dimension of the periodic box to change size
*/ */
MonteCarloAnisotropicBarostat(const Vec3& defaultPressure, double temperature, int frequency = 25, bool scaleX = 1, bool scaleY = 1, bool scaleZ = 1); MonteCarloAnisotropicBarostat(const Vec3& defaultPressure, double temperature, int frequency = 25, bool scaleX = 1, bool scaleY = 1, bool scaleZ = 1);
/** /**
* Get the default pressure (in bar). * Get the default pressure (in bar).
* *
* @return the default pressure acting on the system, measured in bar. * @return the default pressure acting along each axis, measured in bar.
*/ */
Vec3 getDefaultPressure() const { const Vec3& getDefaultPressure() const {
return defaultPressure; return defaultPressure;
} }
/** /**
* Get the true/false flag for scaling the X-axis. * Get whether to allow the X dimension of the periodic box to change size.
*
* @return the true/false flag for scaling the X-axis.
*/ */
bool getScaleX() const { bool getScaleX() const {
return scaleX; return scaleX;
} }
/** /**
* Get the true/false flag for scaling the Y-axis. * Get whether to allow the Y dimension of the periodic box to change size.
*
* @return the true/false flag for scaling the Y-axis.
*/ */
bool getScaleY() const { bool getScaleY() const {
return scaleY; return scaleY;
} }
/** /**
* Get the true/false flag for scaling the Z-axis. * Get whether to allow the Z dimension of the periodic box to change size.
*
* @return the true/false flag for scaling the Z-axis.
*/ */
bool getScaleZ() const { bool getScaleZ() const {
return scaleZ; return scaleZ;
...@@ -172,15 +169,7 @@ private: ...@@ -172,15 +169,7 @@ private:
double temperature; double temperature;
bool scaleX, scaleY, scaleZ; bool scaleX, scaleY, scaleZ;
int frequency, randomNumberSeed; int frequency, randomNumberSeed;
};
double GetTemperature() const {
return temperature;
}
void SetTemperature(double temperature) {
this->temperature = temperature;
}
};
} // namespace OpenMM } // namespace OpenMM
......
...@@ -123,15 +123,7 @@ protected: ...@@ -123,15 +123,7 @@ protected:
private: private:
double defaultPressure, temperature; double defaultPressure, temperature;
int frequency, randomNumberSeed; int frequency, randomNumberSeed;
};
double GetTemperature() const {
return temperature;
}
void SetTemperature(double temperature) {
this->temperature = temperature;
}
};
} // namespace OpenMM } // namespace OpenMM
......
...@@ -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: *
* * * *
...@@ -54,6 +54,8 @@ void OPENMM_EXPORT throwException(const char* file, int line, const std::string& ...@@ -54,6 +54,8 @@ void OPENMM_EXPORT throwException(const char* file, int line, const std::string&
#define ASSERT_EQUAL_VEC(expected, found, tol) {double _norm_ = std::sqrt((expected).dot(expected)); double _scale_ = _norm_ > 1.0 ? _norm_ : 1.0; if ((std::abs(((expected)[0])-((found)[0]))/_scale_ > (tol)) || (std::abs(((expected)[1])-((found)[1]))/_scale_ > (tol)) || (std::abs(((expected)[2])-((found)[2]))/_scale_ > (tol))) {std::stringstream details; details << " Expected "<<(expected)<<", found "<<(found); throwException(__FILE__, __LINE__, details.str());}}; #define ASSERT_EQUAL_VEC(expected, found, tol) {double _norm_ = std::sqrt((expected).dot(expected)); double _scale_ = _norm_ > 1.0 ? _norm_ : 1.0; if ((std::abs(((expected)[0])-((found)[0]))/_scale_ > (tol)) || (std::abs(((expected)[1])-((found)[1]))/_scale_ > (tol)) || (std::abs(((expected)[2])-((found)[2]))/_scale_ > (tol))) {std::stringstream details; details << " Expected "<<(expected)<<", found "<<(found); throwException(__FILE__, __LINE__, details.str());}};
#define ASSERT_USUALLY_TRUE(cond) {if (!(cond)) throwException(__FILE__, __LINE__, "(This test is stochastic and may occasionally fail)");};
#define ASSERT_USUALLY_EQUAL_TOL(expected, found, tol) {double _scale_ = std::abs(expected) > 1.0 ? std::abs(expected) : 1.0; if (!(std::abs((expected)-(found))/_scale_ <= (tol))) {std::stringstream details; details << "Expected "<<(expected)<<", found "<<(found)<<" (This test is stochastic and may occasionally fail)"; throwException(__FILE__, __LINE__, details.str());}}; #define ASSERT_USUALLY_EQUAL_TOL(expected, found, tol) {double _scale_ = std::abs(expected) > 1.0 ? std::abs(expected) : 1.0; if (!(std::abs((expected)-(found))/_scale_ <= (tol))) {std::stringstream details; details << "Expected "<<(expected)<<", found "<<(found)<<" (This test is stochastic and may occasionally fail)"; throwException(__FILE__, __LINE__, details.str());}};
#define ASSERT_VALID_INDEX(index, vector) {if (index < 0 || index >= (int) vector.size()) throwException(__FILE__, __LINE__, "Index out of range");}; #define ASSERT_VALID_INDEX(index, vector) {if (index < 0 || index >= (int) vector.size()) throwException(__FILE__, __LINE__, "Index out of range");};
......
...@@ -6,8 +6,8 @@ ...@@ -6,8 +6,8 @@
* 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) 2010 Stanford University and the Authors. * * Portions copyright (c) 2010-2013 Stanford University and the Authors. *
* Authors: Peter Eastman * * Authors: Peter Eastman, Lee-Ping Wang *
* Contributors: * * Contributors: *
* * * *
* Permission is hereby granted, free of charge, to any person obtaining a * * Permission is hereby granted, free of charge, to any person obtaining a *
...@@ -65,7 +65,7 @@ void MonteCarloAnisotropicBarostatImpl::initialize(ContextImpl& context) { ...@@ -65,7 +65,7 @@ void MonteCarloAnisotropicBarostatImpl::initialize(ContextImpl& context) {
void MonteCarloAnisotropicBarostatImpl::updateContextState(ContextImpl& context) { void MonteCarloAnisotropicBarostatImpl::updateContextState(ContextImpl& context) {
if (++step < owner.getFrequency() || owner.getFrequency() == 0) if (++step < owner.getFrequency() || owner.getFrequency() == 0)
return; return;
if (owner.getScaleX() == 0 && owner.getScaleY() == 0 && owner.getScaleZ() == 0) if (!owner.getScaleX() && !owner.getScaleY() && !owner.getScaleZ())
return; return;
step = 0; step = 0;
...@@ -75,23 +75,26 @@ void MonteCarloAnisotropicBarostatImpl::updateContextState(ContextImpl& context) ...@@ -75,23 +75,26 @@ void MonteCarloAnisotropicBarostatImpl::updateContextState(ContextImpl& context)
double pressure; double pressure;
// Choose which axis to modify at random. // Choose which axis to modify at random.
double rnd = genrand_real2(random)*3.0;
int axis; int axis;
while (1) { while (true) {
if (rnd < 1.0 && owner.getScaleX()) { double rnd = genrand_real2(random)*3.0;
axis = 0; if (rnd < 1.0) {
pressure = context.getParameter(MonteCarloAnisotropicBarostat::PressureX())*(AVOGADRO*1e-25); if (owner.getScaleX()) {
break; axis = 0;
} else if (rnd < 2.0 && owner.getScaleY()) { pressure = context.getParameter(MonteCarloAnisotropicBarostat::PressureX())*(AVOGADRO*1e-25);
axis = 1; break;
pressure = context.getParameter(MonteCarloAnisotropicBarostat::PressureY())*(AVOGADRO*1e-25); }
break; } else if (rnd < 2.0) {
if (owner.getScaleY()) {
axis = 1;
pressure = context.getParameter(MonteCarloAnisotropicBarostat::PressureY())*(AVOGADRO*1e-25);
break;
}
} else if (owner.getScaleZ()) { } else if (owner.getScaleZ()) {
axis = 2; axis = 2;
pressure = context.getParameter(MonteCarloAnisotropicBarostat::PressureZ())*(AVOGADRO*1e-25); pressure = context.getParameter(MonteCarloAnisotropicBarostat::PressureZ())*(AVOGADRO*1e-25);
break; break;
} }
rnd = genrand_real2(random)*3.0;
} }
// Modify the periodic box size. // Modify the periodic box size.
...@@ -101,9 +104,7 @@ void MonteCarloAnisotropicBarostatImpl::updateContextState(ContextImpl& context) ...@@ -101,9 +104,7 @@ void MonteCarloAnisotropicBarostatImpl::updateContextState(ContextImpl& context)
double volume = box[0][0]*box[1][1]*box[2][2]; double volume = box[0][0]*box[1][1]*box[2][2];
double deltaVolume = volumeScale[axis]*2*(genrand_real2(random)-0.5); double deltaVolume = volumeScale[axis]*2*(genrand_real2(random)-0.5);
double newVolume = volume+deltaVolume; double newVolume = volume+deltaVolume;
Vec3 lengthScale; Vec3 lengthScale(1.0, 1.0, 1.0);
for (int i=0; i<3; i++)
lengthScale[i] = 1.0;
lengthScale[axis] = newVolume/volume; lengthScale[axis] = newVolume/volume;
kernel.getAs<ApplyMonteCarloBarostatKernel>().scaleCoordinates(context, lengthScale[0], lengthScale[1], lengthScale[2]); kernel.getAs<ApplyMonteCarloBarostatKernel>().scaleCoordinates(context, lengthScale[0], lengthScale[1], lengthScale[2]);
context.getOwner().setPeriodicBoxVectors(box[0]*lengthScale[0], box[1]*lengthScale[1], box[2]*lengthScale[2]); context.getOwner().setPeriodicBoxVectors(box[0]*lengthScale[0], box[1]*lengthScale[1], box[2]*lengthScale[2]);
......
...@@ -6,8 +6,8 @@ ...@@ -6,8 +6,8 @@
* 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, Lee-Ping Wang *
* Contributors: * * Contributors: *
* * * *
* Permission is hereby granted, free of charge, to any person obtaining a * * Permission is hereby granted, free of charge, to any person obtaining a *
...@@ -191,24 +191,24 @@ void testIdealGasAxis(int axis) { ...@@ -191,24 +191,24 @@ void testIdealGasAxis(int axis) {
for (int j = 0; j < steps; ++j) { for (int j = 0; j < steps; ++j) {
Vec3 box[3]; Vec3 box[3];
context.getState(0).getPeriodicBoxVectors(box[0], box[1], box[2]); context.getState(0).getPeriodicBoxVectors(box[0], box[1], box[2]);
boxX = box[0][0]; boxX = box[0][0];
boxY = box[1][1]; boxY = box[1][1];
boxZ = box[2][2]; boxZ = box[2][2];
volume += box[0][0]*box[1][1]*box[2][2]; volume += box[0][0]*box[1][1]*box[2][2];
integrator.step(frequency); integrator.step(frequency);
} }
volume /= steps; volume /= steps;
double expected = (numParticles+1)*BOLTZ*temp[i]/pressureInMD; double expected = (numParticles+1)*BOLTZ*temp[i]/pressureInMD;
ASSERT_USUALLY_EQUAL_TOL(expected, volume, 3/std::sqrt((double) steps)); ASSERT_USUALLY_EQUAL_TOL(expected, volume, 3/std::sqrt((double) steps));
if (!scaleX) { if (!scaleX) {
ASSERT(boxX == initialLength); ASSERT(boxX == initialLength);
} }
if (!scaleY) { if (!scaleY) {
ASSERT(boxY == 0.5*initialLength); ASSERT(boxY == 0.5*initialLength);
} }
if (!scaleZ) { if (!scaleZ) {
ASSERT(boxZ == 2*initialLength); ASSERT(boxZ == 2*initialLength);
} }
} }
} }
...@@ -274,155 +274,148 @@ void testRandomSeed() { ...@@ -274,155 +274,148 @@ void testRandomSeed() {
} }
} }
/**
* Run a constant pressure simulation on an anisotropic Einstein crystal
* using isotropic and anisotropic barostats. There are a total of 15 simulations:
*
* 1) 3 pressures: 9.0, 10.0, 11.0 bar, for each of the following groups:
* 2) 3 groups of simulations that scale just one axis: x, y, z
* 3) 1 group of simulations that scales all three axes in the anisotropic barostat
* 4) 1 group of simulations that scales all three axes in the isotropic barostat
*
* Results that we will check:
*
* a) In each group of simulations, the volume should decrease with increasing pressure
* b) In the three simulation groups that scale just one axis, the compressibility (i.e. incremental volume change
* with increasing pressure) should go like kx > ky > kz (because the spring constant is largest in the z-direction)
* c) The anisotropic barostat should produce the same result as the isotropic barostat when all three axes are scaled
*/
void testEinsteinCrystal() { void testEinsteinCrystal() {
/* const int numParticles = 64;
const int frequency = 2;
Run a constant pressure simulation on an anisotropic Einstein crystal const int equil = 10000;
using isotropic and anisotropic barostats. There are a total of 15 simulations: const int steps = 5000;
const double pressure = 10.0;
1) 3 pressures: 9.0, 10.0, 11.0 bar, for each of the following groups: const double pressureInMD = pressure*(AVOGADRO*1e-25); // pressure in kJ/mol/nm^3
2) 3 groups of simulations that scale just one axis: x, y, z const double temp = 300.0; // Only test one temperature since we're looking at three pressures.
3) 1 group of simulations that scales all three axes in the anisotropic barostat const double pres3[] = {2.0, 8.0, 15.0};
4) 1 group of simulations that scales all three axes in the isotropic barostat const double initialVolume = numParticles*BOLTZ*temp/pressureInMD;
const double initialLength = std::pow(initialVolume, 1.0/3.0);
Results that we will check: vector<double> initialPositions(3);
vector<double> results;
a) In each group of simulations, the volume should decrease with increasing pressure // Run four groups of anisotropic simulations; scaling just x, y, z, then all three.
b) In the three simulation groups that scale just one axis, the compressibility (i.e. incremental volume change for (int a = 0; a < 4; a++) {
with increasing pressure) should go like kx > ky > kz (because the spring constant is largest in the z-direction) // Test barostat for three different pressures.
c) The anisotropic barostat should produce the same result as the isotropic barostat when all three axes are scaled for (int p = 0; p < 3; p++) {
// Create a system of noninteracting particles attached by harmonic springs to their initial positions.
*/ System system;
system.setDefaultPeriodicBoxVectors(Vec3(initialLength, 0, 0), Vec3(0, initialLength, 0), Vec3(0, 0, initialLength));
const int numParticles = 64; vector<Vec3> positions(numParticles);
const int frequency = 10; OpenMM_SFMT::SFMT sfmt;
const int equil = 10000; init_gen_rand(0, sfmt);
const int steps = 10000; // Anisotropic force constants.
const double pressure = 10.0; CustomExternalForce* force = new CustomExternalForce("0.005*(x-x0)^2 + 0.01*(y-y0)^2 + 0.02*(z-z0)^2");
const double pressureInMD = pressure*(AVOGADRO*1e-25); // pressure in kJ/mol/nm^3 force->addPerParticleParameter("x0");
const double temp = 300.0; // Only test one temperature since we're looking at three pressures. force->addPerParticleParameter("y0");
const double pres3[] = {9.0, 10.0, 11.0}; force->addPerParticleParameter("z0");
const double initialVolume = numParticles*BOLTZ*temp/pressureInMD; NonbondedForce* nb = new NonbondedForce();
const double initialLength = std::pow(initialVolume, 1.0/3.0); nb->setNonbondedMethod(NonbondedForce::CutoffPeriodic);
vector<double> initialPositions(3); for (int i = 0; i < numParticles; ++i) {
// All results. system.addParticle(1.0);
double results[] = {0.0, 0.0, 0.0, 0.0, 0.0, positions[i] = Vec3(((i/16)%4+0.5)*initialLength/4, ((i/4)%4+0.5)*initialLength/4, (i%4+0.5)*initialLength/4);
0.0, 0.0, 0.0, 0.0, 0.0, initialPositions[0] = positions[i][0];
0.0, 0.0, 0.0, 0.0, 0.0}; initialPositions[1] = positions[i][1];
int simNum = 0; initialPositions[2] = positions[i][2];
// Run four groups of anisotropic simulations; scaling just x, y, z, then all three. force->addParticle(i, initialPositions);
for (int a = 0; a < 4; a++) { nb->addParticle(0, initialLength/6, 0.1);
// Test barostat for three different pressures. }
for (int p = 0; p < 3; p++) { system.addForce(force);
// Create a system of noninteracting particles attached by harmonic springs to their initial positions. system.addForce(nb);
System system; // Create the barostat.
system.setDefaultPeriodicBoxVectors(Vec3(initialLength, 0, 0), Vec3(0, initialLength, 0), Vec3(0, 0, initialLength)); MonteCarloAnisotropicBarostat* barostat = new MonteCarloAnisotropicBarostat(Vec3(pres3[p], pres3[p], pres3[p]), temp, frequency, (a==0||a==3), (a==1||a==3), (a==2||a==3));
vector<Vec3> positions(numParticles); system.addForce(barostat);
OpenMM_SFMT::SFMT sfmt; barostat->setTemperature(temp);
init_gen_rand(0, sfmt); LangevinIntegrator integrator(temp, 0.1, 0.01);
// Anisotropic force constants. Context context(system, integrator, platform);
CustomExternalForce* force = new CustomExternalForce("0.005*(x-x0)^2 + 0.01*(y-y0)^2 + 0.02*(z-z0)^2"); context.setPositions(positions);
force->addPerParticleParameter("x0"); // Let it equilibrate.
force->addPerParticleParameter("y0"); integrator.step(equil);
force->addPerParticleParameter("z0"); // Now run it for a while and see if the volume is correct.
for (int i = 0; i < numParticles; ++i) { double volume = 0.0;
system.addParticle(1.0); for (int j = 0; j < steps; ++j) {
positions[i] = Vec3(((i/16)%4+0.5)*initialLength/4, ((i/4)%4+0.5)*initialLength/4, (i%4+0.5)*initialLength/4); Vec3 box[3];
initialPositions[0] = positions[i][0]; context.getState(0).getPeriodicBoxVectors(box[0], box[1], box[2]);
initialPositions[1] = positions[i][1]; volume += box[0][0]*box[1][1]*box[2][2];
initialPositions[2] = positions[i][2]; integrator.step(frequency);
force->addParticle(i, initialPositions); }
} volume /= steps;
system.addForce(force); results.push_back(volume);
// Create the barostat. }
MonteCarloAnisotropicBarostat* barostat = new MonteCarloAnisotropicBarostat(Vec3(pres3[p], pres3[p], pres3[p]), temp, frequency, (a==0||a==3), (a==1||a==3), (a==2||a==3));
system.addForce(barostat);
barostat->setTemperature(temp);
LangevinIntegrator integrator(temp, 0.1, 0.01);
Context context(system, integrator, platform);
context.setPositions(positions);
// Let it equilibrate.
integrator.step(equil);
// Now run it for a while and see if the volume is correct.
double volume = 0.0;
for (int j = 0; j < steps; ++j) {
Vec3 box[3];
context.getState(0).getPeriodicBoxVectors(box[0], box[1], box[2]);
volume += box[0][0]*box[1][1]*box[2][2];
integrator.step(frequency);
}
volume /= steps;
results[simNum] = volume;
simNum += 1;
}
}
for (int p = 0; p < 3; p++) {
// Create a system of noninteracting particles attached by harmonic springs to their initial positions.
System system;
system.setDefaultPeriodicBoxVectors(Vec3(initialLength, 0, 0), Vec3(0, initialLength, 0), Vec3(0, 0, initialLength));
vector<Vec3> positions(numParticles);
OpenMM_SFMT::SFMT sfmt;
init_gen_rand(0, sfmt);
// Anisotropic force constants.
CustomExternalForce* force = new CustomExternalForce("0.005*(x-x0)^2 + 0.01*(y-y0)^2 + 0.02*(z-z0)^2");
force->addPerParticleParameter("x0");
force->addPerParticleParameter("y0");
force->addPerParticleParameter("z0");
for (int i = 0; i < numParticles; ++i) {
system.addParticle(1.0);
positions[i] = Vec3(((i/16)%4+0.5)*initialLength/4, ((i/4)%4+0.5)*initialLength/4, (i%4+0.5)*initialLength/4);
initialPositions[0] = positions[i][0];
initialPositions[1] = positions[i][1];
initialPositions[2] = positions[i][2];
force->addParticle(i, initialPositions);
} }
system.addForce(force); for (int p = 0; p < 3; p++) {
// Create the barostat. // Create a system of noninteracting particles attached by harmonic springs to their initial positions.
MonteCarloBarostat* barostat = new MonteCarloBarostat(pres3[p], temp, frequency); System system;
system.addForce(barostat); system.setDefaultPeriodicBoxVectors(Vec3(initialLength, 0, 0), Vec3(0, initialLength, 0), Vec3(0, 0, initialLength));
barostat->setTemperature(temp); vector<Vec3> positions(numParticles);
LangevinIntegrator integrator(temp, 0.1, 0.01); OpenMM_SFMT::SFMT sfmt;
Context context(system, integrator, platform); init_gen_rand(0, sfmt);
context.setPositions(positions); // Anisotropic force constants.
// Let it equilibrate. CustomExternalForce* force = new CustomExternalForce("0.005*(x-x0)^2 + 0.01*(y-y0)^2 + 0.02*(z-z0)^2");
integrator.step(equil); force->addPerParticleParameter("x0");
// Now run it for a while and see if the volume is correct. force->addPerParticleParameter("y0");
double volume = 0.0; force->addPerParticleParameter("z0");
for (int j = 0; j < steps; ++j) { NonbondedForce* nb = new NonbondedForce();
Vec3 box[3]; nb->setNonbondedMethod(NonbondedForce::CutoffPeriodic);
context.getState(0).getPeriodicBoxVectors(box[0], box[1], box[2]); for (int i = 0; i < numParticles; ++i) {
volume += box[0][0]*box[1][1]*box[2][2]; system.addParticle(1.0);
integrator.step(frequency); positions[i] = Vec3(((i/16)%4+0.5)*initialLength/4, ((i/4)%4+0.5)*initialLength/4, (i%4+0.5)*initialLength/4);
initialPositions[0] = positions[i][0];
initialPositions[1] = positions[i][1];
initialPositions[2] = positions[i][2];
force->addParticle(i, initialPositions);
nb->addParticle(0, initialLength/6, 0.1);
}
system.addForce(force);
system.addForce(nb);
// Create the barostat.
MonteCarloBarostat* barostat = new MonteCarloBarostat(pres3[p], temp, frequency);
system.addForce(barostat);
barostat->setTemperature(temp);
LangevinIntegrator integrator(temp, 0.1, 0.001);
Context context(system, integrator, platform);
context.setPositions(positions);
// Let it equilibrate.
integrator.step(equil);
// Now run it for a while and see if the volume is correct.
double volume = 0.0;
for (int j = 0; j < steps; ++j) {
Vec3 box[3];
context.getState(0).getPeriodicBoxVectors(box[0], box[1], box[2]);
volume += box[0][0]*box[1][1]*box[2][2];
integrator.step(frequency);
}
volume /= steps;
results.push_back(volume);
} }
volume /= steps;
results[simNum] = volume; // Check to see if volumes decrease with increasing pressure.
simNum += 1; ASSERT_USUALLY_TRUE(results[0] > results[1]);
} ASSERT_USUALLY_TRUE(results[1] > results[2]);
ASSERT_USUALLY_TRUE(results[3] > results[4]);
/* ASSERT_USUALLY_TRUE(results[4] > results[5]);
for (int j = 0; j < 15; j++) { ASSERT_USUALLY_TRUE(results[6] > results[7]);
printf("%.6f\n",results[j]); ASSERT_USUALLY_TRUE(results[7] > results[8]);
}
*/
// Check to see if volumes decrease with increasing pressure.
ASSERT(results[0] > results[1]);
ASSERT(results[1] > results[2]);
ASSERT(results[3] > results[4]);
ASSERT(results[4] > results[5]);
ASSERT(results[6] > results[7]);
ASSERT(results[7] > results[8]);
// Check to see if incremental volume changes with increasing pressure go like kx > ky > kz. // Check to see if incremental volume changes with increasing pressure go like kx > ky > kz.
ASSERT((results[0] - results[1]) > (results[3] - results[4])); ASSERT_USUALLY_TRUE((results[0] - results[1]) > (results[3] - results[4]));
ASSERT((results[1] - results[2]) > (results[4] - results[5])); ASSERT_USUALLY_TRUE((results[1] - results[2]) > (results[4] - results[5]));
ASSERT((results[3] - results[4]) > (results[6] - results[7])); ASSERT_USUALLY_TRUE((results[3] - results[4]) > (results[6] - results[7]));
ASSERT((results[4] - results[5]) > (results[7] - results[8])); ASSERT_USUALLY_TRUE((results[4] - results[5]) > (results[7] - results[8]));
// Check to see if the volumes are equal for isotropic and anisotropic (all axis). // Check to see if the volumes are equal for isotropic and anisotropic (all axis).
ASSERT_USUALLY_EQUAL_TOL(results[9], results[12], 3/std::sqrt((double) steps)); ASSERT_USUALLY_EQUAL_TOL(results[9], results[12], 3/std::sqrt((double) steps));
ASSERT_USUALLY_EQUAL_TOL(results[10], results[13], 3/std::sqrt((double) steps)); ASSERT_USUALLY_EQUAL_TOL(results[10], results[13], 3/std::sqrt((double) steps));
ASSERT_USUALLY_EQUAL_TOL(results[11], results[14], 3/std::sqrt((double) steps)); ASSERT_USUALLY_EQUAL_TOL(results[11], results[14], 3/std::sqrt((double) steps));
} }
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {
......
...@@ -6,8 +6,8 @@ ...@@ -6,8 +6,8 @@
* 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, Lee-Ping Wang *
* Contributors: * * Contributors: *
* * * *
* Permission is hereby granted, free of charge, to any person obtaining a * * Permission is hereby granted, free of charge, to any person obtaining a *
...@@ -51,7 +51,7 @@ ...@@ -51,7 +51,7 @@
using namespace OpenMM; using namespace OpenMM;
using namespace std; using namespace std;
static OpenCLPlatform platform; OpenCLPlatform platform;
void testChangingBoxSize() { void testChangingBoxSize() {
System system; System system;
...@@ -191,24 +191,24 @@ void testIdealGasAxis(int axis) { ...@@ -191,24 +191,24 @@ void testIdealGasAxis(int axis) {
for (int j = 0; j < steps; ++j) { for (int j = 0; j < steps; ++j) {
Vec3 box[3]; Vec3 box[3];
context.getState(0).getPeriodicBoxVectors(box[0], box[1], box[2]); context.getState(0).getPeriodicBoxVectors(box[0], box[1], box[2]);
boxX = box[0][0]; boxX = box[0][0];
boxY = box[1][1]; boxY = box[1][1];
boxZ = box[2][2]; boxZ = box[2][2];
volume += box[0][0]*box[1][1]*box[2][2]; volume += box[0][0]*box[1][1]*box[2][2];
integrator.step(frequency); integrator.step(frequency);
} }
volume /= steps; volume /= steps;
double expected = (numParticles+1)*BOLTZ*temp[i]/pressureInMD; double expected = (numParticles+1)*BOLTZ*temp[i]/pressureInMD;
ASSERT_USUALLY_EQUAL_TOL(expected, volume, 3/std::sqrt((double) steps)); ASSERT_USUALLY_EQUAL_TOL(expected, volume, 3/std::sqrt((double) steps));
if (!scaleX) { if (!scaleX) {
ASSERT(boxX == initialLength); ASSERT(boxX == initialLength);
} }
if (!scaleY) { if (!scaleY) {
ASSERT(boxY == 0.5*initialLength); ASSERT(boxY == 0.5*initialLength);
} }
if (!scaleZ) { if (!scaleZ) {
ASSERT(boxZ == 2*initialLength); ASSERT(boxZ == 2*initialLength);
} }
} }
} }
...@@ -274,155 +274,148 @@ void testRandomSeed() { ...@@ -274,155 +274,148 @@ void testRandomSeed() {
} }
} }
/**
* Run a constant pressure simulation on an anisotropic Einstein crystal
* using isotropic and anisotropic barostats. There are a total of 15 simulations:
*
* 1) 3 pressures: 9.0, 10.0, 11.0 bar, for each of the following groups:
* 2) 3 groups of simulations that scale just one axis: x, y, z
* 3) 1 group of simulations that scales all three axes in the anisotropic barostat
* 4) 1 group of simulations that scales all three axes in the isotropic barostat
*
* Results that we will check:
*
* a) In each group of simulations, the volume should decrease with increasing pressure
* b) In the three simulation groups that scale just one axis, the compressibility (i.e. incremental volume change
* with increasing pressure) should go like kx > ky > kz (because the spring constant is largest in the z-direction)
* c) The anisotropic barostat should produce the same result as the isotropic barostat when all three axes are scaled
*/
void testEinsteinCrystal() { void testEinsteinCrystal() {
/* const int numParticles = 64;
const int frequency = 2;
Run a constant pressure simulation on an anisotropic Einstein crystal const int equil = 10000;
using isotropic and anisotropic barostats. There are a total of 15 simulations: const int steps = 5000;
const double pressure = 10.0;
1) 3 pressures: 9.0, 10.0, 11.0 bar, for each of the following groups: const double pressureInMD = pressure*(AVOGADRO*1e-25); // pressure in kJ/mol/nm^3
2) 3 groups of simulations that scale just one axis: x, y, z const double temp = 300.0; // Only test one temperature since we're looking at three pressures.
3) 1 group of simulations that scales all three axes in the anisotropic barostat const double pres3[] = {2.0, 8.0, 15.0};
4) 1 group of simulations that scales all three axes in the isotropic barostat const double initialVolume = numParticles*BOLTZ*temp/pressureInMD;
const double initialLength = std::pow(initialVolume, 1.0/3.0);
Results that we will check: vector<double> initialPositions(3);
vector<double> results;
a) In each group of simulations, the volume should decrease with increasing pressure // Run four groups of anisotropic simulations; scaling just x, y, z, then all three.
b) In the three simulation groups that scale just one axis, the compressibility (i.e. incremental volume change for (int a = 0; a < 4; a++) {
with increasing pressure) should go like kx > ky > kz (because the spring constant is largest in the z-direction) // Test barostat for three different pressures.
c) The anisotropic barostat should produce the same result as the isotropic barostat when all three axes are scaled for (int p = 0; p < 3; p++) {
// Create a system of noninteracting particles attached by harmonic springs to their initial positions.
*/ System system;
system.setDefaultPeriodicBoxVectors(Vec3(initialLength, 0, 0), Vec3(0, initialLength, 0), Vec3(0, 0, initialLength));
const int numParticles = 64; vector<Vec3> positions(numParticles);
const int frequency = 10; OpenMM_SFMT::SFMT sfmt;
const int equil = 10000; init_gen_rand(0, sfmt);
const int steps = 10000; // Anisotropic force constants.
const double pressure = 10.0; CustomExternalForce* force = new CustomExternalForce("0.005*(x-x0)^2 + 0.01*(y-y0)^2 + 0.02*(z-z0)^2");
const double pressureInMD = pressure*(AVOGADRO*1e-25); // pressure in kJ/mol/nm^3 force->addPerParticleParameter("x0");
const double temp = 300.0; // Only test one temperature since we're looking at three pressures. force->addPerParticleParameter("y0");
const double pres3[] = {9.0, 10.0, 11.0}; force->addPerParticleParameter("z0");
const double initialVolume = numParticles*BOLTZ*temp/pressureInMD; NonbondedForce* nb = new NonbondedForce();
const double initialLength = std::pow(initialVolume, 1.0/3.0); nb->setNonbondedMethod(NonbondedForce::CutoffPeriodic);
vector<double> initialPositions(3); for (int i = 0; i < numParticles; ++i) {
// All results. system.addParticle(1.0);
double results[] = {0.0, 0.0, 0.0, 0.0, 0.0, positions[i] = Vec3(((i/16)%4+0.5)*initialLength/4, ((i/4)%4+0.5)*initialLength/4, (i%4+0.5)*initialLength/4);
0.0, 0.0, 0.0, 0.0, 0.0, initialPositions[0] = positions[i][0];
0.0, 0.0, 0.0, 0.0, 0.0}; initialPositions[1] = positions[i][1];
int simNum = 0; initialPositions[2] = positions[i][2];
// Run four groups of anisotropic simulations; scaling just x, y, z, then all three. force->addParticle(i, initialPositions);
for (int a = 0; a < 4; a++) { nb->addParticle(0, initialLength/6, 0.1);
// Test barostat for three different pressures. }
for (int p = 0; p < 3; p++) { system.addForce(force);
// Create a system of noninteracting particles attached by harmonic springs to their initial positions. system.addForce(nb);
System system; // Create the barostat.
system.setDefaultPeriodicBoxVectors(Vec3(initialLength, 0, 0), Vec3(0, initialLength, 0), Vec3(0, 0, initialLength)); MonteCarloAnisotropicBarostat* barostat = new MonteCarloAnisotropicBarostat(Vec3(pres3[p], pres3[p], pres3[p]), temp, frequency, (a==0||a==3), (a==1||a==3), (a==2||a==3));
vector<Vec3> positions(numParticles); system.addForce(barostat);
OpenMM_SFMT::SFMT sfmt; barostat->setTemperature(temp);
init_gen_rand(0, sfmt); LangevinIntegrator integrator(temp, 0.1, 0.01);
// Anisotropic force constants. Context context(system, integrator, platform);
CustomExternalForce* force = new CustomExternalForce("0.005*(x-x0)^2 + 0.01*(y-y0)^2 + 0.02*(z-z0)^2"); context.setPositions(positions);
force->addPerParticleParameter("x0"); // Let it equilibrate.
force->addPerParticleParameter("y0"); integrator.step(equil);
force->addPerParticleParameter("z0"); // Now run it for a while and see if the volume is correct.
for (int i = 0; i < numParticles; ++i) { double volume = 0.0;
system.addParticle(1.0); for (int j = 0; j < steps; ++j) {
positions[i] = Vec3(((i/16)%4+0.5)*initialLength/4, ((i/4)%4+0.5)*initialLength/4, (i%4+0.5)*initialLength/4); Vec3 box[3];
initialPositions[0] = positions[i][0]; context.getState(0).getPeriodicBoxVectors(box[0], box[1], box[2]);
initialPositions[1] = positions[i][1]; volume += box[0][0]*box[1][1]*box[2][2];
initialPositions[2] = positions[i][2]; integrator.step(frequency);
force->addParticle(i, initialPositions); }
} volume /= steps;
system.addForce(force); results.push_back(volume);
// Create the barostat. }
MonteCarloAnisotropicBarostat* barostat = new MonteCarloAnisotropicBarostat(Vec3(pres3[p], pres3[p], pres3[p]), temp, frequency, (a==0||a==3), (a==1||a==3), (a==2||a==3));
system.addForce(barostat);
barostat->setTemperature(temp);
LangevinIntegrator integrator(temp, 0.1, 0.01);
Context context(system, integrator, platform);
context.setPositions(positions);
// Let it equilibrate.
integrator.step(equil);
// Now run it for a while and see if the volume is correct.
double volume = 0.0;
for (int j = 0; j < steps; ++j) {
Vec3 box[3];
context.getState(0).getPeriodicBoxVectors(box[0], box[1], box[2]);
volume += box[0][0]*box[1][1]*box[2][2];
integrator.step(frequency);
}
volume /= steps;
results[simNum] = volume;
simNum += 1;
}
}
for (int p = 0; p < 3; p++) {
// Create a system of noninteracting particles attached by harmonic springs to their initial positions.
System system;
system.setDefaultPeriodicBoxVectors(Vec3(initialLength, 0, 0), Vec3(0, initialLength, 0), Vec3(0, 0, initialLength));
vector<Vec3> positions(numParticles);
OpenMM_SFMT::SFMT sfmt;
init_gen_rand(0, sfmt);
// Anisotropic force constants.
CustomExternalForce* force = new CustomExternalForce("0.005*(x-x0)^2 + 0.01*(y-y0)^2 + 0.02*(z-z0)^2");
force->addPerParticleParameter("x0");
force->addPerParticleParameter("y0");
force->addPerParticleParameter("z0");
for (int i = 0; i < numParticles; ++i) {
system.addParticle(1.0);
positions[i] = Vec3(((i/16)%4+0.5)*initialLength/4, ((i/4)%4+0.5)*initialLength/4, (i%4+0.5)*initialLength/4);
initialPositions[0] = positions[i][0];
initialPositions[1] = positions[i][1];
initialPositions[2] = positions[i][2];
force->addParticle(i, initialPositions);
} }
system.addForce(force); for (int p = 0; p < 3; p++) {
// Create the barostat. // Create a system of noninteracting particles attached by harmonic springs to their initial positions.
MonteCarloBarostat* barostat = new MonteCarloBarostat(pres3[p], temp, frequency); System system;
system.addForce(barostat); system.setDefaultPeriodicBoxVectors(Vec3(initialLength, 0, 0), Vec3(0, initialLength, 0), Vec3(0, 0, initialLength));
barostat->setTemperature(temp); vector<Vec3> positions(numParticles);
LangevinIntegrator integrator(temp, 0.1, 0.01); OpenMM_SFMT::SFMT sfmt;
Context context(system, integrator, platform); init_gen_rand(0, sfmt);
context.setPositions(positions); // Anisotropic force constants.
// Let it equilibrate. CustomExternalForce* force = new CustomExternalForce("0.005*(x-x0)^2 + 0.01*(y-y0)^2 + 0.02*(z-z0)^2");
integrator.step(equil); force->addPerParticleParameter("x0");
// Now run it for a while and see if the volume is correct. force->addPerParticleParameter("y0");
double volume = 0.0; force->addPerParticleParameter("z0");
for (int j = 0; j < steps; ++j) { NonbondedForce* nb = new NonbondedForce();
Vec3 box[3]; nb->setNonbondedMethod(NonbondedForce::CutoffPeriodic);
context.getState(0).getPeriodicBoxVectors(box[0], box[1], box[2]); for (int i = 0; i < numParticles; ++i) {
volume += box[0][0]*box[1][1]*box[2][2]; system.addParticle(1.0);
integrator.step(frequency); positions[i] = Vec3(((i/16)%4+0.5)*initialLength/4, ((i/4)%4+0.5)*initialLength/4, (i%4+0.5)*initialLength/4);
initialPositions[0] = positions[i][0];
initialPositions[1] = positions[i][1];
initialPositions[2] = positions[i][2];
force->addParticle(i, initialPositions);
nb->addParticle(0, initialLength/6, 0.1);
}
system.addForce(force);
system.addForce(nb);
// Create the barostat.
MonteCarloBarostat* barostat = new MonteCarloBarostat(pres3[p], temp, frequency);
system.addForce(barostat);
barostat->setTemperature(temp);
LangevinIntegrator integrator(temp, 0.1, 0.001);
Context context(system, integrator, platform);
context.setPositions(positions);
// Let it equilibrate.
integrator.step(equil);
// Now run it for a while and see if the volume is correct.
double volume = 0.0;
for (int j = 0; j < steps; ++j) {
Vec3 box[3];
context.getState(0).getPeriodicBoxVectors(box[0], box[1], box[2]);
volume += box[0][0]*box[1][1]*box[2][2];
integrator.step(frequency);
}
volume /= steps;
results.push_back(volume);
} }
volume /= steps;
results[simNum] = volume; // Check to see if volumes decrease with increasing pressure.
simNum += 1; ASSERT_USUALLY_TRUE(results[0] > results[1]);
} ASSERT_USUALLY_TRUE(results[1] > results[2]);
ASSERT_USUALLY_TRUE(results[3] > results[4]);
/* ASSERT_USUALLY_TRUE(results[4] > results[5]);
for (int j = 0; j < 15; j++) { ASSERT_USUALLY_TRUE(results[6] > results[7]);
printf("%.6f\n",results[j]); ASSERT_USUALLY_TRUE(results[7] > results[8]);
}
*/
// Check to see if volumes decrease with increasing pressure.
ASSERT(results[0] > results[1]);
ASSERT(results[1] > results[2]);
ASSERT(results[3] > results[4]);
ASSERT(results[4] > results[5]);
ASSERT(results[6] > results[7]);
ASSERT(results[7] > results[8]);
// Check to see if incremental volume changes with increasing pressure go like kx > ky > kz. // Check to see if incremental volume changes with increasing pressure go like kx > ky > kz.
ASSERT((results[0] - results[1]) > (results[3] - results[4])); ASSERT_USUALLY_TRUE((results[0] - results[1]) > (results[3] - results[4]));
ASSERT((results[1] - results[2]) > (results[4] - results[5])); ASSERT_USUALLY_TRUE((results[1] - results[2]) > (results[4] - results[5]));
ASSERT((results[3] - results[4]) > (results[6] - results[7])); ASSERT_USUALLY_TRUE((results[3] - results[4]) > (results[6] - results[7]));
ASSERT((results[4] - results[5]) > (results[7] - results[8])); ASSERT_USUALLY_TRUE((results[4] - results[5]) > (results[7] - results[8]));
// Check to see if the volumes are equal for isotropic and anisotropic (all axis). // Check to see if the volumes are equal for isotropic and anisotropic (all axis).
ASSERT_USUALLY_EQUAL_TOL(results[9], results[12], 3/std::sqrt((double) steps)); ASSERT_USUALLY_EQUAL_TOL(results[9], results[12], 3/std::sqrt((double) steps));
ASSERT_USUALLY_EQUAL_TOL(results[10], results[13], 3/std::sqrt((double) steps)); ASSERT_USUALLY_EQUAL_TOL(results[10], results[13], 3/std::sqrt((double) steps));
ASSERT_USUALLY_EQUAL_TOL(results[11], results[14], 3/std::sqrt((double) steps)); ASSERT_USUALLY_EQUAL_TOL(results[11], results[14], 3/std::sqrt((double) steps));
} }
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {
......
...@@ -6,8 +6,8 @@ ...@@ -6,8 +6,8 @@
* 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-2010 Stanford University and the Authors. * * Portions copyright (c) 2008-2013 Stanford University and the Authors. *
* Authors: Peter Eastman * * Authors: Peter Eastman, Lee-Ping Wang *
* Contributors: * * Contributors: *
* * * *
* Permission is hereby granted, free of charge, to any person obtaining a * * Permission is hereby granted, free of charge, to any person obtaining a *
...@@ -34,6 +34,8 @@ ...@@ -34,6 +34,8 @@
*/ */
#include "openmm/internal/AssertionUtilities.h" #include "openmm/internal/AssertionUtilities.h"
#include "openmm/CustomExternalForce.h"
#include "openmm/MonteCarloBarostat.h"
#include "openmm/MonteCarloAnisotropicBarostat.h" #include "openmm/MonteCarloAnisotropicBarostat.h"
#include "openmm/Context.h" #include "openmm/Context.h"
#include "ReferencePlatform.h" #include "ReferencePlatform.h"
...@@ -190,24 +192,24 @@ void testIdealGasAxis(int axis) { ...@@ -190,24 +192,24 @@ void testIdealGasAxis(int axis) {
for (int j = 0; j < steps; ++j) { for (int j = 0; j < steps; ++j) {
Vec3 box[3]; Vec3 box[3];
context.getState(0).getPeriodicBoxVectors(box[0], box[1], box[2]); context.getState(0).getPeriodicBoxVectors(box[0], box[1], box[2]);
boxX = box[0][0]; boxX = box[0][0];
boxY = box[1][1]; boxY = box[1][1];
boxZ = box[2][2]; boxZ = box[2][2];
volume += box[0][0]*box[1][1]*box[2][2]; volume += box[0][0]*box[1][1]*box[2][2];
integrator.step(frequency); integrator.step(frequency);
} }
volume /= steps; volume /= steps;
double expected = (numParticles+1)*BOLTZ*temp[i]/pressureInMD; double expected = (numParticles+1)*BOLTZ*temp[i]/pressureInMD;
ASSERT_USUALLY_EQUAL_TOL(expected, volume, 3/std::sqrt((double) steps)); ASSERT_USUALLY_EQUAL_TOL(expected, volume, 3/std::sqrt((double) steps));
if (!scaleX) { if (!scaleX) {
ASSERT(boxX == initialLength); ASSERT(boxX == initialLength);
} }
if (!scaleY) { if (!scaleY) {
ASSERT(boxY == 0.5*initialLength); ASSERT(boxY == 0.5*initialLength);
} }
if (!scaleZ) { if (!scaleZ) {
ASSERT(boxZ == 2*initialLength); ASSERT(boxZ == 2*initialLength);
} }
} }
} }
...@@ -274,13 +276,159 @@ void testRandomSeed() { ...@@ -274,13 +276,159 @@ void testRandomSeed() {
} }
} }
/**
* Run a constant pressure simulation on an anisotropic Einstein crystal
* using isotropic and anisotropic barostats. There are a total of 15 simulations:
*
* 1) 3 pressures: 9.0, 10.0, 11.0 bar, for each of the following groups:
* 2) 3 groups of simulations that scale just one axis: x, y, z
* 3) 1 group of simulations that scales all three axes in the anisotropic barostat
* 4) 1 group of simulations that scales all three axes in the isotropic barostat
*
* Results that we will check:
*
* a) In each group of simulations, the volume should decrease with increasing pressure
* b) In the three simulation groups that scale just one axis, the compressibility (i.e. incremental volume change
* with increasing pressure) should go like kx > ky > kz (because the spring constant is largest in the z-direction)
* c) The anisotropic barostat should produce the same result as the isotropic barostat when all three axes are scaled
*/
void testEinsteinCrystal() {
const int numParticles = 64;
const int frequency = 2;
const int equil = 10000;
const int steps = 5000;
const double pressure = 10.0;
const double pressureInMD = pressure*(AVOGADRO*1e-25); // pressure in kJ/mol/nm^3
const double temp = 300.0; // Only test one temperature since we're looking at three pressures.
const double pres3[] = {2.0, 8.0, 15.0};
const double initialVolume = numParticles*BOLTZ*temp/pressureInMD;
const double initialLength = std::pow(initialVolume, 1.0/3.0);
ReferencePlatform platform;
vector<double> initialPositions(3);
vector<double> results;
// Run four groups of anisotropic simulations; scaling just x, y, z, then all three.
for (int a = 0; a < 4; a++) {
// Test barostat for three different pressures.
for (int p = 0; p < 3; p++) {
// Create a system of noninteracting particles attached by harmonic springs to their initial positions.
System system;
system.setDefaultPeriodicBoxVectors(Vec3(initialLength, 0, 0), Vec3(0, initialLength, 0), Vec3(0, 0, initialLength));
vector<Vec3> positions(numParticles);
OpenMM_SFMT::SFMT sfmt;
init_gen_rand(0, sfmt);
// Anisotropic force constants.
CustomExternalForce* force = new CustomExternalForce("0.005*(x-x0)^2 + 0.01*(y-y0)^2 + 0.02*(z-z0)^2");
force->addPerParticleParameter("x0");
force->addPerParticleParameter("y0");
force->addPerParticleParameter("z0");
NonbondedForce* nb = new NonbondedForce();
nb->setNonbondedMethod(NonbondedForce::CutoffPeriodic);
for (int i = 0; i < numParticles; ++i) {
system.addParticle(1.0);
positions[i] = Vec3(((i/16)%4+0.5)*initialLength/4, ((i/4)%4+0.5)*initialLength/4, (i%4+0.5)*initialLength/4);
initialPositions[0] = positions[i][0];
initialPositions[1] = positions[i][1];
initialPositions[2] = positions[i][2];
force->addParticle(i, initialPositions);
nb->addParticle(0, initialLength/6, 0.1);
}
system.addForce(force);
system.addForce(nb);
// Create the barostat.
MonteCarloAnisotropicBarostat* barostat = new MonteCarloAnisotropicBarostat(Vec3(pres3[p], pres3[p], pres3[p]), temp, frequency, (a==0||a==3), (a==1||a==3), (a==2||a==3));
system.addForce(barostat);
barostat->setTemperature(temp);
LangevinIntegrator integrator(temp, 0.1, 0.01);
Context context(system, integrator, platform);
context.setPositions(positions);
// Let it equilibrate.
integrator.step(equil);
// Now run it for a while and see if the volume is correct.
double volume = 0.0;
for (int j = 0; j < steps; ++j) {
Vec3 box[3];
context.getState(0).getPeriodicBoxVectors(box[0], box[1], box[2]);
volume += box[0][0]*box[1][1]*box[2][2];
integrator.step(frequency);
}
volume /= steps;
results.push_back(volume);
}
}
for (int p = 0; p < 3; p++) {
// Create a system of noninteracting particles attached by harmonic springs to their initial positions.
System system;
system.setDefaultPeriodicBoxVectors(Vec3(initialLength, 0, 0), Vec3(0, initialLength, 0), Vec3(0, 0, initialLength));
vector<Vec3> positions(numParticles);
OpenMM_SFMT::SFMT sfmt;
init_gen_rand(0, sfmt);
// Anisotropic force constants.
CustomExternalForce* force = new CustomExternalForce("0.005*(x-x0)^2 + 0.01*(y-y0)^2 + 0.02*(z-z0)^2");
force->addPerParticleParameter("x0");
force->addPerParticleParameter("y0");
force->addPerParticleParameter("z0");
NonbondedForce* nb = new NonbondedForce();
nb->setNonbondedMethod(NonbondedForce::CutoffPeriodic);
for (int i = 0; i < numParticles; ++i) {
system.addParticle(1.0);
positions[i] = Vec3(((i/16)%4+0.5)*initialLength/4, ((i/4)%4+0.5)*initialLength/4, (i%4+0.5)*initialLength/4);
initialPositions[0] = positions[i][0];
initialPositions[1] = positions[i][1];
initialPositions[2] = positions[i][2];
force->addParticle(i, initialPositions);
nb->addParticle(0, initialLength/6, 0.1);
}
system.addForce(force);
system.addForce(nb);
// Create the barostat.
MonteCarloBarostat* barostat = new MonteCarloBarostat(pres3[p], temp, frequency);
system.addForce(barostat);
barostat->setTemperature(temp);
LangevinIntegrator integrator(temp, 0.1, 0.001);
Context context(system, integrator, platform);
context.setPositions(positions);
// Let it equilibrate.
integrator.step(equil);
// Now run it for a while and see if the volume is correct.
double volume = 0.0;
for (int j = 0; j < steps; ++j) {
Vec3 box[3];
context.getState(0).getPeriodicBoxVectors(box[0], box[1], box[2]);
volume += box[0][0]*box[1][1]*box[2][2];
integrator.step(frequency);
}
volume /= steps;
results.push_back(volume);
}
// Check to see if volumes decrease with increasing pressure.
ASSERT_USUALLY_TRUE(results[0] > results[1]);
ASSERT_USUALLY_TRUE(results[1] > results[2]);
ASSERT_USUALLY_TRUE(results[3] > results[4]);
ASSERT_USUALLY_TRUE(results[4] > results[5]);
ASSERT_USUALLY_TRUE(results[6] > results[7]);
ASSERT_USUALLY_TRUE(results[7] > results[8]);
// Check to see if incremental volume changes with increasing pressure go like kx > ky > kz.
ASSERT_USUALLY_TRUE((results[0] - results[1]) > (results[3] - results[4]));
ASSERT_USUALLY_TRUE((results[1] - results[2]) > (results[4] - results[5]));
ASSERT_USUALLY_TRUE((results[3] - results[4]) > (results[6] - results[7]));
ASSERT_USUALLY_TRUE((results[4] - results[5]) > (results[7] - results[8]));
// Check to see if the volumes are equal for isotropic and anisotropic (all axis).
ASSERT_USUALLY_EQUAL_TOL(results[9], results[12], 3/std::sqrt((double) steps));
ASSERT_USUALLY_EQUAL_TOL(results[10], results[13], 3/std::sqrt((double) steps));
ASSERT_USUALLY_EQUAL_TOL(results[11], results[14], 3/std::sqrt((double) steps));
}
int main() { int main() {
try { try {
testIdealGas(); testIdealGas();
testIdealGasAxis(0); testIdealGasAxis(0);
testIdealGasAxis(1); testIdealGasAxis(1);
testIdealGasAxis(2); testIdealGasAxis(2);
testRandomSeed(); testRandomSeed();
testEinsteinCrystal();
} }
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