"openmmapi/vscode:/vscode.git/clone" did not exist on "a85c24286b32fe4016f6dd74f89ad7c723e298d3"
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];
......
......@@ -23,8 +23,9 @@
*/
#ifdef WIN32
#define _USE_MATH_DEFINES // Needed to get M_PI
#define _USE_MATH_DEFINES // Needed to get M_PI
#endif
#include "AmoebaReferenceForce.h"
#include "AmoebaReferenceWcaDispersionForce.h"
#include <cmath>
......@@ -32,280 +33,240 @@
using std::vector;
using namespace OpenMM;
AmoebaReferenceWcaDispersionForce::AmoebaReferenceWcaDispersionForce(double epso, double epsh, double rmino, double rminh,
double awater, double shctd, double dispoff, double slevy) :
_epso(epso), _epsh(epsh), _rmino(rmino), _rminh(rminh), _awater(awater), _shctd(shctd), _dispoff(dispoff), _slevy(slevy) {
}
double AmoebaReferenceWcaDispersionForce::calculatePairIxn(double radiusI, double radiusK,
const Vec3& particleIPosition,
const Vec3& particleJPosition,
const double* const intermediateValues,
Vec3& force) const {
static const double PI = M_PI;
double xr = particleIPosition[0] - particleJPosition[0];
double yr = particleIPosition[1] - particleJPosition[1];
double zr = particleIPosition[2] - particleJPosition[2];
double r2 = xr*xr + yr*yr + zr*zr;
double r = sqrt(r2);
double r3 = r2*r;
double sK = radiusK*_shctd;
double sK2 = sK*sK;
double rmixo = intermediateValues[RMIXO];
double rmixo7 = intermediateValues[RMIXO7];
double emixo = intermediateValues[EMIXO];
double rmixh = intermediateValues[RMIXH];
double rmixh7 = intermediateValues[RMIXH7];
double emixh = intermediateValues[EMIXH];
double ao = intermediateValues[AO];
double ah = intermediateValues[AH];
double sum = 0.0;
double de = 0.0;
if (radiusI < (r + sK)) {
double rmax = (radiusI > (r - sK)) ? radiusI : (r - sK);
double lik = rmax;
double lik2 = lik*lik;
double lik3 = lik2*lik;
double lik4 = lik2*lik2;
AmoebaReferenceWcaDispersionForce::AmoebaReferenceWcaDispersionForce(double epso, double epsh, double rmino,
double rminh, double awater, double shctd,
double dispoff, double slevy) :
_epso(epso), _epsh(epsh), _rmino(rmino), _rminh(rminh), _awater(awater), _shctd(shctd), _dispoff(dispoff),
_slevy(slevy) {
}
if (lik < rmixo) {
static double integralBeforeRMin(double eps, double r, double r2, double sk2,
double lik2, double lik3, double lik4,
double uik2, double uik3, double uik4) {
return -eps * (4.0e0 * M_PI / (48.0e0 * r) *
(3.0e0 * (lik4 - uik4) - 8.0e0 * r * (lik3 - uik3) + 6.0e0 * (r2 - sk2) * (lik2 - uik2)));
}
double uik = (r + sK) < rmixo ? (r + sK) : rmixo;
double uik2 = uik*uik;
double uik3 = uik2*uik;
double uik4 = uik2*uik2;
static double
integralBeforeRminDerivative(double ri, double eps, double rmin, double r, double r2, double r3, double sk, double sk2,
double lik, double lik2, double lik3, double uik, double uik2, double uik3) {
double dl;
if (ri > r - sk) {
dl = (-lik2 + 2.0 * r2 + 2.0 * sk2) * lik2;
} else {
dl = (-lik3 + 4.0 * lik2 * r - 6.0 * lik * r2 + 2.0 * lik * sk2 + 4.0 * r3 - 4.0 * r * sk2) * lik;
}
double du;
if (r + sk > rmin) {
du = -(-uik2 + 2.0 * r2 + 2.0 * sk2) * uik2;
} else {
du = -(-uik3 + 4.0 * uik2 * r - 6.0 * uik * r2 + 2.0 * uik * sk2 + 4.0 * r3 - 4.0 * r * sk2) * uik;
}
return -eps * M_PI * (dl + du) / (4.0 * r2);
}
double term = 4.0*PI/(48.0*r)* (3.0*(lik4-uik4) - 8.0*r*(lik3-uik3) + 6.0*(r2-sK2)*(lik2-uik2));
static double integralAfterRmin(double eps, double rmin7, double r, double r2, double sk2,
double lik, double lik2, double lik3, double lik4, double lik5, double lik10,
double lik11, double lik12, double uik, double uik2, double uik3, double uik4,
double uik5, double uik10, double uik11, double uik12) {
double er7 = eps * rmin7;
double term = 4.0 * M_PI / (120.0 * r * lik5 * uik5)
* (15.0 * uik * lik * r * (uik4 - lik4)
- 10.0 * uik2 * lik2 * (uik3 - lik3)
+ 6.0 * (sk2 - r2) * (uik5 - lik5));
double term2 = 4.0 * M_PI / (2640.0 * r * lik12 * uik12)
* (120.0 * uik * lik * r * (uik11 - lik11)
- 66.0 * uik2 * lik2 * (uik10 - lik10)
+ 55.0 * (sk2 - r2) * (uik12 - lik12));
double idisp = -2.0 * er7 * term;
double irep = er7 * rmin7 * term2;
return irep + idisp;
}
double dl;
if (radiusI > (r - sK)) {
dl = -lik2 + 2.0*(r2 + sK2);
dl *= lik2;
} else {
dl = -lik3 + 4.0*lik2*r - 6.0*lik*r2 + 2.0*lik*sK2 + 4.0*r*(r2 - sK2);
dl *= lik;
}
static double integralAfterRminDerivative(double ri, double eps, double rmin, double rmin7, double rmax,
double r, double r2, double r3, double sk, double sk2, double lik,
double lik2, double lik3, double lik5, double lik6, double lik12,
double lik13, double uik, double uik2, double uik3, double uik6,
double uik13) {
double er7 = eps * rmin7;
double lowerTerm = lik2 * r + r3 - r * sk2;
double upperTerm = uik2 * r + r3 - r * sk2;
double dl;
if (ri > r - sk || rmax < rmin) {
dl = -(-5.0 * lik2 + 3.0 * r2 + 3.0 * sk2) / lik5;
} else {
dl = (5.0 * lik3 - 33.0 * lik * r2 - 3.0 * lik * sk2 + 15.0 * lowerTerm) / lik6;
}
double du = -(5.0 * uik3 - 33.0 * uik * r2 - 3.0 * uik * sk2 + 15.0 * upperTerm) / uik6;
double de = -2.0 * M_PI * er7 * (dl + du) / (15.0 * r2);
double du;
if ((r+sK) > rmixo) {
du = -uik2 + 2.0*(r2 + sK2);
du *= -uik2;
} else {
du = -uik3 + 4.0*uik2*r - 6.0*uik*r2 + 2.0*uik*sK2 + 4.0*r*(r2 - sK2);
du *= -uik;
}
de = -emixo*PI*(dl+du)/(4.0*r2);
sum += -emixo*term;
}
if (ri > r - sk || rmax < rmin) {
dl = -(-6.0 * lik2 + 5.0 * r2 + 5.0 * sk2) / lik12;
} else {
dl = (6.0 * lik3 - 125.0 * lik * r2 - 5.0 * lik * sk2 + 60.0 * lowerTerm) / lik13;
}
du = -(6.0 * uik3 - 125.0 * uik * r2 - 5.0 * uik * sk2 + 60.0 * upperTerm) / uik13;
de += M_PI * er7 * rmin7 * (dl + du) / (60.0 * r2);
if (lik < rmixh) {
double uik = (r + sK) < rmixh ? (r + sK) : rmixh;
double uik2 = uik*uik;
double uik3 = uik2*uik;
double uik4 = uik2*uik2;
double term = 4.0*PI / (48.0*r)*(3.0*(lik4-uik4) - 8.0*r*(lik3-uik3) + 6.0*(r2-sK2)*(lik2-uik2));
double dl;
if (radiusI > (r-sK)) {
dl = -lik2 + 2.0*(r2 + sK2);
dl *= lik2;
} else {
dl = -lik3 + 4.0*lik2*r - 6.0*lik*r2 + 2.0*lik*sK2 + 4.0*r*(r2 -sK2);
dl *= lik;
}
double du;
if (r+sK > rmixh) {
du = -uik2 + 2.0*(r2 + sK2);
du *= -uik2;
} else {
du = -uik3 +4.0*uik2*r - 6.0*uik*r2 + 2.0*uik*sK2 +4.0*r*(r2 - sK2);
du *= -uik;
}
de -= 2.0*emixh*PI*(dl+du)/(4.0*r2);
sum -= 2.0*emixh*term;
}
return de;
}
double uik = r + sK;
double uik2 = uik * uik;
double uik3 = uik2 * uik;
double uik4 = uik2 * uik2;
double uik5 = uik3 * uik2;
double uik6 = uik3 * uik3;
double uik10 = uik5 * uik5;
double uik11 = uik5 * uik6;
double uik12 = uik6 * uik6;
double uik13 = uik10 * uik3;
if (uik > rmixo) {
double lik = rmax > rmixo ? rmax : rmixo;
double lik2 = lik * lik;
double lik3 = lik2 * lik;
double lik4 = lik2 * lik2;
double lik5 = lik2 * lik3;
double lik6 = lik3 * lik3;
double lik10 = lik5 * lik5;
double lik11 = lik5 * lik6;
double lik12 = lik6 * lik6;
double lik13 = lik10 * lik3;
double term = 4.0*PI/(120.0*r*lik5*uik5)*(15.0*uik*lik*r*(uik4-lik4) - 10.0*uik2*lik2*(uik3-lik3) + 6.0*(sK2-r2)*(uik5-lik5));
double dl;
if (radiusI > (r-sK) || rmax < rmixo) {
dl = -5.0*lik2 + 3.0*(r2 + sK2);
dl /= -lik5;
} else {
dl = 5.0*lik3 - 33.0*lik*r2 - 3.0*lik*sK2 + 15.0*(lik2*r+r3-r*sK2);
dl /= lik6;
}
double du = 5.0*uik3 - 33.0*uik*r2 - 3.0*uik*sK2 + 15.0*(uik2*r+r3-r*sK2);
du /= -uik6;
double idisp = -2.0*ao*term;
de -= 2.0*ao*PI*(dl + du)/(15.0*r2);
term = 4.0*PI/(2640.0*r*lik12*uik12) * (120.0*uik*lik*r*(uik11-lik11) - 66.0*uik2*lik2*(uik10-lik10) + 55.0*(sK2-r2)*(uik12-lik12));
if (radiusI > (r-sK) || rmax < rmixo) {
dl = -6.0*lik2 + 5.0*r2 + 5.0*sK2;
dl /= -lik12;
} else {
dl = 6.0*lik3 - 125.0*lik*r2 - 5.0*lik*sK2 + 60.0*(lik2*r+r3-r*sK2);
dl /= lik13;
}
du = 6.0*uik3 - 125.0*uik*r2 - 5.0*uik*sK2 + 60.0*(uik2*r+r3-r*sK2);
du /= -uik13;
de += ao*rmixo7*PI*(dl + du)/(60.0*r2);
sum += ao*rmixo7*term + idisp;
static double interact(double factor, double ri, double sk, double rmix, double emix,
double r, double r2, double r3, Vec3 &force) {
double sum = 0.0;
// Nothing to do if the integral begins beyond r + sk (i.e. atom k does not exclude solvent)
if (ri < r + sk) {
// Zero out the derivative contribution of atom k.
double de = 0.0;
double sk2 = sk * sk;
// Compute the maximum of 1) the beginning of the integral and 2) closest edge of atom K.
double iStart = ri > r - sk ? ri : r - sk;
// Use this as the lower limit for integrating the constant eps value below Rmin.
double lik = iStart;
// Interaction with water from lik to Rmin; nothing to do if the lower limit is greater than Rmin.
if (lik < rmix) {
double lik2 = lik * lik;
double lik3 = lik2 * lik;
double lik4 = lik3 * lik;
// Upper limit is the minimum of Rmin and the farthest edge of atom K.
double uik = r + sk < rmix ? r + sk : rmix;
double uik2 = uik * uik;
double uik3 = uik2 * uik;
double uik4 = uik3 * uik;
sum = integralBeforeRMin(emix, r, r2, sk2, lik2, lik3, lik4, uik2, uik3, uik4);
de = integralBeforeRminDerivative(ri, emix, rmix, r, r2, r3, sk, sk2, lik, lik2, lik3, uik, uik2, uik3);
}
if (uik > rmixh) {
lik = rmax > rmixh ? rmax : rmixh;
lik2 = lik * lik;
lik3 = lik2 * lik;
lik4 = lik2 * lik2;
double lik5 = lik2 * lik3;
double lik6 = lik3 * lik3;
// Upper limit the variable part of Uwca always the farthest edge of atom K.
double uik = r + sk;
// Interaction with water beyond Rmin, from lik to uik = r + sk.
if (uik > rmix) {
// Start the integral at the max of 1) iStart and 2) Rmin.
lik = iStart > rmix ? iStart : rmix;
double lik2 = lik * lik;
double lik3 = lik2 * lik;
double lik4 = lik3 * lik;
double lik5 = lik4 * lik;
double lik6 = lik5 * lik;
double lik10 = lik5 * lik5;
double lik11 = lik5 * lik6;
double lik12 = lik6 * lik6;
double lik13 = lik3 * lik10;
double term = 4.0*PI / (120.0*r*lik5*uik5)*(15.0*uik*lik*r*(uik4-lik4) - 10.0*uik2*lik2*(uik3-lik3) + 6.0*(sK2-r2)*(uik5-lik5));
double dl;
if (radiusI > (r-sK) || rmax < rmixh) {
dl = -5.0*lik2 + 3.0*(r2 + sK2);
dl /= -lik5;
} else {
dl = 5.0*lik3 - 33.0*lik*r2 - 3.0*lik*sK2 + 15.0*(lik2*r+r3-r*sK2);
dl /= lik6;
}
double du = 5.0*uik3 - 33.0*uik*r2 - 3.0*uik*sK2 + 15.0*(uik2*r+r3-r*sK2);
du /= -uik6;
double idisp = -4.0*ah*term;
de = de - 4.0*ah*PI*(dl + du)/(15.0*r2);
term = 4.0*PI / (2640.0*r*lik12*uik12)* (120.0*uik*lik*r*(uik11-lik11)- 66.0*uik2*lik2*(uik10-lik10)+ 55.0*(sK2-r2)*(uik12-lik12));
if (radiusI > (r-sK) || rmax < rmixh) {
dl = -6.0*lik2 + 5.0*r2 + 5.0*sK2;
dl = -dl / lik12;
} else {;
dl = 6.0*lik3 - 125.0*lik*r2 - 5.0*lik*sK2 + 60.0*(lik2*r+r3-r*sK2);
dl = dl / lik13;
}
du = 6.0*uik3 - 125.0*uik*r2 - 5.0*uik*sK2 + 60.0*(uik2*r+r3-r*sK2);
du /= -uik13;
double irep = 2.0*ah*rmixh7*term;
de += ah*rmixh7*PI*(dl+du)/(30.0*r2);
sum += irep + idisp;
double lik11 = lik10 * lik;
double lik12 = lik11 * lik;
double uik2 = uik * uik;
double uik3 = uik2 * uik;
double uik4 = uik3 * uik;
double uik5 = uik4 * uik;
double uik10 = uik5 * uik5;
double uik11 = uik10 * uik;
double uik12 = uik11 * uik;
double rmix3 = rmix * rmix * rmix;
double rmix7 = rmix3 * rmix3 * rmix;
sum += integralAfterRmin(emix, rmix7, r, r2, sk2, lik, lik2, lik3, lik4, lik5, lik10, lik11, lik12, uik,
uik2, uik3, uik4, uik5, uik10, uik11, uik12);
double lik13 = lik12 * lik;
double uik6 = uik5 * uik;
double uik13 = uik12 * uik;
de += integralAfterRminDerivative(ri, emix, rmix, rmix7, iStart, r, r2, r3, sk, sk2, lik, lik2, lik3,
lik5, lik6, lik12, lik13, uik, uik2, uik3, uik6, uik13);
}
// Increment the individual dispersion gradient components.
de *= factor / r;
force[0] += de;
force[1] += de;
force[2] += de;
}
return factor * sum;
}
// increment the individual dispersion gradient components
de *= (_slevy*_awater)/r;
force[0] = de*xr;
force[1] = de*yr;
force[2] = de*zr;
double AmoebaReferenceWcaDispersionForce::calculatePairIxn(double radiusJ,
const Vec3 &particleIPosition,
const Vec3 &particleJPosition,
const double *const intermediateValues,
Vec3 &force) const {
// Atomic separation
double xr = particleIPosition[0] - particleJPosition[0];
double yr = particleIPosition[1] - particleJPosition[1];
double zr = particleIPosition[2] - particleJPosition[2];
double r2 = xr * xr + yr * yr + zr * zr;
double r = sqrt(r2);
double r3 = r2 * r;
// Parameters for atom i with water oxygen.
double rmixo = intermediateValues[RMIXO];
double emixo = intermediateValues[EMIXO];
// Parameters for atom i with water hydrogen.
double rmixh = intermediateValues[RMIXH];
double emixh = intermediateValues[EMIXH];
// Start of integration of dispersion for atom i with water oxygen.
double riO = rmixo / 2.0e0 + _dispoff;
double nO = 1.0e0;
// Start of integration of dispersion for atom i with water hydrogen.
double riH = rmixh / 2.0e0 + _dispoff;
double nH = 2.0e0;
// Atom k blocks the interaction of atom i with solvent.
double sJ = radiusJ * _shctd;
double sum = interact(nO, riO, sJ, rmixo, emixo, r, r2, r3, force) +
interact(nH, riH, sJ, rmixh, emixh, r, r2, r3, force);
force[0] *= xr;
force[1] *= yr;
force[2] *= zr;
return sum;
}
double AmoebaReferenceWcaDispersionForce::calculateForceAndEnergy(int numParticles,
const vector<Vec3>& particlePositions,
const std::vector<double>& radii,
const std::vector<double>& epsilons,
const vector<Vec3> &particlePositions,
const std::vector<double> &radii,
const std::vector<double> &epsilons,
double totalMaximumDispersionEnergy,
vector<Vec3>& forces) const {
// loop over all ixns
double energy = 0.0;
double rmino2 = _rmino*_rmino;
double rmino3 = rmino2*_rmino;
double rminh2 = _rminh*_rminh;
double rminh3 = rminh2*_rminh;
vector<Vec3> &forces) const {
double energy = 0.0;
double rmino2 = _rmino * _rmino;
double rmino3 = rmino2 * _rmino;
double rminh2 = _rminh * _rminh;
double rminh3 = rminh2 * _rminh;
double intermediateValues[LastIntermediateValueIndex];
// loop over all ixns
for (unsigned int ii = 0; ii < static_cast<unsigned int>(numParticles); ii++) {
double epsi = epsilons[ii];
double rmini = radii[ii];
double denominator = sqrt(_epso) + sqrt(epsi);
double emixo = 4.0*_epso*epsi/(denominator*denominator);
intermediateValues[EMIXO] = emixo;
double rminI2 = rmini*rmini;
double rminI3 = rminI2*rmini;
double rmixo = 2.0*(rmino3 + rminI3) / (rmino2 + rminI2);
intermediateValues[RMIXO] = rmixo;
double rmixo7 = rmixo*rmixo*rmixo;
rmixo7 = rmixo7*rmixo7*rmixo;
intermediateValues[RMIXO7] = rmixo7;
double epsi = epsilons[ii];
double rmini = radii[ii];
double rminI2 = rmini * rmini;
double rminI3 = rminI2 * rmini;
intermediateValues[AO] = emixo*rmixo7;
double denominator = sqrt(_epso) + sqrt(epsi);
double emixo = 4.0 * _epso * epsi / (denominator * denominator);
intermediateValues[EMIXO] = emixo;
denominator = sqrt(_epsh) + sqrt(epsi);
double rmixo = 2.0 * (rmino3 + rminI3) / (rmino2 + rminI2);
intermediateValues[RMIXO] = rmixo;
double emixh = 4.0*_epsh*epsi/ (denominator*denominator);
denominator = sqrt(_epsh) + sqrt(epsi);
double emixh = 4.0 * _epsh * epsi / (denominator * denominator);
intermediateValues[EMIXH] = emixh;
double rmixh = 2.0 * (rminh3 + rminI3) / (rminh2 + rminI2);
double rmixh = 2.0 * (rminh3 + rminI3) / (rminh2 + rminI2);
intermediateValues[RMIXH] = rmixh;
double rmixh7 = rmixh*rmixh*rmixh;
rmixh7 = rmixh7*rmixh7*rmixh;
intermediateValues[RMIXH7] = rmixh7;
intermediateValues[AH] = emixh*rmixh7;
// Remove dispersion for atom i by atom j.
for (unsigned int jj = 0; jj < static_cast<unsigned int>(numParticles); jj++) {
if (ii == jj)continue;
if (ii == jj) continue;
Vec3 force;
energy += calculatePairIxn(rmini, radii[jj],
particlePositions[ii], particlePositions[jj],
Vec3 force = Vec3();
energy += calculatePairIxn(radii[jj], particlePositions[ii], particlePositions[jj],
intermediateValues, force);
force[0] *= _slevy * _awater;
force[1] *= _slevy * _awater;
force[2] *= _slevy * _awater;
forces[ii][0] += force[0];
forces[ii][1] += force[1];
forces[ii][2] += force[2];
......@@ -313,11 +274,12 @@ double AmoebaReferenceWcaDispersionForce::calculateForceAndEnergy(int numParticl
forces[jj][0] -= force[0];
forces[jj][1] -= force[1];
forces[jj][2] -= force[2];
}
}
energy = totalMaximumDispersionEnergy - _slevy*_awater*energy;
energy = totalMaximumDispersionEnergy - _slevy * _awater * energy;
return energy;
}
......@@ -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