void testPeriodic() {
    // Create a force that uses periodic boundary conditions, then compare to an identical custom force.
    
    System system;
    system.setDefaultPeriodicBoxVectors(Vec3(3, 0, 0), Vec3(0, 3, 0), Vec3(0, 0, 3));
    int numParticles = 3;
    for (int ii = 0; ii < numParticles; ii++)
        system.addParticle(1.0);
    LangevinIntegrator integrator(0.0, 0.1, 0.01);
    AmoebaAngleForce* amoebaAngleForce = new AmoebaAngleForce();
    double angle      = 100.0;
    double quadraticK = 1.0;
    double cubicK     = 1.0e-01;
    double quarticK   = 1.0e-02;
    double penticK    = 1.0e-03;
    double sexticK    = 1.0e-04;
    amoebaAngleForce->addAngle(0, 1, 2, angle, quadraticK);
    amoebaAngleForce->setAmoebaGlobalAngleCubic(cubicK);
    amoebaAngleForce->setAmoebaGlobalAngleQuartic(quarticK);
    amoebaAngleForce->setAmoebaGlobalAnglePentic(penticK);
    amoebaAngleForce->setAmoebaGlobalAngleSextic(sexticK);
    amoebaAngleForce->setUsesPeriodicBoundaryConditions(true);
    system.addForce(amoebaAngleForce);
    CustomAngleForce* customForce = new CustomAngleForce("k2*delta^2 + k3*delta^3 + k4*delta^4 + k5*delta^5 + k6*delta^6; delta=theta-theta0");
    customForce->addGlobalParameter("theta0", angle*M_PI/180);
    customForce->addGlobalParameter("k2", quadraticK*pow(180/M_PI, 2.0));
    customForce->addGlobalParameter("k3", cubicK*pow(180/M_PI, 3.0));
    customForce->addGlobalParameter("k4", quarticK*pow(180/M_PI, 4.0));
    customForce->addGlobalParameter("k5", penticK*pow(180/M_PI, 5.0));
    customForce->addGlobalParameter("k6", sexticK*pow(180/M_PI, 6.0));
    customForce->addAngle(0, 1, 2);
    customForce->setUsesPeriodicBoundaryConditions(true);
    customForce->setForceGroup(1);
    system.addForce(customForce);
    Context context(system, integrator, Platform::getPlatformByName("CUDA"));

    std::vector<Vec3> positions(numParticles);

    positions[0] = Vec3(0, 1, 0);
    positions[1] = Vec3(0, 0, 0);
    positions[2] = Vec3(0, 0, 2);

    context.setPositions(positions);
    State s1 = context.getState(State::Forces | State::Energy, true, 1);
    State s2 = context.getState(State::Forces | State::Energy, true, 2);
    ASSERT_EQUAL_TOL(s2.getPotentialEnergy(), s1.getPotentialEnergy(), 1e-5);
    for (int i = 0; i < numParticles; i++)
        ASSERT_EQUAL_VEC(s2.getForces()[i], s1.getForces()[i], 1e-5);
}
void* CustomAngleForceProxy::deserialize(const SerializationNode& node) const {
    int version = node.getIntProperty("version");
    if (version < 1 || version > 3)
        throw OpenMMException("Unsupported version number");
    CustomAngleForce* force = NULL;
    try {
        CustomAngleForce* force = new CustomAngleForce(node.getStringProperty("energy"));
        force->setForceGroup(node.getIntProperty("forceGroup", 0));
        if (version > 1)
            force->setUsesPeriodicBoundaryConditions(node.getBoolProperty("usesPeriodic"));
        const SerializationNode& perAngleParams = node.getChildNode("PerAngleParameters");
        for (int i = 0; i < (int) perAngleParams.getChildren().size(); i++) {
            const SerializationNode& parameter = perAngleParams.getChildren()[i];
            force->addPerAngleParameter(parameter.getStringProperty("name"));
        }
        const SerializationNode& globalParams = node.getChildNode("GlobalParameters");
        for (int i = 0; i < (int) globalParams.getChildren().size(); i++) {
            const SerializationNode& parameter = globalParams.getChildren()[i];
            force->addGlobalParameter(parameter.getStringProperty("name"), parameter.getDoubleProperty("default"));
        }
        if (version > 2) {
            const SerializationNode& energyDerivs = node.getChildNode("EnergyParameterDerivatives");
            for (int i = 0; i < (int) energyDerivs.getChildren().size(); i++) {
                const SerializationNode& parameter = energyDerivs.getChildren()[i];
                force->addEnergyParameterDerivative(parameter.getStringProperty("name"));
            }
        }
        const SerializationNode& angles = node.getChildNode("Angles");
        vector<double> params(force->getNumPerAngleParameters());
        for (int i = 0; i < (int) angles.getChildren().size(); i++) {
            const SerializationNode& angle = angles.getChildren()[i];
            for (int j = 0; j < (int) params.size(); j++) {
                stringstream key;
                key << "param";
                key << j+1;
                params[j] = angle.getDoubleProperty(key.str());
            }
            force->addAngle(angle.getIntProperty("p1"), angle.getIntProperty("p2"), angle.getIntProperty("p3"), params);
        }
        return force;
    }
    catch (...) {
        if (force != NULL)
            delete force;
        throw;
    }
}