void testSerialization() { // Create a Force. NonbondedForce force; force.setNonbondedMethod(NonbondedForce::CutoffPeriodic); force.setCutoffDistance(2.0); force.setEwaldErrorTolerance(1e-3); force.setReactionFieldDielectric(50.0); force.setUseDispersionCorrection(false); force.addParticle(1, 0.1, 0.01); force.addParticle(0.5, 0.2, 0.02); force.addParticle(-0.5, 0.3, 0.03); force.addException(0, 1, 2, 0.5, 0.1); force.addException(1, 2, 0.2, 0.4, 0.2); // Serialize and then deserialize it. stringstream buffer; XmlSerializer::serialize<NonbondedForce>(&force, "Force", buffer); NonbondedForce* copy = XmlSerializer::deserialize<NonbondedForce>(buffer); // Compare the two forces to see if they are identical. NonbondedForce& force2 = *copy; ASSERT_EQUAL(force.getNonbondedMethod(), force2.getNonbondedMethod()); ASSERT_EQUAL(force.getCutoffDistance(), force2.getCutoffDistance()); ASSERT_EQUAL(force.getEwaldErrorTolerance(), force2.getEwaldErrorTolerance()); ASSERT_EQUAL(force.getReactionFieldDielectric(), force2.getReactionFieldDielectric()); ASSERT_EQUAL(force.getUseDispersionCorrection(), force2.getUseDispersionCorrection()); ASSERT_EQUAL(force.getNumParticles(), force2.getNumParticles()); for (int i = 0; i < force.getNumParticles(); i++) { double charge1, sigma1, epsilon1; double charge2, sigma2, epsilon2; force.getParticleParameters(i, charge1, sigma1, epsilon1); force2.getParticleParameters(i, charge2, sigma2, epsilon2); ASSERT_EQUAL(charge1, charge2); ASSERT_EQUAL(sigma1, sigma2); ASSERT_EQUAL(epsilon1, epsilon2); } ASSERT_EQUAL(force.getNumExceptions(), force2.getNumExceptions()); for (int i = 0; i < force.getNumExceptions(); i++) { int a1, a2, b1, b2; double charge1, sigma1, epsilon1; double charge2, sigma2, epsilon2; force.getExceptionParameters(i, a1, b1, charge1, sigma1, epsilon1); force2.getExceptionParameters(i, a2, b2, charge2, sigma2, epsilon2); ASSERT_EQUAL(a1, a2); ASSERT_EQUAL(b1, b2); ASSERT_EQUAL(charge1, charge2); ASSERT_EQUAL(sigma1, sigma2); ASSERT_EQUAL(epsilon1, epsilon2); } }
void testPeriodic() { System system; system.addParticle(1.0); system.addParticle(1.0); system.addParticle(1.0); VerletIntegrator integrator(0.01); NonbondedForce* nonbonded = new NonbondedForce(); nonbonded->addParticle(1.0, 1, 0); nonbonded->addParticle(1.0, 1, 0); nonbonded->addParticle(1.0, 1, 0); nonbonded->addException(0, 1, 0.0, 1.0, 0.0); nonbonded->setNonbondedMethod(NonbondedForce::CutoffPeriodic); const double cutoff = 2.0; nonbonded->setCutoffDistance(cutoff); system.setDefaultPeriodicBoxVectors(Vec3(4, 0, 0), Vec3(0, 4, 0), Vec3(0, 0, 4)); system.addForce(nonbonded); Context context(system, integrator, platform); vector<Vec3> positions(3); positions[0] = Vec3(0, 0, 0); positions[1] = Vec3(2, 0, 0); positions[2] = Vec3(3, 0, 0); context.setPositions(positions); State state = context.getState(State::Forces | State::Energy); const vector<Vec3>& forces = state.getForces(); const double eps = 78.3; const double krf = (1.0/(cutoff*cutoff*cutoff))*(eps-1.0)/(2.0*eps+1.0); const double crf = (1.0/cutoff)*(3.0*eps)/(2.0*eps+1.0); const double force = ONE_4PI_EPS0*(1.0)*(1.0-2.0*krf*1.0); ASSERT_EQUAL_VEC(Vec3(force, 0, 0), forces[0], TOL); ASSERT_EQUAL_VEC(Vec3(-force, 0, 0), forces[1], TOL); ASSERT_EQUAL_VEC(Vec3(0, 0, 0), forces[2], TOL); ASSERT_EQUAL_TOL(2*ONE_4PI_EPS0*(1.0)*(1.0+krf*1.0-crf), state.getPotentialEnergy(), TOL); }
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); } }
void testParallelComputation(NonbondedForce::NonbondedMethod method) { System system; const int numParticles = 200; for (int i = 0; i < numParticles; i++) system.addParticle(1.0); NonbondedForce* force = new NonbondedForce(); for (int i = 0; i < numParticles; i++) force->addParticle(i%2-0.5, 0.5, 1.0); force->setNonbondedMethod(method); system.addForce(force); system.setDefaultPeriodicBoxVectors(Vec3(5,0,0), Vec3(0,5,0), Vec3(0,0,5)); 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)); for (int i = 0; i < numParticles; ++i) for (int j = 0; j < i; ++j) { Vec3 delta = positions[i]-positions[j]; if (delta.dot(delta) < 0.1) force->addException(i, j, 0, 1, 0); } // Create two contexts, one with a single device and one with two devices. 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); // See if they agree. 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); // Modify some particle parameters and see if they still agree. for (int i = 0; i < numParticles; i += 5) { double charge, sigma, epsilon; force->getParticleParameters(i, charge, sigma, epsilon); force->setParticleParameters(i, 0.9*charge, sigma, epsilon); } force->updateParametersInContext(context1); force->updateParametersInContext(context2); state1 = context1.getState(State::Forces | State::Energy); 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); }
/** * Make sure that atom reordering respects virtual sites. */ void testReordering() { const double cutoff = 2.0; const double boxSize = 20.0; System system; NonbondedForce* nonbonded = new NonbondedForce(); system.addForce(nonbonded); nonbonded->setNonbondedMethod(NonbondedForce::CutoffNonPeriodic); nonbonded->setCutoffDistance(cutoff); vector<Vec3> positions; OpenMM_SFMT::SFMT sfmt; init_gen_rand(0, sfmt); // Create linear molecules with TwoParticleAverage virtual sites. for (int i = 0; i < 50; i++) { int start = system.getNumParticles(); system.addParticle(1.0); system.addParticle(1.0); system.addParticle(0.0); system.setVirtualSite(start+2, new TwoParticleAverageSite(start, start+1, 0.4, 0.6)); system.addConstraint(start, start+1, 2.0); for (int i = 0; i < 3; i++) { nonbonded->addParticle(0, 0.2, 1); for (int j = 0; j < i; j++) nonbonded->addException(start+i, start+j, 0, 1, 0); } Vec3 pos(boxSize*genrand_real2(sfmt), boxSize*genrand_real2(sfmt), boxSize*genrand_real2(sfmt)); positions.push_back(pos); positions.push_back(pos+Vec3(2, 0, 0)); positions.push_back(Vec3()); } // Create planar molecules with ThreeParticleAverage virtual sites. for (int i = 0; i < 50; i++) { int start = system.getNumParticles(); system.addParticle(1.0); system.addParticle(1.0); system.addParticle(1.0); system.addParticle(0.0); system.setVirtualSite(start+3, new ThreeParticleAverageSite(start, start+1, start+2, 0.3, 0.5, 0.2)); system.addConstraint(start, start+1, 1.0); system.addConstraint(start, start+2, 1.0); system.addConstraint(start+1, start+2, sqrt(2.0)); for (int i = 0; i < 4; i++) { nonbonded->addParticle(0, 0.2, 1); for (int j = 0; j < i; j++) nonbonded->addException(start+i, start+j, 0, 1, 0); } Vec3 pos(boxSize*genrand_real2(sfmt), boxSize*genrand_real2(sfmt), boxSize*genrand_real2(sfmt)); positions.push_back(pos); positions.push_back(pos+Vec3(1, 0, 0)); positions.push_back(pos+Vec3(0, 1, 0)); positions.push_back(Vec3()); } // Create tetrahedral molecules with OutOfPlane virtual sites. for (int i = 0; i < 50; i++) { int start = system.getNumParticles(); system.addParticle(1.0); system.addParticle(1.0); system.addParticle(1.0); system.addParticle(0.0); system.setVirtualSite(start+3, new OutOfPlaneSite(start, start+1, start+2, 0.3, 0.5, 0.2)); system.addConstraint(start, start+1, 1.0); system.addConstraint(start, start+2, 1.0); system.addConstraint(start+1, start+2, sqrt(2.0)); for (int i = 0; i < 4; i++) { nonbonded->addParticle(0, 0.2, 1); for (int j = 0; j < i; j++) nonbonded->addException(start+i, start+j, 0, 1, 0); } Vec3 pos(boxSize*genrand_real2(sfmt), boxSize*genrand_real2(sfmt), boxSize*genrand_real2(sfmt)); positions.push_back(pos); positions.push_back(pos+Vec3(1, 0, 0)); positions.push_back(pos+Vec3(0, 1, 0)); positions.push_back(Vec3()); } // Simulate it and check conservation laws. LangevinIntegrator integrator(300.0, 0.1, 0.002); Context context(system, integrator, platform); context.setPositions(positions); context.applyConstraints(0.0001); for (int i = 0; i < 1000; i++) { State state = context.getState(State::Positions); const vector<Vec3>& pos = state.getPositions(); for (int j = 0; j < 150; j += 3) ASSERT_EQUAL_VEC(pos[j]*0.4+pos[j+1]*0.6, pos[j+2], 1e-5); for (int j = 150; j < 350; j += 4) ASSERT_EQUAL_VEC(pos[j]*0.3+pos[j+1]*0.5+pos[j+2]*0.2, pos[j+3], 1e-5); for (int j = 350; j < 550; j += 4) { Vec3 v12 = pos[j+1]-pos[j]; Vec3 v13 = pos[j+2]-pos[j]; Vec3 cross = v12.cross(v13); ASSERT_EQUAL_VEC(pos[j]+v12*0.3+v13*0.5+cross*0.2, pos[j+3], 1e-5); } integrator.step(1); } }
/** * Make sure that energy, linear momentum, and angular momentum are all conserved * when using virtual sites. */ void testConservationLaws() { System system; NonbondedForce* forceField = new NonbondedForce(); system.addForce(forceField); vector<Vec3> positions; // Create a linear molecule with a TwoParticleAverage virtual site. system.addParticle(1.0); system.addParticle(1.0); system.addParticle(0.0); system.setVirtualSite(2, new TwoParticleAverageSite(0, 1, 0.4, 0.6)); system.addConstraint(0, 1, 2.0); for (int i = 0; i < 3; i++) { forceField->addParticle(0, 1, 10); for (int j = 0; j < i; j++) forceField->addException(i, j, 0, 1, 0); } positions.push_back(Vec3(0, 0, 0)); positions.push_back(Vec3(2, 0, 0)); positions.push_back(Vec3()); // Create a planar molecule with a ThreeParticleAverage virtual site. system.addParticle(1.0); system.addParticle(1.0); system.addParticle(1.0); system.addParticle(0.0); system.setVirtualSite(6, new ThreeParticleAverageSite(3, 4, 5, 0.3, 0.5, 0.2)); system.addConstraint(3, 4, 1.0); system.addConstraint(3, 5, 1.0); system.addConstraint(4, 5, sqrt(2.0)); for (int i = 0; i < 4; i++) { forceField->addParticle(0, 1, 10); for (int j = 0; j < i; j++) forceField->addException(i+3, j+3, 0, 1, 0); } positions.push_back(Vec3(0, 0, 1)); positions.push_back(Vec3(1, 0, 1)); positions.push_back(Vec3(0, 1, 1)); positions.push_back(Vec3()); // Create a tetrahedral molecule with an OutOfPlane virtual site. system.addParticle(1.0); system.addParticle(1.0); system.addParticle(1.0); system.addParticle(0.0); system.setVirtualSite(10, new OutOfPlaneSite(7, 8, 9, 0.3, 0.5, 0.2)); system.addConstraint(7, 8, 1.0); system.addConstraint(7, 9, 1.0); system.addConstraint(8, 9, sqrt(2.0)); for (int i = 0; i < 4; i++) { forceField->addParticle(0, 1, 10); for (int j = 0; j < i; j++) forceField->addException(i+7, j+7, 0, 1, 0); } positions.push_back(Vec3(1, 0, -1)); positions.push_back(Vec3(2, 0, -1)); positions.push_back(Vec3(1, 1, -1)); positions.push_back(Vec3()); // Create a molecule with a LocalCoordinatesSite virtual site. system.addParticle(1.0); system.addParticle(1.0); system.addParticle(1.0); system.addParticle(0.0); system.setVirtualSite(14, new LocalCoordinatesSite(11, 12, 13, Vec3(0.3, 0.3, 0.4), Vec3(1.0, -0.5, -0.5), Vec3(0, -1.0, 1.0), Vec3(0.2, 0.2, 1.0))); system.addConstraint(11, 12, 1.0); system.addConstraint(11, 13, 1.0); system.addConstraint(12, 13, sqrt(2.0)); for (int i = 0; i < 4; i++) { forceField->addParticle(0, 1, 10); for (int j = 0; j < i; j++) forceField->addException(i+11, j+11, 0, 1, 0); } positions.push_back(Vec3(1, 2, 0)); positions.push_back(Vec3(2, 2, 0)); positions.push_back(Vec3(1, 3, 0)); positions.push_back(Vec3()); // Simulate it and check conservation laws. VerletIntegrator integrator(0.002); Context context(system, integrator, platform); context.setPositions(positions); context.applyConstraints(0.0001); int numParticles = system.getNumParticles(); double initialEnergy; Vec3 initialMomentum, initialAngularMomentum; for (int i = 0; i < 1000; i++) { State state = context.getState(State::Positions | State::Velocities | State::Forces | State::Energy); const vector<Vec3>& pos = state.getPositions(); const vector<Vec3>& vel = state.getVelocities(); const vector<Vec3>& f = state.getForces(); double energy = state.getPotentialEnergy(); for (int j = 0; j < numParticles; j++) { Vec3 v = vel[j] + f[j]*0.5*integrator.getStepSize(); energy += 0.5*system.getParticleMass(j)*v.dot(v); } if (i == 0) initialEnergy = energy; else ASSERT_EQUAL_TOL(initialEnergy, energy, 0.01); Vec3 momentum; for (int j = 0; j < numParticles; j++) momentum += vel[j]*system.getParticleMass(j); if (i == 0) initialMomentum = momentum; else ASSERT_EQUAL_VEC(initialMomentum, momentum, 0.02); Vec3 angularMomentum; for (int j = 0; j < numParticles; j++) angularMomentum += pos[j].cross(vel[j])*system.getParticleMass(j); if (i == 0) initialAngularMomentum = angularMomentum; else ASSERT_EQUAL_VEC(initialAngularMomentum, angularMomentum, 0.05); integrator.step(1); } }
void testWithBarostat() { const int gridSize = 3; const int numMolecules = gridSize*gridSize*gridSize; const int numParticles = numMolecules*2; const int numCopies = 5; const double spacing = 2.0; const double cutoff = 3.0; const double boxSize = spacing*(gridSize+1); const double temperature = 300.0; System system; system.setDefaultPeriodicBoxVectors(Vec3(boxSize, 0, 0), Vec3(0, boxSize, 0), Vec3(0, 0, boxSize)); HarmonicBondForce* bonds = new HarmonicBondForce(); system.addForce(bonds); NonbondedForce* nonbonded = new NonbondedForce(); nonbonded->setCutoffDistance(cutoff); nonbonded->setNonbondedMethod(NonbondedForce::PME); nonbonded->setForceGroup(1); nonbonded->setReciprocalSpaceForceGroup(2); system.addForce(nonbonded); system.addForce(new MonteCarloBarostat(0.5, temperature)); // Create a cloud of molecules. OpenMM_SFMT::SFMT sfmt; init_gen_rand(0, sfmt); vector<Vec3> positions(numParticles); for (int i = 0; i < numMolecules; i++) { system.addParticle(1.0); system.addParticle(1.0); nonbonded->addParticle(-0.2, 0.2, 0.2); nonbonded->addParticle(0.2, 0.2, 0.2); nonbonded->addException(2*i, 2*i+1, 0, 1, 0); bonds->addBond(2*i, 2*i+1, 1.0, 10000.0); } RPMDIntegrator integ(numCopies, temperature, 50.0, 0.001); Platform& platform = Platform::getPlatformByName("Reference"); Context context(system, integ, platform); for (int copy = 0; copy < numCopies; copy++) { for (int i = 0; i < gridSize; i++) for (int j = 0; j < gridSize; j++) for (int k = 0; k < gridSize; k++) { Vec3 pos = Vec3(spacing*(i+0.02*genrand_real2(sfmt)), spacing*(j+0.02*genrand_real2(sfmt)), spacing*(k+0.02*genrand_real2(sfmt))); int index = k+gridSize*(j+gridSize*i); positions[2*index] = pos; positions[2*index+1] = Vec3(pos[0]+1.0, pos[1], pos[2]); } integ.setPositions(copy, positions); } // Check the temperature. const int numSteps = 500; integ.step(100); vector<double> ke(numCopies, 0.0); for (int i = 0; i < numSteps; i++) { integ.step(1); vector<State> state(numCopies); for (int j = 0; j < numCopies; j++) state[j] = integ.getState(j, State::Velocities, true); for (int j = 0; j < numParticles; j++) { for (int k = 0; k < numCopies; k++) { Vec3 v = state[k].getVelocities()[j]; ke[k] += 0.5*system.getParticleMass(j)*v.dot(v); } } } double meanKE = 0.0; for (int i = 0; i < numCopies; i++) meanKE += ke[i]; meanKE /= numSteps*numCopies; double expectedKE = 0.5*numCopies*numParticles*3*BOLTZ*temperature; ASSERT_USUALLY_EQUAL_TOL(expectedKE, meanKE, 1e-2); }
void testCoulombLennardJones() { const int numMolecules = 300; const int numParticles = numMolecules*2; const double boxSize = 20.0; // Create two systems: one with a NonbondedForce, and one using a CustomNonbondedForce to implement the same interaction. System standardSystem; System customSystem; for (int i = 0; i < numParticles; i++) { standardSystem.addParticle(1.0); customSystem.addParticle(1.0); } NonbondedForce* standardNonbonded = new NonbondedForce(); CustomNonbondedForce* customNonbonded = new CustomNonbondedForce("4*eps*((sigma/r)^12-(sigma/r)^6)+138.935456*q/r; q=q1*q2; sigma=0.5*(sigma1+sigma2); eps=sqrt(eps1*eps2)"); customNonbonded->addPerParticleParameter("q"); customNonbonded->addPerParticleParameter("sigma"); customNonbonded->addPerParticleParameter("eps"); vector<Vec3> positions(numParticles); vector<Vec3> velocities(numParticles); OpenMM_SFMT::SFMT sfmt; init_gen_rand(0, sfmt); vector<double> params(3); for (int i = 0; i < numMolecules; i++) { if (i < numMolecules/2) { standardNonbonded->addParticle(1.0, 0.2, 0.1); params[0] = 1.0; params[1] = 0.2; params[2] = 0.1; customNonbonded->addParticle(params); standardNonbonded->addParticle(-1.0, 0.1, 0.1); params[0] = -1.0; params[1] = 0.1; customNonbonded->addParticle(params); } else { standardNonbonded->addParticle(1.0, 0.2, 0.2); params[0] = 1.0; params[1] = 0.2; params[2] = 0.2; customNonbonded->addParticle(params); standardNonbonded->addParticle(-1.0, 0.1, 0.2); params[0] = -1.0; params[1] = 0.1; customNonbonded->addParticle(params); } positions[2*i] = Vec3(boxSize*genrand_real2(sfmt), boxSize*genrand_real2(sfmt), boxSize*genrand_real2(sfmt)); positions[2*i+1] = Vec3(positions[2*i][0]+1.0, positions[2*i][1], positions[2*i][2]); velocities[2*i] = Vec3(genrand_real2(sfmt), genrand_real2(sfmt), genrand_real2(sfmt)); velocities[2*i+1] = Vec3(genrand_real2(sfmt), genrand_real2(sfmt), genrand_real2(sfmt)); standardNonbonded->addException(2*i, 2*i+1, 0.0, 1.0, 0.0); customNonbonded->addExclusion(2*i, 2*i+1); } standardNonbonded->setNonbondedMethod(NonbondedForce::NoCutoff); customNonbonded->setNonbondedMethod(CustomNonbondedForce::NoCutoff); standardSystem.addForce(standardNonbonded); customSystem.addForce(customNonbonded); VerletIntegrator integrator1(0.01); VerletIntegrator integrator2(0.01); Context context1(standardSystem, integrator1, platform); Context context2(customSystem, integrator2, platform); context1.setPositions(positions); context2.setPositions(positions); context1.setVelocities(velocities); context2.setVelocities(velocities); State state1 = context1.getState(State::Forces | State::Energy); State state2 = context2.getState(State::Forces | State::Energy); ASSERT_EQUAL_TOL(state1.getPotentialEnergy(), state2.getPotentialEnergy(), 1e-4); for (int i = 0; i < numParticles; i++) { ASSERT_EQUAL_VEC(state1.getForces()[i], state2.getForces()[i], 1e-4); } }
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); } }
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 testEwaldPME(bool includeExceptions) { // Use amorphous NaCl system for the tests const int numParticles = 894; const double cutoff = 1.2; const double boxSize = 3.00646; double tol = 1e-5; ReferencePlatform reference; System system; NonbondedForce* nonbonded = new NonbondedForce(); nonbonded->setNonbondedMethod(NonbondedForce::Ewald); nonbonded->setCutoffDistance(cutoff); nonbonded->setEwaldErrorTolerance(tol); for (int i = 0; i < numParticles/2; i++) system.addParticle(22.99); for (int i = 0; i < numParticles/2; i++) system.addParticle(35.45); for (int i = 0; i < numParticles/2; i++) nonbonded->addParticle(1.0, 1.0,0.0); for (int i = 0; i < numParticles/2; i++) nonbonded->addParticle(-1.0, 1.0,0.0); system.setDefaultPeriodicBoxVectors(Vec3(boxSize, 0, 0), Vec3(0, boxSize, 0), Vec3(0, 0, boxSize)); system.addForce(nonbonded); vector<Vec3> positions(numParticles); #include "nacl_amorph.dat" if (includeExceptions) { // Add some exclusions. for (int i = 0; i < numParticles-1; i++) { Vec3 delta = positions[i]-positions[i+1]; if (sqrt(delta.dot(delta)) < 0.5*cutoff) nonbonded->addException(i, i+1, i%2 == 0 ? 0.0 : 0.5, 1.0, 0.0); } } // (1) Check whether the Reference and CPU platforms agree when using Ewald Method VerletIntegrator integrator1(0.01); VerletIntegrator integrator2(0.01); Context cpuContext(system, integrator1, platform); Context referenceContext(system, integrator2, reference); cpuContext.setPositions(positions); referenceContext.setPositions(positions); State cpuState = cpuContext.getState(State::Forces | State::Energy); State referenceState = referenceContext.getState(State::Forces | State::Energy); tol = 1e-2; for (int i = 0; i < numParticles; i++) { ASSERT_EQUAL_VEC(referenceState.getForces()[i], cpuState.getForces()[i], tol); } tol = 1e-5; ASSERT_EQUAL_TOL(referenceState.getPotentialEnergy(), cpuState.getPotentialEnergy(), tol); // (2) Check whether Ewald method in CPU is self-consistent double norm = 0.0; for (int i = 0; i < numParticles; ++i) { Vec3 f = cpuState.getForces()[i]; norm += f[0]*f[0] + f[1]*f[1] + f[2]*f[2]; } norm = std::sqrt(norm); const double delta = 5e-3; double step = delta/norm; for (int i = 0; i < numParticles; ++i) { Vec3 p = positions[i]; Vec3 f = cpuState.getForces()[i]; positions[i] = Vec3(p[0]-f[0]*step, p[1]-f[1]*step, p[2]-f[2]*step); } VerletIntegrator integrator3(0.01); Context cpuContext2(system, integrator3, platform); cpuContext2.setPositions(positions); tol = 1e-2; State cpuState2 = cpuContext2.getState(State::Energy); ASSERT_EQUAL_TOL(norm, (cpuState2.getPotentialEnergy()-cpuState.getPotentialEnergy())/delta, tol) // (3) Check whether the Reference and CPU platforms agree when using PME nonbonded->setNonbondedMethod(NonbondedForce::PME); cpuContext.reinitialize(); referenceContext.reinitialize(); cpuContext.setPositions(positions); referenceContext.setPositions(positions); cpuState = cpuContext.getState(State::Forces | State::Energy); referenceState = referenceContext.getState(State::Forces | State::Energy); tol = 1e-2; for (int i = 0; i < numParticles; i++) { ASSERT_EQUAL_VEC(referenceState.getForces()[i], cpuState.getForces()[i], tol); } tol = 1e-5; ASSERT_EQUAL_TOL(referenceState.getPotentialEnergy(), cpuState.getPotentialEnergy(), tol); // (4) Check whether PME method in CPU is self-consistent norm = 0.0; for (int i = 0; i < numParticles; ++i) { Vec3 f = cpuState.getForces()[i]; norm += f[0]*f[0] + f[1]*f[1] + f[2]*f[2]; } norm = std::sqrt(norm); step = delta/norm; for (int i = 0; i < numParticles; ++i) { Vec3 p = positions[i]; Vec3 f = cpuState.getForces()[i]; positions[i] = Vec3(p[0]-f[0]*step, p[1]-f[1]*step, p[2]-f[2]*step); } VerletIntegrator integrator4(0.01); Context cpuContext3(system, integrator4, platform); cpuContext3.setPositions(positions); tol = 1e-2; State cpuState3 = cpuContext3.getState(State::Energy); ASSERT_EQUAL_TOL(norm, (cpuState3.getPotentialEnergy()-cpuState.getPotentialEnergy())/delta, tol) }
void testSerialization() { // Create a Force. NonbondedForce force; force.setForceGroup(3); force.setNonbondedMethod(NonbondedForce::CutoffPeriodic); force.setSwitchingDistance(1.5); force.setUseSwitchingFunction(true); force.setCutoffDistance(2.0); force.setEwaldErrorTolerance(1e-3); force.setReactionFieldDielectric(50.0); force.setUseDispersionCorrection(false); double alpha = 0.5; int nx = 3, ny = 5, nz = 7; force.setPMEParameters(alpha, nx, ny, nz); double dalpha = 0.8; int dnx = 4, dny = 6, dnz = 7; force.setLJPMEParameters(dalpha, dnx, dny, dnz); force.addParticle(1, 0.1, 0.01); force.addParticle(0.5, 0.2, 0.02); force.addParticle(-0.5, 0.3, 0.03); force.addException(0, 1, 2, 0.5, 0.1); force.addException(1, 2, 0.2, 0.4, 0.2); force.addGlobalParameter("scale1", 1.0); force.addGlobalParameter("scale2", 2.0); force.addParticleParameterOffset("scale1", 2, 1.5, 2.0, 2.5); force.addExceptionParameterOffset("scale2", 1, -0.1, -0.2, -0.3); // Serialize and then deserialize it. stringstream buffer; XmlSerializer::serialize<NonbondedForce>(&force, "Force", buffer); NonbondedForce* copy = XmlSerializer::deserialize<NonbondedForce>(buffer); // Compare the two forces to see if they are identical. NonbondedForce& force2 = *copy; ASSERT_EQUAL(force.getForceGroup(), force2.getForceGroup()); ASSERT_EQUAL(force.getNonbondedMethod(), force2.getNonbondedMethod()); ASSERT_EQUAL(force.getSwitchingDistance(), force2.getSwitchingDistance()); ASSERT_EQUAL(force.getUseSwitchingFunction(), force2.getUseSwitchingFunction()); ASSERT_EQUAL(force.getCutoffDistance(), force2.getCutoffDistance()); ASSERT_EQUAL(force.getEwaldErrorTolerance(), force2.getEwaldErrorTolerance()); ASSERT_EQUAL(force.getReactionFieldDielectric(), force2.getReactionFieldDielectric()); ASSERT_EQUAL(force.getUseDispersionCorrection(), force2.getUseDispersionCorrection()); ASSERT_EQUAL(force.getNumParticles(), force2.getNumParticles()); ASSERT_EQUAL(force.getNumExceptions(), force2.getNumExceptions()); ASSERT_EQUAL(force.getNumGlobalParameters(), force2.getNumGlobalParameters()); ASSERT_EQUAL(force.getNumParticleParameterOffsets(), force2.getNumParticleParameterOffsets()); ASSERT_EQUAL(force.getNumExceptionParameterOffsets(), force2.getNumExceptionParameterOffsets()); double alpha2; int nx2, ny2, nz2; force2.getPMEParameters(alpha2, nx2, ny2, nz2); ASSERT_EQUAL(alpha, alpha2); ASSERT_EQUAL(nx, nx2); ASSERT_EQUAL(ny, ny2); ASSERT_EQUAL(nz, nz2); double dalpha2; int dnx2, dny2, dnz2; force2.getLJPMEParameters(dalpha2, dnx2, dny2, dnz2); ASSERT_EQUAL(dalpha, dalpha2); ASSERT_EQUAL(dnx, dnx2); ASSERT_EQUAL(dny, dny2); ASSERT_EQUAL(dnz, dnz2); for (int i = 0; i < force.getNumGlobalParameters(); i++) { ASSERT_EQUAL(force.getGlobalParameterName(i), force2.getGlobalParameterName(i)); ASSERT_EQUAL(force.getGlobalParameterDefaultValue(i), force2.getGlobalParameterDefaultValue(i)); } for (int i = 0; i < force.getNumParticleParameterOffsets(); i++) { int index1, index2; string param1, param2; double charge1, sigma1, epsilon1; double charge2, sigma2, epsilon2; force.getParticleParameterOffset(i, param1, index1, charge1, sigma1, epsilon1); force2.getParticleParameterOffset(i, param2, index2, charge2, sigma2, epsilon2); ASSERT_EQUAL(index1, index1); ASSERT_EQUAL(param1, param2); ASSERT_EQUAL(charge1, charge2); ASSERT_EQUAL(sigma1, sigma2); ASSERT_EQUAL(epsilon1, epsilon2); } for (int i = 0; i < force.getNumExceptionParameterOffsets(); i++) { int index1, index2; string param1, param2; double charge1, sigma1, epsilon1; double charge2, sigma2, epsilon2; force.getExceptionParameterOffset(i, param1, index1, charge1, sigma1, epsilon1); force2.getExceptionParameterOffset(i, param2, index2, charge2, sigma2, epsilon2); ASSERT_EQUAL(index1, index1); ASSERT_EQUAL(param1, param2); ASSERT_EQUAL(charge1, charge2); ASSERT_EQUAL(sigma1, sigma2); ASSERT_EQUAL(epsilon1, epsilon2); } for (int i = 0; i < force.getNumParticles(); i++) { double charge1, sigma1, epsilon1; double charge2, sigma2, epsilon2; force.getParticleParameters(i, charge1, sigma1, epsilon1); force2.getParticleParameters(i, charge2, sigma2, epsilon2); ASSERT_EQUAL(charge1, charge2); ASSERT_EQUAL(sigma1, sigma2); ASSERT_EQUAL(epsilon1, epsilon2); } ASSERT_EQUAL(force.getNumExceptions(), force2.getNumExceptions()); for (int i = 0; i < force.getNumExceptions(); i++) { int a1, a2, b1, b2; double charge1, sigma1, epsilon1; double charge2, sigma2, epsilon2; force.getExceptionParameters(i, a1, b1, charge1, sigma1, epsilon1); force2.getExceptionParameters(i, a2, b2, charge2, sigma2, epsilon2); ASSERT_EQUAL(a1, a2); ASSERT_EQUAL(b1, b2); ASSERT_EQUAL(charge1, charge2); ASSERT_EQUAL(sigma1, sigma2); ASSERT_EQUAL(epsilon1, epsilon2); } }
void testChangingParameters() { const int numMolecules = 600; const int numParticles = numMolecules*2; const double cutoff = 2.0; const double boxSize = 20.0; const double tol = 2e-3; ReferencePlatform reference; System system; for (int i = 0; i < numParticles; i++) system.addParticle(1.0); NonbondedForce* nonbonded = new NonbondedForce(); vector<Vec3> positions(numParticles); OpenMM_SFMT::SFMT sfmt; init_gen_rand(0, sfmt); for (int i = 0; i < numMolecules; i++) { if (i < numMolecules/2) { nonbonded->addParticle(-1.0, 0.2, 0.1); nonbonded->addParticle(1.0, 0.1, 0.1); } else { nonbonded->addParticle(-1.0, 0.2, 0.2); nonbonded->addParticle(1.0, 0.1, 0.2); } positions[2*i] = Vec3(boxSize*genrand_real2(sfmt), boxSize*genrand_real2(sfmt), boxSize*genrand_real2(sfmt)); positions[2*i+1] = Vec3(positions[2*i][0]+1.0, positions[2*i][1], positions[2*i][2]); system.addConstraint(2*i, 2*i+1, 1.0); nonbonded->addException(2*i, 2*i+1, 0.0, 0.15, 0.0); } nonbonded->setNonbondedMethod(NonbondedForce::PME); nonbonded->setCutoffDistance(cutoff); system.addForce(nonbonded); system.setDefaultPeriodicBoxVectors(Vec3(boxSize, 0, 0), Vec3(0, boxSize, 0), Vec3(0, 0, boxSize)); // See if Reference and Cuda give the same forces and energies. VerletIntegrator integrator1(0.01); VerletIntegrator integrator2(0.01); Context cuContext(system, integrator1, platform); Context referenceContext(system, integrator2, reference); cuContext.setPositions(positions); referenceContext.setPositions(positions); State cuState = cuContext.getState(State::Forces | State::Energy); State referenceState = referenceContext.getState(State::Forces | State::Energy); for (int i = 0; i < numParticles; i++) ASSERT_EQUAL_VEC(cuState.getForces()[i], referenceState.getForces()[i], tol); ASSERT_EQUAL_TOL(cuState.getPotentialEnergy(), referenceState.getPotentialEnergy(), tol); // Now modify parameters and see if they still agree. for (int i = 0; i < numParticles; i += 5) { double charge, sigma, epsilon; nonbonded->getParticleParameters(i, charge, sigma, epsilon); nonbonded->setParticleParameters(i, 1.5*charge, 1.1*sigma, 1.7*epsilon); } nonbonded->updateParametersInContext(cuContext); nonbonded->updateParametersInContext(referenceContext); cuState = cuContext.getState(State::Forces | State::Energy); referenceState = referenceContext.getState(State::Forces | State::Energy); for (int i = 0; i < numParticles; i++) ASSERT_EQUAL_VEC(cuState.getForces()[i], referenceState.getForces()[i], tol); ASSERT_EQUAL_TOL(cuState.getPotentialEnergy(), referenceState.getPotentialEnergy(), tol); }
void testLargeSystem() { const int numMolecules = 600; const int numParticles = numMolecules*2; const double cutoff = 2.0; const double boxSize = 20.0; const double tol = 2e-3; ReferencePlatform reference; System system; for (int i = 0; i < numParticles; i++) system.addParticle(1.0); NonbondedForce* nonbonded = new NonbondedForce(); HarmonicBondForce* bonds = new HarmonicBondForce(); vector<Vec3> positions(numParticles); vector<Vec3> velocities(numParticles); OpenMM_SFMT::SFMT sfmt; init_gen_rand(0, sfmt); for (int i = 0; i < numMolecules; i++) { if (i < numMolecules/2) { nonbonded->addParticle(-1.0, 0.2, 0.1); nonbonded->addParticle(1.0, 0.1, 0.1); } else { nonbonded->addParticle(-1.0, 0.2, 0.2); nonbonded->addParticle(1.0, 0.1, 0.2); } positions[2*i] = Vec3(boxSize*genrand_real2(sfmt), boxSize*genrand_real2(sfmt), boxSize*genrand_real2(sfmt)); positions[2*i+1] = Vec3(positions[2*i][0]+1.0, positions[2*i][1], positions[2*i][2]); velocities[2*i] = Vec3(genrand_real2(sfmt), genrand_real2(sfmt), genrand_real2(sfmt)); velocities[2*i+1] = Vec3(genrand_real2(sfmt), genrand_real2(sfmt), genrand_real2(sfmt)); bonds->addBond(2*i, 2*i+1, 1.0, 0.1); nonbonded->addException(2*i, 2*i+1, 0.0, 0.15, 0.0); } // Try with cutoffs but not periodic boundary conditions, and make sure the cl and Reference // platforms agree. nonbonded->setNonbondedMethod(NonbondedForce::CutoffNonPeriodic); nonbonded->setCutoffDistance(cutoff); system.addForce(nonbonded); system.addForce(bonds); VerletIntegrator integrator1(0.01); VerletIntegrator integrator2(0.01); Context cuContext(system, integrator1, platform); Context referenceContext(system, integrator2, reference); cuContext.setPositions(positions); cuContext.setVelocities(velocities); referenceContext.setPositions(positions); referenceContext.setVelocities(velocities); State cuState = cuContext.getState(State::Positions | State::Velocities | State::Forces | State::Energy); State referenceState = referenceContext.getState(State::Positions | State::Velocities | State::Forces | State::Energy); for (int i = 0; i < numParticles; i++) { ASSERT_EQUAL_VEC(cuState.getPositions()[i], referenceState.getPositions()[i], tol); ASSERT_EQUAL_VEC(cuState.getVelocities()[i], referenceState.getVelocities()[i], tol); ASSERT_EQUAL_VEC(cuState.getForces()[i], referenceState.getForces()[i], tol); } ASSERT_EQUAL_TOL(cuState.getPotentialEnergy(), referenceState.getPotentialEnergy(), tol); // Now do the same thing with periodic boundary conditions. nonbonded->setNonbondedMethod(NonbondedForce::CutoffPeriodic); system.setDefaultPeriodicBoxVectors(Vec3(boxSize, 0, 0), Vec3(0, boxSize, 0), Vec3(0, 0, boxSize)); cuContext.reinitialize(); referenceContext.reinitialize(); cuContext.setPositions(positions); cuContext.setVelocities(velocities); referenceContext.setPositions(positions); referenceContext.setVelocities(velocities); cuState = cuContext.getState(State::Positions | State::Velocities | State::Forces | State::Energy); referenceState = referenceContext.getState(State::Positions | State::Velocities | State::Forces | State::Energy); for (int i = 0; i < numParticles; i++) { double dx = cuState.getPositions()[i][0]-referenceState.getPositions()[i][0]; double dy = cuState.getPositions()[i][1]-referenceState.getPositions()[i][1]; double dz = cuState.getPositions()[i][2]-referenceState.getPositions()[i][2]; ASSERT_EQUAL_TOL(fmod(cuState.getPositions()[i][0]-referenceState.getPositions()[i][0], boxSize), 0, tol); ASSERT_EQUAL_TOL(fmod(cuState.getPositions()[i][1]-referenceState.getPositions()[i][1], boxSize), 0, tol); ASSERT_EQUAL_TOL(fmod(cuState.getPositions()[i][2]-referenceState.getPositions()[i][2], boxSize), 0, tol); ASSERT_EQUAL_VEC(cuState.getVelocities()[i], referenceState.getVelocities()[i], tol); ASSERT_EQUAL_VEC(cuState.getForces()[i], referenceState.getForces()[i], tol); } ASSERT_EQUAL_TOL(cuState.getPotentialEnergy(), referenceState.getPotentialEnergy(), tol); }