/** * Test a TwoParticleAverageSite virtual site. */ void testTwoParticleAverage() { System system; system.addParticle(1.0); system.addParticle(1.0); system.addParticle(0.0); system.setVirtualSite(2, new TwoParticleAverageSite(0, 1, 0.8, 0.2)); CustomExternalForce* forceField = new CustomExternalForce("-a*x"); system.addForce(forceField); forceField->addPerParticleParameter("a"); vector<double> params(1); params[0] = 0.1; forceField->addParticle(0, params); params[0] = 0.2; forceField->addParticle(1, params); params[0] = 0.3; forceField->addParticle(2, params); LangevinIntegrator integrator(300.0, 0.1, 0.002); Context context(system, integrator, platform); vector<Vec3> positions(3); positions[0] = Vec3(0, 0, 0); positions[1] = Vec3(1, 0, 0); context.setPositions(positions); context.applyConstraints(0.0001); for (int i = 0; i < 1000; i++) { State state = context.getState(State::Positions | State::Forces); const vector<Vec3>& pos = state.getPositions(); ASSERT_EQUAL_VEC(pos[0]*0.8+pos[1]*0.2, pos[2], 1e-5); ASSERT_EQUAL_VEC(Vec3(0.1+0.3*0.8, 0, 0), state.getForces()[0], 1e-4); ASSERT_EQUAL_VEC(Vec3(0.2+0.3*0.2, 0, 0), state.getForces()[1], 1e-4); integrator.step(1); } }
void testForce() { System system; system.addParticle(1.0); system.addParticle(1.0); system.addParticle(1.0); VerletIntegrator integrator(0.01); CustomExternalForce* forceField = new CustomExternalForce("scale*(x+yscale*(y-y0)^2)"); forceField->addPerParticleParameter("y0"); forceField->addPerParticleParameter("yscale"); forceField->addGlobalParameter("scale", 0.5); vector<double> parameters(2); parameters[0] = 0.5; parameters[1] = 2.0; forceField->addParticle(0, parameters); parameters[0] = 1.5; parameters[1] = 3.0; forceField->addParticle(2, parameters); system.addForce(forceField); Context context(system, integrator, platform); vector<Vec3> positions(3); positions[0] = Vec3(0, 2, 0); positions[1] = Vec3(0, 0, 1); positions[2] = Vec3(1, 0, 1); context.setPositions(positions); State state = context.getState(State::Forces | State::Energy); { const vector<Vec3>& forces = state.getForces(); ASSERT_EQUAL_VEC(Vec3(-0.5, -0.5*2.0*2.0*1.5, 0), forces[0], TOL); ASSERT_EQUAL_VEC(Vec3(0, 0, 0), forces[1], TOL); ASSERT_EQUAL_VEC(Vec3(-0.5, 0.5*3.0*2.0*1.5, 0), forces[2], TOL); ASSERT_EQUAL_TOL(0.5*(1.0 + 2.0*1.5*1.5 + 3.0*1.5*1.5), state.getPotentialEnergy(), TOL); } // Try changing the parameters and make sure it's still correct. parameters[0] = 1.4; parameters[1] = 3.5; forceField->setParticleParameters(1, 2, parameters); forceField->updateParametersInContext(context); state = context.getState(State::Forces | State::Energy); { const vector<Vec3>& forces = state.getForces(); ASSERT_EQUAL_VEC(Vec3(-0.5, -0.5*2.0*2.0*1.5, 0), forces[0], TOL); ASSERT_EQUAL_VEC(Vec3(0, 0, 0), forces[1], TOL); ASSERT_EQUAL_VEC(Vec3(-0.5, 0.5*3.5*2.0*1.4, 0), forces[2], TOL); ASSERT_EQUAL_TOL(0.5*(1.0 + 2.0*1.5*1.5 + 3.5*1.4*1.4), state.getPotentialEnergy(), TOL); } }
void testParallelComputation() { System system; const int numParticles = 200; for (int i = 0; i < numParticles; i++) system.addParticle(1.0); CustomExternalForce* force = new CustomExternalForce("x^2+y^2+z^2"); vector<double> params; for (int i = 0; i < numParticles; i++) force->addParticle(i, params); system.addForce(force); OpenMM_SFMT::SFMT sfmt; init_gen_rand(0, sfmt); vector<Vec3> positions(numParticles); for (int i = 0; i < numParticles; i++) positions[i] = Vec3(5*genrand_real2(sfmt), 5*genrand_real2(sfmt), 5*genrand_real2(sfmt)); VerletIntegrator integrator1(0.01); Context context1(system, integrator1, platform); context1.setPositions(positions); State state1 = context1.getState(State::Forces | State::Energy); VerletIntegrator integrator2(0.01); string deviceIndex = platform.getPropertyValue(context1, CudaPlatform::CudaDeviceIndex()); map<string, string> props; props[CudaPlatform::CudaDeviceIndex()] = deviceIndex+","+deviceIndex; Context context2(system, integrator2, platform, props); context2.setPositions(positions); State state2 = context2.getState(State::Forces | State::Energy); ASSERT_EQUAL_TOL(state1.getPotentialEnergy(), state2.getPotentialEnergy(), 1e-5); for (int i = 0; i < numParticles; i++) ASSERT_EQUAL_VEC(state1.getForces()[i], state2.getForces()[i], 1e-5); }
void testManyParameters() { System system; system.addParticle(1.0); VerletIntegrator integrator(0.01); CustomExternalForce* forceField = new CustomExternalForce("xscale*(x-x0)^2+yscale*(y-y0)^2+zscale*(z-z0)^2"); forceField->addPerParticleParameter("x0"); forceField->addPerParticleParameter("y0"); forceField->addPerParticleParameter("z0"); forceField->addPerParticleParameter("xscale"); forceField->addPerParticleParameter("yscale"); forceField->addPerParticleParameter("zscale"); vector<double> parameters(6); parameters[0] = 1.0; parameters[1] = 2.0; parameters[2] = 3.0; parameters[3] = 0.1; parameters[4] = 0.2; parameters[5] = 0.3; forceField->addParticle(0, parameters); system.addForce(forceField); Context context(system, integrator, platform); vector<Vec3> positions(1); positions[0] = Vec3(0, -1, 0); context.setPositions(positions); State state = context.getState(State::Forces | State::Energy); const vector<Vec3>& forces = state.getForces(); ASSERT_EQUAL_VEC(Vec3(2*0.1*1.0, 2*0.2*3.0, 2*0.3*3.0), forces[0], TOL); ASSERT_EQUAL_TOL(0.1*1*1 + 0.2*3*3 + 0.3*3*3, state.getPotentialEnergy(), TOL); }
/** * Test an OutOfPlaneSite virtual site. */ void testOutOfPlane() { System system; system.addParticle(1.0); system.addParticle(1.0); system.addParticle(1.0); system.addParticle(0.0); system.setVirtualSite(3, new OutOfPlaneSite(0, 1, 2, 0.3, 0.4, 0.5)); CustomExternalForce* forceField = new CustomExternalForce("-a*x"); system.addForce(forceField); forceField->addPerParticleParameter("a"); vector<double> params(1); params[0] = 0.1; forceField->addParticle(0, params); params[0] = 0.2; forceField->addParticle(1, params); params[0] = 0.3; forceField->addParticle(2, params); params[0] = 0.4; forceField->addParticle(3, params); LangevinIntegrator integrator(300.0, 0.1, 0.002); Context context(system, integrator, platform); vector<Vec3> positions(4); positions[0] = Vec3(0, 0, 0); positions[1] = Vec3(1, 0, 0); positions[2] = Vec3(0, 1, 0); context.setPositions(positions); context.applyConstraints(0.0001); for (int i = 0; i < 1000; i++) { State state = context.getState(State::Positions | State::Forces); const vector<Vec3>& pos = state.getPositions(); Vec3 v12 = pos[1]-pos[0]; Vec3 v13 = pos[2]-pos[0]; Vec3 cross = v12.cross(v13); ASSERT_EQUAL_VEC(pos[0]+v12*0.3+v13*0.4+cross*0.5, pos[3], 1e-5); const vector<Vec3>& f = state.getForces(); ASSERT_EQUAL_VEC(Vec3(0.1+0.2+0.3+0.4, 0, 0), f[0]+f[1]+f[2], 1e-5); Vec3 f2(0.4*0.3, 0.4*0.5*v13[2], -0.4*0.5*v13[1]); Vec3 f3(0.4*0.4, -0.4*0.5*v12[2], 0.4*0.5*v12[1]); ASSERT_EQUAL_VEC(Vec3(0.1+0.4, 0, 0)-f2-f3, f[0], 1e-5); ASSERT_EQUAL_VEC(Vec3(0.2, 0, 0)+f2, f[1], 1e-5); ASSERT_EQUAL_VEC(Vec3(0.3, 0, 0)+f3, f[2], 1e-5); integrator.step(1); } }
void testPeriodic() { Vec3 vx(5, 0, 0); Vec3 vy(0, 6, 0); Vec3 vz(1, 2, 7); double x0 = 51, y0 = -17, z0 = 11.2; System system; system.setDefaultPeriodicBoxVectors(vx, vy, vz); system.addParticle(1.0); CustomExternalForce* force = new CustomExternalForce("periodicdistance(x, y, z, x0, y0, z0)^2"); force->addPerParticleParameter("x0"); force->addPerParticleParameter("y0"); force->addPerParticleParameter("z0"); vector<double> params(3); params[0] = x0; params[1] = y0; params[2] = z0; force->addParticle(0, params); system.addForce(force); VerletIntegrator integrator(0.01); Context context(system, integrator, platform); vector<Vec3> positions(1); positions[0] = Vec3(0, 2, 0); context.setPositions(positions); for (int i = 0; i < 100; i++) { State state = context.getState(State::Positions | State::Forces | State::Energy); // Apply periodic boundary conditions to the difference between the two positions. Vec3 delta = Vec3(x0, y0, z0)-state.getPositions()[0]; delta -= vz*floor(delta[2]/vz[2]+0.5); delta -= vy*floor(delta[1]/vy[1]+0.5); delta -= vx*floor(delta[0]/vx[0]+0.5); // Verify that the force and energy are correct. ASSERT_EQUAL_VEC(delta*2, state.getForces()[0], 1e-5); ASSERT_EQUAL_TOL(delta.dot(delta), state.getPotentialEnergy(), 1e-5); integrator.step(1); } }
/** * Test a LocalCoordinatesSite virtual site. */ void testLocalCoordinates() { const Vec3 originWeights(0.2, 0.3, 0.5); const Vec3 xWeights(-1.0, 0.5, 0.5); const Vec3 yWeights(0.0, -1.0, 1.0); const Vec3 localPosition(0.4, 0.3, 0.2); System system; system.addParticle(1.0); system.addParticle(1.0); system.addParticle(1.0); system.addParticle(0.0); system.setVirtualSite(3, new LocalCoordinatesSite(0, 1, 2, originWeights, xWeights, yWeights, localPosition)); CustomExternalForce* forceField = new CustomExternalForce("2*x^2+3*y^2+4*z^2"); system.addForce(forceField); vector<double> params; forceField->addParticle(0, params); forceField->addParticle(1, params); forceField->addParticle(2, params); forceField->addParticle(3, params); LangevinIntegrator integrator(300.0, 0.1, 0.002); Context context(system, integrator, platform); vector<Vec3> positions(4), positions2(4), positions3(4); OpenMM_SFMT::SFMT sfmt; init_gen_rand(0, sfmt); for (int i = 0; i < 100; i++) { // Set the particles at random positions. Vec3 xdir, ydir, zdir; do { for (int j = 0; j < 3; j++) positions[j] = Vec3(genrand_real2(sfmt), genrand_real2(sfmt), genrand_real2(sfmt)); xdir = positions[0]*xWeights[0] + positions[1]*xWeights[1] + positions[2]*xWeights[2]; ydir = positions[0]*yWeights[0] + positions[1]*yWeights[1] + positions[2]*yWeights[2]; zdir = xdir.cross(ydir); if (sqrt(xdir.dot(xdir)) > 0.1 && sqrt(ydir.dot(ydir)) > 0.1 && sqrt(zdir.dot(zdir)) > 0.1) break; // These positions give a reasonable coordinate system. } while (true); context.setPositions(positions); context.applyConstraints(0.0001); // See if the virtual site is positioned correctly. State state = context.getState(State::Positions | State::Forces); const vector<Vec3>& pos = state.getPositions(); Vec3 origin = pos[0]*originWeights[0] + pos[1]*originWeights[1] + pos[2]*originWeights[2]; xdir /= sqrt(xdir.dot(xdir)); zdir /= sqrt(zdir.dot(zdir)); ydir = zdir.cross(xdir); ASSERT_EQUAL_VEC(origin+xdir*localPosition[0]+ydir*localPosition[1]+zdir*localPosition[2], pos[3], 1e-5); // Take a small step in the direction of the energy gradient and see whether the potential energy changes by the expected amount. double norm = 0.0; for (int i = 0; i < 3; ++i) { Vec3 f = state.getForces()[i]; norm += f[0]*f[0] + f[1]*f[1] + f[2]*f[2]; } norm = std::sqrt(norm); const double delta = 1e-2; double step = 0.5*delta/norm; for (int i = 0; i < 3; ++i) { Vec3 p = positions[i]; Vec3 f = state.getForces()[i]; positions2[i] = Vec3(p[0]-f[0]*step, p[1]-f[1]*step, p[2]-f[2]*step); positions3[i] = Vec3(p[0]+f[0]*step, p[1]+f[1]*step, p[2]+f[2]*step); } context.setPositions(positions2); context.applyConstraints(0.0001); State state2 = context.getState(State::Energy); context.setPositions(positions3); context.applyConstraints(0.0001); State state3 = context.getState(State::Energy); ASSERT_EQUAL_TOL(norm, (state2.getPotentialEnergy()-state3.getPotentialEnergy())/delta, 1e-3) } }
/** * 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); 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, (a==0||a==3), (a==1||a==3), (a==2||a==3), frequency); 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)); }