Unverified Commit c5c0348a authored by peastman's avatar peastman Committed by GitHub
Browse files

Merge pull request #2518 from andysim/inittemp

Modify temperature initialization mechanism
parents ef02e48d 58d2619e
......@@ -29,185 +29,7 @@
* USE OR OTHER DEALINGS IN THE SOFTWARE. *
* -------------------------------------------------------------------------- */
/**
* This tests the OpenCL implementation of DrudeForce.
*/
#include "OpenCLDrudeTests.h"
#include "TestDrudeForce.h"
#include "openmm/internal/AssertionUtilities.h"
#include "openmm/Context.h"
#include "openmm/NonbondedForce.h"
#include "openmm/Platform.h"
#include "openmm/System.h"
#include "openmm/VerletIntegrator.h"
#include "openmm/DrudeForce.h"
#include "SimTKOpenMMUtilities.h"
#include <iostream>
#include <vector>
using namespace OpenMM;
using namespace std;
extern "C" void registerDrudeOpenCLKernelFactories();
void validateForce(System& system, vector<Vec3>& positions, double expectedEnergy) {
// Given a System containing a Drude force, check that its energy has the expected value.
VerletIntegrator integ(1.0);
Platform& platform = Platform::getPlatformByName("OpenCL");
Context context(system, integ, platform);
context.setPositions(positions);
State state = context.getState(State::Energy | State::Forces);
ASSERT_EQUAL_TOL(expectedEnergy, state.getPotentialEnergy(), 1e-5);
// Try moving each particle along each axis, and see if the energy changes by the correct amount.
double offset = 1e-2;
for (int i = 0; i < system.getNumParticles(); i++)
for (int j = 0; j < 3; j++) {
vector<Vec3> offsetPos = positions;
offsetPos[i][j] = positions[i][j]-offset;
context.setPositions(offsetPos);
double e1 = context.getState(State::Energy | State::Forces).getPotentialEnergy();
offsetPos[i][j] = positions[i][j]+offset;
context.setPositions(offsetPos);
double e2 = context.getState(State::Energy | State::Forces).getPotentialEnergy();
ASSERT_EQUAL_TOL(state.getForces()[i][j], (e1-e2)/(2*offset), 1e-3);
}
}
void testSingleParticle() {
const double k = ONE_4PI_EPS0*1.5;
const double charge = 0.1;
const double alpha = ONE_4PI_EPS0*charge*charge/k;
System system;
system.addParticle(1.0);
system.addParticle(1.0);
DrudeForce* drude = new DrudeForce();
drude->addParticle(1, 0, -1, -1, -1, charge, alpha, 1, 1);
system.addForce(drude);
vector<Vec3> positions(2);
positions[0] = Vec3(-1, 0, 0);
positions[1] = Vec3(2, 0, 0);
validateForce(system, positions, 0.5*k*3*3);
}
void testAnisotropicParticle() {
const double k = ONE_4PI_EPS0*1.5;
const double charge = 0.1;
const double alpha = ONE_4PI_EPS0*charge*charge/k;
const double a1 = 0.8;
const double a2 = 1.1;
const double k1 = k/a1;
const double k2 = k/a2;
const double k3 = k/(3-a1-a2);
System system;
system.addParticle(1.0);
system.addParticle(1.0);
system.addParticle(1.0);
system.addParticle(1.0);
system.addParticle(1.0);
DrudeForce* drude = new DrudeForce();
drude->addParticle(1, 0, 2, 3, 4, charge, alpha, a1, a2);
system.addForce(drude);
vector<Vec3> positions(5);
positions[0] = Vec3(0, 0, 0);
positions[1] = Vec3(0.1, -0.5, 0.8);
positions[2] = Vec3(0, 2, 0);
positions[3] = Vec3(1, 2, 0);
positions[4] = Vec3(1, 2, 3);
validateForce(system, positions, 0.5*k1*0.5*0.5 + 0.5*k2*0.8*0.8 + 0.5*k3*0.1*0.1);
}
double computeScreening(double r, double thole, double alpha1, double alpha2) {
double u = r*thole/pow(alpha1*alpha2, 1.0/6.0);
return 1.0-(1.0+u/2)*exp(-u);
}
void testThole() {
const double k = ONE_4PI_EPS0*1.5;
const double charge = 0.1;
const double alpha = ONE_4PI_EPS0*charge*charge/k;
const double thole = 2.5;
System system;
system.addParticle(1.0);
system.addParticle(1.0);
system.addParticle(1.0);
system.addParticle(1.0);
DrudeForce* drude = new DrudeForce();
drude->addParticle(1, 0, -1, -1, -1, charge, alpha, 1, 1);
drude->addParticle(3, 2, -1, -1, -1, charge, alpha, 1, 1);
drude->addScreenedPair(0, 1, thole);
system.addForce(drude);
vector<Vec3> positions(4);
positions[0] = Vec3(0, 0, 0);
positions[1] = Vec3(0, -0.5, 0);
positions[2] = Vec3(1, 0, 0);
positions[3] = Vec3(1, 0, 0.3);
double energySpring1 = 0.5*k*0.5*0.5;
double energySpring2 = 0.5*k*0.3*0.3;
double energyDipole = 0.0;
double q[] = {-charge, charge, -charge, charge};
for (int i = 0; i < 2; i++)
for (int j = 2; j < 4; j++) {
Vec3 delta = positions[i]-positions[j];
double r = sqrt(delta.dot(delta));
energyDipole += ONE_4PI_EPS0*q[i]*q[j]*computeScreening(r, thole, alpha, alpha)/r;
}
validateForce(system, positions, energySpring1+energySpring2+energyDipole);
}
void testChangingParameters() {
const double k = ONE_4PI_EPS0*1.5;
const double charge = 0.1;
const double alpha = ONE_4PI_EPS0*charge*charge/k;
Platform& platform = Platform::getPlatformByName("OpenCL");
// Create the system.
System system;
system.addParticle(1.0);
system.addParticle(1.0);
DrudeForce* drude = new DrudeForce();
drude->addParticle(1, 0, -1, -1, -1, charge, alpha, 1, 1);
system.addForce(drude);
vector<Vec3> positions(2);
positions[0] = Vec3(-1, 0, 0);
positions[1] = Vec3(2, 0, 0);
// Check the energy.
VerletIntegrator integ(1.0);
Context context(system, integ, platform);
context.setPositions(positions);
State state = context.getState(State::Energy);
ASSERT_EQUAL_TOL(0.5*k*3*3, state.getPotentialEnergy(), 1e-5);
// Modify the parameters.
const double k2 = ONE_4PI_EPS0*2.2;
const double charge2 = 0.3;
const double alpha2 = ONE_4PI_EPS0*charge2*charge2/k2;
drude->setParticleParameters(0, 1, 0, -1, -1, -1, charge2, alpha2, 1, 1);
drude->updateParametersInContext(context);
state = context.getState(State::Energy);
ASSERT_EQUAL_TOL(0.5*k2*3*3, state.getPotentialEnergy(), 1e-5);
}
int main(int argc, char* argv[]) {
try {
registerDrudeOpenCLKernelFactories();
if (argc > 1)
Platform::getPlatformByName("OpenCL").setPropertyDefaultValue("Precision", std::string(argv[1]));
testSingleParticle();
testAnisotropicParticle();
testThole();
testChangingParameters();
}
catch(const std::exception& e) {
std::cout << "exception: " << e.what() << std::endl;
std::cout << "FAIL - ERROR. Test failed." << std::endl;
return 1;
}
std::cout << "Done" << std::endl;
return 0;
}
void runPlatformTests() {}
......@@ -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) 2013-2016 Stanford University and the Authors. *
* Portions copyright (c) 2013 Stanford University and the Authors. *
* Authors: Peter Eastman *
* Contributors: *
* *
......@@ -29,230 +29,7 @@
* USE OR OTHER DEALINGS IN THE SOFTWARE. *
* -------------------------------------------------------------------------- */
/**
* This tests the OpenCL implementation of DrudeLangevinIntegrator.
*/
#include "OpenCLDrudeTests.h"
#include "TestDrudeLangevinIntegrator.h"
#include "openmm/internal/AssertionUtilities.h"
#include "openmm/Context.h"
#include "openmm/NonbondedForce.h"
#include "openmm/Platform.h"
#include "openmm/System.h"
#include "openmm/VirtualSite.h"
#include "openmm/DrudeForce.h"
#include "openmm/DrudeLangevinIntegrator.h"
#include "SimTKOpenMMUtilities.h"
#include <iostream>
#include <vector>
using namespace OpenMM;
using namespace std;
extern "C" OPENMM_EXPORT void registerDrudeOpenCLKernelFactories();
void testSinglePair() {
const double temperature = 300.0;
const double temperatureDrude = 10.0;
const double k = ONE_4PI_EPS0*1.5;
const double charge = 0.1;
const double alpha = ONE_4PI_EPS0*charge*charge/k;
const double mass1 = 1.0;
const double mass2 = 0.1;
const double totalMass = mass1+mass2;
const double reducedMass = (mass1*mass2)/(mass1+mass2);
const double maxDistance = 0.05;
System system;
system.addParticle(mass1);
system.addParticle(mass2);
DrudeForce* drude = new DrudeForce();
drude->addParticle(1, 0, -1, -1, -1, charge, alpha, 1, 1);
system.addForce(drude);
vector<Vec3> positions(2);
positions[0] = Vec3(0, 0, 0);
positions[1] = Vec3(0, 0, 0);
DrudeLangevinIntegrator integ(temperature, 20.0, temperatureDrude, 20.0, 0.003);
integ.setMaxDrudeDistance(maxDistance);
Platform& platform = Platform::getPlatformByName("OpenCL");
Context context(system, integ, platform);
context.setPositions(positions);
// Equilibrate.
integ.step(1000);
// Compute the internal and center of mass temperatures.
double keCM = 0, keInternal = 0;
int numSteps = 10000;
for (int i = 0; i < numSteps; i++) {
integ.step(10);
State state = context.getState(State::Velocities | State::Positions);
const vector<Vec3>& vel = state.getVelocities();
Vec3 velCM = vel[0]*(mass1/totalMass) + vel[1]*(mass2/totalMass);
keCM += 0.5*totalMass*velCM.dot(velCM);
Vec3 velInternal = vel[0]-vel[1];
keInternal += 0.5*reducedMass*velInternal.dot(velInternal);
Vec3 delta = state.getPositions()[0]-state.getPositions()[1];
double distance = sqrt(delta.dot(delta));
ASSERT(distance <= maxDistance*(1+1e-6));
}
ASSERT_USUALLY_EQUAL_TOL(3*0.5*BOLTZ*temperature, keCM/numSteps, 0.1);
ASSERT_USUALLY_EQUAL_TOL(3*0.5*BOLTZ*temperatureDrude, keInternal/numSteps, 0.01);
}
void testWater() {
// Create a box of SWM4-NDP water molecules. This involves constraints, virtual sites,
// and Drude particles.
const int gridSize = 4;
const int numMolecules = gridSize*gridSize*gridSize;
const double spacing = 0.6;
const double boxSize = spacing*(gridSize+1);
const double temperature = 300.0;
const double temperatureDrude = 10.0;
System system;
NonbondedForce* nonbonded = new NonbondedForce();
DrudeForce* drude = new DrudeForce();
system.addForce(nonbonded);
system.addForce(drude);
system.setDefaultPeriodicBoxVectors(Vec3(boxSize, 0, 0), Vec3(0, boxSize, 0), Vec3(0, 0, boxSize));
nonbonded->setNonbondedMethod(NonbondedForce::CutoffPeriodic);
nonbonded->setCutoffDistance(1.0);
for (int i = 0; i < numMolecules; i++) {
int startIndex = system.getNumParticles();
system.addParticle(15.6); // O
system.addParticle(0.4); // D
system.addParticle(1.0); // H1
system.addParticle(1.0); // H2
system.addParticle(0.0); // M
nonbonded->addParticle(1.71636, 0.318395, 0.21094*4.184);
nonbonded->addParticle(-1.71636, 1, 0);
nonbonded->addParticle(0.55733, 1, 0);
nonbonded->addParticle(0.55733, 1, 0);
nonbonded->addParticle(-1.11466, 1, 0);
for (int j = 0; j < 5; j++)
for (int k = 0; k < j; k++)
nonbonded->addException(startIndex+j, startIndex+k, 0, 1, 0);
system.addConstraint(startIndex, startIndex+2, 0.09572);
system.addConstraint(startIndex, startIndex+3, 0.09572);
system.addConstraint(startIndex+2, startIndex+3, 0.15139);
system.setVirtualSite(startIndex+4, new ThreeParticleAverageSite(startIndex, startIndex+2, startIndex+3, 0.786646558, 0.106676721, 0.106676721));
drude->addParticle(startIndex+1, startIndex, -1, -1, -1, -1.71636, ONE_4PI_EPS0*1.71636*1.71636/(100000*4.184), 1, 1);
}
vector<Vec3> positions;
for (int i = 0; i < gridSize; i++)
for (int j = 0; j < gridSize; j++)
for (int k = 0; k < gridSize; k++) {
Vec3 pos(i*spacing, j*spacing, k*spacing);
positions.push_back(pos);
positions.push_back(pos);
positions.push_back(pos+Vec3(0.09572, 0, 0));
positions.push_back(pos+Vec3(-0.023999, 0.092663, 0));
positions.push_back(pos);
}
// Simulate it and check the temperature.
DrudeLangevinIntegrator integ(temperature, 50.0, temperatureDrude, 50.0, 0.0005);
Platform& platform = Platform::getPlatformByName("OpenCL");
Context context(system, integ, platform);
context.setPositions(positions);
context.applyConstraints(1e-5);
// Equilibrate.
integ.step(1000);
// Compute the internal and center of mass temperatures.
double ke = 0;
int numSteps = 10000;
for (int i = 0; i < numSteps; i++) {
integ.step(1);
ke += context.getState(State::Energy).getKineticEnergy();
}
ke /= numSteps;
int numStandardDof = 3*3*numMolecules-system.getNumConstraints();
int numDrudeDof = 3*numMolecules;
int numDof = numStandardDof+numDrudeDof;
double expectedTemp = (numStandardDof*temperature+numDrudeDof*temperatureDrude)/numDof;
ASSERT_USUALLY_EQUAL_TOL(expectedTemp, ke/(0.5*numDof*BOLTZ), 0.02);
}
void testForceEnergyConsistency() {
// Create a box of polarizable particles.
const int gridSize = 3;
const int numAtoms = gridSize*gridSize*gridSize;
const double spacing = 0.6;
const double boxSize = spacing*(gridSize+1);
const double temperature = 300.0;
const double temperatureDrude = 10.0;
System system;
vector<Vec3> positions;
NonbondedForce* nonbonded = new NonbondedForce();
DrudeForce* drude = new DrudeForce();
system.addForce(nonbonded);
system.addForce(drude);
system.setDefaultPeriodicBoxVectors(Vec3(boxSize, 0, 0), Vec3(0, boxSize, 0), Vec3(0, 0, boxSize));
nonbonded->setNonbondedMethod(NonbondedForce::PME);
nonbonded->setCutoffDistance(1.0);
nonbonded->setUseSwitchingFunction(true);
nonbonded->setSwitchingDistance(0.9);
nonbonded->setEwaldErrorTolerance(5e-5);
for (int i = 0; i < numAtoms; i++) {
int startIndex = system.getNumParticles();
system.addParticle(1.0);
system.addParticle(1.0);
nonbonded->addParticle(1.0, 0.3, 1.0);
nonbonded->addParticle(-1.0, 0.3, 1.0);
nonbonded->addException(startIndex, startIndex+1, 0, 1, 0);
drude->addParticle(startIndex+1, startIndex, -1, -1, -1, -1.0, 0.001, 1, 1);
}
for (int i = 0; i < gridSize; i++)
for (int j = 0; j < gridSize; j++)
for (int k = 0; k < gridSize; k++) {
Vec3 pos(i*spacing, j*spacing, k*spacing);
positions.push_back(pos);
positions.push_back(pos);
}
// Simulate it and check that force and energy remain consistent.
DrudeLangevinIntegrator integ(temperature, 50.0, temperatureDrude, 50.0, 0.001);
Platform& platform = Platform::getPlatformByName("OpenCL");
Context context(system, integ, platform);
context.setPositions(positions);
State prevState;
for (int i = 0; i < 100; i++) {
State state = context.getState(State::Energy | State::Forces | State::Positions);
if (i > 0) {
double expectedEnergyChange = 0;
for (int j = 0; j < system.getNumParticles(); j++) {
Vec3 delta = state.getPositions()[j]-prevState.getPositions()[j];
expectedEnergyChange -= 0.5*(state.getForces()[j]+prevState.getForces()[j]).dot(delta);
}
ASSERT_EQUAL_TOL(expectedEnergyChange, state.getPotentialEnergy()-prevState.getPotentialEnergy(), 0.05);
}
prevState = state;
integ.step(1);
}
}
int main(int argc, char* argv[]) {
try {
registerDrudeOpenCLKernelFactories();
if (argc > 1)
Platform::getPlatformByName("OpenCL").setPropertyDefaultValue("Precision", string(argv[1]));
testSinglePair();
testWater();
testForceEnergyConsistency();
}
catch(const std::exception& e) {
std::cout << "exception: " << e.what() << std::endl;
std::cout << "FAIL - ERROR. Test failed." << std::endl;
return 1;
}
std::cout << "Done" << std::endl;
return 0;
}
void runPlatformTests() {}
......@@ -29,32 +29,7 @@
* USE OR OTHER DEALINGS IN THE SOFTWARE. *
* -------------------------------------------------------------------------- */
//#include "ReferenceTests.h"
#include "openmm/internal/AssertionUtilities.h"
#include "openmm/Context.h"
#include "openmm/NonbondedForce.h"
#include "openmm/Platform.h"
#include "openmm/System.h"
#include "openmm/VerletIntegrator.h"
#include "openmm/DrudeForce.h"
#include "OpenCLPlatform.h"
#include "SimTKOpenMMUtilities.h"
#include <iostream>
#include <vector>
using namespace OpenMM;
using namespace std;
extern "C" OPENMM_EXPORT void registerDrudeOpenCLKernelFactories();
void runPlatformTests() { }
#include "OpenCLDrudeTests.h"
#include "TestDrudeNoseHoover.h"
Platform& initializePlatform(int argc, char* argv[]) {
registerDrudeOpenCLKernelFactories();
if (argc > 1) Platform::getPlatformByName("OpenCL").setPropertyDefaultValue("Precision", std::string(argv[1]));
return Platform::getPlatformByName("OpenCL");
}
void runPlatformTests() {}
......@@ -29,116 +29,7 @@
* USE OR OTHER DEALINGS IN THE SOFTWARE. *
* -------------------------------------------------------------------------- */
/**
* This tests the OpenCL implementation of DrudeSCFIntegrator.
*/
#include "OpenCLDrudeTests.h"
#include "TestDrudeSCFIntegrator.h"
#include "openmm/internal/AssertionUtilities.h"
#include "openmm/Context.h"
#include "openmm/NonbondedForce.h"
#include "openmm/Platform.h"
#include "openmm/System.h"
#include "openmm/VirtualSite.h"
#include "openmm/DrudeForce.h"
#include "openmm/DrudeSCFIntegrator.h"
#include "SimTKOpenMMUtilities.h"
#include <iostream>
#include <vector>
using namespace OpenMM;
using namespace std;
extern "C" OPENMM_EXPORT void registerDrudeOpenCLKernelFactories();
void testWater() {
// Create a box of SWM4-NDP water molecules. This involves constraints, virtual sites,
// and Drude particles.
const int gridSize = 4;
const int numMolecules = gridSize*gridSize*gridSize;
const double spacing = 0.6;
const double boxSize = spacing*(gridSize+1);
System system;
NonbondedForce* nonbonded = new NonbondedForce();
DrudeForce* drude = new DrudeForce();
system.addForce(nonbonded);
system.addForce(drude);
system.setDefaultPeriodicBoxVectors(Vec3(boxSize, 0, 0), Vec3(0, boxSize, 0), Vec3(0, 0, boxSize));
nonbonded->setNonbondedMethod(NonbondedForce::CutoffPeriodic);
nonbonded->setCutoffDistance(1.0);
for (int i = 0; i < numMolecules; i++) {
int startIndex = system.getNumParticles();
system.addParticle(15.6); // O
system.addParticle(0.4); // D
system.addParticle(1.0); // H1
system.addParticle(1.0); // H2
system.addParticle(0.0); // M
nonbonded->addParticle(1.71636, 0.318395, 0.21094*4.184);
nonbonded->addParticle(-1.71636, 1, 0);
nonbonded->addParticle(0.55733, 1, 0);
nonbonded->addParticle(0.55733, 1, 0);
nonbonded->addParticle(-1.11466, 1, 0);
for (int j = 0; j < 5; j++)
for (int k = 0; k < j; k++)
nonbonded->addException(startIndex+j, startIndex+k, 0, 1, 0);
system.addConstraint(startIndex, startIndex+2, 0.09572);
system.addConstraint(startIndex, startIndex+3, 0.09572);
system.addConstraint(startIndex+2, startIndex+3, 0.15139);
system.setVirtualSite(startIndex+4, new ThreeParticleAverageSite(startIndex, startIndex+2, startIndex+3, 0.786646558, 0.106676721, 0.106676721));
drude->addParticle(startIndex+1, startIndex, -1, -1, -1, -1.71636, ONE_4PI_EPS0*1.71636*1.71636/(100000*4.184), 1, 1);
}
vector<Vec3> positions;
for (int i = 0; i < gridSize; i++)
for (int j = 0; j < gridSize; j++)
for (int k = 0; k < gridSize; k++) {
Vec3 pos(i*spacing, j*spacing, k*spacing);
positions.push_back(pos);
positions.push_back(pos);
positions.push_back(pos+Vec3(0.09572, 0, 0));
positions.push_back(pos+Vec3(-0.023999, 0.092663, 0));
positions.push_back(pos);
}
// Simulate it and check energy conservation and the total force on the Drude particles.
DrudeSCFIntegrator integ(0.0005);
Platform& platform = Platform::getPlatformByName("OpenCL");
Context context(system, integ, platform);
context.setPositions(positions);
context.applyConstraints(1e-5);
context.setVelocitiesToTemperature(300.0);
State state = context.getState(State::Energy);
double initialEnergy;
int numSteps = 1000;
double maxNorm = (platform.getPropertyValue(context, "Precision") == "double" ? 1.0 : 5.0);
for (int i = 0; i < numSteps; i++) {
integ.step(1);
state = context.getState(State::Energy | State::Forces);
if (i == 0)
initialEnergy = state.getPotentialEnergy()+state.getKineticEnergy();
else
ASSERT_EQUAL_TOL(initialEnergy, state.getPotentialEnergy()+state.getKineticEnergy(), 0.01);
const vector<Vec3>& force = state.getForces();
double norm = 0.0;
for (int j = 1; j < (int) force.size(); j += 5)
norm += sqrt(force[j].dot(force[j]));
norm = (norm/numMolecules);
ASSERT(norm < maxNorm);
}
}
int main(int argc, char* argv[]) {
try {
registerDrudeOpenCLKernelFactories();
if (argc > 1)
Platform::getPlatformByName("OpenCL").setPropertyDefaultValue("Precision", string(argv[1]));
testWater();
}
catch(const std::exception& e) {
std::cout << "exception: " << e.what() << std::endl;
std::cout << "FAIL - ERROR. Test failed." << std::endl;
return 1;
}
std::cout << "Done" << std::endl;
return 0;
}
void runPlatformTests() {}
......@@ -5,6 +5,7 @@ ENABLE_TESTING()
INCLUDE_DIRECTORIES(${OPENMM_DIR}/platforms/reference/include)
INCLUDE_DIRECTORIES(${OPENMM_DIR}/openmmapi/include/openmm)
INCLUDE_DIRECTORIES(${OPENMM_DIR}/platforms/reference/src)
INCLUDE_DIRECTORIES(${OPENMM_DIR}/platforms/reference/tests)
INCLUDE_DIRECTORIES(${OPENMM_DIR}/plugins/drude/tests)
SET(SHARED_OPENMM_DRUDE_TARGET OpenMMDrude)
......
/* -------------------------------------------------------------------------- *
* OpenMM *
* -------------------------------------------------------------------------- *
* This is part of the OpenMM molecular simulation toolkit originating from *
* Simbios, the NIH National Center for Physics-Based Simulation of *
* Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org. *
* *
* Portions copyright (c) 2013 Stanford University and the Authors. *
* Authors: Peter Eastman *
* Contributors: *
* *
* Permission is hereby granted, free of charge, to any person obtaining a *
* copy of this software and associated documentation files (the "Software"), *
* to deal in the Software without restriction, including without limitation *
* the rights to use, copy, modify, merge, publish, distribute, sublicense, *
* and/or sell copies of the Software, and to permit persons to whom the *
* Software is furnished to do so, subject to the following conditions: *
* *
* The above copyright notice and this permission notice shall be included in *
* all copies or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
* THE AUTHORS, CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, *
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR *
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE *
* USE OR OTHER DEALINGS IN THE SOFTWARE. *
* -------------------------------------------------------------------------- */
#include "ReferenceTests.h"
extern "C" void registerDrudeReferenceKernelFactories();
using namespace OpenMM;
void setupKernels (int argc, char* argv[]) {
registerDrudeReferenceKernelFactories();
platform = dynamic_cast<ReferencePlatform&>(Platform::getPlatformByName("Reference"));
initializeTests(argc, argv);
}
......@@ -29,185 +29,7 @@
* USE OR OTHER DEALINGS IN THE SOFTWARE. *
* -------------------------------------------------------------------------- */
/**
* This tests the Reference implementation of DrudeForce.
*/
#include "ReferenceDrudeTests.h"
#include "TestDrudeForce.h"
#include "openmm/internal/AssertionUtilities.h"
#include "openmm/Context.h"
#include "openmm/NonbondedForce.h"
#include "openmm/Platform.h"
#include "openmm/System.h"
#include "openmm/VerletIntegrator.h"
#include "openmm/DrudeForce.h"
#include "SimTKOpenMMUtilities.h"
#include <iostream>
#include <vector>
using namespace OpenMM;
using namespace std;
extern "C" OPENMM_EXPORT void registerDrudeReferenceKernelFactories();
void validateForce(System& system, vector<Vec3>& positions, double expectedEnergy) {
// Given a System containing a Drude force, check that its energy has the expected value.
VerletIntegrator integ(1.0);
Platform& platform = Platform::getPlatformByName("Reference");
Context context(system, integ, platform);
context.setPositions(positions);
State state = context.getState(State::Energy | State::Forces);
ASSERT_EQUAL_TOL(expectedEnergy, state.getPotentialEnergy(), 1e-5);
// Try moving each particle along each axis, and see if the energy changes by the correct amount.
double offset = 1e-3;
for (int i = 0; i < system.getNumParticles(); i++)
for (int j = 0; j < 3; j++) {
vector<Vec3> offsetPos = positions;
offsetPos[i][j] = positions[i][j]-offset;
context.setPositions(offsetPos);
double e1 = context.getState(State::Energy | State::Forces).getPotentialEnergy();
offsetPos[i][j] = positions[i][j]+offset;
context.setPositions(offsetPos);
double e2 = context.getState(State::Energy | State::Forces).getPotentialEnergy();
ASSERT_EQUAL_TOL(state.getForces()[i][j], (e1-e2)/(2*offset), 1e-5);
}
}
void testSingleParticle() {
const double k = ONE_4PI_EPS0*1.5;
const double charge = 0.1;
const double alpha = ONE_4PI_EPS0*charge*charge/k;
System system;
system.addParticle(1.0);
system.addParticle(1.0);
DrudeForce* drude = new DrudeForce();
drude->addParticle(1, 0, -1, -1, -1, charge, alpha, 1, 1);
system.addForce(drude);
ASSERT(!drude->usesPeriodicBoundaryConditions());
ASSERT(!system.usesPeriodicBoundaryConditions());
vector<Vec3> positions(2);
positions[0] = Vec3(-1, 0, 0);
positions[1] = Vec3(2, 0, 0);
validateForce(system, positions, 0.5*k*3*3);
}
void testAnisotropicParticle() {
const double k = ONE_4PI_EPS0*1.5;
const double charge = 0.1;
const double alpha = ONE_4PI_EPS0*charge*charge/k;
const double a1 = 0.8;
const double a2 = 1.1;
const double k1 = k/a1;
const double k2 = k/a2;
const double k3 = k/(3-a1-a2);
System system;
system.addParticle(1.0);
system.addParticle(1.0);
system.addParticle(1.0);
system.addParticle(1.0);
system.addParticle(1.0);
DrudeForce* drude = new DrudeForce();
drude->addParticle(1, 0, 2, 3, 4, charge, alpha, a1, a2);
system.addForce(drude);
vector<Vec3> positions(5);
positions[0] = Vec3(0, 0, 0);
positions[1] = Vec3(0.1, -0.5, 0.8);
positions[2] = Vec3(0, 2, 0);
positions[3] = Vec3(1, 2, 0);
positions[4] = Vec3(1, 2, 3);
validateForce(system, positions, 0.5*k1*0.5*0.5 + 0.5*k2*0.8*0.8 + 0.5*k3*0.1*0.1);
}
double computeScreening(double r, double thole, double alpha1, double alpha2) {
double u = r*thole/pow(alpha1*alpha2, 1.0/6.0);
return 1.0-(1.0+u/2)*exp(-u);
}
void testThole() {
const double k = ONE_4PI_EPS0*1.5;
const double charge = 0.1;
const double alpha = ONE_4PI_EPS0*charge*charge/k;
const double thole = 2.5;
System system;
system.addParticle(1.0);
system.addParticle(1.0);
system.addParticle(1.0);
system.addParticle(1.0);
DrudeForce* drude = new DrudeForce();
drude->addParticle(1, 0, -1, -1, -1, charge, alpha, 1, 1);
drude->addParticle(3, 2, -1, -1, -1, charge, alpha, 1, 1);
drude->addScreenedPair(0, 1, thole);
system.addForce(drude);
vector<Vec3> positions(4);
positions[0] = Vec3(0, 0, 0);
positions[1] = Vec3(0, -0.5, 0);
positions[2] = Vec3(1, 0, 0);
positions[3] = Vec3(1, 0, 0.3);
double energySpring1 = 0.5*k*0.5*0.5;
double energySpring2 = 0.5*k*0.3*0.3;
double energyDipole = 0.0;
double q[] = {-charge, charge, -charge, charge};
for (int i = 0; i < 2; i++)
for (int j = 2; j < 4; j++) {
Vec3 delta = positions[i]-positions[j];
double r = sqrt(delta.dot(delta));
energyDipole += ONE_4PI_EPS0*q[i]*q[j]*computeScreening(r, thole, alpha, alpha)/r;
}
validateForce(system, positions, energySpring1+energySpring2+energyDipole);
}
void testChangingParameters() {
const double k = ONE_4PI_EPS0*1.5;
const double charge = 0.1;
const double alpha = ONE_4PI_EPS0*charge*charge/k;
Platform& platform = Platform::getPlatformByName("Reference");
// Create the system.
System system;
system.addParticle(1.0);
system.addParticle(1.0);
DrudeForce* drude = new DrudeForce();
drude->addParticle(1, 0, -1, -1, -1, charge, alpha, 1, 1);
system.addForce(drude);
vector<Vec3> positions(2);
positions[0] = Vec3(-1, 0, 0);
positions[1] = Vec3(2, 0, 0);
// Check the energy.
VerletIntegrator integ(1.0);
Context context(system, integ, platform);
context.setPositions(positions);
State state = context.getState(State::Energy);
ASSERT_EQUAL_TOL(0.5*k*3*3, state.getPotentialEnergy(), 1e-5);
// Modify the parameters.
const double k2 = ONE_4PI_EPS0*2.2;
const double charge2 = 0.3;
const double alpha2 = ONE_4PI_EPS0*charge2*charge2/k2;
drude->setParticleParameters(0, 1, 0, -1, -1, -1, charge2, alpha2, 1, 1);
drude->updateParametersInContext(context);
state = context.getState(State::Energy);
ASSERT_EQUAL_TOL(0.5*k2*3*3, state.getPotentialEnergy(), 1e-5);
}
int main() {
try {
registerDrudeReferenceKernelFactories();
testSingleParticle();
testAnisotropicParticle();
testThole();
testChangingParameters();
}
catch(const std::exception& e) {
std::cout << "exception: " << e.what() << std::endl;
std::cout << "FAIL - ERROR. Test failed." << std::endl;
return 1;
}
std::cout << "Done" << std::endl;
return 0;
}
void runPlatformTests() {}
......@@ -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) 2013-2016 Stanford University and the Authors. *
* Portions copyright (c) 2013 Stanford University and the Authors. *
* Authors: Peter Eastman *
* Contributors: *
* *
......@@ -29,228 +29,7 @@
* USE OR OTHER DEALINGS IN THE SOFTWARE. *
* -------------------------------------------------------------------------- */
/**
* This tests the Reference implementation of DrudeLangevinIntegrator.
*/
#include "ReferenceDrudeTests.h"
#include "TestDrudeLangevinIntegrator.h"
#include "openmm/internal/AssertionUtilities.h"
#include "openmm/Context.h"
#include "openmm/NonbondedForce.h"
#include "openmm/Platform.h"
#include "openmm/System.h"
#include "openmm/VirtualSite.h"
#include "openmm/DrudeForce.h"
#include "openmm/DrudeLangevinIntegrator.h"
#include "SimTKOpenMMUtilities.h"
#include <iostream>
#include <vector>
using namespace OpenMM;
using namespace std;
extern "C" OPENMM_EXPORT void registerDrudeReferenceKernelFactories();
void testSinglePair() {
const double temperature = 300.0;
const double temperatureDrude = 10.0;
const double k = ONE_4PI_EPS0*1.5;
const double charge = 0.1;
const double alpha = ONE_4PI_EPS0*charge*charge/k;
const double mass1 = 1.0;
const double mass2 = 0.1;
const double totalMass = mass1+mass2;
const double reducedMass = (mass1*mass2)/(mass1+mass2);
const double maxDistance = 0.05;
System system;
system.addParticle(mass1);
system.addParticle(mass2);
DrudeForce* drude = new DrudeForce();
drude->addParticle(1, 0, -1, -1, -1, charge, alpha, 1, 1);
system.addForce(drude);
vector<Vec3> positions(2);
positions[0] = Vec3(0, 0, 0);
positions[1] = Vec3(0, 0, 0);
DrudeLangevinIntegrator integ(temperature, 20.0, temperatureDrude, 20.0, 0.003);
integ.setMaxDrudeDistance(maxDistance);
Platform& platform = Platform::getPlatformByName("Reference");
Context context(system, integ, platform);
context.setPositions(positions);
// Equilibrate.
integ.step(1000);
// Compute the internal and center of mass temperatures.
double keCM = 0, keInternal = 0;
int numSteps = 10000;
for (int i = 0; i < numSteps; i++) {
integ.step(10);
State state = context.getState(State::Velocities | State::Positions);
const vector<Vec3>& vel = state.getVelocities();
Vec3 velCM = vel[0]*(mass1/totalMass) + vel[1]*(mass2/totalMass);
keCM += 0.5*totalMass*velCM.dot(velCM);
Vec3 velInternal = vel[0]-vel[1];
keInternal += 0.5*reducedMass*velInternal.dot(velInternal);
Vec3 delta = state.getPositions()[0]-state.getPositions()[1];
double distance = sqrt(delta.dot(delta));
ASSERT(distance <= maxDistance*(1+1e-6));
}
ASSERT_USUALLY_EQUAL_TOL(3*0.5*BOLTZ*temperature, keCM/numSteps, 0.1);
ASSERT_USUALLY_EQUAL_TOL(3*0.5*BOLTZ*temperatureDrude, keInternal/numSteps, 0.01);
}
void testWater() {
// Create a box of SWM4-NDP water molecules. This involves constraints, virtual sites,
// and Drude particles.
const int gridSize = 3;
const int numMolecules = gridSize*gridSize*gridSize;
const double spacing = 0.6;
const double boxSize = spacing*(gridSize+1);
const double temperature = 300.0;
const double temperatureDrude = 10.0;
System system;
NonbondedForce* nonbonded = new NonbondedForce();
DrudeForce* drude = new DrudeForce();
system.addForce(nonbonded);
system.addForce(drude);
system.setDefaultPeriodicBoxVectors(Vec3(boxSize, 0, 0), Vec3(0, boxSize, 0), Vec3(0, 0, boxSize));
nonbonded->setNonbondedMethod(NonbondedForce::CutoffPeriodic);
nonbonded->setCutoffDistance(1.0);
for (int i = 0; i < numMolecules; i++) {
int startIndex = system.getNumParticles();
system.addParticle(15.6); // O
system.addParticle(0.4); // D
system.addParticle(1.0); // H1
system.addParticle(1.0); // H2
system.addParticle(0.0); // M
nonbonded->addParticle(1.71636, 0.318395, 0.21094*4.184);
nonbonded->addParticle(-1.71636, 1, 0);
nonbonded->addParticle(0.55733, 1, 0);
nonbonded->addParticle(0.55733, 1, 0);
nonbonded->addParticle(-1.11466, 1, 0);
for (int j = 0; j < 5; j++)
for (int k = 0; k < j; k++)
nonbonded->addException(startIndex+j, startIndex+k, 0, 1, 0);
system.addConstraint(startIndex, startIndex+2, 0.09572);
system.addConstraint(startIndex, startIndex+3, 0.09572);
system.addConstraint(startIndex+2, startIndex+3, 0.15139);
system.setVirtualSite(startIndex+4, new ThreeParticleAverageSite(startIndex, startIndex+2, startIndex+3, 0.786646558, 0.106676721, 0.106676721));
drude->addParticle(startIndex+1, startIndex, -1, -1, -1, -1.71636, ONE_4PI_EPS0*1.71636*1.71636/(100000*4.184), 1, 1);
}
vector<Vec3> positions;
for (int i = 0; i < gridSize; i++)
for (int j = 0; j < gridSize; j++)
for (int k = 0; k < gridSize; k++) {
Vec3 pos(i*spacing, j*spacing, k*spacing);
positions.push_back(pos);
positions.push_back(pos);
positions.push_back(pos+Vec3(0.09572, 0, 0));
positions.push_back(pos+Vec3(-0.023999, 0.092663, 0));
positions.push_back(pos);
}
// Simulate it and check the temperature.
DrudeLangevinIntegrator integ(temperature, 50.0, temperatureDrude, 50.0, 0.0005);
Platform& platform = Platform::getPlatformByName("Reference");
Context context(system, integ, platform);
context.setPositions(positions);
context.applyConstraints(1e-5);
// Equilibrate.
integ.step(500);
// Compute the internal and center of mass temperatures.
double ke = 0;
int numSteps = 4000;
for (int i = 0; i < numSteps; i++) {
integ.step(1);
ke += context.getState(State::Energy).getKineticEnergy();
}
ke /= numSteps;
int numStandardDof = 3*3*numMolecules-system.getNumConstraints();
int numDrudeDof = 3*numMolecules;
int numDof = numStandardDof+numDrudeDof;
double expectedTemp = (numStandardDof*temperature+numDrudeDof*temperatureDrude)/numDof;
ASSERT_USUALLY_EQUAL_TOL(expectedTemp, ke/(0.5*numDof*BOLTZ), 0.03);
}
void testForceEnergyConsistency() {
// Create a box of polarizable particles.
const int gridSize = 3;
const int numAtoms = gridSize*gridSize*gridSize;
const double spacing = 0.6;
const double boxSize = spacing*(gridSize+1);
const double temperature = 300.0;
const double temperatureDrude = 10.0;
System system;
vector<Vec3> positions;
NonbondedForce* nonbonded = new NonbondedForce();
DrudeForce* drude = new DrudeForce();
system.addForce(nonbonded);
system.addForce(drude);
system.setDefaultPeriodicBoxVectors(Vec3(boxSize, 0, 0), Vec3(0, boxSize, 0), Vec3(0, 0, boxSize));
nonbonded->setNonbondedMethod(NonbondedForce::PME);
nonbonded->setCutoffDistance(1.0);
nonbonded->setUseSwitchingFunction(true);
nonbonded->setSwitchingDistance(0.9);
nonbonded->setEwaldErrorTolerance(5e-5);
for (int i = 0; i < numAtoms; i++) {
int startIndex = system.getNumParticles();
system.addParticle(1.0);
system.addParticle(1.0);
nonbonded->addParticle(1.0, 0.3, 1.0);
nonbonded->addParticle(-1.0, 0.3, 1.0);
nonbonded->addException(startIndex, startIndex+1, 0, 1, 0);
drude->addParticle(startIndex+1, startIndex, -1, -1, -1, -1.0, 0.001, 1, 1);
}
for (int i = 0; i < gridSize; i++)
for (int j = 0; j < gridSize; j++)
for (int k = 0; k < gridSize; k++) {
Vec3 pos(i*spacing, j*spacing, k*spacing);
positions.push_back(pos);
positions.push_back(pos);
}
// Simulate it and check that force and energy remain consistent.
DrudeLangevinIntegrator integ(temperature, 50.0, temperatureDrude, 50.0, 0.001);
Platform& platform = Platform::getPlatformByName("Reference");
Context context(system, integ, platform);
context.setPositions(positions);
State prevState;
for (int i = 0; i < 100; i++) {
State state = context.getState(State::Energy | State::Forces | State::Positions);
if (i > 0) {
double expectedEnergyChange = 0;
for (int j = 0; j < system.getNumParticles(); j++) {
Vec3 delta = state.getPositions()[j]-prevState.getPositions()[j];
expectedEnergyChange -= 0.5*(state.getForces()[j]+prevState.getForces()[j]).dot(delta);
}
ASSERT_EQUAL_TOL(expectedEnergyChange, state.getPotentialEnergy()-prevState.getPotentialEnergy(), 0.05);
}
prevState = state;
integ.step(1);
}
}
int main() {
try {
registerDrudeReferenceKernelFactories();
testSinglePair();
testWater();
testForceEnergyConsistency();
}
catch(const std::exception& e) {
std::cout << "exception: " << e.what() << std::endl;
std::cout << "FAIL - ERROR. Test failed." << std::endl;
return 1;
}
std::cout << "Done" << std::endl;
return 0;
}
void runPlatformTests() {}
......@@ -29,22 +29,7 @@
* USE OR OTHER DEALINGS IN THE SOFTWARE. *
* -------------------------------------------------------------------------- */
//#include "ReferenceTests.h"
#include "openmm/Platform.h"
using namespace OpenMM;
using namespace std;
//extern "C" OPENMM_EXPORT void registerDrudeReferenceKernelFactories();
Platform& initializePlatform(int argc, char* argv[]) {
/* registerDrudeReferenceKernelFactories();
*/
return Platform::getPlatformByName("Reference");
}
#include "ReferenceDrudeTests.h"
#include "TestDrudeNoseHoover.h"
void runPlatformTests() { }
void runPlatformTests() {}
......@@ -29,113 +29,7 @@
* USE OR OTHER DEALINGS IN THE SOFTWARE. *
* -------------------------------------------------------------------------- */
/**
* This tests the Reference implementation of DrudeSCFIntegrator.
*/
#include "ReferenceDrudeTests.h"
#include "TestDrudeSCFIntegrator.h"
#include "openmm/internal/AssertionUtilities.h"
#include "openmm/Context.h"
#include "openmm/NonbondedForce.h"
#include "openmm/Platform.h"
#include "openmm/System.h"
#include "openmm/VirtualSite.h"
#include "openmm/DrudeForce.h"
#include "openmm/DrudeSCFIntegrator.h"
#include "SimTKOpenMMUtilities.h"
#include <iostream>
#include <vector>
using namespace OpenMM;
using namespace std;
extern "C" OPENMM_EXPORT void registerDrudeReferenceKernelFactories();
void testWater() {
// Create a box of SWM4-NDP water molecules. This involves constraints, virtual sites,
// and Drude particles.
const int gridSize = 3;
const int numMolecules = gridSize*gridSize*gridSize;
const double spacing = 0.6;
const double boxSize = spacing*(gridSize+1);
System system;
NonbondedForce* nonbonded = new NonbondedForce();
DrudeForce* drude = new DrudeForce();
system.addForce(nonbonded);
system.addForce(drude);
system.setDefaultPeriodicBoxVectors(Vec3(boxSize, 0, 0), Vec3(0, boxSize, 0), Vec3(0, 0, boxSize));
nonbonded->setNonbondedMethod(NonbondedForce::CutoffPeriodic);
nonbonded->setCutoffDistance(1.0);
for (int i = 0; i < numMolecules; i++) {
int startIndex = system.getNumParticles();
system.addParticle(15.6); // O
system.addParticle(0.4); // D
system.addParticle(1.0); // H1
system.addParticle(1.0); // H2
system.addParticle(0.0); // M
nonbonded->addParticle(1.71636, 0.318395, 0.21094*4.184);
nonbonded->addParticle(-1.71636, 1, 0);
nonbonded->addParticle(0.55733, 1, 0);
nonbonded->addParticle(0.55733, 1, 0);
nonbonded->addParticle(-1.11466, 1, 0);
for (int j = 0; j < 5; j++)
for (int k = 0; k < j; k++)
nonbonded->addException(startIndex+j, startIndex+k, 0, 1, 0);
system.addConstraint(startIndex, startIndex+2, 0.09572);
system.addConstraint(startIndex, startIndex+3, 0.09572);
system.addConstraint(startIndex+2, startIndex+3, 0.15139);
system.setVirtualSite(startIndex+4, new ThreeParticleAverageSite(startIndex, startIndex+2, startIndex+3, 0.786646558, 0.106676721, 0.106676721));
drude->addParticle(startIndex+1, startIndex, -1, -1, -1, -1.71636, ONE_4PI_EPS0*1.71636*1.71636/(100000*4.184), 1, 1);
}
vector<Vec3> positions;
for (int i = 0; i < gridSize; i++)
for (int j = 0; j < gridSize; j++)
for (int k = 0; k < gridSize; k++) {
Vec3 pos(i*spacing, j*spacing, k*spacing);
positions.push_back(pos);
positions.push_back(pos);
positions.push_back(pos+Vec3(0.09572, 0, 0));
positions.push_back(pos+Vec3(-0.023999, 0.092663, 0));
positions.push_back(pos);
}
// Simulate it and check energy conservation and the total force on the Drude particles.
DrudeSCFIntegrator integ(0.0005);
Platform& platform = Platform::getPlatformByName("Reference");
Context context(system, integ, platform);
context.setPositions(positions);
context.applyConstraints(1e-5);
context.setVelocitiesToTemperature(300.0);
State state = context.getState(State::Energy);
double initialEnergy;
int numSteps = 1000;
for (int i = 0; i < numSteps; i++) {
integ.step(1);
state = context.getState(State::Energy | State::Forces);
if (i == 0)
initialEnergy = state.getPotentialEnergy()+state.getKineticEnergy();
else
ASSERT_EQUAL_TOL(initialEnergy, state.getPotentialEnergy()+state.getKineticEnergy(), 0.01);
const vector<Vec3>& force = state.getForces();
double norm = 0.0;
for (int j = 1; j < (int) force.size(); j += 5)
norm += sqrt(force[j].dot(force[j]));
norm = (norm/numMolecules);
ASSERT(norm < 1.0);
}
}
int main() {
try {
registerDrudeReferenceKernelFactories();
testWater();
}
catch(const std::exception& e) {
std::cout << "exception: " << e.what() << std::endl;
std::cout << "FAIL - ERROR. Test failed." << std::endl;
return 1;
}
std::cout << "Done" << std::endl;
return 0;
}
void runPlatformTests() {}
/* -------------------------------------------------------------------------- *
* OpenMM *
* -------------------------------------------------------------------------- *
* This is part of the OpenMM molecular simulation toolkit originating from *
* Simbios, the NIH National Center for Physics-Based Simulation of *
* Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org. *
* *
* Portions copyright (c) 2013 Stanford University and the Authors. *
* Authors: Peter Eastman *
* Contributors: *
* *
* Permission is hereby granted, free of charge, to any person obtaining a *
* copy of this software and associated documentation files (the "Software"), *
* to deal in the Software without restriction, including without limitation *
* the rights to use, copy, modify, merge, publish, distribute, sublicense, *
* and/or sell copies of the Software, and to permit persons to whom the *
* Software is furnished to do so, subject to the following conditions: *
* *
* The above copyright notice and this permission notice shall be included in *
* all copies or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
* THE AUTHORS, CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, *
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR *
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE *
* USE OR OTHER DEALINGS IN THE SOFTWARE. *
* -------------------------------------------------------------------------- */
#include "openmm/internal/AssertionUtilities.h"
#include "openmm/Context.h"
#include "openmm/NonbondedForce.h"
#include "openmm/OpenMMException.h"
#include "openmm/Platform.h"
#include "openmm/System.h"
#include "openmm/VerletIntegrator.h"
#include "openmm/DrudeForce.h"
#include "SimTKOpenMMUtilities.h"
#include <iostream>
#include <vector>
using namespace OpenMM;
using namespace std;
void validateForce(System& system, vector<Vec3>& positions, double expectedEnergy) {
// Given a System containing a Drude force, check that its energy has the expected value.
VerletIntegrator integ(1.0);
Context context(system, integ, platform);
context.setPositions(positions);
State state = context.getState(State::Energy | State::Forces);
ASSERT_EQUAL_TOL(expectedEnergy, state.getPotentialEnergy(), 1e-5);
// Try moving each particle along each axis, and see if the energy changes by the correct amount.
double offset = 1e-3;
double TOL = 1e-5;
try {
if (platform.getPropertyValue(context, "Precision") != "double") {
offset = 1e-2;
TOL = 5e-4;
}
} catch(OpenMMException) {
// The defaults above are for double precision, which is assumed in this case
}
for (int i = 0; i < system.getNumParticles(); i++)
for (int j = 0; j < 3; j++) {
vector<Vec3> offsetPos = positions;
offsetPos[i][j] = positions[i][j]-offset;
context.setPositions(offsetPos);
double e1 = context.getState(State::Energy | State::Forces).getPotentialEnergy();
offsetPos[i][j] = positions[i][j]+offset;
context.setPositions(offsetPos);
double e2 = context.getState(State::Energy | State::Forces).getPotentialEnergy();
ASSERT_EQUAL_TOL(state.getForces()[i][j], (e1-e2)/(2*offset), TOL);
}
}
void testSingleParticle() {
const double k = ONE_4PI_EPS0*1.5;
const double charge = 0.1;
const double alpha = ONE_4PI_EPS0*charge*charge/k;
System system;
system.addParticle(1.0);
system.addParticle(1.0);
DrudeForce* drude = new DrudeForce();
drude->addParticle(1, 0, -1, -1, -1, charge, alpha, 1, 1);
system.addForce(drude);
ASSERT(!drude->usesPeriodicBoundaryConditions());
ASSERT(!system.usesPeriodicBoundaryConditions());
vector<Vec3> positions(2);
positions[0] = Vec3(-1, 0, 0);
positions[1] = Vec3(2, 0, 0);
validateForce(system, positions, 0.5*k*3*3);
}
void testAnisotropicParticle() {
const double k = ONE_4PI_EPS0*1.5;
const double charge = 0.1;
const double alpha = ONE_4PI_EPS0*charge*charge/k;
const double a1 = 0.8;
const double a2 = 1.1;
const double k1 = k/a1;
const double k2 = k/a2;
const double k3 = k/(3-a1-a2);
System system;
system.addParticle(1.0);
system.addParticle(1.0);
system.addParticle(1.0);
system.addParticle(1.0);
system.addParticle(1.0);
DrudeForce* drude = new DrudeForce();
drude->addParticle(1, 0, 2, 3, 4, charge, alpha, a1, a2);
system.addForce(drude);
vector<Vec3> positions(5);
positions[0] = Vec3(0, 0, 0);
positions[1] = Vec3(0.1, -0.5, 0.8);
positions[2] = Vec3(0, 2, 0);
positions[3] = Vec3(1, 2, 0);
positions[4] = Vec3(1, 2, 3);
validateForce(system, positions, 0.5*k1*0.5*0.5 + 0.5*k2*0.8*0.8 + 0.5*k3*0.1*0.1);
}
double computeScreening(double r, double thole, double alpha1, double alpha2) {
double u = r*thole/pow(alpha1*alpha2, 1.0/6.0);
return 1.0-(1.0+u/2)*exp(-u);
}
void testThole() {
const double k = ONE_4PI_EPS0*1.5;
const double charge = 0.1;
const double alpha = ONE_4PI_EPS0*charge*charge/k;
const double thole = 2.5;
System system;
system.addParticle(1.0);
system.addParticle(1.0);
system.addParticle(1.0);
system.addParticle(1.0);
DrudeForce* drude = new DrudeForce();
drude->addParticle(1, 0, -1, -1, -1, charge, alpha, 1, 1);
drude->addParticle(3, 2, -1, -1, -1, charge, alpha, 1, 1);
drude->addScreenedPair(0, 1, thole);
system.addForce(drude);
vector<Vec3> positions(4);
positions[0] = Vec3(0, 0, 0);
positions[1] = Vec3(0, -0.5, 0);
positions[2] = Vec3(1, 0, 0);
positions[3] = Vec3(1, 0, 0.3);
double energySpring1 = 0.5*k*0.5*0.5;
double energySpring2 = 0.5*k*0.3*0.3;
double energyDipole = 0.0;
double q[] = {-charge, charge, -charge, charge};
for (int i = 0; i < 2; i++)
for (int j = 2; j < 4; j++) {
Vec3 delta = positions[i]-positions[j];
double r = sqrt(delta.dot(delta));
energyDipole += ONE_4PI_EPS0*q[i]*q[j]*computeScreening(r, thole, alpha, alpha)/r;
}
validateForce(system, positions, energySpring1+energySpring2+energyDipole);
}
void testChangingParameters() {
const double k = ONE_4PI_EPS0*1.5;
const double charge = 0.1;
const double alpha = ONE_4PI_EPS0*charge*charge/k;
// Create the system.
System system;
system.addParticle(1.0);
system.addParticle(1.0);
DrudeForce* drude = new DrudeForce();
drude->addParticle(1, 0, -1, -1, -1, charge, alpha, 1, 1);
system.addForce(drude);
vector<Vec3> positions(2);
positions[0] = Vec3(-1, 0, 0);
positions[1] = Vec3(2, 0, 0);
// Check the energy.
VerletIntegrator integ(1.0);
Context context(system, integ, platform);
context.setPositions(positions);
State state = context.getState(State::Energy);
ASSERT_EQUAL_TOL(0.5*k*3*3, state.getPotentialEnergy(), 1e-5);
// Modify the parameters.
const double k2 = ONE_4PI_EPS0*2.2;
const double charge2 = 0.3;
const double alpha2 = ONE_4PI_EPS0*charge2*charge2/k2;
drude->setParticleParameters(0, 1, 0, -1, -1, -1, charge2, alpha2, 1, 1);
drude->updateParametersInContext(context);
state = context.getState(State::Energy);
ASSERT_EQUAL_TOL(0.5*k2*3*3, state.getPotentialEnergy(), 1e-5);
}
void setupKernels(int argc, char* argv[]);
void runPlatformTests();
int main(int argc, char* argv[]) {
try {
setupKernels(argc, argv);
testSingleParticle();
testAnisotropicParticle();
testThole();
testChangingParameters();
runPlatformTests();
}
catch(const std::exception& e) {
std::cout << "exception: " << e.what() << std::endl;
std::cout << "FAIL - ERROR. Test failed." << std::endl;
return 1;
}
std::cout << "Done" << std::endl;
return 0;
}
/* -------------------------------------------------------------------------- *
* OpenMM *
* -------------------------------------------------------------------------- *
* This is part of the OpenMM molecular simulation toolkit originating from *
* Simbios, the NIH National Center for Physics-Based Simulation of *
* Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org. *
* *
* Portions copyright (c) 2013-2016 Stanford University and the Authors. *
* Authors: Peter Eastman *
* Contributors: *
* *
* Permission is hereby granted, free of charge, to any person obtaining a *
* copy of this software and associated documentation files (the "Software"), *
* to deal in the Software without restriction, including without limitation *
* the rights to use, copy, modify, merge, publish, distribute, sublicense, *
* and/or sell copies of the Software, and to permit persons to whom the *
* Software is furnished to do so, subject to the following conditions: *
* *
* The above copyright notice and this permission notice shall be included in *
* all copies or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
* THE AUTHORS, CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, *
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR *
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE *
* USE OR OTHER DEALINGS IN THE SOFTWARE. *
* -------------------------------------------------------------------------- */
#include "openmm/internal/AssertionUtilities.h"
#include "openmm/Context.h"
#include "openmm/NonbondedForce.h"
#include "openmm/Platform.h"
#include "openmm/System.h"
#include "openmm/VirtualSite.h"
#include "openmm/DrudeForce.h"
#include "openmm/DrudeLangevinIntegrator.h"
#include "SimTKOpenMMUtilities.h"
#include <iostream>
#include <vector>
using namespace OpenMM;
using namespace std;
void testSinglePair() {
const double temperature = 300.0;
const double temperatureDrude = 10.0;
const double k = ONE_4PI_EPS0*1.5;
const double charge = 0.1;
const double alpha = ONE_4PI_EPS0*charge*charge/k;
const double mass1 = 1.0;
const double mass2 = 0.1;
const double totalMass = mass1+mass2;
const double reducedMass = (mass1*mass2)/(mass1+mass2);
const double maxDistance = 0.05;
System system;
system.addParticle(mass1);
system.addParticle(mass2);
DrudeForce* drude = new DrudeForce();
drude->addParticle(1, 0, -1, -1, -1, charge, alpha, 1, 1);
system.addForce(drude);
vector<Vec3> positions(2);
positions[0] = Vec3(0, 0, 0);
positions[1] = Vec3(0, 0, 0);
DrudeLangevinIntegrator integ(temperature, 20.0, temperatureDrude, 20.0, 0.003);
integ.setMaxDrudeDistance(maxDistance);
Context context(system, integ, platform);
context.setPositions(positions);
// Equilibrate.
integ.step(1000);
// Compute the internal and center of mass temperatures.
double keCM = 0, keInternal = 0;
int numSteps = 10000;
for (int i = 0; i < numSteps; i++) {
integ.step(10);
State state = context.getState(State::Velocities | State::Positions);
const vector<Vec3>& vel = state.getVelocities();
Vec3 velCM = vel[0]*(mass1/totalMass) + vel[1]*(mass2/totalMass);
keCM += 0.5*totalMass*velCM.dot(velCM);
Vec3 velInternal = vel[0]-vel[1];
keInternal += 0.5*reducedMass*velInternal.dot(velInternal);
Vec3 delta = state.getPositions()[0]-state.getPositions()[1];
double distance = sqrt(delta.dot(delta));
ASSERT(distance <= maxDistance*(1+1e-6));
}
ASSERT_USUALLY_EQUAL_TOL(3*0.5*BOLTZ*temperature, keCM/numSteps, 0.1);
ASSERT_USUALLY_EQUAL_TOL(3*0.5*BOLTZ*temperatureDrude, keInternal/numSteps, 0.01);
}
void testWater() {
// Create a box of SWM4-NDP water molecules. This involves constraints, virtual sites,
// and Drude particles.
const int gridSize = 3;
const int numMolecules = gridSize*gridSize*gridSize;
const double spacing = 0.6;
const double boxSize = spacing*(gridSize+1);
const double temperature = 300.0;
const double temperatureDrude = 10.0;
System system;
NonbondedForce* nonbonded = new NonbondedForce();
DrudeForce* drude = new DrudeForce();
system.addForce(nonbonded);
system.addForce(drude);
system.setDefaultPeriodicBoxVectors(Vec3(boxSize, 0, 0), Vec3(0, boxSize, 0), Vec3(0, 0, boxSize));
nonbonded->setNonbondedMethod(NonbondedForce::CutoffPeriodic);
nonbonded->setCutoffDistance(1.0);
for (int i = 0; i < numMolecules; i++) {
int startIndex = system.getNumParticles();
system.addParticle(15.6); // O
system.addParticle(0.4); // D
system.addParticle(1.0); // H1
system.addParticle(1.0); // H2
system.addParticle(0.0); // M
nonbonded->addParticle(1.71636, 0.318395, 0.21094*4.184);
nonbonded->addParticle(-1.71636, 1, 0);
nonbonded->addParticle(0.55733, 1, 0);
nonbonded->addParticle(0.55733, 1, 0);
nonbonded->addParticle(-1.11466, 1, 0);
for (int j = 0; j < 5; j++)
for (int k = 0; k < j; k++)
nonbonded->addException(startIndex+j, startIndex+k, 0, 1, 0);
system.addConstraint(startIndex, startIndex+2, 0.09572);
system.addConstraint(startIndex, startIndex+3, 0.09572);
system.addConstraint(startIndex+2, startIndex+3, 0.15139);
system.setVirtualSite(startIndex+4, new ThreeParticleAverageSite(startIndex, startIndex+2, startIndex+3, 0.786646558, 0.106676721, 0.106676721));
drude->addParticle(startIndex+1, startIndex, -1, -1, -1, -1.71636, ONE_4PI_EPS0*1.71636*1.71636/(100000*4.184), 1, 1);
}
vector<Vec3> positions;
for (int i = 0; i < gridSize; i++)
for (int j = 0; j < gridSize; j++)
for (int k = 0; k < gridSize; k++) {
Vec3 pos(i*spacing, j*spacing, k*spacing);
positions.push_back(pos);
positions.push_back(pos);
positions.push_back(pos+Vec3(0.09572, 0, 0));
positions.push_back(pos+Vec3(-0.023999, 0.092663, 0));
positions.push_back(pos);
}
// Simulate it and check the temperature.
DrudeLangevinIntegrator integ(temperature, 50.0, temperatureDrude, 50.0, 0.0005);
Context context(system, integ, platform);
context.setPositions(positions);
context.applyConstraints(1e-5);
// Equilibrate.
integ.step(500);
// Compute the internal and center of mass temperatures.
double ke = 0;
int numSteps = 4000;
for (int i = 0; i < numSteps; i++) {
integ.step(1);
ke += context.getState(State::Energy).getKineticEnergy();
}
ke /= numSteps;
int numStandardDof = 3*3*numMolecules-system.getNumConstraints();
int numDrudeDof = 3*numMolecules;
int numDof = numStandardDof+numDrudeDof;
double expectedTemp = (numStandardDof*temperature+numDrudeDof*temperatureDrude)/numDof;
ASSERT_USUALLY_EQUAL_TOL(expectedTemp, ke/(0.5*numDof*BOLTZ), 0.03);
}
void testForceEnergyConsistency() {
// Create a box of polarizable particles.
const int gridSize = 3;
const int numAtoms = gridSize*gridSize*gridSize;
const double spacing = 0.6;
const double boxSize = spacing*(gridSize+1);
const double temperature = 300.0;
const double temperatureDrude = 10.0;
System system;
vector<Vec3> positions;
NonbondedForce* nonbonded = new NonbondedForce();
DrudeForce* drude = new DrudeForce();
system.addForce(nonbonded);
system.addForce(drude);
system.setDefaultPeriodicBoxVectors(Vec3(boxSize, 0, 0), Vec3(0, boxSize, 0), Vec3(0, 0, boxSize));
nonbonded->setNonbondedMethod(NonbondedForce::PME);
nonbonded->setCutoffDistance(1.0);
nonbonded->setUseSwitchingFunction(true);
nonbonded->setSwitchingDistance(0.9);
nonbonded->setEwaldErrorTolerance(5e-5);
for (int i = 0; i < numAtoms; i++) {
int startIndex = system.getNumParticles();
system.addParticle(1.0);
system.addParticle(1.0);
nonbonded->addParticle(1.0, 0.3, 1.0);
nonbonded->addParticle(-1.0, 0.3, 1.0);
nonbonded->addException(startIndex, startIndex+1, 0, 1, 0);
drude->addParticle(startIndex+1, startIndex, -1, -1, -1, -1.0, 0.001, 1, 1);
}
for (int i = 0; i < gridSize; i++)
for (int j = 0; j < gridSize; j++)
for (int k = 0; k < gridSize; k++) {
Vec3 pos(i*spacing, j*spacing, k*spacing);
positions.push_back(pos);
positions.push_back(pos);
}
// Simulate it and check that force and energy remain consistent.
DrudeLangevinIntegrator integ(temperature, 50.0, temperatureDrude, 50.0, 0.001);
Context context(system, integ, platform);
context.setPositions(positions);
State prevState;
for (int i = 0; i < 100; i++) {
State state = context.getState(State::Energy | State::Forces | State::Positions);
if (i > 0) {
double expectedEnergyChange = 0;
for (int j = 0; j < system.getNumParticles(); j++) {
Vec3 delta = state.getPositions()[j]-prevState.getPositions()[j];
expectedEnergyChange -= 0.5*(state.getForces()[j]+prevState.getForces()[j]).dot(delta);
}
ASSERT_EQUAL_TOL(expectedEnergyChange, state.getPotentialEnergy()-prevState.getPotentialEnergy(), 0.05);
}
prevState = state;
integ.step(1);
}
}
void testInitialTemperature() {
// Check temperature initialization for a collection of randomly placed particles
const int numRealParticles = 500000;
const int numParticles = 2 * numRealParticles;
const int nDoF = 3 * numRealParticles;
const double targetTemperature = 300;
const double drudeTemperature = 1;
const double realMass = 10;
const double drudeMass = 1;
System system;
OpenMM_SFMT::SFMT sfmt;
init_gen_rand(0, sfmt);
std::vector<Vec3> positions(numParticles);
DrudeForce* drude = new DrudeForce();
for (int i = 0; i < numRealParticles; i++) {
system.addParticle(realMass);
system.addParticle(drudeMass);
positions[2*i][0] = genrand_real2(sfmt);
positions[2*i][1] = genrand_real2(sfmt);
positions[2*i][2] = genrand_real2(sfmt);
positions[2*i+1][0] = positions[2*i][0] + 0.01*genrand_real2(sfmt);
positions[2*i+1][1] = positions[2*i][1] + 0.01*genrand_real2(sfmt);
positions[2*i+1][2] = positions[2*i][2] + 0.01*genrand_real2(sfmt);
drude->addParticle(2*i+1, 2*i, -1, -1, -1, -1.0, 0.001, 1, 1);
}
system.addForce(drude);
DrudeLangevinIntegrator integrator(targetTemperature, 25, drudeTemperature, 25, 0.001);
Context context(system, integrator, platform);
context.setPositions(positions);
context.setVelocitiesToTemperature(targetTemperature);
auto velocities = context.getState(State::Velocities).getVelocities();
double comKineticEnergy = 0;
double relKineticEnergy = 0;
for (int i = 0; i < numRealParticles; i++) {
int m1 = realMass;
int m2 = drudeMass;
Vec3 v1 = velocities[2*i];
Vec3 v2 = velocities[2*i + 1];
double invMass = 1.0 / (m1 + m2);
double redMass = m1 * m2 * invMass;
double fracM1 = m1 * invMass;
double fracM2 = m2 * invMass;
Vec3 comVelocity = fracM1 * v1 + fracM2 * v2;
Vec3 relVelocity = v2 - v1;
comKineticEnergy += 0.5 * (m1 + m2) * comVelocity.dot(comVelocity);
relKineticEnergy += 0.5 * redMass * relVelocity.dot(relVelocity);
}
double comTemperature = (2*comKineticEnergy / (nDoF*BOLTZ));
double relTemperature = (2*relKineticEnergy / (nDoF*BOLTZ));
ASSERT_USUALLY_EQUAL_TOL(targetTemperature, comTemperature, 0.01);
ASSERT_USUALLY_EQUAL_TOL(drudeTemperature, relTemperature, 0.01);
}
void setupKernels(int argc, char* argv[]);
void runPlatformTests();
int main(int argc, char* argv[]) {
try {
setupKernels(argc, argv);
testInitialTemperature();
testSinglePair();
testWater();
testForceEnergyConsistency();
runPlatformTests();
}
catch(const std::exception& e) {
std::cout << "exception: " << e.what() << std::endl;
std::cout << "FAIL - ERROR. Test failed." << std::endl;
return 1;
}
std::cout << "Done" << std::endl;
return 0;
}
......@@ -52,9 +52,6 @@
using namespace OpenMM;
using namespace std;
extern "C" OPENMM_EXPORT void registerDrudeReferenceKernelFactories();
extern "C" OPENMM_EXPORT void registerKernelFactories();
Platform& initializePlatform(int argc, char* argv[]);
void build_waterbox(System &system, int gridSize, double polarizability, vector<Vec3> & positions) {
// Create a box of SWM4-NDP water molecules. This involves constraints, virtual sites,
......@@ -107,7 +104,7 @@ void build_waterbox(System &system, int gridSize, double polarizability, vector<
}
}
void testWaterBox(Platform& platform) {
void testWaterBox() {
// Create a box of SWM4-NDP water molecules. This involves constraints, virtual sites,
// and Drude particles.
System system;
......@@ -128,8 +125,10 @@ void testWaterBox(Platform& platform) {
int chainLength = 4;
int numMTS = 4;
int numYS = 5;
double frequency = 800.0;
double frequencyDrude = 2000.0;
// N.B. These are higher frequencies than recommeded for production runs, but are used
// here to achieve rapid equilibration to the target temperature, allowing a short run
double frequency = 1000.0;
double frequencyDrude = 1000.0;
int randomSeed = 100;
DrudeNoseHooverIntegrator integ(temperature, frequency,
temperatureDrude, frequencyDrude, 0.0005,
......@@ -137,25 +136,14 @@ void testWaterBox(Platform& platform) {
Context context(system, integ, platform);
context.setPositions(positions);
context.setVelocitiesToTemperature(temperature, randomSeed);
std::vector<Vec3> velocities = context.getState(State::Velocities).getVelocities();
for (int i = 0; i < numMolecules; i++){
Vec3 noize;
for (int j = 0; j < 3; j++){
noize[j] = float(((i+18311)*(j+18253) * 313419097822414) % 18313) / float(18313);
noize[j] *= sqrt(3 * BOLTZ * temperatureDrude / 0.4);
}
velocities[5*i+1] = velocities[5*i] + noize;
}
context.setVelocities(velocities);
context.applyConstraints(1e-6);
// Equilibrate.
integ.step(500);
// Equilibrate
integ.step(1500);
// Compute the internal and center of mass temperatures.
double totalKE = 0;
const int numSteps = 4000;
const int numSteps = 500;
double meanTemp = 0.0;
double meanDrudeTemp = 0.0;
double meanConserved = 0.0;
......@@ -174,15 +162,15 @@ void testWaterBox(Platform& platform) {
double conserved = PE + fullKE + heatBathEnergy;
meanConserved = (i*meanConserved + conserved)/(i+1);
totalKE += KE;
ASSERT(fabs(meanConserved - conserved) < 0.6);
ASSERT(fabs(meanConserved - conserved) < 0.3);
}
totalKE /= numSteps;
ASSERT_USUALLY_EQUAL_TOL(temperature, meanTemp, 0.004);
ASSERT_USUALLY_EQUAL_TOL(temperatureDrude, meanDrudeTemp, 0.004);
ASSERT_USUALLY_EQUAL_TOL(temperature, meanTemp, 0.03);
ASSERT_USUALLY_EQUAL_TOL(temperatureDrude, meanDrudeTemp, 0.03);
}
double testWaterBoxWithHardWallConstraint(Platform& platform, double hardWallConstraint){
double testWaterBoxWithHardWallConstraint(double hardWallConstraint){
// Create a box of SWM4-NDP water molecules. This involves constraints, virtual sites,
// and Drude particles.
System system;
......@@ -213,24 +201,14 @@ double testWaterBoxWithHardWallConstraint(Platform& platform, double hardWallCon
Context context(system, integ, platform);
context.setPositions(positions);
context.setVelocitiesToTemperature(temperature, randomSeed);
std::vector<Vec3> velocities = context.getState(State::Velocities).getVelocities();
for (int i = 0; i < numMolecules; i++){
Vec3 noize;
for (int j = 0; j < 3; j++){
noize[j] = float(((i+18311)*(j+18253) * 313419097822414) % 18313) / float(18313);
noize[j] *= sqrt(3 * BOLTZ * temperatureDrude / 0.4);
}
velocities[5*i+1] = velocities[5*i] + noize;
}
context.setVelocities(velocities);
context.applyConstraints(1e-6);
// Equilibrate.
integ.step(50);
integ.step(10);
// Compute the internal and center of mass temperatures.
double totalKE = 0;
const int numSteps = 10;
const int numSteps = 500;
double meanTemp = 0.0;
double meanDrudeTemp = 0.0;
double meanConserved = 0.0;
......@@ -238,40 +216,90 @@ double testWaterBoxWithHardWallConstraint(Platform& platform, double hardWallCon
for (int i = 0; i < numSteps; i++) {
integ.step(1);
State state = context.getState(State::Energy | State::Positions);
double KE = state.getKineticEnergy();
double PE = state.getPotentialEnergy();
double fullKE = integ.computeTotalKineticEnergy();
double drudeKE = integ.computeDrudeKineticEnergy();
double temp = KE/(0.5*numStandardDof*BOLTZ);
double drudeTemp = drudeKE/(0.5*numDrudeDof*BOLTZ);
meanTemp = (i*meanTemp + temp)/(i+1);
meanDrudeTemp = (i*meanDrudeTemp + drudeTemp)/(i+1);
double heatBathEnergy = integ.computeHeatBathEnergy();
double conserved = PE + fullKE + heatBathEnergy;
meanConserved = (i*meanConserved + conserved)/(i+1);
const auto & positions = state.getPositions();
for(int mol = 0; mol < gridSize*gridSize*gridSize; ++mol) {
auto dR = positions[5*mol+1] - positions[5*mol];
maxR = std::max(maxR, std::sqrt(dR.dot(dR)));
}
totalKE += KE;
}
totalKE /= numSteps;
return maxR;
}
void testInitialTemperature() {
// Check temperature initialization for a collection of randomly placed particles
const int numRealParticles = 500000;
const int numParticles = 2 * numRealParticles;
const int nDoF = 3 * numRealParticles;
const double targetTemperature = 300;
const double drudeTemperature = 1;
const double realMass = 10;
const double drudeMass = 1;
System system;
OpenMM_SFMT::SFMT sfmt;
init_gen_rand(0, sfmt);
std::vector<Vec3> positions(numParticles);
DrudeForce* drude = new DrudeForce();
for (int i = 0; i < numRealParticles; i++) {
system.addParticle(realMass);
system.addParticle(drudeMass);
positions[2*i][0] = genrand_real2(sfmt);
positions[2*i][1] = genrand_real2(sfmt);
positions[2*i][2] = genrand_real2(sfmt);
positions[2*i+1][0] = positions[2*i][0] + 0.01*genrand_real2(sfmt);
positions[2*i+1][1] = positions[2*i][1] + 0.01*genrand_real2(sfmt);
positions[2*i+1][2] = positions[2*i][2] + 0.01*genrand_real2(sfmt);
drude->addParticle(2*i+1, 2*i, -1, -1, -1, -1.0, 0.001, 1, 1);
}
system.addForce(drude);
CMMotionRemover* cmm = new CMMotionRemover(1);
system.addForce(cmm);
DrudeNoseHooverIntegrator integrator(targetTemperature, 25, drudeTemperature, 25, 0.001);
Context context(system, integrator, platform);
context.setPositions(positions);
context.setVelocitiesToTemperature(targetTemperature);
auto velocities = context.getState(State::Velocities).getVelocities();
double comKineticEnergy = 0;
double relKineticEnergy = 0;
for (int i = 0; i < numRealParticles; i++) {
int m1 = realMass;
int m2 = drudeMass;
Vec3 v1 = velocities[2*i];
Vec3 v2 = velocities[2*i + 1];
double invMass = 1.0 / (m1 + m2);
double redMass = m1 * m2 * invMass;
double fracM1 = m1 * invMass;
double fracM2 = m2 * invMass;
Vec3 comVelocity = fracM1 * v1 + fracM2 * v2;
Vec3 relVelocity = v2 - v1;
comKineticEnergy += 0.5 * (m1 + m2) * comVelocity.dot(comVelocity);
relKineticEnergy += 0.5 * redMass * relVelocity.dot(relVelocity);
}
double comTemperature = (2*comKineticEnergy / (nDoF*BOLTZ));
double relTemperature = (2*relKineticEnergy / (nDoF*BOLTZ));
ASSERT_USUALLY_EQUAL_TOL(targetTemperature, comTemperature, 0.01);
ASSERT_USUALLY_EQUAL_TOL(drudeTemperature, relTemperature, 0.01);
}
void setupKernels(int argc, char* argv[]);
void runPlatformTests();
int main(int argc, char* argv[]) {
try {
registerKernelFactories();
setupKernels(argc, argv);
Platform& platform = initializePlatform(argc, argv);
testWaterBox(platform);
testWaterBox();
double maxDrudeDistance = 0.005;
double observedDrudeDistance = testWaterBoxWithHardWallConstraint(platform, 0.0);
double observedDrudeDistance = testWaterBoxWithHardWallConstraint(0.0);
ASSERT(observedDrudeDistance > maxDrudeDistance);
observedDrudeDistance = testWaterBoxWithHardWallConstraint(platform, maxDrudeDistance);
observedDrudeDistance = testWaterBoxWithHardWallConstraint(maxDrudeDistance);
// Remove later: just trying to find out why Jenkins is upset
if(observedDrudeDistance >= maxDrudeDistance) printf("Max distance %16.10f\n", observedDrudeDistance);
ASSERT(observedDrudeDistance < maxDrudeDistance);
testInitialTemperature();
runPlatformTests();
}
catch(const exception& e) {
cout << "exception: " << e.what() << endl;
......
/* -------------------------------------------------------------------------- *
* OpenMM *
* -------------------------------------------------------------------------- *
* This is part of the OpenMM molecular simulation toolkit originating from *
* Simbios, the NIH National Center for Physics-Based Simulation of *
* Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org. *
* *
* Portions copyright (c) 2013 Stanford University and the Authors. *
* Authors: Peter Eastman *
* Contributors: *
* *
* Permission is hereby granted, free of charge, to any person obtaining a *
* copy of this software and associated documentation files (the "Software"), *
* to deal in the Software without restriction, including without limitation *
* the rights to use, copy, modify, merge, publish, distribute, sublicense, *
* and/or sell copies of the Software, and to permit persons to whom the *
* Software is furnished to do so, subject to the following conditions: *
* *
* The above copyright notice and this permission notice shall be included in *
* all copies or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
* THE AUTHORS, CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, *
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR *
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE *
* USE OR OTHER DEALINGS IN THE SOFTWARE. *
* -------------------------------------------------------------------------- */
/**
* This tests the Reference implementation of DrudeSCFIntegrator.
*/
#include "openmm/internal/AssertionUtilities.h"
#include "openmm/Context.h"
#include "openmm/NonbondedForce.h"
#include "openmm/OpenMMException.h"
#include "openmm/Platform.h"
#include "openmm/System.h"
#include "openmm/VirtualSite.h"
#include "openmm/DrudeForce.h"
#include "openmm/DrudeSCFIntegrator.h"
#include "SimTKOpenMMUtilities.h"
#include <iostream>
#include <vector>
using namespace OpenMM;
using namespace std;
void testWater() {
// Create a box of SWM4-NDP water molecules. This involves constraints, virtual sites,
// and Drude particles.
const int gridSize = 3;
const int numMolecules = gridSize*gridSize*gridSize;
const double spacing = 0.6;
const double boxSize = spacing*(gridSize+1);
System system;
NonbondedForce* nonbonded = new NonbondedForce();
DrudeForce* drude = new DrudeForce();
system.addForce(nonbonded);
system.addForce(drude);
system.setDefaultPeriodicBoxVectors(Vec3(boxSize, 0, 0), Vec3(0, boxSize, 0), Vec3(0, 0, boxSize));
nonbonded->setNonbondedMethod(NonbondedForce::CutoffPeriodic);
nonbonded->setCutoffDistance(1.0);
for (int i = 0; i < numMolecules; i++) {
int startIndex = system.getNumParticles();
system.addParticle(15.6); // O
system.addParticle(0.4); // D
system.addParticle(1.0); // H1
system.addParticle(1.0); // H2
system.addParticle(0.0); // M
nonbonded->addParticle(1.71636, 0.318395, 0.21094*4.184);
nonbonded->addParticle(-1.71636, 1, 0);
nonbonded->addParticle(0.55733, 1, 0);
nonbonded->addParticle(0.55733, 1, 0);
nonbonded->addParticle(-1.11466, 1, 0);
for (int j = 0; j < 5; j++)
for (int k = 0; k < j; k++)
nonbonded->addException(startIndex+j, startIndex+k, 0, 1, 0);
system.addConstraint(startIndex, startIndex+2, 0.09572);
system.addConstraint(startIndex, startIndex+3, 0.09572);
system.addConstraint(startIndex+2, startIndex+3, 0.15139);
system.setVirtualSite(startIndex+4, new ThreeParticleAverageSite(startIndex, startIndex+2, startIndex+3, 0.786646558, 0.106676721, 0.106676721));
drude->addParticle(startIndex+1, startIndex, -1, -1, -1, -1.71636, ONE_4PI_EPS0*1.71636*1.71636/(100000*4.184), 1, 1);
}
vector<Vec3> positions;
for (int i = 0; i < gridSize; i++)
for (int j = 0; j < gridSize; j++)
for (int k = 0; k < gridSize; k++) {
Vec3 pos(i*spacing, j*spacing, k*spacing);
positions.push_back(pos);
positions.push_back(pos);
positions.push_back(pos+Vec3(0.09572, 0, 0));
positions.push_back(pos+Vec3(-0.023999, 0.092663, 0));
positions.push_back(pos);
}
// Simulate it and check energy conservation and the total force on the Drude particles.
DrudeSCFIntegrator integ(0.0005);
Context context(system, integ, platform);
context.setPositions(positions);
context.applyConstraints(1e-5);
context.setVelocitiesToTemperature(300.0);
State state = context.getState(State::Energy);
double initialEnergy;
int numSteps = 1000;
double maxNorm = 1.0;
try {
if (platform.getPropertyValue(context, "Precision") != "double") {
maxNorm = 5.0;
}
} catch(OpenMMException) {
// The defaults above are for double precision, which is assumed in this case
}
for (int i = 0; i < numSteps; i++) {
integ.step(1);
state = context.getState(State::Energy | State::Forces);
if (i == 0)
initialEnergy = state.getPotentialEnergy()+state.getKineticEnergy();
else
ASSERT_EQUAL_TOL(initialEnergy, state.getPotentialEnergy()+state.getKineticEnergy(), 0.01);
const vector<Vec3>& force = state.getForces();
double norm = 0.0;
for (int j = 1; j < (int) force.size(); j += 5)
norm += sqrt(force[j].dot(force[j]));
norm = (norm/numMolecules);
// Just to see what's going on with Travis. Remove later!
if(norm < maxNorm) { printf("norm: %8.2f, allowed %6.2f\n", norm, maxNorm); }
ASSERT(norm < maxNorm);
}
}
void testInitialTemperature() {
// Check temperature initialization for a collection of randomly placed particles
const int numRealParticles = 500000;
const int numParticles = 2 * numRealParticles;
const int nDoF = 3 * numRealParticles;
const double targetTemperature = 300;
const double drudeTemperature = 0;
const double realMass = 10;
const double drudeMass = 1;
System system;
OpenMM_SFMT::SFMT sfmt;
init_gen_rand(0, sfmt);
std::vector<Vec3> positions(numParticles);
DrudeForce* drude = new DrudeForce();
for (int i = 0; i < numRealParticles; i++) {
system.addParticle(realMass);
system.addParticle(drudeMass);
positions[2*i][0] = genrand_real2(sfmt);
positions[2*i][1] = genrand_real2(sfmt);
positions[2*i][2] = genrand_real2(sfmt);
positions[2*i+1][0] = positions[2*i][0] + 0.01*genrand_real2(sfmt);
positions[2*i+1][1] = positions[2*i][1] + 0.01*genrand_real2(sfmt);
positions[2*i+1][2] = positions[2*i][2] + 0.01*genrand_real2(sfmt);
drude->addParticle(2*i+1, 2*i, -1, -1, -1, -1.0, 0.001, 1, 1);
}
system.addForce(drude);
DrudeSCFIntegrator integrator(0.001);
Context context(system, integrator, platform);
context.setPositions(positions);
context.setVelocitiesToTemperature(targetTemperature);
auto velocities = context.getState(State::Velocities).getVelocities();
double comKineticEnergy = 0;
double relKineticEnergy = 0;
for (int i = 0; i < numRealParticles; i++) {
int m1 = realMass;
int m2 = drudeMass;
Vec3 v1 = velocities[2*i];
Vec3 v2 = velocities[2*i + 1];
double invMass = 1.0 / (m1 + m2);
double redMass = m1 * m2 * invMass;
double fracM1 = m1 * invMass;
double fracM2 = m2 * invMass;
Vec3 comVelocity = fracM1 * v1 + fracM2 * v2;
Vec3 relVelocity = v2 - v1;
comKineticEnergy += 0.5 * (m1 + m2) * comVelocity.dot(comVelocity);
relKineticEnergy += 0.5 * redMass * relVelocity.dot(relVelocity);
}
double comTemperature = (2*comKineticEnergy / (nDoF*BOLTZ));
double relTemperature = (2*relKineticEnergy / (nDoF*BOLTZ));
ASSERT_USUALLY_EQUAL_TOL(targetTemperature, comTemperature, 0.01);
ASSERT_USUALLY_EQUAL_TOL(drudeTemperature, relTemperature, 0.01);
}
void setupKernels(int argc, char* argv[]);
void runPlatformTests();
int main(int argc, char* argv[]) {
try {
setupKernels(argc, argv);
testWater();
runPlatformTests();
testInitialTemperature();
}
catch(const std::exception& e) {
std::cout << "exception: " << e.what() << std::endl;
std::cout << "FAIL - ERROR. Test failed." << std::endl;
return 1;
}
std::cout << "Done" << std::endl;
return 0;
}
......@@ -251,6 +251,34 @@ void testRandomSeed() {
}
}
void testInitialTemperature() {
// Check temperature initialization for a collection of randomly placed particles
const int numParticles = 500000;
const int nDoF = 3 * numParticles;
const double targetTemperature = 300;
System system;
OpenMM_SFMT::SFMT sfmt;
init_gen_rand(0, sfmt);
std::vector<Vec3> positions(numParticles);
for (int i = 0; i < numParticles; i++) {
system.addParticle(1.0);
positions[i][0] = genrand_real2(sfmt);
positions[i][1] = genrand_real2(sfmt);
positions[i][2] = genrand_real2(sfmt);
}
BrownianIntegrator integrator(300, 2, 0.001);
Context context(system, integrator, platform);
context.setPositions(positions);
context.setVelocitiesToTemperature(targetTemperature);
auto velocities = context.getState(State::Velocities).getVelocities();
double kineticEnergy = 0;
for(const auto &v : velocities) kineticEnergy += 0.5 * v.dot(v);
double temperature = (2*kineticEnergy / (nDoF*BOLTZ));
ASSERT_USUALLY_EQUAL_TOL(targetTemperature, temperature, 0.01);
}
void runPlatformTests();
int main(int argc, char* argv[]) {
......@@ -261,6 +289,7 @@ int main(int argc, char* argv[]) {
testConstraints();
testConstrainedMasslessParticles();
testRandomSeed();
testInitialTemperature();
runPlatformTests();
}
catch(const exception& e) {
......
......@@ -1128,6 +1128,34 @@ void testRecordEnergy() {
}
}
void testInitialTemperature() {
// Check temperature initialization for a collection of randomly placed particles
const int numParticles = 500000;
const int nDoF = 3 * numParticles;
const double targetTemperature = 300;
System system;
OpenMM_SFMT::SFMT sfmt;
init_gen_rand(0, sfmt);
std::vector<Vec3> positions(numParticles);
for (int i = 0; i < numParticles; i++) {
system.addParticle(1.0);
positions[i][0] = genrand_real2(sfmt);
positions[i][1] = genrand_real2(sfmt);
positions[i][2] = genrand_real2(sfmt);
}
CustomIntegrator integrator(0.001);
Context context(system, integrator, platform);
context.setPositions(positions);
context.setVelocitiesToTemperature(targetTemperature);
auto velocities = context.getState(State::Velocities).getVelocities();
double kineticEnergy = 0;
for(const auto &v : velocities) kineticEnergy += 0.5 * v.dot(v);
double temperature = (2*kineticEnergy / (nDoF*BOLTZ));
ASSERT_USUALLY_EQUAL_TOL(targetTemperature, temperature, 0.01);
}
void runPlatformTests();
int main(int argc, char* argv[]) {
......@@ -1155,6 +1183,7 @@ int main(int argc, char* argv[]) {
testUpdateContextState();
testVectorFunctions();
testRecordEnergy();
testInitialTemperature();
runPlatformTests();
}
catch(const exception& e) {
......
......@@ -258,6 +258,34 @@ void testRandomSeed() {
}
}
void testInitialTemperature() {
// Check temperature initialization for a collection of randomly placed particles
const int numParticles = 500000;
const int nDoF = 3 * numParticles;
const double targetTemperature = 300;
System system;
OpenMM_SFMT::SFMT sfmt;
init_gen_rand(0, sfmt);
std::vector<Vec3> positions(numParticles);
for (int i = 0; i < numParticles; i++) {
system.addParticle(1.0);
positions[i][0] = genrand_real2(sfmt);
positions[i][1] = genrand_real2(sfmt);
positions[i][2] = genrand_real2(sfmt);
}
LangevinIntegrator integrator(300, 25, 0.001);
Context context(system, integrator, platform);
context.setPositions(positions);
context.setVelocitiesToTemperature(targetTemperature);
auto velocities = context.getState(State::Velocities).getVelocities();
double kineticEnergy = 0;
for(const auto &v : velocities) kineticEnergy += 0.5 * v.dot(v);
double temperature = (2*kineticEnergy / (nDoF*BOLTZ));
ASSERT_USUALLY_EQUAL_TOL(targetTemperature, temperature, 0.01);
}
void runPlatformTests();
int main(int argc, char* argv[]) {
......@@ -268,6 +296,7 @@ int main(int argc, char* argv[]) {
testConstraints();
testConstrainedMasslessParticles();
testRandomSeed();
testInitialTemperature();
runPlatformTests();
}
catch(const exception& e) {
......
......@@ -261,6 +261,34 @@ void testRandomSeed() {
}
}
void testInitialTemperature() {
// Check temperature initialization for a collection of randomly placed particles
const int numParticles = 500000;
const int nDoF = 3 * numParticles;
const double targetTemperature = 300;
System system;
OpenMM_SFMT::SFMT sfmt;
init_gen_rand(0, sfmt);
std::vector<Vec3> positions(numParticles);
for (int i = 0; i < numParticles; i++) {
system.addParticle(1.0);
positions[i][0] = genrand_real2(sfmt);
positions[i][1] = genrand_real2(sfmt);
positions[i][2] = genrand_real2(sfmt);
}
LangevinMiddleIntegrator integrator(300, 25, 0.001);
Context context(system, integrator, platform);
context.setPositions(positions);
context.setVelocitiesToTemperature(targetTemperature);
auto velocities = context.getState(State::Velocities).getVelocities();
double kineticEnergy = 0;
for(const auto &v : velocities) kineticEnergy += 0.5 * v.dot(v);
double temperature = (2*kineticEnergy / (nDoF*BOLTZ));
ASSERT_USUALLY_EQUAL_TOL(targetTemperature, temperature, 0.01);
}
void runPlatformTests();
int main(int argc, char* argv[]) {
......@@ -271,6 +299,7 @@ int main(int argc, char* argv[]) {
testConstraints();
testConstrainedMasslessParticles();
testRandomSeed();
testInitialTemperature();
runPlatformTests();
}
catch(const exception& e) {
......
......@@ -269,6 +269,34 @@ void testThreeParticleVirtualSite() {
}
}
void testInitialTemperature() {
// Check temperature initialization for a collection of randomly placed particles
const int numParticles = 500000;
const int nDoF = 3 * numParticles;
const double targetTemperature = 300;
System system;
OpenMM_SFMT::SFMT sfmt;
init_gen_rand(0, sfmt);
std::vector<Vec3> positions(numParticles);
for (int i = 0; i < numParticles; i++) {
system.addParticle(1.0);
positions[i][0] = genrand_real2(sfmt);
positions[i][1] = genrand_real2(sfmt);
positions[i][2] = genrand_real2(sfmt);
}
NoseHooverIntegrator integrator(300, 25, 0.001);
Context context(system, integrator, platform);
context.setPositions(positions);
context.setVelocitiesToTemperature(targetTemperature);
auto velocities = context.getState(State::Velocities).getVelocities();
double kineticEnergy = 0;
for(const auto &v : velocities) kineticEnergy += 0.5 * v.dot(v);
double temperature = (2*kineticEnergy / (nDoF*BOLTZ));
ASSERT_USUALLY_EQUAL_TOL(targetTemperature, temperature, 0.01);
}
void runPlatformTests();
int main(int argc, char* argv[]) {
......@@ -279,6 +307,7 @@ int main(int argc, char* argv[]) {
testVVConstrainedClusters();
testVVConstrainedMasslessParticles();
testThreeParticleVirtualSite();
testInitialTemperature();
runPlatformTests();
}
catch(const exception& e) {
......
......@@ -330,6 +330,34 @@ void testArgonBox() {
}
}
void testInitialTemperature() {
// Check temperature initialization for a collection of randomly placed particles
const int numParticles = 500000;
const int nDoF = 3 * numParticles;
const double targetTemperature = 300;
System system;
OpenMM_SFMT::SFMT sfmt;
init_gen_rand(0, sfmt);
std::vector<Vec3> positions(numParticles);
for (int i = 0; i < numParticles; i++) {
system.addParticle(1.0);
positions[i][0] = genrand_real2(sfmt);
positions[i][1] = genrand_real2(sfmt);
positions[i][2] = genrand_real2(sfmt);
}
VariableLangevinIntegrator integrator(300, 25, 1e-5);
Context context(system, integrator, platform);
context.setPositions(positions);
context.setVelocitiesToTemperature(targetTemperature);
auto velocities = context.getState(State::Velocities).getVelocities();
double kineticEnergy = 0;
for(const auto &v : velocities) kineticEnergy += 0.5 * v.dot(v);
double temperature = (2*kineticEnergy / (nDoF*BOLTZ));
ASSERT_USUALLY_EQUAL_TOL(targetTemperature, temperature, 0.01);
}
void runPlatformTests();
int main(int argc, char* argv[]) {
......@@ -341,6 +369,7 @@ int main(int argc, char* argv[]) {
testConstrainedMasslessParticles();
testRandomSeed();
testArgonBox();
testInitialTemperature();
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