void testPMEParameters() { // Create a cloud of random point charges. const int numParticles = 51; const double boxWidth = 4.7; System system; system.setDefaultPeriodicBoxVectors(Vec3(boxWidth, 0, 0), Vec3(0, boxWidth, 0), Vec3(0, 0, boxWidth)); NonbondedForce* force = new NonbondedForce(); system.addForce(force); vector<Vec3> positions(numParticles); OpenMM_SFMT::SFMT sfmt; init_gen_rand(0, sfmt); for (int i = 0; i < numParticles; i++) { system.addParticle(1.0); force->addParticle(-1.0+i*2.0/(numParticles-1), 1.0, 0.0); positions[i] = Vec3(boxWidth*genrand_real2(sfmt), boxWidth*genrand_real2(sfmt), boxWidth*genrand_real2(sfmt)); } force->setNonbondedMethod(NonbondedForce::PME); ReferencePlatform platform; // Compute the energy with an error tolerance of 1e-3. force->setEwaldErrorTolerance(1e-3); VerletIntegrator integrator1(0.01); Context context1(system, integrator1, platform); context1.setPositions(positions); double energy1 = context1.getState(State::Energy).getPotentialEnergy(); // Try again with an error tolerance of 1e-4. force->setEwaldErrorTolerance(1e-4); VerletIntegrator integrator2(0.01); Context context2(system, integrator2, platform); context2.setPositions(positions); double energy2 = context2.getState(State::Energy).getPotentialEnergy(); // Now explicitly set the parameters. These should match the values that were // used for tolerance 1e-3. force->setPMEParameters(2.49291157051793, 32, 32, 32); VerletIntegrator integrator3(0.01); Context context3(system, integrator3, platform); context3.setPositions(positions); double energy3 = context3.getState(State::Energy).getPotentialEnergy(); ASSERT_EQUAL_TOL(energy1, energy3, 1e-6); ASSERT(fabs((energy1-energy2)/energy1) > 1e-5); }
void testEwald2Ions() { System system; 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->setNonbondedMethod(NonbondedForce::Ewald); const double cutoff = 2.0; nonbonded->setCutoffDistance(cutoff); nonbonded->setEwaldErrorTolerance(TOL); system.setDefaultPeriodicBoxVectors(Vec3(6, 0, 0), Vec3(0, 6, 0), Vec3(0, 0, 6)); system.addForce(nonbonded); Context context(system, integrator, platform); vector<Vec3> positions(2); positions[0] = Vec3(3.048000,2.764000,3.156000); positions[1] = Vec3(2.809000,2.888000,2.571000); context.setPositions(positions); State state = context.getState(State::Forces | State::Energy); const vector<Vec3>& forces = state.getForces(); ASSERT_EQUAL_VEC(Vec3(-123.711, 64.1877, -302.716), forces[0], 10*TOL); ASSERT_EQUAL_VEC(Vec3( 123.711, -64.1877, 302.716), forces[1], 10*TOL); ASSERT_EQUAL_TOL(-217.276, state.getPotentialEnergy(), 0.01/*10*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 testWaterSystem() { ReferencePlatform platform; System system; static int numParticles = 648; const double boxSize = 1.86206; for (int i = 0 ; i < numParticles ; i++) { system.addParticle(1.0); } VerletIntegrator integrator(0.01); NonbondedForce* nonbonded = new NonbondedForce(); for (int i = 0 ; i < numParticles/3 ; i++) { nonbonded->addParticle(-0.82, 1, 0); nonbonded->addParticle(0.41, 1, 0); nonbonded->addParticle(0.41, 1, 0); } nonbonded->setNonbondedMethod(NonbondedForce::CutoffPeriodic); const double cutoff = 0.8; nonbonded->setCutoffDistance(cutoff); system.setDefaultPeriodicBoxVectors(Vec3(boxSize, 0, 0), Vec3(0, boxSize, 0), Vec3(0, 0, boxSize)); nonbonded->setEwaldErrorTolerance(EWALD_TOL); system.addForce(nonbonded); Context context(system, integrator, platform); vector<Vec3> positions(numParticles); #include "water.dat" context.setPositions(positions); State state1 = context.getState(State::Forces | State::Energy); const vector<Vec3>& forces = state1.getForces(); // Take a small step in the direction of the energy gradient. double norm = 0.0; for (int i = 0; i < numParticles; ++i) { Vec3 f = state1.getForces()[i]; norm += f[0]*f[0] + f[1]*f[1] + f[2]*f[2]; } norm = std::sqrt(norm); const double delta = 1e-3; double step = delta/norm; for (int i = 0; i < numParticles; ++i) { Vec3 p = positions[i]; Vec3 f = state1.getForces()[i]; positions[i] = Vec3(p[0]-f[0]*step, p[1]-f[1]*step, p[2]-f[2]*step); } context.setPositions(positions); // See whether the potential energy changed by the expected amount. nonbonded->setNonbondedMethod(NonbondedForce::Ewald); State state2 = context.getState(State::Energy); ASSERT_EQUAL_TOL(norm, (state2.getPotentialEnergy()-state1.getPotentialEnergy())/delta, 0.01) }
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 testErrorTolerance(NonbondedForce::NonbondedMethod method) { // Create a cloud of random point charges. const int numParticles = 51; const double boxWidth = 5.0; System system; system.setDefaultPeriodicBoxVectors(Vec3(boxWidth, 0, 0), Vec3(0, boxWidth, 0), Vec3(0, 0, boxWidth)); NonbondedForce* force = new NonbondedForce(); system.addForce(force); vector<Vec3> positions(numParticles); OpenMM_SFMT::SFMT sfmt; init_gen_rand(0, sfmt); for (int i = 0; i < numParticles; i++) { system.addParticle(1.0); force->addParticle(-1.0+i*2.0/(numParticles-1), 1.0, 0.0); positions[i] = Vec3(boxWidth*genrand_real2(sfmt), boxWidth*genrand_real2(sfmt), boxWidth*genrand_real2(sfmt)); } force->setNonbondedMethod(method); ReferencePlatform platform; // For various values of the cutoff and error tolerance, see if the actual error is reasonable. for (double cutoff = 1.0; cutoff < boxWidth/2; cutoff *= 1.2) { force->setCutoffDistance(cutoff); vector<Vec3> refForces; double norm = 0.0; for (double tol = 5e-5; tol < 1e-3; tol *= 2.0) { force->setEwaldErrorTolerance(tol); VerletIntegrator integrator(0.01); Context context(system, integrator, platform); context.setPositions(positions); State state = context.getState(State::Forces); if (refForces.size() == 0) { refForces = state.getForces(); for (int i = 0; i < numParticles; i++) norm += refForces[i].dot(refForces[i]); norm = sqrt(norm); } else { double diff = 0.0; for (int i = 0; i < numParticles; i++) { Vec3 delta = refForces[i]-state.getForces()[i]; diff += delta.dot(delta); } diff = sqrt(diff)/norm; ASSERT(diff < 2*tol); } } } }
void testEwaldExact() { // Use a NaCl crystal to compare the calculated and Madelung energies const int numParticles = 1000; const double cutoff = 1.0; const double boxSize = 2.82; ReferencePlatform platform; System system; for (int i = 0; i < numParticles/2; i++) system.addParticle(22.99); for (int i = 0; i < numParticles/2; i++) system.addParticle(35.45); VerletIntegrator integrator(0.01); NonbondedForce* nonbonded = new NonbondedForce(); 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); nonbonded->setNonbondedMethod(NonbondedForce::Ewald); nonbonded->setCutoffDistance(cutoff); system.setDefaultPeriodicBoxVectors(Vec3(boxSize, 0, 0), Vec3(0, boxSize, 0), Vec3(0, 0, boxSize)); nonbonded->setEwaldErrorTolerance(EWALD_TOL); system.addForce(nonbonded); Context context(system, integrator, platform); vector<Vec3> positions(numParticles); #include "nacl_crystal.dat" context.setPositions(positions); State state = context.getState(State::Forces | State::Energy); const vector<Vec3>& forces = state.getForces(); // The potential energy of an ion in a crystal is // E = - (M*e^2/ 4*pi*epsilon0*a0), // where // M : Madelung constant (dimensionless, for FCC cells such as NaCl it is 1.7476) // e : 1.6022 × 10−19 C // 4*pi*epsilon0: 1.112 × 10−10 C²/(J m) // a0 : 0.282 x 10-9 m (perfect cell) // // E is then the energy per pair of ions, so for our case // E has to be divided by 2 (per ion), multiplied by N(avogadro), multiplied by number of particles, and divided by 1000 for kJ double exactEnergy = - (1.7476 * 1.6022e-19 * 1.6022e-19 * 6.02214e+23 * numParticles) / (1.112e-10 * 0.282e-9 * 2 * 1000); //cout << "exact\t\t: " << exactEnergy << endl; //cout << "calc\t\t: " << state.getPotentialEnergy() << endl; ASSERT_EQUAL_TOL(exactEnergy, state.getPotentialEnergy(), 100*EWALD_TOL); }
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 ReferenceCalcMBPolElectrostaticsForceKernel::initialize(const OpenMM::System& system, const MBPolElectrostaticsForce& force) { numElectrostatics = force.getNumElectrostatics(); charges.resize(numElectrostatics); tholes.resize(5*numElectrostatics); dampingFactors.resize(numElectrostatics); polarity.resize(numElectrostatics); axisTypes.resize(numElectrostatics); multipoleAtomZs.resize(numElectrostatics); multipoleAtomXs.resize(numElectrostatics); multipoleAtomYs.resize(numElectrostatics); multipoleAtomCovalentInfo.resize(numElectrostatics); int dipoleIndex = 0; int quadrupoleIndex = 0; int tholeIndex = 0; int maxCovalentRange = 0; double totalCharge = 0.0; for( int ii = 0; ii < numElectrostatics; ii++ ){ // multipoles int axisType, multipoleAtomZ, multipoleAtomX, multipoleAtomY; double charge, dampingFactorD, polarityD; std::vector<double> dipolesD; std::vector<double> tholesD; force.getElectrostaticsParameters(ii, charge, axisType, multipoleAtomZ, multipoleAtomX, multipoleAtomY, tholesD, dampingFactorD, polarityD ); totalCharge += charge; axisTypes[ii] = axisType; multipoleAtomZs[ii] = multipoleAtomZ; multipoleAtomXs[ii] = multipoleAtomX; multipoleAtomYs[ii] = multipoleAtomY; charges[ii] = static_cast<RealOpenMM>(charge); dampingFactors[ii] = static_cast<RealOpenMM>(dampingFactorD); polarity[ii] = static_cast<RealOpenMM>(polarityD); tholes[tholeIndex++] = static_cast<RealOpenMM>(tholesD[0]); tholes[tholeIndex++] = static_cast<RealOpenMM>(tholesD[1]); tholes[tholeIndex++] = static_cast<RealOpenMM>(tholesD[2]); tholes[tholeIndex++] = static_cast<RealOpenMM>(tholesD[3]); tholes[tholeIndex++] = static_cast<RealOpenMM>(tholesD[4]); // covalent info std::vector< std::vector<int> > covalentLists; force.getCovalentMaps(ii, covalentLists ); multipoleAtomCovalentInfo[ii] = covalentLists; } polarizationType = force.getPolarizationType(); if( polarizationType == MBPolElectrostaticsForce::Mutual ){ mutualInducedMaxIterations = force.getMutualInducedMaxIterations(); mutualInducedTargetEpsilon = force.getMutualInducedTargetEpsilon(); } includeChargeRedistribution = force.getIncludeChargeRedistribution(); // PME nonbondedMethod = force.getNonbondedMethod(); if( nonbondedMethod == MBPolElectrostaticsForce::PME ){ usePme = true; alphaEwald = force.getAEwald(); cutoffDistance = force.getCutoffDistance(); force.getPmeGridDimensions(pmeGridDimension); if (pmeGridDimension[0] == 0 || alphaEwald == 0.0) { NonbondedForce nb; nb.setEwaldErrorTolerance(force.getEwaldErrorTolerance()); nb.setCutoffDistance(force.getCutoffDistance()); int gridSizeX, gridSizeY, gridSizeZ; NonbondedForceImpl::calcPMEParameters(system, nb, alphaEwald, gridSizeX, gridSizeY, gridSizeZ); pmeGridDimension[0] = gridSizeX; pmeGridDimension[1] = gridSizeY; pmeGridDimension[2] = gridSizeZ; std::cout << "Computed PME parameters for MBPolElectrostaticsForce, alphaEwald:" << alphaEwald << " pmeGrid: " << gridSizeX << "," << gridSizeY << ","<< gridSizeZ << std::endl; } } else { usePme = false; } return; }
void testPME(bool triclinic) { // Create a cloud of random point charges. const int numParticles = 51; const double boxWidth = 5.0; const double cutoff = 1.0; Vec3 boxVectors[3]; if (triclinic) { boxVectors[0] = Vec3(boxWidth, 0, 0); boxVectors[1] = Vec3(0.2*boxWidth, boxWidth, 0); boxVectors[2] = Vec3(-0.3*boxWidth, -0.1*boxWidth, boxWidth); } else { boxVectors[0] = Vec3(boxWidth, 0, 0); boxVectors[1] = Vec3(0, boxWidth, 0); boxVectors[2] = Vec3(0, 0, boxWidth); } System system; system.setDefaultPeriodicBoxVectors(boxVectors[0], boxVectors[1], boxVectors[2]); NonbondedForce* force = new NonbondedForce(); system.addForce(force); vector<Vec3> positions(numParticles); OpenMM_SFMT::SFMT sfmt; init_gen_rand(0, sfmt); for (int i = 0; i < numParticles; i++) { system.addParticle(1.0); force->addParticle(-1.0+i*2.0/(numParticles-1), 1.0, 0.0); positions[i] = Vec3(boxWidth*genrand_real2(sfmt), boxWidth*genrand_real2(sfmt), boxWidth*genrand_real2(sfmt)); } force->setNonbondedMethod(NonbondedForce::PME); force->setCutoffDistance(cutoff); force->setReciprocalSpaceForceGroup(1); force->setEwaldErrorTolerance(1e-4); // Compute the reciprocal space forces with the reference platform. Platform& platform = Platform::getPlatformByName("Reference"); VerletIntegrator integrator(0.01); Context context(system, integrator, platform); context.setPositions(positions); State refState = context.getState(State::Forces | State::Energy, false, 1<<1); // Now compute them with the optimized kernel. double alpha; int gridx, gridy, gridz; NonbondedForceImpl::calcPMEParameters(system, *force, alpha, gridx, gridy, gridz, false); CpuCalcPmeReciprocalForceKernel pme(CalcPmeReciprocalForceKernel::Name(), platform); IO io; double sumSquaredCharges = 0; for (int i = 0; i < numParticles; i++) { io.posq.push_back(positions[i][0]); io.posq.push_back(positions[i][1]); io.posq.push_back(positions[i][2]); double charge, sigma, epsilon; force->getParticleParameters(i, charge, sigma, epsilon); io.posq.push_back(charge); sumSquaredCharges += charge*charge; } double ewaldSelfEnergy = -ONE_4PI_EPS0*alpha*sumSquaredCharges/sqrt(M_PI); pme.initialize(gridx, gridy, gridz, numParticles, alpha, true); pme.beginComputation(io, boxVectors, true); double energy = pme.finishComputation(io); // See if they match. ASSERT_EQUAL_TOL(refState.getPotentialEnergy(), energy+ewaldSelfEnergy, 1e-3); for (int i = 0; i < numParticles; i++) ASSERT_EQUAL_VEC(refState.getForces()[i], Vec3(io.force[4*i], io.force[4*i+1], io.force[4*i+2]), 1e-3); }
void testEwaldPME() { double tol = 1e-5; const double boxSize = 3.00646; const double cutoff = 1.2; const int numParticles = 894; // Use amorphous NaCl system // The particles are simple charges, no VdW interactions ReferencePlatform platform; System system; for (int i = 0; i < numParticles/2; i++) system.addParticle(22.99); for (int i = 0; i < numParticles/2; i++) system.addParticle(35.45); VerletIntegrator integrator(0.01); NonbondedForce* nonbonded = new NonbondedForce(); 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); nonbonded->setNonbondedMethod(NonbondedForce::Ewald); nonbonded->setCutoffDistance(cutoff); system.setDefaultPeriodicBoxVectors(Vec3(boxSize, 0, 0), Vec3(0, boxSize, 0), Vec3(0, 0, boxSize)); nonbonded->setEwaldErrorTolerance(EWALD_TOL); system.addForce(nonbonded); Context context(system, integrator, platform); vector<Vec3> positions(numParticles); #include "nacl_amorph.dat" context.setPositions(positions); State state1 = context.getState(State::Forces | State::Energy); const vector<Vec3>& forces1 = state1.getForces(); // (1) CHECK EXACT VALUE OF EWALD ENERGY (Against Gromacs output) tol = 1e-4; ASSERT_EQUAL_TOL(-3.82047e+05, state1.getPotentialEnergy(), tol); // (2) CHECK WHETHER THE EWALD FORCES ARE THE SAME AS THE GROMACS OUTPUT // these are forces for alpha: 2.82756, kmax(x/y/z) = 11 tol = 1e-2; // #include "nacl_amorph_GromacsForcesEwald.dat" // (3) CHECK SELF-CONSISTENCY // Take a small step in the direction of the energy gradient. double norm = 0.0; for (int i = 0; i < numParticles; ++i) { Vec3 f = state1.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 = delta/norm; for (int i = 0; i < numParticles; ++i) { Vec3 p = positions[i]; Vec3 f = state1.getForces()[i]; positions[i] = Vec3(p[0]-f[0]*step, p[1]-f[1]*step, p[2]-f[2]*step); } context.setPositions(positions); // See whether the potential energy changed by the expected amount. State state2 = context.getState(State::Energy); ASSERT_EQUAL_TOL(norm, (state2.getPotentialEnergy()-state1.getPotentialEnergy())/delta, fabs(EWALD_TOL*state2.getPotentialEnergy()/(state2.getPotentialEnergy()-state1.getPotentialEnergy()))) // (4) CHECK EXACT VALUE OF PME ENERGY nonbonded->setNonbondedMethod(NonbondedForce::PME); nonbonded->setEwaldErrorTolerance(PME_TOL); context.reinitialize(); #include "nacl_amorph.dat" context.setPositions(positions); State state3 = context.getState(State::Forces | State::Energy); // Gromacs PME energy for the same mesh tol = 1e-4; ASSERT_EQUAL_TOL(-3.82047e+05, state3.getPotentialEnergy(), tol); // (5) CHECK WHETHER PME FORCES ARE THE SAME AS THE GROMACS OUTPUT USING EWALD tol = 1e-1; // #include "nacl_amorph_GromacsForcesEwald.dat" // (6) CHECK PME FOR SELF-CONSISTENCY // Take a small step in the direction of the energy gradient. norm = 0.0; for (int i = 0; i < numParticles; ++i) { Vec3 f = state3.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 = state3.getForces()[i]; positions[i] = Vec3(p[0]-f[0]*step, p[1]-f[1]*step, p[2]-f[2]*step); } context.setPositions(positions); // See whether the potential energy changed by the expected amount. State state4 = context.getState(State::Energy); ASSERT_EQUAL_TOL(norm, (state4.getPotentialEnergy()-state3.getPotentialEnergy())/delta, fabs(PME_TOL*state4.getPotentialEnergy()/(state4.getPotentialEnergy()-state3.getPotentialEnergy()))) }