Unverified Commit 61a908cd authored by Michael J. Schnieders's avatar Michael J. Schnieders Committed by GitHub
Browse files

Updates to AmoebaVdwForce, AmoebaGeneralizedKirkwoodForce and AmoebaWcaDispersionForce (#4647)

* Update the AMOEBA OpenMM API for vdW, GK and WCA

* Changes needed for the Corrigan et al Generalized Kirkwood model and minor changes to the vdW force to support CpHMD

* Add casts to real for uses of POW in GK; Pass force by reference within the WCA kernel

* Update swigInputConfig for Amoeba vdW and GK forces

* Update TestAPIUnits.testAmoebaVdwForce

* Set the units for getSolventDielectric and getSoluteDielectric to None

* Update default dispersion offset parameter for the AmoebaWcaDispersionForce

* Remove overloaded getParticleParameters and setParticleParameters from AmoebaGeneralizedKirkwoodForce

* Update the AmoebaWcaDispersionForce TestAPIUnits tests to reflect using the correct units for the C++ parameter default values; Update the alanine-dipeptide-amoeba-forces to reflect the updated GK model

* Move neck descreening constants into AmoebaGeneralizedKirkwoodForceImpl; set the default GK dielecticOffset to 0.09; set the default WCA shctd parameter to 0.82

* Fix Python test cases for WCA and GK

* Load AMOEBA/GK parameters into an array of float4

* Cleaned up the AmoebaGeneralizedKirkwoodForce based on feedback from Peter; the one case where backwards compatibility remains a challenge is application of the dielectric offset parameter - in the prior code this was only applied to the nonpolar cavity term, but not to calculation of Born radii; in this revision the dielectric offset is applied to BOTH the nonpolar cavity term and to calculation of Born radii. At this point I can't think of elegant way to maintain backwards compatibility that isn't confusing, nor does it make sense (at least to me) to only apply the concept of the dieletric offset to one aspect (i.e. only to nonpolar cavity or only to Born radii calculation) but not to both.

* Remove 'using std::vector' from AmoebaGeneralizedKirkwoodForceImpl.h; divide by 10 instead of multiplying by 0.1f in amoebaGk.cc

* Added a parameter called descreenOffset, which is applied during calculation of effective Born radii for GK. The parameter dielectricOffset is only used for the nonpolar cavity term consistent with its prior use. All tests in TestAmoebaGeneralizedKirkwoodForce.h are now backwards compatible with their behavior prior to this PR.

* Change two constants in amoebaGk.cc to single precision; Improved the documentation for getNeckConstants in AmoebaGeneralizedKirkwoodForceImpl.h

* Fix comment for setTanhRescaling in AmoebaGeneralizedKirkwoodForce.h, Fix comment for setTanhParameters in AmoebaReferenceGeneralizedKirkwoodForce.h; set the type of parameter GeneralizedKirkwoodTanhRescaling to bool in AmoebaGeneralizedKirkwoodForceProxy.cpp; In ReferenceCalcAmoebaGeneralizedKirkwoodForceKernel return references of per particle parameters instead of copies; update AmoebaReferenceKernels.h method signatures for per particle parameters to return const vector references

* Minor tweaks to the documentation for the tanh rescaling flag

* Improve the comments for the get and setTanhParameters in AmoebaGeneralizedKirkwoodForce.h and AmoebaReferenceGeneralizedKirkwoodForce.h
parent 78c15368
......@@ -49,6 +49,7 @@ void AmoebaReferenceVdwForce::initialize(const AmoebaVdwForce& force) {
int numParticles = force.getNumParticles();
indexIVs.resize(numParticles);
reductions.resize(numParticles);
scaleFactors.resize(numParticles);
isAlchemical.resize(numParticles);
allExclusions.clear();
allExclusions.resize(numParticles);
......@@ -57,7 +58,7 @@ void AmoebaReferenceVdwForce::initialize(const AmoebaVdwForce& force) {
double sigma, epsilon;
bool alchemical;
vector<int> exclusions;
force.getParticleParameters(i, indexIVs[i], sigma, epsilon, reductions[i], alchemical, type);
force.getParticleParameters(i, indexIVs[i], sigma, epsilon, reductions[i], alchemical, type, scaleFactors[i]);
isAlchemical[i] = alchemical;
force.getParticleExclusions(i, exclusions);
for (unsigned int j = 0; j < exclusions.size(); j++)
......@@ -224,6 +225,9 @@ double AmoebaReferenceVdwForce::calculateForceAndEnergy(int numParticles, double
double combinedEpsilon = epsilonMatrix[particleType[ii]][particleType[jj]];
double softcore = 0.0;
// Apply per particle scale factors (for CpHMD).
combinedEpsilon *= scaleFactors[ii] * scaleFactors[jj];
if (this->_alchemicalMethod == AmoebaVdwForce::Decouple && (isAlchemicalI != isAlchemical[jj])) {
combinedEpsilon *= pow(lambda, this->_n);
softcore = this->_alpha * pow(1.0 - lambda, 2);
......@@ -287,6 +291,9 @@ double AmoebaReferenceVdwForce::calculateForceAndEnergy(int numParticles, double
double combinedSigma = sigmaMatrix[particleType[siteI]][particleType[siteJ]];
double combinedEpsilon = epsilonMatrix[particleType[siteI]][particleType[siteJ]];
// Apply per particle scale factors (for CpHMD).
combinedEpsilon *= scaleFactors[siteI] * scaleFactors[siteJ];
double softcore = 0.0;
int isAlchemicalI = isAlchemical[siteI];
int isAlchemicalJ = isAlchemical[siteJ];
......
......@@ -135,6 +135,7 @@ private:
std::vector<std::vector<double> > epsilonMatrix;
std::vector<int> indexIVs;
std::vector<double> reductions;
std::vector<double> scaleFactors;
std::vector<bool> isAlchemical;
std::vector<std::set<int> > allExclusions;
Vec3 _periodicBoxVectors[3];
......
......@@ -91,13 +91,12 @@ private:
double _dispoff;
double _slevy;
enum { EMIXO, RMIXO, RMIXO7, AO, EMIXH, RMIXH, RMIXH7, AH, LastIntermediateValueIndex };
enum { EMIXO, RMIXO, EMIXH, RMIXH, LastIntermediateValueIndex };
/**---------------------------------------------------------------------------------------
Calculate pair ixn
@param radiusI radius of particle I
@param radiusJ radius of particle J
@param particleIPosition particle I position
@param particleJPosition particle J position
......@@ -108,7 +107,7 @@ private:
--------------------------------------------------------------------------------------- */
double calculatePairIxn(double radiusI, double radiusJ,
double calculatePairIxn(double radiusJ,
const OpenMM::Vec3& particleIPosition, const OpenMM::Vec3& particleJPosition,
const double* const intermediateValues,
Vec3& force) const;
......
......@@ -42,46 +42,70 @@ AmoebaGeneralizedKirkwoodForceProxy::AmoebaGeneralizedKirkwoodForceProxy() : Ser
}
void AmoebaGeneralizedKirkwoodForceProxy::serialize(const void* object, SerializationNode& node) const {
node.setIntProperty("version", 2);
node.setIntProperty("version", 3);
const AmoebaGeneralizedKirkwoodForce& force = *reinterpret_cast<const AmoebaGeneralizedKirkwoodForce*>(object);
node.setIntProperty("forceGroup", force.getForceGroup());
node.setStringProperty("name", force.getName());
node.setDoubleProperty("GeneralizedKirkwoodSolventDielectric", force.getSolventDielectric());
node.setDoubleProperty("GeneralizedKirkwoodSoluteDielectric", force.getSoluteDielectric());
//node.setDoubleProperty("GeneralizedKirkwoodDielectricOffset", force.getDielectricOffset());
node.setDoubleProperty("GeneralizedKirkwoodDielectricOffset", force.getDielectricOffset());
node.setDoubleProperty("GeneralizedKirkwoodProbeRadius", force.getProbeRadius());
node.setDoubleProperty("GeneralizedKirkwoodSurfaceAreaFactor", force.getSurfaceAreaFactor());
node.setIntProperty( "GeneralizedKirkwoodIncludeCavityTerm", force.getIncludeCavityTerm());
node.setBoolProperty("GeneralizedKirkwoodTanhRescaling", force.getTanhRescaling());
double b0, b1, b2;
force.getTanhParameters(b0, b1, b2);
node.setDoubleProperty("GeneralizedKirkwoodTanhB0", b0);
node.setDoubleProperty("GeneralizedKirkwoodTanhB1", b1);
node.setDoubleProperty("GeneralizedKirkwoodTanhB2", b2);
node.setDoubleProperty("GeneralizedKirkwoodDescreenOffset", force.getDescreenOffset());
SerializationNode& particles = node.createChildNode("GeneralizedKirkwoodParticles");
for (unsigned int ii = 0; ii < static_cast<unsigned int>(force.getNumParticles()); ii++) {
double radius, charge, scalingFactor;
force.getParticleParameters(ii, charge, radius, scalingFactor);
particles.createChildNode("Particle").setDoubleProperty("charge", charge).setDoubleProperty("radius", radius).setDoubleProperty("scaleFactor", scalingFactor);
double radius, charge, scalingFactor, descreenRadius, neckFactor;
force.getParticleParameters(ii, charge, radius, scalingFactor, descreenRadius, neckFactor);
particles.createChildNode("Particle").setDoubleProperty("charge", charge).setDoubleProperty("radius", radius).setDoubleProperty("scaleFactor", scalingFactor).setDoubleProperty("descreenRadius", descreenRadius).setDoubleProperty("neckFactor", neckFactor);
}
}
void* AmoebaGeneralizedKirkwoodForceProxy::deserialize(const SerializationNode& node) const {
int version = node.getIntProperty("version");
if (version < 1 || version > 2)
if (version < 1 || version > 3)
throw OpenMMException("Unsupported version number");
AmoebaGeneralizedKirkwoodForce* force = new AmoebaGeneralizedKirkwoodForce();
try {
force->setForceGroup(node.getIntProperty("forceGroup", 0));
force->setName(node.getStringProperty("name", force->getName()));
force->setSolventDielectric( node.getDoubleProperty("GeneralizedKirkwoodSolventDielectric"));
force->setSoluteDielectric( node.getDoubleProperty("GeneralizedKirkwoodSoluteDielectric"));
//force->setDielectricOffset( node.getDoubleProperty("GeneralizedKirkwoodDielectricOffset"));
force->setProbeRadius( node.getDoubleProperty("GeneralizedKirkwoodProbeRadius"));
force->setSurfaceAreaFactor( node.getDoubleProperty("GeneralizedKirkwoodSurfaceAreaFactor"));
force->setIncludeCavityTerm( node.getIntProperty( "GeneralizedKirkwoodIncludeCavityTerm"));
force->setSolventDielectric(node.getDoubleProperty("GeneralizedKirkwoodSolventDielectric"));
force->setSoluteDielectric(node.getDoubleProperty("GeneralizedKirkwoodSoluteDielectric"));
force->setProbeRadius(node.getDoubleProperty("GeneralizedKirkwoodProbeRadius"));
force->setSurfaceAreaFactor(node.getDoubleProperty("GeneralizedKirkwoodSurfaceAreaFactor"));
force->setIncludeCavityTerm(node.getIntProperty("GeneralizedKirkwoodIncludeCavityTerm"));
if (version > 2) {
force->setDielectricOffset(node.getDoubleProperty("GeneralizedKirkwoodDielectricOffset"));
force->setTanhRescaling(node.getBoolProperty("GeneralizedKirkwoodTanhRescaling"));
double b0 = node.getDoubleProperty("GeneralizedKirkwoodTanhB0");
double b1 = node.getDoubleProperty("GeneralizedKirkwoodTanhB1");
double b2 = node.getDoubleProperty("GeneralizedKirkwoodTanhB2");
force->setTanhParameters(b0, b1, b2);
force->setDescreenOffset(node.getDoubleProperty("GeneralizedKirkwoodDescreenOffset"));
}
const SerializationNode& particles = node.getChildNode("GeneralizedKirkwoodParticles");
for (unsigned int ii = 0; ii < particles.getChildren().size(); ii++) {
const SerializationNode& particle = particles.getChildren()[ii];
force->addParticle(particle.getDoubleProperty("charge"), particle.getDoubleProperty("radius"), particle.getDoubleProperty("scaleFactor"));
double charge = particle.getDoubleProperty("charge");
double radius = particle.getDoubleProperty("radius");
double scaleFactor = particle.getDoubleProperty("scaleFactor");
double descreenRadius = radius;
double neckFactor = 0.0;
if (version > 2) {
descreenRadius = particle.getDoubleProperty("descreenRadius");
neckFactor = particle.getDoubleProperty("neckFactor");
}
force->addParticle(charge, radius, scaleFactor, descreenRadius, neckFactor);
}
}
catch (...) {
......
......@@ -42,7 +42,7 @@ AmoebaVdwForceProxy::AmoebaVdwForceProxy() : SerializationProxy("AmoebaVdwForce"
}
void AmoebaVdwForceProxy::serialize(const void* object, SerializationNode& node) const {
node.setIntProperty("version", 4);
node.setIntProperty("version", 5);
const AmoebaVdwForce& force = *reinterpret_cast<const AmoebaVdwForce*>(object);
bool useTypes = force.getUseParticleTypes();
......@@ -62,14 +62,14 @@ void AmoebaVdwForceProxy::serialize(const void* object, SerializationNode& node)
SerializationNode& particles = node.createChildNode("VdwParticles");
for (int i = 0; i < force.getNumParticles(); i++) {
int ivIndex, typeIndex;
double sigma, epsilon, reductionFactor;
double sigma, epsilon, reductionFactor, scaleFactor;
bool isAlchemical;
force.getParticleParameters(i, ivIndex, sigma, epsilon, reductionFactor, isAlchemical, typeIndex);
force.getParticleParameters(i, ivIndex, sigma, epsilon, reductionFactor, isAlchemical, typeIndex, scaleFactor);
SerializationNode& particle = particles.createChildNode("Particle");
if (useTypes)
particle.setIntProperty("ivIndex", ivIndex).setIntProperty("type", typeIndex).setDoubleProperty("reductionFactor", reductionFactor).setBoolProperty("isAlchemical", isAlchemical);
particle.setIntProperty("ivIndex", ivIndex).setIntProperty("type", typeIndex).setDoubleProperty("reductionFactor", reductionFactor).setBoolProperty("isAlchemical", isAlchemical).setDoubleProperty("scaleFactor", scaleFactor);
else
particle.setIntProperty("ivIndex", ivIndex).setDoubleProperty("sigma", sigma).setDoubleProperty("epsilon", epsilon).setDoubleProperty("reductionFactor", reductionFactor).setBoolProperty("isAlchemical", isAlchemical);
particle.setIntProperty("ivIndex", ivIndex).setDoubleProperty("sigma", sigma).setDoubleProperty("epsilon", epsilon).setDoubleProperty("reductionFactor", reductionFactor).setBoolProperty("isAlchemical", isAlchemical).setDoubleProperty("scaleFactor", scaleFactor);
std::vector< int > exclusions;
force.getParticleExclusions(i, exclusions);
......@@ -98,7 +98,7 @@ void AmoebaVdwForceProxy::serialize(const void* object, SerializationNode& node)
void* AmoebaVdwForceProxy::deserialize(const SerializationNode& node) const {
int version = node.getIntProperty("version");
if (version < 1 || version > 4)
if (version < 1 || version > 5)
throw OpenMMException("Unsupported version number");
AmoebaVdwForce* force = new AmoebaVdwForce();
try {
......@@ -128,16 +128,30 @@ void* AmoebaVdwForceProxy::deserialize(const SerializationNode& node) const {
if (version < 3)
force->addParticle(particle.getIntProperty("ivIndex"), particle.getDoubleProperty("sigma"),
particle.getDoubleProperty("epsilon"), particle.getDoubleProperty("reductionFactor"));
else if (useTypes)
force->addParticle(particle.getIntProperty("ivIndex"), particle.getIntProperty("type"),
particle.getDoubleProperty("reductionFactor"), particle.getBoolProperty("isAlchemical"));
else
force->addParticle(particle.getIntProperty("ivIndex"), particle.getDoubleProperty("sigma"),
particle.getDoubleProperty("epsilon"), particle.getDoubleProperty("reductionFactor"),
particle.getBoolProperty("isAlchemical"));
else if (version == 4) {
if (useTypes)
force->addParticle(particle.getIntProperty("ivIndex"), particle.getIntProperty("type"),
particle.getDoubleProperty("reductionFactor"),
particle.getBoolProperty("isAlchemical"));
else
force->addParticle(particle.getIntProperty("ivIndex"), particle.getDoubleProperty("sigma"),
particle.getDoubleProperty("epsilon"),
particle.getDoubleProperty("reductionFactor"),
particle.getBoolProperty("isAlchemical"));
} else {
// Version 5 includes per particle scale factor for CpHMD.
if (useTypes)
force->addParticle(particle.getIntProperty("ivIndex"), particle.getIntProperty("type"),
particle.getDoubleProperty("reductionFactor"),
particle.getBoolProperty("isAlchemical"), particle.getDoubleProperty("scaleFactor"));
else
force->addParticle(particle.getIntProperty("ivIndex"), particle.getDoubleProperty("sigma"),
particle.getDoubleProperty("epsilon"),
particle.getDoubleProperty("reductionFactor"),
particle.getBoolProperty("isAlchemical"), particle.getDoubleProperty("scaleFactor"));
}
// exclusions
const SerializationNode& particleExclusions = particle.getChildNode("ParticleExclusions");
std::vector<int> exclusions;
for (int j = 0; j < particleExclusions.getChildren().size(); j++)
......
......@@ -47,46 +47,48 @@ void testSerialization() {
AmoebaGeneralizedKirkwoodForce force1;
force1.setForceGroup(3);
force1.setName("custom name");
force1.setSolventDielectric( 80.0);
force1.setSoluteDielectric( 1.0);
//force1.setDielectricOffset( 0.09);
force1.setProbeRadius( 1.40);
force1.setSurfaceAreaFactor( 0.888);
force1.setIncludeCavityTerm( 1);
force1.setSolventDielectric(80.0);
force1.setSoluteDielectric(1.0);
force1.setDielectricOffset( 0.09);
force1.setProbeRadius(1.40);
force1.setSurfaceAreaFactor(0.888);
force1.setIncludeCavityTerm(1);
force1.setTanhRescaling(0);
force1.addParticle(1.0, 2.0, 0.9);
force1.addParticle(-1.1,2.1, 0.8);
force1.addParticle(0.1, 2.2, 0.7);
force1.addParticle(1.0, 2.0, 0.9, 2.0, 0.0);
force1.addParticle(-1.1, 2.1, 0.8, 2.1, 0.0);
force1.addParticle(0.1, 2.2, 0.7, 2.2, 0.0);
// Serialize and then deserialize it.
stringstream buffer;
XmlSerializer::serialize<AmoebaGeneralizedKirkwoodForce>(&force1, "Force", buffer);
AmoebaGeneralizedKirkwoodForce* copy = XmlSerializer::deserialize<AmoebaGeneralizedKirkwoodForce>(buffer);
AmoebaGeneralizedKirkwoodForce *copy = XmlSerializer::deserialize<AmoebaGeneralizedKirkwoodForce>(buffer);
// Compare the two forces to see if they are identical.
AmoebaGeneralizedKirkwoodForce& force2 = *copy;
AmoebaGeneralizedKirkwoodForce &force2 = *copy;
ASSERT_EQUAL(force1.getForceGroup(), force2.getForceGroup());
ASSERT_EQUAL(force1.getName(), force2.getName());
ASSERT_EQUAL(force1.getSolventDielectric(), force2.getSolventDielectric());
ASSERT_EQUAL(force1.getSoluteDielectric(), force2.getSoluteDielectric());
//ASSERT_EQUAL(force1.getDielectricOffset(), force2.getDielectricOffset());
ASSERT_EQUAL(force1.getProbeRadius(), force2.getProbeRadius());
ASSERT_EQUAL(force1.getSurfaceAreaFactor(), force2.getSurfaceAreaFactor());
ASSERT_EQUAL(force1.getIncludeCavityTerm(), force2.getIncludeCavityTerm());
ASSERT_EQUAL(force1.getSolventDielectric(), force2.getSolventDielectric());
ASSERT_EQUAL(force1.getSoluteDielectric(), force2.getSoluteDielectric());
ASSERT_EQUAL(force1.getDielectricOffset(), force2.getDielectricOffset());
ASSERT_EQUAL(force1.getProbeRadius(), force2.getProbeRadius());
ASSERT_EQUAL(force1.getSurfaceAreaFactor(), force2.getSurfaceAreaFactor());
ASSERT_EQUAL(force1.getIncludeCavityTerm(), force2.getIncludeCavityTerm());
ASSERT_EQUAL(force1.getNumParticles(), force2.getNumParticles());
for (unsigned int ii = 0; ii < static_cast<unsigned int>(force1.getNumParticles()); ii++) {
double radius1, charge1, scaleFactor1;
double radius2, charge2, scaleFactor2;
double radius1, charge1, scaleFactor1, descreen1, neckFactor1;
double radius2, charge2, scaleFactor2, descreen2, neckFactor2;
force1.getParticleParameters(ii, charge1, radius1, scaleFactor1);
force2.getParticleParameters(ii, charge2, radius2, scaleFactor2);
force1.getParticleParameters(ii, charge1, radius1, scaleFactor1, descreen1, neckFactor1);
force2.getParticleParameters(ii, charge2, radius2, scaleFactor2, descreen2, neckFactor2);
ASSERT_EQUAL(charge1, charge2);
ASSERT_EQUAL(radius1, radius2);
ASSERT_EQUAL(scaleFactor1, scaleFactor2);
ASSERT_EQUAL(charge1, charge2)
ASSERT_EQUAL(radius1, radius2)
ASSERT_EQUAL(scaleFactor1, scaleFactor2)
ASSERT_EQUAL(descreen1, descreen2)
}
}
......@@ -95,7 +97,7 @@ int main() {
registerAmoebaSerializationProxies();
testSerialization();
}
catch(const exception& e) {
catch (const exception &e) {
cout << "exception: " << e.what() << endl;
return 1;
}
......
......@@ -54,9 +54,9 @@ void testSerialization() {
force1.setAlchemicalMethod(AmoebaVdwForce::None);
force1.setPotentialFunction(AmoebaVdwForce::Buffered147);
force1.addParticle(0, 1.0, 2.0, 0.9, false);
force1.addParticle(1, 1.1, 2.1, 0.9, true);
force1.addParticle(2, 1.3, 4.1, 0.9, false);
force1.addParticle(0, 1.0, 2.0, 0.9, false, 1.0);
force1.addParticle(1, 1.1, 2.1, 0.9, true, 0.9);
force1.addParticle(2, 1.3, 4.1, 0.9, false, 1.0);
for (int i = 0; i < 3; i++) {
std::vector< int > exclusions;
exclusions.push_back(i);
......@@ -90,14 +90,14 @@ void testSerialization() {
int ivIndex1, type1;
int ivIndex2, type2;
double sigma1, epsilon1, reductionFactor1;
double sigma2, epsilon2, reductionFactor2;
double sigma1, epsilon1, reductionFactor1, scaleFactor1;
double sigma2, epsilon2, reductionFactor2, scaleFactor2;
bool isAlchemical1;
bool isAlchemical2;
force1.getParticleParameters(i, ivIndex1, sigma1, epsilon1, reductionFactor1, isAlchemical1, type1);
force2.getParticleParameters(i, ivIndex2, sigma2, epsilon2, reductionFactor2, isAlchemical2, type2);
force1.getParticleParameters(i, ivIndex1, sigma1, epsilon1, reductionFactor1, isAlchemical1, type1, scaleFactor1);
force2.getParticleParameters(i, ivIndex2, sigma2, epsilon2, reductionFactor2, isAlchemical2, type2, scaleFactor2);
ASSERT_EQUAL(ivIndex1, ivIndex2);
ASSERT_EQUAL(sigma1, sigma2);
......@@ -105,6 +105,7 @@ void testSerialization() {
ASSERT_EQUAL(reductionFactor1, reductionFactor2);
ASSERT_EQUAL(isAlchemical1, isAlchemical2);
ASSERT_EQUAL(type1, type2);
ASSERT_EQUAL(scaleFactor1, scaleFactor2);
}
for (int i = 0; i < force1.getNumParticles(); i++) {
......@@ -131,10 +132,10 @@ void testSerializeTypes() {
AmoebaVdwForce force1;
force1.setPotentialFunction(AmoebaVdwForce::LennardJones);
force1.addParticle(0, 2, 1.0, false);
force1.addParticle(1, 2, 0.9, true);
force1.addParticle(2, 0, 1.0, false);
force1.addParticle(3, 1, 0.9, false);
force1.addParticle(0, 2, 1.0, false, 1.0);
force1.addParticle(1, 2, 0.9, true, 0.0);
force1.addParticle(2, 0, 1.0, false, 0.5);
force1.addParticle(3, 1, 0.9, false, 0.9);
force1.addParticleType(1.1, 2.0);
force1.addParticleType(1.2, 2.1);
force1.addParticleType(1.3, 2.2);
......@@ -158,14 +159,14 @@ void testSerializeTypes() {
int ivIndex1, type1;
int ivIndex2, type2;
double sigma1, epsilon1, reductionFactor1;
double sigma2, epsilon2, reductionFactor2;
double sigma1, epsilon1, reductionFactor1, scaleFactor1;
double sigma2, epsilon2, reductionFactor2, scaleFactor2;
bool isAlchemical1;
bool isAlchemical2;
force1.getParticleParameters(i, ivIndex1, sigma1, epsilon1, reductionFactor1, isAlchemical1, type1);
force2.getParticleParameters(i, ivIndex2, sigma2, epsilon2, reductionFactor2, isAlchemical2, type2);
force1.getParticleParameters(i, ivIndex1, sigma1, epsilon1, reductionFactor1, isAlchemical1, type1, scaleFactor1);
force2.getParticleParameters(i, ivIndex2, sigma2, epsilon2, reductionFactor2, isAlchemical2, type2, scaleFactor2);
ASSERT_EQUAL(ivIndex1, ivIndex2);
ASSERT_EQUAL(sigma1, sigma2);
......@@ -173,6 +174,7 @@ void testSerializeTypes() {
ASSERT_EQUAL(reductionFactor1, reductionFactor2);
ASSERT_EQUAL(isAlchemical1, isAlchemical2);
ASSERT_EQUAL(type1, type2);
ASSERT_EQUAL(scaleFactor1, scaleFactor2);
}
for (int i = 0; i < force1.getNumParticleTypes(); i++) {
double sigma1, epsilon1;
......
......@@ -289,10 +289,10 @@ static void setupMultipoleAmmonia(System& system, AmoebaGeneralizedKirkwoodForce
// addParticle: charge, radius, scalingFactor
 
for (unsigned int ii = 0; ii < 2; ii++) {
amoebaGeneralizedKirkwoodForce->addParticle( -5.7960000e-01, 1.5965000e-01, 6.9000000e-01);
amoebaGeneralizedKirkwoodForce->addParticle( 1.9320000e-01, 1.2360000e-01, 6.9000000e-01);
amoebaGeneralizedKirkwoodForce->addParticle( 1.9320000e-01, 1.2360000e-01, 6.9000000e-01);
amoebaGeneralizedKirkwoodForce->addParticle( 1.9320000e-01, 1.2360000e-01, 6.9000000e-01);
amoebaGeneralizedKirkwoodForce->addParticle( -5.7960000e-01, 1.5965000e-01, 6.9000000e-01, 1.5965000e-01, 0.0f);
amoebaGeneralizedKirkwoodForce->addParticle( 1.9320000e-01, 1.2360000e-01, 6.9000000e-01, 1.2360000e-01, 0.0f);
amoebaGeneralizedKirkwoodForce->addParticle( 1.9320000e-01, 1.2360000e-01, 6.9000000e-01, 1.2360000e-01, 0.0f);
amoebaGeneralizedKirkwoodForce->addParticle( 1.9320000e-01, 1.2360000e-01, 6.9000000e-01, 1.2360000e-01, 0.0f);
}
system.addForce(amoebaGeneralizedKirkwoodForce);
}
......@@ -7167,9 +7167,9 @@ static void testGeneralizedKirkwoodAmmoniaMutualPolarizationWithCavityTerm() {
// Try changing the particle parameters and make sure it's still correct.
for (int i = 0; i < numberOfParticles; i++) {
double charge, radius, scale;
amoebaGeneralizedKirkwoodForce->getParticleParameters(i, charge, radius, scale);
amoebaGeneralizedKirkwoodForce->setParticleParameters(i, charge, 0.9*radius, 1.1*scale);
double charge, radius, scale, descreenRadius, neckscale;
amoebaGeneralizedKirkwoodForce->getParticleParameters(i, charge, radius, scale, descreenRadius, neckscale);
amoebaGeneralizedKirkwoodForce->setParticleParameters(i, charge, 0.9*radius, 1.1*scale, 1.2*descreenRadius, 1.3*neckscale);
}
LangevinIntegrator integrator2(0.0, 0.1, 0.01);
Context context2(system, integrator2, context.getPlatform());
......
......@@ -133,12 +133,12 @@ void testVdw() {
}
for (int ii = 0; ii < amoebaVdwForce->getNumParticles(); ii++) {
int indexIV, type;
double sigma, epsilon, reduction;
double sigma, epsilon, reduction, scaleFactor;
bool isAlchemical;
amoebaVdwForce->getParticleParameters(ii, indexIV, sigma, epsilon, reduction, isAlchemical, type);
amoebaVdwForce->getParticleParameters(ii, indexIV, sigma, epsilon, reduction, isAlchemical, type, scaleFactor);
sigma *= AngstromToNm;
epsilon *= CalToJoule;
amoebaVdwForce->setParticleParameters(ii, indexIV, sigma, epsilon, reduction, isAlchemical, type);
amoebaVdwForce->setParticleParameters(ii, indexIV, sigma, epsilon, reduction, isAlchemical, type, scaleFactor);
}
Context context(system, integrator, platform);
......@@ -163,10 +163,10 @@ void testVdw() {
for (int i = 0; i < numberOfParticles; i++) {
int indexIV, type;
double mass, sigma, epsilon, reduction;
double mass, sigma, epsilon, reduction, scaleFactor;
bool isAlchemical;
amoebaVdwForce->getParticleParameters(i, indexIV, sigma, epsilon, reduction, isAlchemical, type);
amoebaVdwForce->setParticleParameters(i, indexIV, 0.9*sigma, 2.0*epsilon, 0.95*reduction, isAlchemical, type);
amoebaVdwForce->getParticleParameters(i, indexIV, sigma, epsilon, reduction, isAlchemical, type, scaleFactor);
amoebaVdwForce->setParticleParameters(i, indexIV, 0.9*sigma, 2.0*epsilon, 0.95*reduction, isAlchemical, type, scaleFactor);
}
LangevinIntegrator integrator2(0.0, 0.1, 0.01);
Context context2(system, integrator2, platform);
......
......@@ -42,103 +42,122 @@
#define ASSERT_EQUAL_TOL_MOD(expected, found, tol, testname) {double _scale_ = std::abs(expected) > 1.0 ? std::abs(expected) : 1.0; if (!(std::abs((expected)-(found))/_scale_ <= (tol))) {std::stringstream details; details << testname << " Expected "<<(expected)<<", found "<<(found); throwException(__FILE__, __LINE__, details.str());}};
#define ASSERT_EQUAL_VEC_MOD(expected, found, tol,testname) {ASSERT_EQUAL_TOL_MOD((expected)[0], (found)[0], (tol),(testname)); ASSERT_EQUAL_TOL_MOD((expected)[1], (found)[1], (tol),(testname)); ASSERT_EQUAL_TOL_MOD((expected)[2], (found)[2], (tol),(testname));};
#define ASSERT_EQUAL_VEC_MOD(expected, found, tol, testname) {ASSERT_EQUAL_TOL_MOD((expected)[0], (found)[0], (tol),(testname)); ASSERT_EQUAL_TOL_MOD((expected)[1], (found)[1], (tol),(testname)); ASSERT_EQUAL_TOL_MOD((expected)[2], (found)[2], (tol),(testname));};
using namespace OpenMM;
const double TOL = 1e-4;
void compareForcesEnergy(std::string& testName, double expectedEnergy, double energy,
const std::vector<Vec3>& expectedForces,
const std::vector<Vec3>& forces, double tolerance) {
void compareForcesEnergy(std::string &testName, double expectedEnergy, double energy,
const std::vector<Vec3> &expectedForces,
const std::vector<Vec3> &forces, double tolerance) {
for (unsigned int ii = 0; ii < forces.size(); ii++) {
ASSERT_EQUAL_VEC_MOD(expectedForces[ii], forces[ii], tolerance, testName);
ASSERT_EQUAL_VEC_MOD(expectedForces[ii], forces[ii], tolerance, testName)
}
ASSERT_EQUAL_TOL_MOD(expectedEnergy, energy, tolerance, testName);
ASSERT_EQUAL_TOL_MOD(expectedEnergy, energy, tolerance, testName)
}
// test Wca dispersion
// Test Wca Dispersion
void testWcaDispersionAmmonia() {
std::string testName = "testWcaDispersionAmmonia";
int numberOfParticles = 8;
std::string testName = "testWcaDispersionAmmonia";
int numberOfParticles = 8;
// Create the system.
System system;
AmoebaWcaDispersionForce* amoebaWcaDispersionForce = new AmoebaWcaDispersionForce();;
AmoebaWcaDispersionForce *amoebaWcaDispersionForce = new AmoebaWcaDispersionForce();
// Convert kcal/mol to kJ/mol
double epso = 0.1100e0 * 4.184e0;
double epsh = 0.0135e0 * 4.184e0;
// Convert A to nm.
double rmino = 1.7025e-01;
double rminh = 1.3275e-01;
double dispoff = 1.056e-01;
// Convert water number density from water / A^3 to water / nm^3.
double awater = 0.033428e03;
// No units.
double slevy = 1.0e0;
double shctd = 0.75e0;
amoebaWcaDispersionForce->setEpso(epso);
amoebaWcaDispersionForce->setEpsh(epsh);
amoebaWcaDispersionForce->setRmino(rmino);
amoebaWcaDispersionForce->setRminh(rminh);
amoebaWcaDispersionForce->setDispoff(dispoff);
amoebaWcaDispersionForce->setAwater(awater);
amoebaWcaDispersionForce->setSlevy(slevy);
amoebaWcaDispersionForce->setShctd(shctd);
// Amoeba 2009
// vdwtype BUFFERED-14-7
// radiusrule CUBIC-MEAN
// radiustype R-MIN
// radiussize DIAMETER
// epsilonrule HHG
// vdw 45 3.710000000 0.105000000
// vdw 46 2.700000000 0.020000000 0.910
amoebaWcaDispersionForce->setEpso( 4.6024000e-01);
amoebaWcaDispersionForce->setEpsh( 5.6484000e-02);
amoebaWcaDispersionForce->setRmino( 1.7025000e-01);
amoebaWcaDispersionForce->setRminh( 1.3275000e-01);
amoebaWcaDispersionForce->setDispoff(2.6000000e-02);
amoebaWcaDispersionForce->setAwater( 3.3428000e+01);
amoebaWcaDispersionForce->setSlevy( 1.0000000e+00);
amoebaWcaDispersionForce->setShctd( 8.1000000e-01);
for (unsigned int ii = 0; ii < 2; ii++) {
system.addParticle(1.4007000e+01);
amoebaWcaDispersionForce->addParticle(3.71e-01 / 2.0e0, 0.105e0 * 4.184e0);
// addParticle: radius, epsilon
system.addParticle(1.0080000e+00);
amoebaWcaDispersionForce->addParticle(2.7e-01 / 2.0e0, 0.02e0 * 4.184e0);
for (unsigned int ii = 0; ii < 2; ii++) {
system.addParticle( 1.4007000e+01);
amoebaWcaDispersionForce->addParticle( 1.8550000e-01, 4.3932000e-01);
system.addParticle( 1.0080000e+00);
amoebaWcaDispersionForce->addParticle( 1.3500000e-01, 8.3680000e-02);
system.addParticle( 1.0080000e+00);
amoebaWcaDispersionForce->addParticle( 1.3500000e-01, 8.3680000e-02);
system.addParticle( 1.0080000e+00);
amoebaWcaDispersionForce->addParticle( 1.3500000e-01, 8.3680000e-02);
system.addParticle(1.0080000e+00);
amoebaWcaDispersionForce->addParticle(2.7e-01 / 2.0e0, 0.02e0 * 4.184e0);
system.addParticle(1.0080000e+00);
amoebaWcaDispersionForce->addParticle(2.7e-01 / 2.0e0, 0.02e0 * 4.184e0);
}
std::vector<Vec3> positions(numberOfParticles);
positions[0] = Vec3( 1.5927280e-01, 1.7000000e-06, 1.6491000e-03);
positions[1] = Vec3( 2.0805540e-01, -8.1258800e-02, 3.7282500e-02);
positions[2] = Vec3( 2.0843610e-01, 8.0953200e-02, 3.7462200e-02);
positions[3] = Vec3( 1.7280780e-01, 2.0730000e-04, -9.8741700e-02);
positions[4] = Vec3( -1.6743680e-01, 1.5900000e-05, -6.6149000e-03);
positions[5] = Vec3( -2.0428260e-01, 8.1071500e-02, 4.1343900e-02);
positions[6] = Vec3( -6.7308300e-02, 1.2800000e-05, 1.0623300e-02);
positions[7] = Vec3( -2.0426290e-01, -8.1231400e-02, 4.1033500e-02);
positions[0] = Vec3(1.57493055e-01, 0.00085545e-01, 0.12707740e-01);
positions[1] = Vec3(1.94285651e-01, -0.81095826e-01, 0.60622179e-01);
positions[2] = Vec3(1.95030610e-01, 0.80808023e-01, 0.60809749e-01);
positions[3] = Vec3(1.99309092e-01, -0.00015413e-01, -0.79434283e-01);
positions[4] = Vec3(-1.65212994e-01, 0.02174061e-01, -0.04650966e-01);
positions[5] = Vec3(-1.97210029e-01, 0.82501943e-01, 0.47898953e-01);
positions[6] = Vec3(-0.64164714e-01, 0.01492667e-01, 0.03495515e-01);
positions[7] = Vec3(-1.98070588e-01, -0.79434787e-01, 0.45336787e-01);
system.addForce(amoebaWcaDispersionForce);
ASSERT(!amoebaWcaDispersionForce->usesPeriodicBoundaryConditions());
ASSERT(!system.usesPeriodicBoundaryConditions());
LangevinIntegrator integrator(0.0, 0.1, 0.01);
Context context(system, integrator, platform);
context.setPositions(positions);
State state = context.getState(State::Forces | State::Energy);
std::vector<Vec3> forces = state.getForces();
const std::vector<Vec3> &forces = state.getForces();
double energy = state.getPotentialEnergy();
// TINKER-computed values
// Force Field X / Tinker expected energy -5.55412658 * 4.184 and forces.
double expectedEnergy = -5.55412658 * 4.184;
std::vector<Vec3> expectedForces(numberOfParticles);
double expectedEnergy = -2.6981209e+01;
expectedForces[0] = Vec3( 4.7839388e+00, -7.3510133e-04, -5.0382764e-01);
expectedForces[1] = Vec3( 1.4657758e+00, 1.2431003e+00, -6.7075886e-01);
expectedForces[2] = Vec3( 1.4563936e+00, -1.2399917e+00, -6.7443841e-01);
expectedForces[3] = Vec3( 2.1116744e+00, -2.7407512e-03, 1.3271245e+00);
expectedForces[4] = Vec3( -4.7528440e+00, -1.5148066e-03, 1.2653813e+00);
expectedForces[5] = Vec3( -1.1875619e+00, -1.2866678e+00, -3.9109060e-01);
expectedForces[6] = Vec3( -2.6885679e+00, -4.3038639e-04, 3.3763583e-02);
expectedForces[7] = Vec3( -1.1888087e+00, 1.2889802e+00, -3.8615387e-01);
double tolerance = 1.0e-04;
expectedForces[0] = Vec3(-0.33370779, 0.00478895, -0.18780836);
expectedForces[1] = Vec3(1.15078615, 0.07363123, -0.04881103);
expectedForces[2] = Vec3(1.14509754, -0.08727205, -0.05086123);
expectedForces[3] = Vec3(0.97122614, -0.00473535, 0.09417216);
expectedForces[4] = Vec3(-1.64874538, 0.01074006, -0.08477238);
expectedForces[5] = Vec3(-1.28585908, 0.00494013, 0.05864744);
expectedForces[6] = Vec3(1.28677671, -0.00915430, 0.16081161);
expectedForces[7] = Vec3(-1.28557430, 0.00706132, 0.05862179);
double tolerance = 1.0e-04;
compareForcesEnergy(testName, expectedEnergy, energy, expectedForces, forces, tolerance);
// Try changing the particle parameters and make sure it's still correct.
for (int i = 0; i < numberOfParticles; i++) {
double radius, epsilon;
amoebaWcaDispersionForce->getParticleParameters(i, radius, epsilon);
amoebaWcaDispersionForce->setParticleParameters(i, 0.9*radius, 2.0*epsilon);
amoebaWcaDispersionForce->setParticleParameters(i, 0.9 * radius, 2.0 * epsilon);
}
LangevinIntegrator integrator2(0.0, 0.1, 0.01);
Context context2(system, integrator2, platform);
......@@ -148,7 +167,8 @@ void testWcaDispersionAmmonia() {
bool exceptionThrown = false;
try {
// This should throw an exception.
compareForcesEnergy(testName, state1.getPotentialEnergy(), state2.getPotentialEnergy(), state1.getForces(), state2.getForces(), tolerance);
compareForcesEnergy(testName, state1.getPotentialEnergy(), state2.getPotentialEnergy(), state1.getForces(),
state2.getForces(), tolerance);
}
catch (std::exception ex) {
exceptionThrown = true;
......@@ -156,7 +176,8 @@ void testWcaDispersionAmmonia() {
ASSERT(exceptionThrown);
amoebaWcaDispersionForce->updateParametersInContext(context);
state1 = context.getState(State::Forces | State::Energy);
compareForcesEnergy(testName, state1.getPotentialEnergy(), state2.getPotentialEnergy(), state1.getForces(), state2.getForces(), tolerance);
compareForcesEnergy(testName, state1.getPotentialEnergy(), state2.getPotentialEnergy(), state1.getForces(),
state2.getForces(), tolerance);
}
void setupKernels(int argc, char* argv[]);
......
......@@ -240,12 +240,18 @@ UNITS = {
("SerializationProxy", "getTypeName") : (None, ()),
# check getSurfaceAreaFactor
("AmoebaGeneralizedKirkwoodForce", "getParticleParameters") : (None, ('unit.elementary_charge', 'unit.nanometer', None)),
("AmoebaGeneralizedKirkwoodForce", "getParticleParameters") : (None, ('unit.elementary_charge', 'unit.nanometer', None, 'unit.nanometer', None)),
("AmoebaGeneralizedKirkwoodForce", "getDielectricOffset") : ( 'unit.nanometer', ()),
("AmoebaGeneralizedKirkwoodForce", "getSolventDielectric") : (None, ()),
("AmoebaGeneralizedKirkwoodForce", "getSoluteDielectric") : (None, ()),
("AmoebaGeneralizedKirkwoodForce", "getIncludeCavityTerm") : ( None,()),
("AmoebaGeneralizedKirkwoodForce", "getTanhRescaling") : ( None,()),
("AmoebaGeneralizedKirkwoodForce", "getTanhParameters") : ( None,(None, None, None)),
("AmoebaGeneralizedKirkwoodForce", "getDescreenOffset") : ( 'unit.nanometer', ()),
("AmoebaGeneralizedKirkwoodForce", "getProbeRadius") : ( 'unit.nanometer', ()),
("AmoebaGeneralizedKirkwoodForce", "getSurfaceAreaFactor") : ( 'unit.kilojoule_per_mole/(unit.nanometer*unit.nanometer)',()),
("AmoebaMultipoleForce", "getNumMultipoles") : ( None,()),
("AmoebaMultipoleForce", "getPolarizationType") : ( None,()),
("AmoebaMultipoleForce", "getCutoffDistance") : ( 'unit.nanometer',()),
......@@ -297,7 +303,7 @@ UNITS = {
("AmoebaVdwForce", "getSoftcorePower") : ( None, ()),
("AmoebaVdwForce", "getSoftcoreAlpha") : ( None, ()),
("AmoebaVdwForce", "getCutoff") : ( 'unit.nanometer', ()),
("AmoebaVdwForce", "getParticleParameters") : ( None, (None, 'unit.nanometer', 'unit.kilojoule_per_mole', None, None, None)),
("AmoebaVdwForce", "getParticleParameters") : ( None, (None, 'unit.nanometer', 'unit.kilojoule_per_mole', None, None, None, None)),
("AmoebaVdwForce", "getParticleTypeParameters") : ( None, ('unit.nanometer', 'unit.kilojoule_per_mole')),
("AmoebaVdwForce", "getTypePairParameters") : ( None, (None, None, 'unit.nanometer', 'unit.kilojoule_per_mole')),
......
......@@ -639,14 +639,14 @@ class TestAPIUnits(unittest.TestCase):
self.assertEqual(force.getNumParticles(), 2)
q, r, s = force.getParticleParameters(0)
q, r, s, d, k = force.getParticleParameters(0)
self.assertAlmostEqualUnit(q, 1.0*coulomb)
self.assertIs(q.unit, elementary_charge)
self.assertEqual(r, 1.0*angstroms)
self.assertIs(r.unit, nanometer)
self.assertEqual(s, 0.5)
q, r, s = force.getParticleParameters(1)
q, r, s, d, k = force.getParticleParameters(1)
self.assertAlmostEqualUnit(q, 1.0*elementary_charge)
self.assertIs(q.unit, elementary_charge)
self.assertEqual(r, 1.0*nanometer)
......@@ -759,44 +759,47 @@ class TestAPIUnits(unittest.TestCase):
self.assertEqual(force.getNumParticles(), 3)
p, sig, eps, scale, alchemical, type = force.getParticleParameters(0)
p, sig, eps, reduction, alchemical, type, scale = force.getParticleParameters(0)
self.assertEqual(p, 0)
self.assertEqual(sig, 0.1*nanometers)
self.assertIs(sig.unit, nanometers)
self.assertEqual(eps, 1.0*kilojoules_per_mole)
self.assertIs(eps.unit, kilojoules_per_mole)
self.assertEqual(scale, 1.0)
self.assertEqual(reduction, 1.0)
self.assertEqual(type, -1)
self.assertEqual(scale, 1.0)
p, sig, eps, scale, alchemical, type = force.getParticleParameters(1)
p, sig, eps, reduction, alchemical, type, scale = force.getParticleParameters(1)
self.assertEqual(p, 1)
self.assertEqual(sig, 1.0*angstroms)
self.assertIs(sig.unit, nanometers)
self.assertEqual(eps, 1.0*kilocalories_per_mole)
self.assertIs(eps.unit, kilojoules_per_mole)
self.assertEqual(scale, 0.5)
self.assertEqual(reduction, 0.5)
self.assertEqual(type, -1)
self.assertEqual(scale, 1.0)
p, sig, eps, scale, alchemical, type = force.getParticleParameters(2)
p, sig, eps, reduction, alchemical, type, scale = force.getParticleParameters(2)
self.assertEqual(p, 1)
self.assertAlmostEqualUnit(sig, 0.8*angstroms)
self.assertIs(sig.unit, nanometers)
self.assertEqual(eps, 2.0*kilocalories_per_mole)
self.assertIs(eps.unit, kilojoules_per_mole)
self.assertEqual(scale, 0.25)
self.assertEqual(reduction, 0.25)
self.assertEqual(type, -1)
self.assertEqual(scale, 1.0)
def testAmoebaWcaDispersionForce(self):
""" Tests the AmoebaWcaDispersionForce API features """
force = AmoebaWcaDispersionForce()
self.assertEqual(force.getDispoff(), 0.26*nanometer)
self.assertEqual(force.getAwater(), 0.033428*nanometer**-3)
self.assertEqual(force.getEpsh(), 0.0135*kilojoule_per_mole)
self.assertEqual(force.getEpso(), 0.11*kilojoule_per_mole)
self.assertEqual(force.getRminh(), 1.3275*nanometer)
self.assertEqual(force.getRmino(), 1.7025*nanometer)
self.assertEqual(force.getShctd(), 0.81)
self.assertEqual(force.getDispoff(), 0.1056*nanometer)
self.assertEqual(force.getAwater(), 33.428*nanometer**-3)
self.assertEqual(force.getEpsh(), 0.056484*kilojoule_per_mole)
self.assertEqual(force.getEpso(), 0.46024000000000004*kilojoule_per_mole)
self.assertEqual(force.getRminh(), 0.13275*nanometer)
self.assertEqual(force.getRmino(), 0.17025*nanometer)
self.assertEqual(force.getShctd(), 0.82)
self.assertEqual(force.getSlevy(), 1.0)
force.setDispoff(3*angstroms)
......
<?xml version="1.0" ?>
<State time="0" type="State" version="1">
<State openmmVersion="8.1" stepCount="0" time="0" type="State" version="1">
<PeriodicBoxVectors>
<A x="2" y="0" z="0"/>
<B x="0" y="2" z="0"/>
<C x="0" y="0" z="2"/>
</PeriodicBoxVectors>
<Forces>
<Force x="-60.899006905648534" y="-704.8991281062977" z=".0364180169671772"/>
<Force x="1179.7263311404506" y="629.2168542831286" z="1.563821009034657"/>
<Force x="-484.4401097634541" y="135.94210610096957" z="558.0155391926069"/>
<Force x="-484.1625627429897" y="136.02915381525668" z="-557.7203977673528"/>
<Force x="-804.8403408476064" y="-857.579131840898" z="118.43755474210333"/>
<Force x="84.24441920455223" y="-421.86322192608037" z="120.734742628977"/>
<Force x="666.4942549112259" y="-479.03339298154526" z="19.66166555862896"/>
<Force x="-518.9062723002917" y="686.3850843706331" z=".7311137123231983"/>
<Force x="-105.62094199225953" y="710.9845370176521" z="-508.0677463920413"/>
<Force x="285.6690654282444" y="-111.35548608152155" z="847.8096733172159"/>
<Force x="-396.4822698774795" y="24.462223992557014" z="297.9581483103431"/>
<Force x="-168.22340166944585" y="110.99779797739818" z="-824.0916382722021"/>
<Force x="805.7023041833917" y="15.738288953519556" z="-340.2263402925698"/>
<Force x="366.3025998383222" y="-647.7535895818237" z="-273.83047817845636"/>
<Force x="-649.7310636705703" y="-495.52797252280067" z="317.36013856851315"/>
<Force x="-480.120788018345" y="172.44477988154952" z="-16.235455767965004"/>
<Force x="-486.20230228601736" y="982.6376366390087" z="127.20818643942287"/>
<Force x="820.5730252078641" y="-78.71234329038225" z="109.31395566365498"/>
<Force x="355.03388135028814" y="-1391.2776654673507" z=".9942543092954484"/>
<Force x="-511.3467906691061" y="677.7717086552518" z=".008253291392112616"/>
<Force x="293.66157422204157" y="452.68343054060256" z="502.83463917630775"/>
<Force x="293.5683952568295" y="452.70832957117113" z="-502.49604726620026"/>
<Force x="-60.48934767780512" y="-704.2905908945154" z=".03641801581402149"/>
<Force x="1180.5929374826017" y="629.7947038764579" z="1.563821030008981"/>
<Force x="-483.76818418432686" y="136.17122858237818" z="557.445557397338"/>
<Force x="-483.4906371562492" y="136.2582762991838" z="-557.1504159715371"/>
<Force x="-804.7241107189593" y="-856.9162372174255" z="118.25073365873143"/>
<Force x="83.81135991157873" y="-420.22160214948235" z="120.49617758444118"/>
<Force x="667.2849511622113" y="-479.3499008055406" z="19.235509803262467"/>
<Force x="-518.4733820988154" y="686.0855400714895" z=".7330333001425071"/>
<Force x="-105.72909078977926" y="711.0668343181237" z="-508.83224725862544"/>
<Force x="285.3169350792583" y="-111.3017080984095" z="846.4662870215625"/>
<Force x="-397.35954566696427" y="24.822763038626682" z="299.33941483017065"/>
<Force x="-168.27998965108577" y="111.03576033655848" z="-823.1461667946043"/>
<Force x="804.8766900716053" y="16.1343934366816" z="-339.5169767615949"/>
<Force x="365.43779472611493" y="-647.3449172328129" z="-273.0689393022661"/>
<Force x="-649.0369250626607" y="-495.932522816643" z="316.96471153570917"/>
<Force x="-479.0134208353095" y="171.79575146148863" z="-16.236401291749388"/>
<Force x="-486.9195278219171" y="982.2625867919138" z="126.98779152341059"/>
<Force x="819.7259822269968" y="-78.87846748631466" z="109.09059215425442"/>
<Force x="354.68327095554434" y="-1392.2195705508666" z=".9942543256763656"/>
<Force x="-511.1150957463999" y="676.962020571201" z=".008253289146054499"/>
<Force x="293.38125738064446" y="452.02037971976495" z="502.28873599651723"/>
<Force x="293.2880784137142" y="452.0452787481405" z="-501.95014408580874"/>
</Forces>
</State>
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