"examples/vscode:/vscode.git/clone" did not exist on "ba5c5e5404d2d3fdee02e163fc75a44bd960935f"
Commit 77b9b7ba authored by peastman's avatar peastman Committed by GitHub
Browse files

Merge pull request #1534 from saurabhbelsare/master

Added functions to get per atom lab frame permanent dipoles and per atom total dipole moments for AMOEBA
parents 339167f4 92d86ead
...@@ -370,7 +370,13 @@ public: ...@@ -370,7 +370,13 @@ public:
* This can be overridden by explicitly setting an alpha parameter and grid dimensions to use. * This can be overridden by explicitly setting an alpha parameter and grid dimensions to use.
*/ */
void setEwaldErrorTolerance(double tol); void setEwaldErrorTolerance(double tol);
/**
* Get the fixed dipole moments of all particles in the global reference frame.
*
* @param context the Context for which to get the fixed dipoles
* @param[out] dipoles the fixed dipole moment of particle i is stored into the i'th element
*/
void getLabFramePermanentDipoles(Context& context, std::vector<Vec3>& dipoles);
/** /**
* Get the induced dipole moments of all particles. * Get the induced dipole moments of all particles.
* *
...@@ -379,6 +385,14 @@ public: ...@@ -379,6 +385,14 @@ public:
*/ */
void getInducedDipoles(Context& context, std::vector<Vec3>& dipoles); void getInducedDipoles(Context& context, std::vector<Vec3>& dipoles);
/**
* Get the total dipole moments (fixed plus induced) of all particles.
*
* @param context the Context for which to get the total dipoles
* @param[out] dipoles the total dipole moment of particle i is stored into the i'th element
*/
void getTotalDipoles(Context& context, std::vector<Vec3>& dipoles);
/** /**
* Get the electrostatic potential. * Get the electrostatic potential.
* *
......
...@@ -59,7 +59,7 @@ public: ...@@ -59,7 +59,7 @@ public:
/** /**
* Initialize the kernel. * Initialize the kernel.
* *
* @param system the System this kernel will be applied to * @param system the System this kernel will be applied to
* @param force the AmoebaBondForce this kernel will be used for * @param force the AmoebaBondForce this kernel will be used for
*/ */
...@@ -99,7 +99,7 @@ public: ...@@ -99,7 +99,7 @@ public:
/** /**
* Initialize the kernel. * Initialize the kernel.
* *
* @param system the System this kernel will be applied to * @param system the System this kernel will be applied to
* @param force the AmoebaAngleForce this kernel will be used for * @param force the AmoebaAngleForce this kernel will be used for
*/ */
...@@ -139,7 +139,7 @@ public: ...@@ -139,7 +139,7 @@ public:
/** /**
* Initialize the kernel. * Initialize the kernel.
* *
* @param system the System this kernel will be applied to * @param system the System this kernel will be applied to
* @param force the AmoebaInPlaneAngleForce this kernel will be used for * @param force the AmoebaInPlaneAngleForce this kernel will be used for
*/ */
...@@ -179,7 +179,7 @@ public: ...@@ -179,7 +179,7 @@ public:
/** /**
* Initialize the kernel. * Initialize the kernel.
* *
* @param system the System this kernel will be applied to * @param system the System this kernel will be applied to
* @param force the PiTorsionForce this kernel will be used for * @param force the PiTorsionForce this kernel will be used for
*/ */
...@@ -219,7 +219,7 @@ public: ...@@ -219,7 +219,7 @@ public:
/** /**
* Initialize the kernel. * Initialize the kernel.
* *
* @param system the System this kernel will be applied to * @param system the System this kernel will be applied to
* @param force the StretchBendForce this kernel will be used for * @param force the StretchBendForce this kernel will be used for
*/ */
...@@ -259,7 +259,7 @@ public: ...@@ -259,7 +259,7 @@ public:
/** /**
* Initialize the kernel. * Initialize the kernel.
* *
* @param system the System this kernel will be applied to * @param system the System this kernel will be applied to
* @param force the OutOfPlaneBendForce this kernel will be used for * @param force the OutOfPlaneBendForce this kernel will be used for
*/ */
...@@ -299,7 +299,7 @@ public: ...@@ -299,7 +299,7 @@ public:
/** /**
* Initialize the kernel. * Initialize the kernel.
* *
* @param system the System this kernel will be applied to * @param system the System this kernel will be applied to
* @param force the TorsionTorsionForce this kernel will be used for * @param force the TorsionTorsionForce this kernel will be used for
*/ */
...@@ -332,7 +332,7 @@ public: ...@@ -332,7 +332,7 @@ public:
/** /**
* Initialize the kernel. * Initialize the kernel.
* *
* @param system the System this kernel will be applied to * @param system the System this kernel will be applied to
* @param force the MultipoleForce this kernel will be used for * @param force the MultipoleForce this kernel will be used for
*/ */
...@@ -348,7 +348,9 @@ public: ...@@ -348,7 +348,9 @@ public:
*/ */
virtual double execute(ContextImpl& context, bool includeForces, bool includeEnergy) = 0; virtual double execute(ContextImpl& context, bool includeForces, bool includeEnergy) = 0;
virtual void getLabFramePermanentDipoles(ContextImpl& context, std::vector<Vec3>& dipoles) = 0;
virtual void getInducedDipoles(ContextImpl& context, std::vector<Vec3>& dipoles) = 0; virtual void getInducedDipoles(ContextImpl& context, std::vector<Vec3>& dipoles) = 0;
virtual void getTotalDipoles(ContextImpl& context, std::vector<Vec3>& dipoles) = 0;
virtual void getElectrostaticPotential(ContextImpl& context, const std::vector< Vec3 >& inputGrid, virtual void getElectrostaticPotential(ContextImpl& context, const std::vector< Vec3 >& inputGrid,
std::vector< double >& outputElectrostaticPotential) = 0; std::vector< double >& outputElectrostaticPotential) = 0;
...@@ -364,7 +366,7 @@ public: ...@@ -364,7 +366,7 @@ public:
/** /**
* Get the parameters being used for PME. * Get the parameters being used for PME.
* *
* @param alpha the separation parameter * @param alpha the separation parameter
* @param nx the number of grid points along the X axis * @param nx the number of grid points along the X axis
* @param ny the number of grid points along the Y axis * @param ny the number of grid points along the Y axis
...@@ -389,7 +391,7 @@ public: ...@@ -389,7 +391,7 @@ public:
/** /**
* Initialize the kernel. * Initialize the kernel.
* *
* @param system the System this kernel will be applied to * @param system the System this kernel will be applied to
* @param force the GBSAOBCForce this kernel will be used for * @param force the GBSAOBCForce this kernel will be used for
*/ */
...@@ -429,7 +431,7 @@ public: ...@@ -429,7 +431,7 @@ public:
/** /**
* Initialize the kernel. * Initialize the kernel.
* *
* @param system the System this kernel will be applied to * @param system the System this kernel will be applied to
* @param force the GBSAOBCForce this kernel will be used for * @param force the GBSAOBCForce this kernel will be used for
*/ */
...@@ -469,7 +471,7 @@ public: ...@@ -469,7 +471,7 @@ public:
/** /**
* Initialize the kernel. * Initialize the kernel.
* *
* @param system the System this kernel will be applied to * @param system the System this kernel will be applied to
* @param force the GBSAOBCForce this kernel will be used for * @param force the GBSAOBCForce this kernel will be used for
*/ */
......
...@@ -64,7 +64,7 @@ public: ...@@ -64,7 +64,7 @@ public:
/** /**
* Get the CovalentMap for an atom * Get the CovalentMap for an atom
* *
* @param force AmoebaMultipoleForce force reference * @param force AmoebaMultipoleForce force reference
* @param index the index of the atom for which to set parameters * @param index the index of the atom for which to set parameters
* @param minCovalentIndex minimum covalent index * @param minCovalentIndex minimum covalent index
...@@ -76,13 +76,14 @@ public: ...@@ -76,13 +76,14 @@ public:
/** /**
* Get the covalent degree for the CovalentEnd lists * Get the covalent degree for the CovalentEnd lists
* *
* @param force AmoebaMultipoleForce force reference * @param force AmoebaMultipoleForce force reference
* @param covalentDegree covalent degrees for the CovalentEnd lists * @param covalentDegree covalent degrees for the CovalentEnd lists
*/ */
static void getCovalentDegree(const AmoebaMultipoleForce& force, std::vector<int>& covalentDegree); static void getCovalentDegree(const AmoebaMultipoleForce& force, std::vector<int>& covalentDegree);
void getLabFramePermanentDipoles(ContextImpl& context, std::vector<Vec3>& dipoles);
void getInducedDipoles(ContextImpl& context, std::vector<Vec3>& dipoles); void getInducedDipoles(ContextImpl& context, std::vector<Vec3>& dipoles);
void getTotalDipoles(ContextImpl& context, std::vector<Vec3>& dipoles);
void getElectrostaticPotential(ContextImpl& context, const std::vector< Vec3 >& inputGrid, void getElectrostaticPotential(ContextImpl& context, const std::vector< Vec3 >& inputGrid,
std::vector< double >& outputElectrostaticPotential); std::vector< double >& outputElectrostaticPotential);
...@@ -90,7 +91,7 @@ public: ...@@ -90,7 +91,7 @@ public:
void getSystemMultipoleMoments(ContextImpl& context, std::vector< double >& outputMultipoleMoments); void getSystemMultipoleMoments(ContextImpl& context, std::vector< double >& outputMultipoleMoments);
void updateParametersInContext(ContextImpl& context); void updateParametersInContext(ContextImpl& context);
void getPMEParameters(double& alpha, int& nx, int& ny, int& nz) const; void getPMEParameters(double& alpha, int& nx, int& ny, int& nz) const;
private: private:
const AmoebaMultipoleForce& owner; const AmoebaMultipoleForce& owner;
......
...@@ -147,13 +147,13 @@ void AmoebaMultipoleForce::setEwaldErrorTolerance(double tol) { ...@@ -147,13 +147,13 @@ void AmoebaMultipoleForce::setEwaldErrorTolerance(double tol) {
ewaldErrorTol = tol; ewaldErrorTol = tol;
} }
int AmoebaMultipoleForce::addMultipole(double charge, const std::vector<double>& molecularDipole, const std::vector<double>& molecularQuadrupole, int axisType, int AmoebaMultipoleForce::addMultipole(double charge, const std::vector<double>& molecularDipole, const std::vector<double>& molecularQuadrupole, int axisType,
int multipoleAtomZ, int multipoleAtomX, int multipoleAtomY, double thole, double dampingFactor, double polarity) { int multipoleAtomZ, int multipoleAtomX, int multipoleAtomY, double thole, double dampingFactor, double polarity) {
multipoles.push_back(MultipoleInfo(charge, molecularDipole, molecularQuadrupole, axisType, multipoleAtomZ, multipoleAtomX, multipoleAtomY, thole, dampingFactor, polarity)); multipoles.push_back(MultipoleInfo(charge, molecularDipole, molecularQuadrupole, axisType, multipoleAtomZ, multipoleAtomX, multipoleAtomY, thole, dampingFactor, polarity));
return multipoles.size()-1; return multipoles.size()-1;
} }
void AmoebaMultipoleForce::getMultipoleParameters(int index, double& charge, std::vector<double>& molecularDipole, std::vector<double>& molecularQuadrupole, void AmoebaMultipoleForce::getMultipoleParameters(int index, double& charge, std::vector<double>& molecularDipole, std::vector<double>& molecularQuadrupole,
int& axisType, int& multipoleAtomZ, int& multipoleAtomX, int& multipoleAtomY, double& thole, double& dampingFactor, double& polarity) const { int& axisType, int& multipoleAtomZ, int& multipoleAtomX, int& multipoleAtomY, double& thole, double& dampingFactor, double& polarity) const {
charge = multipoles[index].charge; charge = multipoles[index].charge;
...@@ -183,7 +183,7 @@ void AmoebaMultipoleForce::getMultipoleParameters(int index, double& charge, std ...@@ -183,7 +183,7 @@ void AmoebaMultipoleForce::getMultipoleParameters(int index, double& charge, std
polarity = multipoles[index].polarity; polarity = multipoles[index].polarity;
} }
void AmoebaMultipoleForce::setMultipoleParameters(int index, double charge, const std::vector<double>& molecularDipole, const std::vector<double>& molecularQuadrupole, void AmoebaMultipoleForce::setMultipoleParameters(int index, double charge, const std::vector<double>& molecularDipole, const std::vector<double>& molecularQuadrupole,
int axisType, int multipoleAtomZ, int multipoleAtomX, int multipoleAtomY, double thole, double dampingFactor, double polarity) { int axisType, int multipoleAtomZ, int multipoleAtomX, int multipoleAtomY, double thole, double dampingFactor, double polarity) {
multipoles[index].charge = charge; multipoles[index].charge = charge;
...@@ -250,6 +250,14 @@ void AmoebaMultipoleForce::getInducedDipoles(Context& context, vector<Vec3>& dip ...@@ -250,6 +250,14 @@ void AmoebaMultipoleForce::getInducedDipoles(Context& context, vector<Vec3>& dip
dynamic_cast<AmoebaMultipoleForceImpl&>(getImplInContext(context)).getInducedDipoles(getContextImpl(context), dipoles); dynamic_cast<AmoebaMultipoleForceImpl&>(getImplInContext(context)).getInducedDipoles(getContextImpl(context), dipoles);
} }
void AmoebaMultipoleForce::getLabFramePermanentDipoles(Context& context, vector<Vec3>& dipoles) {
dynamic_cast<AmoebaMultipoleForceImpl&>(getImplInContext(context)).getLabFramePermanentDipoles(getContextImpl(context), dipoles);
}
void AmoebaMultipoleForce::getTotalDipoles(Context& context, vector<Vec3>& dipoles) {
dynamic_cast<AmoebaMultipoleForceImpl&>(getImplInContext(context)).getTotalDipoles(getContextImpl(context), dipoles);
}
void AmoebaMultipoleForce::getElectrostaticPotential(const std::vector< Vec3 >& inputGrid, Context& context, std::vector< double >& outputElectrostaticPotential) { void AmoebaMultipoleForce::getElectrostaticPotential(const std::vector< Vec3 >& inputGrid, Context& context, std::vector< double >& outputElectrostaticPotential) {
dynamic_cast<AmoebaMultipoleForceImpl&>(getImplInContext(context)).getElectrostaticPotential(getContextImpl(context), inputGrid, outputElectrostaticPotential); dynamic_cast<AmoebaMultipoleForceImpl&>(getImplInContext(context)).getElectrostaticPotential(getContextImpl(context), inputGrid, outputElectrostaticPotential);
} }
......
...@@ -61,7 +61,7 @@ void AmoebaMultipoleForceImpl::initialize(ContextImpl& context) { ...@@ -61,7 +61,7 @@ void AmoebaMultipoleForceImpl::initialize(ContextImpl& context) {
double cutoff = owner.getCutoffDistance(); double cutoff = owner.getCutoffDistance();
if (cutoff > 0.5*boxVectors[0][0] || cutoff > 0.5*boxVectors[1][1] || cutoff > 0.5*boxVectors[2][2]) if (cutoff > 0.5*boxVectors[0][0] || cutoff > 0.5*boxVectors[1][1] || cutoff > 0.5*boxVectors[2][2])
throw OpenMMException("AmoebaMultipoleForce: The cutoff distance cannot be greater than half the periodic box size."); throw OpenMMException("AmoebaMultipoleForce: The cutoff distance cannot be greater than half the periodic box size.");
} }
double quadrupoleValidationTolerance = 1.0e-05; double quadrupoleValidationTolerance = 1.0e-05;
for (int ii = 0; ii < system.getNumParticles(); ii++) { for (int ii = 0; ii < system.getNumParticles(); ii++) {
...@@ -170,7 +170,7 @@ void AmoebaMultipoleForceImpl::getCovalentRange(const AmoebaMultipoleForce& forc ...@@ -170,7 +170,7 @@ void AmoebaMultipoleForceImpl::getCovalentRange(const AmoebaMultipoleForce& forc
*maxCovalentIndex = covalentList[ii]; *maxCovalentIndex = covalentList[ii];
} }
} }
} }
return; return;
} }
...@@ -179,14 +179,22 @@ void AmoebaMultipoleForceImpl::getCovalentDegree(const AmoebaMultipoleForce& for ...@@ -179,14 +179,22 @@ void AmoebaMultipoleForceImpl::getCovalentDegree(const AmoebaMultipoleForce& for
const int* CovalentDegrees = AmoebaMultipoleForceImpl::getCovalentDegrees(); const int* CovalentDegrees = AmoebaMultipoleForceImpl::getCovalentDegrees();
for (unsigned int kk = 0; kk < AmoebaMultipoleForce::CovalentEnd; kk++) { for (unsigned int kk = 0; kk < AmoebaMultipoleForce::CovalentEnd; kk++) {
covalentDegree[kk] = CovalentDegrees[kk]; covalentDegree[kk] = CovalentDegrees[kk];
} }
return; return;
} }
void AmoebaMultipoleForceImpl::getLabFramePermanentDipoles(ContextImpl& context, vector<Vec3>& dipoles) {
kernel.getAs<CalcAmoebaMultipoleForceKernel>().getLabFramePermanentDipoles(context, dipoles);
}
void AmoebaMultipoleForceImpl::getInducedDipoles(ContextImpl& context, vector<Vec3>& dipoles) { void AmoebaMultipoleForceImpl::getInducedDipoles(ContextImpl& context, vector<Vec3>& dipoles) {
kernel.getAs<CalcAmoebaMultipoleForceKernel>().getInducedDipoles(context, dipoles); kernel.getAs<CalcAmoebaMultipoleForceKernel>().getInducedDipoles(context, dipoles);
} }
void AmoebaMultipoleForceImpl::getTotalDipoles(ContextImpl& context, vector<Vec3>& dipoles) {
kernel.getAs<CalcAmoebaMultipoleForceKernel>().getTotalDipoles(context, dipoles);
}
void AmoebaMultipoleForceImpl::getElectrostaticPotential(ContextImpl& context, const std::vector< Vec3 >& inputGrid, void AmoebaMultipoleForceImpl::getElectrostaticPotential(ContextImpl& context, const std::vector< Vec3 >& inputGrid,
std::vector< double >& outputElectrostaticPotential) { std::vector< double >& outputElectrostaticPotential) {
kernel.getAs<CalcAmoebaMultipoleForceKernel>().getElectrostaticPotential(context, inputGrid, outputElectrostaticPotential); kernel.getAs<CalcAmoebaMultipoleForceKernel>().getElectrostaticPotential(context, inputGrid, outputElectrostaticPotential);
......
...@@ -1992,6 +1992,27 @@ void CudaCalcAmoebaMultipoleForceKernel::ensureMultipolesValid(ContextImpl& cont ...@@ -1992,6 +1992,27 @@ void CudaCalcAmoebaMultipoleForceKernel::ensureMultipolesValid(ContextImpl& cont
context.calcForcesAndEnergy(false, false, -1); context.calcForcesAndEnergy(false, false, -1);
} }
void CudaCalcAmoebaMultipoleForceKernel::getLabFramePermanentDipoles(ContextImpl& context, vector<Vec3>& dipoles) {
ensureMultipolesValid(context);
int numParticles = cu.getNumAtoms();
dipoles.resize(numParticles);
const vector<int>& order = cu.getAtomIndex();
if (cu.getUseDoublePrecision()) {
vector<double> labDipoleVec;
labFrameDipoles->download(labDipoleVec);
for (int i = 0; i < numParticles; i++)
dipoles[order[i]] = Vec3(labDipoleVec[3*i], labDipoleVec[3*i+1], labDipoleVec[3*i+2]);
}
else {
vector<float> labDipoleVec;
labFrameDipoles->download(labDipoleVec);
for (int i = 0; i < numParticles; i++)
dipoles[order[i]] = Vec3(labDipoleVec[3*i], labDipoleVec[3*i+1], labDipoleVec[3*i+2]);
}
}
void CudaCalcAmoebaMultipoleForceKernel::getInducedDipoles(ContextImpl& context, vector<Vec3>& dipoles) { void CudaCalcAmoebaMultipoleForceKernel::getInducedDipoles(ContextImpl& context, vector<Vec3>& dipoles) {
ensureMultipolesValid(context); ensureMultipolesValid(context);
int numParticles = cu.getNumAtoms(); int numParticles = cu.getNumAtoms();
...@@ -2011,6 +2032,48 @@ void CudaCalcAmoebaMultipoleForceKernel::getInducedDipoles(ContextImpl& context, ...@@ -2011,6 +2032,48 @@ void CudaCalcAmoebaMultipoleForceKernel::getInducedDipoles(ContextImpl& context,
} }
} }
void CudaCalcAmoebaMultipoleForceKernel::getTotalDipoles(ContextImpl& context, vector<Vec3>& dipoles) {
ensureMultipolesValid(context);
int numParticles = cu.getNumAtoms();
dipoles.resize(numParticles);
const vector<int>& order = cu.getAtomIndex();
if (cu.getUseDoublePrecision()) {
vector<double4> posqVec;
vector<double> labDipoleVec;
vector<double> inducedDipoleVec;
double totalDipoleVecX;
double totalDipoleVecY;
double totalDipoleVecZ;
inducedDipole->download(inducedDipoleVec);
labFrameDipoles->download(labDipoleVec);
cu.getPosq().download(posqVec);
for (int i = 0; i < numParticles; i++) {
totalDipoleVecX = labDipoleVec[3*i] + inducedDipoleVec[3*i];
totalDipoleVecY = labDipoleVec[3*i+1] + inducedDipoleVec[3*i+1];
totalDipoleVecZ = labDipoleVec[3*i+2] + inducedDipoleVec[3*i+2];
dipoles[order[i]] = Vec3(totalDipoleVecX, totalDipoleVecY, totalDipoleVecZ);
}
}
else {
vector<float4> posqVec;
vector<float> labDipoleVec;
vector<float> inducedDipoleVec;
float totalDipoleVecX;
float totalDipoleVecY;
float totalDipoleVecZ;
inducedDipole->download(inducedDipoleVec);
labFrameDipoles->download(labDipoleVec);
cu.getPosq().download(posqVec);
for (int i = 0; i < numParticles; i++) {
totalDipoleVecX = labDipoleVec[3*i] + inducedDipoleVec[3*i];
totalDipoleVecY = labDipoleVec[3*i+1] + inducedDipoleVec[3*i+1];
totalDipoleVecZ = labDipoleVec[3*i+2] + inducedDipoleVec[3*i+2];
dipoles[order[i]] = Vec3(totalDipoleVecX, totalDipoleVecY, totalDipoleVecZ);
}
}
}
void CudaCalcAmoebaMultipoleForceKernel::getElectrostaticPotential(ContextImpl& context, const vector<Vec3>& inputGrid, vector<double>& outputElectrostaticPotential) { void CudaCalcAmoebaMultipoleForceKernel::getElectrostaticPotential(ContextImpl& context, const vector<Vec3>& inputGrid, vector<double>& outputElectrostaticPotential) {
ensureMultipolesValid(context); ensureMultipolesValid(context);
int numPoints = inputGrid.size(); int numPoints = inputGrid.size();
...@@ -2164,6 +2227,7 @@ void CudaCalcAmoebaMultipoleForceKernel::computeSystemMultipoleMoments(ContextIm ...@@ -2164,6 +2227,7 @@ void CudaCalcAmoebaMultipoleForceKernel::computeSystemMultipoleMoments(ContextIm
outputMultipoleMoments[12] = 100.0*zzqdp*debye; outputMultipoleMoments[12] = 100.0*zzqdp*debye;
} }
void CudaCalcAmoebaMultipoleForceKernel::getSystemMultipoleMoments(ContextImpl& context, vector<double>& outputMultipoleMoments) { void CudaCalcAmoebaMultipoleForceKernel::getSystemMultipoleMoments(ContextImpl& context, vector<double>& outputMultipoleMoments) {
ensureMultipolesValid(context); ensureMultipolesValid(context);
if (cu.getUseDoublePrecision()) if (cu.getUseDoublePrecision())
......
...@@ -328,6 +328,13 @@ public: ...@@ -328,6 +328,13 @@ public:
* @return the potential energy due to the force * @return the potential energy due to the force
*/ */
double execute(ContextImpl& context, bool includeForces, bool includeEnergy); double execute(ContextImpl& context, bool includeForces, bool includeEnergy);
/**
* Get the LabFrame dipole moments of all particles.
*
* @param context the Context for which to get the induced dipoles
* @param dipoles the induced dipole moment of particle i is stored into the i'th element
*/
void getLabFramePermanentDipoles(ContextImpl& context, std::vector<Vec3>& dipoles);
/** /**
* Get the induced dipole moments of all particles. * Get the induced dipole moments of all particles.
* *
...@@ -335,6 +342,13 @@ public: ...@@ -335,6 +342,13 @@ public:
* @param dipoles the induced dipole moment of particle i is stored into the i'th element * @param dipoles the induced dipole moment of particle i is stored into the i'th element
*/ */
void getInducedDipoles(ContextImpl& context, std::vector<Vec3>& dipoles); void getInducedDipoles(ContextImpl& context, std::vector<Vec3>& dipoles);
/**
* Get the total dipole moments of all particles.
*
* @param context the Context for which to get the induced dipoles
* @param dipoles the induced dipole moment of particle i is stored into the i'th element
*/
void getTotalDipoles(ContextImpl& context, std::vector<Vec3>& dipoles);
/** /**
* Execute the kernel to calculate the electrostatic potential * Execute the kernel to calculate the electrostatic potential
* *
......
...@@ -2666,6 +2666,76 @@ static void testParticleInducedDipoles() { ...@@ -2666,6 +2666,76 @@ static void testParticleInducedDipoles() {
ASSERT_EQUAL_VEC(expectedDipole[i], dipole[i], 1e-4); ASSERT_EQUAL_VEC(expectedDipole[i], dipole[i], 1e-4);
} }
// test querying particle lab frame permanent dipoles
static void testParticleLabFramePermanentDipoles() {
int numberOfParticles = 8;
int inputPmeGridDimension = 0;
double cutoff = 9000000.0;
std::vector<Vec3> forces;
double energy;
System system;
AmoebaMultipoleForce* amoebaMultipoleForce = new AmoebaMultipoleForce();;
setupMultipoleAmmonia(system, amoebaMultipoleForce, AmoebaMultipoleForce::NoCutoff, AmoebaMultipoleForce::Mutual,
cutoff, inputPmeGridDimension);
LangevinIntegrator integrator(0.0, 0.1, 0.01);
Context context(system, integrator, Platform::getPlatformByName("CUDA"));
getForcesEnergyMultipoleAmmonia(context, forces, energy);
std::vector<Vec3> dipole;
amoebaMultipoleForce->getLabFramePermanentDipoles(context, dipole);
// Compare to values calculated by TINKER.
std::vector<Vec3> expectedDipole(numberOfParticles);
expectedDipole[0] = Vec3(0.00876454250, -2.04310718E-06, -0.00227593519);
expectedDipole[1] = Vec3(0.000780382180, -0.00432882849, 0.00236926761);
expectedDipole[2] = Vec3(0.000801345883, 0.00431830946, 0.00238143437);
expectedDipole[3] = Vec3(-0.00109746996, 1.16087953e-5, -0.00487407492);
expectedDipole[4] = Vec3(0.00203814102, -2.26554196e-5, 0.00882284298);
expectedDipole[5] = Vec3(-0.00239443187, 0.00432388648, 0.000729303209);
expectedDipole[6] = Vec3(0.00491086743, 2.86430963e-6, -0.000918996348);
expectedDipole[7] = Vec3(-0.00239301946, -0.00432743976, 0.000712674115);
for (int i = 0; i < numberOfParticles; i++)
ASSERT_EQUAL_VEC(expectedDipole[i], dipole[i], 1e-4);
}
// test querying particle total dipoles (fixed + induced)
static void testParticleTotalDipoles() {
int numberOfParticles = 8;
int inputPmeGridDimension = 0;
double cutoff = 9000000.0;
std::vector<Vec3> forces;
double energy;
System system;
AmoebaMultipoleForce* amoebaMultipoleForce = new AmoebaMultipoleForce();;
setupMultipoleAmmonia(system, amoebaMultipoleForce, AmoebaMultipoleForce::NoCutoff, AmoebaMultipoleForce::Mutual,
cutoff, inputPmeGridDimension);
LangevinIntegrator integrator(0.0, 0.1, 0.01);
Context context(system, integrator, Platform::getPlatformByName("CUDA"));
getForcesEnergyMultipoleAmmonia(context, forces, energy);
std::vector<Vec3> dipole;
amoebaMultipoleForce->getTotalDipoles(context, dipole);
// Compare to values calculated by TINKER.
std::vector<Vec3> expectedDipole(numberOfParticles);
expectedDipole[0] = Vec3(0.0119356307, -1.11302433e-6, -0.00296793872);
expectedDipole[1] = Vec3(8.60636211e-4, -0.00460821816, 0.00241705344);
expectedDipole[2] = Vec3(8.80646403e-4, 0.00459728769, 0.00243013245);
expectedDipole[3] = Vec3(-0.00123822377, 1.31555550e-5, -0.00558185336);
expectedDipole[4] = Vec3(0.00399455556, -2.27511931e-5, 0.00955607952);
expectedDipole[5] = Vec3(-0.00157302682, 0.00354892386, 3.40921137e-4);
expectedDipole[6] = Vec3(0.00952428069, 2.14171505e-6, -6.68945865e-4);
expectedDipole[7] = Vec3(-0.00157252460, -0.00355015528, 3.27055162e-4);
for (int i = 0; i < numberOfParticles; i++)
ASSERT_EQUAL_VEC(expectedDipole[i], dipole[i], 1e-4);
}
// test computation of system multipole moments // test computation of system multipole moments
static void testSystemMultipoleMoments() { static void testSystemMultipoleMoments() {
......
...@@ -735,6 +735,48 @@ void ReferenceCalcAmoebaMultipoleForceKernel::getInducedDipoles(ContextImpl& con ...@@ -735,6 +735,48 @@ void ReferenceCalcAmoebaMultipoleForceKernel::getInducedDipoles(ContextImpl& con
delete amoebaReferenceMultipoleForce; delete amoebaReferenceMultipoleForce;
} }
void ReferenceCalcAmoebaMultipoleForceKernel::getLabFramePermanentDipoles(ContextImpl& context, vector<Vec3>& outputDipoles) {
int numParticles = context.getSystem().getNumParticles();
outputDipoles.resize(numParticles);
// Create an AmoebaReferenceMultipoleForce to do the calculation.
AmoebaReferenceMultipoleForce* amoebaReferenceMultipoleForce = setupAmoebaReferenceMultipoleForce(context);
vector<RealVec>& posData = extractPositions(context);
// Retrieve the permanent dipoles in the lab frame.
vector<RealVec> labFramePermanentDipoles;
amoebaReferenceMultipoleForce->calculateLabFramePermanentDipoles(posData, charges, dipoles, quadrupoles, tholes,
dampingFactors, polarity, axisTypes, multipoleAtomZs, multipoleAtomXs, multipoleAtomYs, multipoleAtomCovalentInfo, labFramePermanentDipoles);
for (int i = 0; i < numParticles; i++)
outputDipoles[i] = labFramePermanentDipoles[i];
delete amoebaReferenceMultipoleForce;
}
void ReferenceCalcAmoebaMultipoleForceKernel::getTotalDipoles(ContextImpl& context, vector<Vec3>& outputDipoles) {
int numParticles = context.getSystem().getNumParticles();
outputDipoles.resize(numParticles);
// Create an AmoebaReferenceMultipoleForce to do the calculation.
AmoebaReferenceMultipoleForce* amoebaReferenceMultipoleForce = setupAmoebaReferenceMultipoleForce(context);
vector<RealVec>& posData = extractPositions(context);
// Retrieve the permanent dipoles in the lab frame.
vector<RealVec> totalDipoles;
amoebaReferenceMultipoleForce->calculateTotalDipoles(posData, charges, dipoles, quadrupoles, tholes,
dampingFactors, polarity, axisTypes, multipoleAtomZs, multipoleAtomXs, multipoleAtomYs, multipoleAtomCovalentInfo, totalDipoles);
for (int i = 0; i < numParticles; i++)
outputDipoles[i] = totalDipoles[i];
delete amoebaReferenceMultipoleForce;
}
void ReferenceCalcAmoebaMultipoleForceKernel::getElectrostaticPotential(ContextImpl& context, const std::vector< Vec3 >& inputGrid, void ReferenceCalcAmoebaMultipoleForceKernel::getElectrostaticPotential(ContextImpl& context, const std::vector< Vec3 >& inputGrid,
std::vector< double >& outputElectrostaticPotential) { std::vector< double >& outputElectrostaticPotential) {
......
...@@ -381,6 +381,20 @@ public: ...@@ -381,6 +381,20 @@ public:
* @param dipoles the induced dipole moment of particle i is stored into the i'th element * @param dipoles the induced dipole moment of particle i is stored into the i'th element
*/ */
void getInducedDipoles(ContextImpl& context, std::vector<Vec3>& dipoles); void getInducedDipoles(ContextImpl& context, std::vector<Vec3>& dipoles);
/**
* Get the fixed dipole moments of all particles in the global reference frame.
*
* @param context the Context for which to get the fixed dipoles
* @param dipoles the fixed dipole moment of particle i is stored into the i'th element
*/
void getLabFramePermanentDipoles(ContextImpl& context, std::vector<Vec3>& dipoles);
/**
* Get the total dipole moments of all particles in the global reference frame.
*
* @param context the Context for which to get the fixed dipoles
* @param dipoles the fixed dipole moment of particle i is stored into the i'th element
*/
void getTotalDipoles(ContextImpl& context, std::vector<Vec3>& dipoles);
/** /**
* Calculate the electrostatic potential given vector of grid coordinates. * Calculate the electrostatic potential given vector of grid coordinates.
* *
......
...@@ -35,7 +35,7 @@ using namespace OpenMM; ...@@ -35,7 +35,7 @@ using namespace OpenMM;
AmoebaReferenceMultipoleForce::AmoebaReferenceMultipoleForce() : AmoebaReferenceMultipoleForce::AmoebaReferenceMultipoleForce() :
_nonbondedMethod(NoCutoff), _nonbondedMethod(NoCutoff),
_numParticles(0), _numParticles(0),
_electric(138.9354558456), _electric(138.9354558456),
_dielectric(1.0), _dielectric(1.0),
_mutualInducedDipoleConverged(0), _mutualInducedDipoleConverged(0),
...@@ -51,7 +51,7 @@ AmoebaReferenceMultipoleForce::AmoebaReferenceMultipoleForce() : ...@@ -51,7 +51,7 @@ AmoebaReferenceMultipoleForce::AmoebaReferenceMultipoleForce() :
AmoebaReferenceMultipoleForce::AmoebaReferenceMultipoleForce(NonbondedMethod nonbondedMethod) : AmoebaReferenceMultipoleForce::AmoebaReferenceMultipoleForce(NonbondedMethod nonbondedMethod) :
_nonbondedMethod(nonbondedMethod), _nonbondedMethod(nonbondedMethod),
_numParticles(0), _numParticles(0),
_electric(138.9354558456), _electric(138.9354558456),
_dielectric(1.0), _dielectric(1.0),
_mutualInducedDipoleConverged(0), _mutualInducedDipoleConverged(0),
...@@ -97,7 +97,7 @@ void AmoebaReferenceMultipoleForce::initialize() ...@@ -97,7 +97,7 @@ void AmoebaReferenceMultipoleForce::initialize()
_uScale[index++] = 1.0; _uScale[index++] = 1.0;
} }
AmoebaReferenceMultipoleForce::NonbondedMethod AmoebaReferenceMultipoleForce::getNonbondedMethod() const AmoebaReferenceMultipoleForce::NonbondedMethod AmoebaReferenceMultipoleForce::getNonbondedMethod() const
{ {
return _nonbondedMethod; return _nonbondedMethod;
} }
...@@ -107,7 +107,7 @@ void AmoebaReferenceMultipoleForce::setNonbondedMethod(AmoebaReferenceMultipoleF ...@@ -107,7 +107,7 @@ void AmoebaReferenceMultipoleForce::setNonbondedMethod(AmoebaReferenceMultipoleF
_nonbondedMethod = nonbondedMethod; _nonbondedMethod = nonbondedMethod;
} }
AmoebaReferenceMultipoleForce::PolarizationType AmoebaReferenceMultipoleForce::getPolarizationType() const AmoebaReferenceMultipoleForce::PolarizationType AmoebaReferenceMultipoleForce::getPolarizationType() const
{ {
return _polarizationType; return _polarizationType;
} }
...@@ -117,7 +117,7 @@ void AmoebaReferenceMultipoleForce::setPolarizationType(AmoebaReferenceMultipole ...@@ -117,7 +117,7 @@ void AmoebaReferenceMultipoleForce::setPolarizationType(AmoebaReferenceMultipole
_polarizationType = polarizationType; _polarizationType = polarizationType;
} }
int AmoebaReferenceMultipoleForce::getMutualInducedDipoleConverged() const int AmoebaReferenceMultipoleForce::getMutualInducedDipoleConverged() const
{ {
return _mutualInducedDipoleConverged; return _mutualInducedDipoleConverged;
} }
...@@ -127,7 +127,7 @@ void AmoebaReferenceMultipoleForce::setMutualInducedDipoleConverged(int mutualIn ...@@ -127,7 +127,7 @@ void AmoebaReferenceMultipoleForce::setMutualInducedDipoleConverged(int mutualIn
_mutualInducedDipoleConverged = mutualInducedDipoleConverged; _mutualInducedDipoleConverged = mutualInducedDipoleConverged;
} }
int AmoebaReferenceMultipoleForce::getMutualInducedDipoleIterations() const int AmoebaReferenceMultipoleForce::getMutualInducedDipoleIterations() const
{ {
return _mutualInducedDipoleIterations; return _mutualInducedDipoleIterations;
} }
...@@ -137,7 +137,7 @@ void AmoebaReferenceMultipoleForce::setMutualInducedDipoleIterations(int mutualI ...@@ -137,7 +137,7 @@ void AmoebaReferenceMultipoleForce::setMutualInducedDipoleIterations(int mutualI
_mutualInducedDipoleIterations = mutualInducedDipoleIterations; _mutualInducedDipoleIterations = mutualInducedDipoleIterations;
} }
RealOpenMM AmoebaReferenceMultipoleForce::getMutualInducedDipoleEpsilon() const RealOpenMM AmoebaReferenceMultipoleForce::getMutualInducedDipoleEpsilon() const
{ {
return _mutualInducedDipoleEpsilon; return _mutualInducedDipoleEpsilon;
} }
...@@ -147,7 +147,7 @@ void AmoebaReferenceMultipoleForce::setMutualInducedDipoleEpsilon(RealOpenMM mut ...@@ -147,7 +147,7 @@ void AmoebaReferenceMultipoleForce::setMutualInducedDipoleEpsilon(RealOpenMM mut
_mutualInducedDipoleEpsilon = mutualInducedDipoleEpsilon; _mutualInducedDipoleEpsilon = mutualInducedDipoleEpsilon;
} }
int AmoebaReferenceMultipoleForce::getMaximumMutualInducedDipoleIterations() const int AmoebaReferenceMultipoleForce::getMaximumMutualInducedDipoleIterations() const
{ {
return _maximumMutualInducedDipoleIterations; return _maximumMutualInducedDipoleIterations;
} }
...@@ -157,7 +157,7 @@ void AmoebaReferenceMultipoleForce::setMaximumMutualInducedDipoleIterations(int ...@@ -157,7 +157,7 @@ void AmoebaReferenceMultipoleForce::setMaximumMutualInducedDipoleIterations(int
_maximumMutualInducedDipoleIterations = maximumMutualInducedDipoleIterations; _maximumMutualInducedDipoleIterations = maximumMutualInducedDipoleIterations;
} }
RealOpenMM AmoebaReferenceMultipoleForce::getMutualInducedDipoleTargetEpsilon() const RealOpenMM AmoebaReferenceMultipoleForce::getMutualInducedDipoleTargetEpsilon() const
{ {
return _mutualInducedDipoleTargetEpsilon; return _mutualInducedDipoleTargetEpsilon;
} }
...@@ -184,7 +184,7 @@ void AmoebaReferenceMultipoleForce::setupScaleMaps(const vector< vector< vector< ...@@ -184,7 +184,7 @@ void AmoebaReferenceMultipoleForce::setupScaleMaps(const vector< vector< vector<
/* Setup for scaling maps: /* Setup for scaling maps:
* *
* _scaleMaps[particleIndex][ScaleType] = map, where map[covalentIndex] = scaleFactor * _scaleMaps[particleIndex][ScaleType] = map, where map[covalentIndex] = scaleFactor
* _maxScaleIndex[particleIndex] = max covalent index for particleIndex * _maxScaleIndex[particleIndex] = max covalent index for particleIndex
* *
* multipoleParticleCovalentInfo[ii][jj], jj =0,1,2,3 contains covalent indices (c12, c13, c14, c15) * multipoleParticleCovalentInfo[ii][jj], jj =0,1,2,3 contains covalent indices (c12, c13, c14, c15)
...@@ -220,8 +220,8 @@ void AmoebaReferenceMultipoleForce::setupScaleMaps(const vector< vector< vector< ...@@ -220,8 +220,8 @@ void AmoebaReferenceMultipoleForce::setupScaleMaps(const vector< vector< vector<
hit = 1; hit = 1;
} }
} }
} }
_scaleMaps[ii][P_SCALE][covalentIndex] = hit ? 0.5*_pScale[jj+1] : _pScale[jj+1]; _scaleMaps[ii][P_SCALE][covalentIndex] = hit ? 0.5*_pScale[jj+1] : _pScale[jj+1];
_scaleMaps[ii][M_SCALE][covalentIndex] = _mScale[jj+1]; _scaleMaps[ii][M_SCALE][covalentIndex] = _mScale[jj+1];
_maxScaleIndex[ii] = _maxScaleIndex[ii] < covalentIndex ? covalentIndex : _maxScaleIndex[ii]; _maxScaleIndex[ii] = _maxScaleIndex[ii] < covalentIndex ? covalentIndex : _maxScaleIndex[ii];
...@@ -243,7 +243,7 @@ void AmoebaReferenceMultipoleForce::setupScaleMaps(const vector< vector< vector< ...@@ -243,7 +243,7 @@ void AmoebaReferenceMultipoleForce::setupScaleMaps(const vector< vector< vector<
} }
} }
RealOpenMM AmoebaReferenceMultipoleForce::getMultipoleScaleFactor(unsigned int particleI, unsigned int particleJ, ScaleType scaleType) const RealOpenMM AmoebaReferenceMultipoleForce::getMultipoleScaleFactor(unsigned int particleI, unsigned int particleJ, ScaleType scaleType) const
{ {
MapIntRealOpenMM scaleMap = _scaleMaps[particleI][scaleType]; MapIntRealOpenMM scaleMap = _scaleMaps[particleI][scaleType];
...@@ -255,13 +255,13 @@ RealOpenMM AmoebaReferenceMultipoleForce::getMultipoleScaleFactor(unsigned int p ...@@ -255,13 +255,13 @@ RealOpenMM AmoebaReferenceMultipoleForce::getMultipoleScaleFactor(unsigned int p
} }
} }
void AmoebaReferenceMultipoleForce::getDScaleAndPScale(unsigned int particleI, unsigned int particleJ, RealOpenMM& dScale, RealOpenMM& pScale) const void AmoebaReferenceMultipoleForce::getDScaleAndPScale(unsigned int particleI, unsigned int particleJ, RealOpenMM& dScale, RealOpenMM& pScale) const
{ {
dScale = getMultipoleScaleFactor(particleI, particleJ, D_SCALE); dScale = getMultipoleScaleFactor(particleI, particleJ, D_SCALE);
pScale = getMultipoleScaleFactor(particleI, particleJ, P_SCALE); pScale = getMultipoleScaleFactor(particleI, particleJ, P_SCALE);
} }
void AmoebaReferenceMultipoleForce::getMultipoleScaleFactors(unsigned int particleI, unsigned int particleJ, vector<RealOpenMM>& scaleFactors) const void AmoebaReferenceMultipoleForce::getMultipoleScaleFactors(unsigned int particleI, unsigned int particleJ, vector<RealOpenMM>& scaleFactors) const
{ {
scaleFactors[D_SCALE] = getMultipoleScaleFactor(particleI, particleJ, D_SCALE); scaleFactors[D_SCALE] = getMultipoleScaleFactor(particleI, particleJ, D_SCALE);
scaleFactors[P_SCALE] = getMultipoleScaleFactor(particleI, particleJ, P_SCALE); scaleFactors[P_SCALE] = getMultipoleScaleFactor(particleI, particleJ, P_SCALE);
...@@ -269,7 +269,7 @@ void AmoebaReferenceMultipoleForce::getMultipoleScaleFactors(unsigned int partic ...@@ -269,7 +269,7 @@ void AmoebaReferenceMultipoleForce::getMultipoleScaleFactors(unsigned int partic
scaleFactors[U_SCALE] = getMultipoleScaleFactor(particleI, particleJ, U_SCALE); scaleFactors[U_SCALE] = getMultipoleScaleFactor(particleI, particleJ, U_SCALE);
} }
RealOpenMM AmoebaReferenceMultipoleForce::normalizeRealVec(RealVec& vectorToNormalize) const RealOpenMM AmoebaReferenceMultipoleForce::normalizeRealVec(RealVec& vectorToNormalize) const
{ {
RealOpenMM norm = SQRT(vectorToNormalize.dot(vectorToNormalize)); RealOpenMM norm = SQRT(vectorToNormalize.dot(vectorToNormalize));
if (norm > 0.0) { if (norm > 0.0) {
...@@ -278,26 +278,26 @@ RealOpenMM AmoebaReferenceMultipoleForce::normalizeRealVec(RealVec& vectorToNorm ...@@ -278,26 +278,26 @@ RealOpenMM AmoebaReferenceMultipoleForce::normalizeRealVec(RealVec& vectorToNorm
return norm; return norm;
} }
void AmoebaReferenceMultipoleForce::initializeRealOpenMMVector(vector<RealOpenMM>& vectorToInitialize) const void AmoebaReferenceMultipoleForce::initializeRealOpenMMVector(vector<RealOpenMM>& vectorToInitialize) const
{ {
RealOpenMM zero = 0.0; RealOpenMM zero = 0.0;
vectorToInitialize.resize(_numParticles); vectorToInitialize.resize(_numParticles);
std::fill(vectorToInitialize.begin(), vectorToInitialize.end(), zero); std::fill(vectorToInitialize.begin(), vectorToInitialize.end(), zero);
} }
void AmoebaReferenceMultipoleForce::initializeRealVecVector(vector<RealVec>& vectorToInitialize) const void AmoebaReferenceMultipoleForce::initializeRealVecVector(vector<RealVec>& vectorToInitialize) const
{ {
vectorToInitialize.resize(_numParticles); vectorToInitialize.resize(_numParticles);
RealVec zeroVec(0.0, 0.0, 0.0); RealVec zeroVec(0.0, 0.0, 0.0);
std::fill(vectorToInitialize.begin(), vectorToInitialize.end(), zeroVec); std::fill(vectorToInitialize.begin(), vectorToInitialize.end(), zeroVec);
} }
void AmoebaReferenceMultipoleForce::copyRealVecVector(const vector<OpenMM::RealVec>& inputVector, vector<OpenMM::RealVec>& outputVector) const void AmoebaReferenceMultipoleForce::copyRealVecVector(const vector<OpenMM::RealVec>& inputVector, vector<OpenMM::RealVec>& outputVector) const
{ {
outputVector.resize(inputVector.size()); outputVector.resize(inputVector.size());
for (unsigned int ii = 0; ii < inputVector.size(); ii++) { for (unsigned int ii = 0; ii < inputVector.size(); ii++) {
outputVector[ii] = inputVector[ii]; outputVector[ii] = inputVector[ii];
} }
} }
void AmoebaReferenceMultipoleForce::loadParticleData(const vector<RealVec>& particlePositions, void AmoebaReferenceMultipoleForce::loadParticleData(const vector<RealVec>& particlePositions,
...@@ -307,9 +307,9 @@ void AmoebaReferenceMultipoleForce::loadParticleData(const vector<RealVec>& part ...@@ -307,9 +307,9 @@ void AmoebaReferenceMultipoleForce::loadParticleData(const vector<RealVec>& part
const vector<RealOpenMM>& tholes, const vector<RealOpenMM>& tholes,
const vector<RealOpenMM>& dampingFactors, const vector<RealOpenMM>& dampingFactors,
const vector<RealOpenMM>& polarity, const vector<RealOpenMM>& polarity,
vector<MultipoleParticleData>& particleData) const vector<MultipoleParticleData>& particleData) const
{ {
particleData.resize(_numParticles); particleData.resize(_numParticles);
for (unsigned int ii = 0; ii < _numParticles; ii++) { for (unsigned int ii = 0; ii < _numParticles; ii++) {
...@@ -355,8 +355,8 @@ void AmoebaReferenceMultipoleForce::zeroFixedMultipoleFields() ...@@ -355,8 +355,8 @@ void AmoebaReferenceMultipoleForce::zeroFixedMultipoleFields()
} }
void AmoebaReferenceMultipoleForce::checkChiralCenterAtParticle(MultipoleParticleData& particleI, int axisType, void AmoebaReferenceMultipoleForce::checkChiralCenterAtParticle(MultipoleParticleData& particleI, int axisType,
MultipoleParticleData& particleZ, MultipoleParticleData& particleX, MultipoleParticleData& particleZ, MultipoleParticleData& particleX,
MultipoleParticleData& particleY) const MultipoleParticleData& particleY) const
{ {
if (axisType == AmoebaMultipoleForce::ZThenX || particleY.particleIndex == -1) { if (axisType == AmoebaMultipoleForce::ZThenX || particleY.particleIndex == -1) {
...@@ -384,7 +384,7 @@ void AmoebaReferenceMultipoleForce::checkChiral(vector<MultipoleParticleData>& p ...@@ -384,7 +384,7 @@ void AmoebaReferenceMultipoleForce::checkChiral(vector<MultipoleParticleData>& p
const vector<int>& multipoleAtomXs, const vector<int>& multipoleAtomXs,
const vector<int>& multipoleAtomYs, const vector<int>& multipoleAtomYs,
const vector<int>& multipoleAtomZs, const vector<int>& multipoleAtomZs,
const vector<int>& axisTypes) const const vector<int>& axisTypes) const
{ {
for (unsigned int ii = 0; ii < _numParticles; ii++) { for (unsigned int ii = 0; ii < _numParticles; ii++) {
if (multipoleAtomYs[ii] > -1) { if (multipoleAtomYs[ii] > -1) {
...@@ -400,7 +400,7 @@ void AmoebaReferenceMultipoleForce::applyRotationMatrixToParticle( Multipol ...@@ -400,7 +400,7 @@ void AmoebaReferenceMultipoleForce::applyRotationMatrixToParticle( Multipol
const MultipoleParticleData& particleZ, const MultipoleParticleData& particleZ,
const MultipoleParticleData& particleX, const MultipoleParticleData& particleX,
MultipoleParticleData* particleY, MultipoleParticleData* particleY,
int axisType) const int axisType) const
{ {
// handle case where rotation matrix is identity (e.g. single ion) // handle case where rotation matrix is identity (e.g. single ion)
...@@ -415,25 +415,25 @@ void AmoebaReferenceMultipoleForce::applyRotationMatrixToParticle( Multipol ...@@ -415,25 +415,25 @@ void AmoebaReferenceMultipoleForce::applyRotationMatrixToParticle( Multipol
RealVec vectorX = particleX.position - particleI.position; RealVec vectorX = particleX.position - particleI.position;
normalizeRealVec(vectorZ); normalizeRealVec(vectorZ);
// branch based on axis type // branch based on axis type
if (axisType == AmoebaMultipoleForce::Bisector) { if (axisType == AmoebaMultipoleForce::Bisector) {
// bisector // bisector
// dx = dx1 + dx2 (in TINKER code) // dx = dx1 + dx2 (in TINKER code)
normalizeRealVec(vectorX); normalizeRealVec(vectorX);
vectorZ += vectorX; vectorZ += vectorX;
normalizeRealVec(vectorZ); normalizeRealVec(vectorZ);
} else if (axisType == AmoebaMultipoleForce::ZBisect) { } else if (axisType == AmoebaMultipoleForce::ZBisect) {
// z-bisect // z-bisect
// dx = dx1 + dx2 (in TINKER code) // dx = dx1 + dx2 (in TINKER code)
normalizeRealVec(vectorX); normalizeRealVec(vectorX);
vectorY = particleY->position - particleI.position; vectorY = particleY->position - particleI.position;
...@@ -441,13 +441,13 @@ void AmoebaReferenceMultipoleForce::applyRotationMatrixToParticle( Multipol ...@@ -441,13 +441,13 @@ void AmoebaReferenceMultipoleForce::applyRotationMatrixToParticle( Multipol
vectorX += vectorY; vectorX += vectorY;
normalizeRealVec(vectorX); normalizeRealVec(vectorX);
} else if (axisType == AmoebaMultipoleForce::ThreeFold) { } else if (axisType == AmoebaMultipoleForce::ThreeFold) {
// 3-fold // 3-fold
// dx = dx1 + dx2 + dx3 (in TINKER code) // dx = dx1 + dx2 + dx3 (in TINKER code)
normalizeRealVec(vectorX); normalizeRealVec(vectorX);
vectorY = particleY->position - particleI.position; vectorY = particleY->position - particleI.position;
...@@ -455,15 +455,15 @@ void AmoebaReferenceMultipoleForce::applyRotationMatrixToParticle( Multipol ...@@ -455,15 +455,15 @@ void AmoebaReferenceMultipoleForce::applyRotationMatrixToParticle( Multipol
vectorZ += vectorX + vectorY; vectorZ += vectorX + vectorY;
normalizeRealVec(vectorZ); normalizeRealVec(vectorZ);
} else if (axisType == AmoebaMultipoleForce::ZOnly) { } else if (axisType == AmoebaMultipoleForce::ZOnly) {
// z-only // z-only
vectorX = RealVec(0.1, 0.1, 0.1); vectorX = RealVec(0.1, 0.1, 0.1);
} }
RealOpenMM dot = vectorZ.dot(vectorX); RealOpenMM dot = vectorZ.dot(vectorX);
vectorX -= vectorZ*dot; vectorX -= vectorZ*dot;
...@@ -473,7 +473,7 @@ void AmoebaReferenceMultipoleForce::applyRotationMatrixToParticle( Multipol ...@@ -473,7 +473,7 @@ void AmoebaReferenceMultipoleForce::applyRotationMatrixToParticle( Multipol
RealVec rotationMatrix[3]; RealVec rotationMatrix[3];
rotationMatrix[0] = vectorX; rotationMatrix[0] = vectorX;
rotationMatrix[1] = vectorY; rotationMatrix[1] = vectorY;
rotationMatrix[2] = vectorZ; rotationMatrix[2] = vectorZ;
RealVec labDipole; RealVec labDipole;
for (int ii = 0; ii < 3; ii++) { for (int ii = 0; ii < 3; ii++) {
...@@ -483,7 +483,7 @@ void AmoebaReferenceMultipoleForce::applyRotationMatrixToParticle( Multipol ...@@ -483,7 +483,7 @@ void AmoebaReferenceMultipoleForce::applyRotationMatrixToParticle( Multipol
} }
} }
particleI.dipole = labDipole; particleI.dipole = labDipole;
RealOpenMM mPole[3][3]; RealOpenMM mPole[3][3];
RealOpenMM rPole[3][3] = { { 0.0, 0.0, 0.0 }, RealOpenMM rPole[3][3] = { { 0.0, 0.0, 0.0 },
{ 0.0, 0.0, 0.0 }, { 0.0, 0.0, 0.0 },
...@@ -500,7 +500,7 @@ void AmoebaReferenceMultipoleForce::applyRotationMatrixToParticle( Multipol ...@@ -500,7 +500,7 @@ void AmoebaReferenceMultipoleForce::applyRotationMatrixToParticle( Multipol
mPole[2][0] = particleI.quadrupole[QXZ]; mPole[2][0] = particleI.quadrupole[QXZ];
mPole[2][1] = particleI.quadrupole[QYZ]; mPole[2][1] = particleI.quadrupole[QYZ];
mPole[2][2] = particleI.quadrupole[QZZ]; mPole[2][2] = particleI.quadrupole[QZZ];
for (int ii = 0; ii < 3; ii++) { for (int ii = 0; ii < 3; ii++) {
for (int jj = ii; jj < 3; jj++) { for (int jj = ii; jj < 3; jj++) {
for (int kk = 0; kk < 3; kk++) { for (int kk = 0; kk < 3; kk++) {
...@@ -510,7 +510,7 @@ void AmoebaReferenceMultipoleForce::applyRotationMatrixToParticle( Multipol ...@@ -510,7 +510,7 @@ void AmoebaReferenceMultipoleForce::applyRotationMatrixToParticle( Multipol
} }
} }
} }
particleI.quadrupole[QXX] = rPole[0][0]; particleI.quadrupole[QXX] = rPole[0][0];
particleI.quadrupole[QXY] = rPole[0][1]; particleI.quadrupole[QXY] = rPole[0][1];
particleI.quadrupole[QXZ] = rPole[0][2]; particleI.quadrupole[QXZ] = rPole[0][2];
...@@ -648,7 +648,7 @@ void AmoebaReferenceMultipoleForce::applyRotationMatrix(vector<MultipoleParticle ...@@ -648,7 +648,7 @@ void AmoebaReferenceMultipoleForce::applyRotationMatrix(vector<MultipoleParticle
const vector<int>& multipoleAtomXs, const vector<int>& multipoleAtomXs,
const vector<int>& multipoleAtomYs, const vector<int>& multipoleAtomYs,
const vector<int>& multipoleAtomZs, const vector<int>& multipoleAtomZs,
const vector<int>& axisTypes) const const vector<int>& axisTypes) const
{ {
for (unsigned int ii = 0; ii < _numParticles; ii++) { for (unsigned int ii = 0; ii < _numParticles; ii++) {
...@@ -661,19 +661,19 @@ void AmoebaReferenceMultipoleForce::applyRotationMatrix(vector<MultipoleParticle ...@@ -661,19 +661,19 @@ void AmoebaReferenceMultipoleForce::applyRotationMatrix(vector<MultipoleParticle
void AmoebaReferenceMultipoleForce::getAndScaleInverseRs(RealOpenMM dampI, RealOpenMM dampJ, void AmoebaReferenceMultipoleForce::getAndScaleInverseRs(RealOpenMM dampI, RealOpenMM dampJ,
RealOpenMM tholeI, RealOpenMM tholeJ, RealOpenMM tholeI, RealOpenMM tholeJ,
RealOpenMM r, vector<RealOpenMM>& rrI) const RealOpenMM r, vector<RealOpenMM>& rrI) const
{ {
RealOpenMM rI = 1.0/r; RealOpenMM rI = 1.0/r;
RealOpenMM r2I = rI*rI; RealOpenMM r2I = rI*rI;
rrI[0] = rI*r2I; rrI[0] = rI*r2I;
RealOpenMM constantFactor = 3.0; RealOpenMM constantFactor = 3.0;
for (unsigned int ii = 1; ii < rrI.size(); ii++) { for (unsigned int ii = 1; ii < rrI.size(); ii++) {
rrI[ii] = constantFactor*rrI[ii-1]*r2I; rrI[ii] = constantFactor*rrI[ii-1]*r2I;
constantFactor += 2.0; constantFactor += 2.0;
} }
RealOpenMM damp = dampI*dampJ; RealOpenMM damp = dampI*dampJ;
if (damp != 0.0) { if (damp != 0.0) {
RealOpenMM pgamma = tholeI < tholeJ ? tholeI : tholeJ; RealOpenMM pgamma = tholeI < tholeJ ? tholeI : tholeJ;
...@@ -681,7 +681,7 @@ void AmoebaReferenceMultipoleForce::getAndScaleInverseRs(RealOpenMM dampI, RealO ...@@ -681,7 +681,7 @@ void AmoebaReferenceMultipoleForce::getAndScaleInverseRs(RealOpenMM dampI, RealO
ratio = ratio*ratio*ratio; ratio = ratio*ratio*ratio;
damp = -pgamma*ratio; damp = -pgamma*ratio;
if (damp > -50.0) { if (damp > -50.0) {
RealOpenMM dampExp = EXP(damp); RealOpenMM dampExp = EXP(damp);
rrI[0] *= 1.0 - dampExp; rrI[0] *= 1.0 - dampExp;
...@@ -695,7 +695,7 @@ void AmoebaReferenceMultipoleForce::getAndScaleInverseRs(RealOpenMM dampI, RealO ...@@ -695,7 +695,7 @@ void AmoebaReferenceMultipoleForce::getAndScaleInverseRs(RealOpenMM dampI, RealO
void AmoebaReferenceMultipoleForce::calculateFixedMultipoleFieldPairIxn(const MultipoleParticleData& particleI, void AmoebaReferenceMultipoleForce::calculateFixedMultipoleFieldPairIxn(const MultipoleParticleData& particleI,
const MultipoleParticleData& particleJ, const MultipoleParticleData& particleJ,
RealOpenMM dScale, RealOpenMM pScale) RealOpenMM dScale, RealOpenMM pScale)
{ {
if (particleI.particleIndex == particleJ.particleIndex) if (particleI.particleIndex == particleJ.particleIndex)
...@@ -705,9 +705,9 @@ void AmoebaReferenceMultipoleForce::calculateFixedMultipoleFieldPairIxn(const Mu ...@@ -705,9 +705,9 @@ void AmoebaReferenceMultipoleForce::calculateFixedMultipoleFieldPairIxn(const Mu
RealOpenMM r = SQRT(deltaR.dot(deltaR)); RealOpenMM r = SQRT(deltaR.dot(deltaR));
vector<RealOpenMM> rrI(3); vector<RealOpenMM> rrI(3);
// get scaling factors, if needed // get scaling factors, if needed
getAndScaleInverseRs(particleI.dampingFactor, particleJ.dampingFactor, particleI.thole, particleJ.thole, r, rrI); getAndScaleInverseRs(particleI.dampingFactor, particleJ.dampingFactor, particleI.thole, particleJ.thole, r, rrI);
RealOpenMM rr3 = rrI[0]; RealOpenMM rr3 = rrI[0];
...@@ -722,8 +722,8 @@ void AmoebaReferenceMultipoleForce::calculateFixedMultipoleFieldPairIxn(const Mu ...@@ -722,8 +722,8 @@ void AmoebaReferenceMultipoleForce::calculateFixedMultipoleFieldPairIxn(const Mu
qDotDelta[1] = deltaR[0]*particleJ.quadrupole[QXY] + deltaR[1]*particleJ.quadrupole[QYY] + deltaR[2]*particleJ.quadrupole[QYZ]; qDotDelta[1] = deltaR[0]*particleJ.quadrupole[QXY] + deltaR[1]*particleJ.quadrupole[QYY] + deltaR[2]*particleJ.quadrupole[QYZ];
qDotDelta[2] = deltaR[0]*particleJ.quadrupole[QXZ] + deltaR[1]*particleJ.quadrupole[QYZ] + deltaR[2]*particleJ.quadrupole[QZZ]; qDotDelta[2] = deltaR[0]*particleJ.quadrupole[QXZ] + deltaR[1]*particleJ.quadrupole[QYZ] + deltaR[2]*particleJ.quadrupole[QZZ];
RealOpenMM dipoleDelta = particleJ.dipole.dot(deltaR); RealOpenMM dipoleDelta = particleJ.dipole.dot(deltaR);
RealOpenMM qdpoleDelta = qDotDelta.dot(deltaR); RealOpenMM qdpoleDelta = qDotDelta.dot(deltaR);
RealOpenMM factor = rr3*particleJ.charge - rr5*dipoleDelta + rr7*qdpoleDelta; RealOpenMM factor = rr3*particleJ.charge - rr5*dipoleDelta + rr7*qdpoleDelta;
RealVec field = deltaR*factor + particleJ.dipole*rr3 - qDotDelta*rr5_2; RealVec field = deltaR*factor + particleJ.dipole*rr3 - qDotDelta*rr5_2;
...@@ -731,17 +731,17 @@ void AmoebaReferenceMultipoleForce::calculateFixedMultipoleFieldPairIxn(const Mu ...@@ -731,17 +731,17 @@ void AmoebaReferenceMultipoleForce::calculateFixedMultipoleFieldPairIxn(const Mu
unsigned int particleIndex = particleI.particleIndex; unsigned int particleIndex = particleI.particleIndex;
_fixedMultipoleField[particleIndex] -= field*dScale; _fixedMultipoleField[particleIndex] -= field*dScale;
_fixedMultipoleFieldPolar[particleIndex] -= field*pScale; _fixedMultipoleFieldPolar[particleIndex] -= field*pScale;
// field at particle J due multipoles at particle I // field at particle J due multipoles at particle I
qDotDelta[0] = deltaR[0]*particleI.quadrupole[QXX] + deltaR[1]*particleI.quadrupole[QXY] + deltaR[2]*particleI.quadrupole[QXZ]; qDotDelta[0] = deltaR[0]*particleI.quadrupole[QXX] + deltaR[1]*particleI.quadrupole[QXY] + deltaR[2]*particleI.quadrupole[QXZ];
qDotDelta[1] = deltaR[0]*particleI.quadrupole[QXY] + deltaR[1]*particleI.quadrupole[QYY] + deltaR[2]*particleI.quadrupole[QYZ]; qDotDelta[1] = deltaR[0]*particleI.quadrupole[QXY] + deltaR[1]*particleI.quadrupole[QYY] + deltaR[2]*particleI.quadrupole[QYZ];
qDotDelta[2] = deltaR[0]*particleI.quadrupole[QXZ] + deltaR[1]*particleI.quadrupole[QYZ] + deltaR[2]*particleI.quadrupole[QZZ]; qDotDelta[2] = deltaR[0]*particleI.quadrupole[QXZ] + deltaR[1]*particleI.quadrupole[QYZ] + deltaR[2]*particleI.quadrupole[QZZ];
dipoleDelta = particleI.dipole.dot(deltaR); dipoleDelta = particleI.dipole.dot(deltaR);
qdpoleDelta = qDotDelta.dot(deltaR); qdpoleDelta = qDotDelta.dot(deltaR);
factor = rr3*particleI.charge + rr5*dipoleDelta + rr7*qdpoleDelta; factor = rr3*particleI.charge + rr5*dipoleDelta + rr7*qdpoleDelta;
field = deltaR*factor - particleI.dipole*rr3 - qDotDelta*rr5_2; field = deltaR*factor - particleI.dipole*rr3 - qDotDelta*rr5_2;
particleIndex = particleJ.particleIndex; particleIndex = particleJ.particleIndex;
_fixedMultipoleField[particleIndex] += field*dScale; _fixedMultipoleField[particleIndex] += field*dScale;
...@@ -760,7 +760,7 @@ void AmoebaReferenceMultipoleForce::calculateFixedMultipoleField(const vector<Mu ...@@ -760,7 +760,7 @@ void AmoebaReferenceMultipoleForce::calculateFixedMultipoleField(const vector<Mu
for (unsigned int jj = ii; jj < _numParticles; jj++) { for (unsigned int jj = ii; jj < _numParticles; jj++) {
// if site jj is less than max covalent scaling index then get/apply scaling constants // if site jj is less than max covalent scaling index then get/apply scaling constants
// otherwise add unmodified field and fieldPolar to particle fields // otherwise add unmodified field and fieldPolar to particle fields
RealOpenMM dScale, pScale; RealOpenMM dScale, pScale;
if (jj <= _maxScaleIndex[ii]) { if (jj <= _maxScaleIndex[ii]) {
...@@ -787,13 +787,13 @@ void AmoebaReferenceMultipoleForce::initializeInducedDipoles(vector<UpdateInduce ...@@ -787,13 +787,13 @@ void AmoebaReferenceMultipoleForce::initializeInducedDipoles(vector<UpdateInduce
} }
} }
void AmoebaReferenceMultipoleForce::calculateInducedDipolePairIxn(unsigned int particleI, void AmoebaReferenceMultipoleForce::calculateInducedDipolePairIxn(unsigned int particleI,
unsigned int particleJ, unsigned int particleJ,
RealOpenMM rr3, RealOpenMM rr3,
RealOpenMM rr5, RealOpenMM rr5,
const RealVec& deltaR, const RealVec& deltaR,
const vector<RealVec>& inducedDipole, const vector<RealVec>& inducedDipole,
vector<RealVec>& field) const vector<RealVec>& field) const
{ {
RealOpenMM dDotDelta = rr5*(inducedDipole[particleJ].dot(deltaR)); RealOpenMM dDotDelta = rr5*(inducedDipole[particleJ].dot(deltaR));
...@@ -802,7 +802,7 @@ void AmoebaReferenceMultipoleForce::calculateInducedDipolePairIxn(unsigned int p ...@@ -802,7 +802,7 @@ void AmoebaReferenceMultipoleForce::calculateInducedDipolePairIxn(unsigned int p
field[particleJ] += inducedDipole[particleI]*rr3 + deltaR*dDotDelta; field[particleJ] += inducedDipole[particleI]*rr3 + deltaR*dDotDelta;
} }
void AmoebaReferenceMultipoleForce::calculateInducedDipolePairIxns(const MultipoleParticleData& particleI, void AmoebaReferenceMultipoleForce::calculateInducedDipolePairIxns(const MultipoleParticleData& particleI,
const MultipoleParticleData& particleJ, const MultipoleParticleData& particleJ,
vector<UpdateInducedDipoleFieldStruct>& updateInducedDipoleFields) vector<UpdateInducedDipoleFieldStruct>& updateInducedDipoleFields)
{ {
...@@ -819,10 +819,10 @@ void AmoebaReferenceMultipoleForce::calculateInducedDipolePairIxns(const Multipo ...@@ -819,10 +819,10 @@ void AmoebaReferenceMultipoleForce::calculateInducedDipolePairIxns(const Multipo
getAndScaleInverseRs(particleI.dampingFactor, particleJ.dampingFactor, getAndScaleInverseRs(particleI.dampingFactor, particleJ.dampingFactor,
particleI.thole, particleJ.thole, r, rrI); particleI.thole, particleJ.thole, r, rrI);
RealOpenMM rr3 = -rrI[0]; RealOpenMM rr3 = -rrI[0];
RealOpenMM rr5 = rrI[1]; RealOpenMM rr5 = rrI[1];
for (unsigned int ii = 0; ii < updateInducedDipoleFields.size(); ii++) { for (unsigned int ii = 0; ii < updateInducedDipoleFields.size(); ii++) {
calculateInducedDipolePairIxn(particleI.particleIndex, particleJ.particleIndex, rr3, rr5, deltaR, calculateInducedDipolePairIxn(particleI.particleIndex, particleJ.particleIndex, rr3, rr5, deltaR,
*updateInducedDipoleFields[ii].inducedDipoles, updateInducedDipoleFields[ii].inducedDipoleField); *updateInducedDipoleFields[ii].inducedDipoles, updateInducedDipoleFields[ii].inducedDipoleField);
...@@ -875,13 +875,13 @@ void AmoebaReferenceMultipoleForce::calculateInducedDipolePairIxns(const Multipo ...@@ -875,13 +875,13 @@ void AmoebaReferenceMultipoleForce::calculateInducedDipolePairIxns(const Multipo
void AmoebaReferenceMultipoleForce::calculateInducedDipoleFields(const vector<MultipoleParticleData>& particleData, vector<UpdateInducedDipoleFieldStruct>& updateInducedDipoleFields) { void AmoebaReferenceMultipoleForce::calculateInducedDipoleFields(const vector<MultipoleParticleData>& particleData, vector<UpdateInducedDipoleFieldStruct>& updateInducedDipoleFields) {
// Initialize the fields to zero. // Initialize the fields to zero.
RealVec zeroVec(0.0, 0.0, 0.0); RealVec zeroVec(0.0, 0.0, 0.0);
for (unsigned int ii = 0; ii < updateInducedDipoleFields.size(); ii++) for (unsigned int ii = 0; ii < updateInducedDipoleFields.size(); ii++)
std::fill(updateInducedDipoleFields[ii].inducedDipoleField.begin(), updateInducedDipoleFields[ii].inducedDipoleField.end(), zeroVec); std::fill(updateInducedDipoleFields[ii].inducedDipoleField.begin(), updateInducedDipoleFields[ii].inducedDipoleField.end(), zeroVec);
// Add fields from all induced dipoles. // Add fields from all induced dipoles.
for (unsigned int ii = 0; ii < particleData.size(); ii++) for (unsigned int ii = 0; ii < particleData.size(); ii++)
for (unsigned int jj = ii; jj < particleData.size(); jj++) for (unsigned int jj = ii; jj < particleData.size(); jj++)
calculateInducedDipolePairIxns(particleData[ii], particleData[jj], updateInducedDipoleFields); calculateInducedDipolePairIxns(particleData[ii], particleData[jj], updateInducedDipoleFields);
...@@ -891,7 +891,7 @@ RealOpenMM AmoebaReferenceMultipoleForce::updateInducedDipoleFields(const vector ...@@ -891,7 +891,7 @@ RealOpenMM AmoebaReferenceMultipoleForce::updateInducedDipoleFields(const vector
vector<UpdateInducedDipoleFieldStruct>& updateInducedDipoleFields) vector<UpdateInducedDipoleFieldStruct>& updateInducedDipoleFields)
{ {
// Calculate the fields coming from induced dipoles. // Calculate the fields coming from induced dipoles.
calculateInducedDipoleFields(particleData, updateInducedDipoleFields); calculateInducedDipoleFields(particleData, updateInducedDipoleFields);
// Update the induced dipoles and calculate the convergence factor, maxEpsilon // Update the induced dipoles and calculate the convergence factor, maxEpsilon
...@@ -902,7 +902,7 @@ RealOpenMM AmoebaReferenceMultipoleForce::updateInducedDipoleFields(const vector ...@@ -902,7 +902,7 @@ RealOpenMM AmoebaReferenceMultipoleForce::updateInducedDipoleFields(const vector
*updateInducedDipoleFields[kk].fixedMultipoleField, *updateInducedDipoleFields[kk].fixedMultipoleField,
updateInducedDipoleFields[kk].inducedDipoleField, updateInducedDipoleFields[kk].inducedDipoleField,
*updateInducedDipoleFields[kk].inducedDipoles); *updateInducedDipoleFields[kk].inducedDipoles);
maxEpsilon = epsilon > maxEpsilon ? epsilon : maxEpsilon; maxEpsilon = epsilon > maxEpsilon ? epsilon : maxEpsilon;
} }
...@@ -937,11 +937,11 @@ void AmoebaReferenceMultipoleForce::convergeInduceDipolesBySOR(const vector<Mult ...@@ -937,11 +937,11 @@ void AmoebaReferenceMultipoleForce::convergeInduceDipolesBySOR(const vector<Mult
// loop until (1) induced dipoles are converged or // loop until (1) induced dipoles are converged or
// (2) iterations == max iterations or // (2) iterations == max iterations or
// (3) convergence factor (spsilon) increases // (3) convergence factor (spsilon) increases
while (!done) { while (!done) {
RealOpenMM epsilon = updateInducedDipoleFields(particleData, updateInducedDipoleField); RealOpenMM epsilon = updateInducedDipoleFields(particleData, updateInducedDipoleField);
epsilon = _polarSOR*_debye*SQRT(epsilon/(static_cast<RealOpenMM>(_numParticles))); epsilon = _polarSOR*_debye*SQRT(epsilon/(static_cast<RealOpenMM>(_numParticles)));
if (epsilon < getMutualInducedDipoleTargetEpsilon()) { if (epsilon < getMutualInducedDipoleTargetEpsilon()) {
...@@ -1015,11 +1015,11 @@ void AmoebaReferenceMultipoleForce::convergeInduceDipolesByDIIS(const vector<Mul ...@@ -1015,11 +1015,11 @@ void AmoebaReferenceMultipoleForce::convergeInduceDipolesByDIIS(const vector<Mul
int maxPrevious = 20; int maxPrevious = 20;
for (int iteration = 0; ; iteration++) { for (int iteration = 0; ; iteration++) {
// Compute the field from the induced dipoles. // Compute the field from the induced dipoles.
calculateInducedDipoleFields(particleData, updateInducedDipoleField); calculateInducedDipoleFields(particleData, updateInducedDipoleField);
// Record the current dipoles and the errors in them. // Record the current dipoles and the errors in them.
RealOpenMM maxEpsilon = 0; RealOpenMM maxEpsilon = 0;
prevErrors.push_back(vector<RealVec>()); prevErrors.push_back(vector<RealVec>());
prevErrors.back().resize(_numParticles); prevErrors.back().resize(_numParticles);
...@@ -1041,9 +1041,9 @@ void AmoebaReferenceMultipoleForce::convergeInduceDipolesByDIIS(const vector<Mul ...@@ -1041,9 +1041,9 @@ void AmoebaReferenceMultipoleForce::convergeInduceDipolesByDIIS(const vector<Mul
maxEpsilon = epsilon; maxEpsilon = epsilon;
} }
maxEpsilon = _debye*SQRT(maxEpsilon/_numParticles); maxEpsilon = _debye*SQRT(maxEpsilon/_numParticles);
// Decide whether to stop or continue iterating. // Decide whether to stop or continue iterating.
if (maxEpsilon < getMutualInducedDipoleTargetEpsilon()) if (maxEpsilon < getMutualInducedDipoleTargetEpsilon())
setMutualInducedDipoleConverged(true); setMutualInducedDipoleConverged(true);
if (maxEpsilon < getMutualInducedDipoleTargetEpsilon() || iteration == getMaximumMutualInducedDipoleIterations()) { if (maxEpsilon < getMutualInducedDipoleTargetEpsilon() || iteration == getMaximumMutualInducedDipoleIterations()) {
...@@ -1051,9 +1051,9 @@ void AmoebaReferenceMultipoleForce::convergeInduceDipolesByDIIS(const vector<Mul ...@@ -1051,9 +1051,9 @@ void AmoebaReferenceMultipoleForce::convergeInduceDipolesByDIIS(const vector<Mul
setMutualInducedDipoleIterations(iteration); setMutualInducedDipoleIterations(iteration);
return; return;
} }
// Select the new dipoles. // Select the new dipoles.
if (prevErrors.size() > maxPrevious) { if (prevErrors.size() > maxPrevious) {
prevErrors.erase(prevErrors.begin()); prevErrors.erase(prevErrors.begin());
for (int k = 0; k < numFields; k++) for (int k = 0; k < numFields; k++)
...@@ -1081,9 +1081,9 @@ void AmoebaReferenceMultipoleForce::computeDIISCoefficients(const vector<vector< ...@@ -1081,9 +1081,9 @@ void AmoebaReferenceMultipoleForce::computeDIISCoefficients(const vector<vector<
coefficients[0] = 1; coefficients[0] = 1;
return; return;
} }
// Create the DIIS matrix. // Create the DIIS matrix.
int rank = steps+1; int rank = steps+1;
Array2D<double> b(rank, rank); Array2D<double> b(rank, rank);
b[0][0] = 0; b[0][0] = 0;
...@@ -1096,9 +1096,9 @@ void AmoebaReferenceMultipoleForce::computeDIISCoefficients(const vector<vector< ...@@ -1096,9 +1096,9 @@ void AmoebaReferenceMultipoleForce::computeDIISCoefficients(const vector<vector<
sum += prevErrors[i][k].dot(prevErrors[j][k]); sum += prevErrors[i][k].dot(prevErrors[j][k]);
b[i+1][j+1] = b[j+1][i+1] = sum; b[i+1][j+1] = b[j+1][i+1] = sum;
} }
// Solve using SVD. Since the right hand side is (-1, 0, 0, 0, ...), this is simpler than the general case. // Solve using SVD. Since the right hand side is (-1, 0, 0, 0, ...), this is simpler than the general case.
JAMA::SVD<double> svd(b); JAMA::SVD<double> svd(b);
Array2D<double> u, v; Array2D<double> u, v;
svd.getU(u); svd.getU(u);
...@@ -1111,7 +1111,7 @@ void AmoebaReferenceMultipoleForce::computeDIISCoefficients(const vector<vector< ...@@ -1111,7 +1111,7 @@ void AmoebaReferenceMultipoleForce::computeDIISCoefficients(const vector<vector<
for (int j = 0; j < effectiveRank; j++) for (int j = 0; j < effectiveRank; j++)
d -= u[0][j]*v[i][j]/s[j]; d -= u[0][j]*v[i][j]/s[j];
coefficients[i-1] = d; coefficients[i-1] = d;
} }
} }
void AmoebaReferenceMultipoleForce::calculateInducedDipoles(const vector<MultipoleParticleData>& particleData) void AmoebaReferenceMultipoleForce::calculateInducedDipoles(const vector<MultipoleParticleData>& particleData)
...@@ -1123,12 +1123,12 @@ void AmoebaReferenceMultipoleForce::calculateInducedDipoles(const vector<Multipo ...@@ -1123,12 +1123,12 @@ void AmoebaReferenceMultipoleForce::calculateInducedDipoles(const vector<Multipo
calculateFixedMultipoleField(particleData); calculateFixedMultipoleField(particleData);
// initialize inducedDipoles // initialize inducedDipoles
// if polarization type is 'Direct', then return after initializing; otherwise // if polarization type is 'Direct', then return after initializing; otherwise
// converge induced dipoles. // converge induced dipoles.
for (unsigned int ii = 0; ii < _numParticles; ii++) { for (unsigned int ii = 0; ii < _numParticles; ii++) {
_fixedMultipoleField[ii] *= particleData[ii].polarity; _fixedMultipoleField[ii] *= particleData[ii].polarity;
_fixedMultipoleFieldPolar[ii] *= particleData[ii].polarity; _fixedMultipoleFieldPolar[ii] *= particleData[ii].polarity;
} }
_inducedDipole.resize(_numParticles); _inducedDipole.resize(_numParticles);
...@@ -1549,9 +1549,9 @@ void AmoebaReferenceMultipoleForce::mapTorqueToForceForParticle(const MultipoleP ...@@ -1549,9 +1549,9 @@ void AmoebaReferenceMultipoleForce::mapTorqueToForceForParticle(const MultipoleP
const MultipoleParticleData& particleV, const MultipoleParticleData& particleV,
MultipoleParticleData* particleW, MultipoleParticleData* particleW,
int axisType, const Vec3& torque, int axisType, const Vec3& torque,
vector<RealVec>& forces) const vector<RealVec>& forces) const
{ {
static const int U = 0; static const int U = 0;
static const int V = 1; static const int V = 1;
static const int W = 2; static const int W = 2;
...@@ -1565,17 +1565,17 @@ void AmoebaReferenceMultipoleForce::mapTorqueToForceForParticle(const MultipoleP ...@@ -1565,17 +1565,17 @@ void AmoebaReferenceMultipoleForce::mapTorqueToForceForParticle(const MultipoleP
static const int VS = 10; static const int VS = 10;
static const int WS = 11; static const int WS = 11;
static const int LastVectorIndex = 12; static const int LastVectorIndex = 12;
static const int X = 0; static const int X = 0;
static const int Y = 1; static const int Y = 1;
static const int Z = 2; static const int Z = 2;
static const int I = 3; static const int I = 3;
RealOpenMM norms[LastVectorIndex]; RealOpenMM norms[LastVectorIndex];
RealOpenMM angles[LastVectorIndex][2]; RealOpenMM angles[LastVectorIndex][2];
// --------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------
// get coordinates of this atom and the z & x axis atoms // get coordinates of this atom and the z & x axis atoms
// compute the vector between the atoms and 1/sqrt(d2), d2 is distance between // compute the vector between the atoms and 1/sqrt(d2), d2 is distance between
// this atom and the axis atom // this atom and the axis atom
...@@ -1597,12 +1597,12 @@ void AmoebaReferenceMultipoleForce::mapTorqueToForceForParticle(const MultipoleP ...@@ -1597,12 +1597,12 @@ void AmoebaReferenceMultipoleForce::mapTorqueToForceForParticle(const MultipoleP
vectorW = vectorU.cross(vectorV); vectorW = vectorU.cross(vectorV);
} }
norms[W] = normalizeRealVec(vectorW); norms[W] = normalizeRealVec(vectorW);
RealVec vectorUV, vectorUW, vectorVW; RealVec vectorUV, vectorUW, vectorVW;
vectorUV = vectorV.cross(vectorU); vectorUV = vectorV.cross(vectorU);
vectorUW = vectorW.cross(vectorU); vectorUW = vectorW.cross(vectorU);
vectorVW = vectorW.cross(vectorV); vectorVW = vectorW.cross(vectorV);
norms[UV] = normalizeRealVec(vectorUV); norms[UV] = normalizeRealVec(vectorUV);
norms[UW] = normalizeRealVec(vectorUW); norms[UW] = normalizeRealVec(vectorUW);
norms[VW] = normalizeRealVec(vectorVW); norms[VW] = normalizeRealVec(vectorVW);
...@@ -1612,7 +1612,7 @@ void AmoebaReferenceMultipoleForce::mapTorqueToForceForParticle(const MultipoleP ...@@ -1612,7 +1612,7 @@ void AmoebaReferenceMultipoleForce::mapTorqueToForceForParticle(const MultipoleP
angles[UV][0] = vectorU.dot(vectorV); angles[UV][0] = vectorU.dot(vectorV);
angles[UV][1] = SQRT(1.0 - angles[UV][0]*angles[UV][0]); angles[UV][1] = SQRT(1.0 - angles[UV][0]*angles[UV][0]);
angles[UW][0] = vectorU.dot(vectorW); angles[UW][0] = vectorU.dot(vectorW);
angles[UW][1] = SQRT(1.0 - angles[UW][0]*angles[UW][0]); angles[UW][1] = SQRT(1.0 - angles[UW][0]*angles[UW][0]);
...@@ -1626,26 +1626,26 @@ void AmoebaReferenceMultipoleForce::mapTorqueToForceForParticle(const MultipoleP ...@@ -1626,26 +1626,26 @@ void AmoebaReferenceMultipoleForce::mapTorqueToForceForParticle(const MultipoleP
dphi *= -1.0; dphi *= -1.0;
// branch based on axis type // branch based on axis type
if (axisType == AmoebaMultipoleForce::ZThenX || axisType == AmoebaMultipoleForce::Bisector) { if (axisType == AmoebaMultipoleForce::ZThenX || axisType == AmoebaMultipoleForce::Bisector) {
RealOpenMM factor1; RealOpenMM factor1;
RealOpenMM factor2; RealOpenMM factor2;
RealOpenMM factor3; RealOpenMM factor3;
RealOpenMM factor4; RealOpenMM factor4;
RealOpenMM half = 0.5; RealOpenMM half = 0.5;
factor1 = dphi[V]/(norms[U]*angles[UV][1]); factor1 = dphi[V]/(norms[U]*angles[UV][1]);
factor2 = dphi[W]/(norms[U]); factor2 = dphi[W]/(norms[U]);
factor3 = -dphi[U]/(norms[V]*angles[UV][1]); factor3 = -dphi[U]/(norms[V]*angles[UV][1]);
if (axisType == AmoebaMultipoleForce::Bisector) { if (axisType == AmoebaMultipoleForce::Bisector) {
factor2 *= half; factor2 *= half;
factor4 = half*dphi[W]/(norms[V]); factor4 = half*dphi[W]/(norms[V]);
} else { } else {
factor4 = 0.0; factor4 = 0.0;
} }
for (int ii = 0; ii < 3; ii++) { for (int ii = 0; ii < 3; ii++) {
double forceU = vectorUV[ii]*factor1 + factor2*vectorUW[ii]; double forceU = vectorUV[ii]*factor1 + factor2*vectorUW[ii];
forces[particleU.particleIndex][ii] -= forceU; forces[particleU.particleIndex][ii] -= forceU;
...@@ -1658,7 +1658,7 @@ void AmoebaReferenceMultipoleForce::mapTorqueToForceForParticle(const MultipoleP ...@@ -1658,7 +1658,7 @@ void AmoebaReferenceMultipoleForce::mapTorqueToForceForParticle(const MultipoleP
} else if (axisType == AmoebaMultipoleForce::ZBisect) { } else if (axisType == AmoebaMultipoleForce::ZBisect) {
RealVec vectorR = vectorV + vectorW; RealVec vectorR = vectorV + vectorW;
RealVec vectorS = vectorU.cross(vectorR); RealVec vectorS = vectorU.cross(vectorR);
norms[R] = normalizeRealVec(vectorR); norms[R] = normalizeRealVec(vectorR);
...@@ -1685,7 +1685,7 @@ void AmoebaReferenceMultipoleForce::mapTorqueToForceForParticle(const MultipoleP ...@@ -1685,7 +1685,7 @@ void AmoebaReferenceMultipoleForce::mapTorqueToForceForParticle(const MultipoleP
angles[WS][0] = vectorW.dot(vectorS); angles[WS][0] = vectorW.dot(vectorS);
angles[WS][1] = SQRT(1.0 - angles[WS][0]*angles[WS][0]); angles[WS][1] = SQRT(1.0 - angles[WS][0]*angles[WS][0]);
RealVec t1 = vectorV - vectorS*angles[VS][0]; RealVec t1 = vectorV - vectorS*angles[VS][0];
RealVec t2 = vectorW - vectorS*angles[WS][0]; RealVec t2 = vectorW - vectorS*angles[WS][0];
...@@ -1767,7 +1767,7 @@ void AmoebaReferenceMultipoleForce::mapTorqueToForce(vector<MultipoleParticleDat ...@@ -1767,7 +1767,7 @@ void AmoebaReferenceMultipoleForce::mapTorqueToForce(vector<MultipoleParticleDat
const vector<int>& multipoleAtomZs, const vector<int>& multipoleAtomZs,
const vector<int>& axisTypes, const vector<int>& axisTypes,
vector<RealVec>& torques, vector<RealVec>& torques,
vector<RealVec>& forces) const vector<RealVec>& forces) const
{ {
// map torques to forces // map torques to forces
...@@ -1777,7 +1777,7 @@ void AmoebaReferenceMultipoleForce::mapTorqueToForce(vector<MultipoleParticleDat ...@@ -1777,7 +1777,7 @@ void AmoebaReferenceMultipoleForce::mapTorqueToForce(vector<MultipoleParticleDat
mapTorqueToForceForParticle(particleData[ii], mapTorqueToForceForParticle(particleData[ii],
particleData[multipoleAtomZs[ii]], particleData[multipoleAtomXs[ii]], particleData[multipoleAtomZs[ii]], particleData[multipoleAtomXs[ii]],
multipoleAtomYs[ii] > -1 ? &particleData[multipoleAtomYs[ii]] : NULL, multipoleAtomYs[ii] > -1 ? &particleData[multipoleAtomYs[ii]] : NULL,
axisTypes[ii], torques[ii], forces); axisTypes[ii], torques[ii], forces);
} }
} }
} }
...@@ -1790,7 +1790,7 @@ RealOpenMM AmoebaReferenceMultipoleForce::calculateElectrostatic(const vector<Mu ...@@ -1790,7 +1790,7 @@ RealOpenMM AmoebaReferenceMultipoleForce::calculateElectrostatic(const vector<Mu
vector<RealOpenMM> scaleFactors(LAST_SCALE_TYPE_INDEX); vector<RealOpenMM> scaleFactors(LAST_SCALE_TYPE_INDEX);
for (unsigned int kk = 0; kk < scaleFactors.size(); kk++) { for (unsigned int kk = 0; kk < scaleFactors.size(); kk++) {
scaleFactors[kk] = 1.0; scaleFactors[kk] = 1.0;
} }
// main loop over particle pairs // main loop over particle pairs
...@@ -1943,6 +1943,64 @@ void AmoebaReferenceMultipoleForce::calculateInducedDipoles(const vector<RealVec ...@@ -1943,6 +1943,64 @@ void AmoebaReferenceMultipoleForce::calculateInducedDipoles(const vector<RealVec
outputInducedDipoles = _inducedDipole; outputInducedDipoles = _inducedDipole;
} }
void AmoebaReferenceMultipoleForce::calculateLabFramePermanentDipoles(const vector<RealVec>& particlePositions,
const vector<RealOpenMM>& charges,
const vector<RealOpenMM>& dipoles,
const vector<RealOpenMM>& quadrupoles,
const vector<RealOpenMM>& tholes,
const vector<RealOpenMM>& dampingFactors,
const vector<RealOpenMM>& polarity,
const vector<int>& axisTypes,
const vector<int>& multipoleAtomZs,
const vector<int>& multipoleAtomXs,
const vector<int>& multipoleAtomYs,
const vector< vector< vector<int> > >& multipoleAtomCovalentInfo,
vector<RealVec>& outputRotatedPermanentDipoles) {
// setup, including calculating permanent dipoles
vector<MultipoleParticleData> particleData;
setup(particlePositions, charges, dipoles, quadrupoles, tholes,
dampingFactors, polarity, axisTypes, multipoleAtomZs, multipoleAtomXs, multipoleAtomYs,
multipoleAtomCovalentInfo, particleData);
outputRotatedPermanentDipoles.resize(_numParticles);
for (int i = 0; i < _numParticles; i++)
{
outputRotatedPermanentDipoles[i] = particleData[i].dipole;
}
}
void AmoebaReferenceMultipoleForce::calculateTotalDipoles(const vector<RealVec>& particlePositions,
const vector<RealOpenMM>& charges,
const vector<RealOpenMM>& dipoles,
const vector<RealOpenMM>& quadrupoles,
const vector<RealOpenMM>& tholes,
const vector<RealOpenMM>& dampingFactors,
const vector<RealOpenMM>& polarity,
const vector<int>& axisTypes,
const vector<int>& multipoleAtomZs,
const vector<int>& multipoleAtomXs,
const vector<int>& multipoleAtomYs,
const vector< vector< vector<int> > >& multipoleAtomCovalentInfo,
vector<RealVec>& outputTotalDipoles) {
// setup, including calculating permanent dipoles
vector<MultipoleParticleData> particleData;
setup(particlePositions, charges, dipoles, quadrupoles, tholes,
dampingFactors, polarity, axisTypes, multipoleAtomZs, multipoleAtomXs, multipoleAtomYs,
multipoleAtomCovalentInfo, particleData);
outputTotalDipoles.resize(_numParticles);
for (int i = 0; i < _numParticles; i++)
{
for (int j = 0; j < 3; j++)
{
outputTotalDipoles[i][j] = particleData[i].dipole[j] + _inducedDipole[i][j];
}
}
}
void AmoebaReferenceMultipoleForce::calculateAmoebaSystemMultipoleMoments(const vector<RealOpenMM>& masses, void AmoebaReferenceMultipoleForce::calculateAmoebaSystemMultipoleMoments(const vector<RealOpenMM>& masses,
const vector<RealVec>& particlePositions, const vector<RealVec>& particlePositions,
const vector<RealOpenMM>& charges, const vector<RealOpenMM>& charges,
...@@ -2018,7 +2076,7 @@ void AmoebaReferenceMultipoleForce::calculateAmoebaSystemMultipoleMoments(const ...@@ -2018,7 +2076,7 @@ void AmoebaReferenceMultipoleForce::calculateAmoebaSystemMultipoleMoments(const
} }
// convert the quadrupole from traced to traceless form // convert the quadrupole from traced to traceless form
outputMultipoleMoments.resize(13); outputMultipoleMoments.resize(13);
RealOpenMM qave = (xxqdp + yyqdp + zzqdp)/3.0; RealOpenMM qave = (xxqdp + yyqdp + zzqdp)/3.0;
outputMultipoleMoments[4] = 0.5*(xxqdp-qave); outputMultipoleMoments[4] = 0.5*(xxqdp-qave);
...@@ -2041,7 +2099,7 @@ void AmoebaReferenceMultipoleForce::calculateAmoebaSystemMultipoleMoments(const ...@@ -2041,7 +2099,7 @@ void AmoebaReferenceMultipoleForce::calculateAmoebaSystemMultipoleMoments(const
outputMultipoleMoments[7] = outputMultipoleMoments[5]; outputMultipoleMoments[7] = outputMultipoleMoments[5];
outputMultipoleMoments[10] = outputMultipoleMoments[6]; outputMultipoleMoments[10] = outputMultipoleMoments[6];
outputMultipoleMoments[11] = outputMultipoleMoments[9]; outputMultipoleMoments[11] = outputMultipoleMoments[9];
RealOpenMM debye = 4.80321; RealOpenMM debye = 4.80321;
outputMultipoleMoments[0] = netchg; outputMultipoleMoments[0] = netchg;
...@@ -2050,16 +2108,16 @@ void AmoebaReferenceMultipoleForce::calculateAmoebaSystemMultipoleMoments(const ...@@ -2050,16 +2108,16 @@ void AmoebaReferenceMultipoleForce::calculateAmoebaSystemMultipoleMoments(const
outputMultipoleMoments[1] = dpl[0]; outputMultipoleMoments[1] = dpl[0];
outputMultipoleMoments[2] = dpl[1]; outputMultipoleMoments[2] = dpl[1];
outputMultipoleMoments[3] = dpl[2]; outputMultipoleMoments[3] = dpl[2];
debye *= 3.0; debye *= 3.0;
for (unsigned int ii = 4; ii < 13; ii++) { for (unsigned int ii = 4; ii < 13; ii++) {
outputMultipoleMoments[ii] *= 100.0*debye; outputMultipoleMoments[ii] *= 100.0*debye;
} }
} }
RealOpenMM AmoebaReferenceMultipoleForce::calculateElectrostaticPotentialForParticleGridPoint(const MultipoleParticleData& particleI, const RealVec& gridPoint) const RealOpenMM AmoebaReferenceMultipoleForce::calculateElectrostaticPotentialForParticleGridPoint(const MultipoleParticleData& particleI, const RealVec& gridPoint) const
{ {
RealVec deltaR = particleI.position - gridPoint; RealVec deltaR = particleI.position - gridPoint;
getPeriodicDelta(deltaR); getPeriodicDelta(deltaR);
...@@ -2101,7 +2159,7 @@ void AmoebaReferenceMultipoleForce::calculateElectrostaticPotential(const vector ...@@ -2101,7 +2159,7 @@ void AmoebaReferenceMultipoleForce::calculateElectrostaticPotential(const vector
{ {
// setup, including calculating induced dipoles // setup, including calculating induced dipoles
// initialize potential // initialize potential
// calculate contribution of each particle to potential at grid point // calculate contribution of each particle to potential at grid point
// apply prefactor // apply prefactor
...@@ -2130,12 +2188,12 @@ void AmoebaReferenceMultipoleForce::calculateElectrostaticPotential(const vector ...@@ -2130,12 +2188,12 @@ void AmoebaReferenceMultipoleForce::calculateElectrostaticPotential(const vector
AmoebaReferenceMultipoleForce::UpdateInducedDipoleFieldStruct::UpdateInducedDipoleFieldStruct(vector<OpenMM::RealVec>& inputFixed_E_Field, vector<OpenMM::RealVec>& inputInducedDipoles, vector<vector<RealVec> >& extrapolatedDipoles, vector<vector<RealOpenMM> >& extrapolatedDipoleFieldGradient) : AmoebaReferenceMultipoleForce::UpdateInducedDipoleFieldStruct::UpdateInducedDipoleFieldStruct(vector<OpenMM::RealVec>& inputFixed_E_Field, vector<OpenMM::RealVec>& inputInducedDipoles, vector<vector<RealVec> >& extrapolatedDipoles, vector<vector<RealOpenMM> >& extrapolatedDipoleFieldGradient) :
fixedMultipoleField(&inputFixed_E_Field), inducedDipoles(&inputInducedDipoles), extrapolatedDipoles(&extrapolatedDipoles), extrapolatedDipoleFieldGradient(&extrapolatedDipoleFieldGradient) { fixedMultipoleField(&inputFixed_E_Field), inducedDipoles(&inputInducedDipoles), extrapolatedDipoles(&extrapolatedDipoles), extrapolatedDipoleFieldGradient(&extrapolatedDipoleFieldGradient) {
inducedDipoleField.resize(fixedMultipoleField->size()); inducedDipoleField.resize(fixedMultipoleField->size());
} }
AmoebaReferenceGeneralizedKirkwoodMultipoleForce::AmoebaReferenceGeneralizedKirkwoodMultipoleForce(AmoebaReferenceGeneralizedKirkwoodForce* amoebaReferenceGeneralizedKirkwoodForce) : AmoebaReferenceGeneralizedKirkwoodMultipoleForce::AmoebaReferenceGeneralizedKirkwoodMultipoleForce(AmoebaReferenceGeneralizedKirkwoodForce* amoebaReferenceGeneralizedKirkwoodForce) :
AmoebaReferenceMultipoleForce(NoCutoff), AmoebaReferenceMultipoleForce(NoCutoff),
_amoebaReferenceGeneralizedKirkwoodForce(amoebaReferenceGeneralizedKirkwoodForce), _amoebaReferenceGeneralizedKirkwoodForce(amoebaReferenceGeneralizedKirkwoodForce),
_gkc(2.455) _gkc(2.455)
{ {
RealOpenMM solventDielectric = _amoebaReferenceGeneralizedKirkwoodForce->getSolventDielectric(); RealOpenMM solventDielectric = _amoebaReferenceGeneralizedKirkwoodForce->getSolventDielectric();
...@@ -2156,33 +2214,33 @@ AmoebaReferenceGeneralizedKirkwoodMultipoleForce::AmoebaReferenceGeneralizedKirk ...@@ -2156,33 +2214,33 @@ AmoebaReferenceGeneralizedKirkwoodMultipoleForce::AmoebaReferenceGeneralizedKirk
for (unsigned int ii = 0; ii < _scaledRadii.size(); ii++) { for (unsigned int ii = 0; ii < _scaledRadii.size(); ii++) {
_scaledRadii[ii] *= _atomicRadii[ii]; _scaledRadii[ii] *= _atomicRadii[ii];
} }
} }
AmoebaReferenceGeneralizedKirkwoodMultipoleForce::~AmoebaReferenceGeneralizedKirkwoodMultipoleForce() AmoebaReferenceGeneralizedKirkwoodMultipoleForce::~AmoebaReferenceGeneralizedKirkwoodMultipoleForce()
{ {
delete _amoebaReferenceGeneralizedKirkwoodForce; delete _amoebaReferenceGeneralizedKirkwoodForce;
}; };
int AmoebaReferenceGeneralizedKirkwoodMultipoleForce::getIncludeCavityTerm() const int AmoebaReferenceGeneralizedKirkwoodMultipoleForce::getIncludeCavityTerm() const
{ {
return _includeCavityTerm; return _includeCavityTerm;
}; };
RealOpenMM AmoebaReferenceGeneralizedKirkwoodMultipoleForce::getProbeRadius() const RealOpenMM AmoebaReferenceGeneralizedKirkwoodMultipoleForce::getProbeRadius() const
{ {
return _probeRadius; return _probeRadius;
}; };
RealOpenMM AmoebaReferenceGeneralizedKirkwoodMultipoleForce::getSurfaceAreaFactor() const RealOpenMM AmoebaReferenceGeneralizedKirkwoodMultipoleForce::getSurfaceAreaFactor() const
{ {
return _surfaceAreaFactor; return _surfaceAreaFactor;
}; };
RealOpenMM AmoebaReferenceGeneralizedKirkwoodMultipoleForce::getDielectricOffset() const RealOpenMM AmoebaReferenceGeneralizedKirkwoodMultipoleForce::getDielectricOffset() const
{ {
return _dielectricOffset; return _dielectricOffset;
}; };
void AmoebaReferenceGeneralizedKirkwoodMultipoleForce::zeroFixedMultipoleFields() void AmoebaReferenceGeneralizedKirkwoodMultipoleForce::zeroFixedMultipoleFields()
{ {
this->AmoebaReferenceMultipoleForce::zeroFixedMultipoleFields(); this->AmoebaReferenceMultipoleForce::zeroFixedMultipoleFields();
...@@ -2197,7 +2255,7 @@ void AmoebaReferenceGeneralizedKirkwoodMultipoleForce::calculateFixedMultipoleFi ...@@ -2197,7 +2255,7 @@ void AmoebaReferenceGeneralizedKirkwoodMultipoleForce::calculateFixedMultipoleFi
this->AmoebaReferenceMultipoleForce::calculateFixedMultipoleFieldPairIxn(particleI, particleJ, dScale, pScale); this->AmoebaReferenceMultipoleForce::calculateFixedMultipoleFieldPairIxn(particleI, particleJ, dScale, pScale);
// get deltaR, R2, and R between 2 atoms // get deltaR, R2, and R between 2 atoms
RealVec deltaR = particleJ.position - particleI.position; RealVec deltaR = particleJ.position - particleI.position;
RealOpenMM r = SQRT(deltaR.dot(deltaR)); RealOpenMM r = SQRT(deltaR.dot(deltaR));
RealOpenMM rb2 = _bornRadii[particleI.particleIndex]*_bornRadii[particleJ.particleIndex]; RealOpenMM rb2 = _bornRadii[particleI.particleIndex]*_bornRadii[particleJ.particleIndex];
...@@ -2207,36 +2265,36 @@ void AmoebaReferenceGeneralizedKirkwoodMultipoleForce::calculateFixedMultipoleFi ...@@ -2207,36 +2265,36 @@ void AmoebaReferenceGeneralizedKirkwoodMultipoleForce::calculateFixedMultipoleFi
RealOpenMM uxi = particleI.dipole[0]; RealOpenMM uxi = particleI.dipole[0];
RealOpenMM uyi = particleI.dipole[1]; RealOpenMM uyi = particleI.dipole[1];
RealOpenMM uzi = particleI.dipole[2]; RealOpenMM uzi = particleI.dipole[2];
RealOpenMM qxxi = particleI.quadrupole[QXX]; RealOpenMM qxxi = particleI.quadrupole[QXX];
RealOpenMM qxyi = particleI.quadrupole[QXY]; RealOpenMM qxyi = particleI.quadrupole[QXY];
RealOpenMM qxzi = particleI.quadrupole[QXZ]; RealOpenMM qxzi = particleI.quadrupole[QXZ];
RealOpenMM qyyi = particleI.quadrupole[QYY]; RealOpenMM qyyi = particleI.quadrupole[QYY];
RealOpenMM qyzi = particleI.quadrupole[QYZ]; RealOpenMM qyzi = particleI.quadrupole[QYZ];
RealOpenMM qzzi = particleI.quadrupole[QZZ]; RealOpenMM qzzi = particleI.quadrupole[QZZ];
RealOpenMM xr = deltaR[0]; RealOpenMM xr = deltaR[0];
RealOpenMM yr = deltaR[1]; RealOpenMM yr = deltaR[1];
RealOpenMM zr = deltaR[2]; RealOpenMM zr = deltaR[2];
RealOpenMM ck = particleJ.charge; RealOpenMM ck = particleJ.charge;
RealOpenMM xr2 = xr*xr; RealOpenMM xr2 = xr*xr;
RealOpenMM yr2 = yr*yr; RealOpenMM yr2 = yr*yr;
RealOpenMM zr2 = zr*zr; RealOpenMM zr2 = zr*zr;
RealOpenMM r2 = xr2 + yr2 + zr2; RealOpenMM r2 = xr2 + yr2 + zr2;
RealOpenMM uxk = particleJ.dipole[0]; RealOpenMM uxk = particleJ.dipole[0];
RealOpenMM uyk = particleJ.dipole[1]; RealOpenMM uyk = particleJ.dipole[1];
RealOpenMM uzk = particleJ.dipole[2]; RealOpenMM uzk = particleJ.dipole[2];
RealOpenMM qxxk = particleJ.quadrupole[QXX]; RealOpenMM qxxk = particleJ.quadrupole[QXX];
RealOpenMM qxyk = particleJ.quadrupole[QXY]; RealOpenMM qxyk = particleJ.quadrupole[QXY];
RealOpenMM qxzk = particleJ.quadrupole[QXZ]; RealOpenMM qxzk = particleJ.quadrupole[QXZ];
RealOpenMM qyyk = particleJ.quadrupole[QYY]; RealOpenMM qyyk = particleJ.quadrupole[QYY];
RealOpenMM qyzk = particleJ.quadrupole[QYZ]; RealOpenMM qyzk = particleJ.quadrupole[QYZ];
RealOpenMM qzzk = particleJ.quadrupole[QZZ]; RealOpenMM qzzk = particleJ.quadrupole[QZZ];
RealOpenMM expterm = EXP(-r2/(_gkc*rb2)); RealOpenMM expterm = EXP(-r2/(_gkc*rb2));
RealOpenMM expc = expterm/_gkc; RealOpenMM expc = expterm/_gkc;
RealOpenMM dexpc = -2.0/(_gkc*rb2); RealOpenMM dexpc = -2.0/(_gkc*rb2);
...@@ -2424,11 +2482,11 @@ void AmoebaReferenceGeneralizedKirkwoodMultipoleForce::calculateFixedMultipoleFi ...@@ -2424,11 +2482,11 @@ void AmoebaReferenceGeneralizedKirkwoodMultipoleForce::calculateFixedMultipoleFi
void AmoebaReferenceGeneralizedKirkwoodMultipoleForce::calculateInducedDipolePairGkIxn(const MultipoleParticleData& particleI, void AmoebaReferenceGeneralizedKirkwoodMultipoleForce::calculateInducedDipolePairGkIxn(const MultipoleParticleData& particleI,
const MultipoleParticleData& particleJ, const MultipoleParticleData& particleJ,
const vector<OpenMM::RealVec>& inputFields, const vector<OpenMM::RealVec>& inputFields,
vector<OpenMM::RealVec>& outputFields) const vector<OpenMM::RealVec>& outputFields) const
{ {
RealOpenMM a[3][3]; RealOpenMM a[3][3];
RealVec deltaR = particleJ.position - particleI.position; RealVec deltaR = particleJ.position - particleI.position;
RealOpenMM r = SQRT(deltaR.dot(deltaR)); RealOpenMM r = SQRT(deltaR.dot(deltaR));
...@@ -2446,7 +2504,7 @@ void AmoebaReferenceGeneralizedKirkwoodMultipoleForce::calculateInducedDipolePai ...@@ -2446,7 +2504,7 @@ void AmoebaReferenceGeneralizedKirkwoodMultipoleForce::calculateInducedDipolePai
RealOpenMM r2 = xr2 + yr2 + zr2; RealOpenMM r2 = xr2 + yr2 + zr2;
RealOpenMM expterm = EXP(-r2/(_gkc*rb2)); RealOpenMM expterm = EXP(-r2/(_gkc*rb2));
RealOpenMM expc = expterm /_gkc; RealOpenMM expc = expterm /_gkc;
RealOpenMM gf2 = 1.0/(r2+rb2*expterm); RealOpenMM gf2 = 1.0/(r2+rb2*expterm);
RealOpenMM gf = SQRT(gf2); RealOpenMM gf = SQRT(gf2);
...@@ -2457,7 +2515,7 @@ void AmoebaReferenceGeneralizedKirkwoodMultipoleForce::calculateInducedDipolePai ...@@ -2457,7 +2515,7 @@ void AmoebaReferenceGeneralizedKirkwoodMultipoleForce::calculateInducedDipolePai
RealVec duks = inputFields[jIndex]; RealVec duks = inputFields[jIndex];
// reaction potential auxiliary terms // reaction potential auxiliary terms
a[1][0] = -gf3; a[1][0] = -gf3;
a[2][0] = 3.0*gf5; a[2][0] = 3.0*gf5;
...@@ -2465,22 +2523,22 @@ void AmoebaReferenceGeneralizedKirkwoodMultipoleForce::calculateInducedDipolePai ...@@ -2465,22 +2523,22 @@ void AmoebaReferenceGeneralizedKirkwoodMultipoleForce::calculateInducedDipolePai
RealOpenMM expc1 = 1.0 - expc; RealOpenMM expc1 = 1.0 - expc;
a[1][1] = expc1*a[2][0]; a[1][1] = expc1*a[2][0];
// unweighted dipole reaction potential gradient tensor // unweighted dipole reaction potential gradient tensor
RealVec gux, guy, guz; RealVec gux, guy, guz;
gux[0] = (a[1][0] + xr2*a[1][1]); gux[0] = (a[1][0] + xr2*a[1][1]);
gux[1] = xr*yr*a[1][1]; gux[1] = xr*yr*a[1][1];
gux[2] = xr*zr*a[1][1]; gux[2] = xr*zr*a[1][1];
guy[0] = gux[1]; guy[0] = gux[1];
guy[1] = (a[1][0] + yr2*a[1][1]); guy[1] = (a[1][0] + yr2*a[1][1]);
guy[2] = yr*zr*a[1][1]; guy[2] = yr*zr*a[1][1];
guz[0] = gux[2]; guz[0] = gux[2];
guz[1] = guy[2]; guz[1] = guy[2];
guz[2] = (a[1][0] + zr2*a[1][1]); guz[2] = (a[1][0] + zr2*a[1][1]);
outputFields[iIndex][0] += _fd*duks.dot(gux); outputFields[iIndex][0] += _fd*duks.dot(gux);
outputFields[iIndex][1] += _fd*duks.dot(guy); outputFields[iIndex][1] += _fd*duks.dot(guy);
outputFields[iIndex][2] += _fd*duks.dot(guz); outputFields[iIndex][2] += _fd*duks.dot(guz);
...@@ -2527,8 +2585,8 @@ void AmoebaReferenceGeneralizedKirkwoodMultipoleForce::calculateInducedDipoles(c ...@@ -2527,8 +2585,8 @@ void AmoebaReferenceGeneralizedKirkwoodMultipoleForce::calculateInducedDipoles(c
for (unsigned int ii = 0; ii < _numParticles; ii++) { for (unsigned int ii = 0; ii < _numParticles; ii++) {
_fixedMultipoleField[ii] *= particleData[ii].polarity; _fixedMultipoleField[ii] *= particleData[ii].polarity;
_fixedMultipoleFieldPolar[ii] *= particleData[ii].polarity; _fixedMultipoleFieldPolar[ii] *= particleData[ii].polarity;
_gkField[ii] *= particleData[ii].polarity; _gkField[ii] *= particleData[ii].polarity;
_inducedDipole[ii] = _fixedMultipoleField[ii]; _inducedDipole[ii] = _fixedMultipoleField[ii];
_inducedDipolePolar[ii] = _fixedMultipoleFieldPolar[ii]; _inducedDipolePolar[ii] = _fixedMultipoleFieldPolar[ii];
...@@ -2561,7 +2619,7 @@ RealOpenMM AmoebaReferenceGeneralizedKirkwoodMultipoleForce::calculateKirkwoodPa ...@@ -2561,7 +2619,7 @@ RealOpenMM AmoebaReferenceGeneralizedKirkwoodMultipoleForce::calculateKirkwoodPa
const MultipoleParticleData& particleJ, const MultipoleParticleData& particleJ,
vector<RealVec>& forces, vector<RealVec>& forces,
vector<RealVec>& torques, vector<RealVec>& torques,
vector<RealOpenMM>& dBorn) const vector<RealOpenMM>& dBorn) const
{ {
RealOpenMM e,ei; RealOpenMM e,ei;
...@@ -2612,9 +2670,9 @@ RealOpenMM AmoebaReferenceGeneralizedKirkwoodMultipoleForce::calculateKirkwoodPa ...@@ -2612,9 +2670,9 @@ RealOpenMM AmoebaReferenceGeneralizedKirkwoodMultipoleForce::calculateKirkwoodPa
szi = _inducedDipoleS[iIndex][2] + _inducedDipolePolarS[iIndex][2]; szi = _inducedDipoleS[iIndex][2] + _inducedDipolePolarS[iIndex][2];
// decide whether to compute the current interaction // decide whether to compute the current interaction
RealVec deltaR = particleJ.position - particleI.position; RealVec deltaR = particleJ.position - particleI.position;
RealOpenMM r = SQRT(deltaR.dot(deltaR)); RealOpenMM r = SQRT(deltaR.dot(deltaR));
xr = deltaR[0]; xr = deltaR[0];
yr = deltaR[1]; yr = deltaR[1];
...@@ -4006,7 +4064,7 @@ RealOpenMM AmoebaReferenceGeneralizedKirkwoodMultipoleForce::calculateElectrosta ...@@ -4006,7 +4064,7 @@ RealOpenMM AmoebaReferenceGeneralizedKirkwoodMultipoleForce::calculateElectrosta
vector<RealOpenMM> scaleFactors(LAST_SCALE_TYPE_INDEX); vector<RealOpenMM> scaleFactors(LAST_SCALE_TYPE_INDEX);
for (unsigned int kk = 0; kk < scaleFactors.size(); kk++) { for (unsigned int kk = 0; kk < scaleFactors.size(); kk++) {
scaleFactors[kk] = 1.0; scaleFactors[kk] = 1.0;
} }
RealOpenMM eDiffEnergy = 0.0; RealOpenMM eDiffEnergy = 0.0;
for (unsigned int ii = 0; ii < particleData.size(); ii++) { for (unsigned int ii = 0; ii < particleData.size(); ii++) {
...@@ -4081,7 +4139,7 @@ RealOpenMM AmoebaReferenceGeneralizedKirkwoodMultipoleForce::calculateElectrosta ...@@ -4081,7 +4139,7 @@ RealOpenMM AmoebaReferenceGeneralizedKirkwoodMultipoleForce::calculateElectrosta
} }
void AmoebaReferenceGeneralizedKirkwoodMultipoleForce::calculateGrycukChainRulePairIxn(const MultipoleParticleData& particleI, const MultipoleParticleData& particleJ, void AmoebaReferenceGeneralizedKirkwoodMultipoleForce::calculateGrycukChainRulePairIxn(const MultipoleParticleData& particleI, const MultipoleParticleData& particleJ,
const vector<RealOpenMM>& dBorn, vector<RealVec>& forces) const const vector<RealOpenMM>& dBorn, vector<RealVec>& forces) const
{ {
unsigned int iIndex = particleI.particleIndex; unsigned int iIndex = particleI.particleIndex;
unsigned int jIndex = particleJ.particleIndex; unsigned int jIndex = particleJ.particleIndex;
...@@ -4129,7 +4187,7 @@ void AmoebaReferenceGeneralizedKirkwoodMultipoleForce::calculateGrycukChainRuleP ...@@ -4129,7 +4187,7 @@ void AmoebaReferenceGeneralizedKirkwoodMultipoleForce::calculateGrycukChainRuleP
uik = r + sk; uik = r + sk;
uik4 = uik*uik; uik4 = uik*uik;
uik4 = uik4*uik4; uik4 = uik4*uik4;
de -= 0.25*M_PI*(sk2+4.0*sk*r+r2)/ (r2*uik4); de -= 0.25*M_PI*(sk2+4.0*sk*r+r2)/ (r2*uik4);
RealOpenMM dbr = term*de/r; RealOpenMM dbr = term*de/r;
de = dbr*dBorn[iIndex]; de = dbr*dBorn[iIndex];
...@@ -4138,7 +4196,7 @@ void AmoebaReferenceGeneralizedKirkwoodMultipoleForce::calculateGrycukChainRuleP ...@@ -4138,7 +4196,7 @@ void AmoebaReferenceGeneralizedKirkwoodMultipoleForce::calculateGrycukChainRuleP
forces[jIndex] += deltaR*de; forces[jIndex] += deltaR*de;
} }
RealOpenMM AmoebaReferenceGeneralizedKirkwoodMultipoleForce::calculateCavityTermEnergyAndForces(vector<RealOpenMM>& dBorn) const RealOpenMM AmoebaReferenceGeneralizedKirkwoodMultipoleForce::calculateCavityTermEnergyAndForces(vector<RealOpenMM>& dBorn) const
{ {
RealOpenMM energy = 0.0; RealOpenMM energy = 0.0;
...@@ -4165,7 +4223,7 @@ RealOpenMM AmoebaReferenceGeneralizedKirkwoodMultipoleForce::calculateKirkwoodED ...@@ -4165,7 +4223,7 @@ RealOpenMM AmoebaReferenceGeneralizedKirkwoodMultipoleForce::calculateKirkwoodED
const MultipoleParticleData& particleJ, const MultipoleParticleData& particleJ,
RealOpenMM pscale, RealOpenMM dscale, RealOpenMM pscale, RealOpenMM dscale,
vector<RealVec>& forces, vector<RealVec>& forces,
vector<RealVec>& torques) const vector<RealVec>& torques) const
{ {
static const RealOpenMM uscale = 1.0; static const RealOpenMM uscale = 1.0;
...@@ -4214,7 +4272,7 @@ RealOpenMM AmoebaReferenceGeneralizedKirkwoodMultipoleForce::calculateKirkwoodED ...@@ -4214,7 +4272,7 @@ RealOpenMM AmoebaReferenceGeneralizedKirkwoodMultipoleForce::calculateKirkwoodED
RealOpenMM ddsc7_3 = 0.0; RealOpenMM ddsc7_3 = 0.0;
// apply Thole polarization damping to scale factors // apply Thole polarization damping to scale factors
RealOpenMM damp = particleI.dampingFactor*particleJ.dampingFactor; RealOpenMM damp = particleI.dampingFactor*particleJ.dampingFactor;
if (damp != 0.0) { if (damp != 0.0) {
pgamma = particleJ.thole > particleI.thole ? particleI.thole : particleJ.thole; pgamma = particleJ.thole > particleI.thole ? particleI.thole : particleJ.thole;
...@@ -4251,9 +4309,9 @@ RealOpenMM AmoebaReferenceGeneralizedKirkwoodMultipoleForce::calculateKirkwoodED ...@@ -4251,9 +4309,9 @@ RealOpenMM AmoebaReferenceGeneralizedKirkwoodMultipoleForce::calculateKirkwoodED
psc3 = scale3*pscale; psc3 = scale3*pscale;
psc5 = scale5*pscale; psc5 = scale5*pscale;
psc7 = scale7*pscale; psc7 = scale7*pscale;
// construct auxiliary vectors for permanent terms // construct auxiliary vectors for permanent terms
RealOpenMM dixr1 = particleI.dipole[1]*zr - particleI.dipole[2]*yr; RealOpenMM dixr1 = particleI.dipole[1]*zr - particleI.dipole[2]*yr;
RealOpenMM dixr2 = particleI.dipole[2]*xr - particleI.dipole[0]*zr; RealOpenMM dixr2 = particleI.dipole[2]*xr - particleI.dipole[0]*zr;
RealOpenMM dixr3 = particleI.dipole[0]*yr - particleI.dipole[1]*xr; RealOpenMM dixr3 = particleI.dipole[0]*yr - particleI.dipole[1]*xr;
...@@ -4279,14 +4337,14 @@ RealOpenMM AmoebaReferenceGeneralizedKirkwoodMultipoleForce::calculateKirkwoodED ...@@ -4279,14 +4337,14 @@ RealOpenMM AmoebaReferenceGeneralizedKirkwoodMultipoleForce::calculateKirkwoodED
RealOpenMM rxqkr3 = xr*qkr2 - yr*qkr1; RealOpenMM rxqkr3 = xr*qkr2 - yr*qkr1;
// get intermediate variables for permanent energy terms // get intermediate variables for permanent energy terms
RealOpenMM sc3 = particleI.dipole[0]*xr + particleI.dipole[1]*yr + particleI.dipole[2]*zr; RealOpenMM sc3 = particleI.dipole[0]*xr + particleI.dipole[1]*yr + particleI.dipole[2]*zr;
RealOpenMM sc4 = particleJ.dipole[0]*xr + particleJ.dipole[1]*yr + particleJ.dipole[2]*zr; RealOpenMM sc4 = particleJ.dipole[0]*xr + particleJ.dipole[1]*yr + particleJ.dipole[2]*zr;
RealOpenMM sc5 = qir1*xr + qir2*yr + qir3*zr; RealOpenMM sc5 = qir1*xr + qir2*yr + qir3*zr;
RealOpenMM sc6 = qkr1*xr + qkr2*yr + qkr3*zr; RealOpenMM sc6 = qkr1*xr + qkr2*yr + qkr3*zr;
// construct auxiliary vectors for induced terms // construct auxiliary vectors for induced terms
RealOpenMM dixuk1 = particleI.dipole[1]*_inducedDipoleS[jIndex][2] - particleI.dipole[2]*_inducedDipoleS[jIndex][1]; RealOpenMM dixuk1 = particleI.dipole[1]*_inducedDipoleS[jIndex][2] - particleI.dipole[2]*_inducedDipoleS[jIndex][1];
RealOpenMM dixuk2 = particleI.dipole[2]*_inducedDipoleS[jIndex][0] - particleI.dipole[0]*_inducedDipoleS[jIndex][2]; RealOpenMM dixuk2 = particleI.dipole[2]*_inducedDipoleS[jIndex][0] - particleI.dipole[0]*_inducedDipoleS[jIndex][2];
RealOpenMM dixuk3 = particleI.dipole[0]*_inducedDipoleS[jIndex][1] - particleI.dipole[1]*_inducedDipoleS[jIndex][0]; RealOpenMM dixuk3 = particleI.dipole[0]*_inducedDipoleS[jIndex][1] - particleI.dipole[1]*_inducedDipoleS[jIndex][0];
...@@ -4350,7 +4408,7 @@ RealOpenMM AmoebaReferenceGeneralizedKirkwoodMultipoleForce::calculateKirkwoodED ...@@ -4350,7 +4408,7 @@ RealOpenMM AmoebaReferenceGeneralizedKirkwoodMultipoleForce::calculateKirkwoodED
RealOpenMM rxqkuip1 = yr*qkuip3 - zr*qkuip2; RealOpenMM rxqkuip1 = yr*qkuip3 - zr*qkuip2;
RealOpenMM rxqkuip2 = zr*qkuip1 - xr*qkuip3; RealOpenMM rxqkuip2 = zr*qkuip1 - xr*qkuip3;
RealOpenMM rxqkuip3 = xr*qkuip2 - yr*qkuip1; RealOpenMM rxqkuip3 = xr*qkuip2 - yr*qkuip1;
// get intermediate variables for induction energy terms // get intermediate variables for induction energy terms
RealOpenMM sci1 = _inducedDipoleS[iIndex][0]*particleJ.dipole[0] + _inducedDipoleS[iIndex][1]*particleJ.dipole[1] + RealOpenMM sci1 = _inducedDipoleS[iIndex][0]*particleJ.dipole[0] + _inducedDipoleS[iIndex][1]*particleJ.dipole[1] +
...@@ -4408,7 +4466,7 @@ RealOpenMM AmoebaReferenceGeneralizedKirkwoodMultipoleForce::calculateKirkwoodED ...@@ -4408,7 +4466,7 @@ RealOpenMM AmoebaReferenceGeneralizedKirkwoodMultipoleForce::calculateKirkwoodED
RealOpenMM gfi6 = -rr7*(sci3*psc7+scip3*dsc7); RealOpenMM gfi6 = -rr7*(sci3*psc7+scip3*dsc7);
// get the induced force // get the induced force
RealOpenMM ftm2i1 = gfi1*xr + 0.5* RealOpenMM ftm2i1 = gfi1*xr + 0.5*
(- rr3*particleJ.charge*(_inducedDipoleS[iIndex][0]*psc3+_inducedDipolePolarS[iIndex][0]*dsc3) (- rr3*particleJ.charge*(_inducedDipoleS[iIndex][0]*psc3+_inducedDipolePolarS[iIndex][0]*dsc3)
+ rr5*sc4*(_inducedDipoleS[iIndex][0]*psc5+_inducedDipolePolarS[iIndex][0]*dsc5) + rr5*sc4*(_inducedDipoleS[iIndex][0]*psc5+_inducedDipolePolarS[iIndex][0]*dsc5)
...@@ -4423,7 +4481,7 @@ RealOpenMM AmoebaReferenceGeneralizedKirkwoodMultipoleForce::calculateKirkwoodED ...@@ -4423,7 +4481,7 @@ RealOpenMM AmoebaReferenceGeneralizedKirkwoodMultipoleForce::calculateKirkwoodED
+ 0.5*gfi4*((qkui1-qiuk1)*psc5 + 0.5*gfi4*((qkui1-qiuk1)*psc5
+ (qkuip1-qiukp1)*dsc5) + (qkuip1-qiukp1)*dsc5)
+ gfi5*qir1 + gfi6*qkr1; + gfi5*qir1 + gfi6*qkr1;
RealOpenMM ftm2i2 = gfi1*yr + 0.5* RealOpenMM ftm2i2 = gfi1*yr + 0.5*
(- rr3*particleJ.charge*(_inducedDipoleS[iIndex][1]*psc3+_inducedDipolePolarS[iIndex][1]*dsc3) (- rr3*particleJ.charge*(_inducedDipoleS[iIndex][1]*psc3+_inducedDipolePolarS[iIndex][1]*dsc3)
+ rr5*sc4*(_inducedDipoleS[iIndex][1]*psc5+_inducedDipolePolarS[iIndex][1]*dsc5) + rr5*sc4*(_inducedDipoleS[iIndex][1]*psc5+_inducedDipolePolarS[iIndex][1]*dsc5)
...@@ -4453,7 +4511,7 @@ RealOpenMM AmoebaReferenceGeneralizedKirkwoodMultipoleForce::calculateKirkwoodED ...@@ -4453,7 +4511,7 @@ RealOpenMM AmoebaReferenceGeneralizedKirkwoodMultipoleForce::calculateKirkwoodED
+ 0.5*gfi4*((qkui3-qiuk3)*psc5 + 0.5*gfi4*((qkui3-qiuk3)*psc5
+ (qkuip3-qiukp3)*dsc5) + (qkuip3-qiukp3)*dsc5)
+ gfi5*qir3 + gfi6*qkr3; + gfi5*qir3 + gfi6*qkr3;
// intermediate values needed for partially excluded interactions // intermediate values needed for partially excluded interactions
RealOpenMM fridmp1 = 0.5*(rr3*((gli1+gli6)*pscale RealOpenMM fridmp1 = 0.5*(rr3*((gli1+gli6)*pscale
...@@ -4475,7 +4533,7 @@ RealOpenMM AmoebaReferenceGeneralizedKirkwoodMultipoleForce::calculateKirkwoodED ...@@ -4475,7 +4533,7 @@ RealOpenMM AmoebaReferenceGeneralizedKirkwoodMultipoleForce::calculateKirkwoodED
+ rr7*(gli3*pscale+glip3*dscale)*ddsc7_3); + rr7*(gli3*pscale+glip3*dscale)*ddsc7_3);
// get the induced-induced derivative terms // get the induced-induced derivative terms
RealOpenMM findmp1 = 0.5*uscale*(scip2*rr3*ddsc3_1 RealOpenMM findmp1 = 0.5*uscale*(scip2*rr3*ddsc3_1
- rr5*ddsc5_1*(sci3*scip4+scip3*sci4)); - rr5*ddsc5_1*(sci3*scip4+scip3*sci4));
...@@ -4501,12 +4559,12 @@ RealOpenMM AmoebaReferenceGeneralizedKirkwoodMultipoleForce::calculateKirkwoodED ...@@ -4501,12 +4559,12 @@ RealOpenMM AmoebaReferenceGeneralizedKirkwoodMultipoleForce::calculateKirkwoodED
ftm2i1 -= fdir1 - findmp1; ftm2i1 -= fdir1 - findmp1;
ftm2i2 -= fdir2 - findmp2; ftm2i2 -= fdir2 - findmp2;
ftm2i3 -= fdir3 - findmp3; ftm2i3 -= fdir3 - findmp3;
} }
// now perform the torque calculation // now perform the torque calculation
// intermediate terms for torque between multipoles i and j // intermediate terms for torque between multipoles i and j
RealOpenMM gti2 = 0.5*(sci4*psc5+scip4*dsc5)*rr5; RealOpenMM gti2 = 0.5*(sci4*psc5+scip4*dsc5)*rr5;
RealOpenMM gti3 = 0.5*(sci3*psc5+scip3*dsc5)*rr5; RealOpenMM gti3 = 0.5*(sci3*psc5+scip3*dsc5)*rr5;
RealOpenMM gti4 = gfi4; RealOpenMM gti4 = gfi4;
...@@ -4538,7 +4596,7 @@ RealOpenMM AmoebaReferenceGeneralizedKirkwoodMultipoleForce::calculateKirkwoodED ...@@ -4538,7 +4596,7 @@ RealOpenMM AmoebaReferenceGeneralizedKirkwoodMultipoleForce::calculateKirkwoodED
RealOpenMM ttm3i3 = -rr3*(dkxui3*psc3+dkxuip3*dsc3)*0.5 RealOpenMM ttm3i3 = -rr3*(dkxui3*psc3+dkxuip3*dsc3)*0.5
+ gti3*dkxr3 - gti4*((uixqkr3+rxqkui3)*psc5 + gti3*dkxr3 - gti4*((uixqkr3+rxqkui3)*psc5
+(uixqkrp3+rxqkuip3)*dsc5)*0.5 - gti6*rxqkr3; +(uixqkrp3+rxqkuip3)*dsc5)*0.5 - gti6*rxqkr3;
// update force and torque // update force and torque
RealVec force, torqueI, torqueJ; RealVec force, torqueI, torqueJ;
...@@ -4802,7 +4860,7 @@ RealOpenMM AmoebaReferenceGeneralizedKirkwoodMultipoleForce::calculateKirkwoodED ...@@ -4802,7 +4860,7 @@ RealOpenMM AmoebaReferenceGeneralizedKirkwoodMultipoleForce::calculateKirkwoodED
+ gti3*dkxr3 - gti4*((uixqkr3+rxqkui3)*psc5 + gti3*dkxr3 - gti4*((uixqkr3+rxqkui3)*psc5
+(uixqkrp3+rxqkuip3)*dsc5)*0.5 - gti6*rxqkr3; +(uixqkrp3+rxqkuip3)*dsc5)*0.5 - gti6*rxqkr3;
// update force and torque // update force and torque
force[0] += ftm2i1; force[0] += ftm2i1;
force[1] += ftm2i2; force[1] += ftm2i2;
...@@ -4827,13 +4885,13 @@ const RealOpenMM AmoebaReferencePmeMultipoleForce::SQRT_PI = 1.77245385091; ...@@ -4827,13 +4885,13 @@ const RealOpenMM AmoebaReferencePmeMultipoleForce::SQRT_PI = 1.77245385091;
AmoebaReferencePmeMultipoleForce::AmoebaReferencePmeMultipoleForce() : AmoebaReferencePmeMultipoleForce::AmoebaReferencePmeMultipoleForce() :
AmoebaReferenceMultipoleForce(PME), AmoebaReferenceMultipoleForce(PME),
_cutoffDistance(1.0), _cutoffDistanceSquared(1.0), _cutoffDistance(1.0), _cutoffDistanceSquared(1.0),
_pmeGridSize(0), _totalGridSize(0), _alphaEwald(0.0) _pmeGridSize(0), _totalGridSize(0), _alphaEwald(0.0)
{ {
_fftplan = NULL; _fftplan = NULL;
_pmeGrid = NULL; _pmeGrid = NULL;
_pmeGridDimensions = IntVec(-1, -1, -1); _pmeGridDimensions = IntVec(-1, -1, -1);
} }
AmoebaReferencePmeMultipoleForce::~AmoebaReferencePmeMultipoleForce() AmoebaReferencePmeMultipoleForce::~AmoebaReferencePmeMultipoleForce()
{ {
...@@ -4844,19 +4902,19 @@ AmoebaReferencePmeMultipoleForce::~AmoebaReferencePmeMultipoleForce() ...@@ -4844,19 +4902,19 @@ AmoebaReferencePmeMultipoleForce::~AmoebaReferencePmeMultipoleForce()
delete _pmeGrid; delete _pmeGrid;
} }
}; };
RealOpenMM AmoebaReferencePmeMultipoleForce::getCutoffDistance() const RealOpenMM AmoebaReferencePmeMultipoleForce::getCutoffDistance() const
{ {
return _cutoffDistance; return _cutoffDistance;
}; };
void AmoebaReferencePmeMultipoleForce::setCutoffDistance(RealOpenMM cutoffDistance) void AmoebaReferencePmeMultipoleForce::setCutoffDistance(RealOpenMM cutoffDistance)
{ {
_cutoffDistance = cutoffDistance; _cutoffDistance = cutoffDistance;
_cutoffDistanceSquared = cutoffDistance*cutoffDistance; _cutoffDistanceSquared = cutoffDistance*cutoffDistance;
}; };
RealOpenMM AmoebaReferencePmeMultipoleForce::getAlphaEwald() const RealOpenMM AmoebaReferencePmeMultipoleForce::getAlphaEwald() const
{ {
return _alphaEwald; return _alphaEwald;
}; };
...@@ -4866,7 +4924,7 @@ void AmoebaReferencePmeMultipoleForce::setAlphaEwald(RealOpenMM alphaEwald) ...@@ -4866,7 +4924,7 @@ void AmoebaReferencePmeMultipoleForce::setAlphaEwald(RealOpenMM alphaEwald)
_alphaEwald = alphaEwald; _alphaEwald = alphaEwald;
}; };
void AmoebaReferencePmeMultipoleForce::getPmeGridDimensions(vector<int>& pmeGridDimensions) const void AmoebaReferencePmeMultipoleForce::getPmeGridDimensions(vector<int>& pmeGridDimensions) const
{ {
pmeGridDimensions.resize(3); pmeGridDimensions.resize(3);
...@@ -4879,7 +4937,7 @@ void AmoebaReferencePmeMultipoleForce::getPmeGridDimensions(vector<int>& pmeGrid ...@@ -4879,7 +4937,7 @@ void AmoebaReferencePmeMultipoleForce::getPmeGridDimensions(vector<int>& pmeGrid
void AmoebaReferencePmeMultipoleForce::setPmeGridDimensions(vector<int>& pmeGridDimensions) void AmoebaReferencePmeMultipoleForce::setPmeGridDimensions(vector<int>& pmeGridDimensions)
{ {
if ((pmeGridDimensions[0] == _pmeGridDimensions[0]) && if ((pmeGridDimensions[0] == _pmeGridDimensions[0]) &&
(pmeGridDimensions[1] == _pmeGridDimensions[1]) && (pmeGridDimensions[1] == _pmeGridDimensions[1]) &&
(pmeGridDimensions[2] == _pmeGridDimensions[2])) (pmeGridDimensions[2] == _pmeGridDimensions[2]))
return; return;
...@@ -4953,9 +5011,9 @@ void AmoebaReferencePmeMultipoleForce::initializePmeGrid() ...@@ -4953,9 +5011,9 @@ void AmoebaReferencePmeMultipoleForce::initializePmeGrid()
for (int jj = 0; jj < _totalGridSize; jj++) { for (int jj = 0; jj < _totalGridSize; jj++) {
_pmeGrid[jj].re = _pmeGrid[jj].im = 0.0; _pmeGrid[jj].re = _pmeGrid[jj].im = 0.0;
} }
} }
void AmoebaReferencePmeMultipoleForce::getPeriodicDelta(RealVec& deltaR) const void AmoebaReferencePmeMultipoleForce::getPeriodicDelta(RealVec& deltaR) const
{ {
deltaR -= _periodicBoxVectors[2]*FLOOR(deltaR[2]*_recipBoxVectors[2][2]+0.5); deltaR -= _periodicBoxVectors[2]*FLOOR(deltaR[2]*_recipBoxVectors[2][2]+0.5);
deltaR -= _periodicBoxVectors[1]*FLOOR(deltaR[1]*_recipBoxVectors[1][1]+0.5); deltaR -= _periodicBoxVectors[1]*FLOOR(deltaR[1]*_recipBoxVectors[1][1]+0.5);
...@@ -4965,8 +5023,8 @@ void AmoebaReferencePmeMultipoleForce::getPeriodicDelta(RealVec& deltaR) const ...@@ -4965,8 +5023,8 @@ void AmoebaReferencePmeMultipoleForce::getPeriodicDelta(RealVec& deltaR) const
void AmoebaReferencePmeMultipoleForce::getDampedInverseDistances(const MultipoleParticleData& particleI, void AmoebaReferencePmeMultipoleForce::getDampedInverseDistances(const MultipoleParticleData& particleI,
const MultipoleParticleData& particleJ, const MultipoleParticleData& particleJ,
RealOpenMM dscale, RealOpenMM pscale, RealOpenMM r, RealOpenMM dscale, RealOpenMM pscale, RealOpenMM r,
RealVec& dampedDInverseDistances, RealVec& dampedDInverseDistances,
RealVec& dampedPInverseDistances) const RealVec& dampedPInverseDistances) const
{ {
RealVec scaleFactor = RealVec(1.0, 1.0, 1.0); RealVec scaleFactor = RealVec(1.0, 1.0, 1.0);
...@@ -4984,8 +5042,8 @@ void AmoebaReferencePmeMultipoleForce::getDampedInverseDistances(const Multipole ...@@ -4984,8 +5042,8 @@ void AmoebaReferencePmeMultipoleForce::getDampedInverseDistances(const Multipole
scaleFactor[0] = 1.0 - expdamp; scaleFactor[0] = 1.0 - expdamp;
scaleFactor[1] = 1.0 - expdamp*(1.0-damp); scaleFactor[1] = 1.0 - expdamp*(1.0-damp);
scaleFactor[2] = 1.0 - expdamp*(1.0-damp+(0.6f*damp*damp)); scaleFactor[2] = 1.0 - expdamp*(1.0-damp+(0.6f*damp*damp));
} }
} }
RealVec dampedDScale = scaleFactor*dscale; RealVec dampedDScale = scaleFactor*dscale;
RealOpenMM r2 = r*r; RealOpenMM r2 = r*r;
...@@ -5000,7 +5058,7 @@ void AmoebaReferencePmeMultipoleForce::getDampedInverseDistances(const Multipole ...@@ -5000,7 +5058,7 @@ void AmoebaReferencePmeMultipoleForce::getDampedInverseDistances(const Multipole
dampedPInverseDistances = dampedDInverseDistances; dampedPInverseDistances = dampedDInverseDistances;
} else { } else {
RealVec dampedPScale = scaleFactor*pscale; RealVec dampedPScale = scaleFactor*pscale;
dampedPInverseDistances[0] = (1.0-dampedPScale[0])/r3; dampedPInverseDistances[0] = (1.0-dampedPScale[0])/r3;
dampedPInverseDistances[1] = 3.0*(1.0-dampedPScale[1])/r5; dampedPInverseDistances[1] = 3.0*(1.0-dampedPScale[1])/r5;
dampedPInverseDistances[2] = 15.0*(1.0-dampedPScale[2])/r7; dampedPInverseDistances[2] = 15.0*(1.0-dampedPScale[2])/r7;
} }
...@@ -5168,7 +5226,7 @@ void AmoebaReferencePmeMultipoleForce::calculateFixedMultipoleFieldPairIxn(const ...@@ -5168,7 +5226,7 @@ void AmoebaReferencePmeMultipoleForce::calculateFixedMultipoleFieldPairIxn(const
RealVec qj = RealVec(qxJ.dot(deltaR), qyJ.dot(deltaR), qzJ.dot(deltaR)); RealVec qj = RealVec(qxJ.dot(deltaR), qyJ.dot(deltaR), qzJ.dot(deltaR));
RealOpenMM qjr = qj.dot(deltaR); RealOpenMM qjr = qj.dot(deltaR);
RealVec fim = qj*(2.0*bn2) - particleJ.dipole*bn1 - deltaR*(bn1*particleJ.charge - bn2*djr+bn3*qjr); RealVec fim = qj*(2.0*bn2) - particleJ.dipole*bn1 - deltaR*(bn1*particleJ.charge - bn2*djr+bn3*qjr);
RealVec fjm = qi*(-2.0*bn2) - particleI.dipole*bn1 + deltaR*(bn1*particleI.charge + bn2*dir+bn3*qir); RealVec fjm = qi*(-2.0*bn2) - particleI.dipole*bn1 + deltaR*(bn1*particleI.charge + bn2*dir+bn3*qir);
...@@ -5297,7 +5355,7 @@ void AmoebaReferencePmeMultipoleForce::computeBSplinePoint(vector<RealOpenMM4>& ...@@ -5297,7 +5355,7 @@ void AmoebaReferencePmeMultipoleForce::computeBSplinePoint(vector<RealOpenMM4>&
/** /**
* Compute b-spline coefficients. * Compute b-spline coefficients.
*/ */
void AmoebaReferencePmeMultipoleForce::computeAmoebaBsplines(const vector<MultipoleParticleData>& particleData) void AmoebaReferencePmeMultipoleForce::computeAmoebaBsplines(const vector<MultipoleParticleData>& particleData)
{ {
// get the B-spline coefficients for each multipole site // get the B-spline coefficients for each multipole site
...@@ -5319,7 +5377,7 @@ void AmoebaReferencePmeMultipoleForce::computeAmoebaBsplines(const vector<Multip ...@@ -5319,7 +5377,7 @@ void AmoebaReferencePmeMultipoleForce::computeAmoebaBsplines(const vector<Multip
_thetai[jj][ii*AMOEBA_PME_ORDER+kk] = thetaiTemp[kk]; _thetai[jj][ii*AMOEBA_PME_ORDER+kk] = thetaiTemp[kk];
} }
} }
// Record the grid point. // Record the grid point.
_iGrid[ii] = igrid; _iGrid[ii] = igrid;
...@@ -5328,7 +5386,7 @@ void AmoebaReferencePmeMultipoleForce::computeAmoebaBsplines(const vector<Multip ...@@ -5328,7 +5386,7 @@ void AmoebaReferencePmeMultipoleForce::computeAmoebaBsplines(const vector<Multip
void AmoebaReferencePmeMultipoleForce::transformMultipolesToFractionalCoordinates(const vector<MultipoleParticleData>& particleData) { void AmoebaReferencePmeMultipoleForce::transformMultipolesToFractionalCoordinates(const vector<MultipoleParticleData>& particleData) {
// Build matrices for transforming the dipoles and quadrupoles. // Build matrices for transforming the dipoles and quadrupoles.
RealVec a[3]; RealVec a[3];
for (int i = 0; i < 3; i++) for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++) for (int j = 0; j < 3; j++)
...@@ -5364,7 +5422,7 @@ void AmoebaReferencePmeMultipoleForce::transformMultipolesToFractionalCoordinate ...@@ -5364,7 +5422,7 @@ void AmoebaReferencePmeMultipoleForce::transformMultipolesToFractionalCoordinate
void AmoebaReferencePmeMultipoleForce::transformPotentialToCartesianCoordinates(const vector<RealOpenMM>& fphi, vector<RealOpenMM>& cphi) const { void AmoebaReferencePmeMultipoleForce::transformPotentialToCartesianCoordinates(const vector<RealOpenMM>& fphi, vector<RealOpenMM>& cphi) const {
// Build a matrix for transforming the potential. // Build a matrix for transforming the potential.
RealVec a[3]; RealVec a[3];
for (int i = 0; i < 3; i++) for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++) for (int j = 0; j < 3; j++)
...@@ -5386,9 +5444,9 @@ void AmoebaReferencePmeMultipoleForce::transformPotentialToCartesianCoordinates( ...@@ -5386,9 +5444,9 @@ void AmoebaReferencePmeMultipoleForce::transformPotentialToCartesianCoordinates(
b[i][j] += a[index1[i]][index2[j]]*a[index2[i]][index1[j]]; b[i][j] += a[index1[i]][index2[j]]*a[index2[i]][index1[j]];
} }
} }
// Transform the potential. // Transform the potential.
for (int i = 0; i < _numParticles; i++) { for (int i = 0; i < _numParticles; i++) {
cphi[10*i] = fphi[20*i]; cphi[10*i] = fphi[20*i];
cphi[10*i+1] = a[0][0]*fphi[20*i+1] + a[0][1]*fphi[20*i+2] + a[0][2]*fphi[20*i+3]; cphi[10*i+1] = a[0][0]*fphi[20*i+1] + a[0][1]*fphi[20*i+2] + a[0][2]*fphi[20*i+3];
...@@ -5402,18 +5460,18 @@ void AmoebaReferencePmeMultipoleForce::transformPotentialToCartesianCoordinates( ...@@ -5402,18 +5460,18 @@ void AmoebaReferencePmeMultipoleForce::transformPotentialToCartesianCoordinates(
} }
} }
void AmoebaReferencePmeMultipoleForce::spreadFixedMultipolesOntoGrid(const vector<MultipoleParticleData>& particleData) void AmoebaReferencePmeMultipoleForce::spreadFixedMultipolesOntoGrid(const vector<MultipoleParticleData>& particleData)
{ {
transformMultipolesToFractionalCoordinates(particleData); transformMultipolesToFractionalCoordinates(particleData);
// Clear the grid. // Clear the grid.
for (int gridIndex = 0; gridIndex < _totalGridSize; gridIndex++) for (int gridIndex = 0; gridIndex < _totalGridSize; gridIndex++)
_pmeGrid[gridIndex] = t_complex(0, 0); _pmeGrid[gridIndex] = t_complex(0, 0);
// Loop over atoms and spread them on the grid. // Loop over atoms and spread them on the grid.
for (int atomIndex = 0; atomIndex < _numParticles; atomIndex++) { for (int atomIndex = 0; atomIndex < _numParticles; atomIndex++) {
RealOpenMM atomCharge = _transformed[atomIndex].charge; RealOpenMM atomCharge = _transformed[atomIndex].charge;
RealVec atomDipole = RealVec(_transformed[atomIndex].dipole[0], RealVec atomDipole = RealVec(_transformed[atomIndex].dipole[0],
...@@ -5597,19 +5655,19 @@ void AmoebaReferencePmeMultipoleForce::computeFixedPotentialFromGrid() ...@@ -5597,19 +5655,19 @@ void AmoebaReferencePmeMultipoleForce::computeFixedPotentialFromGrid()
void AmoebaReferencePmeMultipoleForce::spreadInducedDipolesOnGrid(const vector<RealVec>& inputInducedDipole, void AmoebaReferencePmeMultipoleForce::spreadInducedDipolesOnGrid(const vector<RealVec>& inputInducedDipole,
const vector<RealVec>& inputInducedDipolePolar) { const vector<RealVec>& inputInducedDipolePolar) {
// Create the matrix to convert from Cartesian to fractional coordinates. // Create the matrix to convert from Cartesian to fractional coordinates.
RealVec cartToFrac[3]; RealVec cartToFrac[3];
for (int i = 0; i < 3; i++) for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++) for (int j = 0; j < 3; j++)
cartToFrac[j][i] = _pmeGridDimensions[j]*_recipBoxVectors[i][j]; cartToFrac[j][i] = _pmeGridDimensions[j]*_recipBoxVectors[i][j];
// Clear the grid. // Clear the grid.
for (int gridIndex = 0; gridIndex < _totalGridSize; gridIndex++) for (int gridIndex = 0; gridIndex < _totalGridSize; gridIndex++)
_pmeGrid[gridIndex] = t_complex(0, 0); _pmeGrid[gridIndex] = t_complex(0, 0);
// Loop over atoms and spread them on the grid. // Loop over atoms and spread them on the grid.
for (int atomIndex = 0; atomIndex < _numParticles; atomIndex++) { for (int atomIndex = 0; atomIndex < _numParticles; atomIndex++) {
RealVec inducedDipole = RealVec(inputInducedDipole[atomIndex][0]*cartToFrac[0][0] + inputInducedDipole[atomIndex][1]*cartToFrac[0][1] + inputInducedDipole[atomIndex][2]*cartToFrac[0][2], RealVec inducedDipole = RealVec(inputInducedDipole[atomIndex][0]*cartToFrac[0][0] + inputInducedDipole[atomIndex][1]*cartToFrac[0][1] + inputInducedDipole[atomIndex][2]*cartToFrac[0][2],
inputInducedDipole[atomIndex][0]*cartToFrac[1][0] + inputInducedDipole[atomIndex][1]*cartToFrac[1][1] + inputInducedDipole[atomIndex][2]*cartToFrac[1][2], inputInducedDipole[atomIndex][0]*cartToFrac[1][0] + inputInducedDipole[atomIndex][1]*cartToFrac[1][1] + inputInducedDipole[atomIndex][2]*cartToFrac[1][2],
...@@ -5624,7 +5682,7 @@ void AmoebaReferencePmeMultipoleForce::spreadInducedDipolesOnGrid(const vector<R ...@@ -5624,7 +5682,7 @@ void AmoebaReferencePmeMultipoleForce::spreadInducedDipolesOnGrid(const vector<R
int y = (gridPoint[1]+iy) % _pmeGridDimensions[1]; int y = (gridPoint[1]+iy) % _pmeGridDimensions[1];
for (int iz = 0; iz < AMOEBA_PME_ORDER; iz++) { for (int iz = 0; iz < AMOEBA_PME_ORDER; iz++) {
int z = (gridPoint[2]+iz) % _pmeGridDimensions[2]; int z = (gridPoint[2]+iz) % _pmeGridDimensions[2];
RealOpenMM4 t = _thetai[0][atomIndex*AMOEBA_PME_ORDER+ix]; RealOpenMM4 t = _thetai[0][atomIndex*AMOEBA_PME_ORDER+ix];
RealOpenMM4 u = _thetai[1][atomIndex*AMOEBA_PME_ORDER+iy]; RealOpenMM4 u = _thetai[1][atomIndex*AMOEBA_PME_ORDER+iy];
RealOpenMM4 v = _thetai[2][atomIndex*AMOEBA_PME_ORDER+iz]; RealOpenMM4 v = _thetai[2][atomIndex*AMOEBA_PME_ORDER+iz];
...@@ -5846,7 +5904,7 @@ void AmoebaReferencePmeMultipoleForce::computeInducedPotentialFromGrid() ...@@ -5846,7 +5904,7 @@ void AmoebaReferencePmeMultipoleForce::computeInducedPotentialFromGrid()
} }
RealOpenMM AmoebaReferencePmeMultipoleForce::computeReciprocalSpaceFixedMultipoleForceAndEnergy(const vector<MultipoleParticleData>& particleData, RealOpenMM AmoebaReferencePmeMultipoleForce::computeReciprocalSpaceFixedMultipoleForceAndEnergy(const vector<MultipoleParticleData>& particleData,
vector<RealVec>& forces, vector<RealVec>& torques) const vector<RealVec>& forces, vector<RealVec>& torques) const
{ {
RealOpenMM multipole[10]; RealOpenMM multipole[10];
const int deriv1[] = {1, 4, 7, 8, 10, 15, 17, 13, 14, 19}; const int deriv1[] = {1, 4, 7, 8, 10, 15, 17, 13, 14, 19};
...@@ -5926,7 +5984,7 @@ RealOpenMM AmoebaReferencePmeMultipoleForce::computeReciprocalSpaceFixedMultipol ...@@ -5926,7 +5984,7 @@ RealOpenMM AmoebaReferencePmeMultipoleForce::computeReciprocalSpaceFixedMultipol
*/ */
RealOpenMM AmoebaReferencePmeMultipoleForce::computeReciprocalSpaceInducedDipoleForceAndEnergy(AmoebaReferenceMultipoleForce::PolarizationType polarizationType, RealOpenMM AmoebaReferencePmeMultipoleForce::computeReciprocalSpaceInducedDipoleForceAndEnergy(AmoebaReferenceMultipoleForce::PolarizationType polarizationType,
const vector<MultipoleParticleData>& particleData, const vector<MultipoleParticleData>& particleData,
vector<RealVec>& forces, vector<RealVec>& torques) const vector<RealVec>& forces, vector<RealVec>& torques) const
{ {
RealOpenMM multipole[10]; RealOpenMM multipole[10];
...@@ -6089,13 +6147,13 @@ void AmoebaReferencePmeMultipoleForce::calculateInducedDipoleFields(const vector ...@@ -6089,13 +6147,13 @@ void AmoebaReferencePmeMultipoleForce::calculateInducedDipoleFields(const vector
vector<UpdateInducedDipoleFieldStruct>& updateInducedDipoleFields) vector<UpdateInducedDipoleFieldStruct>& updateInducedDipoleFields)
{ {
// Initialize the fields to zero. // Initialize the fields to zero.
RealVec zeroVec(0.0, 0.0, 0.0); RealVec zeroVec(0.0, 0.0, 0.0);
for (unsigned int ii = 0; ii < updateInducedDipoleFields.size(); ii++) for (unsigned int ii = 0; ii < updateInducedDipoleFields.size(); ii++)
std::fill(updateInducedDipoleFields[ii].inducedDipoleField.begin(), updateInducedDipoleFields[ii].inducedDipoleField.end(), zeroVec); std::fill(updateInducedDipoleFields[ii].inducedDipoleField.begin(), updateInducedDipoleFields[ii].inducedDipoleField.end(), zeroVec);
// Add fields from direct space interactions. // Add fields from direct space interactions.
for (unsigned int ii = 0; ii < particleData.size(); ii++) { for (unsigned int ii = 0; ii < particleData.size(); ii++) {
for (unsigned int jj = ii + 1; jj < particleData.size(); jj++) { for (unsigned int jj = ii + 1; jj < particleData.size(); jj++) {
calculateDirectInducedDipolePairIxns(particleData[ii], particleData[jj], updateInducedDipoleFields); calculateDirectInducedDipolePairIxns(particleData[ii], particleData[jj], updateInducedDipoleFields);
...@@ -6176,7 +6234,7 @@ void AmoebaReferencePmeMultipoleForce::calculateInducedDipoleFields(const vector ...@@ -6176,7 +6234,7 @@ void AmoebaReferencePmeMultipoleForce::calculateInducedDipoleFields(const vector
vector<RealVec>& field = updateInducedDipoleFields[ii].inducedDipoleField; vector<RealVec>& field = updateInducedDipoleFields[ii].inducedDipoleField;
for (unsigned int jj = 0; jj < particleData.size(); jj++) { for (unsigned int jj = 0; jj < particleData.size(); jj++) {
field[jj] += inducedDipoles[jj]*term; field[jj] += inducedDipoles[jj]*term;
} }
} }
} }
...@@ -6184,7 +6242,7 @@ void AmoebaReferencePmeMultipoleForce::calculateDirectInducedDipolePairIxn(unsig ...@@ -6184,7 +6242,7 @@ void AmoebaReferencePmeMultipoleForce::calculateDirectInducedDipolePairIxn(unsig
RealOpenMM preFactor1, RealOpenMM preFactor2, RealOpenMM preFactor1, RealOpenMM preFactor2,
const RealVec& delta, const RealVec& delta,
const vector<RealVec>& inducedDipole, const vector<RealVec>& inducedDipole,
vector<RealVec>& field) const vector<RealVec>& field) const
{ {
// field at i due induced dipole at j // field at i due induced dipole at j
...@@ -6204,7 +6262,7 @@ void AmoebaReferencePmeMultipoleForce::calculateDirectInducedDipolePairIxns(cons ...@@ -6204,7 +6262,7 @@ void AmoebaReferencePmeMultipoleForce::calculateDirectInducedDipolePairIxns(cons
{ {
// compute the real space portion of the Ewald summation // compute the real space portion of the Ewald summation
RealOpenMM uscale = 1.0; RealOpenMM uscale = 1.0;
RealVec deltaR = particleJ.position - particleI.position; RealVec deltaR = particleJ.position - particleI.position;
...@@ -6321,7 +6379,7 @@ void AmoebaReferencePmeMultipoleForce::calculateDirectInducedDipolePairIxns(cons ...@@ -6321,7 +6379,7 @@ void AmoebaReferencePmeMultipoleForce::calculateDirectInducedDipolePairIxns(cons
} }
} }
RealOpenMM AmoebaReferencePmeMultipoleForce::calculatePmeSelfEnergy(const vector<MultipoleParticleData>& particleData) const RealOpenMM AmoebaReferencePmeMultipoleForce::calculatePmeSelfEnergy(const vector<MultipoleParticleData>& particleData) const
{ {
RealOpenMM cii = 0.0; RealOpenMM cii = 0.0;
RealOpenMM dii = 0.0; RealOpenMM dii = 0.0;
...@@ -6349,7 +6407,7 @@ RealOpenMM AmoebaReferencePmeMultipoleForce::calculatePmeSelfEnergy(const vector ...@@ -6349,7 +6407,7 @@ RealOpenMM AmoebaReferencePmeMultipoleForce::calculatePmeSelfEnergy(const vector
} }
void AmoebaReferencePmeMultipoleForce::calculatePmeSelfTorque(const vector<MultipoleParticleData>& particleData, void AmoebaReferencePmeMultipoleForce::calculatePmeSelfTorque(const vector<MultipoleParticleData>& particleData,
vector<RealVec>& torques) const vector<RealVec>& torques) const
{ {
RealOpenMM term = (2.0/3.0)*(_electric/_dielectric)*(_alphaEwald*_alphaEwald*_alphaEwald)/SQRT_PI; RealOpenMM term = (2.0/3.0)*(_electric/_dielectric)*(_alphaEwald*_alphaEwald*_alphaEwald)/SQRT_PI;
for (unsigned int ii = 0; ii < _numParticles; ii++) { for (unsigned int ii = 0; ii < _numParticles; ii++) {
...@@ -6363,11 +6421,11 @@ void AmoebaReferencePmeMultipoleForce::calculatePmeSelfTorque(const vector<Multi ...@@ -6363,11 +6421,11 @@ void AmoebaReferencePmeMultipoleForce::calculatePmeSelfTorque(const vector<Multi
} }
} }
RealOpenMM AmoebaReferencePmeMultipoleForce::calculatePmeDirectElectrostaticPairIxn(const MultipoleParticleData& particleI, RealOpenMM AmoebaReferencePmeMultipoleForce::calculatePmeDirectElectrostaticPairIxn(const MultipoleParticleData& particleI,
const MultipoleParticleData& particleJ, const MultipoleParticleData& particleJ,
const vector<RealOpenMM>& scalingFactors, const vector<RealOpenMM>& scalingFactors,
vector<RealVec>& forces, vector<RealVec>& forces,
vector<RealVec>& torques) const vector<RealVec>& torques) const
{ {
unsigned int iIndex = particleI.particleIndex; unsigned int iIndex = particleI.particleIndex;
...@@ -6787,7 +6845,7 @@ RealOpenMM AmoebaReferencePmeMultipoleForce::calculateElectrostatic(const vector ...@@ -6787,7 +6845,7 @@ RealOpenMM AmoebaReferencePmeMultipoleForce::calculateElectrostatic(const vector
vector<RealOpenMM> scaleFactors(LAST_SCALE_TYPE_INDEX); vector<RealOpenMM> scaleFactors(LAST_SCALE_TYPE_INDEX);
for (unsigned int kk = 0; kk < scaleFactors.size(); kk++) { for (unsigned int kk = 0; kk < scaleFactors.size(); kk++) {
scaleFactors[kk] = 1.0; scaleFactors[kk] = 1.0;
} }
// loop over particle pairs for direct space interactions // loop over particle pairs for direct space interactions
......
...@@ -533,6 +533,75 @@ public: ...@@ -533,6 +533,75 @@ public:
const std::vector< std::vector< std::vector<int> > >& multipoleAtomCovalentInfo, const std::vector< std::vector< std::vector<int> > >& multipoleAtomCovalentInfo,
std::vector<RealVec>& outputInducedDipoles); std::vector<RealVec>& outputInducedDipoles);
/**
* Calculate particle permanent dipoles rotated in the lab frame.
*
* @param masses particle masses
* @param particlePositions Cartesian coordinates of particles
* @param charges scalar charges for each particle
* @param dipoles molecular frame dipoles for each particle
* @param quadrupoles molecular frame quadrupoles for each particle
* @param tholes Thole factors for each particle
* @param dampingFactors dampling factors for each particle
* @param polarity polarity for each particle
* @param axisTypes axis type (Z-then-X, ...) for each particle
* @param multipoleAtomZs indicies of particle specifying the molecular frame z-axis for each particle
* @param multipoleAtomXs indicies of particle specifying the molecular frame x-axis for each particle
* @param multipoleAtomYs indicies of particle specifying the molecular frame y-axis for each particle
* @param multipoleAtomCovalentInfo covalent info needed to set scaling factors
* @param outputMultipoleMoments output multipole moments
*/
void calculateLabFramePermanentDipoles(const std::vector<RealVec>& particlePositions,
const std::vector<RealOpenMM>& charges,
const std::vector<RealOpenMM>& dipoles,
const std::vector<RealOpenMM>& quadrupoles,
const std::vector<RealOpenMM>& tholes,
const std::vector<RealOpenMM>& dampingFactors,
const std::vector<RealOpenMM>& polarity,
const std::vector<int>& axisTypes,
const std::vector<int>& multipoleAtomZs,
const std::vector<int>& multipoleAtomXs,
const std::vector<int>& multipoleAtomYs,
const std::vector< vector< vector<int> > >& multipoleAtomCovalentInfo,
std::vector<RealVec>& outputRotatedPermanentDipoles);
/**
* Calculate particle total dipoles.
*
* @param masses particle masses
* @param particlePositions Cartesian coordinates of particles
* @param charges scalar charges for each particle
* @param dipoles molecular frame dipoles for each particle
* @param quadrupoles molecular frame quadrupoles for each particle
* @param tholes Thole factors for each particle
* @param dampingFactors dampling factors for each particle
* @param polarity polarity for each particle
* @param axisTypes axis type (Z-then-X, ...) for each particle
* @param multipoleAtomZs indicies of particle specifying the molecular frame z-axis for each particle
* @param multipoleAtomXs indicies of particle specifying the molecular frame x-axis for each particle
* @param multipoleAtomYs indicies of particle specifying the molecular frame y-axis for each particle
* @param multipoleAtomCovalentInfo covalent info needed to set scaling factors
* @param outputMultipoleMoments output multipole moments
*/
void calculateTotalDipoles(const std::vector<RealVec>& particlePositions,
const std::vector<RealOpenMM>& charges,
const std::vector<RealOpenMM>& dipoles,
const std::vector<RealOpenMM>& quadrupoles,
const std::vector<RealOpenMM>& tholes,
const std::vector<RealOpenMM>& dampingFactors,
const std::vector<RealOpenMM>& polarity,
const std::vector<int>& axisTypes,
const std::vector<int>& multipoleAtomZs,
const std::vector<int>& multipoleAtomXs,
const std::vector<int>& multipoleAtomYs,
const std::vector< vector< vector<int> > >& multipoleAtomCovalentInfo,
std::vector<RealVec>& outputRotatedPermanentDipoles);
/** /**
* Calculate system multipole moments. * Calculate system multipole moments.
* *
......
...@@ -2587,6 +2587,76 @@ static void testParticleInducedDipoles() { ...@@ -2587,6 +2587,76 @@ static void testParticleInducedDipoles() {
ASSERT_EQUAL_VEC(expectedDipole[i], dipole[i], 1e-4); ASSERT_EQUAL_VEC(expectedDipole[i], dipole[i], 1e-4);
} }
// test querying particle lab frame permanent dipoles
static void testParticleLabFramePermanentDipoles() {
int numberOfParticles = 8;
int inputPmeGridDimension = 0;
double cutoff = 9000000.0;
std::vector<Vec3> forces;
double energy;
System system;
AmoebaMultipoleForce* amoebaMultipoleForce = new AmoebaMultipoleForce();;
setupMultipoleAmmonia(system, amoebaMultipoleForce, AmoebaMultipoleForce::NoCutoff, AmoebaMultipoleForce::Mutual,
cutoff, inputPmeGridDimension);
LangevinIntegrator integrator(0.0, 0.1, 0.01);
Context context(system, integrator, Platform::getPlatformByName("Reference"));
getForcesEnergyMultipoleAmmonia(context, forces, energy);
std::vector<Vec3> dipole;
amoebaMultipoleForce->getLabFramePermanentDipoles(context, dipole);
// Compare to values calculated by TINKER.
std::vector<Vec3> expectedDipole(numberOfParticles);
expectedDipole[0] = Vec3(0.00876454250, -2.04310718E-06, -0.00227593519);
expectedDipole[1] = Vec3(0.000780382180, -0.00432882849, 0.00236926761);
expectedDipole[2] = Vec3(0.000801345883, 0.00431830946, 0.00238143437);
expectedDipole[3] = Vec3(-0.00109746996, 1.16087953e-5, -0.00487407492);
expectedDipole[4] = Vec3(0.00203814102, -2.26554196e-5, 0.00882284298);
expectedDipole[5] = Vec3(-0.00239443187, 0.00432388648, 0.000729303209);
expectedDipole[6] = Vec3(0.00491086743, 2.86430963e-6, -0.000918996348);
expectedDipole[7] = Vec3(-0.00239301946, -0.00432743976, 0.000712674115);
for (int i = 0; i < numberOfParticles; i++)
ASSERT_EQUAL_VEC(expectedDipole[i], dipole[i], 1e-4);
}
// test querying particle total dipoles (fixed + induced)
static void testParticleTotalDipoles() {
int numberOfParticles = 8;
int inputPmeGridDimension = 0;
double cutoff = 9000000.0;
std::vector<Vec3> forces;
double energy;
System system;
AmoebaMultipoleForce* amoebaMultipoleForce = new AmoebaMultipoleForce();;
setupMultipoleAmmonia(system, amoebaMultipoleForce, AmoebaMultipoleForce::NoCutoff, AmoebaMultipoleForce::Mutual,
cutoff, inputPmeGridDimension);
LangevinIntegrator integrator(0.0, 0.1, 0.01);
Context context(system, integrator, Platform::getPlatformByName("Reference"));
getForcesEnergyMultipoleAmmonia(context, forces, energy);
std::vector<Vec3> dipole;
amoebaMultipoleForce->getTotalDipoles(context, dipole);
// Compare to values calculated by TINKER.
std::vector<Vec3> expectedDipole(numberOfParticles);
expectedDipole[0] = Vec3(0.0119356307, -1.11302433e-6, -0.00296793872);
expectedDipole[1] = Vec3(8.60636211e-4, -0.00460821816, 0.00241705344);
expectedDipole[2] = Vec3(8.80646403e-4, 0.00459728769, 0.00243013245);
expectedDipole[3] = Vec3(-0.00123822377, 1.31555550e-5, -0.00558185336);
expectedDipole[4] = Vec3(0.00399455556, -2.27511931e-5, 0.00955607952);
expectedDipole[5] = Vec3(-0.00157302682, 0.00354892386, 3.40921137e-4);
expectedDipole[6] = Vec3(0.00952428069, 2.14171505e-6, -6.68945865e-4);
expectedDipole[7] = Vec3(-0.00157252460, -0.00355015528, 3.27055162e-4);
for (int i = 0; i < numberOfParticles; i++)
ASSERT_EQUAL_VEC(expectedDipole[i], dipole[i], 1e-4);
}
// test computation of system multipole moments // test computation of system multipole moments
static void testSystemMultipoleMoments() { static void testSystemMultipoleMoments() {
......
...@@ -124,6 +124,8 @@ NO_OUTPUT_ARGS = [('LocalEnergyMinimizer', 'minimize', 'context'), ...@@ -124,6 +124,8 @@ NO_OUTPUT_ARGS = [('LocalEnergyMinimizer', 'minimize', 'context'),
('AmoebaMultipoleForce', 'setCovalentMap', 'covalentAtoms'), ('AmoebaMultipoleForce', 'setCovalentMap', 'covalentAtoms'),
('AmoebaMultipoleForce', 'getElectrostaticPotential', 'context'), ('AmoebaMultipoleForce', 'getElectrostaticPotential', 'context'),
('AmoebaMultipoleForce', 'getInducedDipoles', 'context'), ('AmoebaMultipoleForce', 'getInducedDipoles', 'context'),
('AmoebaMultipoleForce', 'getLabFramePermanentDipoles', 'context'),
('AmoebaMultipoleForce', 'getTotalDipoles', 'context'),
] ]
# SWIG assumes the target language shadow class owns the C++ class # SWIG assumes the target language shadow class owns the C++ class
...@@ -274,6 +276,8 @@ UNITS = { ...@@ -274,6 +276,8 @@ UNITS = {
#("AmoebaMultipoleForce", "getElectrostaticPotential") : ( ('unit.kilojoule_per_mole'), ()), #("AmoebaMultipoleForce", "getElectrostaticPotential") : ( ('unit.kilojoule_per_mole'), ()),
("AmoebaMultipoleForce", "getElectrostaticPotential") : ( None, ()), ("AmoebaMultipoleForce", "getElectrostaticPotential") : ( None, ()),
("AmoebaMultipoleForce", "getInducedDipoles") : ( None, ()), ("AmoebaMultipoleForce", "getInducedDipoles") : ( None, ()),
("AmoebaMultipoleForce", "getLabFramePermanentDipoles") : ( None, ()),
("AmoebaMultipoleForce", "getTotalDipoles") : ( None, ()),
("AmoebaMultipoleForce", "getSystemMultipoleMoments") : ( None, ()), ("AmoebaMultipoleForce", "getSystemMultipoleMoments") : ( None, ()),
("AmoebaOutOfPlaneBendForce", "getNumOutOfPlaneBends") : ( None, ()), ("AmoebaOutOfPlaneBendForce", "getNumOutOfPlaneBends") : ( None, ()),
......
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