/**
 * 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 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);
}
Exemplo n.º 4
0
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);
    }
}
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);
    }
}
/**
 * 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);
    }
}
/**
 * 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));
}