Commit cecc774a authored by Peter Eastman's avatar Peter Eastman
Browse files

Merge branch 'master' into hardwall

parents 1dfa0e59 a20944f6
/* -------------------------------------------------------------------------- *
* OpenMM *
* -------------------------------------------------------------------------- *
......@@ -7,7 +6,7 @@
* Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org. *
* *
* Portions copyright (c) 2008-2015 Stanford University and the Authors. *
* Portions copyright (c) 2015 Stanford University and the Authors. *
* Authors: Peter Eastman *
* Contributors: *
* *
......@@ -30,964 +29,8 @@
* USE OR OTHER DEALINGS IN THE SOFTWARE. *
* -------------------------------------------------------------------------- */
/**
* This tests all the different force terms in the reference implementation of CustomNonbondedForce.
*/
#ifdef WIN32
#define _USE_MATH_DEFINES // Needed to get M_PI
#endif
#include "openmm/internal/AssertionUtilities.h"
#include "sfmt/SFMT.h"
#include "openmm/Context.h"
#include "ReferencePlatform.h"
#include "openmm/CustomNonbondedForce.h"
#include "openmm/NonbondedForce.h"
#include "openmm/System.h"
#include "openmm/VerletIntegrator.h"
#include <cmath>
#include <iostream>
#include <set>
#include <vector>
using namespace OpenMM;
using namespace std;
ReferencePlatform platform;
const double TOL = 1e-5;
void testSimpleExpression() {
System system;
system.addParticle(1.0);
system.addParticle(1.0);
VerletIntegrator integrator(0.01);
CustomNonbondedForce* forceField = new CustomNonbondedForce("-0.1*r^3");
forceField->addParticle(vector<double>());
forceField->addParticle(vector<double>());
system.addForce(forceField);
Context context(system, integrator, platform);
vector<Vec3> positions(2);
positions[0] = Vec3(0, 0, 0);
positions[1] = Vec3(2, 0, 0);
context.setPositions(positions);
State state = context.getState(State::Forces | State::Energy);
const vector<Vec3>& forces = state.getForces();
double force = 0.1*3*(2*2);
ASSERT_EQUAL_VEC(Vec3(-force, 0, 0), forces[0], TOL);
ASSERT_EQUAL_VEC(Vec3(force, 0, 0), forces[1], TOL);
ASSERT_EQUAL_TOL(-0.1*(2*2*2), state.getPotentialEnergy(), TOL);
}
void testParameters() {
System system;
system.addParticle(1.0);
system.addParticle(1.0);
VerletIntegrator integrator(0.01);
CustomNonbondedForce* forceField = new CustomNonbondedForce("scale*a*(r*b)^3; a=a1*a2; b=c+b1+b2");
forceField->addPerParticleParameter("a");
forceField->addPerParticleParameter("b");
forceField->addGlobalParameter("scale", 3.0);
forceField->addGlobalParameter("c", -1.0);
vector<double> params(2);
params[0] = 1.5;
params[1] = 2.0;
forceField->addParticle(params);
params[0] = 2.0;
params[1] = 3.0;
forceField->addParticle(params);
system.addForce(forceField);
Context context(system, integrator, platform);
vector<Vec3> positions(2);
positions[0] = Vec3(0, 0, 0);
positions[1] = Vec3(2, 0, 0);
context.setPositions(positions);
context.setParameter("scale", 1.0);
context.setParameter("c", 0.0);
State state = context.getState(State::Forces | State::Energy);
vector<Vec3> forces = state.getForces();
double force = -3.0*3*5.0*(10*10);
ASSERT_EQUAL_VEC(Vec3(-force, 0, 0), forces[0], TOL);
ASSERT_EQUAL_VEC(Vec3(force, 0, 0), forces[1], TOL);
ASSERT_EQUAL_TOL(3.0*(10*10*10), state.getPotentialEnergy(), TOL);
// Try changing the global parameters and make sure it's still correct.
context.setParameter("scale", 1.5);
context.setParameter("c", 1.0);
state = context.getState(State::Forces | State::Energy);
forces = state.getForces();
force = -1.5*3.0*3*6.0*(12*12);
ASSERT_EQUAL_VEC(Vec3(-force, 0, 0), forces[0], TOL);
ASSERT_EQUAL_VEC(Vec3(force, 0, 0), forces[1], TOL);
ASSERT_EQUAL_TOL(1.5*3.0*(12*12*12), state.getPotentialEnergy(), TOL);
// Try changing the per-particle parameters and make sure it's still correct.
params[0] = 1.6;
params[1] = 2.1;
forceField->setParticleParameters(0, params);
params[0] = 1.9;
params[1] = 2.8;
forceField->setParticleParameters(1, params);
forceField->updateParametersInContext(context);
state = context.getState(State::Forces | State::Energy);
forces = state.getForces();
force = -1.5*1.6*1.9*3*5.9*(11.8*11.8);
ASSERT_EQUAL_VEC(Vec3(-force, 0, 0), forces[0], TOL);
ASSERT_EQUAL_VEC(Vec3(force, 0, 0), forces[1], TOL);
ASSERT_EQUAL_TOL(1.5*1.6*1.9*(11.8*11.8*11.8), state.getPotentialEnergy(), TOL);
}
void testExclusions() {
System system;
VerletIntegrator integrator(0.01);
CustomNonbondedForce* nonbonded = new CustomNonbondedForce("a*r; a=a1+a2");
nonbonded->addPerParticleParameter("a");
vector<double> params(1);
vector<Vec3> positions(4);
for (int i = 0; i < 4; i++) {
system.addParticle(1.0);
params[0] = i+1;
nonbonded->addParticle(params);
positions[i] = Vec3(i, 0, 0);
}
nonbonded->addExclusion(0, 1);
nonbonded->addExclusion(1, 2);
nonbonded->addExclusion(2, 3);
nonbonded->addExclusion(0, 2);
nonbonded->addExclusion(1, 3);
system.addForce(nonbonded);
Context context(system, integrator, platform);
context.setPositions(positions);
State state = context.getState(State::Forces | State::Energy);
const vector<Vec3>& forces = state.getForces();
ASSERT_EQUAL_VEC(Vec3(1+4, 0, 0), forces[0], TOL);
ASSERT_EQUAL_VEC(Vec3(0, 0, 0), forces[1], TOL);
ASSERT_EQUAL_VEC(Vec3(0, 0, 0), forces[2], TOL);
ASSERT_EQUAL_VEC(Vec3(-(1+4), 0, 0), forces[3], TOL);
ASSERT_EQUAL_TOL((1+4)*3.0, state.getPotentialEnergy(), TOL);
}
void testCutoff() {
System system;
system.addParticle(1.0);
system.addParticle(1.0);
system.addParticle(1.0);
VerletIntegrator integrator(0.01);
CustomNonbondedForce* forceField = new CustomNonbondedForce("r");
forceField->addParticle(vector<double>());
forceField->addParticle(vector<double>());
forceField->addParticle(vector<double>());
forceField->setNonbondedMethod(CustomNonbondedForce::CutoffNonPeriodic);
forceField->setCutoffDistance(2.5);
system.addForce(forceField);
ASSERT(!forceField->usesPeriodicBoundaryConditions());
ASSERT(!system.usesPeriodicBoundaryConditions());
Context context(system, integrator, platform);
vector<Vec3> positions(3);
positions[0] = Vec3(0, 0, 0);
positions[1] = Vec3(0, 2, 0);
positions[2] = Vec3(0, 3, 0);
context.setPositions(positions);
State state = context.getState(State::Forces | State::Energy);
const vector<Vec3>& forces = state.getForces();
ASSERT_EQUAL_VEC(Vec3(0, 1, 0), forces[0], TOL);
ASSERT_EQUAL_VEC(Vec3(0, 0, 0), forces[1], TOL);
ASSERT_EQUAL_VEC(Vec3(0, -1, 0), forces[2], TOL);
ASSERT_EQUAL_TOL(2.0+1.0, state.getPotentialEnergy(), TOL);
}
void testPeriodic() {
System system;
system.addParticle(1.0);
system.addParticle(1.0);
system.addParticle(1.0);
VerletIntegrator integrator(0.01);
CustomNonbondedForce* forceField = new CustomNonbondedForce("r");
forceField->addParticle(vector<double>());
forceField->addParticle(vector<double>());
forceField->addParticle(vector<double>());
forceField->setNonbondedMethod(CustomNonbondedForce::CutoffPeriodic);
forceField->setCutoffDistance(2.0);
system.setDefaultPeriodicBoxVectors(Vec3(4, 0, 0), Vec3(0, 4, 0), Vec3(0, 0, 4));
system.addForce(forceField);
ASSERT(forceField->usesPeriodicBoundaryConditions());
ASSERT(system.usesPeriodicBoundaryConditions());
Context context(system, integrator, platform);
vector<Vec3> positions(3);
positions[0] = Vec3(0, 0, 0);
positions[1] = Vec3(0, 2.1, 0);
positions[2] = Vec3(0, 3, 0);
context.setPositions(positions);
State state = context.getState(State::Forces | State::Energy);
const vector<Vec3>& forces = state.getForces();
ASSERT_EQUAL_VEC(Vec3(0, -2, 0), forces[0], TOL);
ASSERT_EQUAL_VEC(Vec3(0, 2, 0), forces[1], TOL);
ASSERT_EQUAL_VEC(Vec3(0, 0, 0), forces[2], TOL);
ASSERT_EQUAL_TOL(1.9+1+0.9, state.getPotentialEnergy(), TOL);
}
void testTriclinic() {
System system;
system.addParticle(1.0);
system.addParticle(1.0);
Vec3 a(3.1, 0, 0);
Vec3 b(0.4, 3.5, 0);
Vec3 c(-0.1, -0.5, 4.0);
system.setDefaultPeriodicBoxVectors(a, b, c);
VerletIntegrator integrator(0.01);
CustomNonbondedForce* nonbonded = new CustomNonbondedForce("r");
nonbonded->addParticle(vector<double>());
nonbonded->addParticle(vector<double>());
nonbonded->setNonbondedMethod(CustomNonbondedForce::CutoffPeriodic);
const double cutoff = 1.5;
nonbonded->setCutoffDistance(cutoff);
system.addForce(nonbonded);
Context context(system, integrator, platform);
vector<Vec3> positions(2);
OpenMM_SFMT::SFMT sfmt;
init_gen_rand(0, sfmt);
for (int iteration = 0; iteration < 50; iteration++) {
// Generate random positions for the two particles.
positions[0] = a*genrand_real2(sfmt) + b*genrand_real2(sfmt) + c*genrand_real2(sfmt);
positions[1] = a*genrand_real2(sfmt) + b*genrand_real2(sfmt) + c*genrand_real2(sfmt);
context.setPositions(positions);
// Loop over all possible periodic copies and find the nearest one.
Vec3 delta;
double distance2 = 100.0;
for (int i = -1; i < 2; i++)
for (int j = -1; j < 2; j++)
for (int k = -1; k < 2; k++) {
Vec3 d = positions[1]-positions[0]+a*i+b*j+c*k;
if (d.dot(d) < distance2) {
delta = d;
distance2 = d.dot(d);
}
}
double distance = sqrt(distance2);
// See if the force and energy are correct.
State state = context.getState(State::Forces | State::Energy);
if (distance >= cutoff) {
ASSERT_EQUAL(0.0, state.getPotentialEnergy());
ASSERT_EQUAL_VEC(Vec3(0, 0, 0), state.getForces()[0], 0);
ASSERT_EQUAL_VEC(Vec3(0, 0, 0), state.getForces()[1], 0);
}
else {
const Vec3 force = delta/sqrt(delta.dot(delta));
ASSERT_EQUAL_TOL(distance, state.getPotentialEnergy(), TOL);
ASSERT_EQUAL_VEC(force, state.getForces()[0], TOL);
ASSERT_EQUAL_VEC(-force, state.getForces()[1], TOL);
}
}
}
void testContinuous1DFunction() {
System system;
system.addParticle(1.0);
system.addParticle(1.0);
VerletIntegrator integrator(0.01);
CustomNonbondedForce* forceField = new CustomNonbondedForce("fn(r)+1");
forceField->addParticle(vector<double>());
forceField->addParticle(vector<double>());
vector<double> table;
for (int i = 0; i < 21; i++)
table.push_back(sin(0.25*i));
forceField->addTabulatedFunction("fn", new Continuous1DFunction(table, 1.0, 6.0));
system.addForce(forceField);
Context context(system, integrator, platform);
vector<Vec3> positions(2);
positions[0] = Vec3(0, 0, 0);
for (int i = 1; i < 30; i++) {
double x = (7.0/30.0)*i;
positions[1] = Vec3(x, 0, 0);
context.setPositions(positions);
State state = context.getState(State::Forces | State::Energy);
const vector<Vec3>& forces = state.getForces();
double force = (x < 1.0 || x > 6.0 ? 0.0 : -cos(x-1.0));
double energy = (x < 1.0 || x > 6.0 ? 0.0 : sin(x-1.0))+1.0;
ASSERT_EQUAL_VEC(Vec3(-force, 0, 0), forces[0], 0.1);
ASSERT_EQUAL_VEC(Vec3(force, 0, 0), forces[1], 0.1);
ASSERT_EQUAL_TOL(energy, state.getPotentialEnergy(), 0.02);
}
for (int i = 1; i < 20; i++) {
double x = 0.25*i+1.0;
positions[1] = Vec3(x, 0, 0);
context.setPositions(positions);
State state = context.getState(State::Energy);
double energy = (x < 1.0 || x > 6.0 ? 0.0 : sin(x-1.0))+1.0;
ASSERT_EQUAL_TOL(energy, state.getPotentialEnergy(), 1e-4);
}
}
void testContinuous2DFunction() {
const int xsize = 20;
const int ysize = 21;
const double xmin = 0.4;
const double xmax = 1.5;
const double ymin = 0.0;
const double ymax = 2.1;
System system;
system.addParticle(1.0);
system.addParticle(1.0);
VerletIntegrator integrator(0.01);
CustomNonbondedForce* forceField = new CustomNonbondedForce("fn(r,a)+1");
forceField->addGlobalParameter("a", 0.0);
forceField->addParticle(vector<double>());
forceField->addParticle(vector<double>());
vector<double> table(xsize*ysize);
for (int i = 0; i < xsize; i++) {
for (int j = 0; j < ysize; j++) {
double x = xmin + i*(xmax-xmin)/xsize;
double y = ymin + j*(ymax-ymin)/ysize;
table[i+xsize*j] = sin(0.25*x)*cos(0.33*y);
}
}
forceField->addTabulatedFunction("fn", new Continuous2DFunction(xsize, ysize, table, xmin, xmax, ymin, ymax));
system.addForce(forceField);
Context context(system, integrator, platform);
vector<Vec3> positions(2);
positions[0] = Vec3(0, 0, 0);
for (double x = xmin-0.15; x < xmax+0.2; x += 0.1) {
for (double y = ymin-0.15; y < ymax+0.2; y += 0.1) {
positions[1] = Vec3(x, 0, 0);
context.setParameter("a", y);
context.setPositions(positions);
State state = context.getState(State::Forces | State::Energy);
const vector<Vec3>& forces = state.getForces();
double energy = 1;
double force = 0;
if (x >= xmin && x <= xmax && y >= ymin && y <= ymax) {
energy = sin(0.25*x)*cos(0.33*y)+1.0;
force = -0.25*cos(0.25*x)*cos(0.33*y);
}
ASSERT_EQUAL_VEC(Vec3(-force, 0, 0), forces[0], 0.1);
ASSERT_EQUAL_VEC(Vec3(force, 0, 0), forces[1], 0.1);
ASSERT_EQUAL_TOL(energy, state.getPotentialEnergy(), 0.02);
}
}
}
void testContinuous3DFunction() {
const int xsize = 10;
const int ysize = 11;
const int zsize = 12;
const double xmin = 0.6;
const double xmax = 1.1;
const double ymin = 0.0;
const double ymax = 0.7;
const double zmin = 0.2;
const double zmax = 0.9;
System system;
system.addParticle(1.0);
system.addParticle(1.0);
VerletIntegrator integrator(0.01);
CustomNonbondedForce* forceField = new CustomNonbondedForce("fn(r,a,b)+1");
forceField->addGlobalParameter("a", 0.0);
forceField->addGlobalParameter("b", 0.0);
forceField->addParticle(vector<double>());
forceField->addParticle(vector<double>());
vector<double> table(xsize*ysize*zsize);
for (int i = 0; i < xsize; i++) {
for (int j = 0; j < ysize; j++) {
for (int k = 0; k < zsize; k++) {
double x = xmin + i*(xmax-xmin)/xsize;
double y = ymin + j*(ymax-ymin)/ysize;
double z = zmin + k*(zmax-zmin)/zsize;
table[i+xsize*j+xsize*ysize*k] = sin(0.25*x)*cos(0.33*y)*(1+z);
}
}
}
forceField->addTabulatedFunction("fn", new Continuous3DFunction(xsize, ysize, zsize, table, xmin, xmax, ymin, ymax, zmin, zmax));
system.addForce(forceField);
Context context(system, integrator, platform);
vector<Vec3> positions(2);
positions[0] = Vec3(0, 0, 0);
for (double x = xmin-0.15; x < xmax+0.2; x += 0.1) {
for (double y = ymin-0.15; y < ymax+0.2; y += 0.1) {
for (double z = zmin-0.15; z < zmax+0.2; z += 0.1) {
positions[1] = Vec3(x, 0, 0);
context.setParameter("a", y);
context.setParameter("b", z);
context.setPositions(positions);
State state = context.getState(State::Forces | State::Energy);
const vector<Vec3>& forces = state.getForces();
double energy = 1;
double force = 0;
if (x >= xmin && x <= xmax && y >= ymin && y <= ymax && z >= zmin && z <= zmax) {
energy = sin(0.25*x)*cos(0.33*y)*(1.0+z)+1.0;
force = -0.25*cos(0.25*x)*cos(0.33*y)*(1.0+z);
}
ASSERT_EQUAL_VEC(Vec3(-force, 0, 0), forces[0], 0.1);
ASSERT_EQUAL_VEC(Vec3(force, 0, 0), forces[1], 0.1);
ASSERT_EQUAL_TOL(energy, state.getPotentialEnergy(), 0.05);
}
}
}
}
void testDiscrete1DFunction() {
System system;
system.addParticle(1.0);
system.addParticle(1.0);
VerletIntegrator integrator(0.01);
CustomNonbondedForce* forceField = new CustomNonbondedForce("fn(r)+1");
forceField->addParticle(vector<double>());
forceField->addParticle(vector<double>());
vector<double> table;
for (int i = 0; i < 21; i++)
table.push_back(sin(0.25*i));
forceField->addTabulatedFunction("fn", new Discrete1DFunction(table));
system.addForce(forceField);
Context context(system, integrator, platform);
vector<Vec3> positions(2);
positions[0] = Vec3(0, 0, 0);
for (int i = 0; i < (int) table.size(); i++) {
positions[1] = Vec3(i, 0, 0);
context.setPositions(positions);
State state = context.getState(State::Forces | State::Energy);
const vector<Vec3>& forces = state.getForces();
ASSERT_EQUAL_VEC(Vec3(0, 0, 0), forces[0], 1e-6);
ASSERT_EQUAL_VEC(Vec3(0, 0, 0), forces[1], 1e-6);
ASSERT_EQUAL_TOL(table[i]+1.0, state.getPotentialEnergy(), 1e-6);
}
}
void testDiscrete2DFunction() {
const int xsize = 10;
const int ysize = 5;
System system;
system.addParticle(1.0);
system.addParticle(1.0);
VerletIntegrator integrator(0.01);
CustomNonbondedForce* forceField = new CustomNonbondedForce("fn(r,a)+1");
forceField->addGlobalParameter("a", 0.0);
forceField->addParticle(vector<double>());
forceField->addParticle(vector<double>());
vector<double> table;
for (int i = 0; i < xsize; i++)
for (int j = 0; j < ysize; j++)
table.push_back(sin(0.25*i)+cos(0.33*j));
forceField->addTabulatedFunction("fn", new Discrete2DFunction(xsize, ysize, table));
system.addForce(forceField);
Context context(system, integrator, platform);
vector<Vec3> positions(2);
positions[0] = Vec3(0, 0, 0);
for (int i = 0; i < (int) table.size(); i++) {
positions[1] = Vec3(i%xsize, 0, 0);
context.setPositions(positions);
context.setParameter("a", i/xsize);
State state = context.getState(State::Forces | State::Energy);
const vector<Vec3>& forces = state.getForces();
ASSERT_EQUAL_VEC(Vec3(0, 0, 0), forces[0], 1e-6);
ASSERT_EQUAL_VEC(Vec3(0, 0, 0), forces[1], 1e-6);
ASSERT_EQUAL_TOL(table[i]+1.0, state.getPotentialEnergy(), 1e-6);
}
}
void testDiscrete3DFunction() {
const int xsize = 8;
const int ysize = 5;
const int zsize = 6;
System system;
system.addParticle(1.0);
system.addParticle(1.0);
VerletIntegrator integrator(0.01);
CustomNonbondedForce* forceField = new CustomNonbondedForce("fn(r,a,b)+1");
forceField->addGlobalParameter("a", 0.0);
forceField->addGlobalParameter("b", 0.0);
forceField->addParticle(vector<double>());
forceField->addParticle(vector<double>());
vector<double> table;
for (int i = 0; i < xsize; i++)
for (int j = 0; j < ysize; j++)
for (int k = 0; k < zsize; k++)
table.push_back(sin(0.25*i)+cos(0.33*j)+0.12345*k);
forceField->addTabulatedFunction("fn", new Discrete3DFunction(xsize, ysize, zsize, table));
system.addForce(forceField);
Context context(system, integrator, platform);
vector<Vec3> positions(2);
positions[0] = Vec3(0, 0, 0);
for (int i = 0; i < (int) table.size(); i++) {
positions[1] = Vec3(i%xsize, 0, 0);
context.setPositions(positions);
context.setParameter("a", (i/xsize)%ysize);
context.setParameter("b", i/(xsize*ysize));
State state = context.getState(State::Forces | State::Energy);
const vector<Vec3>& forces = state.getForces();
ASSERT_EQUAL_VEC(Vec3(0, 0, 0), forces[0], 1e-6);
ASSERT_EQUAL_VEC(Vec3(0, 0, 0), forces[1], 1e-6);
ASSERT_EQUAL_TOL(table[i]+1.0, state.getPotentialEnergy(), 1e-6);
}
}
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);
ASSERT(!customNonbonded->usesPeriodicBoundaryConditions());
ASSERT(!customSystem.usesPeriodicBoundaryConditions());
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 testSwitchingFunction() {
System system;
system.addParticle(1.0);
system.addParticle(1.0);
VerletIntegrator integrator(0.01);
CustomNonbondedForce* nonbonded = new CustomNonbondedForce("10/r^2");
vector<double> params;
nonbonded->addParticle(params);
nonbonded->addParticle(params);
nonbonded->setNonbondedMethod(CustomNonbondedForce::CutoffNonPeriodic);
nonbonded->setCutoffDistance(2.0);
nonbonded->setUseSwitchingFunction(true);
nonbonded->setSwitchingDistance(1.5);
system.addForce(nonbonded);
Context context(system, integrator, platform);
vector<Vec3> positions(2);
positions[0] = Vec3(0, 0, 0);
// Compute the interaction at various distances.
for (double r = 1.0; r < 2.5; r += 0.1) {
positions[1] = Vec3(r, 0, 0);
context.setPositions(positions);
State state = context.getState(State::Forces | State::Energy);
// See if the energy is correct.
double expectedEnergy = 10/(r*r);
double switchValue;
if (r <= 1.5)
switchValue = 1;
else if (r >= 2.0)
switchValue = 0;
else {
double t = (r-1.5)/0.5;
switchValue = 1+t*t*t*(-10+t*(15-t*6));
}
ASSERT_EQUAL_TOL(switchValue*expectedEnergy, state.getPotentialEnergy(), TOL);
// See if the force is the gradient of the energy.
double delta = 1e-3;
positions[1] = Vec3(r-delta, 0, 0);
context.setPositions(positions);
double e1 = context.getState(State::Energy).getPotentialEnergy();
positions[1] = Vec3(r+delta, 0, 0);
context.setPositions(positions);
double e2 = context.getState(State::Energy).getPotentialEnergy();
ASSERT_EQUAL_TOL((e2-e1)/(2*delta), state.getForces()[0][0], 1e-3);
}
}
void testLongRangeCorrection() {
// Create a box of particles.
int gridSize = 5;
int numParticles = gridSize*gridSize*gridSize;
double boxSize = gridSize*0.7;
double cutoff = boxSize/3;
System standardSystem;
System customSystem;
VerletIntegrator integrator1(0.01);
VerletIntegrator integrator2(0.01);
NonbondedForce* standardNonbonded = new NonbondedForce();
CustomNonbondedForce* customNonbonded = new CustomNonbondedForce("4*eps*((sigma/r)^12-(sigma/r)^6); sigma=0.5*(sigma1+sigma2); eps=sqrt(eps1*eps2)");
customNonbonded->addPerParticleParameter("sigma");
customNonbonded->addPerParticleParameter("eps");
vector<Vec3> positions(numParticles);
int index = 0;
vector<double> params1(2);
params1[0] = 1.1;
params1[1] = 0.5;
vector<double> params2(2);
params2[0] = 1;
params2[1] = 1;
for (int i = 0; i < gridSize; i++)
for (int j = 0; j < gridSize; j++)
for (int k = 0; k < gridSize; k++) {
standardSystem.addParticle(1.0);
customSystem.addParticle(1.0);
if (index%2 == 0) {
standardNonbonded->addParticle(0, params1[0], params1[1]);
customNonbonded->addParticle(params1);
}
else {
standardNonbonded->addParticle(0, params2[0], params2[1]);
customNonbonded->addParticle(params2);
}
positions[index] = Vec3(i*boxSize/gridSize, j*boxSize/gridSize, k*boxSize/gridSize);
index++;
}
standardNonbonded->setNonbondedMethod(NonbondedForce::CutoffPeriodic);
customNonbonded->setNonbondedMethod(CustomNonbondedForce::CutoffPeriodic);
standardNonbonded->setCutoffDistance(cutoff);
customNonbonded->setCutoffDistance(cutoff);
standardSystem.setDefaultPeriodicBoxVectors(Vec3(boxSize, 0, 0), Vec3(0, boxSize, 0), Vec3(0, 0, boxSize));
customSystem.setDefaultPeriodicBoxVectors(Vec3(boxSize, 0, 0), Vec3(0, boxSize, 0), Vec3(0, 0, boxSize));
standardNonbonded->setUseDispersionCorrection(true);
customNonbonded->setUseLongRangeCorrection(true);
standardNonbonded->setUseSwitchingFunction(true);
customNonbonded->setUseSwitchingFunction(true);
standardNonbonded->setSwitchingDistance(0.8*cutoff);
customNonbonded->setSwitchingDistance(0.8*cutoff);
standardSystem.addForce(standardNonbonded);
customSystem.addForce(customNonbonded);
// Compute the correction for the standard force.
Context context1(standardSystem, integrator1, platform);
context1.setPositions(positions);
double standardEnergy1 = context1.getState(State::Energy).getPotentialEnergy();
standardNonbonded->setUseDispersionCorrection(false);
context1.reinitialize();
context1.setPositions(positions);
double standardEnergy2 = context1.getState(State::Energy).getPotentialEnergy();
// Compute the correction for the custom force.
Context context2(customSystem, integrator2, platform);
context2.setPositions(positions);
double customEnergy1 = context2.getState(State::Energy).getPotentialEnergy();
customNonbonded->setUseLongRangeCorrection(false);
context2.reinitialize();
context2.setPositions(positions);
double customEnergy2 = context2.getState(State::Energy).getPotentialEnergy();
// See if they agree.
ASSERT_EQUAL_TOL(standardEnergy1-standardEnergy2, customEnergy1-customEnergy2, 1e-4);
}
void testInteractionGroups() {
const int numParticles = 6;
System system;
VerletIntegrator integrator(0.01);
CustomNonbondedForce* nonbonded = new CustomNonbondedForce("v1+v2");
nonbonded->addPerParticleParameter("v");
vector<double> params(1, 0.001);
for (int i = 0; i < numParticles; i++) {
system.addParticle(1.0);
nonbonded->addParticle(params);
params[0] *= 10;
}
set<int> set1, set2, set3, set4;
set1.insert(2);
set2.insert(0);
set2.insert(1);
set2.insert(2);
set2.insert(3);
set2.insert(4);
set2.insert(5);
nonbonded->addInteractionGroup(set1, set2); // Particle 2 interacts with every other particle.
set3.insert(0);
set3.insert(1);
set4.insert(4);
set4.insert(5);
nonbonded->addInteractionGroup(set3, set4); // Particles 0 and 1 interact with 4 and 5.
nonbonded->addExclusion(1, 2); // Add an exclusion to make sure it gets skipped.
system.addForce(nonbonded);
Context context(system, integrator, platform);
vector<Vec3> positions(numParticles);
context.setPositions(positions);
State state = context.getState(State::Energy);
double expectedEnergy = 331.423; // Each digit is the number of interactions a particle particle is involved in.
ASSERT_EQUAL_TOL(expectedEnergy, state.getPotentialEnergy(), TOL);
}
void testLargeInteractionGroup() {
const int numMolecules = 300;
const int numParticles = numMolecules*2;
const double boxSize = 20.0;
// Create a large system.
System system;
system.setDefaultPeriodicBoxVectors(Vec3(boxSize, 0, 0), Vec3(0, boxSize, 0), Vec3(0, 0, boxSize));
for (int i = 0; i < numParticles; i++)
system.addParticle(1.0);
CustomNonbondedForce* nonbonded = 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)");
nonbonded->addPerParticleParameter("q");
nonbonded->addPerParticleParameter("sigma");
nonbonded->addPerParticleParameter("eps");
vector<Vec3> positions(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) {
params[0] = 1.0;
params[1] = 0.2;
params[2] = 0.1;
nonbonded->addParticle(params);
params[0] = -1.0;
params[1] = 0.1;
nonbonded->addParticle(params);
}
else {
params[0] = 1.0;
params[1] = 0.2;
params[2] = 0.2;
nonbonded->addParticle(params);
params[0] = -1.0;
params[1] = 0.1;
nonbonded->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]);
nonbonded->addExclusion(2*i, 2*i+1);
}
nonbonded->setNonbondedMethod(CustomNonbondedForce::CutoffPeriodic);
system.addForce(nonbonded);
// Compute the forces.
VerletIntegrator integrator(0.01);
Context context(system, integrator, platform);
context.setPositions(positions);
State state1 = context.getState(State::Forces);
// Modify the force so only one particle interacts with everything else.
set<int> set1, set2;
set1.insert(151);
for (int i = 0; i < numParticles; i++)
set2.insert(i);
nonbonded->addInteractionGroup(set1, set2);
context.reinitialize();
context.setPositions(positions);
State state2 = context.getState(State::Forces);
// The force on that one particle should be the same.
ASSERT_EQUAL_VEC(state1.getForces()[151], state2.getForces()[151], 1e-4);
// Modify the interaction group so it includes all interactions. This should now reproduce the original forces
// on all atoms.
for (int i = 0; i < numParticles; i++)
set1.insert(i);
nonbonded->setInteractionGroupParameters(0, set1, set2);
context.reinitialize();
context.setPositions(positions);
State state3 = context.getState(State::Forces);
for (int i = 0; i < numParticles; i++)
ASSERT_EQUAL_VEC(state1.getForces()[i], state3.getForces()[i], 1e-4);
}
void testInteractionGroupLongRangeCorrection() {
const int numParticles = 10;
const double boxSize = 10.0;
const double cutoff = 0.5;
System system;
system.setDefaultPeriodicBoxVectors(Vec3(boxSize, 0, 0), Vec3(0, boxSize, 0), Vec3(0, 0, boxSize));
CustomNonbondedForce* nonbonded = new CustomNonbondedForce("c1*c2*r^-4");
nonbonded->addPerParticleParameter("c");
vector<Vec3> positions(numParticles);
vector<double> params(1);
for (int i = 0; i < numParticles; i++) {
system.addParticle(1.0);
params[0] = (i%2 == 0 ? 1.1 : 2.0);
nonbonded->addParticle(params);
positions[i] = Vec3(0.5*i, 0, 0);
}
nonbonded->setNonbondedMethod(CustomNonbondedForce::CutoffPeriodic);
nonbonded->setCutoffDistance(cutoff);
system.addForce(nonbonded);
// Setup nonbonded groups. They involve 1 interaction of type AA,
// 2 of type BB, and 5 of type AB.
set<int> set1, set2, set3, set4, set5;
set1.insert(0);
set1.insert(1);
set1.insert(2);
nonbonded->addInteractionGroup(set1, set1);
set2.insert(3);
set3.insert(4);
set3.insert(6);
set3.insert(8);
nonbonded->addInteractionGroup(set2, set3);
set4.insert(5);
set5.insert(7);
set5.insert(9);
nonbonded->addInteractionGroup(set4, set5);
// Compute energy with and without the correction.
VerletIntegrator integrator(0.01);
Context context(system, integrator, platform);
context.setPositions(positions);
double energy1 = context.getState(State::Energy).getPotentialEnergy();
nonbonded->setUseLongRangeCorrection(true);
context.reinitialize();
context.setPositions(positions);
double energy2 = context.getState(State::Energy).getPotentialEnergy();
// Check the result.
double sum = (1.1*1.1 + 2*2.0*2.0 + 5*1.1*2.0)*2.0;
int numPairs = (numParticles*(numParticles+1))/2;
double expected = 2*M_PI*numParticles*numParticles*sum/(numPairs*boxSize*boxSize*boxSize);
ASSERT_EQUAL_TOL(expected, energy2-energy1, 1e-4);
}
void testMultipleCutoffs() {
System system;
system.addParticle(1.0);
system.addParticle(1.0);
VerletIntegrator integrator(0.01);
// Add multiple nonbonded forces that have different cutoffs.
CustomNonbondedForce* nonbonded1 = new CustomNonbondedForce("2*r");
nonbonded1->addParticle(vector<double>());
nonbonded1->addParticle(vector<double>());
nonbonded1->setNonbondedMethod(CustomNonbondedForce::CutoffNonPeriodic);
nonbonded1->setCutoffDistance(2.5);
system.addForce(nonbonded1);
CustomNonbondedForce* nonbonded2 = new CustomNonbondedForce("3*r");
nonbonded2->addParticle(vector<double>());
nonbonded2->addParticle(vector<double>());
nonbonded2->setNonbondedMethod(CustomNonbondedForce::CutoffNonPeriodic);
nonbonded2->setCutoffDistance(2.9);
nonbonded2->setForceGroup(1);
system.addForce(nonbonded2);
Context context(system, integrator, platform);
vector<Vec3> positions(2);
positions[0] = Vec3(0, 0, 0);
positions[1] = Vec3(0, 0, 0);
for (double r = 2.4; r < 3.2; r += 0.2) {
positions[1][1] = r;
context.setPositions(positions);
double e1 = (r < 2.5 ? 2.0*r : 0.0);
double e2 = (r < 2.9 ? 3.0*r : 0.0);
double f1 = (r < 2.5 ? 2.0 : 0.0);
double f2 = (r < 2.9 ? 3.0 : 0.0);
// Check the first force.
State state = context.getState(State::Forces | State::Energy, false, 1);
ASSERT_EQUAL_VEC(Vec3(0, f1, 0), state.getForces()[0], TOL);
ASSERT_EQUAL_VEC(Vec3(0, -f1, 0), state.getForces()[1], TOL);
ASSERT_EQUAL_TOL(e1, state.getPotentialEnergy(), TOL);
// Check the second force.
state = context.getState(State::Forces | State::Energy, false, 2);
ASSERT_EQUAL_VEC(Vec3(0, f2, 0), state.getForces()[0], TOL);
ASSERT_EQUAL_VEC(Vec3(0, -f2, 0), state.getForces()[1], TOL);
ASSERT_EQUAL_TOL(e2, state.getPotentialEnergy(), TOL);
// Check the sum of both forces.
state = context.getState(State::Forces | State::Energy);
ASSERT_EQUAL_VEC(Vec3(0, f1+f2, 0), state.getForces()[0], TOL);
ASSERT_EQUAL_VEC(Vec3(0, -f1-f2, 0), state.getForces()[1], TOL);
ASSERT_EQUAL_TOL(e1+e2, state.getPotentialEnergy(), TOL);
}
}
#include "ReferenceTests.h"
#include "TestCustomNonbondedForce.h"
int main() {
try {
testSimpleExpression();
testParameters();
testExclusions();
testCutoff();
testPeriodic();
testTriclinic();
testContinuous1DFunction();
testContinuous2DFunction();
testContinuous3DFunction();
testDiscrete1DFunction();
testDiscrete2DFunction();
testDiscrete3DFunction();
testCoulombLennardJones();
testSwitchingFunction();
testLongRangeCorrection();
testInteractionGroups();
testLargeInteractionGroup();
testInteractionGroupLongRangeCorrection();
testMultipleCutoffs();
}
catch(const exception& e) {
cout << "exception: " << e.what() << endl;
return 1;
}
cout << "Done" << endl;
return 0;
void runPlatformTests() {
}
......@@ -6,7 +6,7 @@
* Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org. *
* *
* Portions copyright (c) 2008-2010 Stanford University and the Authors. *
* Portions copyright (c) 2015 Stanford University and the Authors. *
* Authors: Peter Eastman *
* Contributors: *
* *
......@@ -29,162 +29,8 @@
* USE OR OTHER DEALINGS IN THE SOFTWARE. *
* -------------------------------------------------------------------------- */
/**
* This tests the reference implementation of CustomTorsionForce.
*/
#include "ReferenceTests.h"
#include "TestCustomTorsionForce.h"
#ifdef WIN32
#define _USE_MATH_DEFINES // Needed to get M_PI
#endif
#include "openmm/internal/AssertionUtilities.h"
#include "openmm/Context.h"
#include "ReferencePlatform.h"
#include "openmm/CustomTorsionForce.h"
#include "openmm/PeriodicTorsionForce.h"
#include "openmm/System.h"
#include "openmm/VerletIntegrator.h"
#include "sfmt/SFMT.h"
#include <iostream>
#include <vector>
using namespace OpenMM;
using namespace std;
ReferencePlatform platform;
const double TOL = 1e-5;
void testTorsions() {
// Create a system using a CustomTorsionForce.
System customSystem;
customSystem.addParticle(1.0);
customSystem.addParticle(1.0);
customSystem.addParticle(1.0);
customSystem.addParticle(1.0);
customSystem.addParticle(1.0);
CustomTorsionForce* custom = new CustomTorsionForce("k*(1+cos(n*theta-theta0))");
custom->addPerTorsionParameter("theta0");
custom->addPerTorsionParameter("n");
custom->addGlobalParameter("k", 0.5);
vector<double> parameters(2);
parameters[0] = 1.5;
parameters[1] = 1;
custom->addTorsion(0, 1, 2, 3, parameters);
parameters[0] = 2.0;
parameters[1] = 2;
custom->addTorsion(1, 2, 3, 4, parameters);
customSystem.addForce(custom);
ASSERT(!custom->usesPeriodicBoundaryConditions());
ASSERT(!customSystem.usesPeriodicBoundaryConditions());
// Create an identical system using a PeriodicTorsionForce.
System harmonicSystem;
harmonicSystem.addParticle(1.0);
harmonicSystem.addParticle(1.0);
harmonicSystem.addParticle(1.0);
harmonicSystem.addParticle(1.0);
harmonicSystem.addParticle(1.0);
VerletIntegrator integrator(0.01);
PeriodicTorsionForce* periodic = new PeriodicTorsionForce();
periodic->addTorsion(0, 1, 2, 3, 1, 1.5, 0.5);
periodic->addTorsion(1, 2, 3, 4, 2, 2.0, 0.5);
harmonicSystem.addForce(periodic);
// Set the atoms in various positions, and verify that both systems give identical forces and energy.
OpenMM_SFMT::SFMT sfmt;
init_gen_rand(0, sfmt);
vector<Vec3> positions(5);
VerletIntegrator integrator1(0.01);
VerletIntegrator integrator2(0.01);
Context c1(customSystem, integrator1, platform);
Context c2(harmonicSystem, integrator2, platform);
for (int i = 0; i < 50; i++) {
for (int j = 0; j < (int) positions.size(); j++)
positions[j] = Vec3(5.0*genrand_real2(sfmt), 5.0*genrand_real2(sfmt), 5.0*genrand_real2(sfmt));
c1.setPositions(positions);
c2.setPositions(positions);
State s1 = c1.getState(State::Forces | State::Energy);
State s2 = c2.getState(State::Forces | State::Energy);
for (int i = 0; i < customSystem.getNumParticles(); i++)
ASSERT_EQUAL_VEC(s1.getForces()[i], s2.getForces()[i], TOL);
ASSERT_EQUAL_TOL(s1.getPotentialEnergy(), s2.getPotentialEnergy(), TOL);
}
// Try changing the torsion parameters and make sure it's still correct.
parameters[0] = 1.6;
parameters[1] = 2;
custom->setTorsionParameters(0, 0, 1, 2, 3, parameters);
parameters[0] = 2.1;
parameters[1] = 3;
custom->setTorsionParameters(1, 1, 2, 3, 4, parameters);
custom->updateParametersInContext(c1);
periodic->setTorsionParameters(0, 0, 1, 2, 3, 2, 1.6, 0.5);
periodic->setTorsionParameters(1, 1, 2, 3, 4, 3, 2.1, 0.5);
periodic->updateParametersInContext(c2);
{
for (int j = 0; j < (int) positions.size(); j++)
positions[j] = Vec3(5.0*genrand_real2(sfmt), 5.0*genrand_real2(sfmt), 5.0*genrand_real2(sfmt));
c1.setPositions(positions);
c2.setPositions(positions);
State s1 = c1.getState(State::Forces | State::Energy);
State s2 = c2.getState(State::Forces | State::Energy);
const vector<Vec3>& forces = s1.getForces();
for (int i = 0; i < customSystem.getNumParticles(); i++)
ASSERT_EQUAL_VEC(s1.getForces()[i], s2.getForces()[i], TOL);
ASSERT_EQUAL_TOL(s1.getPotentialEnergy(), s2.getPotentialEnergy(), TOL);
}
}
void testRange() {
System system;
system.addParticle(1.0);
system.addParticle(1.0);
system.addParticle(1.0);
system.addParticle(1.0);
CustomTorsionForce* custom = new CustomTorsionForce("theta");
custom->addTorsion(0, 1, 2, 3, vector<double>());
system.addForce(custom);
// Set the atoms in various positions, and verify that the angle is always in the expected range.
OpenMM_SFMT::SFMT sfmt;
init_gen_rand(0, sfmt);
vector<Vec3> positions(4);
VerletIntegrator integrator(0.01);
double minAngle = 1000;
double maxAngle = -1000;
Context context(system, integrator, platform);
for (int i = 0; i < 100; i++) {
for (int j = 0; j < (int) positions.size(); j++)
positions[j] = Vec3(5.0*genrand_real2(sfmt), 5.0*genrand_real2(sfmt), 5.0*genrand_real2(sfmt));
context.setPositions(positions);
double angle = context.getState(State::Energy).getPotentialEnergy();
if (angle < minAngle)
minAngle = angle;
if (angle > maxAngle)
maxAngle = angle;
}
ASSERT(minAngle >= -M_PI);
ASSERT(maxAngle <= M_PI);
void runPlatformTests() {
}
int main() {
try {
testTorsions();
testRange();
}
catch(const exception& e) {
cout << "exception: " << e.what() << endl;
return 1;
}
cout << "Done" << endl;
return 0;
}
......@@ -6,7 +6,7 @@
* Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org. *
* *
* Portions copyright (c) 2008-2014 Stanford University and the Authors. *
* Portions copyright (c) 2015 Stanford University and the Authors. *
* Authors: Peter Eastman *
* Contributors: *
* *
......@@ -29,455 +29,8 @@
* USE OR OTHER DEALINGS IN THE SOFTWARE. *
* -------------------------------------------------------------------------- */
/**
* This tests the Eewald summation method reference implementation of NonbondedForce.
*/
#include "ReferenceTests.h"
#include "TestEwald.h"
#include "openmm/internal/AssertionUtilities.h"
#include "openmm/Context.h"
#include "ReferencePlatform.h"
#include "openmm/NonbondedForce.h"
#include "openmm/System.h"
#include "openmm/VerletIntegrator.h"
#include "SimTKOpenMMRealType.h"
#include "sfmt/SFMT.h"
#include "openmm/HarmonicBondForce.h"
#include "openmm/internal/NonbondedForceImpl.h"
#include <iostream>
#include <vector>
using namespace OpenMM;
using namespace std;
ReferencePlatform platform;
const double EWALD_TOL = 1e-5;
const double PME_TOL = 5e-5;
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;
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() {
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
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())))
}
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);
system.setDefaultPeriodicBoxVectors(Vec3(6, 0, 0), Vec3(0, 6, 0), Vec3(0, 0, 6));
nonbonded->setEwaldErrorTolerance(EWALD_TOL);
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*EWALD_TOL);
ASSERT_EQUAL_VEC(Vec3(123.711, -64.1877, 302.716), forces[1], 10*EWALD_TOL);
ASSERT_EQUAL_TOL(-217.276, state.getPotentialEnergy(), 10*EWALD_TOL);
}
void testWaterSystem() {
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 testTriclinic() {
// Create a triclinic box containing eight particles.
System system;
system.setDefaultPeriodicBoxVectors(Vec3(2.5, 0, 0), Vec3(0.5, 3.0, 0), Vec3(0.7, 0.9, 3.5));
for (int i = 0; i < 8; i++)
system.addParticle(1.0);
NonbondedForce* force = new NonbondedForce();
system.addForce(force);
force->setNonbondedMethod(NonbondedForce::PME);
force->setCutoffDistance(1.0);
force->setPMEParameters(3.45891, 32, 40, 48);
for (int i = 0; i < 4; i++)
force->addParticle(-1, 0.440104, 0.4184); // Cl parameters
for (int i = 0; i < 4; i++)
force->addParticle(1, 0.332840, 0.0115897); // Na parameters
vector<Vec3> positions(8);
positions[0] = Vec3(1.744, 2.788, 3.162);
positions[1] = Vec3(1.048, 0.762, 2.340);
positions[2] = Vec3(2.489, 1.570, 2.817);
positions[3] = Vec3(1.027, 1.893, 3.271);
positions[4] = Vec3(0.937, 0.825, 0.009);
positions[5] = Vec3(2.290, 1.887, 3.352);
positions[6] = Vec3(1.266, 1.111, 2.894);
positions[7] = Vec3(0.933, 1.862, 3.490);
// Compute the forces and energy.
VerletIntegrator integ(0.001);
Context context(system, integ, platform);
context.setPositions(positions);
State state = context.getState(State::Forces | State::Energy);
// Compare them to values computed by Gromacs.
double expectedEnergy = -963.370;
vector<Vec3> expectedForce(8);
expectedForce[0] = Vec3(4.25253e+01, -1.23503e+02, 1.22139e+02);
expectedForce[1] = Vec3(9.74752e+01, 1.68213e+02, 1.93169e+02);
expectedForce[2] = Vec3(-1.50348e+02, 1.29165e+02, 3.70435e+02);
expectedForce[3] = Vec3(9.18644e+02, -3.52571e+00, -1.34772e+03);
expectedForce[4] = Vec3(-1.61193e+02, 9.01528e+01, -7.12904e+01);
expectedForce[5] = Vec3(2.82630e+02, 2.78029e+01, -3.72864e+02);
expectedForce[6] = Vec3(-1.47454e+02, -2.14448e+02, -3.55789e+02);
expectedForce[7] = Vec3(-8.82195e+02, -7.39132e+01, 1.46202e+03);
for (int i = 0; i < 8; i++) {
ASSERT_EQUAL_VEC(expectedForce[i], state.getForces()[i], 1e-4);
}
ASSERT_EQUAL_TOL(expectedEnergy, state.getPotentialEnergy(), 1e-4);
}
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);
// 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);
}
if (method == NonbondedForce::PME) {
// See if the PME parameters were calculated correctly.
double expectedAlpha, actualAlpha;
int expectedSize[3], actualSize[3];
NonbondedForceImpl::calcPMEParameters(system, *force, expectedAlpha, expectedSize[0], expectedSize[1], expectedSize[2]);
force->getPMEParametersInContext(context, actualAlpha, actualSize[0], actualSize[1], actualSize[2]);
ASSERT_EQUAL_TOL(expectedAlpha, actualAlpha, 1e-5);
for (int i = 0; i < 3; i++) {
ASSERT(actualSize[i] >= expectedSize[i]);
ASSERT(actualSize[i] < expectedSize[i]+10);
}
}
}
}
}
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);
// 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);
}
int main() {
try {
testEwaldExact();
testEwaldPME();
// testEwald2Ions();
// testWaterSystem();
testTriclinic();
testErrorTolerance(NonbondedForce::Ewald);
testErrorTolerance(NonbondedForce::PME);
testPMEParameters();
}
catch(const exception& e) {
cout << "exception: " << e.what() << endl;
return 1;
}
cout << "Done" << endl;
return 0;
void runPlatformTests() {
}
......@@ -6,7 +6,7 @@
* Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org. *
* *
* Portions copyright (c) 2008-2013 Stanford University and the Authors. *
* Portions copyright (c) 2015 Stanford University and the Authors. *
* Authors: Peter Eastman *
* Contributors: *
* *
......@@ -29,216 +29,8 @@
* USE OR OTHER DEALINGS IN THE SOFTWARE. *
* -------------------------------------------------------------------------- */
/**
* This tests the reference implementation of GBSAOBCForce.
*/
#include "ReferenceTests.h"
#include "TestGBSAOBCForce.h"
#include "openmm/internal/AssertionUtilities.h"
#include "openmm/Context.h"
#include "ReferencePlatform.h"
#include "openmm/GBSAOBCForce.h"
#include "openmm/System.h"
#include "openmm/LangevinIntegrator.h"
#include "openmm/NonbondedForce.h"
#include "SimTKOpenMMRealType.h"
#include "sfmt/SFMT.h"
#include <iostream>
#include <vector>
using namespace OpenMM;
using namespace std;
ReferencePlatform platform;
const double TOL = 1e-5;
void testSingleParticle() {
System system;
system.addParticle(2.0);
LangevinIntegrator integrator(0, 0.1, 0.01);
GBSAOBCForce* forceField = new GBSAOBCForce();
forceField->addParticle(0.5, 0.15, 1);
system.addForce(forceField);
ASSERT(!forceField->usesPeriodicBoundaryConditions());
ASSERT(!system.usesPeriodicBoundaryConditions());
Context context(system, integrator, platform);
vector<Vec3> positions(1);
positions[0] = Vec3(0, 0, 0);
context.setPositions(positions);
State state = context.getState(State::Energy);
double bornRadius = 0.15-0.009; // dielectric offset
double eps0 = EPSILON0;
double bornEnergy = (-0.5*0.5/(8*PI_M*eps0))*(1.0/forceField->getSoluteDielectric()-1.0/forceField->getSolventDielectric())/bornRadius;
double extendedRadius = 0.15+0.14; // probe radius
double nonpolarEnergy = 4*PI_M*2.25936*extendedRadius*extendedRadius*std::pow(0.15/bornRadius, 6.0);
ASSERT_EQUAL_TOL((bornEnergy+nonpolarEnergy), state.getPotentialEnergy(), 0.01);
// Change the parameters and see if it is still correct.
forceField->setParticleParameters(0, 0.4, 0.25, 1);
forceField->updateParametersInContext(context);
state = context.getState(State::Energy);
bornRadius = 0.25-0.009; // dielectric offset
bornEnergy = (-0.4*0.4/(8*PI_M*eps0))*(1.0/forceField->getSoluteDielectric()-1.0/forceField->getSolventDielectric())/bornRadius;
extendedRadius = 0.25+0.14;
nonpolarEnergy = 4*PI_M*2.25936*extendedRadius*extendedRadius*std::pow(0.25/bornRadius, 6.0);
ASSERT_EQUAL_TOL((bornEnergy+nonpolarEnergy), state.getPotentialEnergy(), 0.01);
}
void testGlobalSettings() {
System system;
system.addParticle(2.0);
LangevinIntegrator integrator(0, 0.1, 0.01);
GBSAOBCForce* forceField = new GBSAOBCForce();
forceField->addParticle(0.5, 0.15, 1);
const double soluteDielectric = 2.1;
const double solventDielectric = 35.0;
const double surfaceAreaEnergy = 0.75;
forceField->setSoluteDielectric(soluteDielectric);
forceField->setSolventDielectric(solventDielectric);
forceField->setSurfaceAreaEnergy(surfaceAreaEnergy);
system.addForce(forceField);
ASSERT(!forceField->usesPeriodicBoundaryConditions());
ASSERT(!system.usesPeriodicBoundaryConditions());
Context context(system, integrator, platform);
vector<Vec3> positions(1);
positions[0] = Vec3(0, 0, 0);
context.setPositions(positions);
State state = context.getState(State::Energy);
double bornRadius = 0.15-0.009; // dielectric offset
double eps0 = EPSILON0;
double bornEnergy = (-0.5*0.5/(8*PI_M*eps0))*(1.0/soluteDielectric-1.0/solventDielectric)/bornRadius;
double extendedRadius = 0.15+0.14; // probe radius
double nonpolarEnergy = 4*PI_M*surfaceAreaEnergy*extendedRadius*extendedRadius*std::pow(0.15/bornRadius, 6.0);
ASSERT_EQUAL_TOL((bornEnergy+nonpolarEnergy), state.getPotentialEnergy(), 0.01);
}
void testCutoffAndPeriodic() {
System system;
system.addParticle(1.0);
system.addParticle(1.0);
LangevinIntegrator integrator(0, 0.1, 0.01);
GBSAOBCForce* gbsa = new GBSAOBCForce();
NonbondedForce* nonbonded = new NonbondedForce();
gbsa->addParticle(-1, 0.15, 1);
nonbonded->addParticle(-1, 1, 0);
gbsa->addParticle(1, 0.15, 1);
nonbonded->addParticle(1, 1, 0);
const double cutoffDistance = 3.0;
const double boxSize = 10.0;
nonbonded->setCutoffDistance(cutoffDistance);
gbsa->setCutoffDistance(cutoffDistance);
system.setDefaultPeriodicBoxVectors(Vec3(boxSize, 0, 0), Vec3(0, boxSize, 0), Vec3(0, 0, boxSize));
system.addForce(gbsa);
system.addForce(nonbonded);
vector<Vec3> positions(2);
positions[0] = Vec3(0, 0, 0);
positions[1] = Vec3(2, 0, 0);
// Calculate the forces for both cutoff and periodic with two different atom positions.
nonbonded->setNonbondedMethod(NonbondedForce::CutoffNonPeriodic);
gbsa->setNonbondedMethod(GBSAOBCForce::CutoffNonPeriodic);
ASSERT(!nonbonded->usesPeriodicBoundaryConditions());
ASSERT(!gbsa->usesPeriodicBoundaryConditions());
ASSERT(!system.usesPeriodicBoundaryConditions());
Context context(system, integrator, platform);
context.setPositions(positions);
State state1 = context.getState(State::Forces);
nonbonded->setNonbondedMethod(NonbondedForce::CutoffPeriodic);
gbsa->setNonbondedMethod(GBSAOBCForce::CutoffPeriodic);
ASSERT(nonbonded->usesPeriodicBoundaryConditions());
ASSERT(gbsa->usesPeriodicBoundaryConditions());
ASSERT(system.usesPeriodicBoundaryConditions());
context.reinitialize();
context.setPositions(positions);
State state2 = context.getState(State::Forces);
positions[1][0]+= boxSize;
nonbonded->setNonbondedMethod(NonbondedForce::CutoffNonPeriodic);
gbsa->setNonbondedMethod(GBSAOBCForce::CutoffNonPeriodic);
ASSERT(!nonbonded->usesPeriodicBoundaryConditions());
ASSERT(!gbsa->usesPeriodicBoundaryConditions());
ASSERT(!system.usesPeriodicBoundaryConditions());
context.reinitialize();
context.setPositions(positions);
State state3 = context.getState(State::Forces);
nonbonded->setNonbondedMethod(NonbondedForce::CutoffPeriodic);
gbsa->setNonbondedMethod(GBSAOBCForce::CutoffPeriodic);
ASSERT(nonbonded->usesPeriodicBoundaryConditions());
ASSERT(gbsa->usesPeriodicBoundaryConditions());
ASSERT(system.usesPeriodicBoundaryConditions());
context.reinitialize();
context.setPositions(positions);
State state4 = context.getState(State::Forces);
// All forces should be identical, exception state3 which should be zero.
ASSERT_EQUAL_VEC(state1.getForces()[0], state2.getForces()[0], 0.01);
ASSERT_EQUAL_VEC(state1.getForces()[1], state2.getForces()[1], 0.01);
ASSERT_EQUAL_VEC(state1.getForces()[0], state4.getForces()[0], 0.01);
ASSERT_EQUAL_VEC(state1.getForces()[1], state4.getForces()[1], 0.01);
ASSERT_EQUAL_VEC(state3.getForces()[0], Vec3(0, 0, 0), 0.01);
ASSERT_EQUAL_VEC(state3.getForces()[1], Vec3(0, 0, 0), 0.01);
}
void testForce() {
const int numParticles = 10;
System system;
LangevinIntegrator integrator(0, 0.1, 0.01);
GBSAOBCForce* forceField = new GBSAOBCForce();
for (int i = 0; i < numParticles; ++i) {
system.addParticle(1.0);
forceField->addParticle(i%2 == 0 ? -1 : 1, 0.15, 1);
}
system.addForce(forceField);
Context context(system, integrator, platform);
// Set random positions for all the particles.
vector<Vec3> positions(numParticles);
OpenMM_SFMT::SFMT sfmt;
init_gen_rand(0, sfmt);
for (int i = 0; i < numParticles; ++i)
positions[i] = Vec3(5.0*genrand_real2(sfmt), 5.0*genrand_real2(sfmt), 5.0*genrand_real2(sfmt));
context.setPositions(positions);
State state = context.getState(State::Forces | State::Energy);
// 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 < numParticles; ++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;
vector<Vec3> positions2(numParticles), positions3(numParticles);
for (int i = 0; i < numParticles; ++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);
State state2 = context.getState(State::Energy);
context.setPositions(positions3);
State state3 = context.getState(State::Energy);
ASSERT_EQUAL_TOL(norm, (state2.getPotentialEnergy()-state3.getPotentialEnergy())/delta, 1e-3)
}
int main() {
try {
testSingleParticle();
testGlobalSettings();
testCutoffAndPeriodic();
testForce();
}
catch(const exception& e) {
cout << "exception: " << e.what() << endl;
return 1;
}
cout << "Done" << endl;
return 0;
void runPlatformTests() {
}
......@@ -6,7 +6,7 @@
* Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org. *
* *
* Portions copyright (c) 2008 Stanford University and the Authors. *
* Portions copyright (c) 2015 Stanford University and the Authors. *
* Authors: Peter Eastman *
* Contributors: *
* *
......@@ -29,85 +29,8 @@
* USE OR OTHER DEALINGS IN THE SOFTWARE. *
* -------------------------------------------------------------------------- */
/**
* This tests all the different force terms in the reference implementation of HarmonicAngleForce.
*/
#include "ReferenceTests.h"
#include "TestHarmonicAngleForce.h"
#include "openmm/internal/AssertionUtilities.h"
#include "openmm/Context.h"
#include "ReferencePlatform.h"
#include "openmm/HarmonicAngleForce.h"
#include "openmm/System.h"
#include "openmm/VerletIntegrator.h"
#include "SimTKOpenMMRealType.h"
#include <iostream>
#include <vector>
using namespace OpenMM;
using namespace std;
ReferencePlatform platform;
const double TOL = 1e-5;
void testAngles() {
System system;
system.addParticle(1.0);
system.addParticle(1.0);
system.addParticle(1.0);
system.addParticle(1.0);
VerletIntegrator integrator(0.01);
HarmonicAngleForce* forceField = new HarmonicAngleForce();
forceField->addAngle(0, 1, 2, PI_M/3, 1.1);
forceField->addAngle(1, 2, 3, PI_M/2, 1.2);
system.addForce(forceField);
ASSERT(!forceField->usesPeriodicBoundaryConditions());
ASSERT(!system.usesPeriodicBoundaryConditions());
Context context(system, integrator, platform);
vector<Vec3> positions(4);
positions[0] = Vec3(0, 1, 0);
positions[1] = Vec3(0, 0, 0);
positions[2] = Vec3(1, 0, 0);
positions[3] = Vec3(2, 1, 0);
context.setPositions(positions);
State state = context.getState(State::Forces | State::Energy);
{
const vector<Vec3>& forces = state.getForces();
double torque1 = 1.1*PI_M/6;
double torque2 = 1.2*PI_M/4;
ASSERT_EQUAL_VEC(Vec3(torque1, 0, 0), forces[0], TOL);
ASSERT_EQUAL_VEC(Vec3(-0.5*torque2, 0.5*torque2, 0), forces[3], TOL); // reduced by sqrt(2) due to the bond length, another sqrt(2) due to the angle
ASSERT_EQUAL_VEC(Vec3(forces[0][0]+forces[1][0]+forces[2][0]+forces[3][0], forces[0][1]+forces[1][1]+forces[2][1]+forces[3][1], forces[0][2]+forces[1][2]+forces[2][2]+forces[3][2]), Vec3(0, 0, 0), TOL);
ASSERT_EQUAL_TOL(0.5*1.1*(PI_M/6)*(PI_M/6) + 0.5*1.2*(PI_M/4)*(PI_M/4), state.getPotentialEnergy(), TOL);
}
// Try changing the angle parameters and make sure it's still correct.
forceField->setAngleParameters(0, 0, 1, 2, PI_M/3.1, 1.3);
forceField->setAngleParameters(1, 1, 2, 3, PI_M/2.1, 1.4);
forceField->updateParametersInContext(context);
state = context.getState(State::Forces | State::Energy);
{
const vector<Vec3>& forces = state.getForces();
double dtheta1 = (PI_M/2)-(PI_M/3.1);
double dtheta2 = (3*PI_M/4)-(PI_M/2.1);
double torque1 = 1.3*dtheta1;
double torque2 = 1.4*dtheta2;
ASSERT_EQUAL_VEC(Vec3(torque1, 0, 0), forces[0], TOL);
ASSERT_EQUAL_VEC(Vec3(-0.5*torque2, 0.5*torque2, 0), forces[3], TOL); // reduced by sqrt(2) due to the bond length, another sqrt(2) due to the angle
ASSERT_EQUAL_VEC(Vec3(forces[0][0]+forces[1][0]+forces[2][0]+forces[3][0], forces[0][1]+forces[1][1]+forces[2][1]+forces[3][1], forces[0][2]+forces[1][2]+forces[2][2]+forces[3][2]), Vec3(0, 0, 0), TOL);
ASSERT_EQUAL_TOL(0.5*1.3*dtheta1*dtheta1 + 0.5*1.4*dtheta2*dtheta2, state.getPotentialEnergy(), TOL);
}
}
int main() {
try {
testAngles();
}
catch(const exception& e) {
cout << "exception: " << e.what() << endl;
return 1;
}
cout << "Done" << endl;
return 0;
void runPlatformTests() {
}
......@@ -6,7 +6,7 @@
* Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org. *
* *
* Portions copyright (c) 2008 Stanford University and the Authors. *
* Portions copyright (c) 2015 Stanford University and the Authors. *
* Authors: Peter Eastman *
* Contributors: *
* *
......@@ -29,79 +29,8 @@
* USE OR OTHER DEALINGS IN THE SOFTWARE. *
* -------------------------------------------------------------------------- */
/**
* This tests all the different force terms in the reference implementation of HarmonicBondForce.
*/
#include "ReferenceTests.h"
#include "TestHarmonicBondForce.h"
#include "openmm/internal/AssertionUtilities.h"
#include "openmm/Context.h"
#include "ReferencePlatform.h"
#include "openmm/HarmonicBondForce.h"
#include "openmm/System.h"
#include "openmm/VerletIntegrator.h"
#include "SimTKOpenMMRealType.h"
#include <iostream>
#include <vector>
using namespace OpenMM;
using namespace std;
ReferencePlatform platform;
const double TOL = 1e-5;
void testBonds() {
System system;
system.addParticle(1.0);
system.addParticle(1.0);
system.addParticle(1.0);
VerletIntegrator integrator(0.01);
HarmonicBondForce* forceField = new HarmonicBondForce();
forceField->addBond(0, 1, 1.5, 0.8);
forceField->addBond(1, 2, 1.2, 0.7);
system.addForce(forceField);
ASSERT(!forceField->usesPeriodicBoundaryConditions());
ASSERT(!system.usesPeriodicBoundaryConditions());
Context context(system, integrator, platform);
vector<Vec3> positions(3);
positions[0] = Vec3(0, 2, 0);
positions[1] = Vec3(0, 0, 0);
positions[2] = Vec3(1, 0, 0);
context.setPositions(positions);
State state = context.getState(State::Forces | State::Energy);
{
const vector<Vec3>& forces = state.getForces();
ASSERT_EQUAL_VEC(Vec3(0, -0.8*0.5, 0), forces[0], TOL);
ASSERT_EQUAL_VEC(Vec3(0.7*0.2, 0, 0), forces[2], TOL);
ASSERT_EQUAL_VEC(Vec3(-forces[0][0]-forces[2][0], -forces[0][1]-forces[2][1], -forces[0][2]-forces[2][2]), forces[1], TOL);
ASSERT_EQUAL_TOL(0.5*0.8*0.5*0.5 + 0.5*0.7*0.2*0.2, state.getPotentialEnergy(), TOL);
}
// Try changing the bond parameters and make sure it's still correct.
forceField->setBondParameters(0, 0, 1, 1.6, 0.9);
forceField->setBondParameters(1, 1, 2, 1.3, 0.8);
forceField->updateParametersInContext(context);
state = context.getState(State::Forces | State::Energy);
{
const vector<Vec3>& forces = state.getForces();
ASSERT_EQUAL_VEC(Vec3(0, -0.9*0.4, 0), forces[0], TOL);
ASSERT_EQUAL_VEC(Vec3(0.8*0.3, 0, 0), forces[2], TOL);
ASSERT_EQUAL_VEC(Vec3(-forces[0][0]-forces[2][0], -forces[0][1]-forces[2][1], -forces[0][2]-forces[2][2]), forces[1], TOL);
ASSERT_EQUAL_TOL(0.5*0.9*0.4*0.4 + 0.5*0.8*0.3*0.3, state.getPotentialEnergy(), TOL);
}
}
int main() {
try {
cout << "Running test..." << endl;
testBonds();
}
catch(const exception& e) {
cout << "exception: " << e.what() << endl;
cout << "FAIL - ERROR. Test failed." << endl;
return 1;
}
cout << "PASS - Test succeeded." << endl;
return 0;
void runPlatformTests() {
}
......@@ -6,7 +6,7 @@
* Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org. *
* *
* Portions copyright (c) 2008 Stanford University and the Authors. *
* Portions copyright (c) 2015 Stanford University and the Authors. *
* Authors: Peter Eastman *
* Contributors: *
* *
......@@ -29,248 +29,8 @@
* USE OR OTHER DEALINGS IN THE SOFTWARE. *
* -------------------------------------------------------------------------- */
/**
* This tests the reference implementation of LangevinIntegrator.
*/
#include "ReferenceTests.h"
#include "TestLangevinIntegrator.h"
#include "openmm/internal/AssertionUtilities.h"
#include "openmm/Context.h"
#include "ReferencePlatform.h"
#include "openmm/HarmonicBondForce.h"
#include "openmm/NonbondedForce.h"
#include "openmm/System.h"
#include "openmm/LangevinIntegrator.h"
#include "SimTKOpenMMRealType.h"
#include "sfmt/SFMT.h"
#include <iostream>
#include <vector>
using namespace OpenMM;
using namespace std;
ReferencePlatform platform;
const double TOL = 1e-5;
void testSingleBond() {
System system;
system.addParticle(2.0);
system.addParticle(2.0);
LangevinIntegrator integrator(0, 0.1, 0.01);
HarmonicBondForce* forceField = new HarmonicBondForce();
forceField->addBond(0, 1, 1.5, 1);
system.addForce(forceField);
Context context(system, integrator, platform);
vector<Vec3> positions(2);
positions[0] = Vec3(-1, 0, 0);
positions[1] = Vec3(1, 0, 0);
context.setPositions(positions);
// This is simply a damped harmonic oscillator, so compare it to the analytical solution.
double freq = std::sqrt(1-0.05*0.05);
for (int i = 0; i < 1000; ++i) {
State state = context.getState(State::Positions | State::Velocities);
double time = state.getTime();
double expectedDist = 1.5+0.5*std::exp(-0.05*time)*std::cos(freq*time);
ASSERT_EQUAL_VEC(Vec3(-0.5*expectedDist, 0, 0), state.getPositions()[0], 0.02);
ASSERT_EQUAL_VEC(Vec3(0.5*expectedDist, 0, 0), state.getPositions()[1], 0.02);
double expectedSpeed = -0.5*std::exp(-0.05*time)*(0.05*std::cos(freq*time)+freq*std::sin(freq*time));
ASSERT_EQUAL_VEC(Vec3(-0.5*expectedSpeed, 0, 0), state.getVelocities()[0], 0.02);
ASSERT_EQUAL_VEC(Vec3(0.5*expectedSpeed, 0, 0), state.getVelocities()[1], 0.02);
integrator.step(1);
}
// Not set the friction to a tiny value and see if it conserves energy.
integrator.setFriction(5e-5);
context.setPositions(positions);
State state = context.getState(State::Energy);
double initialEnergy = state.getKineticEnergy()+state.getPotentialEnergy();
for (int i = 0; i < 1000; ++i) {
state = context.getState(State::Energy);
double energy = state.getKineticEnergy()+state.getPotentialEnergy();
ASSERT_EQUAL_TOL(initialEnergy, energy, 0.01);
integrator.step(1);
}
}
void testTemperature() {
const int numParticles = 8;
const double temp = 100.0;
System system;
LangevinIntegrator integrator(temp, 2.0, 0.01);
NonbondedForce* forceField = new NonbondedForce();
for (int i = 0; i < numParticles; ++i) {
system.addParticle(2.0);
forceField->addParticle((i%2 == 0 ? 1.0 : -1.0), 1.0, 5.0);
}
system.addForce(forceField);
Context context(system, integrator, platform);
vector<Vec3> positions(numParticles);
for (int i = 0; i < numParticles; ++i)
positions[i] = Vec3((i%2 == 0 ? 2 : -2), (i%4 < 2 ? 2 : -2), (i < 4 ? 2 : -2));
context.setPositions(positions);
// Let it equilibrate.
integrator.step(10000);
// Now run it for a while and see if the temperature is correct.
double ke = 0.0;
for (int i = 0; i < 10000; ++i) {
State state = context.getState(State::Energy);
ke += state.getKineticEnergy();
integrator.step(1);
}
ke /= 10000;
double expected = 0.5*numParticles*3*BOLTZ*temp;
ASSERT_USUALLY_EQUAL_TOL(expected, ke, 6/std::sqrt(10000.0));
}
void testConstraints() {
const int numParticles = 8;
const double temp = 100.0;
System system;
LangevinIntegrator integrator(temp, 2.0, 0.01);
integrator.setConstraintTolerance(1e-5);
NonbondedForce* forceField = new NonbondedForce();
for (int i = 0; i < numParticles; ++i) {
system.addParticle(10.0);
forceField->addParticle((i%2 == 0 ? 0.2 : -0.2), 0.5, 5.0);
}
for (int i = 0; i < numParticles-1; ++i)
system.addConstraint(i, i+1, 1.0);
system.addForce(forceField);
Context context(system, integrator, platform);
vector<Vec3> positions(numParticles);
vector<Vec3> velocities(numParticles);
OpenMM_SFMT::SFMT sfmt;
init_gen_rand(0, sfmt);
for (int i = 0; i < numParticles; ++i) {
positions[i] = Vec3(i/2, (i+1)/2, 0);
velocities[i] = Vec3(genrand_real2(sfmt)-0.5, genrand_real2(sfmt)-0.5, genrand_real2(sfmt)-0.5);
}
context.setPositions(positions);
context.setVelocities(velocities);
// Simulate it and see whether the constraints remain satisfied.
for (int i = 0; i < 1000; ++i) {
State state = context.getState(State::Positions);
for (int j = 0; j < numParticles-1; ++j) {
Vec3 p1 = state.getPositions()[j];
Vec3 p2 = state.getPositions()[j+1];
double dist = std::sqrt((p1[0]-p2[0])*(p1[0]-p2[0])+(p1[1]-p2[1])*(p1[1]-p2[1])+(p1[2]-p2[2])*(p1[2]-p2[2]));
ASSERT_EQUAL_TOL(1.0, dist, 2e-5);
}
integrator.step(1);
}
}
void testConstrainedMasslessParticles() {
System system;
system.addParticle(0.0);
system.addParticle(1.0);
system.addConstraint(0, 1, 1.5);
vector<Vec3> positions(2);
positions[0] = Vec3(-1, 0, 0);
positions[1] = Vec3(1, 0, 0);
LangevinIntegrator integrator(300.0, 2.0, 0.01);
bool failed = false;
try {
// This should throw an exception.
Context context(system, integrator, platform);
}
catch (exception& ex) {
failed = true;
}
ASSERT(failed);
// Now make both particles massless, which should work.
system.setParticleMass(1, 0.0);
Context context(system, integrator, platform);
context.setPositions(positions);
context.setVelocitiesToTemperature(300.0);
integrator.step(1);
State state = context.getState(State::Velocities | State::Positions);
ASSERT_EQUAL(0.0, state.getVelocities()[0][0]);
}
void testRandomSeed() {
const int numParticles = 8;
const double temp = 100.0;
const double collisionFreq = 10.0;
System system;
LangevinIntegrator integrator(temp, 2.0, 0.01);
NonbondedForce* forceField = new NonbondedForce();
for (int i = 0; i < numParticles; ++i) {
system.addParticle(2.0);
forceField->addParticle((i%2 == 0 ? 1.0 : -1.0), 1.0, 5.0);
}
system.addForce(forceField);
vector<Vec3> positions(numParticles);
vector<Vec3> velocities(numParticles);
for (int i = 0; i < numParticles; ++i) {
positions[i] = Vec3((i%2 == 0 ? 2 : -2), (i%4 < 2 ? 2 : -2), (i < 4 ? 2 : -2));
velocities[i] = Vec3(0, 0, 0);
}
// Try twice with the same random seed.
integrator.setRandomNumberSeed(5);
Context context(system, integrator, platform);
context.setPositions(positions);
context.setVelocities(velocities);
integrator.step(10);
State state1 = context.getState(State::Positions);
context.reinitialize();
context.setPositions(positions);
context.setVelocities(velocities);
integrator.step(10);
State state2 = context.getState(State::Positions);
// Try twice with a different random seed.
integrator.setRandomNumberSeed(10);
context.reinitialize();
context.setPositions(positions);
context.setVelocities(velocities);
integrator.step(10);
State state3 = context.getState(State::Positions);
context.reinitialize();
context.setPositions(positions);
context.setVelocities(velocities);
integrator.step(10);
State state4 = context.getState(State::Positions);
// Compare the results.
for (int i = 0; i < numParticles; i++) {
for (int j = 0; j < 3; j++) {
ASSERT(state1.getPositions()[i][j] == state2.getPositions()[i][j]);
ASSERT(state3.getPositions()[i][j] == state4.getPositions()[i][j]);
ASSERT(state1.getPositions()[i][j] != state3.getPositions()[i][j]);
}
}
}
int main() {
try {
testSingleBond();
testTemperature();
testConstraints();
testConstrainedMasslessParticles();
testRandomSeed();
}
catch(const exception& e) {
cout << "exception: " << e.what() << endl;
return 1;
}
cout << "Done" << endl;
return 0;
void runPlatformTests() {
}
/* -------------------------------------------------------------------------- *
* OpenMM *
* -------------------------------------------------------------------------- *
......@@ -7,7 +6,7 @@
* Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org. *
* *
* Portions copyright (c) 2010-2015 Stanford University and the Authors. *
* Portions copyright (c) 2015 Stanford University and the Authors. *
* Authors: Peter Eastman *
* Contributors: *
* *
......@@ -30,185 +29,8 @@
* USE OR OTHER DEALINGS IN THE SOFTWARE. *
* -------------------------------------------------------------------------- */
#include "openmm/internal/AssertionUtilities.h"
#include "ReferencePlatform.h"
#include "openmm/Context.h"
#include "openmm/HarmonicBondForce.h"
#include "openmm/LocalEnergyMinimizer.h"
#include "openmm/NonbondedForce.h"
#include "openmm/VerletIntegrator.h"
#include "openmm/VirtualSite.h"
#include "sfmt/SFMT.h"
#include <iostream>
#include <vector>
using namespace OpenMM;
using namespace std;
ReferencePlatform platform;
void testHarmonicBonds() {
const int numParticles = 10;
System system;
HarmonicBondForce* bonds = new HarmonicBondForce();
system.addForce(bonds);
// Create a chain of particles connected by harmonic bonds.
vector<Vec3> positions(numParticles);
for (int i = 0; i < numParticles; i++) {
system.addParticle(1.0);
positions[i] = Vec3(i, 0, 0);
if (i > 0)
bonds->addBond(i-1, i, 1+0.1*i, 1);
}
// Minimize it and check that all bonds are at their equilibrium distances.
VerletIntegrator integrator(0.01);
Context context(system, integrator, platform);
context.setPositions(positions);
LocalEnergyMinimizer::minimize(context, 1e-5);
State state = context.getState(State::Positions);
for (int i = 1; i < numParticles; i++) {
Vec3 delta = state.getPositions()[i]-state.getPositions()[i-1];
ASSERT_EQUAL_TOL(1+0.1*i, sqrt(delta.dot(delta)), 1e-4);
}
}
void testLargeSystem() {
const int numMolecules = 25;
const int numParticles = numMolecules*2;
const double cutoff = 2.0;
const double boxSize = 4.0;
const double tolerance = 5;
System system;
system.setDefaultPeriodicBoxVectors(Vec3(boxSize, 0, 0), Vec3(0, boxSize, 0), Vec3(0, 0, boxSize));
NonbondedForce* nonbonded = new NonbondedForce();
nonbonded->setCutoffDistance(cutoff);
nonbonded->setNonbondedMethod(NonbondedForce::CutoffPeriodic);
system.addForce(nonbonded);
// 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(-1.0, 0.2, 0.2);
nonbonded->addParticle(1.0, 0.2, 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);
}
// Minimize it and verify that the energy has decreased.
VerletIntegrator integrator(0.01);
Context context(system, integrator, platform);
context.setPositions(positions);
State initialState = context.getState(State::Forces | State::Energy);
LocalEnergyMinimizer::minimize(context, tolerance);
State finalState = context.getState(State::Forces | State::Energy | State::Positions);
ASSERT(finalState.getPotentialEnergy() < initialState.getPotentialEnergy());
// Compute the force magnitude, subtracting off any component parallel to a constraint, and
// check that it satisfies the requested tolerance.
double forceNorm = 0.0;
for (int i = 0; i < numParticles; i += 2) {
Vec3 dir = finalState.getPositions()[i+1]-finalState.getPositions()[i];
double distance = sqrt(dir.dot(dir));
dir *= 1.0/distance;
Vec3 f = finalState.getForces()[i];
f -= dir*dir.dot(f);
forceNorm += f.dot(f);
f = finalState.getForces()[i+1];
f -= dir*dir.dot(f);
forceNorm += f.dot(f);
}
forceNorm = sqrt(forceNorm/(5*numMolecules));
ASSERT(forceNorm < 2*tolerance);
}
void testVirtualSites() {
const int numMolecules = 25;
const int numParticles = numMolecules*3;
const double cutoff = 2.0;
const double boxSize = 4.0;
const double tolerance = 5;
System system;
system.setDefaultPeriodicBoxVectors(Vec3(boxSize, 0, 0), Vec3(0, boxSize, 0), Vec3(0, 0, boxSize));
NonbondedForce* nonbonded = new NonbondedForce();
nonbonded->setCutoffDistance(cutoff);
nonbonded->setNonbondedMethod(NonbondedForce::CutoffPeriodic);
system.addForce(nonbonded);
// 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);
system.addParticle(0.0);
nonbonded->addParticle(-1.0, 0.2, 0.2);
nonbonded->addParticle(0.5, 0.2, 0.2);
nonbonded->addParticle(0.5, 0.2, 0.2);
positions[3*i] = Vec3(boxSize*genrand_real2(sfmt), boxSize*genrand_real2(sfmt), boxSize*genrand_real2(sfmt));
positions[3*i+1] = Vec3(positions[3*i][0]+1.0, positions[3*i][1], positions[3*i][2]);
positions[3*i+2] = Vec3();
system.addConstraint(3*i, 3*i+1, 1.0);
system.setVirtualSite(3*i+2, new TwoParticleAverageSite(3*i, 3*i+1, 0.5, 0.5));
}
// Minimize it and verify that the energy has decreased.
VerletIntegrator integrator(0.01);
Context context(system, integrator, platform);
context.setPositions(positions);
context.applyConstraints(1e-5);
State initialState = context.getState(State::Forces | State::Energy);
LocalEnergyMinimizer::minimize(context, tolerance);
State finalState = context.getState(State::Forces | State::Energy | State::Positions);
ASSERT(finalState.getPotentialEnergy() < initialState.getPotentialEnergy());
// Compute the force magnitude, subtracting off any component parallel to a constraint, and
// check that it satisfies the requested tolerance.
double forceNorm = 0.0;
for (int i = 0; i < numParticles; i += 3) {
Vec3 dir = finalState.getPositions()[i+1]-finalState.getPositions()[i];
double distance = sqrt(dir.dot(dir));
dir *= 1.0/distance;
Vec3 f = finalState.getForces()[i];
f -= dir*dir.dot(f);
forceNorm += f.dot(f);
f = finalState.getForces()[i+1];
f -= dir*dir.dot(f);
forceNorm += f.dot(f);
// Check the virtual site location.
ASSERT_EQUAL_VEC((finalState.getPositions()[i+1]+finalState.getPositions()[i])*0.5, finalState.getPositions()[i+2], 1e-5);
}
forceNorm = sqrt(forceNorm/(5*numMolecules));
ASSERT(forceNorm < 2*tolerance);
}
#include "ReferenceTests.h"
#include "TestLocalEnergyMinimizer.h"
int main() {
try {
testHarmonicBonds();
testLargeSystem();
testVirtualSites();
}
catch(const exception& e) {
cout << "exception: " << e.what() << endl;
return 1;
}
cout << "Done" << endl;
return 0;
void runPlatformTests() {
}
......@@ -6,8 +6,8 @@
* Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org. *
* *
* Portions copyright (c) 2008-2013 Stanford University and the Authors. *
* Authors: Peter Eastman, Lee-Ping Wang *
* Portions copyright (c) 2015 Stanford University and the Authors. *
* Authors: Peter Eastman *
* Contributors: *
* *
* Permission is hereby granted, free of charge, to any person obtaining a *
......@@ -29,454 +29,8 @@
* USE OR OTHER DEALINGS IN THE SOFTWARE. *
* -------------------------------------------------------------------------- */
/**
* This tests the reference implementation of MonteCarloAnisotropicBarostat.
*/
#include "ReferenceTests.h"
#include "TestMonteCarloAnisotropicBarostat.h"
#include "openmm/internal/AssertionUtilities.h"
#include "openmm/CustomExternalForce.h"
#include "openmm/MonteCarloBarostat.h"
#include "openmm/MonteCarloAnisotropicBarostat.h"
#include "openmm/Context.h"
#include "ReferencePlatform.h"
#include "openmm/NonbondedForce.h"
#include "openmm/System.h"
#include "openmm/LangevinIntegrator.h"
#include "openmm/VerletIntegrator.h"
#include "sfmt/SFMT.h"
#include "SimTKOpenMMRealType.h"
#include <iostream>
#include <vector>
using namespace OpenMM;
using namespace std;
ReferencePlatform platform;
void testIdealGas() {
const int numParticles = 64;
const int frequency = 10;
const int steps = 1000;
const double pressure = 1.5;
const double pressureInMD = pressure*(AVOGADRO*1e-25); // pressure in kJ/mol/nm^3
const double temp[] = {300.0, 600.0, 1000.0};
const double initialVolume = numParticles*BOLTZ*temp[1]/pressureInMD;
const double initialLength = std::pow(initialVolume, 1.0/3.0);
// Create a gas of noninteracting particles.
System system;
system.setDefaultPeriodicBoxVectors(Vec3(initialLength, 0, 0), Vec3(0, 0.5*initialLength, 0), Vec3(0, 0, 2*initialLength));
vector<Vec3> positions(numParticles);
OpenMM_SFMT::SFMT sfmt;
init_gen_rand(0, sfmt);
for (int i = 0; i < numParticles; ++i) {
system.addParticle(1.0);
positions[i] = Vec3(initialLength*genrand_real2(sfmt), 0.5*initialLength*genrand_real2(sfmt), 2*initialLength*genrand_real2(sfmt));
}
MonteCarloAnisotropicBarostat* barostat = new MonteCarloAnisotropicBarostat(Vec3(pressure, pressure, pressure), temp[0], true, true, true, frequency);
system.addForce(barostat);
ASSERT(barostat->usesPeriodicBoundaryConditions());
ASSERT(system.usesPeriodicBoundaryConditions());
// Test it for three different temperatures.
for (int i = 0; i < 3; i++) {
barostat->setTemperature(temp[i]);
LangevinIntegrator integrator(temp[i], 0.1, 0.01);
Context context(system, integrator, platform);
context.setPositions(positions);
// Let it equilibrate.
integrator.step(10000);
// 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;
double expected = (numParticles+1)*BOLTZ*temp[i]/pressureInMD;
ASSERT_USUALLY_EQUAL_TOL(expected, volume, 3/std::sqrt((double) steps));
}
}
void testIdealGasAxis(int axis) {
// Test scaling just one axis.
const int numParticles = 64;
const int frequency = 10;
const int steps = 1000;
const double pressure = 1.5;
const double pressureInMD = pressure*(AVOGADRO*1e-25); // pressure in kJ/mol/nm^3
const double temp[] = {300.0, 600.0, 1000.0};
const double initialVolume = numParticles*BOLTZ*temp[1]/pressureInMD;
const double initialLength = std::pow(initialVolume, 1.0/3.0);
const bool scaleX = (axis == 0);
const bool scaleY = (axis == 1);
const bool scaleZ = (axis == 2);
double boxX;
double boxY;
double boxZ;
// Create a gas of noninteracting particles.
System system;
system.setDefaultPeriodicBoxVectors(Vec3(initialLength, 0, 0), Vec3(0, 0.5*initialLength, 0), Vec3(0, 0, 2*initialLength));
vector<Vec3> positions(numParticles);
OpenMM_SFMT::SFMT sfmt;
init_gen_rand(0, sfmt);
for (int i = 0; i < numParticles; ++i) {
system.addParticle(1.0);
positions[i] = Vec3(initialLength*genrand_real2(sfmt), 0.5*initialLength*genrand_real2(sfmt), 2*initialLength*genrand_real2(sfmt));
}
MonteCarloAnisotropicBarostat* barostat = new MonteCarloAnisotropicBarostat(Vec3(pressure, pressure, pressure), temp[0], scaleX, scaleY, scaleZ, frequency);
system.addForce(barostat);
ASSERT(barostat->usesPeriodicBoundaryConditions());
ASSERT(system.usesPeriodicBoundaryConditions());
// Test it for three different temperatures.
for (int i = 0; i < 3; i++) {
barostat->setTemperature(temp[i]);
LangevinIntegrator integrator(temp[i], 0.1, 0.01);
Context context(system, integrator, platform);
context.setPositions(positions);
// Let it equilibrate.
integrator.step(10000);
// 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]);
boxX = box[0][0];
boxY = box[1][1];
boxZ = box[2][2];
volume += box[0][0]*box[1][1]*box[2][2];
integrator.step(frequency);
}
volume /= steps;
double expected = (numParticles+1)*BOLTZ*temp[i]/pressureInMD;
ASSERT_USUALLY_EQUAL_TOL(expected, volume, 3/std::sqrt((double) steps));
if (!scaleX) {
ASSERT(boxX == initialLength);
}
if (!scaleY) {
ASSERT(boxY == 0.5*initialLength);
}
if (!scaleZ) {
ASSERT(boxZ == 2*initialLength);
}
}
void runPlatformTests() {
}
void testRandomSeed() {
const int numParticles = 8;
const double temp = 100.0;
const double pressure = 1.5;
System system;
system.setDefaultPeriodicBoxVectors(Vec3(8, 0, 0), Vec3(0, 8, 0), Vec3(0, 0, 8));
VerletIntegrator integrator(0.01);
NonbondedForce* forceField = new NonbondedForce();
forceField->setNonbondedMethod(NonbondedForce::CutoffPeriodic);
for (int i = 0; i < numParticles; ++i) {
system.addParticle(2.0);
forceField->addParticle((i%2 == 0 ? 1.0 : -1.0), 1.0, 5.0);
}
system.addForce(forceField);
MonteCarloAnisotropicBarostat* barostat = new MonteCarloAnisotropicBarostat(Vec3(pressure, pressure, pressure), temp, true, true, true, 1);
system.addForce(barostat);
ASSERT(barostat->usesPeriodicBoundaryConditions());
ASSERT(system.usesPeriodicBoundaryConditions());
vector<Vec3> positions(numParticles);
vector<Vec3> velocities(numParticles);
for (int i = 0; i < numParticles; ++i) {
positions[i] = Vec3((i%2 == 0 ? 2 : -2), (i%4 < 2 ? 2 : -2), (i < 4 ? 2 : -2));
velocities[i] = Vec3(0, 0, 0);
}
// Try twice with the same random seed.
barostat->setRandomNumberSeed(5);
Context context(system, integrator, platform);
context.setPositions(positions);
context.setVelocities(velocities);
integrator.step(10);
State state1 = context.getState(State::Positions);
context.reinitialize();
context.setPositions(positions);
context.setVelocities(velocities);
integrator.step(10);
State state2 = context.getState(State::Positions);
// Try twice with a different random seed.
barostat->setRandomNumberSeed(10);
context.reinitialize();
context.setPositions(positions);
context.setVelocities(velocities);
integrator.step(10);
State state3 = context.getState(State::Positions);
context.reinitialize();
context.setPositions(positions);
context.setVelocities(velocities);
integrator.step(10);
State state4 = context.getState(State::Positions);
// Compare the results.
for (int i = 0; i < numParticles; i++) {
for (int j = 0; j < 3; j++) {
ASSERT(state1.getPositions()[i][j] == state2.getPositions()[i][j]);
ASSERT(state3.getPositions()[i][j] == state4.getPositions()[i][j]);
ASSERT(state1.getPositions()[i][j] != state3.getPositions()[i][j]);
}
}
}
void testTriclinic() {
const int numParticles = 64;
const int frequency = 10;
const int steps = 1000;
const double pressure = 1.5;
const double pressureInMD = pressure*(AVOGADRO*1e-25); // pressure in kJ/mol/nm^3
const double temperature = 300.0;
const double initialVolume = numParticles*BOLTZ*temperature/pressureInMD;
const double initialLength = std::pow(initialVolume, 1.0/3.0);
// Create a gas of noninteracting particles.
System system;
Vec3 initialBox[3];
initialBox[0] = Vec3(initialLength, 0, 0);
initialBox[1] = Vec3(0.2*initialLength, initialLength, 0);
initialBox[2] = Vec3(0.1*initialLength, 0.3*initialLength, initialLength);
system.setDefaultPeriodicBoxVectors(initialBox[0], initialBox[1], initialBox[2]);
vector<Vec3> positions(numParticles);
OpenMM_SFMT::SFMT sfmt;
init_gen_rand(0, sfmt);
for (int i = 0; i < numParticles; ++i) {
system.addParticle(1.0);
positions[i] = Vec3(initialLength*genrand_real2(sfmt), initialLength*genrand_real2(sfmt), initialLength*genrand_real2(sfmt));
}
MonteCarloAnisotropicBarostat* barostat = new MonteCarloAnisotropicBarostat(Vec3(pressure, pressure, pressure), temperature, true, true, true, frequency);
system.addForce(barostat);
// Run a simulation
LangevinIntegrator integrator(temperature, 0.1, 0.01);
Context context(system, integrator, platform);
context.setPositions(positions);
// Let it equilibrate.
integrator.step(10000);
// 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;
double expected = (numParticles+1)*BOLTZ*temperature/pressureInMD;
ASSERT_USUALLY_EQUAL_TOL(expected, volume, 3/std::sqrt((double) steps));
// Make sure the box vectors have been scaled consistently.
State state = context.getState(State::Positions);
Vec3 box[3];
state.getPeriodicBoxVectors(box[0], box[1], box[2]);
double xscale = box[2][0]/(0.1*initialLength);
double yscale = box[2][1]/(0.3*initialLength);
double zscale = box[2][2]/(1.0*initialLength);
for (int i = 0; i < 3; i++) {
ASSERT_EQUAL_VEC(Vec3(xscale*initialBox[i][0], yscale*initialBox[i][1], zscale*initialBox[i][2]), box[i], 1e-5);
}
// The barostat should have put all particles inside the first periodic box. One integration step
// has happened since then, so they may have moved slightly outside it.
for (int i = 0; i < numParticles; i++) {
Vec3 pos = state.getPositions()[i];
ASSERT(pos[2]/box[2][2] > -1 && pos[2]/box[2][2] < 2);
pos -= box[2]*floor(pos[2]/box[2][2]);
ASSERT(pos[1]/box[1][1] > -1 && pos[1]/box[1][1] < 2);
pos -= box[1]*floor(pos[1]/box[1][1]);
ASSERT(pos[0]/box[0][0] > -1 && pos[0]/box[0][0] < 2);
}
}
/**
* 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));
}
int main() {
try {
testIdealGas();
testIdealGasAxis(0);
testIdealGasAxis(1);
testIdealGasAxis(2);
testRandomSeed();
testTriclinic();
//testEinsteinCrystal();
}
catch(const exception& e) {
cout << "exception: " << e.what() << endl;
return 1;
}
cout << "Done" << endl;
return 0;
}
......@@ -6,7 +6,7 @@
* Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org. *
* *
* Portions copyright (c) 2008-2010 Stanford University and the Authors. *
* Portions copyright (c) 2015 Stanford University and the Authors. *
* Authors: Peter Eastman *
* Contributors: *
* *
......@@ -29,196 +29,8 @@
* USE OR OTHER DEALINGS IN THE SOFTWARE. *
* -------------------------------------------------------------------------- */
/**
* This tests the reference implementation of MonteCarloBarostat.
*/
#include "ReferenceTests.h"
#include "TestMonteCarloBarostat.h"
#include "openmm/internal/AssertionUtilities.h"
#include "openmm/MonteCarloBarostat.h"
#include "openmm/Context.h"
#include "ReferencePlatform.h"
#include "openmm/NonbondedForce.h"
#include "openmm/System.h"
#include "openmm/LangevinIntegrator.h"
#include "openmm/VerletIntegrator.h"
#include "sfmt/SFMT.h"
#include "SimTKOpenMMRealType.h"
#include <iostream>
#include <vector>
using namespace OpenMM;
using namespace std;
ReferencePlatform platform;
void testChangingBoxSize() {
System system;
system.setDefaultPeriodicBoxVectors(Vec3(4, 0, 0), Vec3(0, 5, 0), Vec3(0, 0, 6));
system.addParticle(1.0);
NonbondedForce* nb = new NonbondedForce();
nb->setNonbondedMethod(NonbondedForce::CutoffPeriodic);
nb->setCutoffDistance(2.0);
nb->addParticle(1, 0.5, 0.5);
system.addForce(nb);
LangevinIntegrator integrator(300.0, 1.0, 0.01);
Context context(system, integrator, platform);
vector<Vec3> positions;
positions.push_back(Vec3());
context.setPositions(positions);
Vec3 x, y, z;
context.getState(State::Forces).getPeriodicBoxVectors(x, y, z);
ASSERT_EQUAL_VEC(Vec3(4, 0, 0), x, 0);
ASSERT_EQUAL_VEC(Vec3(0, 5, 0), y, 0);
ASSERT_EQUAL_VEC(Vec3(0, 0, 6), z, 0);
context.setPeriodicBoxVectors(Vec3(7, 0, 0), Vec3(0, 8, 0), Vec3(0, 0, 9));
context.getState(State::Forces).getPeriodicBoxVectors(x, y, z);
ASSERT_EQUAL_VEC(Vec3(7, 0, 0), x, 0);
ASSERT_EQUAL_VEC(Vec3(0, 8, 0), y, 0);
ASSERT_EQUAL_VEC(Vec3(0, 0, 9), z, 0);
// Shrinking the box too small should produce an exception.
context.setPeriodicBoxVectors(Vec3(7, 0, 0), Vec3(0, 3.9, 0), Vec3(0, 0, 9));
bool ok = true;
try {
context.getState(State::Forces).getPeriodicBoxVectors(x, y, z);
ok = false;
}
catch (exception& ex) {
}
ASSERT(ok);
}
void testIdealGas() {
const int numParticles = 64;
const int frequency = 10;
const int steps = 1000;
const double pressure = 1.5;
const double pressureInMD = pressure*(AVOGADRO*1e-25); // pressure in kJ/mol/nm^3
const double temp[] = {300.0, 600.0, 1000.0};
const double initialVolume = numParticles*BOLTZ*temp[1]/pressureInMD;
const double initialLength = std::pow(initialVolume, 1.0/3.0);
// Create a gas of noninteracting particles.
System system;
system.setDefaultPeriodicBoxVectors(Vec3(initialLength, 0, 0), Vec3(0, 0.5*initialLength, 0), Vec3(0, 0, 2*initialLength));
vector<Vec3> positions(numParticles);
OpenMM_SFMT::SFMT sfmt;
init_gen_rand(0, sfmt);
for (int i = 0; i < numParticles; ++i) {
system.addParticle(1.0);
positions[i] = Vec3(initialLength*genrand_real2(sfmt), 0.5*initialLength*genrand_real2(sfmt), 2*initialLength*genrand_real2(sfmt));
}
MonteCarloBarostat* barostat = new MonteCarloBarostat(pressure, temp[0], frequency);
system.addForce(barostat);
ASSERT(barostat->usesPeriodicBoundaryConditions());
ASSERT(system.usesPeriodicBoundaryConditions());
// Test it for three different temperatures.
for (int i = 0; i < 3; i++) {
barostat->setTemperature(temp[i]);
LangevinIntegrator integrator(temp[i], 0.1, 0.01);
Context context(system, integrator, platform);
context.setPositions(positions);
// Let it equilibrate.
integrator.step(10000);
// 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];
ASSERT_EQUAL_TOL(0.5*box[0][0], box[1][1], 1e-5);
ASSERT_EQUAL_TOL(2*box[0][0], box[2][2], 1e-5);
integrator.step(frequency);
}
volume /= steps;
double expected = (numParticles+1)*BOLTZ*temp[i]/pressureInMD;
ASSERT_USUALLY_EQUAL_TOL(expected, volume, 3/std::sqrt((double) steps));
}
void runPlatformTests() {
}
void testRandomSeed() {
const int numParticles = 8;
const double temp = 100.0;
const double pressure = 1.5;
System system;
system.setDefaultPeriodicBoxVectors(Vec3(8, 0, 0), Vec3(0, 8, 0), Vec3(0, 0, 8));
VerletIntegrator integrator(0.01);
NonbondedForce* forceField = new NonbondedForce();
forceField->setNonbondedMethod(NonbondedForce::CutoffPeriodic);
for (int i = 0; i < numParticles; ++i) {
system.addParticle(2.0);
forceField->addParticle((i%2 == 0 ? 1.0 : -1.0), 1.0, 5.0);
}
system.addForce(forceField);
MonteCarloBarostat* barostat = new MonteCarloBarostat(pressure, temp, 1);
system.addForce(barostat);
ASSERT(barostat->usesPeriodicBoundaryConditions());
ASSERT(system.usesPeriodicBoundaryConditions());
vector<Vec3> positions(numParticles);
vector<Vec3> velocities(numParticles);
for (int i = 0; i < numParticles; ++i) {
positions[i] = Vec3((i%2 == 0 ? 2 : -2), (i%4 < 2 ? 2 : -2), (i < 4 ? 2 : -2));
velocities[i] = Vec3(0, 0, 0);
}
// Try twice with the same random seed.
barostat->setRandomNumberSeed(5);
Context context(system, integrator, platform);
context.setPositions(positions);
context.setVelocities(velocities);
integrator.step(10);
State state1 = context.getState(State::Positions);
context.reinitialize();
context.setPositions(positions);
context.setVelocities(velocities);
integrator.step(10);
State state2 = context.getState(State::Positions);
// Try twice with a different random seed.
barostat->setRandomNumberSeed(10);
context.reinitialize();
context.setPositions(positions);
context.setVelocities(velocities);
integrator.step(10);
State state3 = context.getState(State::Positions);
context.reinitialize();
context.setPositions(positions);
context.setVelocities(velocities);
integrator.step(10);
State state4 = context.getState(State::Positions);
// Compare the results.
for (int i = 0; i < numParticles; i++) {
for (int j = 0; j < 3; j++) {
ASSERT(state1.getPositions()[i][j] == state2.getPositions()[i][j]);
ASSERT(state3.getPositions()[i][j] == state4.getPositions()[i][j]);
ASSERT(state1.getPositions()[i][j] != state3.getPositions()[i][j]);
}
}
}
int main() {
try {
testChangingBoxSize();
testIdealGas();
testRandomSeed();
}
catch(const exception& e) {
cout << "exception: " << e.what() << endl;
return 1;
}
cout << "Done" << endl;
return 0;
}
......@@ -6,7 +6,7 @@
* Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org. *
* *
* Portions copyright (c) 2008-2013 Stanford University and the Authors. *
* Portions copyright (c) 2015 Stanford University and the Authors. *
* Authors: Peter Eastman *
* Contributors: *
* *
......@@ -29,546 +29,8 @@
* USE OR OTHER DEALINGS IN THE SOFTWARE. *
* -------------------------------------------------------------------------- */
/**
* This tests all the different force terms in the reference implementation of NonbondedForce.
*/
#include "ReferenceTests.h"
#include "TestNonbondedForce.h"
#include "openmm/internal/AssertionUtilities.h"
#include "openmm/Context.h"
#include "ReferencePlatform.h"
#include "openmm/NonbondedForce.h"
#include "openmm/System.h"
#include "openmm/VerletIntegrator.h"
#include "SimTKOpenMMRealType.h"
#include "openmm/HarmonicBondForce.h"
#include "sfmt/SFMT.h"
#include <iostream>
#include <vector>
using namespace OpenMM;
using namespace std;
ReferencePlatform platform;
const double TOL = 1e-5;
void testCoulomb() {
System system;
system.addParticle(1.0);
system.addParticle(1.0);
VerletIntegrator integrator(0.01);
NonbondedForce* forceField = new NonbondedForce();
forceField->addParticle(0.5, 1, 0);
forceField->addParticle(-1.5, 1, 0);
system.addForce(forceField);
ASSERT(!forceField->usesPeriodicBoundaryConditions());
ASSERT(!system.usesPeriodicBoundaryConditions());
Context context(system, integrator, platform);
vector<Vec3> positions(2);
positions[0] = Vec3(0, 0, 0);
positions[1] = Vec3(2, 0, 0);
context.setPositions(positions);
State state = context.getState(State::Forces | State::Energy);
const vector<Vec3>& forces = state.getForces();
double force = ONE_4PI_EPS0*(-0.75)/4.0;
ASSERT_EQUAL_VEC(Vec3(-force, 0, 0), forces[0], TOL);
ASSERT_EQUAL_VEC(Vec3(force, 0, 0), forces[1], TOL);
ASSERT_EQUAL_TOL(ONE_4PI_EPS0*(-0.75)/2.0, state.getPotentialEnergy(), TOL);
}
void testLJ() {
System system;
system.addParticle(1.0);
system.addParticle(1.0);
VerletIntegrator integrator(0.01);
NonbondedForce* forceField = new NonbondedForce();
forceField->addParticle(0, 1.2, 1);
forceField->addParticle(0, 1.4, 2);
system.addForce(forceField);
ASSERT(!forceField->usesPeriodicBoundaryConditions());
ASSERT(!system.usesPeriodicBoundaryConditions());
Context context(system, integrator, platform);
vector<Vec3> positions(2);
positions[0] = Vec3(0, 0, 0);
positions[1] = Vec3(2, 0, 0);
context.setPositions(positions);
State state = context.getState(State::Forces | State::Energy);
const vector<Vec3>& forces = state.getForces();
double x = 1.3/2.0;
double eps = SQRT_TWO;
double force = 4.0*eps*(12*std::pow(x, 12.0)-6*std::pow(x, 6.0))/2.0;
ASSERT_EQUAL_VEC(Vec3(-force, 0, 0), forces[0], TOL);
ASSERT_EQUAL_VEC(Vec3(force, 0, 0), forces[1], TOL);
ASSERT_EQUAL_TOL(4.0*eps*(std::pow(x, 12.0)-std::pow(x, 6.0)), state.getPotentialEnergy(), TOL);
}
void testExclusionsAnd14() {
System system;
VerletIntegrator integrator(0.01);
NonbondedForce* nonbonded = new NonbondedForce();
for (int i = 0; i < 5; i++) {
system.addParticle(1.0);
nonbonded->addParticle(0, 1.5, 0);
}
vector<pair<int, int> > bonds;
bonds.push_back(pair<int, int>(0, 1));
bonds.push_back(pair<int, int>(1, 2));
bonds.push_back(pair<int, int>(2, 3));
bonds.push_back(pair<int, int>(3, 4));
nonbonded->createExceptionsFromBonds(bonds, 0.0, 0.0);
int first14, second14;
for (int i = 0; i < nonbonded->getNumExceptions(); i++) {
int particle1, particle2;
double chargeProd, sigma, epsilon;
nonbonded->getExceptionParameters(i, particle1, particle2, chargeProd, sigma, epsilon);
if ((particle1 == 0 && particle2 == 3) || (particle1 == 3 && particle2 == 0))
first14 = i;
if ((particle1 == 1 && particle2 == 4) || (particle1 == 4 && particle2 == 1))
second14 = i;
}
system.addForce(nonbonded);
Context context(system, integrator, platform);
for (int i = 1; i < 5; ++i) {
// Test LJ forces
vector<Vec3> positions(5);
const double r = 1.0;
for (int j = 0; j < 5; ++j) {
nonbonded->setParticleParameters(j, 0, 1.5, 0);
positions[j] = Vec3(0, j, 0);
}
nonbonded->setParticleParameters(0, 0, 1.5, 1);
nonbonded->setParticleParameters(i, 0, 1.5, 1);
nonbonded->setExceptionParameters(first14, 0, 3, 0, 1.5, i == 3 ? 0.5 : 0.0);
nonbonded->setExceptionParameters(second14, 1, 4, 0, 1.5, 0.0);
positions[i] = Vec3(r, 0, 0);
context.reinitialize();
context.setPositions(positions);
State state = context.getState(State::Forces | State::Energy);
const vector<Vec3>& forces = state.getForces();
double x = 1.5/r;
double eps = 1.0;
double force = 4.0*eps*(12*std::pow(x, 12.0)-6*std::pow(x, 6.0))/r;
double energy = 4.0*eps*(std::pow(x, 12.0)-std::pow(x, 6.0));
if (i == 3) {
force *= 0.5;
energy *= 0.5;
}
if (i < 3) {
force = 0;
energy = 0;
}
ASSERT_EQUAL_VEC(Vec3(-force, 0, 0), forces[0], TOL);
ASSERT_EQUAL_VEC(Vec3(force, 0, 0), forces[i], TOL);
ASSERT_EQUAL_TOL(energy, state.getPotentialEnergy(), TOL);
// Test Coulomb forces
nonbonded->setParticleParameters(0, 2, 1.5, 0);
nonbonded->setParticleParameters(i, 2, 1.5, 0);
nonbonded->setExceptionParameters(first14, 0, 3, i == 3 ? 4/1.2 : 0, 1.5, 0);
nonbonded->setExceptionParameters(second14, 1, 4, 0, 1.5, 0);
nonbonded->updateParametersInContext(context);
state = context.getState(State::Forces | State::Energy);
const vector<Vec3>& forces2 = state.getForces();
force = ONE_4PI_EPS0*4/(r*r);
energy = ONE_4PI_EPS0*4/r;
if (i == 3) {
force /= 1.2;
energy /= 1.2;
}
if (i < 3) {
force = 0;
energy = 0;
}
ASSERT_EQUAL_VEC(Vec3(-force, 0, 0), forces2[0], TOL);
ASSERT_EQUAL_VEC(Vec3(force, 0, 0), forces2[i], TOL);
ASSERT_EQUAL_TOL(energy, state.getPotentialEnergy(), TOL);
}
}
void testCutoff() {
System system;
system.addParticle(1.0);
system.addParticle(1.0);
system.addParticle(1.0);
VerletIntegrator integrator(0.01);
NonbondedForce* forceField = new NonbondedForce();
forceField->addParticle(1.0, 1, 0);
forceField->addParticle(1.0, 1, 0);
forceField->addParticle(1.0, 1, 0);
forceField->setNonbondedMethod(NonbondedForce::CutoffNonPeriodic);
const double cutoff = 2.9;
forceField->setCutoffDistance(cutoff);
const double eps = 50.0;
forceField->setReactionFieldDielectric(eps);
system.addForce(forceField);
ASSERT(!forceField->usesPeriodicBoundaryConditions());
ASSERT(!system.usesPeriodicBoundaryConditions());
Context context(system, integrator, platform);
vector<Vec3> positions(3);
positions[0] = Vec3(0, 0, 0);
positions[1] = Vec3(0, 2, 0);
positions[2] = Vec3(0, 3, 0);
context.setPositions(positions);
State state = context.getState(State::Forces | State::Energy);
const vector<Vec3>& forces = state.getForces();
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 force1 = ONE_4PI_EPS0*(1.0)*(0.25-2.0*krf*2.0);
const double force2 = ONE_4PI_EPS0*(1.0)*(1.0-2.0*krf*1.0);
ASSERT_EQUAL_VEC(Vec3(0, -force1, 0), forces[0], TOL);
ASSERT_EQUAL_VEC(Vec3(0, force1-force2, 0), forces[1], TOL);
ASSERT_EQUAL_VEC(Vec3(0, force2, 0), forces[2], TOL);
const double energy1 = ONE_4PI_EPS0*(1.0)*(0.5+krf*4.0-crf);
const double energy2 = ONE_4PI_EPS0*(1.0)*(1.0+krf*1.0-crf);
ASSERT_EQUAL_TOL(energy1+energy2, state.getPotentialEnergy(), TOL);
}
void testCutoff14() {
System system;
VerletIntegrator integrator(0.01);
NonbondedForce* nonbonded = new NonbondedForce();
for (int i = 0; i < 5; i++) {
system.addParticle(1.0);
nonbonded->addParticle(0, 1.5, 0);
}
nonbonded->setNonbondedMethod(NonbondedForce::CutoffNonPeriodic);
const double cutoff = 3.5;
nonbonded->setCutoffDistance(cutoff);
const double eps = 30.0;
nonbonded->setReactionFieldDielectric(eps);
vector<pair<int, int> > bonds;
bonds.push_back(pair<int, int>(0, 1));
bonds.push_back(pair<int, int>(1, 2));
bonds.push_back(pair<int, int>(2, 3));
bonds.push_back(pair<int, int>(3, 4));
nonbonded->createExceptionsFromBonds(bonds, 0.0, 0.0);
int first14, second14;
for (int i = 0; i < nonbonded->getNumExceptions(); i++) {
int particle1, particle2;
double chargeProd, sigma, epsilon;
nonbonded->getExceptionParameters(i, particle1, particle2, chargeProd, sigma, epsilon);
if ((particle1 == 0 && particle2 == 3) || (particle1 == 3 && particle2 == 0))
first14 = i;
if ((particle1 == 1 && particle2 == 4) || (particle1 == 4 && particle2 == 1))
second14 = i;
}
system.addForce(nonbonded);
ASSERT(!nonbonded->usesPeriodicBoundaryConditions());
ASSERT(!system.usesPeriodicBoundaryConditions());
Context context(system, integrator, platform);
vector<Vec3> positions(5);
positions[0] = Vec3(0, 0, 0);
positions[1] = Vec3(1, 0, 0);
positions[2] = Vec3(2, 0, 0);
positions[3] = Vec3(3, 0, 0);
positions[4] = Vec3(4, 0, 0);
for (int i = 1; i < 5; ++i) {
// Test LJ forces
nonbonded->setParticleParameters(0, 0, 1.5, 1);
for (int j = 1; j < 5; ++j)
nonbonded->setParticleParameters(j, 0, 1.5, 0);
nonbonded->setParticleParameters(i, 0, 1.5, 1);
nonbonded->setExceptionParameters(first14, 0, 3, 0, 1.5, i == 3 ? 0.5 : 0.0);
nonbonded->setExceptionParameters(second14, 1, 4, 0, 1.5, 0.0);
context.reinitialize();
context.setPositions(positions);
State state = context.getState(State::Forces | State::Energy);
const vector<Vec3>& forces = state.getForces();
double r = positions[i][0];
double x = 1.5/r;
double e = 1.0;
double force = 4.0*e*(12*std::pow(x, 12.0)-6*std::pow(x, 6.0))/r;
double energy = 4.0*e*(std::pow(x, 12.0)-std::pow(x, 6.0));
if (i == 3) {
force *= 0.5;
energy *= 0.5;
}
if (i < 3 || r > cutoff) {
force = 0;
energy = 0;
}
ASSERT_EQUAL_VEC(Vec3(-force, 0, 0), forces[0], TOL);
ASSERT_EQUAL_VEC(Vec3(force, 0, 0), forces[i], TOL);
ASSERT_EQUAL_TOL(energy, state.getPotentialEnergy(), TOL);
// Test Coulomb forces
const double q = 0.7;
nonbonded->setParticleParameters(0, q, 1.5, 0);
nonbonded->setParticleParameters(i, q, 1.5, 0);
nonbonded->setExceptionParameters(first14, 0, 3, i == 3 ? q*q/1.2 : 0, 1.5, 0);
nonbonded->setExceptionParameters(second14, 1, 4, 0, 1.5, 0);
nonbonded->updateParametersInContext(context);
state = context.getState(State::Forces | State::Energy);
const vector<Vec3>& forces2 = state.getForces();
force = ONE_4PI_EPS0*q*q/(r*r);
energy = ONE_4PI_EPS0*q*q/r;
if (i == 3) {
force /= 1.2;
energy /= 1.2;
}
if (i < 3 || r > cutoff) {
force = 0;
energy = 0;
}
ASSERT_EQUAL_VEC(Vec3(-force, 0, 0), forces2[0], TOL);
ASSERT_EQUAL_VEC(Vec3(force, 0, 0), forces2[i], TOL);
ASSERT_EQUAL_TOL(energy, state.getPotentialEnergy(), TOL);
}
}
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);
ASSERT(nonbonded->usesPeriodicBoundaryConditions());
ASSERT(system.usesPeriodicBoundaryConditions());
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 testTriclinic() {
System system;
system.addParticle(1.0);
system.addParticle(1.0);
Vec3 a(3.1, 0, 0);
Vec3 b(0.4, 3.5, 0);
Vec3 c(-0.1, -0.5, 4.0);
system.setDefaultPeriodicBoxVectors(a, b, c);
VerletIntegrator integrator(0.01);
NonbondedForce* nonbonded = new NonbondedForce();
nonbonded->addParticle(1.0, 1, 0);
nonbonded->addParticle(1.0, 1, 0);
nonbonded->setNonbondedMethod(NonbondedForce::CutoffPeriodic);
const double cutoff = 1.5;
nonbonded->setCutoffDistance(cutoff);
system.addForce(nonbonded);
Context context(system, integrator, platform);
vector<Vec3> positions(2);
OpenMM_SFMT::SFMT sfmt;
init_gen_rand(0, sfmt);
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);
for (int iteration = 0; iteration < 50; iteration++) {
// Generate random positions for the two particles.
positions[0] = a*genrand_real2(sfmt) + b*genrand_real2(sfmt) + c*genrand_real2(sfmt);
positions[1] = a*genrand_real2(sfmt) + b*genrand_real2(sfmt) + c*genrand_real2(sfmt);
context.setPositions(positions);
// Loop over all possible periodic copies and find the nearest one.
Vec3 delta;
double distance2 = 100.0;
for (int i = -1; i < 2; i++)
for (int j = -1; j < 2; j++)
for (int k = -1; k < 2; k++) {
Vec3 d = positions[1]-positions[0]+a*i+b*j+c*k;
if (d.dot(d) < distance2) {
delta = d;
distance2 = d.dot(d);
}
}
double distance = sqrt(distance2);
// See if the force and energy are correct.
State state = context.getState(State::Forces | State::Energy);
if (distance >= cutoff) {
ASSERT_EQUAL(0.0, state.getPotentialEnergy());
ASSERT_EQUAL_VEC(Vec3(0, 0, 0), state.getForces()[0], 0);
ASSERT_EQUAL_VEC(Vec3(0, 0, 0), state.getForces()[1], 0);
}
else {
const Vec3 force = delta*ONE_4PI_EPS0*(-1.0/(distance*distance*distance)+2.0*krf);
ASSERT_EQUAL_TOL(ONE_4PI_EPS0*(1.0/distance+krf*distance*distance-crf), state.getPotentialEnergy(), TOL);
ASSERT_EQUAL_VEC(force, state.getForces()[0], TOL);
ASSERT_EQUAL_VEC(-force, state.getForces()[1], TOL);
}
}
}
void testDispersionCorrection() {
// Create a box full of identical particles.
int gridSize = 5;
int numParticles = gridSize*gridSize*gridSize;
double boxSize = gridSize*0.7;
double cutoff = boxSize/3;
System system;
VerletIntegrator integrator(0.01);
NonbondedForce* nonbonded = new NonbondedForce();
vector<Vec3> positions(numParticles);
int index = 0;
for (int i = 0; i < gridSize; i++)
for (int j = 0; j < gridSize; j++)
for (int k = 0; k < gridSize; k++) {
system.addParticle(1.0);
nonbonded->addParticle(0, 1.1, 0.5);
positions[index] = Vec3(i*boxSize/gridSize, j*boxSize/gridSize, k*boxSize/gridSize);
index++;
}
nonbonded->setNonbondedMethod(NonbondedForce::CutoffPeriodic);
nonbonded->setCutoffDistance(cutoff);
system.setDefaultPeriodicBoxVectors(Vec3(boxSize, 0, 0), Vec3(0, boxSize, 0), Vec3(0, 0, boxSize));
system.addForce(nonbonded);
ASSERT(nonbonded->usesPeriodicBoundaryConditions());
ASSERT(system.usesPeriodicBoundaryConditions());
// See if the correction has the correct value.
Context context(system, integrator, platform);
context.setPositions(positions);
double energy1 = context.getState(State::Energy).getPotentialEnergy();
nonbonded->setUseDispersionCorrection(false);
context.reinitialize();
context.setPositions(positions);
double energy2 = context.getState(State::Energy).getPotentialEnergy();
double term1 = (0.5*pow(1.1, 12)/pow(cutoff, 9))/9;
double term2 = (0.5*pow(1.1, 6)/pow(cutoff, 3))/3;
double expected = 8*M_PI*numParticles*numParticles*(term1-term2)/(boxSize*boxSize*boxSize);
ASSERT_EQUAL_TOL(expected, energy1-energy2, 1e-4);
// Now modify half the particles to be different, and see if it is still correct.
int numType2 = 0;
for (int i = 0; i < numParticles; i += 2) {
nonbonded->setParticleParameters(i, 0, 1, 1);
numType2++;
}
int numType1 = numParticles-numType2;
nonbonded->updateParametersInContext(context);
energy2 = context.getState(State::Energy).getPotentialEnergy();
nonbonded->setUseDispersionCorrection(true);
context.reinitialize();
context.setPositions(positions);
energy1 = context.getState(State::Energy).getPotentialEnergy();
term1 = ((numType1*(numType1+1))/2)*(0.5*pow(1.1, 12)/pow(cutoff, 9))/9;
term2 = ((numType1*(numType1+1))/2)*(0.5*pow(1.1, 6)/pow(cutoff, 3))/3;
term1 += ((numType2*(numType2+1))/2)*(1*pow(1.0, 12)/pow(cutoff, 9))/9;
term2 += ((numType2*(numType2+1))/2)*(1*pow(1.0, 6)/pow(cutoff, 3))/3;
double combinedSigma = 0.5*(1+1.1);
double combinedEpsilon = sqrt(1*0.5);
term1 += (numType1*numType2)*(combinedEpsilon*pow(combinedSigma, 12)/pow(cutoff, 9))/9;
term2 += (numType1*numType2)*(combinedEpsilon*pow(combinedSigma, 6)/pow(cutoff, 3))/3;
term1 /= (numParticles*(numParticles+1))/2;
term2 /= (numParticles*(numParticles+1))/2;
expected = 8*M_PI*numParticles*numParticles*(term1-term2)/(boxSize*boxSize*boxSize);
ASSERT_EQUAL_TOL(expected, energy1-energy2, 1e-4);
}
void testSwitchingFunction(NonbondedForce::NonbondedMethod method) {
System system;
system.setDefaultPeriodicBoxVectors(Vec3(6, 0, 0), Vec3(0, 6, 0), Vec3(0, 0, 6));
system.addParticle(1.0);
system.addParticle(1.0);
VerletIntegrator integrator(0.01);
NonbondedForce* nonbonded = new NonbondedForce();
nonbonded->addParticle(0, 1.2, 1);
nonbonded->addParticle(0, 1.4, 2);
nonbonded->setNonbondedMethod(method);
nonbonded->setCutoffDistance(2.0);
nonbonded->setUseSwitchingFunction(true);
nonbonded->setSwitchingDistance(1.5);
nonbonded->setUseDispersionCorrection(false);
system.addForce(nonbonded);
if (method == NonbondedForce::PME) {
ASSERT(nonbonded->usesPeriodicBoundaryConditions());
ASSERT(system.usesPeriodicBoundaryConditions());
}
else {
ASSERT(!nonbonded->usesPeriodicBoundaryConditions());
ASSERT(!system.usesPeriodicBoundaryConditions());
}
Context context(system, integrator, platform);
vector<Vec3> positions(2);
positions[0] = Vec3(0, 0, 0);
double eps = SQRT_TWO;
// Compute the interaction at various distances.
for (double r = 1.0; r < 2.5; r += 0.1) {
positions[1] = Vec3(r, 0, 0);
context.setPositions(positions);
State state = context.getState(State::Forces | State::Energy);
// See if the energy is correct.
double x = 1.3/r;
double expectedEnergy = 4.0*eps*(std::pow(x, 12.0)-std::pow(x, 6.0));
double switchValue;
if (r <= 1.5)
switchValue = 1;
else if (r >= 2.0)
switchValue = 0;
else {
double t = (r-1.5)/0.5;
switchValue = 1+t*t*t*(-10+t*(15-t*6));
}
ASSERT_EQUAL_TOL(switchValue*expectedEnergy, state.getPotentialEnergy(), TOL);
// See if the force is the gradient of the energy.
double delta = 1e-3;
positions[1] = Vec3(r-delta, 0, 0);
context.setPositions(positions);
double e1 = context.getState(State::Energy).getPotentialEnergy();
positions[1] = Vec3(r+delta, 0, 0);
context.setPositions(positions);
double e2 = context.getState(State::Energy).getPotentialEnergy();
ASSERT_EQUAL_TOL((e2-e1)/(2*delta), state.getForces()[0][0], 1e-3);
}
}
int main() {
try {
testCoulomb();
testLJ();
testExclusionsAnd14();
testCutoff();
testCutoff14();
testPeriodic();
testTriclinic();
testDispersionCorrection();
testSwitchingFunction(NonbondedForce::CutoffNonPeriodic);
testSwitchingFunction(NonbondedForce::PME);
}
catch(const exception& e) {
cout << "exception: " << e.what() << endl;
return 1;
}
cout << "Done" << endl;
return 0;
void runPlatformTests() {
}
......@@ -6,7 +6,7 @@
* Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org. *
* *
* Portions copyright (c) 2008 Stanford University and the Authors. *
* Portions copyright (c) 2015 Stanford University and the Authors. *
* Authors: Peter Eastman *
* Contributors: *
* *
......@@ -29,80 +29,8 @@
* USE OR OTHER DEALINGS IN THE SOFTWARE. *
* -------------------------------------------------------------------------- */
/**
* This tests all the different force terms in the reference implementation of PeriodicTorsionForce.
*/
#include "ReferenceTests.h"
#include "TestPeriodicTorsionForce.h"
#include "openmm/internal/AssertionUtilities.h"
#include "openmm/Context.h"
#include "ReferencePlatform.h"
#include "openmm/PeriodicTorsionForce.h"
#include "openmm/System.h"
#include "openmm/VerletIntegrator.h"
#include "SimTKOpenMMRealType.h"
#include <iostream>
#include <vector>
using namespace OpenMM;
using namespace std;
ReferencePlatform platform;
const double TOL = 1e-5;
void testPeriodicTorsions() {
System system;
system.addParticle(1.0);
system.addParticle(1.0);
system.addParticle(1.0);
system.addParticle(1.0);
VerletIntegrator integrator(0.01);
PeriodicTorsionForce* forceField = new PeriodicTorsionForce();
forceField->addTorsion(0, 1, 2, 3, 2, PI_M/3, 1.1);
system.addForce(forceField);
ASSERT(!forceField->usesPeriodicBoundaryConditions());
ASSERT(!system.usesPeriodicBoundaryConditions());
Context context(system, integrator, platform);
vector<Vec3> positions(4);
positions[0] = Vec3(0, 1, 0);
positions[1] = Vec3(0, 0, 0);
positions[2] = Vec3(1, 0, 0);
positions[3] = Vec3(1, 0, 2);
context.setPositions(positions);
State state = context.getState(State::Forces | State::Energy);
{
const vector<Vec3>& forces = state.getForces();
double torque = -2*1.1*std::sin(2*PI_M/3);
ASSERT_EQUAL_VEC(Vec3(0, 0, torque), forces[0], TOL);
ASSERT_EQUAL_VEC(Vec3(0, 0.5*torque, 0), forces[3], TOL);
ASSERT_EQUAL_VEC(Vec3(forces[0][0]+forces[1][0]+forces[2][0]+forces[3][0], forces[0][1]+forces[1][1]+forces[2][1]+forces[3][1], forces[0][2]+forces[1][2]+forces[2][2]+forces[3][2]), Vec3(0, 0, 0), TOL);
ASSERT_EQUAL_TOL(1.1*(1+std::cos(2*PI_M/3)), state.getPotentialEnergy(), TOL);
}
// Try changing the torsion parameters and make sure it's still correct.
forceField->setTorsionParameters(0, 0, 1, 2, 3, 3, PI_M/3.2, 1.3);
forceField->updateParametersInContext(context);
state = context.getState(State::Forces | State::Energy);
{
const vector<Vec3>& forces = state.getForces();
double dtheta = (3*PI_M/2)-(PI_M/3.2);
double torque = -3*1.3*std::sin(dtheta);
ASSERT_EQUAL_VEC(Vec3(0, 0, torque), forces[0], TOL);
ASSERT_EQUAL_VEC(Vec3(0, 0.5*torque, 0), forces[3], TOL);
ASSERT_EQUAL_VEC(Vec3(forces[0][0]+forces[1][0]+forces[2][0]+forces[3][0], forces[0][1]+forces[1][1]+forces[2][1]+forces[3][1], forces[0][2]+forces[1][2]+forces[2][2]+forces[3][2]), Vec3(0, 0, 0), TOL);
ASSERT_EQUAL_TOL(1.3*(1+std::cos(dtheta)), state.getPotentialEnergy(), TOL);
}
}
int main() {
try {
testPeriodicTorsions();
}
catch(const exception& e) {
cout << "exception: " << e.what() << endl;
return 1;
}
cout << "Done" << endl;
return 0;
void runPlatformTests() {
}
......@@ -6,7 +6,7 @@
* Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org. *
* *
* Portions copyright (c) 2008 Stanford University and the Authors. *
* Portions copyright (c) 2015 Stanford University and the Authors. *
* Authors: Peter Eastman *
* Contributors: *
* *
......@@ -29,99 +29,8 @@
* USE OR OTHER DEALINGS IN THE SOFTWARE. *
* -------------------------------------------------------------------------- */
/**
* This tests all the different force terms in the reference implementation of RBTorsionForce.
*/
#include "ReferenceTests.h"
#include "TestRBTorsionForce.h"
#include "openmm/internal/AssertionUtilities.h"
#include "openmm/Context.h"
#include "ReferencePlatform.h"
#include "openmm/RBTorsionForce.h"
#include "openmm/System.h"
#include "openmm/VerletIntegrator.h"
#include "SimTKOpenMMRealType.h"
#include <iostream>
#include <vector>
using namespace OpenMM;
using namespace std;
ReferencePlatform platform;
const double TOL = 1e-5;
void testRBTorsions() {
System system;
system.addParticle(1.0);
system.addParticle(1.0);
system.addParticle(1.0);
system.addParticle(1.0);
VerletIntegrator integrator(0.01);
RBTorsionForce* forceField = new RBTorsionForce();
forceField->addTorsion(0, 1, 2, 3, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6);
system.addForce(forceField);
ASSERT(!forceField->usesPeriodicBoundaryConditions());
ASSERT(!system.usesPeriodicBoundaryConditions());
Context context(system, integrator, platform);
vector<Vec3> positions(4);
positions[0] = Vec3(0, 1, 0);
positions[1] = Vec3(0, 0, 0);
positions[2] = Vec3(1, 0, 0);
positions[3] = Vec3(1, 1, 1);
context.setPositions(positions);
State state = context.getState(State::Forces | State::Energy);
{
const vector<Vec3>& forces = state.getForces();
double psi = 0.25*PI_M - PI_M;
double torque = 0.0;
for (int i = 1; i < 6; ++i) {
double c = 0.1*(i+1);
torque += -c*i*std::pow(std::cos(psi), i-1)*std::sin(psi);
}
ASSERT_EQUAL_VEC(Vec3(0, 0, torque), forces[0], TOL);
ASSERT_EQUAL_VEC(Vec3(0, 0.5*torque, -0.5*torque), forces[3], TOL);
ASSERT_EQUAL_VEC(Vec3(forces[0][0]+forces[1][0]+forces[2][0]+forces[3][0], forces[0][1]+forces[1][1]+forces[2][1]+forces[3][1], forces[0][2]+forces[1][2]+forces[2][2]+forces[3][2]), Vec3(0, 0, 0), TOL);
double energy = 0.0;
for (int i = 0; i < 6; ++i) {
double c = 0.1*(i+1);
energy += c*std::pow(std::cos(psi), i);
}
ASSERT_EQUAL_TOL(energy, state.getPotentialEnergy(), TOL);
}
// Try changing the torsion parameters and make sure it's still correct.
forceField->setTorsionParameters(0, 0, 1, 2, 3, 0.11, 0.22, 0.33, 0.44, 0.55, 0.66);
forceField->updateParametersInContext(context);
state = context.getState(State::Forces | State::Energy);
{
const vector<Vec3>& forces = state.getForces();
double psi = 0.25*PI_M - PI_M;
double torque = 0.0;
for (int i = 1; i < 6; ++i) {
double c = 0.11*(i+1);
torque += -c*i*std::pow(std::cos(psi), i-1)*std::sin(psi);
}
ASSERT_EQUAL_VEC(Vec3(0, 0, torque), forces[0], TOL);
ASSERT_EQUAL_VEC(Vec3(0, 0.5*torque, -0.5*torque), forces[3], TOL);
ASSERT_EQUAL_VEC(Vec3(forces[0][0]+forces[1][0]+forces[2][0]+forces[3][0], forces[0][1]+forces[1][1]+forces[2][1]+forces[3][1], forces[0][2]+forces[1][2]+forces[2][2]+forces[3][2]), Vec3(0, 0, 0), TOL);
double energy = 0.0;
for (int i = 0; i < 6; ++i) {
double c = 0.11*(i+1);
energy += c*std::pow(std::cos(psi), i);
}
ASSERT_EQUAL_TOL(energy, state.getPotentialEnergy(), TOL);
}
}
int main() {
try {
testRBTorsions();
}
catch(const exception& e) {
cout << "exception: " << e.what() << endl;
return 1;
}
cout << "Done" << endl;
return 0;
void runPlatformTests() {
}
/* -------------------------------------------------------------------------- *
* OpenMM *
* -------------------------------------------------------------------------- *
......@@ -7,7 +6,7 @@
* Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org. *
* *
* Portions copyright (c) 2008-2009 Stanford University and the Authors. *
* Portions copyright (c) 2015 Stanford University and the Authors. *
* Authors: Peter Eastman *
* Contributors: *
* *
......@@ -30,88 +29,8 @@
* USE OR OTHER DEALINGS IN THE SOFTWARE. *
* -------------------------------------------------------------------------- */
/**
* This tests the reference implementation of the SETTLE algorithm.
*/
#include "openmm/internal/AssertionUtilities.h"
#include "openmm/Context.h"
#include "ReferencePlatform.h"
#include "openmm/NonbondedForce.h"
#include "openmm/System.h"
#include "openmm/LangevinIntegrator.h"
#include "sfmt/SFMT.h"
#include <iostream>
#include <vector>
using namespace OpenMM;
using namespace std;
ReferencePlatform platform;
void testConstraints() {
const int numMolecules = 10;
const int numParticles = numMolecules*3;
const int numConstraints = numMolecules*3;
const double temp = 100.0;
System system;
LangevinIntegrator integrator(temp, 2.0, 0.001);
integrator.setConstraintTolerance(1e-5);
NonbondedForce* forceField = new NonbondedForce();
for (int i = 0; i < numMolecules; ++i) {
system.addParticle(16.0);
system.addParticle(1.0);
system.addParticle(1.0);
forceField->addParticle(-0.82, 0.317, 0.65);
forceField->addParticle(0.41, 1.0, 0.0);
forceField->addParticle(0.41, 1.0, 0.0);
system.addConstraint(i*3, i*3+1, 0.1);
system.addConstraint(i*3, i*3+2, 0.1);
system.addConstraint(i*3+1, i*3+2, 0.163);
}
system.addForce(forceField);
Context context(system, integrator, platform);
vector<Vec3> positions(numParticles);
vector<Vec3> velocities(numParticles);
OpenMM_SFMT::SFMT sfmt;
init_gen_rand(0, sfmt);
for (int i = 0; i < numMolecules; ++i) {
positions[i*3] = Vec3((i%4)*0.4, (i/4)*0.4, 0);
positions[i*3+1] = positions[i*3]+Vec3(0.1, 0, 0);
positions[i*3+2] = positions[i*3]+Vec3(-0.03333, 0.09428, 0);
velocities[i*3] = Vec3(genrand_real2(sfmt)-0.5, genrand_real2(sfmt)-0.5, genrand_real2(sfmt)-0.5);
velocities[i*3+1] = Vec3(genrand_real2(sfmt)-0.5, genrand_real2(sfmt)-0.5, genrand_real2(sfmt)-0.5);
velocities[i*3+2] = Vec3(genrand_real2(sfmt)-0.5, genrand_real2(sfmt)-0.5, genrand_real2(sfmt)-0.5);
}
context.setPositions(positions);
context.setVelocities(velocities);
// Simulate it and see whether the constraints remain satisfied.
for (int i = 0; i < 1000; ++i) {
integrator.step(1);
State state = context.getState(State::Positions | State::Forces);
for (int j = 0; j < numConstraints; ++j) {
int particle1, particle2;
double distance;
system.getConstraintParameters(j, particle1, particle2, distance);
Vec3 p1 = state.getPositions()[particle1];
Vec3 p2 = state.getPositions()[particle2];
double dist = std::sqrt((p1[0]-p2[0])*(p1[0]-p2[0])+(p1[1]-p2[1])*(p1[1]-p2[1])+(p1[2]-p2[2])*(p1[2]-p2[2]));
ASSERT_EQUAL_TOL(distance, dist, 1e-5);
}
}
}
#include "ReferenceTests.h"
#include "TestSettle.h"
int main(int argc, char* argv[]) {
try {
testConstraints();
}
catch(const exception& e) {
cout << "exception: " << e.what() << endl;
return 1;
}
cout << "Done" << endl;
return 0;
void runPlatformTests() {
}
......@@ -6,7 +6,7 @@
* Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org. *
* *
* Portions copyright (c) 2008-2009 Stanford University and the Authors. *
* Portions copyright (c) 2015 Stanford University and the Authors. *
* Authors: Peter Eastman *
* Contributors: *
* *
......@@ -29,309 +29,8 @@
* USE OR OTHER DEALINGS IN THE SOFTWARE. *
* -------------------------------------------------------------------------- */
/**
* This tests the reference implementation of VariableLangevinIntegrator.
*/
#include "ReferenceTests.h"
#include "TestVariableLangevinIntegrator.h"
#include "openmm/internal/AssertionUtilities.h"
#include "openmm/Context.h"
#include "ReferencePlatform.h"
#include "openmm/HarmonicBondForce.h"
#include "openmm/NonbondedForce.h"
#include "openmm/System.h"
#include "openmm/VariableLangevinIntegrator.h"
#include "SimTKOpenMMRealType.h"
#include "sfmt/SFMT.h"
#include <iostream>
#include <vector>
using namespace OpenMM;
using namespace std;
ReferencePlatform platform;
const double TOL = 1e-5;
void testSingleBond() {
System system;
system.addParticle(2.0);
system.addParticle(2.0);
VariableLangevinIntegrator integrator(0, 0.1, 1e-6);
HarmonicBondForce* forceField = new HarmonicBondForce();
forceField->addBond(0, 1, 1.5, 1);
system.addForce(forceField);
Context context(system, integrator, platform);
vector<Vec3> positions(2);
positions[0] = Vec3(-1, 0, 0);
positions[1] = Vec3(1, 0, 0);
context.setPositions(positions);
// This is simply a damped harmonic oscillator, so compare it to the analytical solution.
double freq = std::sqrt(1-0.05*0.05);
for (int i = 0; i < 1000; ++i) {
State state = context.getState(State::Positions | State::Velocities);
double time = state.getTime();
double expectedDist = 1.5+0.5*std::exp(-0.05*time)*std::cos(freq*time);
ASSERT_EQUAL_VEC(Vec3(-0.5*expectedDist, 0, 0), state.getPositions()[0], 0.02);
ASSERT_EQUAL_VEC(Vec3(0.5*expectedDist, 0, 0), state.getPositions()[1], 0.02);
double expectedSpeed = -0.5*std::exp(-0.05*time)*(0.05*std::cos(freq*time)+freq*std::sin(freq*time));
ASSERT_EQUAL_VEC(Vec3(-0.5*expectedSpeed, 0, 0), state.getVelocities()[0], 0.02);
ASSERT_EQUAL_VEC(Vec3(0.5*expectedSpeed, 0, 0), state.getVelocities()[1], 0.02);
integrator.step(1);
}
// Now set the friction to a tiny value and see if it conserves energy.
integrator.setFriction(5e-5);
context.setPositions(positions);
State state = context.getState(State::Energy);
double initialEnergy = state.getKineticEnergy()+state.getPotentialEnergy();
for (int i = 0; i < 1000; ++i) {
state = context.getState(State::Energy);
double energy = state.getKineticEnergy()+state.getPotentialEnergy();
ASSERT_EQUAL_TOL(initialEnergy, energy, 0.05);
integrator.step(1);
}
}
void testTemperature() {
const int numParticles = 8;
const double temp = 100.0;
System system;
VariableLangevinIntegrator integrator(temp, 5.0, 5e-5);
NonbondedForce* forceField = new NonbondedForce();
for (int i = 0; i < numParticles; ++i) {
system.addParticle(2.0);
forceField->addParticle((i%2 == 0 ? 1.0 : -1.0), 1.0, 5.0);
}
system.addForce(forceField);
Context context(system, integrator, platform);
vector<Vec3> positions(numParticles);
for (int i = 0; i < numParticles; ++i)
positions[i] = Vec3((i%2 == 0 ? 2 : -2), (i%4 < 2 ? 2 : -2), (i < 4 ? 2 : -2));
context.setPositions(positions);
context.setVelocitiesToTemperature(temp);
// Let it equilibrate.
integrator.step(5000);
// Now run it for a while and see if the temperature is correct.
double ke = 0.0;
for (int i = 0; i < 5000; ++i) {
State state = context.getState(State::Energy);
ke += state.getKineticEnergy();
integrator.step(5);
}
ke /= 5000;
double expected = 0.5*numParticles*3*BOLTZ*temp;
ASSERT_USUALLY_EQUAL_TOL(expected, ke, 0.1);
}
void testConstraints() {
const int numParticles = 8;
const double temp = 100.0;
System system;
VariableLangevinIntegrator integrator(temp, 2.0, 1e-5);
integrator.setConstraintTolerance(1e-5);
integrator.setRandomNumberSeed(0);
NonbondedForce* forceField = new NonbondedForce();
for (int i = 0; i < numParticles; ++i) {
system.addParticle(10.0);
forceField->addParticle((i%2 == 0 ? 0.2 : -0.2), 0.5, 5.0);
}
for (int i = 0; i < numParticles-1; ++i)
system.addConstraint(i, i+1, 1.0);
system.addForce(forceField);
Context context(system, integrator, platform);
vector<Vec3> positions(numParticles);
vector<Vec3> velocities(numParticles);
OpenMM_SFMT::SFMT sfmt;
init_gen_rand(0, sfmt);
for (int i = 0; i < numParticles; ++i) {
positions[i] = Vec3(i/2, (i+1)/2, 0);
velocities[i] = Vec3(genrand_real2(sfmt)-0.5, genrand_real2(sfmt)-0.5, genrand_real2(sfmt)-0.5);
}
context.setPositions(positions);
context.setVelocities(velocities);
// Simulate it and see whether the constraints remain satisfied.
for (int i = 0; i < 1000; ++i) {
State state = context.getState(State::Positions);
for (int j = 0; j < numParticles-1; ++j) {
Vec3 p1 = state.getPositions()[j];
Vec3 p2 = state.getPositions()[j+1];
double dist = std::sqrt((p1[0]-p2[0])*(p1[0]-p2[0])+(p1[1]-p2[1])*(p1[1]-p2[1])+(p1[2]-p2[2])*(p1[2]-p2[2]));
ASSERT_EQUAL_TOL(1.0, dist, 2e-5);
}
integrator.step(1);
}
}
void testConstrainedMasslessParticles() {
System system;
system.addParticle(0.0);
system.addParticle(1.0);
system.addConstraint(0, 1, 1.5);
vector<Vec3> positions(2);
positions[0] = Vec3(-1, 0, 0);
positions[1] = Vec3(1, 0, 0);
VariableLangevinIntegrator integrator(300.0, 2.0, 0.01);
bool failed = false;
try {
// This should throw an exception.
Context context(system, integrator, platform);
}
catch (exception& ex) {
failed = true;
}
ASSERT(failed);
// Now make both particles massless, which should work.
system.setParticleMass(1, 0.0);
Context context(system, integrator, platform);
context.setPositions(positions);
context.setVelocitiesToTemperature(300.0);
integrator.step(1);
State state = context.getState(State::Velocities | State::Positions);
ASSERT_EQUAL(0.0, state.getVelocities()[0][0]);
}
void testRandomSeed() {
const int numParticles = 8;
const double temp = 100.0;
System system;
VariableLangevinIntegrator integrator(temp, 2.0, 1e-5);
NonbondedForce* forceField = new NonbondedForce();
for (int i = 0; i < numParticles; ++i) {
system.addParticle(2.0);
forceField->addParticle((i%2 == 0 ? 1.0 : -1.0), 1.0, 5.0);
}
system.addForce(forceField);
vector<Vec3> positions(numParticles);
vector<Vec3> velocities(numParticles);
for (int i = 0; i < numParticles; ++i) {
positions[i] = Vec3((i%2 == 0 ? 2 : -2), (i%4 < 2 ? 2 : -2), (i < 4 ? 2 : -2));
velocities[i] = Vec3(0, 0, 0);
}
// Try twice with the same random seed.
integrator.setRandomNumberSeed(5);
Context context(system, integrator, platform);
context.setPositions(positions);
context.setVelocities(velocities);
integrator.step(10);
State state1 = context.getState(State::Positions);
context.reinitialize();
context.setPositions(positions);
context.setVelocities(velocities);
integrator.step(10);
State state2 = context.getState(State::Positions);
// Try twice with a different random seed.
integrator.setRandomNumberSeed(10);
context.reinitialize();
context.setPositions(positions);
context.setVelocities(velocities);
integrator.step(10);
State state3 = context.getState(State::Positions);
context.reinitialize();
context.setPositions(positions);
context.setVelocities(velocities);
integrator.step(10);
State state4 = context.getState(State::Positions);
// Compare the results.
for (int i = 0; i < numParticles; i++) {
for (int j = 0; j < 3; j++) {
ASSERT(state1.getPositions()[i][j] == state2.getPositions()[i][j]);
ASSERT(state3.getPositions()[i][j] == state4.getPositions()[i][j]);
ASSERT(state1.getPositions()[i][j] != state3.getPositions()[i][j]);
}
}
}
void testArgonBox() {
const int gridSize = 8;
const double mass = 40.0; // Ar atomic mass
const double temp = 120.0; // K
const double epsilon = BOLTZ * temp; // L-J well depth for Ar
const double sigma = 0.34; // L-J size for Ar in nm
const double density = 0.8; // atoms / sigma^3
double cellSize = sigma / pow(density, 0.333);
double boxSize = gridSize * cellSize;
double cutoff = 2.0 * sigma;
// Create a box of argon atoms.
System system;
NonbondedForce* nonbonded = new NonbondedForce();
vector<Vec3> positions;
OpenMM_SFMT::SFMT sfmt;
init_gen_rand(0, sfmt);
const Vec3 half(0.5, 0.5, 0.5);
int numParticles = 0;
for (int i = 0; i < gridSize; i++) {
for (int j = 0; j < gridSize; j++) {
for (int k = 0; k < gridSize; k++) {
system.addParticle(mass);
nonbonded->addParticle(0, sigma, epsilon);
positions.push_back((Vec3(i, j, k) + half + Vec3(genrand_real2(sfmt), genrand_real2(sfmt), genrand_real2(sfmt))*0.1) * cellSize);
++numParticles;
}
}
}
nonbonded->setNonbondedMethod(NonbondedForce::CutoffPeriodic);
nonbonded->setCutoffDistance(cutoff);
system.setDefaultPeriodicBoxVectors(Vec3(boxSize, 0, 0), Vec3(0, boxSize, 0), Vec3(0, 0, boxSize));
system.addForce(nonbonded);
VariableLangevinIntegrator integrator(temp, 6.0, 1e-4);
Context context(system, integrator, platform);
context.setPositions(positions);
context.setVelocitiesToTemperature(temp);
// Equilibrate.
integrator.stepTo(2.0);
// Make sure the temperature is correct.
double ke = 0.0;
for (int i = 0; i < 1000; ++i) {
double t = 2.0 + 0.02 * (i + 1);
integrator.stepTo(t);
State state = context.getState(State::Energy);
ke += state.getKineticEnergy();
}
ke /= 1000;
double expected = 1.5 * numParticles * BOLTZ * temp;
ASSERT_USUALLY_EQUAL_TOL(expected, ke, 0.01);
}
int main() {
try {
testSingleBond();
testTemperature();
testConstraints();
testConstrainedMasslessParticles();
testRandomSeed();
testArgonBox();
}
catch(const exception& e) {
cout << "exception: " << e.what() << endl;
return 1;
}
cout << "Done" << endl;
return 0;
void runPlatformTests() {
}
......@@ -6,7 +6,7 @@
* Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org. *
* *
* Portions copyright (c) 2008-2009 Stanford University and the Authors. *
* Portions copyright (c) 2015 Stanford University and the Authors. *
* Authors: Peter Eastman *
* Contributors: *
* *
......@@ -29,286 +29,8 @@
* USE OR OTHER DEALINGS IN THE SOFTWARE. *
* -------------------------------------------------------------------------- */
/**
* This tests the reference implementation of VariableVerletIntegrator.
*/
#include "ReferenceTests.h"
#include "TestVariableVerletIntegrator.h"
#include "openmm/internal/AssertionUtilities.h"
#include "openmm/Context.h"
#include "ReferencePlatform.h"
#include "openmm/HarmonicBondForce.h"
#include "openmm/NonbondedForce.h"
#include "openmm/System.h"
#include "openmm/VariableVerletIntegrator.h"
#include "SimTKOpenMMRealType.h"
#include "sfmt/SFMT.h"
#include <iostream>
#include <vector>
using namespace OpenMM;
using namespace std;
ReferencePlatform platform;
const double TOL = 1e-5;
void testSingleBond() {
System system;
system.addParticle(2.0);
system.addParticle(2.0);
VariableVerletIntegrator integrator(1e-6);
HarmonicBondForce* forceField = new HarmonicBondForce();
forceField->addBond(0, 1, 1.5, 1);
system.addForce(forceField);
Context context(system, integrator, platform);
vector<Vec3> positions(2);
positions[0] = Vec3(-1, 0, 0);
positions[1] = Vec3(1, 0, 0);
context.setPositions(positions);
// This is simply a harmonic oscillator, so compare it to the analytical solution.
const double freq = 1.0;
State state = context.getState(State::Energy);
const double initialEnergy = state.getKineticEnergy()+state.getPotentialEnergy();
for (int i = 0; i < 1000; ++i) {
state = context.getState(State::Positions | State::Velocities | State::Energy);
double time = state.getTime();
double expectedDist = 1.5+0.5*std::cos(freq*time);
ASSERT_EQUAL_VEC(Vec3(-0.5*expectedDist, 0, 0), state.getPositions()[0], 0.02);
ASSERT_EQUAL_VEC(Vec3(0.5*expectedDist, 0, 0), state.getPositions()[1], 0.02);
double expectedSpeed = -0.5*freq*std::sin(freq*time);
ASSERT_EQUAL_VEC(Vec3(-0.5*expectedSpeed, 0, 0), state.getVelocities()[0], 0.02);
ASSERT_EQUAL_VEC(Vec3(0.5*expectedSpeed, 0, 0), state.getVelocities()[1], 0.02);
double energy = state.getKineticEnergy()+state.getPotentialEnergy();
ASSERT_EQUAL_TOL(initialEnergy, energy, 0.05);
integrator.step(1);
}
ASSERT(state.getTime() > 1.0);
}
void testConstraints() {
const int numParticles = 8;
const double temp = 500.0;
System system;
VariableVerletIntegrator integrator(1e-5);
integrator.setConstraintTolerance(1e-5);
NonbondedForce* forceField = new NonbondedForce();
for (int i = 0; i < numParticles; ++i) {
system.addParticle(i%2 == 0 ? 5.0 : 10.0);
forceField->addParticle((i%2 == 0 ? 0.2 : -0.2), 0.5, 5.0);
}
for (int i = 0; i < numParticles-1; ++i)
system.addConstraint(i, i+1, 1.0);
system.addForce(forceField);
Context context(system, integrator, platform);
vector<Vec3> positions(numParticles);
vector<Vec3> velocities(numParticles);
OpenMM_SFMT::SFMT sfmt;
init_gen_rand(0, sfmt);
for (int i = 0; i < numParticles; ++i) {
positions[i] = Vec3(i/2, (i+1)/2, 0);
velocities[i] = Vec3(genrand_real2(sfmt)-0.5, genrand_real2(sfmt)-0.5, genrand_real2(sfmt)-0.5);
}
context.setPositions(positions);
context.setVelocities(velocities);
// Simulate it and see whether the constraints remain satisfied.
double initialEnergy = 0.0;
for (int i = 0; i < 1000; ++i) {
State state = context.getState(State::Positions | State::Energy | State::Velocities | State::Forces);
for (int j = 0; j < system.getNumConstraints(); ++j) {
int particle1, particle2;
double distance;
system.getConstraintParameters(j, particle1, particle2, distance);
Vec3 p1 = state.getPositions()[particle1];
Vec3 p2 = state.getPositions()[particle2];
double dist = std::sqrt((p1[0]-p2[0])*(p1[0]-p2[0])+(p1[1]-p2[1])*(p1[1]-p2[1])+(p1[2]-p2[2])*(p1[2]-p2[2]));
ASSERT_EQUAL_TOL(distance, dist, 2e-5);
}
double energy = state.getKineticEnergy()+state.getPotentialEnergy();
if (i == 1)
initialEnergy = energy;
else if (i > 1)
ASSERT_EQUAL_TOL(initialEnergy, energy, 0.01);
integrator.step(1);
}
double finalTime = context.getState(State::Positions).getTime();
ASSERT(finalTime > 0.1);
// Now try the stepTo() method.
finalTime += 0.5;
integrator.stepTo(finalTime);
ASSERT_EQUAL(finalTime, context.getState(State::Positions).getTime());
}
void testConstrainedClusters() {
const int numParticles = 7;
const double temp = 500.0;
System system;
VariableVerletIntegrator integrator(1e-5);
integrator.setConstraintTolerance(1e-5);
NonbondedForce* forceField = new NonbondedForce();
for (int i = 0; i < numParticles; ++i) {
system.addParticle(i > 1 ? 1.0 : 10.0);
forceField->addParticle((i%2 == 0 ? 0.2 : -0.2), 0.5, 5.0);
}
system.addConstraint(0, 1, 1.0);
system.addConstraint(0, 2, 1.0);
system.addConstraint(0, 3, 1.0);
system.addConstraint(0, 4, 1.0);
system.addConstraint(1, 5, 1.0);
system.addConstraint(1, 6, 1.0);
system.addConstraint(2, 3, sqrt(2.0));
system.addConstraint(2, 4, sqrt(2.0));
system.addConstraint(3, 4, sqrt(2.0));
system.addConstraint(5, 6, sqrt(2.0));
system.addForce(forceField);
Context context(system, integrator, platform);
vector<Vec3> positions(numParticles);
positions[0] = Vec3(0, 0, 0);
positions[1] = Vec3(1, 0, 0);
positions[2] = Vec3(-1, 0, 0);
positions[3] = Vec3(0, 1, 0);
positions[4] = Vec3(0, 0, 1);
positions[5] = Vec3(2, 0, 0);
positions[6] = Vec3(1, 1, 0);
vector<Vec3> velocities(numParticles);
OpenMM_SFMT::SFMT sfmt;
init_gen_rand(0, sfmt);
for (int i = 0; i < numParticles; ++i)
velocities[i] = Vec3(genrand_real2(sfmt)-0.5, genrand_real2(sfmt)-0.5, genrand_real2(sfmt)-0.5);
context.setPositions(positions);
context.setVelocities(velocities);
// Simulate it and see whether the constraints remain satisfied.
double initialEnergy = 0.0;
for (int i = 0; i < 1000; ++i) {
State state = context.getState(State::Positions | State::Energy | State::Velocities | State::Forces);
for (int j = 0; j < system.getNumConstraints(); ++j) {
int particle1, particle2;
double distance;
system.getConstraintParameters(j, particle1, particle2, distance);
Vec3 p1 = state.getPositions()[particle1];
Vec3 p2 = state.getPositions()[particle2];
double dist = std::sqrt((p1[0]-p2[0])*(p1[0]-p2[0])+(p1[1]-p2[1])*(p1[1]-p2[1])+(p1[2]-p2[2])*(p1[2]-p2[2]));
ASSERT_EQUAL_TOL(distance, dist, 2e-5);
}
double energy = state.getKineticEnergy()+state.getPotentialEnergy();
if (i == 1)
initialEnergy = energy;
else if (i > 1)
ASSERT_EQUAL_TOL(initialEnergy, energy, 0.01);
integrator.step(1);
}
ASSERT(context.getState(State::Positions).getTime() > 0.1);
void runPlatformTests() {
}
void testConstrainedMasslessParticles() {
System system;
system.addParticle(0.0);
system.addParticle(1.0);
system.addConstraint(0, 1, 1.5);
vector<Vec3> positions(2);
positions[0] = Vec3(-1, 0, 0);
positions[1] = Vec3(1, 0, 0);
VariableVerletIntegrator integrator(0.01);
bool failed = false;
try {
// This should throw an exception.
Context context(system, integrator, platform);
}
catch (exception& ex) {
failed = true;
}
ASSERT(failed);
// Now make both particles massless, which should work.
system.setParticleMass(1, 0.0);
Context context(system, integrator, platform);
context.setPositions(positions);
context.setVelocitiesToTemperature(300.0);
integrator.step(1);
State state = context.getState(State::Velocities | State::Positions);
ASSERT_EQUAL(0.0, state.getVelocities()[0][0]);
}
void testArgonBox() {
const int gridSize = 8;
const double mass = 40.0; // Ar atomic mass
const double temp = 120.0; // K
const double epsilon = BOLTZ * temp; // L-J well depth for Ar
const double sigma = 0.34; // L-J size for Ar in nm
const double density = 0.8; // atoms / sigma^3
double cellSize = sigma / pow(density, 0.333);
double boxSize = gridSize * cellSize;
double cutoff = 2.0 * sigma;
// Create a box of argon atoms.
System system;
NonbondedForce* nonbonded = new NonbondedForce();
vector<Vec3> positions;
OpenMM_SFMT::SFMT sfmt;
init_gen_rand(0, sfmt);
const Vec3 half(0.5, 0.5, 0.5);
for (int i = 0; i < gridSize; i++) {
for (int j = 0; j < gridSize; j++) {
for (int k = 0; k < gridSize; k++) {
system.addParticle(mass);
nonbonded->addParticle(0, sigma, epsilon);
positions.push_back((Vec3(i, j, k) + half + Vec3(genrand_real2(sfmt), genrand_real2(sfmt), genrand_real2(sfmt))*0.1) * cellSize);
}
}
}
nonbonded->setNonbondedMethod(NonbondedForce::CutoffPeriodic);
nonbonded->setCutoffDistance(cutoff);
system.setDefaultPeriodicBoxVectors(Vec3(boxSize, 0, 0), Vec3(0, boxSize, 0), Vec3(0, 0, boxSize));
system.addForce(nonbonded);
VariableVerletIntegrator integrator(1e-5);
Context context(system, integrator, platform);
context.setPositions(positions);
context.setVelocitiesToTemperature(temp);
// Equilibrate.
integrator.stepTo(1.0);
// Simulate it and see whether energy remains constant.
State state0 = context.getState(State::Energy);
double initialEnergy = state0.getKineticEnergy() + state0.getPotentialEnergy();
for (int i = 0; i < 20; i++) {
double t = 1.0 + 0.05*(i+1);
integrator.stepTo(t);
State state = context.getState(State::Energy);
double energy = state.getKineticEnergy() + state.getPotentialEnergy();
ASSERT_EQUAL_TOL(initialEnergy, energy, 0.01);
}
}
int main() {
try {
testSingleBond();
testConstraints();
testConstrainedClusters();
testConstrainedMasslessParticles();
testArgonBox();
}
catch(const exception& e) {
cout << "exception: " << e.what() << endl;
return 1;
}
cout << "Done" << endl;
return 0;
}
......@@ -6,7 +6,7 @@
* Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org. *
* *
* Portions copyright (c) 2008 Stanford University and the Authors. *
* Portions copyright (c) 2015 Stanford University and the Authors. *
* Authors: Peter Eastman *
* Contributors: *
* *
......@@ -29,218 +29,8 @@
* USE OR OTHER DEALINGS IN THE SOFTWARE. *
* -------------------------------------------------------------------------- */
/**
* This tests the reference implementation of VerletIntegrator.
*/
#include "ReferenceTests.h"
#include "TestVerletIntegrator.h"
#include "openmm/internal/AssertionUtilities.h"
#include "openmm/Context.h"
#include "ReferencePlatform.h"
#include "openmm/HarmonicBondForce.h"
#include "openmm/NonbondedForce.h"
#include "openmm/System.h"
#include "openmm/VerletIntegrator.h"
#include "SimTKOpenMMRealType.h"
#include "sfmt/SFMT.h"
#include <iostream>
#include <vector>
using namespace OpenMM;
using namespace std;
ReferencePlatform platform;
const double TOL = 1e-5;
void testSingleBond() {
System system;
system.addParticle(2.0);
system.addParticle(2.0);
VerletIntegrator integrator(0.01);
HarmonicBondForce* forceField = new HarmonicBondForce();
forceField->addBond(0, 1, 1.5, 1);
system.addForce(forceField);
Context context(system, integrator, platform);
vector<Vec3> positions(2);
positions[0] = Vec3(-1, 0, 0);
positions[1] = Vec3(1, 0, 0);
context.setPositions(positions);
// This is simply a harmonic oscillator, so compare it to the analytical solution.
const double freq = 1.0;;
State state = context.getState(State::Energy);
const double initialEnergy = state.getKineticEnergy()+state.getPotentialEnergy();
for (int i = 0; i < 1000; ++i) {
state = context.getState(State::Positions | State::Velocities | State::Energy);
double time = state.getTime();
double expectedDist = 1.5+0.5*std::cos(freq*time);
ASSERT_EQUAL_VEC(Vec3(-0.5*expectedDist, 0, 0), state.getPositions()[0], 0.02);
ASSERT_EQUAL_VEC(Vec3(0.5*expectedDist, 0, 0), state.getPositions()[1], 0.02);
double expectedSpeed = -0.5*freq*std::sin(freq*time);
ASSERT_EQUAL_VEC(Vec3(-0.5*expectedSpeed, 0, 0), state.getVelocities()[0], 0.02);
ASSERT_EQUAL_VEC(Vec3(0.5*expectedSpeed, 0, 0), state.getVelocities()[1], 0.02);
double energy = state.getKineticEnergy()+state.getPotentialEnergy();
ASSERT_EQUAL_TOL(initialEnergy, energy, 0.01);
integrator.step(1);
}
}
void testConstraints() {
const int numParticles = 8;
const double temp = 500.0;
System system;
VerletIntegrator integrator(0.002);
integrator.setConstraintTolerance(1e-5);
NonbondedForce* forceField = new NonbondedForce();
for (int i = 0; i < numParticles; ++i) {
system.addParticle(i%2 == 0 ? 5.0 : 10.0);
forceField->addParticle((i%2 == 0 ? 0.2 : -0.2), 0.5, 5.0);
}
for (int i = 0; i < numParticles-1; ++i)
system.addConstraint(i, i+1, 1.0);
system.addForce(forceField);
Context context(system, integrator, platform);
vector<Vec3> positions(numParticles);
vector<Vec3> velocities(numParticles);
OpenMM_SFMT::SFMT sfmt;
init_gen_rand(0, sfmt);
for (int i = 0; i < numParticles; ++i) {
positions[i] = Vec3(i/2, (i+1)/2, 0);
velocities[i] = Vec3(genrand_real2(sfmt)-0.5, genrand_real2(sfmt)-0.5, genrand_real2(sfmt)-0.5);
}
context.setPositions(positions);
context.setVelocities(velocities);
// Simulate it and see whether the constraints remain satisfied.
double initialEnergy = 0.0;
for (int i = 0; i < 1000; ++i) {
State state = context.getState(State::Positions | State::Energy | State::Velocities | State::Forces);
for (int j = 0; j < system.getNumConstraints(); ++j) {
int particle1, particle2;
double distance;
system.getConstraintParameters(j, particle1, particle2, distance);
Vec3 p1 = state.getPositions()[particle1];
Vec3 p2 = state.getPositions()[particle2];
double dist = std::sqrt((p1[0]-p2[0])*(p1[0]-p2[0])+(p1[1]-p2[1])*(p1[1]-p2[1])+(p1[2]-p2[2])*(p1[2]-p2[2]));
ASSERT_EQUAL_TOL(distance, dist, 2e-5);
}
double energy = state.getPotentialEnergy()+state.getKineticEnergy();
if (i == 1)
initialEnergy = energy;
else if (i > 1)
ASSERT_EQUAL_TOL(initialEnergy, energy, 0.01);
integrator.step(1);
}
}
void testConstrainedClusters() {
const int numParticles = 7;
const double temp = 500.0;
System system;
VerletIntegrator integrator(0.001);
integrator.setConstraintTolerance(1e-5);
NonbondedForce* forceField = new NonbondedForce();
for (int i = 0; i < numParticles; ++i) {
system.addParticle(i > 1 ? 1.0 : 10.0);
forceField->addParticle((i%2 == 0 ? 0.2 : -0.2), 0.5, 5.0);
}
system.addConstraint(0, 1, 1.0);
system.addConstraint(0, 2, 1.0);
system.addConstraint(0, 3, 1.0);
system.addConstraint(0, 4, 1.0);
system.addConstraint(1, 5, 1.0);
system.addConstraint(1, 6, 1.0);
system.addConstraint(2, 3, sqrt(2.0));
system.addConstraint(2, 4, sqrt(2.0));
system.addConstraint(3, 4, sqrt(2.0));
system.addConstraint(5, 6, sqrt(2.0));
system.addForce(forceField);
Context context(system, integrator, platform);
vector<Vec3> positions(numParticles);
positions[0] = Vec3(0, 0, 0);
positions[1] = Vec3(1, 0, 0);
positions[2] = Vec3(-1, 0, 0);
positions[3] = Vec3(0, 1, 0);
positions[4] = Vec3(0, 0, 1);
positions[5] = Vec3(2, 0, 0);
positions[6] = Vec3(1, 1, 0);
vector<Vec3> velocities(numParticles);
OpenMM_SFMT::SFMT sfmt;
init_gen_rand(0, sfmt);
for (int i = 0; i < numParticles; ++i)
velocities[i] = Vec3(genrand_real2(sfmt)-0.5, genrand_real2(sfmt)-0.5, genrand_real2(sfmt)-0.5);
context.setPositions(positions);
context.setVelocities(velocities);
// Simulate it and see whether the constraints remain satisfied.
double initialEnergy = 0.0;
for (int i = 0; i < 1000; ++i) {
State state = context.getState(State::Positions | State::Energy | State::Velocities | State::Forces);
for (int j = 0; j < system.getNumConstraints(); ++j) {
int particle1, particle2;
double distance;
system.getConstraintParameters(j, particle1, particle2, distance);
Vec3 p1 = state.getPositions()[particle1];
Vec3 p2 = state.getPositions()[particle2];
double dist = std::sqrt((p1[0]-p2[0])*(p1[0]-p2[0])+(p1[1]-p2[1])*(p1[1]-p2[1])+(p1[2]-p2[2])*(p1[2]-p2[2]));
ASSERT_EQUAL_TOL(distance, dist, 2e-5);
}
double energy = state.getPotentialEnergy()+state.getKineticEnergy();
if (i == 1)
initialEnergy = energy;
else if (i > 1)
ASSERT_EQUAL_TOL(initialEnergy, energy, 0.01);
integrator.step(1);
}
}
void testConstrainedMasslessParticles() {
System system;
system.addParticle(0.0);
system.addParticle(1.0);
system.addConstraint(0, 1, 1.5);
vector<Vec3> positions(2);
positions[0] = Vec3(-1, 0, 0);
positions[1] = Vec3(1, 0, 0);
VerletIntegrator integrator(0.01);
bool failed = false;
try {
// This should throw an exception.
Context context(system, integrator, platform);
}
catch (exception& ex) {
failed = true;
}
ASSERT(failed);
// Now make both particles massless, which should work.
system.setParticleMass(1, 0.0);
Context context(system, integrator, platform);
context.setPositions(positions);
context.setVelocitiesToTemperature(300.0);
integrator.step(1);
State state = context.getState(State::Velocities | State::Positions);
ASSERT_EQUAL(0.0, state.getVelocities()[0][0]);
}
int main() {
try {
testSingleBond();
testConstraints();
testConstrainedClusters();
testConstrainedMasslessParticles();
}
catch(const exception& e) {
cout << "exception: " << e.what() << endl;
return 1;
}
cout << "Done" << endl;
return 0;
void runPlatformTests() {
}
......@@ -6,7 +6,7 @@
* Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org. *
* *
* Portions copyright (c) 2012-2014 Stanford University and the Authors. *
* Portions copyright (c) 2015 Stanford University and the Authors. *
* Authors: Peter Eastman *
* Contributors: *
* *
......@@ -29,408 +29,8 @@
* USE OR OTHER DEALINGS IN THE SOFTWARE. *
* -------------------------------------------------------------------------- */
/**
* This tests the reference implementation of virtual sites.
*/
#include "ReferenceTests.h"
#include "TestVirtualSites.h"
#include "openmm/internal/AssertionUtilities.h"
#include "openmm/Context.h"
#include "ReferencePlatform.h"
#include "openmm/CustomBondForce.h"
#include "openmm/CustomExternalForce.h"
#include "openmm/LangevinIntegrator.h"
#include "openmm/NonbondedForce.h"
#include "openmm/System.h"
#include "openmm/VerletIntegrator.h"
#include "openmm/VirtualSite.h"
#include "sfmt/SFMT.h"
#include <iostream>
#include <vector>
using namespace OpenMM;
using namespace std;
ReferencePlatform platform;
/**
* Check that massless particles are handled correctly.
*/
void testMasslessParticle() {
System system;
system.addParticle(0.0);
system.addParticle(1.0);
CustomBondForce* bonds = new CustomBondForce("-1/r");
system.addForce(bonds);
vector<double> params;
bonds->addBond(0, 1, params);
VerletIntegrator integrator(0.002);
Context context(system, integrator, platform);
vector<Vec3> positions(2);
positions[0] = Vec3(0, 0, 0);
positions[1] = Vec3(1, 0, 0);
context.setPositions(positions);
vector<Vec3> velocities(2);
velocities[0] = Vec3(0, 0, 0);
velocities[1] = Vec3(0, 1, 0);
context.setVelocities(velocities);
// The second particle should move in a circular orbit around the first one.
// Compare it to the analytical solution.
for (int i = 0; i < 1000; ++i) {
State state = context.getState(State::Positions | State::Velocities | State::Forces);
double time = state.getTime();
ASSERT_EQUAL_VEC(Vec3(0, 0, 0), state.getPositions()[0], 0.0);
ASSERT_EQUAL_VEC(Vec3(0, 0, 0), state.getVelocities()[0], 0.0);
ASSERT_EQUAL_VEC(Vec3(cos(time), sin(time), 0), state.getPositions()[1], 0.01);
ASSERT_EQUAL_VEC(Vec3(-sin(time), cos(time), 0), state.getVelocities()[1], 0.01);
integrator.step(1);
}
}
/**
* 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-10);
ASSERT_EQUAL_VEC(Vec3(0.1+0.3*0.8, 0, 0), state.getForces()[0], 1e-10);
ASSERT_EQUAL_VEC(Vec3(0.2+0.3*0.2, 0, 0), state.getForces()[1], 1e-10);
integrator.step(1);
}
}
/**
* Test a ThreeParticleAverageSite virtual site.
*/
void testThreeParticleAverage() {
System system;
system.addParticle(1.0);
system.addParticle(1.0);
system.addParticle(1.0);
system.addParticle(0.0);
system.setVirtualSite(3, new ThreeParticleAverageSite(0, 1, 2, 0.2, 0.3, 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();
ASSERT_EQUAL_VEC(pos[0]*0.2+pos[1]*0.3+pos[2]*0.5, pos[3], 1e-10);
ASSERT_EQUAL_VEC(Vec3(0.1+0.4*0.2, 0, 0), state.getForces()[0], 1e-10);
ASSERT_EQUAL_VEC(Vec3(0.2+0.4*0.3, 0, 0), state.getForces()[1], 1e-10);
ASSERT_EQUAL_VEC(Vec3(0.3+0.4*0.5, 0, 0), state.getForces()[2], 1e-10);
integrator.step(1);
}
}
/**
* 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-10);
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-10);
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-10);
ASSERT_EQUAL_VEC(Vec3(0.2, 0, 0)+f2, f[1], 1e-10);
ASSERT_EQUAL_VEC(Vec3(0.3, 0, 0)+f3, f[2], 1e-10);
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-10);
// 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)
}
}
/**
* 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, 1e-10);
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, 1e-10);
integrator.step(1);
}
}
int main() {
try {
testMasslessParticle();
testTwoParticleAverage();
testThreeParticleAverage();
testOutOfPlane();
testLocalCoordinates();
testConservationLaws();
}
catch(const exception& e) {
cout << "exception: " << e.what() << endl;
return 1;
}
cout << "Done" << endl;
return 0;
void runPlatformTests() {
}
positions[0] = Vec3(1.066000,1.628000,0.835000);
positions[1] = Vec3(1.072000,0.428000,0.190000);
positions[2] = Vec3(0.524000,1.442000,1.160000);
positions[3] = Vec3(2.383000,1.524000,1.119000);
positions[4] = Vec3(0.390000,1.441000,0.575000);
positions[5] = Vec3(0.618000,0.399000,0.819000);
positions[6] = Vec3(1.003000,1.257000,1.543000);
positions[7] = Vec3(2.933000,1.569000,0.642000);
positions[8] = Vec3(0.849000,0.739000,0.089000);
positions[9] = Vec3(0.060000,0.794000,0.766000);
positions[10] = Vec3(1.652000,1.405000,1.010000);
positions[11] = Vec3(2.843000,1.533000,1.781000);
positions[12] = Vec3(0.952000,1.309000,0.996000);
positions[13] = Vec3(1.847000,1.402000,0.313000);
positions[14] = Vec3(2.674000,0.083000,1.691000);
positions[15] = Vec3(1.763000,2.104000,0.728000);
positions[16] = Vec3(0.914000,0.574000,0.982000);
positions[17] = Vec3(0.514000,0.078000,0.891000);
positions[18] = Vec3(0.538000,0.766000,1.110000);
positions[19] = Vec3(0.808000,0.676000,0.570000);
positions[20] = Vec3(0.178000,0.014000,0.628000);
positions[21] = Vec3(1.329000,1.333000,0.339000);
positions[22] = Vec3(1.029000,1.678000,0.503000);
positions[23] = Vec3(1.423000,1.767000,1.104000);
positions[24] = Vec3(1.966000,1.051000,0.282000);
positions[25] = Vec3(1.596000,1.971000,0.194000);
positions[26] = Vec3(1.025000,1.043000,2.809000);
positions[27] = Vec3(1.628000,2.614000,0.088000);
positions[28] = Vec3(0.440000,0.606000,0.141000);
positions[29] = Vec3(1.050000,2.821000,2.517000);
positions[30] = Vec3(0.644000,1.604000,0.770000);
positions[31] = Vec3(0.637000,0.917000,0.392000);
positions[32] = Vec3(0.611000,2.768000,0.013000);
positions[33] = Vec3(1.892000,0.660000,0.473000);
positions[34] = Vec3(1.052000,2.081000,0.982000);
positions[35] = Vec3(1.508000,2.300000,0.439000);
positions[36] = Vec3(2.617000,0.328000,1.099000);
positions[37] = Vec3(0.910000,0.040000,0.259000);
positions[38] = Vec3(1.195000,1.494000,1.202000);
positions[39] = Vec3(2.657000,0.997000,0.564000);
positions[40] = Vec3(1.465000,1.580000,0.648000);
positions[41] = Vec3(0.154000,2.538000,1.331000);
positions[42] = Vec3(0.849000,1.476000,1.365000);
positions[43] = Vec3(0.898000,0.987000,1.178000);
positions[44] = Vec3(0.958000,0.656000,1.358000);
positions[45] = Vec3(1.067000,0.934000,0.211000);
positions[46] = Vec3(1.030000,0.319000,1.281000);
positions[47] = Vec3(2.709000,0.807000,0.240000);
positions[48] = Vec3(0.837000,1.362000,0.588000);
positions[49] = Vec3(2.080000,0.791000,2.947000);
positions[50] = Vec3(0.200000,0.266000,1.474000);
positions[51] = Vec3(0.848000,0.379000,1.625000);
positions[52] = Vec3(0.637000,1.071000,0.821000);
positions[53] = Vec3(1.324000,0.757000,2.951000);
positions[54] = Vec3(2.666000,0.935000,1.373000);
positions[55] = Vec3(1.584000,1.025000,1.703000);
positions[56] = Vec3(1.699000,0.636000,0.038000);
positions[57] = Vec3(1.099000,1.644000,1.879000);
positions[58] = Vec3(2.897000,1.302000,1.522000);
positions[59] = Vec3(1.753000,0.949000,2.885000);
positions[60] = Vec3(2.502000,1.321000,0.752000);
positions[61] = Vec3(0.545000,0.193000,1.959000);
positions[62] = Vec3(1.098000,2.646000,1.706000);
positions[63] = Vec3(0.001000,1.205000,0.670000);
positions[64] = Vec3(2.997000,0.061000,1.040000);
positions[65] = Vec3(0.662000,0.828000,1.535000);
positions[66] = Vec3(1.252000,1.246000,0.780000);
positions[67] = Vec3(1.173000,0.472000,0.810000);
positions[68] = Vec3(0.124000,0.622000,2.992000);
positions[69] = Vec3(1.036000,0.883000,0.848000);
positions[70] = Vec3(1.423000,2.146000,1.340000);
positions[71] = Vec3(2.391000,1.136000,1.165000);
positions[72] = Vec3(1.189000,2.961000,0.425000);
positions[73] = Vec3(1.584000,2.500000,0.782000);
positions[74] = Vec3(0.565000,1.122000,1.240000);
positions[75] = Vec3(1.733000,1.716000,1.763000);
positions[76] = Vec3(1.548000,1.522000,0.041000);
positions[77] = Vec3(1.485000,0.561000,0.369000);
positions[78] = Vec3(0.350000,1.661000,0.928000);
positions[79] = Vec3(1.653000,1.223000,0.578000);
positions[80] = Vec3(0.648000,1.349000,0.253000);
positions[81] = Vec3(0.340000,1.820000,0.483000);
positions[82] = Vec3(2.926000,0.119000,1.421000);
positions[83] = Vec3(1.512000,1.084000,0.156000);
positions[84] = Vec3(1.600000,2.115000,1.792000);
positions[85] = Vec3(1.089000,0.934000,1.584000);
positions[86] = Vec3(1.276000,1.104000,1.230000);
positions[87] = Vec3(0.485000,0.305000,0.428000);
positions[88] = Vec3(1.317000,1.261000,1.795000);
positions[89] = Vec3(0.039000,1.413000,1.085000);
positions[90] = Vec3(0.453000,0.701000,0.605000);
positions[91] = Vec3(1.283000,1.937000,0.752000);
positions[92] = Vec3(0.212000,1.416000,1.447000);
positions[93] = Vec3(0.203000,0.358000,0.723000);
positions[94] = Vec3(0.556000,0.445000,1.364000);
positions[95] = Vec3(1.436000,0.861000,0.911000);
positions[96] = Vec3(0.358000,0.966000,0.176000);
positions[97] = Vec3(1.478000,2.715000,0.427000);
positions[98] = Vec3(1.581000,0.575000,0.809000);
positions[99] = Vec3(1.007000,2.153000,2.887000);
positions[100] = Vec3(2.343000,0.663000,2.513000);
positions[101] = Vec3(2.105000,0.649000,1.635000);
positions[102] = Vec3(0.875000,0.743000,2.459000);
positions[103] = Vec3(0.229000,1.315000,1.879000);
positions[104] = Vec3(0.285000,0.935000,1.700000);
positions[105] = Vec3(2.269000,1.284000,2.234000);
positions[106] = Vec3(1.406000,1.149000,2.767000);
positions[107] = Vec3(1.076000,0.220000,1.849000);
positions[108] = Vec3(2.001000,1.532000,2.881000);
positions[109] = Vec3(2.893000,0.485000,1.860000);
positions[110] = Vec3(1.621000,1.786000,2.624000);
positions[111] = Vec3(0.500000,0.616000,1.818000);
positions[112] = Vec3(0.938000,2.978000,2.104000);
positions[113] = Vec3(0.550000,2.081000,0.454000);
positions[114] = Vec3(1.121000,0.685000,2.196000);
positions[115] = Vec3(1.088000,1.385000,2.184000);
positions[116] = Vec3(1.122000,2.705000,2.080000);
positions[117] = Vec3(0.918000,1.767000,2.861000);
positions[118] = Vec3(2.748000,0.232000,2.126000);
positions[119] = Vec3(1.238000,2.766000,0.109000);
positions[120] = Vec3(1.380000,0.785000,1.961000);
positions[121] = Vec3(1.236000,1.757000,0.150000);
positions[122] = Vec3(1.339000,2.187000,2.592000);
positions[123] = Vec3(1.414000,0.342000,2.714000);
positions[124] = Vec3(1.310000,0.770000,2.589000);
positions[125] = Vec3(1.686000,0.765000,2.321000);
positions[126] = Vec3(1.659000,1.367000,2.780000);
positions[127] = Vec3(0.141000,0.095000,1.903000);
positions[128] = Vec3(2.084000,1.002000,2.520000);
positions[129] = Vec3(2.819000,1.286000,2.626000);
positions[130] = Vec3(1.257000,1.044000,2.401000);
positions[131] = Vec3(1.064000,0.546000,2.839000);
positions[132] = Vec3(0.078000,1.246000,0.010000);
positions[133] = Vec3(1.506000,0.420000,2.223000);
positions[134] = Vec3(1.778000,0.699000,1.920000);
positions[135] = Vec3(1.315000,1.721000,2.733000);
positions[136] = Vec3(0.114000,0.281000,0.279000);
positions[137] = Vec3(1.082000,1.421000,2.596000);
positions[138] = Vec3(3.001000,0.592000,2.276000);
positions[139] = Vec3(1.384000,2.227000,2.992000);
positions[140] = Vec3(1.353000,0.044000,1.985000);
positions[141] = Vec3(1.367000,1.832000,2.383000);
positions[142] = Vec3(0.853000,1.119000,2.230000);
positions[143] = Vec3(1.675000,1.482000,2.295000);
positions[144] = Vec3(1.334000,1.890000,1.904000);
positions[145] = Vec3(1.630000,0.140000,2.939000);
positions[146] = Vec3(0.195000,1.290000,2.300000);
positions[147] = Vec3(2.178000,1.173000,3.001000);
positions[148] = Vec3(0.637000,0.655000,2.126000);
positions[149] = Vec3(0.993000,1.796000,2.529000);
positions[150] = Vec3(0.910000,0.701000,1.845000);
positions[151] = Vec3(0.191000,2.128000,0.355000);
positions[152] = Vec3(0.692000,1.163000,2.578000);
positions[153] = Vec3(1.154000,1.052000,1.974000);
positions[154] = Vec3(1.682000,0.335000,2.509000);
positions[155] = Vec3(0.569000,1.032000,1.895000);
positions[156] = Vec3(1.800000,2.796000,1.295000);
positions[157] = Vec3(2.517000,2.347000,2.878000);
positions[158] = Vec3(0.639000,2.470000,1.678000);
positions[159] = Vec3(0.634000,2.006000,1.829000);
positions[160] = Vec3(0.892000,0.215000,0.566000);
positions[161] = Vec3(1.800000,2.143000,1.491000);
positions[162] = Vec3(1.898000,0.226000,2.765000);
positions[163] = Vec3(0.791000,1.738000,0.260000);
positions[164] = Vec3(0.437000,1.740000,2.048000);
positions[165] = Vec3(1.687000,2.438000,1.166000);
positions[166] = Vec3(1.337000,2.304000,1.643000);
positions[167] = Vec3(1.270000,2.397000,1.033000);
positions[168] = Vec3(0.702000,2.429000,0.591000);
positions[169] = Vec3(0.842000,1.976000,0.724000);
positions[170] = Vec3(1.965000,0.095000,1.206000);
positions[171] = Vec3(0.355000,2.710000,0.618000);
positions[172] = Vec3(0.745000,1.434000,2.781000);
positions[173] = Vec3(0.707000,2.171000,1.502000);
positions[174] = Vec3(1.294000,2.696000,0.847000);
positions[175] = Vec3(1.143000,2.075000,0.276000);
positions[176] = Vec3(1.111000,2.474000,0.312000);
positions[177] = Vec3(1.144000,2.316000,0.633000);
positions[178] = Vec3(1.335000,0.292000,0.515000);
positions[179] = Vec3(1.926000,2.813000,2.703000);
positions[180] = Vec3(0.559000,2.314000,2.904000);
positions[181] = Vec3(1.308000,1.605000,1.534000);
positions[182] = Vec3(0.773000,2.913000,1.217000);
positions[183] = Vec3(1.612000,0.082000,1.027000);
positions[184] = Vec3(1.510000,0.287000,1.787000);
positions[185] = Vec3(0.716000,1.424000,1.843000);
positions[186] = Vec3(1.267000,0.685000,1.160000);
positions[187] = Vec3(0.306000,1.653000,1.717000);
positions[188] = Vec3(0.349000,0.020000,1.275000);
positions[189] = Vec3(0.166000,1.979000,0.804000);
positions[190] = Vec3(1.523000,2.992000,0.711000);
positions[191] = Vec3(1.998000,2.146000,0.088000);
positions[192] = Vec3(0.047000,2.513000,0.642000);
positions[193] = Vec3(0.501000,1.793000,1.438000);
positions[194] = Vec3(1.099000,2.010000,1.626000);
positions[195] = Vec3(2.580000,2.854000,1.328000);
positions[196] = Vec3(1.080000,2.779000,1.190000);
positions[197] = Vec3(0.901000,2.561000,0.948000);
positions[198] = Vec3(0.920000,2.990000,0.844000);
positions[199] = Vec3(0.819000,2.924000,1.711000);
positions[200] = Vec3(0.434000,1.516000,0.063000);
positions[201] = Vec3(1.470000,0.058000,0.231000);
positions[202] = Vec3(0.530000,3.005000,1.550000);
positions[203] = Vec3(0.447000,2.330000,1.277000);
positions[204] = Vec3(1.632000,2.683000,1.593000);
positions[205] = Vec3(0.885000,1.835000,2.072000);
positions[206] = Vec3(0.868000,2.601000,1.425000);
positions[207] = Vec3(0.720000,2.242000,0.907000);
positions[208] = Vec3(1.194000,0.144000,1.065000);
positions[209] = Vec3(0.448000,2.485000,0.959000);
positions[210] = Vec3(1.377000,2.694000,1.352000);
positions[211] = Vec3(1.305000,2.928000,2.713000);
positions[212] = Vec3(1.784000,2.456000,1.981000);
positions[213] = Vec3(0.354000,2.136000,1.563000);
positions[214] = Vec3(0.489000,2.000000,1.108000);
positions[215] = Vec3(1.884000,2.221000,0.461000);
positions[216] = Vec3(1.860000,2.540000,0.306000);
positions[217] = Vec3(1.753000,2.335000,2.768000);
positions[218] = Vec3(1.536000,2.441000,2.344000);
positions[219] = Vec3(0.531000,0.025000,2.235000);
positions[220] = Vec3(0.809000,0.011000,2.834000);
positions[221] = Vec3(0.289000,2.614000,2.879000);
positions[222] = Vec3(0.613000,1.891000,2.337000);
positions[223] = Vec3(0.507000,0.037000,2.694000);
positions[224] = Vec3(0.882000,2.185000,2.583000);
positions[225] = Vec3(0.503000,2.051000,2.615000);
positions[226] = Vec3(1.907000,1.956000,2.831000);
positions[227] = Vec3(2.833000,2.769000,1.644000);
positions[228] = Vec3(1.141000,0.113000,2.945000);
positions[229] = Vec3(0.600000,1.338000,2.200000);
positions[230] = Vec3(0.904000,2.360000,1.952000);
positions[231] = Vec3(0.738000,1.568000,2.437000);
positions[232] = Vec3(1.136000,2.535000,2.805000);
positions[233] = Vec3(1.430000,2.767000,2.321000);
positions[234] = Vec3(1.078000,2.470000,2.385000);
positions[235] = Vec3(0.296000,2.376000,2.560000);
positions[236] = Vec3(0.719000,0.300000,0.075000);
positions[237] = Vec3(0.518000,1.911000,0.080000);
positions[238] = Vec3(0.381000,1.570000,2.450000);
positions[239] = Vec3(0.716000,2.581000,2.697000);
positions[240] = Vec3(1.473000,2.617000,1.936000);
positions[241] = Vec3(0.421000,2.449000,0.229000);
positions[242] = Vec3(0.425000,2.817000,1.910000);
positions[243] = Vec3(1.312000,2.294000,2.057000);
positions[244] = Vec3(1.239000,0.007000,1.539000);
positions[245] = Vec3(0.822000,0.379000,2.086000);
positions[246] = Vec3(0.560000,2.562000,2.227000);
positions[247] = Vec3(0.863000,2.417000,0.050000);
positions[248] = Vec3(1.263000,0.151000,2.332000);
positions[249] = Vec3(0.237000,0.208000,2.336000);
positions[250] = Vec3(0.437000,2.370000,1.910000);
positions[251] = Vec3(1.119000,2.058000,2.207000);
positions[252] = Vec3(1.960000,1.749000,0.118000);
positions[253] = Vec3(2.415000,0.870000,2.757000);
positions[254] = Vec3(1.781000,0.342000,0.366000);
positions[255] = Vec3(2.172000,1.279000,1.421000);
positions[256] = Vec3(1.986000,0.715000,1.301000);
positions[257] = Vec3(1.657000,1.804000,0.810000);
positions[258] = Vec3(2.405000,1.202000,0.416000);
positions[259] = Vec3(1.932000,1.457000,0.786000);
positions[260] = Vec3(1.901000,1.271000,1.207000);
positions[261] = Vec3(1.864000,0.301000,0.810000);
positions[262] = Vec3(1.658000,0.673000,1.558000);
positions[263] = Vec3(2.637000,2.247000,0.396000);
positions[264] = Vec3(1.353000,0.369000,1.438000);
positions[265] = Vec3(0.530000,2.688000,1.346000);
positions[266] = Vec3(0.237000,0.485000,1.047000);
positions[267] = Vec3(2.806000,0.601000,0.822000);
positions[268] = Vec3(1.617000,2.018000,2.136000);
positions[269] = Vec3(2.000000,2.898000,0.022000);
positions[270] = Vec3(2.049000,1.883000,1.001000);
positions[271] = Vec3(2.477000,0.355000,1.786000);
positions[272] = Vec3(1.646000,0.983000,1.266000);
positions[273] = Vec3(1.683000,2.097000,1.114000);
positions[274] = Vec3(2.161000,0.921000,1.065000);
positions[275] = Vec3(2.099000,0.463000,1.942000);
positions[276] = Vec3(2.561000,1.638000,0.572000);
positions[277] = Vec3(2.205000,0.395000,1.005000);
positions[278] = Vec3(2.836000,0.203000,0.698000);
positions[279] = Vec3(2.662000,0.909000,0.966000);
positions[280] = Vec3(0.334000,0.350000,2.767000);
positions[281] = Vec3(2.241000,2.934000,1.248000);
positions[282] = Vec3(2.599000,2.953000,0.921000);
positions[283] = Vec3(2.219000,0.262000,0.058000);
positions[284] = Vec3(0.274000,0.656000,1.456000);
positions[285] = Vec3(1.814000,1.008000,0.882000);
positions[286] = Vec3(2.793000,1.395000,0.316000);
positions[287] = Vec3(0.773000,1.753000,1.639000);
positions[288] = Vec3(2.321000,0.994000,1.591000);
positions[289] = Vec3(2.243000,2.255000,1.690000);
positions[290] = Vec3(0.178000,1.342000,0.327000);
positions[291] = Vec3(1.623000,1.756000,1.426000);
positions[292] = Vec3(2.252000,0.109000,0.375000);
positions[293] = Vec3(3.003000,1.895000,1.895000);
positions[294] = Vec3(0.407000,0.831000,2.756000);
positions[295] = Vec3(2.193000,0.956000,0.632000);
positions[296] = Vec3(2.405000,0.641000,1.107000);
positions[297] = Vec3(2.361000,0.958000,0.162000);
positions[298] = Vec3(2.173000,1.544000,0.528000);
positions[299] = Vec3(1.565000,1.380000,1.428000);
positions[300] = Vec3(2.342000,0.538000,0.253000);
positions[301] = Vec3(1.910000,0.701000,0.954000);
positions[302] = Vec3(2.910000,0.288000,2.938000);
positions[303] = Vec3(0.257000,1.189000,0.958000);
positions[304] = Vec3(0.134000,1.775000,1.243000);
positions[305] = Vec3(2.476000,1.583000,1.956000);
positions[306] = Vec3(1.838000,1.791000,2.354000);
positions[307] = Vec3(1.906000,1.338000,1.696000);
positions[308] = Vec3(2.413000,2.869000,0.166000);
positions[309] = Vec3(3.006000,1.038000,1.322000);
positions[310] = Vec3(1.961000,0.962000,1.605000);
positions[311] = Vec3(0.082000,2.857000,0.020000);
positions[312] = Vec3(2.408000,1.499000,0.062000);
positions[313] = Vec3(2.349000,0.267000,1.415000);
positions[314] = Vec3(2.327000,1.717000,2.350000);
positions[315] = Vec3(2.928000,0.810000,1.582000);
positions[316] = Vec3(2.150000,0.336000,0.576000);
positions[317] = Vec3(2.664000,1.085000,2.962000);
positions[318] = Vec3(2.851000,0.670000,1.174000);
positions[319] = Vec3(1.954000,1.013000,1.975000);
positions[320] = Vec3(2.474000,1.542000,1.545000);
positions[321] = Vec3(2.826000,0.455000,1.490000);
positions[322] = Vec3(2.140000,2.826000,0.558000);
positions[323] = Vec3(2.151000,1.684000,1.780000);
positions[324] = Vec3(0.174000,0.673000,0.397000);
positions[325] = Vec3(0.066000,1.708000,0.160000);
positions[326] = Vec3(2.158000,0.303000,2.582000);
positions[327] = Vec3(2.602000,1.611000,2.632000);
positions[328] = Vec3(2.566000,1.138000,2.465000);
positions[329] = Vec3(2.026000,1.443000,2.477000);
positions[330] = Vec3(2.365000,0.309000,2.255000);
positions[331] = Vec3(1.636000,1.107000,2.058000);
positions[332] = Vec3(2.522000,2.584000,2.399000);
positions[333] = Vec3(2.537000,2.900000,2.158000);
positions[334] = Vec3(2.660000,0.537000,2.577000);
positions[335] = Vec3(2.679000,1.158000,1.724000);
positions[336] = Vec3(0.220000,1.894000,2.498000);
positions[337] = Vec3(2.266000,1.248000,1.837000);
positions[338] = Vec3(0.055000,1.656000,2.128000);
positions[339] = Vec3(2.899000,1.902000,2.823000);
positions[340] = Vec3(0.085000,2.994000,2.720000);
positions[341] = Vec3(0.013000,0.889000,2.468000);
positions[342] = Vec3(1.804000,0.372000,1.636000);
positions[343] = Vec3(0.201000,1.616000,2.824000);
positions[344] = Vec3(0.369000,1.273000,2.699000);
positions[345] = Vec3(2.996000,0.355000,2.596000);
positions[346] = Vec3(2.867000,1.314000,2.107000);
positions[347] = Vec3(2.611000,0.563000,2.140000);
positions[348] = Vec3(2.676000,2.954000,2.955000);
positions[349] = Vec3(0.256000,0.848000,2.062000);
positions[350] = Vec3(2.530000,0.028000,2.528000);
positions[351] = Vec3(0.537000,1.273000,1.596000);
positions[352] = Vec3(0.004000,1.004000,0.401000);
positions[353] = Vec3(1.676000,1.060000,2.463000);
positions[354] = Vec3(2.622000,1.473000,2.257000);
positions[355] = Vec3(2.917000,2.991000,2.316000);
positions[356] = Vec3(0.672000,1.123000,2.984000);
positions[357] = Vec3(2.229000,1.806000,2.673000);
positions[358] = Vec3(0.463000,0.951000,2.383000);
positions[359] = Vec3(2.126000,0.049000,2.037000);
positions[360] = Vec3(2.868000,0.876000,2.015000);
positions[361] = Vec3(2.720000,2.582000,0.079000);
positions[362] = Vec3(1.966000,0.693000,2.624000);
positions[363] = Vec3(1.971000,0.398000,2.318000);
positions[364] = Vec3(0.337000,0.630000,2.458000);
positions[365] = Vec3(2.562000,1.044000,2.040000);
positions[366] = Vec3(2.817000,1.485000,2.963000);
positions[367] = Vec3(2.514000,0.621000,2.992000);
positions[368] = Vec3(3.000000,1.551000,2.496000);
positions[369] = Vec3(0.698000,2.167000,2.180000);
positions[370] = Vec3(2.693000,0.849000,2.389000);
positions[371] = Vec3(2.092000,2.565000,2.986000);
positions[372] = Vec3(2.010000,3.001000,0.819000);
positions[373] = Vec3(2.392000,2.622000,1.636000);
positions[374] = Vec3(2.086000,2.325000,1.340000);
positions[375] = Vec3(2.578000,2.971000,0.502000);
positions[376] = Vec3(1.871000,2.789000,2.225000);
positions[377] = Vec3(2.230000,2.985000,1.594000);
positions[378] = Vec3(2.860000,2.788000,0.729000);
positions[379] = Vec3(2.051000,1.928000,1.472000);
positions[380] = Vec3(2.307000,2.219000,1.067000);
positions[381] = Vec3(2.369000,2.572000,1.289000);
positions[382] = Vec3(2.206000,1.924000,0.693000);
positions[383] = Vec3(1.984000,2.058000,2.005000);
positions[384] = Vec3(2.287000,1.854000,0.317000);
positions[385] = Vec3(2.525000,0.345000,0.686000);
positions[386] = Vec3(2.933000,1.920000,1.053000);
positions[387] = Vec3(0.324000,2.324000,0.601000);
positions[388] = Vec3(2.042000,1.576000,1.277000);
positions[389] = Vec3(0.031000,2.376000,0.949000);
positions[390] = Vec3(2.519000,2.250000,1.465000);
positions[391] = Vec3(0.221000,2.722000,1.652000);
positions[392] = Vec3(2.409000,2.361000,2.051000);
positions[393] = Vec3(2.472000,1.917000,1.673000);
positions[394] = Vec3(0.999000,2.715000,0.562000);
positions[395] = Vec3(1.669000,0.017000,1.508000);
positions[396] = Vec3(1.924000,1.777000,0.542000);
positions[397] = Vec3(2.635000,2.634000,1.905000);
positions[398] = Vec3(2.042000,2.628000,1.025000);
positions[399] = Vec3(2.694000,1.974000,2.009000);
positions[400] = Vec3(2.988000,2.221000,1.333000);
positions[401] = Vec3(1.772000,0.196000,1.978000);
positions[402] = Vec3(2.893000,2.961000,0.283000);
positions[403] = Vec3(2.615000,0.261000,0.245000);
positions[404] = Vec3(2.797000,2.521000,1.412000);
positions[405] = Vec3(0.013000,2.497000,0.246000);
positions[406] = Vec3(1.875000,2.861000,1.801000);
positions[407] = Vec3(2.800000,2.617000,1.049000);
positions[408] = Vec3(2.824000,1.858000,1.487000);
positions[409] = Vec3(2.434000,1.868000,1.275000);
positions[410] = Vec3(2.814000,0.526000,0.384000);
positions[411] = Vec3(2.844000,2.545000,2.246000);
positions[412] = Vec3(1.896000,2.587000,0.719000);
positions[413] = Vec3(0.350000,0.055000,0.076000);
positions[414] = Vec3(2.686000,1.784000,0.222000);
positions[415] = Vec3(2.724000,1.604000,0.989000);
positions[416] = Vec3(0.807000,1.761000,1.122000);
positions[417] = Vec3(2.120000,2.382000,2.226000);
positions[418] = Vec3(2.058000,1.587000,2.067000);
positions[419] = Vec3(2.904000,2.571000,2.686000);
positions[420] = Vec3(2.228000,2.910000,2.410000);
positions[421] = Vec3(2.797000,2.142000,0.114000);
positions[422] = Vec3(2.905000,1.875000,0.480000);
positions[423] = Vec3(1.881000,2.565000,2.469000);
positions[424] = Vec3(2.404000,1.929000,2.999000);
positions[425] = Vec3(2.389000,2.814000,2.782000);
positions[426] = Vec3(2.520000,0.301000,2.815000);
positions[427] = Vec3(2.726000,1.907000,2.339000);
positions[428] = Vec3(2.880000,2.273000,2.500000);
positions[429] = Vec3(2.574000,2.045000,2.716000);
positions[430] = Vec3(2.988000,2.288000,2.001000);
positions[431] = Vec3(0.011000,2.341000,2.904000);
positions[432] = Vec3(0.215000,2.265000,2.257000);
positions[433] = Vec3(2.268000,2.311000,0.234000);
positions[434] = Vec3(2.462000,2.621000,0.550000);
positions[435] = Vec3(1.530000,2.540000,2.728000);
positions[436] = Vec3(2.162000,2.306000,2.687000);
positions[437] = Vec3(2.748000,2.301000,1.734000);
positions[438] = Vec3(2.334000,1.976000,2.041000);
positions[439] = Vec3(1.981000,2.076000,2.443000);
positions[440] = Vec3(2.301000,1.367000,2.665000);
positions[441] = Vec3(2.399000,2.164000,2.403000);
positions[442] = Vec3(0.244000,2.713000,2.257000);
positions[443] = Vec3(0.683000,0.488000,2.781000);
positions[444] = Vec3(2.194000,2.711000,1.993000);
positions[445] = Vec3(2.947000,2.848000,2.001000);
positions[446] = Vec3(0.223000,1.981000,2.913000);
positions[447] = Vec3(0.010000,1.226000,0.917000);
positions[448] = Vec3(1.911000,0.426000,0.582000);
positions[449] = Vec3(2.204000,0.015000,0.136000);
positions[450] = Vec3(0.927000,0.138000,1.645000);
positions[451] = Vec3(0.155000,0.885000,1.479000);
positions[452] = Vec3(1.550000,1.933000,1.261000);
positions[453] = Vec3(1.304000,0.407000,0.287000);
positions[454] = Vec3(0.270000,1.384000,2.910000);
positions[455] = Vec3(0.516000,1.817000,1.695000);
positions[456] = Vec3(1.458000,2.879000,1.523000);
positions[457] = Vec3(1.702000,1.670000,0.593000);
positions[458] = Vec3(1.974000,1.380000,0.534000);
positions[459] = Vec3(2.835000,1.185000,0.479000);
positions[460] = Vec3(0.548000,2.979000,1.126000);
positions[461] = Vec3(1.202000,2.174000,1.466000);
positions[462] = Vec3(1.237000,1.701000,0.653000);
positions[463] = Vec3(2.939000,0.761000,0.349000);
positions[464] = Vec3(1.667000,2.119000,0.377000);
positions[465] = Vec3(1.196000,0.552000,1.372000);
positions[466] = Vec3(1.416000,0.901000,1.178000);
positions[467] = Vec3(0.519000,1.577000,2.227000);
positions[468] = Vec3(1.214000,1.281000,1.063000);
positions[469] = Vec3(0.822000,0.433000,1.375000);
positions[470] = Vec3(0.095000,2.760000,0.604000);
positions[471] = Vec3(1.325000,2.144000,1.848000);
positions[472] = Vec3(0.681000,0.896000,1.285000);
positions[473] = Vec3(0.406000,2.936000,0.717000);
positions[474] = Vec3(0.565000,1.852000,0.349000);
positions[475] = Vec3(0.597000,1.651000,1.020000);
positions[476] = Vec3(1.236000,0.170000,1.335000);
positions[477] = Vec3(0.586000,0.441000,1.980000);
positions[478] = Vec3(1.443000,1.208000,1.575000);
positions[479] = Vec3(0.247000,0.243000,0.502000);
positions[480] = Vec3(1.386000,1.564000,0.236000);
positions[481] = Vec3(0.871000,1.063000,0.930000);
positions[482] = Vec3(0.136000,0.992000,0.621000);
positions[483] = Vec3(0.889000,0.986000,0.010000);
positions[484] = Vec3(1.107000,2.731000,1.452000);
positions[485] = Vec3(0.942000,2.471000,0.517000);
positions[486] = Vec3(0.989000,0.652000,0.747000);
positions[487] = Vec3(0.899000,1.235000,2.707000);
positions[488] = Vec3(1.105000,0.684000,0.068000);
positions[489] = Vec3(1.660000,1.235000,2.276000);
positions[490] = Vec3(1.593000,1.883000,1.915000);
positions[491] = Vec3(1.528000,1.613000,0.920000);
positions[492] = Vec3(0.459000,1.046000,1.011000);
positions[493] = Vec3(0.213000,0.612000,0.644000);
positions[494] = Vec3(0.078000,1.392000,1.676000);
positions[495] = Vec3(0.605000,0.491000,0.574000);
positions[496] = Vec3(0.990000,1.586000,1.076000);
positions[497] = Vec3(0.297000,1.434000,1.028000);
positions[498] = Vec3(1.101000,1.471000,1.443000);
positions[499] = Vec3(0.072000,0.139000,1.653000);
positions[500] = Vec3(0.633000,0.884000,0.645000);
positions[501] = Vec3(0.352000,2.841000,1.463000);
positions[502] = Vec3(0.418000,0.774000,0.350000);
positions[503] = Vec3(2.641000,0.198000,0.869000);
positions[504] = Vec3(0.608000,1.341000,0.695000);
positions[505] = Vec3(1.778000,1.151000,1.830000);
positions[506] = Vec3(1.669000,0.342000,2.768000);
positions[507] = Vec3(1.256000,0.994000,0.798000);
positions[508] = Vec3(1.068000,0.375000,1.036000);
positions[509] = Vec3(0.910000,0.758000,1.589000);
positions[510] = Vec3(0.243000,2.452000,0.805000);
positions[511] = Vec3(1.018000,0.764000,1.122000);
positions[512] = Vec3(2.464000,1.089000,1.404000);
positions[513] = Vec3(0.670000,0.564000,0.034000);
positions[514] = Vec3(0.030000,1.296000,1.310000);
positions[515] = Vec3(1.210000,1.785000,1.691000);
positions[516] = Vec3(0.022000,0.620000,0.974000);
positions[517] = Vec3(1.499000,1.277000,2.986000);
positions[518] = Vec3(1.227000,1.896000,1.006000);
positions[519] = Vec3(0.528000,1.022000,1.635000);
positions[520] = Vec3(1.887000,2.670000,0.089000);
positions[521] = Vec3(1.661000,0.825000,0.793000);
positions[522] = Vec3(0.831000,1.494000,0.374000);
positions[523] = Vec3(1.356000,0.613000,0.930000);
positions[524] = Vec3(0.667000,0.600000,0.968000);
positions[525] = Vec3(1.154000,1.702000,2.925000);
positions[526] = Vec3(1.420000,1.581000,1.289000);
positions[527] = Vec3(1.383000,0.041000,0.932000);
positions[528] = Vec3(1.727000,0.140000,1.725000);
positions[529] = Vec3(0.711000,1.215000,2.004000);
positions[530] = Vec3(1.061000,1.067000,1.366000);
positions[531] = Vec3(0.377000,0.597000,1.224000);
positions[532] = Vec3(0.274000,0.719000,1.842000);
positions[533] = Vec3(0.840000,1.658000,1.874000);
positions[534] = Vec3(0.877000,0.290000,0.311000);
positions[535] = Vec3(2.130000,1.153000,1.196000);
positions[536] = Vec3(1.028000,1.379000,0.747000);
positions[537] = Vec3(1.107000,2.450000,2.079000);
positions[538] = Vec3(1.419000,1.333000,0.585000);
positions[539] = Vec3(0.430000,1.305000,1.369000);
positions[540] = Vec3(0.775000,1.363000,1.596000);
positions[541] = Vec3(1.522000,2.009000,0.736000);
positions[542] = Vec3(0.857000,1.722000,0.696000);
positions[543] = Vec3(0.722000,2.831000,1.478000);
positions[544] = Vec3(0.411000,1.673000,0.681000);
positions[545] = Vec3(1.511000,0.456000,0.597000);
positions[546] = Vec3(2.684000,0.820000,2.996000);
positions[547] = Vec3(1.593000,1.713000,2.369000);
positions[548] = Vec3(1.113000,0.803000,1.958000);
positions[549] = Vec3(1.267000,1.095000,0.254000);
positions[550] = Vec3(2.120000,0.540000,2.477000);
positions[551] = Vec3(0.566000,1.409000,2.588000);
positions[552] = Vec3(0.261000,0.872000,2.546000);
positions[553] = Vec3(1.878000,1.446000,2.680000);
positions[554] = Vec3(0.878000,1.606000,2.658000);
positions[555] = Vec3(1.564000,0.749000,1.786000);
positions[556] = Vec3(1.412000,1.942000,2.625000);
positions[557] = Vec3(1.660000,1.114000,2.710000);
positions[558] = Vec3(1.118000,0.813000,2.424000);
positions[559] = Vec3(1.482000,0.893000,2.434000);
positions[560] = Vec3(1.093000,1.129000,1.740000);
positions[561] = Vec3(2.163000,0.849000,2.709000);
positions[562] = Vec3(1.201000,1.429000,1.957000);
positions[563] = Vec3(0.235000,2.258000,2.002000);
positions[564] = Vec3(0.413000,1.444000,0.314000);
positions[565] = Vec3(0.164000,0.450000,2.408000);
positions[566] = Vec3(1.551000,0.851000,0.033000);
positions[567] = Vec3(0.659000,0.228000,2.807000);
positions[568] = Vec3(0.741000,0.131000,2.124000);
positions[569] = Vec3(0.455000,0.567000,2.682000);
positions[570] = Vec3(0.729000,0.971000,2.408000);
positions[571] = Vec3(1.487000,2.820000,0.162000);
positions[572] = Vec3(1.855000,0.700000,2.858000);
positions[573] = Vec3(0.305000,1.074000,1.926000);
positions[574] = Vec3(1.300000,0.153000,1.747000);
positions[575] = Vec3(1.272000,1.249000,2.568000);
positions[576] = Vec3(0.431000,0.743000,2.238000);
positions[577] = Vec3(0.493000,0.240000,0.184000);
positions[578] = Vec3(1.734000,0.506000,2.317000);
positions[579] = Vec3(0.874000,0.631000,2.692000);
positions[580] = Vec3(0.473000,2.790000,2.161000);
positions[581] = Vec3(1.310000,0.571000,2.759000);
positions[582] = Vec3(0.677000,0.798000,1.916000);
positions[583] = Vec3(0.944000,0.442000,1.858000);
positions[584] = Vec3(3.006000,2.098000,2.976000);
positions[585] = Vec3(0.864000,0.592000,2.231000);
positions[586] = Vec3(1.366000,0.611000,2.147000);
positions[587] = Vec3(2.871000,1.217000,2.880000);
positions[588] = Vec3(1.674000,2.664000,2.336000);
positions[589] = Vec3(1.757000,0.879000,2.101000);
positions[590] = Vec3(1.293000,2.939000,2.457000);
positions[591] = Vec3(1.108000,1.131000,2.206000);
positions[592] = Vec3(1.207000,1.658000,2.498000);
positions[593] = Vec3(0.850000,1.373000,2.312000);
positions[594] = Vec3(1.413000,1.060000,1.939000);
positions[595] = Vec3(1.138000,0.140000,2.102000);
positions[596] = Vec3(0.752000,1.307000,1.190000);
positions[597] = Vec3(1.254000,0.942000,2.790000);
positions[598] = Vec3(1.544000,1.614000,2.800000);
positions[599] = Vec3(2.128000,0.302000,2.833000);
positions[600] = Vec3(0.300000,1.744000,0.027000);
positions[601] = Vec3(1.878000,2.986000,2.060000);
positions[602] = Vec3(1.528000,0.233000,2.045000);
positions[603] = Vec3(1.146000,1.817000,2.067000);
positions[604] = Vec3(1.037000,2.746000,0.813000);
positions[605] = Vec3(0.524000,0.610000,1.566000);
positions[606] = Vec3(0.945000,2.964000,0.503000);
positions[607] = Vec3(1.788000,2.565000,0.965000);
positions[608] = Vec3(0.471000,2.510000,0.491000);
positions[609] = Vec3(0.512000,2.043000,1.371000);
positions[610] = Vec3(2.316000,2.423000,1.494000);
positions[611] = Vec3(1.575000,2.394000,2.953000);
positions[612] = Vec3(2.845000,2.869000,0.985000);
positions[613] = Vec3(1.016000,2.335000,1.003000);
positions[614] = Vec3(0.998000,2.830000,1.879000);
positions[615] = Vec3(0.624000,2.508000,0.075000);
positions[616] = Vec3(1.362000,2.808000,2.069000);
positions[617] = Vec3(1.747000,0.068000,0.810000);
positions[618] = Vec3(1.768000,2.355000,0.661000);
positions[619] = Vec3(1.535000,2.410000,2.085000);
positions[620] = Vec3(0.844000,2.004000,1.646000);
positions[621] = Vec3(1.124000,0.280000,0.649000);
positions[622] = Vec3(0.689000,2.170000,0.648000);
positions[623] = Vec3(0.849000,2.666000,1.175000);
positions[624] = Vec3(2.975000,1.963000,1.308000);
positions[625] = Vec3(1.074000,2.082000,0.714000);
positions[626] = Vec3(1.284000,2.651000,1.110000);
positions[627] = Vec3(1.669000,0.205000,0.180000);
positions[628] = Vec3(1.716000,0.047000,1.253000);
positions[629] = Vec3(0.501000,2.241000,1.043000);
positions[630] = Vec3(1.038000,1.833000,0.305000);
positions[631] = Vec3(0.646000,2.431000,1.424000);
positions[632] = Vec3(1.383000,2.059000,2.230000);
positions[633] = Vec3(0.370000,2.566000,1.192000);
positions[634] = Vec3(1.355000,2.006000,0.120000);
positions[635] = Vec3(2.113000,0.075000,0.589000);
positions[636] = Vec3(1.850000,0.448000,1.890000);
positions[637] = Vec3(1.215000,2.704000,0.405000);
positions[638] = Vec3(0.575000,2.997000,1.798000);
positions[639] = Vec3(0.967000,2.586000,2.603000);
positions[640] = Vec3(0.276000,1.669000,1.430000);
positions[641] = Vec3(1.483000,2.284000,1.128000);
positions[642] = Vec3(0.983000,3.003000,1.099000);
positions[643] = Vec3(0.539000,2.222000,1.720000);
positions[644] = Vec3(0.648000,2.826000,2.751000);
positions[645] = Vec3(0.803000,1.994000,0.993000);
positions[646] = Vec3(0.451000,0.216000,1.438000);
positions[647] = Vec3(1.604000,2.512000,0.334000);
positions[648] = Vec3(1.980000,2.022000,0.588000);
positions[649] = Vec3(1.843000,2.834000,1.544000);
positions[650] = Vec3(1.835000,3.005000,2.858000);
positions[651] = Vec3(0.679000,2.499000,0.838000);
positions[652] = Vec3(0.012000,2.637000,1.524000);
positions[653] = Vec3(0.314000,2.065000,0.602000);
positions[654] = Vec3(1.157000,0.004000,0.173000);
positions[655] = Vec3(0.736000,1.705000,1.382000);
positions[656] = Vec3(1.511000,2.736000,0.690000);
positions[657] = Vec3(1.330000,2.541000,1.735000);
positions[658] = Vec3(0.744000,0.170000,0.785000);
positions[659] = Vec3(2.593000,2.794000,0.703000);
positions[660] = Vec3(0.275000,1.872000,1.043000);
positions[661] = Vec3(1.624000,2.608000,1.341000);
positions[662] = Vec3(1.382000,0.122000,2.855000);
positions[663] = Vec3(1.326000,2.434000,0.783000);
positions[664] = Vec3(0.117000,0.116000,1.254000);
positions[665] = Vec3(1.045000,2.970000,2.748000);
positions[666] = Vec3(1.341000,2.692000,2.799000);
positions[667] = Vec3(1.797000,2.586000,2.709000);
positions[668] = Vec3(0.890000,2.484000,1.716000);
positions[669] = Vec3(2.373000,2.558000,1.889000);
positions[670] = Vec3(1.566000,2.323000,2.574000);
positions[671] = Vec3(1.257000,2.280000,0.399000);
positions[672] = Vec3(0.679000,2.130000,2.434000);
positions[673] = Vec3(2.016000,2.334000,2.462000);
positions[674] = Vec3(1.077000,2.213000,2.416000);
positions[675] = Vec3(0.581000,1.950000,2.081000);
positions[676] = Vec3(0.805000,2.315000,2.810000);
positions[677] = Vec3(0.844000,1.787000,2.322000);
positions[678] = Vec3(0.980000,2.205000,0.129000);
positions[679] = Vec3(2.468000,0.603000,2.740000);
positions[680] = Vec3(2.366000,2.403000,2.299000);
positions[681] = Vec3(0.337000,2.487000,2.329000);
positions[682] = Vec3(2.007000,2.793000,2.452000);
positions[683] = Vec3(1.072000,2.571000,0.063000);
positions[684] = Vec3(1.217000,2.283000,2.806000);
positions[685] = Vec3(0.459000,2.477000,2.728000);
positions[686] = Vec3(0.958000,1.975000,2.710000);
positions[687] = Vec3(0.914000,2.111000,2.052000);
positions[688] = Vec3(0.768000,2.958000,0.075000);
positions[689] = Vec3(0.474000,1.805000,2.533000);
positions[690] = Vec3(1.313000,2.552000,2.395000);
positions[691] = Vec3(1.853000,2.014000,2.229000);
positions[692] = Vec3(2.405000,2.230000,2.658000);
positions[693] = Vec3(0.727000,1.781000,0.016000);
positions[694] = Vec3(0.974000,2.791000,2.271000);
positions[695] = Vec3(0.438000,0.096000,2.457000);
positions[696] = Vec3(0.652000,2.392000,2.064000);
positions[697] = Vec3(1.972000,2.209000,2.834000);
positions[698] = Vec3(0.333000,0.141000,2.088000);
positions[699] = Vec3(1.813000,1.952000,0.063000);
positions[700] = Vec3(0.166000,2.838000,1.877000);
positions[701] = Vec3(1.772000,0.487000,0.951000);
positions[702] = Vec3(1.924000,1.404000,1.434000);
positions[703] = Vec3(2.734000,0.348000,1.712000);
positions[704] = Vec3(2.874000,0.729000,1.811000);
positions[705] = Vec3(1.841000,0.877000,1.137000);
positions[706] = Vec3(2.327000,1.491000,1.768000);
positions[707] = Vec3(1.916000,1.483000,1.057000);
positions[708] = Vec3(2.783000,0.850000,0.745000);
positions[709] = Vec3(1.829000,1.526000,0.085000);
positions[710] = Vec3(2.426000,1.082000,0.652000);
positions[711] = Vec3(1.645000,1.241000,1.217000);
positions[712] = Vec3(2.286000,0.725000,0.084000);
positions[713] = Vec3(2.755000,0.691000,1.421000);
positions[714] = Vec3(2.651000,0.591000,1.023000);
positions[715] = Vec3(2.040000,0.863000,0.442000);
positions[716] = Vec3(0.035000,0.109000,2.497000);
positions[717] = Vec3(0.127000,1.410000,0.572000);
positions[718] = Vec3(2.174000,0.357000,0.307000);
positions[719] = Vec3(2.705000,1.508000,0.758000);
positions[720] = Vec3(2.223000,1.407000,2.913000);
positions[721] = Vec3(2.528000,1.722000,1.088000);
positions[722] = Vec3(2.860000,0.345000,0.198000);
positions[723] = Vec3(2.580000,1.789000,1.479000);
positions[724] = Vec3(2.779000,0.295000,1.295000);
positions[725] = Vec3(0.097000,0.434000,2.826000);
positions[726] = Vec3(2.952000,1.654000,1.091000);
positions[727] = Vec3(0.119000,1.878000,0.343000);
positions[728] = Vec3(1.718000,1.173000,0.327000);
positions[729] = Vec3(2.833000,0.016000,0.527000);
positions[730] = Vec3(2.085000,1.779000,2.888000);
positions[731] = Vec3(2.754000,2.952000,1.485000);
positions[732] = Vec3(2.826000,0.935000,1.162000);
positions[733] = Vec3(1.564000,1.585000,1.615000);
positions[734] = Vec3(2.132000,0.645000,1.093000);
positions[735] = Vec3(2.294000,1.490000,1.350000);
positions[736] = Vec3(0.081000,0.490000,1.479000);
positions[737] = Vec3(2.118000,1.165000,1.642000);
positions[738] = Vec3(2.141000,0.121000,1.390000);
positions[739] = Vec3(2.385000,0.389000,1.196000);
positions[740] = Vec3(0.049000,0.166000,0.817000);
positions[741] = Vec3(1.993000,0.806000,1.814000);
positions[742] = Vec3(0.006000,1.450000,0.171000);
positions[743] = Vec3(2.297000,0.428000,0.764000);
positions[744] = Vec3(2.851000,0.469000,2.114000);
positions[745] = Vec3(1.814000,1.957000,0.945000);
positions[746] = Vec3(0.386000,0.327000,0.902000);
positions[747] = Vec3(2.452000,1.070000,1.807000);
positions[748] = Vec3(2.309000,1.537000,2.159000);
positions[749] = Vec3(2.712000,1.497000,2.007000);
positions[750] = Vec3(1.727000,0.924000,1.503000);
positions[751] = Vec3(0.861000,0.801000,0.344000);
positions[752] = Vec3(1.740000,1.245000,0.819000);
positions[753] = Vec3(0.117000,0.042000,0.197000);
positions[754] = Vec3(2.557000,0.996000,0.317000);
positions[755] = Vec3(2.228000,1.588000,2.548000);
positions[756] = Vec3(2.849000,1.557000,2.708000);
positions[757] = Vec3(0.152000,1.107000,0.219000);
positions[758] = Vec3(2.460000,1.318000,1.002000);
positions[759] = Vec3(2.405000,1.436000,0.528000);
positions[760] = Vec3(2.135000,1.179000,2.046000);
positions[761] = Vec3(1.726000,0.588000,0.286000);
positions[762] = Vec3(2.831000,1.053000,1.538000);
positions[763] = Vec3(1.932000,1.556000,1.833000);
positions[764] = Vec3(2.423000,0.900000,1.064000);
positions[765] = Vec3(3.001000,1.807000,0.709000);
positions[766] = Vec3(0.578000,1.095000,0.223000);
positions[767] = Vec3(2.215000,1.160000,0.252000);
positions[768] = Vec3(2.050000,0.921000,0.835000);
positions[769] = Vec3(2.080000,1.682000,0.738000);
positions[770] = Vec3(2.851000,1.753000,0.027000);
positions[771] = Vec3(0.203000,0.509000,0.202000);
positions[772] = Vec3(1.967000,1.018000,0.018000);
positions[773] = Vec3(1.869000,0.878000,2.472000);
positions[774] = Vec3(1.917000,0.228000,2.507000);
positions[775] = Vec3(0.316000,0.795000,2.991000);
positions[776] = Vec3(2.175000,1.229000,2.472000);
positions[777] = Vec3(2.405000,1.062000,2.931000);
positions[778] = Vec3(2.501000,0.511000,2.369000);
positions[779] = Vec3(2.641000,0.819000,2.141000);
positions[780] = Vec3(0.649000,1.384000,3.006000);
positions[781] = Vec3(1.012000,0.329000,2.963000);
positions[782] = Vec3(2.755000,0.350000,2.718000);
positions[783] = Vec3(2.315000,0.153000,2.454000);
positions[784] = Vec3(2.583000,1.696000,2.389000);
positions[785] = Vec3(0.439000,2.593000,1.776000);
positions[786] = Vec3(2.630000,1.390000,0.116000);
positions[787] = Vec3(2.854000,0.669000,2.478000);
positions[788] = Vec3(2.551000,1.342000,2.621000);
positions[789] = Vec3(2.533000,2.734000,2.987000);
positions[790] = Vec3(2.772000,2.446000,2.875000);
positions[791] = Vec3(2.817000,1.051000,2.498000);
positions[792] = Vec3(2.688000,1.404000,1.621000);
positions[793] = Vec3(0.083000,2.737000,2.775000);
positions[794] = Vec3(2.514000,0.322000,2.041000);
positions[795] = Vec3(2.470000,0.900000,2.504000);
positions[796] = Vec3(2.790000,0.444000,0.624000);
positions[797] = Vec3(0.040000,0.840000,2.202000);
positions[798] = Vec3(0.530000,1.067000,2.764000);
positions[799] = Vec3(0.191000,1.385000,2.541000);
positions[800] = Vec3(2.465000,0.363000,0.051000);
positions[801] = Vec3(1.850000,1.902000,2.592000);
positions[802] = Vec3(1.432000,0.306000,2.449000);
positions[803] = Vec3(2.259000,0.489000,1.753000);
positions[804] = Vec3(2.803000,1.118000,1.956000);
positions[805] = Vec3(2.426000,0.147000,1.636000);
positions[806] = Vec3(2.880000,1.846000,2.133000);
positions[807] = Vec3(2.862000,2.110000,1.867000);
positions[808] = Vec3(0.424000,1.184000,2.299000);
positions[809] = Vec3(2.518000,1.218000,2.228000);
positions[810] = Vec3(2.153000,0.834000,1.468000);
positions[811] = Vec3(0.105000,1.397000,2.088000);
positions[812] = Vec3(2.579000,0.601000,0.316000);
positions[813] = Vec3(2.594000,2.106000,2.968000);
positions[814] = Vec3(0.448000,1.435000,1.783000);
positions[815] = Vec3(2.125000,0.299000,2.132000);
positions[816] = Vec3(2.849000,1.402000,2.356000);
positions[817] = Vec3(2.956000,0.091000,2.078000);
positions[818] = Vec3(0.156000,1.696000,2.357000);
positions[819] = Vec3(1.566000,2.211000,1.557000);
positions[820] = Vec3(2.047000,0.194000,0.985000);
positions[821] = Vec3(1.947000,2.680000,0.488000);
positions[822] = Vec3(2.343000,2.796000,1.447000);
positions[823] = Vec3(2.006000,2.332000,0.265000);
positions[824] = Vec3(2.396000,1.834000,0.546000);
positions[825] = Vec3(2.538000,2.059000,2.207000);
positions[826] = Vec3(0.110000,2.360000,0.447000);
positions[827] = Vec3(2.198000,2.448000,1.136000);
positions[828] = Vec3(2.420000,2.121000,1.271000);
positions[829] = Vec3(0.422000,2.192000,0.260000);
positions[830] = Vec3(2.145000,2.767000,2.839000);
positions[831] = Vec3(2.434000,2.398000,0.421000);
positions[832] = Vec3(2.489000,2.175000,1.718000);
positions[833] = Vec3(2.870000,2.527000,0.814000);
positions[834] = Vec3(2.741000,2.016000,0.337000);
positions[835] = Vec3(1.997000,2.574000,2.107000);
positions[836] = Vec3(0.002000,2.128000,0.932000);
positions[837] = Vec3(2.787000,2.375000,0.234000);
positions[838] = Vec3(2.235000,1.852000,1.620000);
positions[839] = Vec3(2.782000,1.642000,0.422000);
positions[840] = Vec3(2.915000,1.760000,1.699000);
positions[841] = Vec3(2.047000,2.178000,1.549000);
positions[842] = Vec3(1.808000,1.878000,1.556000);
positions[843] = Vec3(2.224000,2.043000,0.913000);
positions[844] = Vec3(2.619000,2.611000,1.237000);
positions[845] = Vec3(2.916000,2.726000,0.168000);
positions[846] = Vec3(2.021000,2.833000,1.176000);
positions[847] = Vec3(2.967000,2.308000,2.258000);
positions[848] = Vec3(2.778000,2.270000,1.477000);
positions[849] = Vec3(2.121000,1.834000,2.002000);
positions[850] = Vec3(2.097000,2.752000,0.808000);
positions[851] = Vec3(1.897000,0.566000,1.501000);
positions[852] = Vec3(0.359000,2.802000,0.036000);
positions[853] = Vec3(2.966000,2.454000,1.186000);
positions[854] = Vec3(2.461000,2.964000,1.132000);
positions[855] = Vec3(2.093000,1.821000,1.243000);
positions[856] = Vec3(1.706000,2.659000,1.841000);
positions[857] = Vec3(2.074000,1.709000,0.342000);
positions[858] = Vec3(2.137000,2.894000,1.813000);
positions[859] = Vec3(0.223000,2.293000,1.417000);
positions[860] = Vec3(2.637000,0.007000,0.197000);
positions[861] = Vec3(1.416000,0.050000,0.483000);
positions[862] = Vec3(1.845000,2.250000,1.251000);
positions[863] = Vec3(2.906000,0.034000,2.896000);
positions[864] = Vec3(2.481000,0.204000,0.474000);
positions[865] = Vec3(2.234000,2.051000,0.158000);
positions[866] = Vec3(0.185000,2.453000,0.055000);
positions[867] = Vec3(2.509000,0.048000,2.786000);
positions[868] = Vec3(2.202000,2.206000,2.027000);
positions[869] = Vec3(0.061000,2.367000,2.656000);
positions[870] = Vec3(3.003000,2.755000,2.241000);
positions[871] = Vec3(0.297000,2.131000,2.463000);
positions[872] = Vec3(1.553000,0.429000,1.573000);
positions[873] = Vec3(2.506000,1.832000,1.911000);
positions[874] = Vec3(2.472000,1.814000,2.759000);
positions[875] = Vec3(1.922000,1.563000,2.278000);
positions[876] = Vec3(2.623000,2.666000,2.169000);
positions[877] = Vec3(0.120000,1.834000,2.723000);
positions[878] = Vec3(0.294000,0.103000,2.826000);
positions[879] = Vec3(2.364000,2.821000,0.417000);
positions[880] = Vec3(2.446000,1.734000,0.153000);
positions[881] = Vec3(2.777000,2.037000,2.565000);
positions[882] = Vec3(2.837000,2.477000,1.924000);
positions[883] = Vec3(2.221000,1.961000,2.443000);
positions[884] = Vec3(2.284000,2.895000,2.157000);
positions[885] = Vec3(2.728000,2.880000,1.861000);
positions[886] = Vec3(0.454000,2.080000,2.868000);
positions[887] = Vec3(2.430000,2.790000,2.524000);
positions[888] = Vec3(1.808000,2.213000,1.899000);
positions[889] = Vec3(2.666000,0.053000,2.309000);
positions[890] = Vec3(2.290000,2.408000,2.995000);
positions[891] = Vec3(2.646000,2.592000,1.625000);
positions[892] = Vec3(2.750000,2.508000,2.489000);
positions[893] = Vec3(0.211000,1.753000,1.939000);
ASSERT_EQUAL_VEC(Vec3(-1.98249623e+02, -2.23814501e+02, -1.02563796e+02), forces1[0], tol);
ASSERT_EQUAL_VEC(Vec3( 4.44017199e+02, -3.90390764e+02, -8.09518867e+01), forces1[1], tol);
ASSERT_EQUAL_VEC(Vec3(-8.60789585e+01, -1.05598857e+01, -1.87798624e+02), forces1[2], tol);
ASSERT_EQUAL_VEC(Vec3( 7.05363534e+02, -1.69549058e+02, 2.72466252e+02), forces1[3], tol);
ASSERT_EQUAL_VEC(Vec3(-2.19333527e+01, 5.33284079e+02, -4.56197740e+01), forces1[4], tol);
ASSERT_EQUAL_VEC(Vec3(-3.04281837e+02, 2.02941152e+02, 1.20252918e+02), forces1[5], tol);
ASSERT_EQUAL_VEC(Vec3( 1.14210928e+02, 1.21079746e+02, 3.40929279e+02), forces1[6], tol);
ASSERT_EQUAL_VEC(Vec3(-4.02295448e+01, 3.78740176e+02, 1.07262493e+02), forces1[7], tol);
ASSERT_EQUAL_VEC(Vec3( 9.42807014e+01, 1.00891659e+01, 1.57729175e+02), forces1[8], tol);
ASSERT_EQUAL_VEC(Vec3( 2.98424830e+02, 1.18614285e+02, -4.17503362e+02), forces1[9], tol);
ASSERT_EQUAL_VEC(Vec3( 4.74881296e+01, -5.56206792e+00, -1.92332367e+02), forces1[10], tol);
ASSERT_EQUAL_VEC(Vec3(-3.61201909e+02, 4.00192313e+02, -3.38469181e+02), forces1[11], tol);
ASSERT_EQUAL_VEC(Vec3( 1.59855287e+02, -2.22184057e+02, -1.71814608e+02), forces1[12], tol);
ASSERT_EQUAL_VEC(Vec3( 8.02099562e+01, -2.46176977e+02, 1.49725960e+02), forces1[13], tol);
ASSERT_EQUAL_VEC(Vec3(-2.41478578e+02, -8.22327152e+01, -4.19802587e+02), forces1[14], tol);
ASSERT_EQUAL_VEC(Vec3(-2.42767767e+02, 4.99651070e+00, 1.89314504e+01), forces1[15], tol);
ASSERT_EQUAL_VEC(Vec3(-2.32048202e+02, 2.91141605e+02, -1.56147406e+02), forces1[16], tol);
ASSERT_EQUAL_VEC(Vec3(-8.77435572e+01, -4.75580602e+02, -2.02736231e+02), forces1[17], tol);
ASSERT_EQUAL_VEC(Vec3( 3.96101687e+02, -6.37149049e+02, 1.78647561e+02), forces1[18], tol);
ASSERT_EQUAL_VEC(Vec3( 3.72219707e+02, 1.17763530e+02, 2.14244129e+02), forces1[19], tol);
ASSERT_EQUAL_VEC(Vec3( 4.50062822e+02, -1.37575208e+01, 2.39948230e+02), forces1[20], tol);
ASSERT_EQUAL_VEC(Vec3( 2.00029587e+02, 3.30130171e+01, 8.51610501e+01), forces1[21], tol);
ASSERT_EQUAL_VEC(Vec3( 1.97192878e+02, 7.33293743e+02, 2.15221280e+01), forces1[22], tol);
ASSERT_EQUAL_VEC(Vec3(-1.44377368e+02, 9.15716307e+01, 3.18892747e+01), forces1[23], tol);
ASSERT_EQUAL_VEC(Vec3( 2.94552317e+02, -3.69040394e+02, 2.70288945e+00), forces1[24], tol);
ASSERT_EQUAL_VEC(Vec3( 3.23120142e+02, 8.86114454e+02, 3.24450894e+02), forces1[25], tol);
ASSERT_EQUAL_VEC(Vec3(-6.03295072e+01, 1.68456989e+02, 2.31869997e+02), forces1[26], tol);
ASSERT_EQUAL_VEC(Vec3( 2.93194241e+01, 5.76413549e+01, 3.64010103e+02), forces1[27], tol);
ASSERT_EQUAL_VEC(Vec3( 3.27452172e-01, 1.34469263e+02, -4.39755475e+01), forces1[28], tol);
ASSERT_EQUAL_VEC(Vec3(-1.38717677e+02, -2.99570604e+02, -2.80339165e+02), forces1[29], tol);
ASSERT_EQUAL_VEC(Vec3(-8.27937873e+01, 4.40618490e+02, 9.65587062e+01), forces1[30], tol);
ASSERT_EQUAL_VEC(Vec3( 2.03855205e+01, -4.37745725e+01, -5.06881413e+01), forces1[31], tol);
ASSERT_EQUAL_VEC(Vec3(-2.60745980e+02, 3.70683505e+02, 1.28613276e+02), forces1[32], tol);
ASSERT_EQUAL_VEC(Vec3( 1.97960989e+02, -1.51970994e+01, -2.85732029e+02), forces1[33], tol);
ASSERT_EQUAL_VEC(Vec3(-2.63675933e+01, -1.29448310e+01, -2.36632787e+02), forces1[34], tol);
ASSERT_EQUAL_VEC(Vec3( 2.13614470e+02, -2.29632590e+02, -7.72843645e+02), forces1[35], tol);
ASSERT_EQUAL_VEC(Vec3(-4.26287747e+01, 9.00718113e+01, 3.48807737e+02), forces1[36], tol);
ASSERT_EQUAL_VEC(Vec3(-2.27779547e+02, -8.46450565e+00, -3.25578367e+02), forces1[37], tol);
ASSERT_EQUAL_VEC(Vec3(-8.32052751e+01, -2.50968185e+02, 5.80103845e+01), forces1[38], tol);
ASSERT_EQUAL_VEC(Vec3(-2.32232234e+02, 1.30470270e+02, 6.06495521e+00), forces1[39], tol);
ASSERT_EQUAL_VEC(Vec3(-1.14455494e+02, -3.48721419e+01, -8.76129455e+01), forces1[40], tol);
ASSERT_EQUAL_VEC(Vec3(-2.55340689e+02, -2.79204188e+02, -1.68792912e+02), forces1[41], tol);
ASSERT_EQUAL_VEC(Vec3(-2.02307452e+02, 1.47895858e+02, 1.75220456e+02), forces1[42], tol);
ASSERT_EQUAL_VEC(Vec3( 2.63185604e+01, -4.46985150e+02, -2.38821173e+00), forces1[43], tol);
ASSERT_EQUAL_VEC(Vec3( 2.55944000e+02, -2.93439747e+02, 3.28025974e+02), forces1[44], tol);
ASSERT_EQUAL_VEC(Vec3( 1.14311082e+02, 2.24380865e+02, -8.29696179e+01), forces1[45], tol);
ASSERT_EQUAL_VEC(Vec3( 1.43326861e+01, 2.46491923e+02, -3.75165407e+02), forces1[46], tol);
ASSERT_EQUAL_VEC(Vec3(-4.34792273e+02, -9.73250775e+01, -1.91085096e+02), forces1[47], tol);
ASSERT_EQUAL_VEC(Vec3( 5.23123306e+01, 5.50975366e+02, 2.90221875e+02), forces1[48], tol);
ASSERT_EQUAL_VEC(Vec3(-4.83894734e+01, 1.71476996e+02, -3.42294535e+02), forces1[49], tol);
ASSERT_EQUAL_VEC(Vec3(-2.61463419e+02, 9.01733107e+01, 3.64114372e+02), forces1[50], tol);
ASSERT_EQUAL_VEC(Vec3( 4.43625180e+02, -5.19324924e+02, -7.74662441e+01), forces1[51], tol);
ASSERT_EQUAL_VEC(Vec3( 2.15611073e+02, -5.54577351e+02, 1.71371860e+02), forces1[52], tol);
ASSERT_EQUAL_VEC(Vec3(-2.14763168e+02, 3.56711285e+02, -3.71984643e+02), forces1[53], tol);
ASSERT_EQUAL_VEC(Vec3( 2.22124509e+02, 2.73223318e+02, 1.61942563e+02), forces1[54], tol);
ASSERT_EQUAL_VEC(Vec3( 2.85121911e+02, 3.19143598e+02, -1.98763562e+02), forces1[55], tol);
ASSERT_EQUAL_VEC(Vec3( 3.90039513e+02, 5.91179187e+02, 8.76217046e+01), forces1[56], tol);
ASSERT_EQUAL_VEC(Vec3( 1.69060954e+02, -9.43142940e+01, 3.42180838e+02), forces1[57], tol);
ASSERT_EQUAL_VEC(Vec3( 2.93242309e+01, -1.03060738e+02, 5.38025654e+01), forces1[58], tol);
ASSERT_EQUAL_VEC(Vec3( 4.09077298e+01, 1.30026064e+02, -7.16489622e+01), forces1[59], tol);
ASSERT_EQUAL_VEC(Vec3(-1.89926185e+02, 5.52058739e+00, 4.53549674e+02), forces1[60], tol);
ASSERT_EQUAL_VEC(Vec3(-1.11572641e+02, 1.24178624e+02, 4.53016222e+02), forces1[61], tol);
ASSERT_EQUAL_VEC(Vec3( 1.54599730e+02, -2.70150115e+02, -7.89492689e+01), forces1[62], tol);
ASSERT_EQUAL_VEC(Vec3( 2.84023026e+02, -8.89934391e+01, 3.24522873e+02), forces1[63], tol);
ASSERT_EQUAL_VEC(Vec3( 1.44364280e+02, -7.96613821e+01, -2.53253756e+02), forces1[64], tol);
ASSERT_EQUAL_VEC(Vec3(-1.77719009e+02, 8.42958696e+01, -1.41078307e+02), forces1[65], tol);
ASSERT_EQUAL_VEC(Vec3(-2.69676443e+02, -5.26499439e+02, -1.04061540e+02), forces1[66], tol);
ASSERT_EQUAL_VEC(Vec3(-2.41597419e+02, -9.41368613e+01, 3.30311173e+01), forces1[67], tol);
ASSERT_EQUAL_VEC(Vec3( 6.81038324e+02, -5.79215726e+02, -9.61706079e-01), forces1[68], tol);
ASSERT_EQUAL_VEC(Vec3( 2.88768459e+02, 2.41571264e+02, -1.90674774e+02), forces1[69], tol);
ASSERT_EQUAL_VEC(Vec3(-1.31534697e+01, 1.14188032e+01, 4.13763857e+01), forces1[70], tol);
ASSERT_EQUAL_VEC(Vec3( 5.14246004e+01, -9.72308438e+01, 1.27291275e+01), forces1[71], tol);
ASSERT_EQUAL_VEC(Vec3( 7.15804377e+01, -1.94144101e+02, -2.48994830e+02), forces1[72], tol);
ASSERT_EQUAL_VEC(Vec3(-9.16897832e+01, 1.23379998e+02, -2.86172337e+02), forces1[73], tol);
ASSERT_EQUAL_VEC(Vec3( 9.82636682e+01, -7.07069901e+01, -1.29205135e+02), forces1[74], tol);
ASSERT_EQUAL_VEC(Vec3(-2.15565256e+02, -1.43101075e+02, -1.96437192e+02), forces1[75], tol);
ASSERT_EQUAL_VEC(Vec3(-4.61171340e+02, -4.03099962e+02, -4.56004212e+01), forces1[76], tol);
ASSERT_EQUAL_VEC(Vec3( 1.52879441e+02, -9.37786956e+02, 2.93596349e+02), forces1[77], tol);
ASSERT_EQUAL_VEC(Vec3( 2.45712108e+02, 1.99809051e+01, 1.17930077e+02), forces1[78], tol);
ASSERT_EQUAL_VEC(Vec3(-2.37060951e+02, 2.49499642e+02, 2.13427566e+02), forces1[79], tol);
ASSERT_EQUAL_VEC(Vec3(-2.90549561e+02, 1.91148461e+02, -4.24254939e+02), forces1[80], tol);
ASSERT_EQUAL_VEC(Vec3( 2.98836434e+02, -5.77088507e+00, 2.66616885e+02), forces1[81], tol);
ASSERT_EQUAL_VEC(Vec3(-3.26780820e+02, -2.54755214e+02, -3.00078477e+02), forces1[82], tol);
ASSERT_EQUAL_VEC(Vec3(-2.95124543e+02, 1.21754917e+02, -2.83484560e+02), forces1[83], tol);
ASSERT_EQUAL_VEC(Vec3( 4.04593126e+02, 1.66803214e+02, -5.58277653e+01), forces1[84], tol);
ASSERT_EQUAL_VEC(Vec3(-8.21659385e+02, 5.19877016e+02, 5.32042741e+01), forces1[85], tol);
ASSERT_EQUAL_VEC(Vec3(-3.49440439e+02, -1.47667513e+02, -4.95516065e+02), forces1[86], tol);
ASSERT_EQUAL_VEC(Vec3(-4.48873387e+02, -7.41398759e+01, -4.28615258e+02), forces1[87], tol);
ASSERT_EQUAL_VEC(Vec3(-1.97035390e+02, -3.40619723e+02, 2.10014966e+00), forces1[88], tol);
ASSERT_EQUAL_VEC(Vec3( 9.52521998e+01, -4.20785075e+02, 5.76686444e+01), forces1[89], tol);
ASSERT_EQUAL_VEC(Vec3( 1.21817965e+02, -1.40423441e+02, -2.93286277e+02), forces1[90], tol);
ASSERT_EQUAL_VEC(Vec3( 2.10624944e+02, 5.87898789e+01, 7.23535437e+01), forces1[91], tol);
ASSERT_EQUAL_VEC(Vec3( 6.72728656e+01, -1.46951701e+02, -2.63713434e+02), forces1[92], tol);
ASSERT_EQUAL_VEC(Vec3( 3.96770378e+02, -4.38652663e+02, -2.57914104e+02), forces1[93], tol);
ASSERT_EQUAL_VEC(Vec3(-2.66188937e+01, 1.59124875e+01, 4.36635801e+02), forces1[94], tol);
ASSERT_EQUAL_VEC(Vec3(-3.00011356e+02, -6.20659688e+01, -4.29149613e+02), forces1[95], tol);
ASSERT_EQUAL_VEC(Vec3(-1.77048319e+02, 5.58928761e+01, 1.05662322e+02), forces1[96], tol);
ASSERT_EQUAL_VEC(Vec3(-6.92523247e+01, -5.45436653e+02, -2.10975756e+01), forces1[97], tol);
ASSERT_EQUAL_VEC(Vec3(-3.90136268e+01, -1.21131977e+02, 1.68383284e+01), forces1[98], tol);
ASSERT_EQUAL_VEC(Vec3(-2.19709988e+01, 1.51713780e+02, -3.39192065e+01), forces1[99], tol);
ASSERT_EQUAL_VEC(Vec3( 9.20577922e+01, -4.22560191e+02, -1.80919327e+01), forces1[100], tol);
ASSERT_EQUAL_VEC(Vec3( 5.80665653e+01, 1.45405257e+02, -4.53162940e+01), forces1[101], tol);
ASSERT_EQUAL_VEC(Vec3( 6.12566193e+02, -1.41221826e+01, 2.13579029e+02), forces1[102], tol);
ASSERT_EQUAL_VEC(Vec3(-1.02954777e+02, -8.76455433e+01, 2.20912307e+02), forces1[103], tol);
ASSERT_EQUAL_VEC(Vec3( 1.44791352e+02, -2.88999813e+02, 1.68134104e+02), forces1[104], tol);
ASSERT_EQUAL_VEC(Vec3( 9.68813543e+01, -2.62260178e+02, -3.07286290e+02), forces1[105], tol);
ASSERT_EQUAL_VEC(Vec3(-1.09040958e+02, -2.43241310e+02, -2.45238041e+02), forces1[106], tol);
ASSERT_EQUAL_VEC(Vec3( 1.89550491e+02, 1.49319105e+02, -3.74285248e+02), forces1[107], tol);
ASSERT_EQUAL_VEC(Vec3( 1.20763399e+02, -1.66082622e+02, -4.46381843e+02), forces1[108], tol);
ASSERT_EQUAL_VEC(Vec3(-6.91025497e+02, 6.62244311e+02, 3.06047534e+01), forces1[109], tol);
ASSERT_EQUAL_VEC(Vec3(-1.61280438e+02, 8.89755700e+01, -3.35630671e+01), forces1[110], tol);
ASSERT_EQUAL_VEC(Vec3(-4.17338144e+02, 4.26061590e+01, -7.77158559e+01), forces1[111], tol);
ASSERT_EQUAL_VEC(Vec3(-4.01742893e+01, 8.24881666e+01, 4.51855292e+02), forces1[112], tol);
ASSERT_EQUAL_VEC(Vec3(-1.40970558e+01, -2.40647487e+02, -1.27598454e+02), forces1[113], tol);
ASSERT_EQUAL_VEC(Vec3( 2.83839724e+02, 2.31041423e+02, 1.33456841e+01), forces1[114], tol);
ASSERT_EQUAL_VEC(Vec3( 5.10867089e+01, -7.13361227e+02, -4.07617467e+02), forces1[115], tol);
ASSERT_EQUAL_VEC(Vec3(-1.56090473e+02, -1.33121120e+02, 2.25952345e+02), forces1[116], tol);
ASSERT_EQUAL_VEC(Vec3( 3.69607528e+01, -1.35106805e+01, 1.10579378e+02), forces1[117], tol);
ASSERT_EQUAL_VEC(Vec3( 3.37809441e+02, 8.10519215e+01, -5.39817090e+01), forces1[118], tol);
ASSERT_EQUAL_VEC(Vec3( 5.49975558e+01, 7.55006073e+01, 1.80545525e+02), forces1[119], tol);
ASSERT_EQUAL_VEC(Vec3( 1.10971245e+02, -3.83971391e+02, 2.77991517e+01), forces1[120], tol);
ASSERT_EQUAL_VEC(Vec3(-2.63190529e+02, -3.16285880e+02, -3.95218767e+02), forces1[121], tol);
ASSERT_EQUAL_VEC(Vec3( 3.42556090e+02, -2.35748098e+02, 4.60355306e+02), forces1[122], tol);
ASSERT_EQUAL_VEC(Vec3( 2.47668934e+02, 2.42870015e+02, 1.52218275e+02), forces1[123], tol);
ASSERT_EQUAL_VEC(Vec3(-2.70198409e+02, 2.08574994e+02, -1.86859372e+02), forces1[124], tol);
ASSERT_EQUAL_VEC(Vec3( 3.24104326e+02, 2.55001752e+02, -1.10880955e+02), forces1[125], tol);
ASSERT_EQUAL_VEC(Vec3( 5.16197105e+02, -5.06150948e+01, -2.80857737e+02), forces1[126], tol);
ASSERT_EQUAL_VEC(Vec3(-3.30849573e+02, -3.47291965e+02, 1.45237748e+02), forces1[127], tol);
ASSERT_EQUAL_VEC(Vec3(-1.28356764e+02, 7.69134918e+01, 2.14911544e+02), forces1[128], tol);
ASSERT_EQUAL_VEC(Vec3(-1.91469755e+02, -2.11473339e+02, 1.35333200e+02), forces1[129], tol);
ASSERT_EQUAL_VEC(Vec3(-2.69887152e+02, 1.11337990e+02, -8.98463460e+01), forces1[130], tol);
ASSERT_EQUAL_VEC(Vec3(-1.36374172e+02, -2.14238377e+02, -5.83866682e+01), forces1[131], tol);
ASSERT_EQUAL_VEC(Vec3(-1.38562455e+02, 2.36128851e+02, -2.08960526e+01), forces1[132], tol);
ASSERT_EQUAL_VEC(Vec3(-2.47599869e+02, 3.98208933e+02, -1.64765063e+02), forces1[133], tol);
ASSERT_EQUAL_VEC(Vec3(-8.37476276e+01, 4.07965070e+02, -1.24163988e+02), forces1[134], tol);
ASSERT_EQUAL_VEC(Vec3(-1.60159071e+02, -6.49998978e+00, 3.55060364e+02), forces1[135], tol);
ASSERT_EQUAL_VEC(Vec3( 3.58249937e+02, -2.81395872e+02, -1.28020722e+02), forces1[136], tol);
ASSERT_EQUAL_VEC(Vec3( 3.33746078e+02, -3.31207307e+02, -2.41631386e+01), forces1[137], tol);
ASSERT_EQUAL_VEC(Vec3(-3.00676509e+02, -1.70097272e+02, 8.30597971e+01), forces1[138], tol);
ASSERT_EQUAL_VEC(Vec3(-1.94891185e+01, 1.35846931e+02, -3.65144038e+02), forces1[139], tol);
ASSERT_EQUAL_VEC(Vec3(-1.77181788e+02, 4.44531481e+01, 2.11474121e+02), forces1[140], tol);
ASSERT_EQUAL_VEC(Vec3( 4.59275521e+02, -3.23637233e+02, 3.67800802e+02), forces1[141], tol);
ASSERT_EQUAL_VEC(Vec3( 4.22121423e+02, 2.22068413e+02, 2.92465809e+02), forces1[142], tol);
ASSERT_EQUAL_VEC(Vec3( 2.83763316e+02, -2.92602600e+02, 8.18013996e+01), forces1[143], tol);
ASSERT_EQUAL_VEC(Vec3(-5.70559390e+01, 2.40295170e+02, -3.40862650e+01), forces1[144], tol);
ASSERT_EQUAL_VEC(Vec3( 1.04075222e+02, 1.58996510e+02, 1.10304777e+01), forces1[145], tol);
ASSERT_EQUAL_VEC(Vec3( 5.88502616e+02, 2.07880339e+02, -7.36908474e+01), forces1[146], tol);
ASSERT_EQUAL_VEC(Vec3( 2.82632623e+02, 2.25995810e+02, 2.16745828e+02), forces1[147], tol);
ASSERT_EQUAL_VEC(Vec3(-2.02435189e+02, 1.65899995e+02, -1.47471730e+02), forces1[148], tol);
ASSERT_EQUAL_VEC(Vec3(-2.96622169e+02, -4.87423685e+02, 1.60844164e+02), forces1[149], tol);
ASSERT_EQUAL_VEC(Vec3( 2.94715891e+02, 1.30347848e+02, 2.82729974e+01), forces1[150], tol);
ASSERT_EQUAL_VEC(Vec3( 3.70885287e+02, 3.22229123e+01, 8.74581871e+01), forces1[151], tol);
ASSERT_EQUAL_VEC(Vec3( 3.43994702e+02, -2.07935556e+02, 3.49067383e+02), forces1[152], tol);
ASSERT_EQUAL_VEC(Vec3( 1.43099882e+01, -2.80336001e+02, 5.69626314e+01), forces1[153], tol);
ASSERT_EQUAL_VEC(Vec3( 9.71948870e+01, 1.42192042e+01, 1.00139441e+02), forces1[154], tol);
ASSERT_EQUAL_VEC(Vec3( 2.56990028e+02, -1.87993683e+01, -1.07611017e+02), forces1[155], tol);
ASSERT_EQUAL_VEC(Vec3( 4.20275466e+02, 1.16943633e+02, 4.29575935e+02), forces1[156], tol);
ASSERT_EQUAL_VEC(Vec3(-2.05593618e+02, -2.74949188e+02, 1.32851151e+02), forces1[157], tol);
ASSERT_EQUAL_VEC(Vec3( 2.88917359e+01, 1.07824736e+01, -2.09437913e+02), forces1[158], tol);
ASSERT_EQUAL_VEC(Vec3(-4.54601782e+02, -2.58268961e+01, -2.66964578e+02), forces1[159], tol);
ASSERT_EQUAL_VEC(Vec3( 5.91049307e+02, -7.76470493e+01, 9.29094217e+01), forces1[160], tol);
ASSERT_EQUAL_VEC(Vec3(-8.26256556e+00, 2.39614788e+02, 1.04874825e+02), forces1[161], tol);
ASSERT_EQUAL_VEC(Vec3( 4.79904829e+01, -1.73703832e+02, -5.98140813e+01), forces1[162], tol);
ASSERT_EQUAL_VEC(Vec3(-1.26007450e+02, 1.64300309e+02, -4.23289873e+02), forces1[163], tol);
ASSERT_EQUAL_VEC(Vec3(-2.03205374e+02, 1.46921807e+02, 2.50701909e+02), forces1[164], tol);
ASSERT_EQUAL_VEC(Vec3(-1.38813652e+02, 1.32930267e+02, 2.75556760e+02), forces1[165], tol);
ASSERT_EQUAL_VEC(Vec3( 9.19035624e+01, -1.55653738e+02, 1.24210719e+02), forces1[166], tol);
ASSERT_EQUAL_VEC(Vec3( 2.09938122e+02, 1.26973729e+02, -3.32817155e+02), forces1[167], tol);
ASSERT_EQUAL_VEC(Vec3( 2.56206143e+02, 6.10122855e+00, 3.42771101e+02), forces1[168], tol);
ASSERT_EQUAL_VEC(Vec3( 3.11438984e+02, 9.42198048e+01, -8.51953228e+00), forces1[169], tol);
ASSERT_EQUAL_VEC(Vec3( 1.78692126e+02, -1.36964221e+01, -2.21830640e+01), forces1[170], tol);
ASSERT_EQUAL_VEC(Vec3(-4.69093086e+01, 5.77169523e+02, 1.04262190e+02), forces1[171], tol);
ASSERT_EQUAL_VEC(Vec3( 1.31254187e+02, 7.57774979e+01, -9.08207172e+01), forces1[172], tol);
ASSERT_EQUAL_VEC(Vec3(-1.69436483e+02, -3.62015361e+02, 2.68889227e+02), forces1[173], tol);
ASSERT_EQUAL_VEC(Vec3(-1.68463797e+02, -2.62627703e+02, 7.41106664e+01), forces1[174], tol);
ASSERT_EQUAL_VEC(Vec3(-2.53154790e+02, 2.68914173e+02, -3.92489521e+02), forces1[175], tol);
ASSERT_EQUAL_VEC(Vec3( 1.95110073e+02, -5.76611142e+01, 3.91951958e+02), forces1[176], tol);
ASSERT_EQUAL_VEC(Vec3( 2.43338357e+02, -3.91477149e+02, -8.63953114e+01), forces1[177], tol);
ASSERT_EQUAL_VEC(Vec3(-1.34179863e+02, 1.46591166e+02, 1.19878040e+02), forces1[178], tol);
ASSERT_EQUAL_VEC(Vec3( 1.63174304e+02, -2.66960106e+02, 1.65770978e+02), forces1[179], tol);
ASSERT_EQUAL_VEC(Vec3( 5.46495327e+01, 6.58671492e+01, -4.94244139e+02), forces1[180], tol);
ASSERT_EQUAL_VEC(Vec3(-1.62957088e+02, 1.53806002e+02, 2.72147870e+01), forces1[181], tol);
ASSERT_EQUAL_VEC(Vec3(-1.97769720e+02, -9.08099669e+01, -5.59653565e+02), forces1[182], tol);
ASSERT_EQUAL_VEC(Vec3(-9.59272102e+01, -3.67219179e+02, -1.37098968e+02), forces1[183], tol);
ASSERT_EQUAL_VEC(Vec3(-3.13273853e+02, -4.77528382e+02, -2.82320271e+02), forces1[184], tol);
ASSERT_EQUAL_VEC(Vec3( 3.47090516e+01, -1.51861017e+02, -2.71782882e+02), forces1[185], tol);
ASSERT_EQUAL_VEC(Vec3(-1.09985174e+02, 7.60658358e+01, -1.90751868e+02), forces1[186], tol);
ASSERT_EQUAL_VEC(Vec3( 3.49595143e+02, 1.52747707e+02, 4.22488620e+02), forces1[187], tol);
ASSERT_EQUAL_VEC(Vec3(-3.59725754e+01, 3.55475701e+01, -7.87334885e+01), forces1[188], tol);
ASSERT_EQUAL_VEC(Vec3(-5.53788647e+02, 1.58101235e+02, -5.47105678e+01), forces1[189], tol);
ASSERT_EQUAL_VEC(Vec3( 8.55520733e+01, -2.88444498e+02, 2.54318577e+01), forces1[190], tol);
ASSERT_EQUAL_VEC(Vec3( 1.96800399e+02, 1.69077439e+02, 2.41074010e+02), forces1[191], tol);
ASSERT_EQUAL_VEC(Vec3( 7.64011738e+01, 3.05710609e+02, 2.76162549e+02), forces1[192], tol);
ASSERT_EQUAL_VEC(Vec3( 1.23265853e+02, 8.57500919e+01, 3.46722858e+02), forces1[193], tol);
ASSERT_EQUAL_VEC(Vec3(-9.05651135e+01, 5.50685032e+01, -4.12462772e+02), forces1[194], tol);
ASSERT_EQUAL_VEC(Vec3(-2.70875191e+00, 1.30545633e+02, -1.55140021e+02), forces1[195], tol);
ASSERT_EQUAL_VEC(Vec3(-1.37389162e+02, -2.94304654e+02, -1.24611408e+02), forces1[196], tol);
ASSERT_EQUAL_VEC(Vec3(-3.56088703e+02, -9.03800091e+01, 3.14895993e+02), forces1[197], tol);
ASSERT_EQUAL_VEC(Vec3(-7.79280727e+01, 1.12179786e+02, 3.86364249e+02), forces1[198], tol);
ASSERT_EQUAL_VEC(Vec3( 2.43080372e+02, 5.36466609e+02, -9.47357535e+01), forces1[199], tol);
ASSERT_EQUAL_VEC(Vec3(-7.39441823e+01, -3.53188450e+02, 1.42886370e+01), forces1[200], tol);
ASSERT_EQUAL_VEC(Vec3( 6.56556933e+02, -2.80423857e+02, 2.95738831e+02), forces1[201], tol);
ASSERT_EQUAL_VEC(Vec3(-2.72306632e+02, -1.44066176e+02, 2.55684522e+02), forces1[202], tol);
ASSERT_EQUAL_VEC(Vec3(-3.75638941e+01, 2.99415967e+02, -4.12490294e+02), forces1[203], tol);
ASSERT_EQUAL_VEC(Vec3( 3.64685442e+02, 3.39117459e+02, -8.08643870e+01), forces1[204], tol);
ASSERT_EQUAL_VEC(Vec3( 4.32799610e+02, -1.78895030e+02, 3.86635331e+02), forces1[205], tol);
ASSERT_EQUAL_VEC(Vec3(-9.04462035e+01, 3.89960088e+02, -5.47950733e+02), forces1[206], tol);
ASSERT_EQUAL_VEC(Vec3(-6.35043448e+02, 1.85486756e+01, 3.17016236e+01), forces1[207], tol);
ASSERT_EQUAL_VEC(Vec3(-5.92896097e+01, -2.85274924e+02, -1.42348620e+02), forces1[208], tol);
ASSERT_EQUAL_VEC(Vec3(-1.35207298e+02, -3.56081009e+02, 1.07991189e+02), forces1[209], tol);
ASSERT_EQUAL_VEC(Vec3( 3.94900105e+02, 9.99453126e+01, -1.57827968e+02), forces1[210], tol);
ASSERT_EQUAL_VEC(Vec3(-1.56528334e+02, -1.66144237e+02, -4.78550980e+00), forces1[211], tol);
ASSERT_EQUAL_VEC(Vec3(-1.77280437e+02, -9.51924294e+01, -3.42048132e+02), forces1[212], tol);
ASSERT_EQUAL_VEC(Vec3( 2.97739381e+02, 7.18137153e+02, -4.94774385e+02), forces1[213], tol);
ASSERT_EQUAL_VEC(Vec3(-5.12807812e+02, 7.63618980e+02, -5.44479748e+01), forces1[214], tol);
ASSERT_EQUAL_VEC(Vec3(-2.55718099e+02, -4.46413837e+02, -1.72488334e+02), forces1[215], tol);
ASSERT_EQUAL_VEC(Vec3( 3.15155213e+02, 5.62372598e+02, 2.42573447e+02), forces1[216], tol);
ASSERT_EQUAL_VEC(Vec3(-2.90686044e+01, 3.91008419e+02, 1.36603112e+02), forces1[217], tol);
ASSERT_EQUAL_VEC(Vec3(-4.26382270e+02, 5.13937396e+02, 8.38021440e+01), forces1[218], tol);
ASSERT_EQUAL_VEC(Vec3(-2.88415254e+02, 6.41404241e+01, 3.02418529e+02), forces1[219], tol);
ASSERT_EQUAL_VEC(Vec3(-1.13264423e+01, -3.20432026e+02, 1.98735533e+02), forces1[220], tol);
ASSERT_EQUAL_VEC(Vec3(-1.73201040e+02, 2.09799717e+02, 2.60748570e+02), forces1[221], tol);
ASSERT_EQUAL_VEC(Vec3( 4.34181587e+02, -1.25026352e+02, 2.52906543e+02), forces1[222], tol);
ASSERT_EQUAL_VEC(Vec3(-3.09528945e+02, 2.63415228e+02, -2.33523213e+02), forces1[223], tol);
ASSERT_EQUAL_VEC(Vec3( 8.65524156e+01, -5.32178407e+02, -2.36443769e+02), forces1[224], tol);
ASSERT_EQUAL_VEC(Vec3(-1.74965243e+02, -2.30177295e+02, -1.33679144e+02), forces1[225], tol);
ASSERT_EQUAL_VEC(Vec3( 1.53826901e+02, -1.24391764e+02, -3.93679341e+01), forces1[226], tol);
ASSERT_EQUAL_VEC(Vec3(-4.51778358e+02, -7.75156480e+01, -5.36133859e+02), forces1[227], tol);
ASSERT_EQUAL_VEC(Vec3( 5.99205686e+01, 2.97292572e+02, 5.99156789e+01), forces1[228], tol);
ASSERT_EQUAL_VEC(Vec3(-3.10624444e+02, 2.94567470e+00, -3.63693136e+01), forces1[229], tol);
ASSERT_EQUAL_VEC(Vec3( 3.85876747e+02, 1.41639709e+02, 2.75108543e+02), forces1[230], tol);
ASSERT_EQUAL_VEC(Vec3( 5.43221531e+02, -3.20245253e+02, -3.02057882e+01), forces1[231], tol);
ASSERT_EQUAL_VEC(Vec3( 3.85583563e+02, 6.64091011e+01, -3.49559777e+01), forces1[232], tol);
ASSERT_EQUAL_VEC(Vec3(-4.52863518e+02, -3.40704717e+02, 1.28905508e+02), forces1[233], tol);
ASSERT_EQUAL_VEC(Vec3( 9.34276939e+02, -1.39482274e+02, 3.57869403e+02), forces1[234], tol);
ASSERT_EQUAL_VEC(Vec3(-2.03067148e+02, 7.44441061e+01, 9.27320006e+01), forces1[235], tol);
ASSERT_EQUAL_VEC(Vec3(-5.97531539e+02, 1.26804078e+02, 2.78893030e+02), forces1[236], tol);
ASSERT_EQUAL_VEC(Vec3( 5.67860193e+02, -5.34681659e+02, -1.84230283e+02), forces1[237], tol);
ASSERT_EQUAL_VEC(Vec3( 2.13190333e+02, 2.99783610e+02, -2.66458597e+02), forces1[238], tol);
ASSERT_EQUAL_VEC(Vec3( 3.11473403e+01, 4.54685224e+02, 3.81034869e+01), forces1[239], tol);
ASSERT_EQUAL_VEC(Vec3( 1.57514487e+02, 3.19318405e+01, -1.57720436e+02), forces1[240], tol);
ASSERT_EQUAL_VEC(Vec3( 4.34917141e+02, -4.69320639e+02, -3.96620193e+01), forces1[241], tol);
ASSERT_EQUAL_VEC(Vec3(-3.54127895e+01, -1.00145510e+02, 7.07653587e+01), forces1[242], tol);
ASSERT_EQUAL_VEC(Vec3( 3.29669760e+02, 3.63920894e+02, -3.89179023e+02), forces1[243], tol);
ASSERT_EQUAL_VEC(Vec3( 7.63985812e+02, 2.97343162e+02, -5.39992884e+01), forces1[244], tol);
ASSERT_EQUAL_VEC(Vec3(-2.67917290e+02, 2.23564800e+02, -1.67266290e+02), forces1[245], tol);
ASSERT_EQUAL_VEC(Vec3(-6.45289709e+02, -9.38444040e+00, -5.71116404e+02), forces1[246], tol);
ASSERT_EQUAL_VEC(Vec3( 1.48220582e+02, -1.78737448e+02, 8.43814640e+01), forces1[247], tol);
ASSERT_EQUAL_VEC(Vec3( 3.26731869e+02, -3.84524983e+02, 2.88339532e+02), forces1[248], tol);
ASSERT_EQUAL_VEC(Vec3( 2.58365914e+02, 5.43905231e+01, 3.38641536e+02), forces1[249], tol);
ASSERT_EQUAL_VEC(Vec3(-3.51600062e+02, -3.06740536e+02, -2.99454859e+01), forces1[250], tol);
ASSERT_EQUAL_VEC(Vec3(-2.78580097e+01, 2.36156123e+02, 1.06081152e+01), forces1[251], tol);
ASSERT_EQUAL_VEC(Vec3(-1.75899733e+02, -9.38608562e+00, 1.84207408e+02), forces1[252], tol);
ASSERT_EQUAL_VEC(Vec3(-5.60399133e+02, 1.90145998e+02, -2.90215623e+02), forces1[253], tol);
ASSERT_EQUAL_VEC(Vec3(-1.69434526e+02, 2.23702263e+02, -3.41994449e+02), forces1[254], tol);
ASSERT_EQUAL_VEC(Vec3(-6.96578236e+01, 1.66438449e+02, -6.77499321e+00), forces1[255], tol);
ASSERT_EQUAL_VEC(Vec3( 2.73655591e+02, 1.49071325e+00, 7.80565008e+01), forces1[256], tol);
ASSERT_EQUAL_VEC(Vec3(-2.58366132e+02, 8.46288614e+01, -9.42098129e+01), forces1[257], tol);
ASSERT_EQUAL_VEC(Vec3(-4.25847146e+02, 9.84292470e+00, 6.39709870e+01), forces1[258], tol);
ASSERT_EQUAL_VEC(Vec3( 1.12083087e+02, 6.10255158e+01, -1.38662830e+02), forces1[259], tol);
ASSERT_EQUAL_VEC(Vec3( 3.21828259e+01, 3.67493765e+02, 6.86972453e+01), forces1[260], tol);
ASSERT_EQUAL_VEC(Vec3(-3.72206379e+02, 3.36289489e+02, 3.15273168e+02), forces1[261], tol);
ASSERT_EQUAL_VEC(Vec3( 4.28443395e+01, 5.93339946e+01, 4.86905134e+02), forces1[262], tol);
ASSERT_EQUAL_VEC(Vec3( 3.03267567e+01, 3.70293845e+02, -5.83420472e+02), forces1[263], tol);
ASSERT_EQUAL_VEC(Vec3(-1.61738517e+02, 3.70576503e+02, -2.03659425e+01), forces1[264], tol);
ASSERT_EQUAL_VEC(Vec3(-6.67229637e+02, -1.24339105e+01, -9.41959187e+01), forces1[265], tol);
ASSERT_EQUAL_VEC(Vec3( 4.66955725e+02, 3.49515015e+02, 2.04652411e+02), forces1[266], tol);
ASSERT_EQUAL_VEC(Vec3(-3.52483062e+02, -1.24093769e+01, 2.24828418e+01), forces1[267], tol);
ASSERT_EQUAL_VEC(Vec3( 4.58746968e+01, -2.20898924e+02, 7.64552584e+01), forces1[268], tol);
ASSERT_EQUAL_VEC(Vec3(-4.43630058e+01, -1.14899930e+02, -2.87900025e+02), forces1[269], tol);
ASSERT_EQUAL_VEC(Vec3(-9.22312443e+01, 6.40883419e+02, 3.17836134e+02), forces1[270], tol);
ASSERT_EQUAL_VEC(Vec3(-2.15623653e+02, -2.05460783e+02, 6.85627383e+01), forces1[271], tol);
ASSERT_EQUAL_VEC(Vec3( 2.24218604e+02, -1.43080040e+02, -4.17826551e+01), forces1[272], tol);
ASSERT_EQUAL_VEC(Vec3( 1.79823435e+02, -3.72425962e+02, 2.12155872e+02), forces1[273], tol);
ASSERT_EQUAL_VEC(Vec3( 9.74694224e+01, 7.66864835e+01, -5.04542197e+02), forces1[274], tol);
ASSERT_EQUAL_VEC(Vec3( 2.18132641e+01, -5.76171362e+02, -3.23926383e+02), forces1[275], tol);
ASSERT_EQUAL_VEC(Vec3(-2.97073240e+02, -1.47150317e+02, -1.56565197e+02), forces1[276], tol);
ASSERT_EQUAL_VEC(Vec3( 4.21710637e+01, -2.91447830e+02, -1.50257335e+02), forces1[277], tol);
ASSERT_EQUAL_VEC(Vec3( 1.75442608e+02, -9.12859677e+01, 6.45491342e+01), forces1[278], tol);
ASSERT_EQUAL_VEC(Vec3( 1.50392869e+02, -1.42464974e+02, 2.67956841e+02), forces1[279], tol);
ASSERT_EQUAL_VEC(Vec3(-4.86651400e+02, -2.18667898e+01, 1.45713495e+02), forces1[280], tol);
ASSERT_EQUAL_VEC(Vec3( 9.28412896e+01, -6.94874667e+01, -8.89080435e+00), forces1[281], tol);
ASSERT_EQUAL_VEC(Vec3(-5.85240520e+01, 2.84901112e+02, 4.82507287e+02), forces1[282], tol);
ASSERT_EQUAL_VEC(Vec3(-2.83558886e+01, -1.73735212e+02, -3.77887550e+02), forces1[283], tol);
ASSERT_EQUAL_VEC(Vec3(-4.34949543e+02, -1.30350821e+02, -3.31882572e+02), forces1[284], tol);
ASSERT_EQUAL_VEC(Vec3( 3.85307564e+01, -2.54990214e+02, -5.18576435e+02), forces1[285], tol);
ASSERT_EQUAL_VEC(Vec3(-7.78941445e+01, 1.25379603e+02, -3.80663737e+02), forces1[286], tol);
ASSERT_EQUAL_VEC(Vec3(-1.27210929e+02, 3.88670263e+02, 9.40643492e+01), forces1[287], tol);
ASSERT_EQUAL_VEC(Vec3( 1.39431191e+02, 4.03532370e+02, -4.33316557e+02), forces1[288], tol);
ASSERT_EQUAL_VEC(Vec3(-2.89025871e+00, -2.06301323e+02, -7.48588265e+02), forces1[289], tol);
ASSERT_EQUAL_VEC(Vec3( 1.07859207e+01, 2.35870879e+02, -1.38371079e+02), forces1[290], tol);
ASSERT_EQUAL_VEC(Vec3( 8.60427044e+01, 5.74941322e+02, 1.95760444e+02), forces1[291], tol);
ASSERT_EQUAL_VEC(Vec3(-2.45607229e+02, 1.95528757e+02, 1.22011573e+02), forces1[292], tol);
ASSERT_EQUAL_VEC(Vec3(-3.33845399e+02, -2.71367745e+02, -3.43358433e+02), forces1[293], tol);
ASSERT_EQUAL_VEC(Vec3(-4.28617139e+02, 1.60904342e+02, 2.18072650e+02), forces1[294], tol);
ASSERT_EQUAL_VEC(Vec3(-5.40064874e+02, -1.47448303e+02, 4.35229212e+02), forces1[295], tol);
ASSERT_EQUAL_VEC(Vec3( 1.48815476e+02, 3.45639218e+01, -1.25812382e+02), forces1[296], tol);
ASSERT_EQUAL_VEC(Vec3( 3.39107837e+02, 3.68504225e+01, 2.99572367e+01), forces1[297], tol);
ASSERT_EQUAL_VEC(Vec3(-1.67736264e+02, -2.84608426e+02, 3.88526274e+01), forces1[298], tol);
ASSERT_EQUAL_VEC(Vec3(-3.21480824e+02, -4.49315290e+02, 1.41881616e+02), forces1[299], tol);
ASSERT_EQUAL_VEC(Vec3( 1.16232320e+02, 6.79621131e+00, -1.08227149e+02), forces1[300], tol);
ASSERT_EQUAL_VEC(Vec3(-1.85570778e+02, -2.95260833e+02, 5.01922551e+02), forces1[301], tol);
ASSERT_EQUAL_VEC(Vec3( 9.27568700e+01, -3.83929681e+02, -2.77944844e+02), forces1[302], tol);
ASSERT_EQUAL_VEC(Vec3(-3.97618765e+01, 2.84553517e+02, 2.33447892e+02), forces1[303], tol);
ASSERT_EQUAL_VEC(Vec3( 1.40428160e+00, 2.25397804e+02, 9.47967922e+01), forces1[304], tol);
ASSERT_EQUAL_VEC(Vec3( 1.06100768e+02, 2.06346469e+02, -2.00231208e+02), forces1[305], tol);
ASSERT_EQUAL_VEC(Vec3(-3.33973365e+02, -3.09218137e+01, -9.23886091e+01), forces1[306], tol);
ASSERT_EQUAL_VEC(Vec3( 1.47272865e+01, 1.47732830e+02, 2.61213219e+02), forces1[307], tol);
ASSERT_EQUAL_VEC(Vec3( 4.44151506e+01, 1.15101400e+02, 1.21319924e+02), forces1[308], tol);
ASSERT_EQUAL_VEC(Vec3(-2.53470761e+02, 2.00350042e+02, 3.38960006e+01), forces1[309], tol);
ASSERT_EQUAL_VEC(Vec3( 1.06594777e+02, -1.16077611e+02, 5.96359334e+01), forces1[310], tol);
ASSERT_EQUAL_VEC(Vec3(-2.01610478e+02, 4.15967754e+01, 4.19293914e+02), forces1[311], tol);
ASSERT_EQUAL_VEC(Vec3( 4.40516207e+02, 1.34417352e+02, 7.74205210e+01), forces1[312], tol);
ASSERT_EQUAL_VEC(Vec3(-3.89160154e+02, -2.83344572e+02, -4.23546685e+02), forces1[313], tol);
ASSERT_EQUAL_VEC(Vec3( 2.42068089e+02, -6.45978033e+02, 3.91506210e+02), forces1[314], tol);
ASSERT_EQUAL_VEC(Vec3(-3.71531404e+02, -4.54039493e+01, 5.02846871e+02), forces1[315], tol);
ASSERT_EQUAL_VEC(Vec3(-3.52589600e+02, 8.61061306e+01, 2.63798519e+02), forces1[316], tol);
ASSERT_EQUAL_VEC(Vec3( 2.18337261e+01, -1.24898517e+02, -2.32012734e+02), forces1[317], tol);
ASSERT_EQUAL_VEC(Vec3(-4.28763733e+02, 2.69298277e+02, -1.63307373e+02), forces1[318], tol);
ASSERT_EQUAL_VEC(Vec3( 6.90348283e+01, 2.01119799e+02, -1.25394407e+02), forces1[319], tol);
ASSERT_EQUAL_VEC(Vec3( 4.42904652e+01, -1.48495330e+02, 9.26084336e+01), forces1[320], tol);
ASSERT_EQUAL_VEC(Vec3(-3.52950825e+01, 1.28676758e+02, -3.63357597e+02), forces1[321], tol);
ASSERT_EQUAL_VEC(Vec3(-5.24105679e+02, -9.42729828e+01, 1.18442536e+01), forces1[322], tol);
ASSERT_EQUAL_VEC(Vec3( 2.26120919e+02, 3.21635617e+02, -3.90518982e+02), forces1[323], tol);
ASSERT_EQUAL_VEC(Vec3( 3.59312804e+01, -3.07405817e+02, -5.38361197e+01), forces1[324], tol);
ASSERT_EQUAL_VEC(Vec3(-1.17174424e+02, 2.29573805e+02, 1.57259382e+02), forces1[325], tol);
ASSERT_EQUAL_VEC(Vec3(-9.86427642e+01, -2.13494062e+02, 9.34926885e+01), forces1[326], tol);
ASSERT_EQUAL_VEC(Vec3( 2.32229566e+02, 6.06664996e+01, -3.57479293e+02), forces1[327], tol);
ASSERT_EQUAL_VEC(Vec3(-4.16211931e+01, 1.12554898e+02, -2.00660318e+02), forces1[328], tol);
ASSERT_EQUAL_VEC(Vec3(-5.14819799e+01, 2.96285849e+02, 3.68505357e+02), forces1[329], tol);
ASSERT_EQUAL_VEC(Vec3( 1.02082491e+02, -9.70741301e+01, 3.02493232e+01), forces1[330], tol);
ASSERT_EQUAL_VEC(Vec3(-1.03774260e+02, -1.54760894e+02, 2.10251674e+02), forces1[331], tol);
ASSERT_EQUAL_VEC(Vec3( 1.90529911e+02, 6.04211367e+01, -9.59263387e+01), forces1[332], tol);
ASSERT_EQUAL_VEC(Vec3(-1.16797386e+02, -5.22831328e+02, 5.66578589e+02), forces1[333], tol);
ASSERT_EQUAL_VEC(Vec3( 2.18104094e+02, -1.37421044e+02, 1.53038417e+02), forces1[334], tol);
ASSERT_EQUAL_VEC(Vec3(-9.04351491e+01, -1.47606538e+02, 8.89490712e+01), forces1[335], tol);
ASSERT_EQUAL_VEC(Vec3( 2.09976599e+01, -2.05500194e+02, 1.26830512e+02), forces1[336], tol);
ASSERT_EQUAL_VEC(Vec3(-1.68042754e+02, -2.97763125e+02, -1.20743571e+02), forces1[337], tol);
ASSERT_EQUAL_VEC(Vec3( 3.99127236e+02, 2.95151794e+02, 3.91132406e+02), forces1[338], tol);
ASSERT_EQUAL_VEC(Vec3( 6.84892471e+02, -2.36108313e+02, 3.42251555e+02), forces1[339], tol);
ASSERT_EQUAL_VEC(Vec3(-1.26672745e+02, 1.75724905e+02, 1.84788262e+02), forces1[340], tol);
ASSERT_EQUAL_VEC(Vec3(-1.32600337e+01, 4.13643381e+01, -7.23942729e+01), forces1[341], tol);
ASSERT_EQUAL_VEC(Vec3(-2.77915476e+02, 3.10253507e+02, 1.84685854e+01), forces1[342], tol);
ASSERT_EQUAL_VEC(Vec3( 1.17778181e+02, 2.88287609e+02, 2.83205737e+02), forces1[343], tol);
ASSERT_EQUAL_VEC(Vec3(-2.14268136e+01, 5.21209080e+02, 1.00248187e+02), forces1[344], tol);
ASSERT_EQUAL_VEC(Vec3( 1.24343155e+02, -1.12330818e+02, 2.11958643e+02), forces1[345], tol);
ASSERT_EQUAL_VEC(Vec3( 6.50667567e+00, 2.20334635e+02, -3.03866910e+02), forces1[346], tol);
ASSERT_EQUAL_VEC(Vec3( 3.27052657e+02, 1.14360733e+02, 4.53348042e+02), forces1[347], tol);
ASSERT_EQUAL_VEC(Vec3( 3.89440847e+01, 3.55283477e+02, 2.25739503e+01), forces1[348], tol);
ASSERT_EQUAL_VEC(Vec3(-2.25235013e+02, -2.30566443e+02, -2.32657384e+02), forces1[349], tol);
ASSERT_EQUAL_VEC(Vec3(-4.40565237e+02, -4.18860845e+01, -8.73230334e+01), forces1[350], tol);
ASSERT_EQUAL_VEC(Vec3( 2.03282144e+02, -7.41935514e+01, -3.11210179e+02), forces1[351], tol);
ASSERT_EQUAL_VEC(Vec3( 3.14039162e+02, -4.52223142e+01, 1.15692941e+02), forces1[352], tol);
ASSERT_EQUAL_VEC(Vec3(-3.05758853e+02, -8.44053388e+01, 2.77050868e+02), forces1[353], tol);
ASSERT_EQUAL_VEC(Vec3( 6.03148128e+02, 7.49868064e+01, 1.08098034e+02), forces1[354], tol);
ASSERT_EQUAL_VEC(Vec3( 1.19374260e+02, 3.21999447e+02, 1.19594257e+01), forces1[355], tol);
ASSERT_EQUAL_VEC(Vec3( 1.06938874e+02, 2.87582961e+01, 2.37371892e+02), forces1[356], tol);
ASSERT_EQUAL_VEC(Vec3( 3.21431483e+02, -7.92928539e+02, 4.18517370e+01), forces1[357], tol);
ASSERT_EQUAL_VEC(Vec3(-1.08651668e+02, 2.36736551e+02, -5.75424218e+02), forces1[358], tol);
ASSERT_EQUAL_VEC(Vec3(-1.53739502e+02, -4.66583392e+02, 3.71587906e+02), forces1[359], tol);
ASSERT_EQUAL_VEC(Vec3(-1.56290228e+02, -8.97065131e+01, -3.26308160e+02), forces1[360], tol);
ASSERT_EQUAL_VEC(Vec3( 3.42780206e+02, -3.18082387e+01, -4.08917799e+02), forces1[361], tol);
ASSERT_EQUAL_VEC(Vec3( 1.47584556e+02, 4.76918724e+02, 4.19802714e+01), forces1[362], tol);
ASSERT_EQUAL_VEC(Vec3( 6.13790369e-01, -4.13596811e+01, 4.39898790e+02), forces1[363], tol);
ASSERT_EQUAL_VEC(Vec3(-2.56833301e+02, -1.83037454e+02, 5.99913896e+01), forces1[364], tol);
ASSERT_EQUAL_VEC(Vec3( 2.48242646e+02, 5.50143719e+01, -1.55138787e+01), forces1[365], tol);
ASSERT_EQUAL_VEC(Vec3(-1.93628316e+02, -1.86534319e+01, 6.08100347e-01), forces1[366], tol);
ASSERT_EQUAL_VEC(Vec3(-1.63211080e+02, 1.90023269e+02, -4.93452441e+02), forces1[367], tol);
ASSERT_EQUAL_VEC(Vec3(-2.85383893e+01, -4.66032637e+02, -2.10849424e+02), forces1[368], tol);
ASSERT_EQUAL_VEC(Vec3( 2.27151522e+02, 1.52613464e+01, -1.64052797e+02), forces1[369], tol);
ASSERT_EQUAL_VEC(Vec3(-1.07153893e+02, 2.01257828e+01, 6.04845684e+01), forces1[370], tol);
ASSERT_EQUAL_VEC(Vec3( 2.41332167e+02, 4.05683718e+02, -8.71412268e+01), forces1[371], tol);
ASSERT_EQUAL_VEC(Vec3( 1.33799918e+02, 1.72174836e+02, -1.15342391e+02), forces1[372], tol);
ASSERT_EQUAL_VEC(Vec3( 4.19296496e+02, -4.68248373e+02, -3.30514440e+02), forces1[373], tol);
ASSERT_EQUAL_VEC(Vec3(-1.88005342e+02, -2.43525310e+02, 8.87016270e+01), forces1[374], tol);
ASSERT_EQUAL_VEC(Vec3( 1.54021861e+02, 2.52343848e+02, 1.76403306e+02), forces1[375], tol);
ASSERT_EQUAL_VEC(Vec3(-1.10645837e+02, 3.33150389e+01, 1.23771287e+02), forces1[376], tol);
ASSERT_EQUAL_VEC(Vec3( 1.64776571e+02, 8.96065400e-01, 1.56210970e+02), forces1[377], tol);
ASSERT_EQUAL_VEC(Vec3(-1.65400014e+02, -1.50236349e+02, 2.41793992e+02), forces1[378], tol);
ASSERT_EQUAL_VEC(Vec3( 4.78064936e+02, -2.36432797e+02, 4.04933329e+02), forces1[379], tol);
ASSERT_EQUAL_VEC(Vec3(-2.33366235e+02, -7.47065850e+02, 1.09704004e+02), forces1[380], tol);
ASSERT_EQUAL_VEC(Vec3( 1.09391089e+02, -3.37517532e+02, 1.99876556e+02), forces1[381], tol);
ASSERT_EQUAL_VEC(Vec3( 1.45367287e+02, 1.34686504e+02, 3.46411849e+02), forces1[382], tol);
ASSERT_EQUAL_VEC(Vec3(-8.86938780e+01, 1.70740693e+02, 1.86633045e+02), forces1[383], tol);
ASSERT_EQUAL_VEC(Vec3( 1.03267086e+02, -2.54994757e+02, -2.04500741e+01), forces1[384], tol);
ASSERT_EQUAL_VEC(Vec3(-5.06124981e+02, -3.72066492e+02, 9.10787327e+01), forces1[385], tol);
ASSERT_EQUAL_VEC(Vec3( 4.33830100e+02, 5.30903070e+02, 1.18187318e+02), forces1[386], tol);
ASSERT_EQUAL_VEC(Vec3(-3.34819970e+02, 2.29884568e+02, 1.73578789e+02), forces1[387], tol);
ASSERT_EQUAL_VEC(Vec3(-3.26221462e+00, 3.50944116e+02, -5.69053250e+00), forces1[388], tol);
ASSERT_EQUAL_VEC(Vec3(-3.18235913e+02, -4.16037121e+02, 3.93503581e+00), forces1[389], tol);
ASSERT_EQUAL_VEC(Vec3(-9.01641240e+01, -2.83129838e+02, -1.78590049e+02), forces1[390], tol);
ASSERT_EQUAL_VEC(Vec3(-1.86933795e+02, 2.96536275e+02, -1.29641779e+02), forces1[391], tol);
ASSERT_EQUAL_VEC(Vec3(-7.78116915e+02, 3.37770325e+02, 2.46251396e+02), forces1[392], tol);
ASSERT_EQUAL_VEC(Vec3(-4.88284661e+02, -3.62815567e+02, 1.09199755e+02), forces1[393], tol);
ASSERT_EQUAL_VEC(Vec3( 1.68815587e+02, -1.54091010e+02, 2.44129524e+02), forces1[394], tol);
ASSERT_EQUAL_VEC(Vec3( 8.59930047e+01, -4.73421243e+02, 1.81326697e+02), forces1[395], tol);
ASSERT_EQUAL_VEC(Vec3(-5.15421845e+01, 3.80615163e+01, 1.48495241e+02), forces1[396], tol);
ASSERT_EQUAL_VEC(Vec3( 4.40902491e+02, -9.36772951e+01, 2.19937945e+02), forces1[397], tol);
ASSERT_EQUAL_VEC(Vec3( 4.55969069e+01, 5.74615780e+02, -5.74937832e+01), forces1[398], tol);
ASSERT_EQUAL_VEC(Vec3( 1.07815006e+02, -3.19754972e+02, 1.16024132e+01), forces1[399], tol);
ASSERT_EQUAL_VEC(Vec3(-1.73815462e+02, -1.72653517e+02, 3.26124498e+02), forces1[400], tol);
ASSERT_EQUAL_VEC(Vec3(-4.48598839e+02, -4.95402699e+02, -1.37769354e+02), forces1[401], tol);
ASSERT_EQUAL_VEC(Vec3( 1.43484140e+02, -7.71666828e+01, 1.27161815e+02), forces1[402], tol);
ASSERT_EQUAL_VEC(Vec3( 1.26016451e+02, -3.81399993e+02, -2.85764454e+02), forces1[403], tol);
ASSERT_EQUAL_VEC(Vec3(-2.53052241e+02, -2.67885549e+02, 1.51849847e+02), forces1[404], tol);
ASSERT_EQUAL_VEC(Vec3(-1.01063516e+02, -1.79837645e+02, -2.05759531e+02), forces1[405], tol);
ASSERT_EQUAL_VEC(Vec3( 1.32065714e+02, -2.56988983e+02, -4.06119988e+02), forces1[406], tol);
ASSERT_EQUAL_VEC(Vec3( 5.49845413e+01, 5.21020888e+01, -2.40829321e+01), forces1[407], tol);
ASSERT_EQUAL_VEC(Vec3(-3.04151550e+01, -3.45385052e+02, 3.52732396e+02), forces1[408], tol);
ASSERT_EQUAL_VEC(Vec3( 5.76196622e+02, 2.51408790e+02, -2.05694905e+02), forces1[409], tol);
ASSERT_EQUAL_VEC(Vec3(-5.46232631e+02, -3.81426810e+01, 1.55940692e+02), forces1[410], tol);
ASSERT_EQUAL_VEC(Vec3(-1.00759671e+02, 2.13338964e+02, 3.33068075e+02), forces1[411], tol);
ASSERT_EQUAL_VEC(Vec3( 8.00270910e+01, 1.85305844e+02, -4.43363841e+02), forces1[412], tol);
ASSERT_EQUAL_VEC(Vec3(-1.53961901e+02, 1.76452569e+01, 5.07044493e+01), forces1[413], tol);
ASSERT_EQUAL_VEC(Vec3(-2.54074128e+01, -1.01739609e+02, -1.47118371e+02), forces1[414], tol);
ASSERT_EQUAL_VEC(Vec3(-2.53018564e+02, 2.89709169e+02, -1.78282141e+02), forces1[415], tol);
ASSERT_EQUAL_VEC(Vec3(-1.30257510e+02, -4.66610129e+02, -3.53374887e+02), forces1[416], tol);
ASSERT_EQUAL_VEC(Vec3( 1.82472116e+02, 3.77974857e+02, 1.90476251e+02), forces1[417], tol);
ASSERT_EQUAL_VEC(Vec3(-2.59801797e+02, 3.29708022e+02, 3.93439213e+02), forces1[418], tol);
ASSERT_EQUAL_VEC(Vec3(-1.08573872e+01, -5.30931700e+02, -1.53102646e+01), forces1[419], tol);
ASSERT_EQUAL_VEC(Vec3(-3.27827015e+01, -3.45915139e+02, -1.45604367e+02), forces1[420], tol);
ASSERT_EQUAL_VEC(Vec3(-1.92424159e+02, 3.69087390e+00, -1.26537763e+02), forces1[421], tol);
ASSERT_EQUAL_VEC(Vec3( 2.61430852e+02, -1.63239575e+02, -1.22742408e+02), forces1[422], tol);
ASSERT_EQUAL_VEC(Vec3(-1.42902423e+02, 4.15429338e+02, 5.04220960e+02), forces1[423], tol);
ASSERT_EQUAL_VEC(Vec3( 3.36581855e+02, -1.06051528e+02, 4.54169047e+02), forces1[424], tol);
ASSERT_EQUAL_VEC(Vec3(-2.32221397e+02, -1.86391490e+00, 4.48412526e+01), forces1[425], tol);
ASSERT_EQUAL_VEC(Vec3( 4.12435657e+02, -5.04677016e+02, 4.00894902e+02), forces1[426], tol);
ASSERT_EQUAL_VEC(Vec3(-1.54630518e+02, -2.68571764e+02, -1.52962938e+01), forces1[427], tol);
ASSERT_EQUAL_VEC(Vec3( 2.69725750e+02, 2.18570974e+02, -1.75370463e+02), forces1[428], tol);
ASSERT_EQUAL_VEC(Vec3(-1.77174496e+01, -4.66383654e+01, 6.24325771e+01), forces1[429], tol);
ASSERT_EQUAL_VEC(Vec3(-1.29891497e+02, 8.07513513e+01, 1.84796034e+01), forces1[430], tol);
ASSERT_EQUAL_VEC(Vec3( 1.76173623e+02, -3.20023221e+02, -2.47794470e+02), forces1[431], tol);
ASSERT_EQUAL_VEC(Vec3(-1.68165877e+02, 2.05742253e+02, -1.17371622e+02), forces1[432], tol);
ASSERT_EQUAL_VEC(Vec3(-1.93368937e+02, 7.77030375e+01, -1.60942447e+02), forces1[433], tol);
ASSERT_EQUAL_VEC(Vec3(-8.97795426e+01, 2.56568674e+02, -3.94711205e+02), forces1[434], tol);
ASSERT_EQUAL_VEC(Vec3(-2.33025806e+02, 9.53111817e+01, 3.02984350e+02), forces1[435], tol);
ASSERT_EQUAL_VEC(Vec3( 4.34416040e+01, -4.44341362e+02, -4.99903855e+00), forces1[436], tol);
ASSERT_EQUAL_VEC(Vec3( 3.32041110e+02, -4.10874874e+02, -6.13697373e+01), forces1[437], tol);
ASSERT_EQUAL_VEC(Vec3(-1.45971034e+02, -2.28311940e+02, -2.99006034e+02), forces1[438], tol);
ASSERT_EQUAL_VEC(Vec3(-1.38544644e+02, -1.27394726e+02, -2.93844502e+02), forces1[439], tol);
ASSERT_EQUAL_VEC(Vec3( 2.97433737e+02, 3.38274025e+02, -2.14834485e+02), forces1[440], tol);
ASSERT_EQUAL_VEC(Vec3( 2.49325388e+01, 1.88464224e+02, -8.02957242e+01), forces1[441], tol);
ASSERT_EQUAL_VEC(Vec3(-1.32436289e+02, -3.86539713e+02, -6.69802109e+01), forces1[442], tol);
ASSERT_EQUAL_VEC(Vec3( 1.84591010e+01, 2.24796153e+02, -1.36943849e+02), forces1[443], tol);
ASSERT_EQUAL_VEC(Vec3( 2.15317088e+02, 1.06092575e+02, -1.25718903e+02), forces1[444], tol);
ASSERT_EQUAL_VEC(Vec3( 3.07793097e+02, 4.34340797e+02, 2.17184869e+01), forces1[445], tol);
ASSERT_EQUAL_VEC(Vec3( 8.06779109e+00, 4.99925960e+01, -3.14233384e+02), forces1[446], tol);
ASSERT_EQUAL_VEC(Vec3( 1.36943103e+03, 6.44803649e+02, -3.85109067e+02), forces1[447], tol);
ASSERT_EQUAL_VEC(Vec3( 3.32242272e+02, -1.04208324e+02, -4.08589589e+02), forces1[448], tol);
ASSERT_EQUAL_VEC(Vec3( 3.73255876e+02, 2.70951542e+02, 3.80697548e+02), forces1[449], tol);
ASSERT_EQUAL_VEC(Vec3(-2.09798900e+02, 4.29900198e+02, 8.40839028e+02), forces1[450], tol);
ASSERT_EQUAL_VEC(Vec3(-3.48538763e+02, -3.63130308e+02, 5.10379446e+02), forces1[451], tol);
ASSERT_EQUAL_VEC(Vec3(-3.28341714e+00, 4.69698489e-01, -6.05231376e+00), forces1[452], tol);
ASSERT_EQUAL_VEC(Vec3( 3.83253792e+01, 3.59830676e+02, 8.67060383e+02), forces1[453], tol);
ASSERT_EQUAL_VEC(Vec3(-1.57391804e+02, 5.68715567e+02, -1.87718510e+02), forces1[454], tol);
ASSERT_EQUAL_VEC(Vec3( 4.16155580e+02, 5.83079478e+01, -5.75730469e+02), forces1[455], tol);
ASSERT_EQUAL_VEC(Vec3( 2.58779906e+02, -1.00655656e+02, -4.43420159e+02), forces1[456], tol);
ASSERT_EQUAL_VEC(Vec3( 1.38953668e+02, 4.93490759e+02, 9.06786307e+02), forces1[457], tol);
ASSERT_EQUAL_VEC(Vec3(-4.81272447e+01, 9.53310029e+02, -1.83936453e+02), forces1[458], tol);
ASSERT_EQUAL_VEC(Vec3( 6.64523579e+02, -3.77321549e+02, 1.93098326e+02), forces1[459], tol);
ASSERT_EQUAL_VEC(Vec3( 8.34547360e+01, 1.77795489e+02, 4.70670853e+02), forces1[460], tol);
ASSERT_EQUAL_VEC(Vec3( 9.98948440e+02, -3.98575936e+02, 9.83617396e+02), forces1[461], tol);
ASSERT_EQUAL_VEC(Vec3(-5.06587875e+02, -4.10766357e+01, 4.75189712e+02), forces1[462], tol);
ASSERT_EQUAL_VEC(Vec3(-7.58615745e+01, 1.45234354e+02, 4.52909546e+01), forces1[463], tol);
ASSERT_EQUAL_VEC(Vec3(-1.13502225e+02, 5.29861493e+02, -3.00954235e+02), forces1[464], tol);
ASSERT_EQUAL_VEC(Vec3(-1.46461752e+02, -3.86629519e+02, -6.42383694e+02), forces1[465], tol);
ASSERT_EQUAL_VEC(Vec3(-2.70141632e+02, 3.67195269e+02, -1.78826923e+02), forces1[466], tol);
ASSERT_EQUAL_VEC(Vec3(-1.23815721e+02, -1.77323218e+02, 2.37363709e+02), forces1[467], tol);
ASSERT_EQUAL_VEC(Vec3(-4.94036325e+02, 1.34870851e+02, 6.98764302e+02), forces1[468], tol);
ASSERT_EQUAL_VEC(Vec3( 7.08752797e+02, 1.85258249e+02, 5.51159246e+02), forces1[469], tol);
ASSERT_EQUAL_VEC(Vec3( 2.01291495e+02, -5.84720738e+02, 5.82921267e+02), forces1[470], tol);
ASSERT_EQUAL_VEC(Vec3( 5.29650511e+02, 4.07054187e+01, 9.99156291e+01), forces1[471], tol);
ASSERT_EQUAL_VEC(Vec3(-1.19707215e+02, 4.42978965e+02, -2.70437350e+02), forces1[472], tol);
ASSERT_EQUAL_VEC(Vec3(-6.87780787e+02, -1.32227443e+02, -7.35818874e+01), forces1[473], tol);
ASSERT_EQUAL_VEC(Vec3(-2.99625888e+02, 8.14649386e+02, 2.72145234e+01), forces1[474], tol);
ASSERT_EQUAL_VEC(Vec3(-2.26042622e+02, -3.49838280e+02, -2.73671757e+02), forces1[475], tol);
ASSERT_EQUAL_VEC(Vec3(-3.38859328e+02, 8.74208207e+02, 2.98575880e+02), forces1[476], tol);
ASSERT_EQUAL_VEC(Vec3( 5.63054826e+02, 1.51181551e+02, -1.10341988e+02), forces1[477], tol);
ASSERT_EQUAL_VEC(Vec3( 3.44122544e+02, 2.73292900e+02, 5.44577027e+02), forces1[478], tol);
ASSERT_EQUAL_VEC(Vec3( 3.29121922e+01, 3.66764263e+02, 3.30304492e+02), forces1[479], tol);
ASSERT_EQUAL_VEC(Vec3(-4.98111364e+01, -3.26335514e+02, -6.47508532e+02), forces1[480], tol);
ASSERT_EQUAL_VEC(Vec3( 6.05970391e+01, -1.41039500e+02, 4.05545963e+02), forces1[481], tol);
ASSERT_EQUAL_VEC(Vec3(-1.35191146e+03, 5.06763950e+01, -1.40076012e+02), forces1[482], tol);
ASSERT_EQUAL_VEC(Vec3( 7.70144671e+01, -2.68985491e+02, -1.75067707e+02), forces1[483], tol);
ASSERT_EQUAL_VEC(Vec3(-1.89547256e+02, -2.32975097e+02, -2.54877338e+02), forces1[484], tol);
ASSERT_EQUAL_VEC(Vec3( 2.37141279e+02, 6.49194662e+02, 1.01757997e+02), forces1[485], tol);
ASSERT_EQUAL_VEC(Vec3(-3.38583468e+02, 1.34692366e+02, 7.89810999e+02), forces1[486], tol);
ASSERT_EQUAL_VEC(Vec3(-5.59974436e+02, -1.46817839e+02, 3.62598852e+01), forces1[487], tol);
ASSERT_EQUAL_VEC(Vec3(-3.52715734e+02, 1.16573021e+02, -4.55490471e+02), forces1[488], tol);
ASSERT_EQUAL_VEC(Vec3( 6.56757625e+01, 1.02550774e+02, -9.81490452e+01), forces1[489], tol);
ASSERT_EQUAL_VEC(Vec3(-4.22997875e+02, 8.89168040e+02, -9.19714230e+01), forces1[490], tol);
ASSERT_EQUAL_VEC(Vec3( 5.13351576e+02, 4.56111157e+02, -6.86014949e+01), forces1[491], tol);
ASSERT_EQUAL_VEC(Vec3( 1.12137269e+02, 6.84704993e+02, 4.44636019e+01), forces1[492], tol);
ASSERT_EQUAL_VEC(Vec3( 2.15980740e+02, 3.33093803e+02, -4.41281918e+02), forces1[493], tol);
ASSERT_EQUAL_VEC(Vec3(-4.77801486e-01, -2.16935238e+02, -5.39082462e+02), forces1[494], tol);
ASSERT_EQUAL_VEC(Vec3(-3.91584043e+02, 2.80049288e+02, 4.50318805e+02), forces1[495], tol);
ASSERT_EQUAL_VEC(Vec3( 2.40458755e+02, -1.24624268e+02, -2.70106867e+02), forces1[496], tol);
ASSERT_EQUAL_VEC(Vec3( 7.23763468e+01, 1.59346249e+01, -5.23998896e+01), forces1[497], tol);
ASSERT_EQUAL_VEC(Vec3(-1.41166795e+02, -3.23959396e+02, -4.05099054e+02), forces1[498], tol);
ASSERT_EQUAL_VEC(Vec3( 3.93723518e+02, 3.77829620e+02, -4.56301133e+02), forces1[499], tol);
ASSERT_EQUAL_VEC(Vec3(-2.68009341e+02, -2.12736933e+02, -7.96445111e+02), forces1[500], tol);
ASSERT_EQUAL_VEC(Vec3( 9.59480600e+02, 1.74115813e+02, 1.16126031e+01), forces1[501], tol);
ASSERT_EQUAL_VEC(Vec3(-3.93575465e+01, 1.29079012e+01, -1.89350441e+02), forces1[502], tol);
ASSERT_EQUAL_VEC(Vec3( 2.49464458e+02, 7.84144311e+01, -4.78252942e+02), forces1[503], tol);
ASSERT_EQUAL_VEC(Vec3( 5.16031453e+02, 6.52899912e+02, -5.05259519e+02), forces1[504], tol);
ASSERT_EQUAL_VEC(Vec3( 5.10845388e+01, -5.19606468e+02, 6.82761515e+00), forces1[505], tol);
ASSERT_EQUAL_VEC(Vec3(-7.68910836e+01, -1.13215494e+03, -7.05213833e+02), forces1[506], tol);
ASSERT_EQUAL_VEC(Vec3(-2.13058157e+02, 1.70549578e+00, 8.26365735e+02), forces1[507], tol);
ASSERT_EQUAL_VEC(Vec3( 1.86647493e+01, 1.91383875e+02, 4.18426660e+02), forces1[508], tol);
ASSERT_EQUAL_VEC(Vec3( 2.35361629e+02, 3.79668975e+02, -3.13824585e+02), forces1[509], tol);
ASSERT_EQUAL_VEC(Vec3(-1.26205360e+02, -2.59705171e+02, -6.76333230e+02), forces1[510], tol);
ASSERT_EQUAL_VEC(Vec3(-1.38240856e+02, -3.38607085e+02, 2.20460843e+02), forces1[511], tol);
ASSERT_EQUAL_VEC(Vec3(-2.25429800e+02, -7.80817051e+02, -2.43643666e+02), forces1[512], tol);
ASSERT_EQUAL_VEC(Vec3(-3.10585529e+01, 4.16080403e+01, 6.15476974e+01), forces1[513], tol);
ASSERT_EQUAL_VEC(Vec3( 6.75639613e+01, 9.67778416e+01, 6.03424295e+02), forces1[514], tol);
ASSERT_EQUAL_VEC(Vec3(-1.27080812e+02, -4.74945218e+01, 4.67735437e+02), forces1[515], tol);
ASSERT_EQUAL_VEC(Vec3(-2.72716149e+02, 3.30769806e+01, -2.81976538e+02), forces1[516], tol);
ASSERT_EQUAL_VEC(Vec3( 4.78709091e+02, 8.27541480e+01, -3.32366235e+02), forces1[517], tol);
ASSERT_EQUAL_VEC(Vec3( 1.48574421e+02, 1.88635391e+02, -5.92117141e+02), forces1[518], tol);
ASSERT_EQUAL_VEC(Vec3(-9.24795792e+01, 6.86826191e+01, 5.16804407e+02), forces1[519], tol);
ASSERT_EQUAL_VEC(Vec3( 4.32902806e+02, -4.67485932e+02, 1.09369501e+02), forces1[520], tol);
ASSERT_EQUAL_VEC(Vec3(-2.19075642e+02, 2.09097287e+00, 1.05235595e+03), forces1[521], tol);
ASSERT_EQUAL_VEC(Vec3(-3.95717922e+02, -3.31346394e+02, 6.33930848e+02), forces1[522], tol);
ASSERT_EQUAL_VEC(Vec3( 2.29152652e+01, 5.34146722e+02, 3.47887307e+01), forces1[523], tol);
ASSERT_EQUAL_VEC(Vec3( 4.58874513e+02, -2.39333403e+02, 5.46784973e+01), forces1[524], tol);
ASSERT_EQUAL_VEC(Vec3( 9.97690238e+01, 6.32211339e+02, -2.38267224e+02), forces1[525], tol);
ASSERT_EQUAL_VEC(Vec3(-5.41815785e+02, 2.23592441e+02, 3.43988502e+02), forces1[526], tol);
ASSERT_EQUAL_VEC(Vec3( 6.66020295e+02, 4.01173887e+02, 3.95444004e+02), forces1[527], tol);
ASSERT_EQUAL_VEC(Vec3(-3.06415141e+02, 9.39706150e+02, -8.27903644e+01), forces1[528], tol);
ASSERT_EQUAL_VEC(Vec3(-6.74078464e+02, 3.08248161e+02, 3.73260958e+02), forces1[529], tol);
ASSERT_EQUAL_VEC(Vec3( 2.12314396e+02, -1.45381664e+02, 2.64300301e+02), forces1[530], tol);
ASSERT_EQUAL_VEC(Vec3( 5.45322328e+01, -8.21066892e+01, 1.51406100e+01), forces1[531], tol);
ASSERT_EQUAL_VEC(Vec3( 1.18910001e+03, 9.92330102e+02, 1.56915624e+02), forces1[532], tol);
ASSERT_EQUAL_VEC(Vec3( 4.27125646e+02, 1.57551086e+02, -3.44348874e+02), forces1[533], tol);
ASSERT_EQUAL_VEC(Vec3( 4.25889733e+02, -8.14519408e+02, -2.37525602e+02), forces1[534], tol);
ASSERT_EQUAL_VEC(Vec3( 1.28754744e+02, 2.55775344e+02, 4.07880224e+02), forces1[535], tol);
ASSERT_EQUAL_VEC(Vec3(-4.27463797e+02, 1.69782847e+02, 1.81381656e+02), forces1[536], tol);
ASSERT_EQUAL_VEC(Vec3(-1.35673145e+01, 6.03614577e+01, -2.72320347e+02), forces1[537], tol);
ASSERT_EQUAL_VEC(Vec3( 3.33191831e+02, 2.70237134e+02, -1.39415972e+02), forces1[538], tol);
ASSERT_EQUAL_VEC(Vec3( 4.27473593e+02, 1.39188845e+02, 1.50065516e+02), forces1[539], tol);
ASSERT_EQUAL_VEC(Vec3(-8.29214695e+01, -1.00138197e+02, -6.82077450e+01), forces1[540], tol);
ASSERT_EQUAL_VEC(Vec3( 4.42484380e+02, -8.63519014e+02, 5.13464509e+02), forces1[541], tol);
ASSERT_EQUAL_VEC(Vec3( 2.60684466e+02, -6.37356238e+01, 1.70982604e+02), forces1[542], tol);
ASSERT_EQUAL_VEC(Vec3(-3.90400226e+02, -6.79695956e+01, -2.36488456e+02), forces1[543], tol);
ASSERT_EQUAL_VEC(Vec3( 2.47126838e+02, -6.89575321e+02, 2.32358423e+02), forces1[544], tol);
ASSERT_EQUAL_VEC(Vec3(-5.36761583e+02, 2.88858194e+02, -6.13643573e+02), forces1[545], tol);
ASSERT_EQUAL_VEC(Vec3(-7.93525749e+02, 1.07662863e+02, 1.05022658e+03), forces1[546], tol);
ASSERT_EQUAL_VEC(Vec3( 5.82900512e+02, 1.51819356e+02, 4.23873744e+02), forces1[547], tol);
ASSERT_EQUAL_VEC(Vec3( 1.88325572e+02, 3.18360865e+02, 2.45830156e+02), forces1[548], tol);
ASSERT_EQUAL_VEC(Vec3( 2.61705414e+02, 2.14134022e+02, -1.84880999e+02), forces1[549], tol);
ASSERT_EQUAL_VEC(Vec3(-6.23192888e+01, -3.92071740e+02, 3.63685367e+02), forces1[550], tol);
ASSERT_EQUAL_VEC(Vec3( 2.19338376e+02, -3.09058211e+02, 2.60284559e+02), forces1[551], tol);
ASSERT_EQUAL_VEC(Vec3( 5.02128707e+02, -5.84024469e+02, -2.55784996e+02), forces1[552], tol);
ASSERT_EQUAL_VEC(Vec3( 3.03909158e+02, 8.14707135e+01, 3.89632253e+02), forces1[553], tol);
ASSERT_EQUAL_VEC(Vec3(-1.63583654e+01, -1.20664911e+02, -1.80828909e+02), forces1[554], tol);
ASSERT_EQUAL_VEC(Vec3( 5.41992468e+02, 1.88563930e+02, 1.13691301e+02), forces1[555], tol);
ASSERT_EQUAL_VEC(Vec3( 1.22553432e+01, -7.61799733e+02, -5.24485038e+02), forces1[556], tol);
ASSERT_EQUAL_VEC(Vec3(-4.66745376e+02, 1.60550422e+02, 1.60943530e+02), forces1[557], tol);
ASSERT_EQUAL_VEC(Vec3( 1.52464706e+02, -2.28608668e+02, -5.94662997e+01), forces1[558], tol);
ASSERT_EQUAL_VEC(Vec3( 1.80874781e+02, 2.02580348e+02, 2.57314201e+02), forces1[559], tol);
ASSERT_EQUAL_VEC(Vec3( 6.16836590e+02, -3.58505580e+02, -5.69821380e+02), forces1[560], tol);
ASSERT_EQUAL_VEC(Vec3(-1.57121513e+02, -1.13551528e+02, -1.64421656e+02), forces1[561], tol);
ASSERT_EQUAL_VEC(Vec3(-5.95579860e+02, 1.76709860e+02, -1.20420471e+02), forces1[562], tol);
ASSERT_EQUAL_VEC(Vec3(-6.71038012e+01, 7.56227414e+02, 5.71572718e+02), forces1[563], tol);
ASSERT_EQUAL_VEC(Vec3(-2.03114489e+02, -5.27279840e+02, -8.87711978e+01), forces1[564], tol);
ASSERT_EQUAL_VEC(Vec3(-1.55593136e+02, 2.45854044e+02, 3.01808246e+01), forces1[565], tol);
ASSERT_EQUAL_VEC(Vec3( 1.30686350e+02, -2.25620374e+02, -3.22214134e+02), forces1[566], tol);
ASSERT_EQUAL_VEC(Vec3( 8.49438622e+00, -1.67348790e+02, 1.64216652e+02), forces1[567], tol);
ASSERT_EQUAL_VEC(Vec3(-2.31492960e+02, -5.94613152e+00, -4.92415232e+02), forces1[568], tol);
ASSERT_EQUAL_VEC(Vec3(-1.99398672e+02, -1.39998676e+02, 2.20660038e+01), forces1[569], tol);
ASSERT_EQUAL_VEC(Vec3(-2.65259682e+02, 6.41897575e+02, 2.96772423e+01), forces1[570], tol);
ASSERT_EQUAL_VEC(Vec3(-6.43663438e+02, -9.18800569e+01, 4.59932589e+02), forces1[571], tol);
ASSERT_EQUAL_VEC(Vec3( 3.08969568e+02, 8.15568208e+02, 4.66102799e+02), forces1[572], tol);
ASSERT_EQUAL_VEC(Vec3( 3.27967531e+02, -3.65749496e+02, -7.14744561e+02), forces1[573], tol);
ASSERT_EQUAL_VEC(Vec3(-2.27124135e+01, 1.71611100e+02, 5.53831720e+02), forces1[574], tol);
ASSERT_EQUAL_VEC(Vec3(-3.66175366e+02, -4.26970546e+02, 4.19237233e+02), forces1[575], tol);
ASSERT_EQUAL_VEC(Vec3( 3.35350939e+02, 4.34257827e+02, 3.88519433e+02), forces1[576], tol);
ASSERT_EQUAL_VEC(Vec3( 1.92391997e+02, 3.19970198e+01, 2.85801367e+02), forces1[577], tol);
ASSERT_EQUAL_VEC(Vec3(-1.98802026e+02, -5.26322826e+02, 3.78354799e+02), forces1[578], tol);
ASSERT_EQUAL_VEC(Vec3( 1.45671139e+01, -4.73231557e+02, 1.99919499e+02), forces1[579], tol);
ASSERT_EQUAL_VEC(Vec3(-4.54983469e+02, -4.41701623e+02, -1.24425267e+01), forces1[580], tol);
ASSERT_EQUAL_VEC(Vec3(-4.14904603e+02, 2.39751623e+02, 1.50738334e+02), forces1[581], tol);
ASSERT_EQUAL_VEC(Vec3(-3.06008464e+02, -5.48479559e+02, 1.68208702e+02), forces1[582], tol);
ASSERT_EQUAL_VEC(Vec3(-4.75151928e+02, -5.13940177e+02, -3.05856025e+02), forces1[583], tol);
ASSERT_EQUAL_VEC(Vec3(-1.94335510e+02, 1.30203790e+02, -3.61867940e+02), forces1[584], tol);
ASSERT_EQUAL_VEC(Vec3(-3.90897312e+02, 9.91504462e+01, -5.15012540e+02), forces1[585], tol);
ASSERT_EQUAL_VEC(Vec3(-6.84365306e+01, 4.13335965e+01, -2.50193483e+02), forces1[586], tol);
ASSERT_EQUAL_VEC(Vec3(-1.33552662e+02, 4.37424563e+02, 3.93116748e+02), forces1[587], tol);
ASSERT_EQUAL_VEC(Vec3( 2.17908981e+02, -3.56944423e+02, -2.19072785e+01), forces1[588], tol);
ASSERT_EQUAL_VEC(Vec3(-6.71090167e+01, -2.85234245e+01, -4.77739167e+02), forces1[589], tol);
ASSERT_EQUAL_VEC(Vec3(-1.52916503e+02, 1.00727331e+02, 7.10681385e+01), forces1[590], tol);
ASSERT_EQUAL_VEC(Vec3(-4.51746452e+02, 3.40018663e+02, -4.62193306e+02), forces1[591], tol);
ASSERT_EQUAL_VEC(Vec3(-3.10163906e+02, 9.78195399e+02, 6.76387827e+02), forces1[592], tol);
ASSERT_EQUAL_VEC(Vec3(-3.27558084e+02, -1.79605758e+02, -2.92242524e+02), forces1[593], tol);
ASSERT_EQUAL_VEC(Vec3(-1.59840534e+02, 2.90890718e+01, -2.88814524e+02), forces1[594], tol);
ASSERT_EQUAL_VEC(Vec3( 2.51648228e+02, -6.16115272e+02, -2.74650686e+02), forces1[595], tol);
ASSERT_EQUAL_VEC(Vec3(-4.29795517e+02, 2.97846602e+02, 2.42790721e+02), forces1[596], tol);
ASSERT_EQUAL_VEC(Vec3(-6.54290494e+01, -3.71027774e+00, 1.09362533e+02), forces1[597], tol);
ASSERT_EQUAL_VEC(Vec3(-1.13635998e+02, 1.22421969e+02, -1.64181430e+02), forces1[598], tol);
ASSERT_EQUAL_VEC(Vec3(-4.58014050e+02, -5.24237205e+02, -4.23517393e+02), forces1[599], tol);
ASSERT_EQUAL_VEC(Vec3(-4.01134799e+02, -3.10910001e+02, -4.86430772e+02), forces1[600], tol);
ASSERT_EQUAL_VEC(Vec3( 5.98531056e+02, 2.14442508e+02, -3.57310754e+02), forces1[601], tol);
ASSERT_EQUAL_VEC(Vec3( 2.56644787e+02, 7.12047294e+01, -6.74281621e+02), forces1[602], tol);
ASSERT_EQUAL_VEC(Vec3(-4.29285173e+02, 3.33264141e+02, -9.12908079e+02), forces1[603], tol);
ASSERT_EQUAL_VEC(Vec3(-2.69676355e+01, -1.31676984e+02, -3.65274088e+02), forces1[604], tol);
ASSERT_EQUAL_VEC(Vec3(-9.51192754e+00, 3.29563459e+02, 1.84451527e+01), forces1[605], tol);
ASSERT_EQUAL_VEC(Vec3( 1.01475969e+03, 8.03401303e+01, -6.28558583e+02), forces1[606], tol);
ASSERT_EQUAL_VEC(Vec3( 2.39384773e+02, -4.08077252e+02, -1.62875513e+02), forces1[607], tol);
ASSERT_EQUAL_VEC(Vec3(-3.93619429e+02, -5.17089524e+02, 5.21862274e+02), forces1[608], tol);
ASSERT_EQUAL_VEC(Vec3(-1.37188342e+02, -3.86301530e+01, 4.26852072e+02), forces1[609], tol);
ASSERT_EQUAL_VEC(Vec3( 4.56729328e+02, 1.63797747e+02, 1.01225457e+01), forces1[610], tol);
ASSERT_EQUAL_VEC(Vec3(-1.53128373e+02, 1.02270086e+02, -7.27501148e+02), forces1[611], tol);
ASSERT_EQUAL_VEC(Vec3(-1.82095738e+02, 7.23918424e+00, -5.10663887e+02), forces1[612], tol);
ASSERT_EQUAL_VEC(Vec3( 3.54485642e+02, -8.64332815e+01, -4.10166872e+02), forces1[613], tol);
ASSERT_EQUAL_VEC(Vec3(-6.67557801e+01, -7.57917504e+01, 7.77239103e+01), forces1[614], tol);
ASSERT_EQUAL_VEC(Vec3( 5.34069903e+01, -3.49052174e+02, -4.41040697e+02), forces1[615], tol);
ASSERT_EQUAL_VEC(Vec3(-2.55993121e+02, -1.85106916e+02, -1.67700877e+01), forces1[616], tol);
ASSERT_EQUAL_VEC(Vec3(-2.60226735e+02, 4.50682021e+02, 6.87571461e+02), forces1[617], tol);
ASSERT_EQUAL_VEC(Vec3(-4.84797099e+01, -1.47670701e+02, 1.30460152e+02), forces1[618], tol);
ASSERT_EQUAL_VEC(Vec3(-5.03867743e+02, 3.00373068e+02, 2.90989581e+01), forces1[619], tol);
ASSERT_EQUAL_VEC(Vec3(-3.26901569e+02, -2.54115591e+02, -1.97292733e+02), forces1[620], tol);
ASSERT_EQUAL_VEC(Vec3( 2.23124353e+02, 4.31821924e+02, -3.45785529e+02), forces1[621], tol);
ASSERT_EQUAL_VEC(Vec3(-2.69721533e+00, -7.70690632e+01, -4.19805145e-01), forces1[622], tol);
ASSERT_EQUAL_VEC(Vec3( 8.20412852e+02, 7.99595268e+02, 4.14368303e+02), forces1[623], tol);
ASSERT_EQUAL_VEC(Vec3(-6.20301649e+01, -4.27145295e+02, -3.70568508e+02), forces1[624], tol);
ASSERT_EQUAL_VEC(Vec3(-2.49452268e+01, -1.44630413e+02, 7.41551042e+02), forces1[625], tol);
ASSERT_EQUAL_VEC(Vec3(-6.41685916e+02, 6.29980324e+01, 1.57997582e+02), forces1[626], tol);
ASSERT_EQUAL_VEC(Vec3(-5.89339824e+02, -2.66715415e+02, 7.40290187e+01), forces1[627], tol);
ASSERT_EQUAL_VEC(Vec3( 5.26893408e+02, -4.19407888e+02, -2.37125726e+02), forces1[628], tol);
ASSERT_EQUAL_VEC(Vec3( 4.42841034e+02, -3.91060128e+00, 5.35417838e+02), forces1[629], tol);
ASSERT_EQUAL_VEC(Vec3( 2.38357324e+02, -5.34613334e+02, 2.63026026e+02), forces1[630], tol);
ASSERT_EQUAL_VEC(Vec3(-2.48018560e+02, -1.44967289e+01, 7.55539331e+02), forces1[631], tol);
ASSERT_EQUAL_VEC(Vec3(-8.06994987e+01, -4.42696335e+02, -4.11279013e+02), forces1[632], tol);
ASSERT_EQUAL_VEC(Vec3( 4.64135556e+02, -7.82576620e+02, 7.48567804e+02), forces1[633], tol);
ASSERT_EQUAL_VEC(Vec3( 3.75022611e+02, 3.06926438e+02, 2.76985876e+02), forces1[634], tol);
ASSERT_EQUAL_VEC(Vec3( 4.43672142e+02, -1.70168440e+02, -2.61360725e+02), forces1[635], tol);
ASSERT_EQUAL_VEC(Vec3( 5.20775219e+02, -7.66015101e+00, -2.76123645e+02), forces1[636], tol);
ASSERT_EQUAL_VEC(Vec3(-2.28464032e+02, 5.33849652e+02, -3.58040265e+01), forces1[637], tol);
ASSERT_EQUAL_VEC(Vec3(-1.70105892e+02, -7.30464804e+01, -3.73115603e+02), forces1[638], tol);
ASSERT_EQUAL_VEC(Vec3( 1.90860772e+02, 1.53722926e+02, -9.75608771e+00), forces1[639], tol);
ASSERT_EQUAL_VEC(Vec3( 2.59606432e+02, 2.75260084e+01, -2.02338885e+02), forces1[640], tol);
ASSERT_EQUAL_VEC(Vec3( 3.25211743e+02, 2.78186538e+01, 5.21700978e+02), forces1[641], tol);
ASSERT_EQUAL_VEC(Vec3( 1.12827984e+02, -6.70115666e+02, -7.85972574e+01), forces1[642], tol);
ASSERT_EQUAL_VEC(Vec3(-1.09776266e+01, -4.90753809e+01, -2.91319708e+02), forces1[643], tol);
ASSERT_EQUAL_VEC(Vec3( 2.77337771e+02, 2.29328824e+02, 5.59770839e+02), forces1[644], tol);
ASSERT_EQUAL_VEC(Vec3( 4.59572121e+02, 1.21311799e+02, -4.79315463e+02), forces1[645], tol);
ASSERT_EQUAL_VEC(Vec3(-5.30617073e+02, -3.96120276e+02, -4.37634971e+01), forces1[646], tol);
ASSERT_EQUAL_VEC(Vec3( 5.87394968e+01, 3.74984490e+02, -4.60404623e+00), forces1[647], tol);
ASSERT_EQUAL_VEC(Vec3(-5.72051098e+02, -3.24819120e+02, -5.11080203e+01), forces1[648], tol);
ASSERT_EQUAL_VEC(Vec3(-1.51869992e+03, 1.63914081e+02, -7.68191132e+01), forces1[649], tol);
ASSERT_EQUAL_VEC(Vec3( 4.86996174e+02, 5.16874032e+02, -7.62783455e+01), forces1[650], tol);
ASSERT_EQUAL_VEC(Vec3( 2.48291844e+02, -8.90767951e+02, 6.71591151e+01), forces1[651], tol);
ASSERT_EQUAL_VEC(Vec3(-1.55902671e+00, 1.36945322e+02, -1.04198745e+02), forces1[652], tol);
ASSERT_EQUAL_VEC(Vec3(-1.20204512e+02, 2.89617545e+02, -6.63132635e+02), forces1[653], tol);
ASSERT_EQUAL_VEC(Vec3(-1.59431738e+02, -4.16178632e+02, 2.79410323e+02), forces1[654], tol);
ASSERT_EQUAL_VEC(Vec3(-2.98742818e+02, -2.96272331e+02, 3.36156942e+02), forces1[655], tol);
ASSERT_EQUAL_VEC(Vec3(-4.91918911e+02, -4.26618142e+01, 1.25349379e+01), forces1[656], tol);
ASSERT_EQUAL_VEC(Vec3(-2.47949202e+02, -2.67488422e+02, 2.21868927e+02), forces1[657], tol);
ASSERT_EQUAL_VEC(Vec3(-2.42806912e+01, 2.61371857e+01, -6.65056079e+01), forces1[658], tol);
ASSERT_EQUAL_VEC(Vec3( 3.64167110e+02, 4.13052005e+02, -5.51357152e+02), forces1[659], tol);
ASSERT_EQUAL_VEC(Vec3( 3.74835025e+02, -6.33613808e+02, 5.37740565e+00), forces1[660], tol);
ASSERT_EQUAL_VEC(Vec3( 6.51925635e+01, 5.97066743e+02, 2.54479164e+01), forces1[661], tol);
ASSERT_EQUAL_VEC(Vec3(-2.53444698e+02, -7.31192246e-02, -3.64183108e+02), forces1[662], tol);
ASSERT_EQUAL_VEC(Vec3(-4.19912375e+01, 5.32489389e+02, 5.67529873e+02), forces1[663], tol);
ASSERT_EQUAL_VEC(Vec3( 2.18364904e+01, -2.88671495e+02, 2.73914838e+02), forces1[664], tol);
ASSERT_EQUAL_VEC(Vec3( 2.56684383e+02, 1.75277073e+02, 2.67819228e+02), forces1[665], tol);
ASSERT_EQUAL_VEC(Vec3(-1.42021939e+02, -1.93549789e+02, -4.45927086e+02), forces1[666], tol);
ASSERT_EQUAL_VEC(Vec3(-5.74451363e+01, -4.30855207e+02, -7.59667162e+02), forces1[667], tol);
ASSERT_EQUAL_VEC(Vec3(-4.90348302e+02, 2.12533193e+02, 1.21388630e+02), forces1[668], tol);
ASSERT_EQUAL_VEC(Vec3( 1.94617803e+02, 1.32085507e+02, 1.71425945e+02), forces1[669], tol);
ASSERT_EQUAL_VEC(Vec3(-2.73616831e+02, 7.78021634e+02, 2.78045494e+02), forces1[670], tol);
ASSERT_EQUAL_VEC(Vec3(-2.62086498e+02, 3.53986939e+02, 1.59714894e+02), forces1[671], tol);
ASSERT_EQUAL_VEC(Vec3( 3.73999411e+01, -6.21073575e+02, -1.89719863e+02), forces1[672], tol);
ASSERT_EQUAL_VEC(Vec3( 2.70260860e+02, -1.98707881e+02, -1.68446389e+02), forces1[673], tol);
ASSERT_EQUAL_VEC(Vec3(-3.72177189e+02, 2.16561917e+02, -7.51668953e+01), forces1[674], tol);
ASSERT_EQUAL_VEC(Vec3( 4.60661709e+02, -2.41016609e+02, 1.87547311e+02), forces1[675], tol);
ASSERT_EQUAL_VEC(Vec3(-8.64826359e+00, -4.96643240e+01, 4.14108827e+02), forces1[676], tol);
ASSERT_EQUAL_VEC(Vec3(-5.53564746e+02, 2.02028062e+02, 2.48782202e+02), forces1[677], tol);
ASSERT_EQUAL_VEC(Vec3( 4.12907277e+02, 3.97611070e+02, -7.77110932e+02), forces1[678], tol);
ASSERT_EQUAL_VEC(Vec3( 4.37842786e+02, 3.87794283e+02, -2.57117992e+02), forces1[679], tol);
ASSERT_EQUAL_VEC(Vec3(-1.99542679e+02, -5.31843337e+02, -7.08353449e+02), forces1[680], tol);
ASSERT_EQUAL_VEC(Vec3( 7.16866224e+01, 2.15343692e+02, -2.17970964e+02), forces1[681], tol);
ASSERT_EQUAL_VEC(Vec3(-2.27242549e+02, -3.33098733e+02, -8.95890783e+01), forces1[682], tol);
ASSERT_EQUAL_VEC(Vec3( 1.12351413e+02, -2.81401181e+02, 1.84388002e+02), forces1[683], tol);
ASSERT_EQUAL_VEC(Vec3( 5.30452008e+01, -1.73617150e+02, 3.80454625e+02), forces1[684], tol);
ASSERT_EQUAL_VEC(Vec3(-1.53115972e+02, -4.19542700e+02, 3.62046980e+02), forces1[685], tol);
ASSERT_EQUAL_VEC(Vec3(-8.25931831e+01, 1.22022997e+02, -2.05397162e+01), forces1[686], tol);
ASSERT_EQUAL_VEC(Vec3(-4.09536810e+02, 3.88110352e+01, 7.34591479e+02), forces1[687], tol);
ASSERT_EQUAL_VEC(Vec3( 2.30482332e+02, 4.58992821e+01, -3.44448449e+02), forces1[688], tol);
ASSERT_EQUAL_VEC(Vec3(-2.99021647e+02, 6.71173507e+02, -1.01234738e+03), forces1[689], tol);
ASSERT_EQUAL_VEC(Vec3( 2.93609447e+02, -1.62689588e+01, -5.52892039e+02), forces1[690], tol);
ASSERT_EQUAL_VEC(Vec3(-1.41608406e+02, -4.87778496e+02, 2.73057317e+02), forces1[691], tol);
ASSERT_EQUAL_VEC(Vec3(-1.26623333e+02, -4.36398742e+02, 7.19789728e+01), forces1[692], tol);
ASSERT_EQUAL_VEC(Vec3( 1.33484619e+02, 2.25668941e+02, 5.65772889e+02), forces1[693], tol);
ASSERT_EQUAL_VEC(Vec3( 7.34328437e+02, 4.69241195e+02, -5.53029876e+02), forces1[694], tol);
ASSERT_EQUAL_VEC(Vec3(-6.36703964e+01, -2.05105546e+02, -4.73051447e+02), forces1[695], tol);
ASSERT_EQUAL_VEC(Vec3(-3.35917709e+02, -2.75962396e+02, 4.10424916e+02), forces1[696], tol);
ASSERT_EQUAL_VEC(Vec3(-1.98439482e+02, -2.81389872e+02, -1.48450243e+02), forces1[697], tol);
ASSERT_EQUAL_VEC(Vec3( 6.96988850e+02, -2.08391821e+02, -1.22085562e+02), forces1[698], tol);
ASSERT_EQUAL_VEC(Vec3( 6.16645748e+02, -2.04946166e+02, -2.28454418e+02), forces1[699], tol);
ASSERT_EQUAL_VEC(Vec3( 1.59003207e+02, 2.68316786e+02, -1.13415654e+02), forces1[700], tol);
ASSERT_EQUAL_VEC(Vec3( 2.52361394e+02, 3.78641684e+02, -1.30511196e+03), forces1[701], tol);
ASSERT_EQUAL_VEC(Vec3( 7.30824903e+02, -3.70993861e+02, -6.60674538e+02), forces1[702], tol);
ASSERT_EQUAL_VEC(Vec3(-4.64466620e+01, 1.28056043e+02, 8.75616746e+01), forces1[703], tol);
ASSERT_EQUAL_VEC(Vec3( 3.26209378e+02, -1.75823502e+02, 7.02935072e+01), forces1[704], tol);
ASSERT_EQUAL_VEC(Vec3( 9.80733438e+01, -2.26694393e+02, -2.00005470e+02), forces1[705], tol);
ASSERT_EQUAL_VEC(Vec3( 5.01964740e+01, 2.67230250e+02, 5.24657350e+02), forces1[706], tol);
ASSERT_EQUAL_VEC(Vec3(-2.52920558e+02, -8.04959983e+02, 4.03061082e+02), forces1[707], tol);
ASSERT_EQUAL_VEC(Vec3(-2.40233840e+02, -2.54317274e+02, 5.06457155e+02), forces1[708], tol);
ASSERT_EQUAL_VEC(Vec3( 4.48943662e+02, 3.49500783e+02, 2.88397394e+02), forces1[709], tol);
ASSERT_EQUAL_VEC(Vec3( 3.10154028e+02, 3.91036441e+02, -9.77447313e+02), forces1[710], tol);
ASSERT_EQUAL_VEC(Vec3( 7.63180049e+02, 2.06937000e+02, 1.03491184e+02), forces1[711], tol);
ASSERT_EQUAL_VEC(Vec3( 4.64445959e+02, 2.52682144e+02, 7.37736774e+01), forces1[712], tol);
ASSERT_EQUAL_VEC(Vec3( 9.14937811e+02, 3.40042036e+02, -2.99018712e+02), forces1[713], tol);
ASSERT_EQUAL_VEC(Vec3( 5.05299633e+02, 1.59569259e+02, -9.12327108e+00), forces1[714], tol);
ASSERT_EQUAL_VEC(Vec3(-3.46682702e+02, 1.40007400e+02, 1.54710821e+02), forces1[715], tol);
ASSERT_EQUAL_VEC(Vec3(-1.66209573e+01, -4.02206977e+01, -1.23824382e+02), forces1[716], tol);
ASSERT_EQUAL_VEC(Vec3(-7.57122358e+01, -4.48068180e+02, -4.21106487e+02), forces1[717], tol);
ASSERT_EQUAL_VEC(Vec3( 1.04054861e+03, -2.36054466e+02, 1.43176151e+01), forces1[718], tol);
ASSERT_EQUAL_VEC(Vec3(-2.65447945e+01, 6.06566369e+02, 4.91348853e+01), forces1[719], tol);
ASSERT_EQUAL_VEC(Vec3(-1.33799337e+02, -3.09748236e+02, 2.79834422e+01), forces1[720], tol);
ASSERT_EQUAL_VEC(Vec3(-9.76340308e+01, -1.13791543e+03, 4.60481356e+02), forces1[721], tol);
ASSERT_EQUAL_VEC(Vec3(-2.56814290e+02, 9.31514446e+01, 3.24980766e+02), forces1[722], tol);
ASSERT_EQUAL_VEC(Vec3(-8.66006627e+01, 5.60562069e+02, 2.96440803e+02), forces1[723], tol);
ASSERT_EQUAL_VEC(Vec3( 1.26231775e+02, 1.40946887e+02, 3.97459193e+02), forces1[724], tol);
ASSERT_EQUAL_VEC(Vec3( 1.03134630e+02, -1.43345076e+02, 1.91574929e+02), forces1[725], tol);
ASSERT_EQUAL_VEC(Vec3(-9.29586783e+01, 4.44777503e+01, -9.69828338e+01), forces1[726], tol);
ASSERT_EQUAL_VEC(Vec3(-1.07833245e+02, 4.40219598e+01, 2.20888632e+02), forces1[727], tol);
ASSERT_EQUAL_VEC(Vec3( 2.92915083e+02, 5.54179388e+02, 3.57016084e+02), forces1[728], tol);
ASSERT_EQUAL_VEC(Vec3(-6.11696651e+02, 1.34361189e+02, 4.74917740e+01), forces1[729], tol);
ASSERT_EQUAL_VEC(Vec3(-8.07629704e+02, 1.63946818e+01, -2.56529488e+02), forces1[730], tol);
ASSERT_EQUAL_VEC(Vec3( 5.08866790e+01, 1.35087356e+01, 4.82186568e+02), forces1[731], tol);
ASSERT_EQUAL_VEC(Vec3(-5.01056820e+02, -7.35300196e+02, 6.67917664e+02), forces1[732], tol);
ASSERT_EQUAL_VEC(Vec3(-1.75353826e+00, 6.70720055e+02, -1.03873923e+03), forces1[733], tol);
ASSERT_EQUAL_VEC(Vec3(-1.95461285e+02, 4.63755838e+02, 7.41243174e+01), forces1[734], tol);
ASSERT_EQUAL_VEC(Vec3(-2.73265807e+02, -4.33952751e+02, -5.86859952e+02), forces1[735], tol);
ASSERT_EQUAL_VEC(Vec3( 3.87492595e+02, -3.57416581e+02, -1.22674219e+02), forces1[736], tol);
ASSERT_EQUAL_VEC(Vec3( 2.10277679e+02, -8.24319147e+01, -3.92467268e+02), forces1[737], tol);
ASSERT_EQUAL_VEC(Vec3( 8.30183105e+02, -6.58248549e+02, -4.70873747e+02), forces1[738], tol);
ASSERT_EQUAL_VEC(Vec3( 1.32224273e+02, 1.07242154e+02, -2.42599632e+02), forces1[739], tol);
ASSERT_EQUAL_VEC(Vec3(-4.36748065e+02, 2.00463318e+01, -3.45993944e+02), forces1[740], tol);
ASSERT_EQUAL_VEC(Vec3(-5.77263697e+02, 1.27439720e+02, -4.58810491e+02), forces1[741], tol);
ASSERT_EQUAL_VEC(Vec3( 1.97481946e+02, -2.58388548e+02, 1.34658918e+02), forces1[742], tol);
ASSERT_EQUAL_VEC(Vec3( 3.87665419e+01, -8.11439393e+02, -2.37617083e+02), forces1[743], tol);
ASSERT_EQUAL_VEC(Vec3(-5.43756717e+02, 7.08678099e+01, -1.18510886e+02), forces1[744], tol);
ASSERT_EQUAL_VEC(Vec3(-4.18101023e+02, 9.12399127e+01, -3.26402702e+02), forces1[745], tol);
ASSERT_EQUAL_VEC(Vec3(-1.04715128e+02, 4.73092172e+02, -5.52394413e+02), forces1[746], tol);
ASSERT_EQUAL_VEC(Vec3( 1.50377202e+02, 6.74569790e+02, -8.65206460e+01), forces1[747], tol);
ASSERT_EQUAL_VEC(Vec3(-4.91854971e+01, 9.70238350e+01, 4.14795171e+01), forces1[748], tol);
ASSERT_EQUAL_VEC(Vec3(-1.53136450e+02, -3.14704270e+02, 1.64509568e+02), forces1[749], tol);
ASSERT_EQUAL_VEC(Vec3( 5.38381589e-01, -2.20857586e+02, 2.62858346e+02), forces1[750], tol);
ASSERT_EQUAL_VEC(Vec3(-6.92118459e+02, 2.00416528e+02, -2.65829388e+02), forces1[751], tol);
ASSERT_EQUAL_VEC(Vec3( 3.79128765e+01, -1.05184233e+02, -3.82602540e+01), forces1[752], tol);
ASSERT_EQUAL_VEC(Vec3(-2.53012338e+02, 2.11976275e+02, -3.81671037e+02), forces1[753], tol);
ASSERT_EQUAL_VEC(Vec3(-2.51271776e+02, -3.93332088e+02, -1.33288963e+01), forces1[754], tol);
ASSERT_EQUAL_VEC(Vec3(-3.01457396e+01, 1.45589903e+02, -6.30529431e+01), forces1[755], tol);
ASSERT_EQUAL_VEC(Vec3(-2.76296961e+02, -4.61689259e+02, -2.69337581e+02), forces1[756], tol);
ASSERT_EQUAL_VEC(Vec3( 6.48638526e+01, 3.68743791e+02, 2.32261042e+02), forces1[757], tol);
ASSERT_EQUAL_VEC(Vec3(-4.48058548e+02, 4.24011162e+02, 2.00613297e+02), forces1[758], tol);
ASSERT_EQUAL_VEC(Vec3( 1.50464122e+01, -7.33758427e+01, 4.00841100e+02), forces1[759], tol);
ASSERT_EQUAL_VEC(Vec3( 4.06782738e+02, 1.22303609e+02, -3.26339153e+02), forces1[760], tol);
ASSERT_EQUAL_VEC(Vec3(-3.55711012e+02, -4.23650736e+02, 1.92960061e+02), forces1[761], tol);
ASSERT_EQUAL_VEC(Vec3(-4.78915336e+01, 1.13386663e+02, -4.55722593e+02), forces1[762], tol);
ASSERT_EQUAL_VEC(Vec3( 5.50242083e+02, 3.20971060e+02, -2.04129569e+02), forces1[763], tol);
ASSERT_EQUAL_VEC(Vec3(-3.98415518e+01, 6.43149241e+01, 2.62918629e+02), forces1[764], tol);
ASSERT_EQUAL_VEC(Vec3(-7.85279974e+01, -7.50534137e+01, -7.62991530e+02), forces1[765], tol);
ASSERT_EQUAL_VEC(Vec3( 9.55632719e+00, -3.95488033e+02, -8.95724718e+01), forces1[766], tol);
ASSERT_EQUAL_VEC(Vec3( 4.83248484e+02, -7.40432533e+02, -5.32388436e+02), forces1[767], tol);
ASSERT_EQUAL_VEC(Vec3(-1.61825590e+02, 8.15278549e+01, 4.71854738e+02), forces1[768], tol);
ASSERT_EQUAL_VEC(Vec3(-3.30808156e+02, -4.21996977e+01, -1.05047553e+03), forces1[769], tol);
ASSERT_EQUAL_VEC(Vec3( 3.34236506e+02, -1.34581292e+02, 3.33945615e+02), forces1[770], tol);
ASSERT_EQUAL_VEC(Vec3( 2.25548584e+01, 6.94629483e+02, 3.48264737e+01), forces1[771], tol);
ASSERT_EQUAL_VEC(Vec3( 4.92311713e+02, -3.56653650e+02, 7.11601228e+01), forces1[772], tol);
ASSERT_EQUAL_VEC(Vec3( 2.17161427e+02, 4.05017188e+01, 3.47168571e+02), forces1[773], tol);
ASSERT_EQUAL_VEC(Vec3(-1.61160760e+01, 1.47265327e+03, 5.49839208e+02), forces1[774], tol);
ASSERT_EQUAL_VEC(Vec3( 8.39508346e+01, -4.18462111e+02, 2.34856125e+02), forces1[775], tol);
ASSERT_EQUAL_VEC(Vec3(-2.31363191e+02, 2.37282362e+02, 6.61594930e+01), forces1[776], tol);
ASSERT_EQUAL_VEC(Vec3(-6.23920550e+01, -7.00163723e+02, 4.91292544e+02), forces1[777], tol);
ASSERT_EQUAL_VEC(Vec3( 2.68222410e+01, 2.46989964e+02, 6.49012925e-01), forces1[778], tol);
ASSERT_EQUAL_VEC(Vec3( 5.26234504e+02, 1.10220781e+02, 3.46422754e+02), forces1[779], tol);
ASSERT_EQUAL_VEC(Vec3(-3.29262429e+02, -3.87187275e+02, 8.51736504e+01), forces1[780], tol);
ASSERT_EQUAL_VEC(Vec3( 6.42859552e+02, 2.34857815e+02, 3.71368125e+02), forces1[781], tol);
ASSERT_EQUAL_VEC(Vec3(-3.24691537e+02, 5.64035803e+02, -4.45828642e+01), forces1[782], tol);
ASSERT_EQUAL_VEC(Vec3( 5.74664181e+01, 1.20257138e+02, -5.84616728e+01), forces1[783], tol);
ASSERT_EQUAL_VEC(Vec3(-2.83822896e+02, -2.10782913e+02, 1.66012284e+02), forces1[784], tol);
ASSERT_EQUAL_VEC(Vec3( 4.01591578e+02, -2.85111660e+02, 2.49467396e+02), forces1[785], tol);
ASSERT_EQUAL_VEC(Vec3( 3.05502334e+02, 7.04806702e+02, -5.94571548e+01), forces1[786], tol);
ASSERT_EQUAL_VEC(Vec3(-5.06843754e+02, 3.13614505e+02, -7.30043017e+02), forces1[787], tol);
ASSERT_EQUAL_VEC(Vec3(-1.65622368e+02, -1.02579327e+02, -4.10521389e+02), forces1[788], tol);
ASSERT_EQUAL_VEC(Vec3( 2.50016463e+02, 9.82377276e+02, 3.08289476e+02), forces1[789], tol);
ASSERT_EQUAL_VEC(Vec3( 3.60198825e+02, 3.06200915e+02, 4.01455745e+02), forces1[790], tol);
ASSERT_EQUAL_VEC(Vec3(-5.15582709e+02, -3.80887100e+02, -2.65984423e+02), forces1[791], tol);
ASSERT_EQUAL_VEC(Vec3( 9.16914445e+02, -2.70992304e+02, 2.88892766e+02), forces1[792], tol);
ASSERT_EQUAL_VEC(Vec3( 5.58063411e+01, 1.28556880e+02, 4.36834411e+02), forces1[793], tol);
ASSERT_EQUAL_VEC(Vec3( 3.48855713e+02, 3.63953389e+02, 3.78554885e+02), forces1[794], tol);
ASSERT_EQUAL_VEC(Vec3( 6.36625415e+02, -1.91568531e+02, 4.14269326e+02), forces1[795], tol);
ASSERT_EQUAL_VEC(Vec3(-3.31399256e+01, -1.47717726e+02, 3.03791102e+02), forces1[796], tol);
ASSERT_EQUAL_VEC(Vec3(-8.19625890e+01, -5.66609696e+02, -7.77438618e+01), forces1[797], tol);
ASSERT_EQUAL_VEC(Vec3( 1.20348179e+02, 3.66643084e+02, -2.38052731e+02), forces1[798], tol);
ASSERT_EQUAL_VEC(Vec3( 3.60705602e+02, 4.45404187e+02, -6.03564902e+02), forces1[799], tol);
ASSERT_EQUAL_VEC(Vec3(-8.71394660e+01, 1.63937553e+02, -1.43929941e+02), forces1[800], tol);
ASSERT_EQUAL_VEC(Vec3(-9.99012969e+01, 1.96050644e+01, -5.85512840e+01), forces1[801], tol);
ASSERT_EQUAL_VEC(Vec3( 4.81464959e+02, -7.04133234e+01, -2.08003335e+02), forces1[802], tol);
ASSERT_EQUAL_VEC(Vec3(-7.69383659e+02, 5.40518266e+01, 5.38423746e+02), forces1[803], tol);
ASSERT_EQUAL_VEC(Vec3(-6.60834570e+02, -1.87942951e+02, 4.11979846e+02), forces1[804], tol);
ASSERT_EQUAL_VEC(Vec3( 1.06277928e+02, 3.55317856e+02, -2.27807465e+02), forces1[805], tol);
ASSERT_EQUAL_VEC(Vec3(-2.66112993e+02, 2.41378629e+02, -4.99239330e+02), forces1[806], tol);
ASSERT_EQUAL_VEC(Vec3(-5.23091617e+01, 9.33588668e+01, 7.23830549e+02), forces1[807], tol);
ASSERT_EQUAL_VEC(Vec3( 2.82159908e+00, 6.04185043e+01, 1.65510623e+02), forces1[808], tol);
ASSERT_EQUAL_VEC(Vec3(-2.97249356e+02, -5.53850820e+01, 3.87132557e+02), forces1[809], tol);
ASSERT_EQUAL_VEC(Vec3(-8.18654835e+02, -2.66089588e+02, 1.07282779e+03), forces1[810], tol);
ASSERT_EQUAL_VEC(Vec3(-2.43727371e+02, -2.51641252e+02, 3.46339026e+02), forces1[811], tol);
ASSERT_EQUAL_VEC(Vec3( 4.04775249e+02, 1.12602806e+02, -6.66755260e+02), forces1[812], tol);
ASSERT_EQUAL_VEC(Vec3(-8.30889004e+01, -9.75627382e+01, -5.15294649e+02), forces1[813], tol);
ASSERT_EQUAL_VEC(Vec3(-8.88900158e+01, -2.43585030e+02, -4.28254176e+02), forces1[814], tol);
ASSERT_EQUAL_VEC(Vec3(-9.15239756e+01, 4.50356816e+02, -2.64185924e+02), forces1[815], tol);
ASSERT_EQUAL_VEC(Vec3(-9.11727906e+01, 4.24890801e+02, -1.85085639e+02), forces1[816], tol);
ASSERT_EQUAL_VEC(Vec3(-3.91190277e+02, -6.86391314e+02, 1.52743025e+02), forces1[817], tol);
ASSERT_EQUAL_VEC(Vec3(-8.21031361e+01, -2.49525239e+02, 5.27536153e+02), forces1[818], tol);
ASSERT_EQUAL_VEC(Vec3(-1.09414974e+02, -6.29098191e+02, 3.34955724e+02), forces1[819], tol);
ASSERT_EQUAL_VEC(Vec3(-4.17387560e+02, -9.10777301e+01, -3.54885448e+01), forces1[820], tol);
ASSERT_EQUAL_VEC(Vec3( 2.79543439e+02, -5.39041840e+02, 5.07374351e+02), forces1[821], tol);
ASSERT_EQUAL_VEC(Vec3( 2.14822336e+02, 1.92270744e+01, -3.14848497e+02), forces1[822], tol);
ASSERT_EQUAL_VEC(Vec3(-1.92605165e+02, -4.26132560e+02, -6.22677382e+00), forces1[823], tol);
ASSERT_EQUAL_VEC(Vec3(-6.55145464e+02, -4.86073333e+02, -3.89399825e+02), forces1[824], tol);
ASSERT_EQUAL_VEC(Vec3(-1.89867341e+02, -3.77315894e+02, -1.36214663e+02), forces1[825], tol);
ASSERT_EQUAL_VEC(Vec3( 4.60001225e+02, 1.99094537e+02, 2.82584474e+02), forces1[826], tol);
ASSERT_EQUAL_VEC(Vec3( 1.21553284e+02, -1.21203683e+02, 7.24058067e+02), forces1[827], tol);
ASSERT_EQUAL_VEC(Vec3(-9.16986437e+01, 7.09307382e+01, 7.89267421e+01), forces1[828], tol);
ASSERT_EQUAL_VEC(Vec3(-3.83808972e+02, 8.44796287e+01, 1.09604935e+03), forces1[829], tol);
ASSERT_EQUAL_VEC(Vec3(-3.65392705e+02, -1.88802374e+02, 4.62347013e+02), forces1[830], tol);
ASSERT_EQUAL_VEC(Vec3( 5.76337730e+02, -9.41995570e+01, -1.98141882e+02), forces1[831], tol);
ASSERT_EQUAL_VEC(Vec3(-4.21540736e+02, -5.96961388e+01, -8.33517850e+02), forces1[832], tol);
ASSERT_EQUAL_VEC(Vec3( 1.20307619e+03, 4.53518250e+02, 2.29372510e+02), forces1[833], tol);
ASSERT_EQUAL_VEC(Vec3( 2.92097304e+02, 4.13164379e+01, -3.61110495e+02), forces1[834], tol);
ASSERT_EQUAL_VEC(Vec3( 2.49877930e+02, -1.26742169e+02, 2.01739900e+02), forces1[835], tol);
ASSERT_EQUAL_VEC(Vec3( 4.82075008e+02, -4.36717554e+02, 2.10931182e+02), forces1[836], tol);
ASSERT_EQUAL_VEC(Vec3( 7.40048230e+00, -3.07870679e+02, -2.19267744e+02), forces1[837], tol);
ASSERT_EQUAL_VEC(Vec3(-1.77929397e+02, -1.78455309e+02, 3.27383377e+02), forces1[838], tol);
ASSERT_EQUAL_VEC(Vec3(-1.43502125e+02, 2.36677035e+02, 1.76250580e+02), forces1[839], tol);
ASSERT_EQUAL_VEC(Vec3(-3.11015280e+02, 3.30102369e+01, 2.29419157e+02), forces1[840], tol);
ASSERT_EQUAL_VEC(Vec3( 7.16636078e+01, -1.43182827e+00, -8.22939586e+02), forces1[841], tol);
ASSERT_EQUAL_VEC(Vec3(-2.21647238e+02, -4.50480586e+01, -4.49964194e+02), forces1[842], tol);
ASSERT_EQUAL_VEC(Vec3(-4.26322975e+02, -2.93336811e+02, 2.05038938e+02), forces1[843], tol);
ASSERT_EQUAL_VEC(Vec3( 8.06775958e+01, 3.83777410e+02, 4.54379037e+02), forces1[844], tol);
ASSERT_EQUAL_VEC(Vec3( 1.36404637e+01, -1.12124193e+02, -2.08562538e+02), forces1[845], tol);
ASSERT_EQUAL_VEC(Vec3(-4.31330496e+01, 1.28721913e+02, 2.41017400e+02), forces1[846], tol);
ASSERT_EQUAL_VEC(Vec3( 3.71916499e+02, 3.66494598e+02, -1.02567885e+02), forces1[847], tol);
ASSERT_EQUAL_VEC(Vec3(-2.30281354e+02, 7.54907643e+02, 1.07586282e+02), forces1[848], tol);
ASSERT_EQUAL_VEC(Vec3( 2.67865075e+02, -1.51751902e+01, -1.70086059e+02), forces1[849], tol);
ASSERT_EQUAL_VEC(Vec3(-9.39303971e+02, 5.64031361e+01, -2.22408474e+02), forces1[850], tol);
ASSERT_EQUAL_VEC(Vec3(-1.01324797e+02, 3.31075650e+02, 7.07373134e+02), forces1[851], tol);
ASSERT_EQUAL_VEC(Vec3( 1.78185244e+02, 8.50185531e+01, -6.39941165e+02), forces1[852], tol);
ASSERT_EQUAL_VEC(Vec3( 3.33075565e+02, 9.10263334e+01, 1.20466005e+02), forces1[853], tol);
ASSERT_EQUAL_VEC(Vec3( 4.22270537e+01, -7.37202915e+02, 6.08451352e+02), forces1[854], tol);
ASSERT_EQUAL_VEC(Vec3(-4.16913834e+02, -4.82510498e+02, 3.91902755e+01), forces1[855], tol);
ASSERT_EQUAL_VEC(Vec3(-4.09087109e+02, -1.95401814e+02, -1.49030786e+02), forces1[856], tol);
ASSERT_EQUAL_VEC(Vec3(-2.89287185e+01, 3.78198264e+02, 2.19689536e+02), forces1[857], tol);
ASSERT_EQUAL_VEC(Vec3(-2.77576965e+02, 7.90464050e+01, 3.49478331e+02), forces1[858], tol);
ASSERT_EQUAL_VEC(Vec3( 4.71047132e+02, -1.88937165e+02, -2.48124657e+02), forces1[859], tol);
ASSERT_EQUAL_VEC(Vec3(-1.51518383e+02, 4.96193568e+01, -2.65467571e+02), forces1[860], tol);
ASSERT_EQUAL_VEC(Vec3(-7.66418994e+02, 8.46521518e+01, -3.64791094e+02), forces1[861], tol);
ASSERT_EQUAL_VEC(Vec3(-5.09774310e+02, 2.10742961e+01, 3.01814635e+02), forces1[862], tol);
ASSERT_EQUAL_VEC(Vec3(-9.75620816e+00, 7.12587243e+01, 9.37836693e+01), forces1[863], tol);
ASSERT_EQUAL_VEC(Vec3( 9.17385110e+01, -4.41182772e+02, -1.00398188e+02), forces1[864], tol);
ASSERT_EQUAL_VEC(Vec3(-2.52943928e+01, -1.90239695e+02, 4.81670302e+01), forces1[865], tol);
ASSERT_EQUAL_VEC(Vec3(-5.44266700e+02, 2.54811263e+02, -1.75181268e+02), forces1[866], tol);
ASSERT_EQUAL_VEC(Vec3( 4.08120113e+02, -1.23559887e+02, -2.06942160e+02), forces1[867], tol);
ASSERT_EQUAL_VEC(Vec3( 4.38405816e+02, -1.73677702e+02, 4.68119374e+02), forces1[868], tol);
ASSERT_EQUAL_VEC(Vec3(-3.66077262e+02, 3.32534980e+02, 2.43501977e+02), forces1[869], tol);
ASSERT_EQUAL_VEC(Vec3( 2.09710096e+02, 2.98077408e+02, -4.55256035e+02), forces1[870], tol);
ASSERT_EQUAL_VEC(Vec3( 4.70548055e+01, -8.03931446e+01, 1.44535738e+02), forces1[871], tol);
ASSERT_EQUAL_VEC(Vec3( 4.51389996e+01, -3.68239965e+02, 2.65390908e+02), forces1[872], tol);
ASSERT_EQUAL_VEC(Vec3(-2.22430692e+02, 4.99756041e+02, 1.50958469e+02), forces1[873], tol);
ASSERT_EQUAL_VEC(Vec3(-3.32911574e+02, 7.08248783e+02, -2.53293033e+02), forces1[874], tol);
ASSERT_EQUAL_VEC(Vec3(-2.24293564e+02, 3.61202816e+02, 3.14067039e+02), forces1[875], tol);
ASSERT_EQUAL_VEC(Vec3( 2.85994423e+01, 3.62421372e+02, -2.25792435e+01), forces1[876], tol);
ASSERT_EQUAL_VEC(Vec3( 3.25319985e+02, -8.59870785e+01, 6.05364581e+02), forces1[877], tol);
ASSERT_EQUAL_VEC(Vec3( 3.08171871e+02, 2.08420326e+02, -3.04923980e+02), forces1[878], tol);
ASSERT_EQUAL_VEC(Vec3( 3.65570529e+02, 1.38310252e+02, 2.10363363e+02), forces1[879], tol);
ASSERT_EQUAL_VEC(Vec3( 6.56661313e+01, 4.58001752e+02, -1.05217312e+02), forces1[880], tol);
ASSERT_EQUAL_VEC(Vec3(-7.04591192e+02, 1.95857268e+02, 6.29174462e+01), forces1[881], tol);
ASSERT_EQUAL_VEC(Vec3(-5.03143865e+02, -4.91096731e+02, 2.40904263e+00), forces1[882], tol);
ASSERT_EQUAL_VEC(Vec3(-6.84583679e+01, 8.09254534e+01, 2.50188730e+02), forces1[883], tol);
ASSERT_EQUAL_VEC(Vec3(-5.35217316e+01, 1.91746428e+02, -1.93678243e+02), forces1[884], tol);
ASSERT_EQUAL_VEC(Vec3( 5.56342532e+02, -6.69136606e+02, -2.99835083e+02), forces1[885], tol);
ASSERT_EQUAL_VEC(Vec3(-3.35801693e+02, 5.18603710e+01, -2.16409061e+02), forces1[886], tol);
ASSERT_EQUAL_VEC(Vec3(-1.13151480e+02, 6.23015348e+02, -1.73858304e+01), forces1[887], tol);
ASSERT_EQUAL_VEC(Vec3(-4.64256120e+02, -2.03157899e+02, 4.33730698e+02), forces1[888], tol);
ASSERT_EQUAL_VEC(Vec3( 5.74737750e+01, -4.61823893e+02, -4.22521242e+02), forces1[889], tol);
ASSERT_EQUAL_VEC(Vec3(-3.53693728e+02, 1.03991649e+02, 1.47435093e+02), forces1[890], tol);
ASSERT_EQUAL_VEC(Vec3( 9.55664834e+01, 4.15286634e+02, 1.37712413e+02), forces1[891], tol);
ASSERT_EQUAL_VEC(Vec3( 4.43136857e+02, -3.70066034e+01, -4.01965849e+02), forces1[892], tol);
ASSERT_EQUAL_VEC(Vec3( 1.26699422e+02, -2.55274151e+02, 1.49229513e+02), forces1[893], tol);
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment