Commit 2d2f05ce authored by Andy Simmonett's avatar Andy Simmonett
Browse files

Merge branch 'master' of github.com:pandegroup/openmm into genpt

parents 94823d84 4d32047c
...@@ -75,6 +75,6 @@ SET_TARGET_PROPERTIES(${SHARED_TARGET} PROPERTIES LINK_FLAGS "${EXTRA_LINK_FLAGS ...@@ -75,6 +75,6 @@ SET_TARGET_PROPERTIES(${SHARED_TARGET} PROPERTIES LINK_FLAGS "${EXTRA_LINK_FLAGS
INSTALL(TARGETS ${SHARED_TARGET} DESTINATION ${CMAKE_INSTALL_PREFIX}/lib/plugins) INSTALL(TARGETS ${SHARED_TARGET} DESTINATION ${CMAKE_INSTALL_PREFIX}/lib/plugins)
IF(BUILD_TESTING) IF(BUILD_TESTING AND OPENMM_BUILD_REFERENCE_TESTS)
SUBDIRS (tests) SUBDIRS (tests)
ENDIF(BUILD_TESTING) ENDIF(BUILD_TESTING AND OPENMM_BUILD_REFERENCE_TESTS)
...@@ -6317,7 +6317,7 @@ RealOpenMM AmoebaReferencePmeMultipoleForce::calculatePmeSelfEnergy(const vector ...@@ -6317,7 +6317,7 @@ RealOpenMM AmoebaReferencePmeMultipoleForce::calculatePmeSelfEnergy(const vector
cii += particleI.charge*particleI.charge; cii += particleI.charge*particleI.charge;
RealVec dipole(particleI.sphericalDipole[1], particleI.sphericalDipole[2], particleI.sphericalDipole[0]); RealVec dipole(particleI.sphericalDipole[1], particleI.sphericalDipole[2], particleI.sphericalDipole[0]);
dii += dipole.dot(dipole + (_inducedDipole[ii] + _inducedDipolePolar[ii])*0.5); dii += dipole.dot(dipole + (_inducedDipole[ii]+_inducedDipolePolar[ii])*0.5);
qii += (particleI.sphericalQuadrupole[0]*particleI.sphericalQuadrupole[0] qii += (particleI.sphericalQuadrupole[0]*particleI.sphericalQuadrupole[0]
+particleI.sphericalQuadrupole[1]*particleI.sphericalQuadrupole[1] +particleI.sphericalQuadrupole[1]*particleI.sphericalQuadrupole[1]
......
...@@ -49,7 +49,7 @@ static const int PME_ORDER = 5; ...@@ -49,7 +49,7 @@ static const int PME_ORDER = 5;
bool CpuCalcPmeReciprocalForceKernel::hasInitializedThreads = false; bool CpuCalcPmeReciprocalForceKernel::hasInitializedThreads = false;
int CpuCalcPmeReciprocalForceKernel::numThreads = 0; int CpuCalcPmeReciprocalForceKernel::numThreads = 0;
static void spreadCharge(int start, int end, float* posq, float* grid, int gridx, int gridy, int gridz, int numParticles, Vec3* periodicBoxVectors, Vec3* recipBoxVectors) { static void spreadCharge(float* posq, float* grid, int gridx, int gridy, int gridz, int numParticles, Vec3* periodicBoxVectors, Vec3* recipBoxVectors, gmx_atomic_t& atomicCounter) {
float temp[4]; float temp[4];
fvec4 boxSize((float) periodicBoxVectors[0][0], (float) periodicBoxVectors[1][1], (float) periodicBoxVectors[2][2], 0); fvec4 boxSize((float) periodicBoxVectors[0][0], (float) periodicBoxVectors[1][1], (float) periodicBoxVectors[2][2], 0);
fvec4 invBoxSize((float) recipBoxVectors[0][0], (float) recipBoxVectors[1][1], (float) recipBoxVectors[2][2], 0); fvec4 invBoxSize((float) recipBoxVectors[0][0], (float) recipBoxVectors[1][1], (float) recipBoxVectors[2][2], 0);
...@@ -64,7 +64,11 @@ static void spreadCharge(int start, int end, float* posq, float* grid, int gridx ...@@ -64,7 +64,11 @@ static void spreadCharge(int start, int end, float* posq, float* grid, int gridx
const float epsilonFactor = sqrt(ONE_4PI_EPS0); const float epsilonFactor = sqrt(ONE_4PI_EPS0);
memset(grid, 0, sizeof(float)*gridx*gridy*gridz); memset(grid, 0, sizeof(float)*gridx*gridy*gridz);
for (int i = start; i < end; i++) { while (true) {
int i = gmx_atomic_fetch_add(&atomicCounter, 1);
if (i >= numParticles)
break;
// Find the position relative to the nearest grid point. // Find the position relative to the nearest grid point.
fvec4 pos(&posq[4*i]); fvec4 pos(&posq[4*i]);
...@@ -225,25 +229,15 @@ static double reciprocalEnergy(int start, int end, fftwf_complex* grid, int grid ...@@ -225,25 +229,15 @@ static double reciprocalEnergy(int start, int end, fftwf_complex* grid, int grid
return 0.5*energy; return 0.5*energy;
} }
static void reciprocalConvolution(int start, int end, fftwf_complex* grid, int gridx, int gridy, int gridz, vector<float>& recipEterm) { static void reciprocalConvolution(int start, int end, fftwf_complex* grid, vector<float>& recipEterm) {
const unsigned int zsize = gridz/2+1; for (int index = start; index < end; index++) {
const unsigned int yzsize = gridy*zsize; float eterm = recipEterm[index];
grid[index][0] *= eterm;
int firstz = (start == 0 ? 1 : 0); grid[index][1] *= eterm;
for (int kx = start; kx < end; kx++) {
for (int ky = 0; ky < gridy; ky++) {
for (int kz = firstz; kz < zsize; kz++) {
int index = kx*yzsize + ky*zsize + kz;
float eterm = recipEterm[index];
grid[index][0] *= eterm;
grid[index][1] *= eterm;
}
firstz = 0;
}
} }
} }
static void interpolateForces(int start, int end, float* posq, float* force, float* grid, int gridx, int gridy, int gridz, int numParticles, Vec3* periodicBoxVectors, Vec3* recipBoxVectors) { static void interpolateForces(float* posq, float* force, float* grid, int gridx, int gridy, int gridz, int numParticles, Vec3* periodicBoxVectors, Vec3* recipBoxVectors, gmx_atomic_t& atomicCounter) {
fvec4 boxSize((float) periodicBoxVectors[0][0], (float) periodicBoxVectors[1][1], (float) periodicBoxVectors[2][2], 0); fvec4 boxSize((float) periodicBoxVectors[0][0], (float) periodicBoxVectors[1][1], (float) periodicBoxVectors[2][2], 0);
fvec4 invBoxSize((float) recipBoxVectors[0][0], (float) recipBoxVectors[1][1], (float) recipBoxVectors[2][2], 0); fvec4 invBoxSize((float) recipBoxVectors[0][0], (float) recipBoxVectors[1][1], (float) recipBoxVectors[2][2], 0);
fvec4 recipBoxVec0((float) recipBoxVectors[0][0], (float) recipBoxVectors[0][1], (float) recipBoxVectors[0][2], 0); fvec4 recipBoxVec0((float) recipBoxVectors[0][0], (float) recipBoxVectors[0][1], (float) recipBoxVectors[0][2], 0);
...@@ -254,7 +248,11 @@ static void interpolateForces(int start, int end, float* posq, float* force, flo ...@@ -254,7 +248,11 @@ static void interpolateForces(int start, int end, float* posq, float* force, flo
fvec4 one(1); fvec4 one(1);
fvec4 scale(1.0f/(PME_ORDER-1)); fvec4 scale(1.0f/(PME_ORDER-1));
const float epsilonFactor = sqrt(ONE_4PI_EPS0); const float epsilonFactor = sqrt(ONE_4PI_EPS0);
for (int i = start; i < end; i++) { while (true) {
int i = gmx_atomic_fetch_add(&atomicCounter, 1);
if (i >= numParticles)
break;
// Find the position relative to the nearest grid point. // Find the position relative to the nearest grid point.
fvec4 pos(&posq[4*i]); fvec4 pos(&posq[4*i]);
...@@ -485,6 +483,7 @@ void CpuCalcPmeReciprocalForceKernel::runMainThread() { ...@@ -485,6 +483,7 @@ void CpuCalcPmeReciprocalForceKernel::runMainThread() {
break; break;
posq = io->getPosq(); posq = io->getPosq();
ComputeTask task(*this); ComputeTask task(*this);
gmx_atomic_set(&atomicCounter, 0);
threads.execute(task); // Signal threads to perform charge spreading. threads.execute(task); // Signal threads to perform charge spreading.
threads.waitForThreads(); threads.waitForThreads();
threads.resumeThreads(); // Signal threads to sum the charge grids. threads.resumeThreads(); // Signal threads to sum the charge grids.
...@@ -503,6 +502,7 @@ void CpuCalcPmeReciprocalForceKernel::runMainThread() { ...@@ -503,6 +502,7 @@ void CpuCalcPmeReciprocalForceKernel::runMainThread() {
threads.resumeThreads(); // Signal threads to perform reciprocal convolution. threads.resumeThreads(); // Signal threads to perform reciprocal convolution.
threads.waitForThreads(); threads.waitForThreads();
fftwf_execute_dft_c2r(backwardFFT, complexGrid, realGrid); fftwf_execute_dft_c2r(backwardFFT, complexGrid, realGrid);
gmx_atomic_set(&atomicCounter, 0);
threads.resumeThreads(); // Signal threads to interpolate forces. threads.resumeThreads(); // Signal threads to interpolate forces.
threads.waitForThreads(); threads.waitForThreads();
isFinished = true; isFinished = true;
...@@ -515,14 +515,15 @@ void CpuCalcPmeReciprocalForceKernel::runMainThread() { ...@@ -515,14 +515,15 @@ void CpuCalcPmeReciprocalForceKernel::runMainThread() {
} }
void CpuCalcPmeReciprocalForceKernel::runWorkerThread(ThreadPool& threads, int index) { void CpuCalcPmeReciprocalForceKernel::runWorkerThread(ThreadPool& threads, int index) {
int particleStart = (index*numParticles)/numThreads;
int particleEnd = ((index+1)*numParticles)/numThreads;
int gridxStart = (index*gridx)/numThreads; int gridxStart = (index*gridx)/numThreads;
int gridxEnd = ((index+1)*gridx)/numThreads; int gridxEnd = ((index+1)*gridx)/numThreads;
int gridSize = (gridx*gridy*gridz+3)/4; int gridSize = (gridx*gridy*gridz+3)/4;
int gridStart = 4*((index*gridSize)/numThreads); int gridStart = 4*((index*gridSize)/numThreads);
int gridEnd = 4*(((index+1)*gridSize)/numThreads); int gridEnd = 4*(((index+1)*gridSize)/numThreads);
spreadCharge(particleStart, particleEnd, posq, tempGrid[index], gridx, gridy, gridz, numParticles, periodicBoxVectors, recipBoxVectors); int complexSize = gridx*gridy*(gridz/2+1);
int complexStart = max(1, ((index*complexSize)/numThreads));
int complexEnd = (((index+1)*complexSize)/numThreads);
spreadCharge(posq, tempGrid[index], gridx, gridy, gridz, numParticles, periodicBoxVectors, recipBoxVectors, atomicCounter);
threads.syncThreads(); threads.syncThreads();
int numGrids = tempGrid.size(); int numGrids = tempGrid.size();
for (int i = gridStart; i < gridEnd; i += 4) { for (int i = gridStart; i < gridEnd; i += 4) {
...@@ -540,9 +541,9 @@ void CpuCalcPmeReciprocalForceKernel::runWorkerThread(ThreadPool& threads, int i ...@@ -540,9 +541,9 @@ void CpuCalcPmeReciprocalForceKernel::runWorkerThread(ThreadPool& threads, int i
threadEnergy[index] = reciprocalEnergy(gridxStart, gridxEnd, complexGrid, gridx, gridy, gridz, alpha, bsplineModuli, periodicBoxVectors, recipBoxVectors); threadEnergy[index] = reciprocalEnergy(gridxStart, gridxEnd, complexGrid, gridx, gridy, gridz, alpha, bsplineModuli, periodicBoxVectors, recipBoxVectors);
threads.syncThreads(); threads.syncThreads();
} }
reciprocalConvolution(gridxStart, gridxEnd, complexGrid, gridx, gridy, gridz, recipEterm); reciprocalConvolution(complexStart, complexEnd, complexGrid, recipEterm);
threads.syncThreads(); threads.syncThreads();
interpolateForces(particleStart, particleEnd, posq, &force[0], realGrid, gridx, gridy, gridz, numParticles, periodicBoxVectors, recipBoxVectors); interpolateForces(posq, &force[0], realGrid, gridx, gridy, gridz, numParticles, periodicBoxVectors, recipBoxVectors, atomicCounter);
} }
void CpuCalcPmeReciprocalForceKernel::beginComputation(IO& io, const Vec3* periodicBoxVectors, bool includeEnergy) { void CpuCalcPmeReciprocalForceKernel::beginComputation(IO& io, const Vec3* periodicBoxVectors, bool includeEnergy) {
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* Biological Structures at Stanford, funded under the NIH Roadmap for * * Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org. * * Medical Research, grant U54 GM072970. See https://simtk.org. *
* * * *
* Portions copyright (c) 2013-2014 Stanford University and the Authors. * * Portions copyright (c) 2013-2015 Stanford University and the Authors. *
* Authors: Peter Eastman * * Authors: Peter Eastman *
* Contributors: * * Contributors: *
* * * *
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
#include "internal/windowsExportPme.h" #include "internal/windowsExportPme.h"
#include "openmm/kernels.h" #include "openmm/kernels.h"
#include "openmm/Vec3.h" #include "openmm/Vec3.h"
#include "openmm/internal/gmx_atomic.h"
#include "openmm/internal/ThreadPool.h" #include "openmm/internal/ThreadPool.h"
#include <fftw3.h> #include <fftw3.h>
#include <pthread.h> #include <pthread.h>
...@@ -130,6 +131,7 @@ private: ...@@ -130,6 +131,7 @@ private:
float* posq; float* posq;
Vec3 periodicBoxVectors[3], recipBoxVectors[3]; Vec3 periodicBoxVectors[3], recipBoxVectors[3];
bool includeEnergy; bool includeEnergy;
gmx_atomic_t atomicCounter;
}; };
} // namespace OpenMM } // namespace OpenMM
......
...@@ -145,6 +145,6 @@ ENDIF (EXECUTABLE_OUTPUT_PATH) ...@@ -145,6 +145,6 @@ ENDIF (EXECUTABLE_OUTPUT_PATH)
#INCLUDE(ApiDoxygen.cmake) #INCLUDE(ApiDoxygen.cmake)
IF(BUILD_TESTING) IF(BUILD_TESTING AND OPENMM_BUILD_SERIALIZATION_TESTS)
ADD_SUBDIRECTORY(serialization/tests) ADD_SUBDIRECTORY(serialization/tests)
ENDIF(BUILD_TESTING) ENDIF(BUILD_TESTING AND OPENMM_BUILD_SERIALIZATION_TESTS)
...@@ -44,7 +44,7 @@ namespace OpenMM { ...@@ -44,7 +44,7 @@ namespace OpenMM {
* it applies: an anisotropic harmonic force connecting each Drude particle to its parent particle; and * it applies: an anisotropic harmonic force connecting each Drude particle to its parent particle; and
* a screened Coulomb interaction between specific pairs of dipoles. The latter is typically used between * a screened Coulomb interaction between specific pairs of dipoles. The latter is typically used between
* closely bonded particles whose Coulomb interaction would otherwise be fully excluded. * closely bonded particles whose Coulomb interaction would otherwise be fully excluded.
* *
* To use this class, create a DrudeForce object, then call addParticle() once for each Drude particle in the * To use this class, create a DrudeForce object, then call addParticle() once for each Drude particle in the
* System to define its parameters. After a particle has been added, you can modify its force field parameters * System to define its parameters. After a particle has been added, you can modify its force field parameters
* by calling setParticleParameters(). This will have no effect on Contexts that already exist unless you * by calling setParticleParameters(). This will have no effect on Contexts that already exist unless you
...@@ -91,19 +91,19 @@ public: ...@@ -91,19 +91,19 @@ public:
/** /**
* Get the parameters for a Drude particle. * Get the parameters for a Drude particle.
* *
* @param index the index of the Drude particle for which to get parameters * @param index the index of the Drude particle for which to get parameters
* @param particle the index within the System of the Drude particle * @param[out] particle the index within the System of the Drude particle
* @param particle1 the index within the System of the particle to which the Drude particle is attached * @param[out] particle1 the index within the System of the particle to which the Drude particle is attached
* @param particle2 the index within the System of the second particle used for defining anisotropic polarizability. * @param[out] particle2 the index within the System of the second particle used for defining anisotropic polarizability.
* This may be set to -1, in which case aniso12 will be ignored. * This may be set to -1, in which case aniso12 will be ignored.
* @param particle3 the index within the System of the third particle used for defining anisotropic polarizability. * @param[out] particle3 the index within the System of the third particle used for defining anisotropic polarizability.
* This may be set to -1, in which case aniso34 will be ignored. * This may be set to -1, in which case aniso34 will be ignored.
* @param particle4 the index within the System of the fourth particle used for defining anisotropic polarizability. * @param[out] particle4 the index within the System of the fourth particle used for defining anisotropic polarizability.
* This may be set to -1, in which case aniso34 will be ignored. * This may be set to -1, in which case aniso34 will be ignored.
* @param charge The charge on the Drude particle * @param[out] charge The charge on the Drude particle
* @param polarizability The isotropic polarizability * @param[out] polarizability The isotropic polarizability
* @param aniso12 The scale factor for the polarizability along the direction defined by particle1 and particle2 * @param[out] aniso12 The scale factor for the polarizability along the direction defined by particle1 and particle2
* @param aniso34 The scale factor for the polarizability along the direction defined by particle3 and particle4 * @param[out] aniso34 The scale factor for the polarizability along the direction defined by particle3 and particle4
*/ */
void getParticleParameters(int index, int& particle, int& particle1, int& particle2, int& particle3, int& particle4, double& charge, double& polarizability, double& aniso12, double& aniso34) const; void getParticleParameters(int index, int& particle, int& particle1, int& particle2, int& particle3, int& particle4, double& charge, double& polarizability, double& aniso12, double& aniso34) const;
/** /**
...@@ -135,16 +135,16 @@ public: ...@@ -135,16 +135,16 @@ public:
int addScreenedPair(int particle1, int particle2, double thole); int addScreenedPair(int particle1, int particle2, double thole);
/** /**
* Get the force field parameters for screened pair. * Get the force field parameters for screened pair.
* *
* @param index the index of the pair for which to get parameters * @param index the index of the pair for which to get parameters
* @param particle1 the index within this Force of the first particle involved in the interaction * @param[out] particle1 the index within this Force of the first particle involved in the interaction
* @param particle2 the index within this Force of the second particle involved in the interaction * @param[out] particle2 the index within this Force of the second particle involved in the interaction
* @param thole the Thole screening factor * @param[out] thole the Thole screening factor
*/ */
void getScreenedPairParameters(int index, int& particle1, int& particle2, double& thole) const; void getScreenedPairParameters(int index, int& particle1, int& particle2, double& thole) const;
/** /**
* Set the force field parameters for screened pair. * Set the force field parameters for screened pair.
* *
* @param index the index of the pair for which to get parameters * @param index the index of the pair for which to get parameters
* @param particle1 the index within this Force of the first particle involved in the interaction * @param particle1 the index within this Force of the first particle involved in the interaction
* @param particle2 the index within this Force of the second particle involved in the interaction * @param particle2 the index within this Force of the second particle involved in the interaction
...@@ -156,7 +156,7 @@ public: ...@@ -156,7 +156,7 @@ public:
* provides an efficient method to update certain parameters in an existing Context without needing to reinitialize it. * provides an efficient method to update certain parameters in an existing Context without needing to reinitialize it.
* Simply call setParticleParameters() and setScreenedPairParameters() to modify this object's parameters, then call * Simply call setParticleParameters() and setScreenedPairParameters() to modify this object's parameters, then call
* updateParametersInContext() to copy them over to the Context. * updateParametersInContext() to copy them over to the Context.
* *
* This method has several limitations. It can be used to modify the numeric parameters associated with a particle or * This method has several limitations. It can be used to modify the numeric parameters associated with a particle or
* screened pair (polarizability, thole, etc.), but not the identities of the particles they involve. It also cannot * screened pair (polarizability, thole, etc.), but not the identities of the particles they involve. It also cannot
* be used to add new particles or screenedPairs, only to change the parameters of existing ones. * be used to add new particles or screenedPairs, only to change the parameters of existing ones.
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* Biological Structures at Stanford, funded under the NIH Roadmap for * * Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org. * * Medical Research, grant U54 GM072970. See https://simtk.org. *
* * * *
* Portions copyright (c) 2008-2013 Stanford University and the Authors. * * Portions copyright (c) 2008-2015 Stanford University and the Authors. *
* Authors: Peter Eastman * * Authors: Peter Eastman *
* Contributors: * * Contributors: *
* * * *
...@@ -44,6 +44,10 @@ namespace OpenMM { ...@@ -44,6 +44,10 @@ namespace OpenMM {
* are not part of a Drude particle pair), as well as to the center of mass of each Drude particle pair. * are not part of a Drude particle pair), as well as to the center of mass of each Drude particle pair.
* A second thermostat, typically with a much lower temperature, is applied to the relative internal * A second thermostat, typically with a much lower temperature, is applied to the relative internal
* displacement of each pair. * displacement of each pair.
*
* This integrator can optionally set an upper limit on how far any Drude particle is ever allowed to
* get from its parent particle. This can sometimes help to improve stability. The limit is enforced
* with a hard wall constraint.
* *
* This Integrator requires the System to include a DrudeForce, which it uses to identify the Drude * This Integrator requires the System to include a DrudeForce, which it uses to identify the Drude
* particles. * particles.
...@@ -129,6 +133,16 @@ public: ...@@ -129,6 +133,16 @@ public:
void setDrudeFriction(double coeff) { void setDrudeFriction(double coeff) {
drudeFriction = coeff; drudeFriction = coeff;
} }
/**
* Get the maximum distance a Drude particle can ever move from its parent particle, measured in nm. This is implemented
* with a hard wall constraint. If this distance is set to 0 (the default), the hard wall constraint is omitted.
*/
double getMaxDrudeDistance() const;
/**
* Set the maximum distance a Drude particle can ever move from its parent particle, measured in nm. This is implemented
* with a hard wall constraint. If this distance is set to 0 (the default), the hard wall constraint is omitted.
*/
void setMaxDrudeDistance(double distance);
/** /**
* Get the random number seed. See setRandomNumberSeed() for details. * Get the random number seed. See setRandomNumberSeed() for details.
*/ */
...@@ -181,7 +195,7 @@ protected: ...@@ -181,7 +195,7 @@ protected:
*/ */
double computeKineticEnergy(); double computeKineticEnergy();
private: private:
double temperature, friction, drudeTemperature, drudeFriction; double temperature, friction, drudeTemperature, drudeFriction, maxDrudeDistance;
int randomNumberSeed; int randomNumberSeed;
Kernel kernel; Kernel kernel;
}; };
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Biological Structures at Stanford, funded under the NIH Roadmap for * * Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org. * * Medical Research, grant U54 GM072970. See https://simtk.org. *
* * * *
* Portions copyright (c) 2008-2013 Stanford University and the Authors. * * Portions copyright (c) 2008-2015 Stanford University and the Authors. *
* Authors: Peter Eastman * * Authors: Peter Eastman *
* Contributors: * * Contributors: *
* * * *
...@@ -46,11 +46,22 @@ DrudeLangevinIntegrator::DrudeLangevinIntegrator(double temperature, double fric ...@@ -46,11 +46,22 @@ DrudeLangevinIntegrator::DrudeLangevinIntegrator(double temperature, double fric
setFriction(frictionCoeff); setFriction(frictionCoeff);
setDrudeTemperature(drudeTemperature); setDrudeTemperature(drudeTemperature);
setDrudeFriction(drudeFrictionCoeff); setDrudeFriction(drudeFrictionCoeff);
setMaxDrudeDistance(0);
setStepSize(stepSize); setStepSize(stepSize);
setConstraintTolerance(1e-5); setConstraintTolerance(1e-5);
setRandomNumberSeed(0); setRandomNumberSeed(0);
} }
double DrudeLangevinIntegrator::getMaxDrudeDistance() const {
return maxDrudeDistance;
}
void DrudeLangevinIntegrator::setMaxDrudeDistance(double distance) {
if (distance < 0)
throw OpenMMException("setMaxDrudeDistance: Distance cannot be negative");
maxDrudeDistance = distance;
}
void DrudeLangevinIntegrator::initialize(ContextImpl& contextRef) { void DrudeLangevinIntegrator::initialize(ContextImpl& contextRef) {
if (owner != NULL && &contextRef.getOwner() != owner) if (owner != NULL && &contextRef.getOwner() != owner)
throw OpenMMException("This Integrator is already bound to a context"); throw OpenMMException("This Integrator is already bound to a context");
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Biological Structures at Stanford, funded under the NIH Roadmap for * * Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org. * * Medical Research, grant U54 GM072970. See https://simtk.org. *
* * * *
* Portions copyright (c) 2013 Stanford University and the Authors. * * Portions copyright (c) 2013-2015 Stanford University and the Authors. *
* Authors: Peter Eastman * * Authors: Peter Eastman *
* Contributors: * * Contributors: *
* * * *
...@@ -279,6 +279,7 @@ void CudaIntegrateDrudeLangevinStepKernel::initialize(const System& system, cons ...@@ -279,6 +279,7 @@ void CudaIntegrateDrudeLangevinStepKernel::initialize(const System& system, cons
CUmodule module = cu.createModule(CudaKernelSources::vectorOps+CudaDrudeKernelSources::drudeLangevin, defines, ""); CUmodule module = cu.createModule(CudaKernelSources::vectorOps+CudaDrudeKernelSources::drudeLangevin, defines, "");
kernel1 = cu.getKernel(module, "integrateDrudeLangevinPart1"); kernel1 = cu.getKernel(module, "integrateDrudeLangevinPart1");
kernel2 = cu.getKernel(module, "integrateDrudeLangevinPart2"); kernel2 = cu.getKernel(module, "integrateDrudeLangevinPart2");
hardwallKernel = cu.getKernel(module, "applyHardWallConstraints");
prevStepSize = -1.0; prevStepSize = -1.0;
} }
...@@ -296,6 +297,8 @@ void CudaIntegrateDrudeLangevinStepKernel::execute(ContextImpl& context, const D ...@@ -296,6 +297,8 @@ void CudaIntegrateDrudeLangevinStepKernel::execute(ContextImpl& context, const D
double vscaleDrude = exp(-stepSize*integrator.getDrudeFriction()); double vscaleDrude = exp(-stepSize*integrator.getDrudeFriction());
double fscaleDrude = (1-vscaleDrude)/integrator.getDrudeFriction()/(double) 0x100000000; double fscaleDrude = (1-vscaleDrude)/integrator.getDrudeFriction()/(double) 0x100000000;
double noisescaleDrude = sqrt(2*BOLTZ*integrator.getDrudeTemperature()*integrator.getDrudeFriction())*sqrt(0.5*(1-vscaleDrude*vscaleDrude)/integrator.getDrudeFriction()); double noisescaleDrude = sqrt(2*BOLTZ*integrator.getDrudeTemperature()*integrator.getDrudeFriction())*sqrt(0.5*(1-vscaleDrude*vscaleDrude)/integrator.getDrudeFriction());
double maxDrudeDistance = integrator.getMaxDrudeDistance();
double hardwallscaleDrude = sqrt(BOLTZ*integrator.getDrudeTemperature());
if (stepSize != prevStepSize) { if (stepSize != prevStepSize) {
if (cu.getUseDoublePrecision() || cu.getUseMixedPrecision()) { if (cu.getUseDoublePrecision() || cu.getUseMixedPrecision()) {
double2 ss = make_double2(0, stepSize); double2 ss = make_double2(0, stepSize);
...@@ -316,7 +319,9 @@ void CudaIntegrateDrudeLangevinStepKernel::execute(ContextImpl& context, const D ...@@ -316,7 +319,9 @@ void CudaIntegrateDrudeLangevinStepKernel::execute(ContextImpl& context, const D
float vscaleDrudeFloat = (float) vscaleDrude; float vscaleDrudeFloat = (float) vscaleDrude;
float fscaleDrudeFloat = (float) fscaleDrude; float fscaleDrudeFloat = (float) fscaleDrude;
float noisescaleDrudeFloat = (float) noisescaleDrude; float noisescaleDrudeFloat = (float) noisescaleDrude;
void *vscalePtr, *fscalePtr, *noisescalePtr, *vscaleDrudePtr, *fscaleDrudePtr, *noisescaleDrudePtr; float maxDrudeDistanceFloat =(float) maxDrudeDistance;
float hardwallscaleDrudeFloat = (float) hardwallscaleDrude;
void *vscalePtr, *fscalePtr, *noisescalePtr, *vscaleDrudePtr, *fscaleDrudePtr, *noisescaleDrudePtr, *maxDrudeDistancePtr, *hardwallscaleDrudePtr;
if (cu.getUseDoublePrecision() || cu.getUseMixedPrecision()) { if (cu.getUseDoublePrecision() || cu.getUseMixedPrecision()) {
vscalePtr = &vscale; vscalePtr = &vscale;
fscalePtr = &fscale; fscalePtr = &fscale;
...@@ -324,6 +329,8 @@ void CudaIntegrateDrudeLangevinStepKernel::execute(ContextImpl& context, const D ...@@ -324,6 +329,8 @@ void CudaIntegrateDrudeLangevinStepKernel::execute(ContextImpl& context, const D
vscaleDrudePtr = &vscaleDrude; vscaleDrudePtr = &vscaleDrude;
fscaleDrudePtr = &fscaleDrude; fscaleDrudePtr = &fscaleDrude;
noisescaleDrudePtr = &noisescaleDrude; noisescaleDrudePtr = &noisescaleDrude;
maxDrudeDistancePtr = &maxDrudeDistance;
hardwallscaleDrudePtr = &hardwallscaleDrude;
} }
else { else {
vscalePtr = &vscaleFloat; vscalePtr = &vscaleFloat;
...@@ -332,6 +339,8 @@ void CudaIntegrateDrudeLangevinStepKernel::execute(ContextImpl& context, const D ...@@ -332,6 +339,8 @@ void CudaIntegrateDrudeLangevinStepKernel::execute(ContextImpl& context, const D
vscaleDrudePtr = &vscaleDrudeFloat; vscaleDrudePtr = &vscaleDrudeFloat;
fscaleDrudePtr = &fscaleDrudeFloat; fscaleDrudePtr = &fscaleDrudeFloat;
noisescaleDrudePtr = &noisescaleDrudeFloat; noisescaleDrudePtr = &noisescaleDrudeFloat;
maxDrudeDistancePtr = &maxDrudeDistanceFloat;
hardwallscaleDrudePtr = &hardwallscaleDrudeFloat;
} }
// Call the first integration kernel. // Call the first integration kernel.
...@@ -352,6 +361,12 @@ void CudaIntegrateDrudeLangevinStepKernel::execute(ContextImpl& context, const D ...@@ -352,6 +361,12 @@ void CudaIntegrateDrudeLangevinStepKernel::execute(ContextImpl& context, const D
void* args2[] = {&cu.getPosq().getDevicePointer(), &posCorrection, &integration.getPosDelta().getDevicePointer(), void* args2[] = {&cu.getPosq().getDevicePointer(), &posCorrection, &integration.getPosDelta().getDevicePointer(),
&cu.getVelm().getDevicePointer(), &integration.getStepSize().getDevicePointer()}; &cu.getVelm().getDevicePointer(), &integration.getStepSize().getDevicePointer()};
cu.executeKernel(kernel2, args2, numAtoms); cu.executeKernel(kernel2, args2, numAtoms);
// Apply hard wall constraints.
void* hardwallArgs[] = {&cu.getPosq().getDevicePointer(), &posCorrection, &cu.getVelm().getDevicePointer(),
&pairParticles->getDevicePointer(), &integration.getStepSize().getDevicePointer(), maxDrudeDistancePtr, hardwallscaleDrudePtr};
cu.executeKernel(hardwallKernel, hardwallArgs, pairParticles->getSize());
integration.computeVirtualSites(); integration.computeVirtualSites();
// Update the time and step count. // Update the time and step count.
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* Biological Structures at Stanford, funded under the NIH Roadmap for * * Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org. * * Medical Research, grant U54 GM072970. See https://simtk.org. *
* * * *
* Portions copyright (c) 2013 Stanford University and the Authors. * * Portions copyright (c) 2013-2015 Stanford University and the Authors. *
* Authors: Peter Eastman * * Authors: Peter Eastman *
* Contributors: * * Contributors: *
* * * *
...@@ -113,7 +113,7 @@ private: ...@@ -113,7 +113,7 @@ private:
double prevStepSize; double prevStepSize;
CudaArray* normalParticles; CudaArray* normalParticles;
CudaArray* pairParticles; CudaArray* pairParticles;
CUfunction kernel1, kernel2; CUfunction kernel1, kernel2, hardwallKernel;
}; };
/** /**
......
...@@ -101,3 +101,111 @@ extern "C" __global__ void integrateDrudeLangevinPart2(real4* __restrict__ posq, ...@@ -101,3 +101,111 @@ extern "C" __global__ void integrateDrudeLangevinPart2(real4* __restrict__ posq,
index += blockDim.x*gridDim.x; index += blockDim.x*gridDim.x;
} }
} }
/**
* Apply hard wall constraints
*/
extern "C" __global__ void applyHardWallConstraints(real4* __restrict__ posq, real4* __restrict__ posqCorrection, mixed4* __restrict__ velm,
const int2* __restrict__ pairParticles, const mixed2* __restrict__ dt, mixed maxDrudeDistance, mixed hardwallscaleDrude) {
mixed stepSize = dt[0].y;
for (int i = blockIdx.x*blockDim.x+threadIdx.x; i < NUM_PAIRS; i += blockDim.x*gridDim.x) {
int2 particles = pairParticles[i];
#ifdef USE_MIXED_PRECISION
real4 posReal1 = posq[particles.x];
real4 posReal2 = posq[particles.y];
real4 posCorr1 = posqCorrection[particles.x];
real4 posCorr2 = posqCorrection[particles.y];
mixed4 pos1 = make_mixed4(posReal1.x+(mixed)posCorr1.x, posReal1.y+(mixed)posCorr1.y, posReal1.z+(mixed)posCorr1.z, posReal1.w);
mixed4 pos2 = make_mixed4(posReal2.x+(mixed)posCorr2.x, posReal2.y+(mixed)posCorr2.y, posReal2.z+(mixed)posCorr2.z, posReal2.w);
#else
mixed4 pos1 = posq[particles.x];
mixed4 pos2 = posq[particles.y];
#endif
mixed4 delta = pos1-pos2;
mixed r = SQRT(delta.x*delta.x + delta.y*delta.y + delta.z*delta.z);
mixed rInv = RECIP(r);
if (rInv*maxDrudeDistance < 1) {
// The constraint has been violated, so make the inter-particle distance "bounce"
// off the hard wall.
mixed4 bondDir = delta*rInv;
mixed4 vel1 = velm[particles.x];
mixed4 vel2 = velm[particles.y];
mixed mass1 = RECIP(vel1.w);
mixed mass2 = RECIP(vel2.w);
mixed deltaR = r-maxDrudeDistance;
mixed deltaT = stepSize;
mixed dotvr1 = vel1.x*bondDir.x + vel1.y*bondDir.y + vel1.z*bondDir.z;
mixed4 vb1 = bondDir*dotvr1;
mixed4 vp1 = vel1-vb1;
if (vel2.w == 0) {
// The parent particle is massless, so move only the Drude particle.
if (dotvr1 != 0)
deltaT = deltaR/fabs(dotvr1);
if (deltaT > stepSize)
deltaT = stepSize;
dotvr1 = -dotvr1*hardwallscaleDrude/(fabs(dotvr1)*SQRT(mass1));
mixed dr = -deltaR + deltaT*dotvr1;
pos1.x += bondDir.x*dr;
pos1.y += bondDir.y*dr;
pos1.z += bondDir.z*dr;
#ifdef USE_MIXED_PRECISION
posq[particles.x] = make_real4((real) pos1.x, (real) pos1.y, (real) pos1.z, (real) pos1.w);
posqCorrection[particles.x] = make_real4(pos1.x-(real) pos1.x, pos1.y-(real) pos1.y, pos1.z-(real) pos1.z, 0);
#else
posq[particles.x] = pos1;
#endif
vel1.x = vp1.x + bondDir.x*dotvr1;
vel1.y = vp1.y + bondDir.y*dotvr1;
vel1.z = vp1.z + bondDir.z*dotvr1;
velm[particles.x] = vel1;
}
else {
// Move both particles.
mixed invTotalMass = RECIP(mass1+mass2);
mixed dotvr2 = vel2.x*bondDir.x + vel2.y*bondDir.y + vel2.z*bondDir.z;
mixed4 vb2 = bondDir*dotvr2;
mixed4 vp2 = vel2-vb2;
mixed vbCMass = (mass1*dotvr1 + mass2*dotvr2)*invTotalMass;
dotvr1 -= vbCMass;
dotvr2 -= vbCMass;
if (dotvr1 != dotvr2)
deltaT = deltaR/fabs(dotvr1-dotvr2);
if (deltaT > stepSize)
deltaT = stepSize;
mixed vBond = hardwallscaleDrude/SQRT(mass1);
dotvr1 = -dotvr1*vBond*mass2*invTotalMass/fabs(dotvr1);
dotvr2 = -dotvr2*vBond*mass1*invTotalMass/fabs(dotvr2);
mixed dr1 = -deltaR*mass2*invTotalMass + deltaT*dotvr1;
mixed dr2 = deltaR*mass1*invTotalMass + deltaT*dotvr2;
dotvr1 += vbCMass;
dotvr2 += vbCMass;
pos1.x += bondDir.x*dr1;
pos1.y += bondDir.y*dr1;
pos1.z += bondDir.z*dr1;
pos2.x += bondDir.x*dr2;
pos2.y += bondDir.y*dr2;
pos2.z += bondDir.z*dr2;
#ifdef USE_MIXED_PRECISION
posq[particles.x] = make_real4((real) pos1.x, (real) pos1.y, (real) pos1.z, (real) pos1.w);
posq[particles.y] = make_real4((real) pos2.x, (real) pos2.y, (real) pos2.z, (real) pos2.w);
posqCorrection[particles.x] = make_real4(pos1.x-(real) pos1.x, pos1.y-(real) pos1.y, pos1.z-(real) pos1.z, 0);
posqCorrection[particles.y] = make_real4(pos2.x-(real) pos2.x, pos2.y-(real) pos2.y, pos2.z-(real) pos2.z, 0);
#else
posq[particles.x] = pos1;
posq[particles.y] = pos2;
#endif
vel1.x = vp1.x + bondDir.x*dotvr1;
vel1.y = vp1.y + bondDir.y*dotvr1;
vel1.z = vp1.z + bondDir.z*dotvr1;
vel2.x = vp2.x + bondDir.x*dotvr2;
vel2.y = vp2.y + bondDir.y*dotvr2;
vel2.z = vp2.z + bondDir.z*dotvr2;
velm[particles.x] = vel1;
velm[particles.y] = vel2;
}
}
}
}
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Biological Structures at Stanford, funded under the NIH Roadmap for * * Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org. * * Medical Research, grant U54 GM072970. See https://simtk.org. *
* * * *
* Portions copyright (c) 2013 Stanford University and the Authors. * * Portions copyright (c) 2013-2015 Stanford University and the Authors. *
* Authors: Peter Eastman * * Authors: Peter Eastman *
* Contributors: * * Contributors: *
* * * *
...@@ -60,6 +60,7 @@ void testSinglePair() { ...@@ -60,6 +60,7 @@ void testSinglePair() {
const double mass2 = 0.1; const double mass2 = 0.1;
const double totalMass = mass1+mass2; const double totalMass = mass1+mass2;
const double reducedMass = (mass1*mass2)/(mass1+mass2); const double reducedMass = (mass1*mass2)/(mass1+mass2);
const double maxDistance = 0.05;
System system; System system;
system.addParticle(mass1); system.addParticle(mass1);
system.addParticle(mass2); system.addParticle(mass2);
...@@ -70,6 +71,7 @@ void testSinglePair() { ...@@ -70,6 +71,7 @@ void testSinglePair() {
positions[0] = Vec3(0, 0, 0); positions[0] = Vec3(0, 0, 0);
positions[1] = Vec3(0, 0, 0); positions[1] = Vec3(0, 0, 0);
DrudeLangevinIntegrator integ(temperature, 20.0, temperatureDrude, 20.0, 0.003); DrudeLangevinIntegrator integ(temperature, 20.0, temperatureDrude, 20.0, 0.003);
integ.setMaxDrudeDistance(maxDistance);
Platform& platform = Platform::getPlatformByName("CUDA"); Platform& platform = Platform::getPlatformByName("CUDA");
Context context(system, integ, platform); Context context(system, integ, platform);
context.setPositions(positions); context.setPositions(positions);
...@@ -84,12 +86,15 @@ void testSinglePair() { ...@@ -84,12 +86,15 @@ void testSinglePair() {
int numSteps = 10000; int numSteps = 10000;
for (int i = 0; i < numSteps; i++) { for (int i = 0; i < numSteps; i++) {
integ.step(10); integ.step(10);
State state = context.getState(State::Velocities); State state = context.getState(State::Velocities | State::Positions);
const vector<Vec3>& vel = state.getVelocities(); const vector<Vec3>& vel = state.getVelocities();
Vec3 velCM = vel[0]*(mass1/totalMass) + vel[1]*(mass2/totalMass); Vec3 velCM = vel[0]*(mass1/totalMass) + vel[1]*(mass2/totalMass);
keCM += 0.5*totalMass*velCM.dot(velCM); keCM += 0.5*totalMass*velCM.dot(velCM);
Vec3 velInternal = vel[0]-vel[1]; Vec3 velInternal = vel[0]-vel[1];
keInternal += 0.5*reducedMass*velInternal.dot(velInternal); keInternal += 0.5*reducedMass*velInternal.dot(velInternal);
Vec3 delta = state.getPositions()[0]-state.getPositions()[1];
double distance = sqrt(delta.dot(delta));
ASSERT(distance <= maxDistance*(1+1e-6));
} }
ASSERT_USUALLY_EQUAL_TOL(3*0.5*BOLTZ*temperature, keCM/numSteps, 0.1); ASSERT_USUALLY_EQUAL_TOL(3*0.5*BOLTZ*temperature, keCM/numSteps, 0.1);
ASSERT_USUALLY_EQUAL_TOL(3*0.5*BOLTZ*temperatureDrude, keInternal/numSteps, 0.01); ASSERT_USUALLY_EQUAL_TOL(3*0.5*BOLTZ*temperatureDrude, keInternal/numSteps, 0.01);
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Biological Structures at Stanford, funded under the NIH Roadmap for * * Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org. * * Medical Research, grant U54 GM072970. See https://simtk.org. *
* * * *
* Portions copyright (c) 2013 Stanford University and the Authors. * * Portions copyright (c) 2013-2015 Stanford University and the Authors. *
* Authors: Peter Eastman * * Authors: Peter Eastman *
* Contributors: * * Contributors: *
* * * *
...@@ -284,6 +284,7 @@ void OpenCLIntegrateDrudeLangevinStepKernel::initialize(const System& system, co ...@@ -284,6 +284,7 @@ void OpenCLIntegrateDrudeLangevinStepKernel::initialize(const System& system, co
cl::Program program = cl.createProgram(OpenCLDrudeKernelSources::drudeLangevin, defines, ""); cl::Program program = cl.createProgram(OpenCLDrudeKernelSources::drudeLangevin, defines, "");
kernel1 = cl::Kernel(program, "integrateDrudeLangevinPart1"); kernel1 = cl::Kernel(program, "integrateDrudeLangevinPart1");
kernel2 = cl::Kernel(program, "integrateDrudeLangevinPart2"); kernel2 = cl::Kernel(program, "integrateDrudeLangevinPart2");
hardwallKernel = cl::Kernel(program, "applyHardWallConstraints");
prevStepSize = -1.0; prevStepSize = -1.0;
} }
...@@ -307,6 +308,14 @@ void OpenCLIntegrateDrudeLangevinStepKernel::execute(ContextImpl& context, const ...@@ -307,6 +308,14 @@ void OpenCLIntegrateDrudeLangevinStepKernel::execute(ContextImpl& context, const
kernel2.setArg<cl::Buffer>(2, integration.getPosDelta().getDeviceBuffer()); kernel2.setArg<cl::Buffer>(2, integration.getPosDelta().getDeviceBuffer());
kernel2.setArg<cl::Buffer>(3, cl.getVelm().getDeviceBuffer()); kernel2.setArg<cl::Buffer>(3, cl.getVelm().getDeviceBuffer());
kernel2.setArg<cl::Buffer>(4, integration.getStepSize().getDeviceBuffer()); kernel2.setArg<cl::Buffer>(4, integration.getStepSize().getDeviceBuffer());
hardwallKernel.setArg<cl::Buffer>(0, cl.getPosq().getDeviceBuffer());
if (cl.getUseMixedPrecision())
hardwallKernel.setArg<cl::Buffer>(1, cl.getPosqCorrection().getDeviceBuffer());
else
hardwallKernel.setArg<void*>(1, NULL);
hardwallKernel.setArg<cl::Buffer>(2, cl.getVelm().getDeviceBuffer());
hardwallKernel.setArg<cl::Buffer>(3, pairParticles->getDeviceBuffer());
hardwallKernel.setArg<cl::Buffer>(4, integration.getStepSize().getDeviceBuffer());
} }
// Compute integrator coefficients. // Compute integrator coefficients.
...@@ -318,6 +327,8 @@ void OpenCLIntegrateDrudeLangevinStepKernel::execute(ContextImpl& context, const ...@@ -318,6 +327,8 @@ void OpenCLIntegrateDrudeLangevinStepKernel::execute(ContextImpl& context, const
double vscaleDrude = exp(-stepSize*integrator.getDrudeFriction()); double vscaleDrude = exp(-stepSize*integrator.getDrudeFriction());
double fscaleDrude = (1-vscaleDrude)/integrator.getDrudeFriction(); double fscaleDrude = (1-vscaleDrude)/integrator.getDrudeFriction();
double noisescaleDrude = sqrt(2*BOLTZ*integrator.getDrudeTemperature()*integrator.getDrudeFriction())*sqrt(0.5*(1-vscaleDrude*vscaleDrude)/integrator.getDrudeFriction()); double noisescaleDrude = sqrt(2*BOLTZ*integrator.getDrudeTemperature()*integrator.getDrudeFriction())*sqrt(0.5*(1-vscaleDrude*vscaleDrude)/integrator.getDrudeFriction());
double maxDrudeDistance = integrator.getMaxDrudeDistance();
double hardwallscaleDrude = sqrt(BOLTZ*integrator.getDrudeTemperature());
if (stepSize != prevStepSize) { if (stepSize != prevStepSize) {
if (cl.getUseDoublePrecision() || cl.getUseMixedPrecision()) { if (cl.getUseDoublePrecision() || cl.getUseMixedPrecision()) {
mm_double2 ss = mm_double2(0, stepSize); mm_double2 ss = mm_double2(0, stepSize);
...@@ -336,6 +347,8 @@ void OpenCLIntegrateDrudeLangevinStepKernel::execute(ContextImpl& context, const ...@@ -336,6 +347,8 @@ void OpenCLIntegrateDrudeLangevinStepKernel::execute(ContextImpl& context, const
kernel1.setArg<cl_double>(9, vscaleDrude); kernel1.setArg<cl_double>(9, vscaleDrude);
kernel1.setArg<cl_double>(10, fscaleDrude); kernel1.setArg<cl_double>(10, fscaleDrude);
kernel1.setArg<cl_double>(11, noisescaleDrude); kernel1.setArg<cl_double>(11, noisescaleDrude);
hardwallKernel.setArg<cl_double>(5, maxDrudeDistance);
hardwallKernel.setArg<cl_double>(6, hardwallscaleDrude);
} }
else { else {
kernel1.setArg<cl_float>(6, (cl_float) vscale); kernel1.setArg<cl_float>(6, (cl_float) vscale);
...@@ -344,6 +357,8 @@ void OpenCLIntegrateDrudeLangevinStepKernel::execute(ContextImpl& context, const ...@@ -344,6 +357,8 @@ void OpenCLIntegrateDrudeLangevinStepKernel::execute(ContextImpl& context, const
kernel1.setArg<cl_float>(9, (cl_float) vscaleDrude); kernel1.setArg<cl_float>(9, (cl_float) vscaleDrude);
kernel1.setArg<cl_float>(10, (cl_float) fscaleDrude); kernel1.setArg<cl_float>(10, (cl_float) fscaleDrude);
kernel1.setArg<cl_float>(11, (cl_float) noisescaleDrude); kernel1.setArg<cl_float>(11, (cl_float) noisescaleDrude);
hardwallKernel.setArg<cl_float>(5, (cl_float) maxDrudeDistance);
hardwallKernel.setArg<cl_float>(6, (cl_float) hardwallscaleDrude);
} }
// Call the first integration kernel. // Call the first integration kernel.
...@@ -358,6 +373,10 @@ void OpenCLIntegrateDrudeLangevinStepKernel::execute(ContextImpl& context, const ...@@ -358,6 +373,10 @@ void OpenCLIntegrateDrudeLangevinStepKernel::execute(ContextImpl& context, const
// Call the second integration kernel. // Call the second integration kernel.
cl.executeKernel(kernel2, numAtoms); cl.executeKernel(kernel2, numAtoms);
// Apply hard wall constraints.
cl.executeKernel(hardwallKernel, pairParticles->getSize());
integration.computeVirtualSites(); integration.computeVirtualSites();
// Update the time and step count. // Update the time and step count.
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* Biological Structures at Stanford, funded under the NIH Roadmap for * * Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org. * * Medical Research, grant U54 GM072970. See https://simtk.org. *
* * * *
* Portions copyright (c) 2013 Stanford University and the Authors. * * Portions copyright (c) 2013-2015 Stanford University and the Authors. *
* Authors: Peter Eastman * * Authors: Peter Eastman *
* Contributors: * * Contributors: *
* * * *
...@@ -114,7 +114,7 @@ private: ...@@ -114,7 +114,7 @@ private:
double prevStepSize; double prevStepSize;
OpenCLArray* normalParticles; OpenCLArray* normalParticles;
OpenCLArray* pairParticles; OpenCLArray* pairParticles;
cl::Kernel kernel1, kernel2; cl::Kernel kernel1, kernel2, hardwallKernel;
}; };
/** /**
......
...@@ -101,3 +101,99 @@ __kernel void integrateDrudeLangevinPart2(__global real4* restrict posq, __globa ...@@ -101,3 +101,99 @@ __kernel void integrateDrudeLangevinPart2(__global real4* restrict posq, __globa
index += get_global_size(0); index += get_global_size(0);
} }
} }
/**
* Apply hard wall constraints
*/
__kernel void applyHardWallConstraints(__global real4* restrict posq, __global real4* restrict posqCorrection, __global mixed4* restrict velm,
__global const int2* restrict pairParticles, __global const mixed2* restrict dt, mixed maxDrudeDistance, mixed hardwallscaleDrude) {
mixed stepSize = dt[0].y;
for (int i = get_global_id(0); i < NUM_PAIRS; i += get_global_size(0)) {
int2 particles = pairParticles[i];
#ifdef USE_MIXED_PRECISION
real4 posReal1 = posq[particles.x];
real4 posReal2 = posq[particles.y];
real4 posCorr1 = posqCorrection[particles.x];
real4 posCorr2 = posqCorrection[particles.y];
mixed4 pos1 = (mixed4) (posReal1.x+(mixed)posCorr1.x, posReal1.y+(mixed)posCorr1.y, posReal1.z+(mixed)posCorr1.z, posReal1.w);
mixed4 pos2 = (mixed4) (posReal2.x+(mixed)posCorr2.x, posReal2.y+(mixed)posCorr2.y, posReal2.z+(mixed)posCorr2.z, posReal2.w);
#else
mixed4 pos1 = posq[particles.x];
mixed4 pos2 = posq[particles.y];
#endif
mixed4 delta = pos1-pos2;
mixed r = sqrt(delta.x*delta.x + delta.y*delta.y + delta.z*delta.z);
mixed rInv = 1/r;
if (rInv*maxDrudeDistance < 1) {
// The constraint has been violated, so make the inter-particle distance "bounce"
// off the hard wall.
mixed4 bondDir = delta*rInv;
mixed4 vel1 = velm[particles.x];
mixed4 vel2 = velm[particles.y];
mixed mass1 = 1/vel1.w;
mixed mass2 = 1/vel2.w;
mixed deltaR = r-maxDrudeDistance;
mixed deltaT = stepSize;
mixed dotvr1 = vel1.x*bondDir.x + vel1.y*bondDir.y + vel1.z*bondDir.z;
mixed4 vb1 = bondDir*dotvr1;
mixed4 vp1 = vel1-vb1;
if (vel2.w == 0) {
// The parent particle is massless, so move only the Drude particle.
if (dotvr1 != 0)
deltaT = deltaR/fabs(dotvr1);
if (deltaT > stepSize)
deltaT = stepSize;
dotvr1 = -dotvr1*hardwallscaleDrude/(fabs(dotvr1)*sqrt(mass1));
mixed dr = -deltaR + deltaT*dotvr1;
pos1.xyz += bondDir.xyz*dr;
#ifdef USE_MIXED_PRECISION
posq[particles.x] = (real4) ((real) pos1.x, (real) pos1.y, (real) pos1.z, (real) pos1.w);
posqCorrection[particles.x] = (real4) (pos1.x-(real) pos1.x, pos1.y-(real) pos1.y, pos1.z-(real) pos1.z, 0);
#else
posq[particles.x] = pos1;
#endif
vel1.xyz = vp1.xyz + bondDir.xyz*dotvr1;
velm[particles.x] = vel1;
}
else {
// Move both particles.
mixed invTotalMass = 1/(mass1+mass2);
mixed dotvr2 = vel2.x*bondDir.x + vel2.y*bondDir.y + vel2.z*bondDir.z;
mixed4 vb2 = bondDir*dotvr2;
mixed4 vp2 = vel2-vb2;
mixed vbCMass = (mass1*dotvr1 + mass2*dotvr2)*invTotalMass;
dotvr1 -= vbCMass;
dotvr2 -= vbCMass;
if (dotvr1 != dotvr2)
deltaT = deltaR/fabs(dotvr1-dotvr2);
if (deltaT > stepSize)
deltaT = stepSize;
mixed vBond = hardwallscaleDrude/sqrt(mass1);
dotvr1 = -dotvr1*vBond*mass2*invTotalMass/fabs(dotvr1);
dotvr2 = -dotvr2*vBond*mass1*invTotalMass/fabs(dotvr2);
mixed dr1 = -deltaR*mass2*invTotalMass + deltaT*dotvr1;
mixed dr2 = deltaR*mass1*invTotalMass + deltaT*dotvr2;
dotvr1 += vbCMass;
dotvr2 += vbCMass;
pos1.xyz += bondDir.xyz*dr1;
pos2.xyz += bondDir.xyz*dr2;
#ifdef USE_MIXED_PRECISION
posq[particles.x] = (real4) ((real) pos1.x, (real) pos1.y, (real) pos1.z, (real) pos1.w);
posq[particles.y] = (real4) ((real) pos2.x, (real) pos2.y, (real) pos2.z, (real) pos2.w);
posqCorrection[particles.x] = (real4) (pos1.x-(real) pos1.x, pos1.y-(real) pos1.y, pos1.z-(real) pos1.z, 0);
posqCorrection[particles.y] = (real4) (pos2.x-(real) pos2.x, pos2.y-(real) pos2.y, pos2.z-(real) pos2.z, 0);
#else
posq[particles.x] = pos1;
posq[particles.y] = pos2;
#endif
vel1.xyz = vp1.xyz + bondDir.xyz*dotvr1;
vel2.xyz = vp2.xyz + bondDir.xyz*dotvr2;
velm[particles.x] = vel1;
velm[particles.y] = vel2;
}
}
}
}
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Biological Structures at Stanford, funded under the NIH Roadmap for * * Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org. * * Medical Research, grant U54 GM072970. See https://simtk.org. *
* * * *
* Portions copyright (c) 2013 Stanford University and the Authors. * * Portions copyright (c) 2013-2015 Stanford University and the Authors. *
* Authors: Peter Eastman * * Authors: Peter Eastman *
* Contributors: * * Contributors: *
* * * *
...@@ -60,6 +60,7 @@ void testSinglePair() { ...@@ -60,6 +60,7 @@ void testSinglePair() {
const double mass2 = 0.1; const double mass2 = 0.1;
const double totalMass = mass1+mass2; const double totalMass = mass1+mass2;
const double reducedMass = (mass1*mass2)/(mass1+mass2); const double reducedMass = (mass1*mass2)/(mass1+mass2);
const double maxDistance = 0.05;
System system; System system;
system.addParticle(mass1); system.addParticle(mass1);
system.addParticle(mass2); system.addParticle(mass2);
...@@ -70,6 +71,7 @@ void testSinglePair() { ...@@ -70,6 +71,7 @@ void testSinglePair() {
positions[0] = Vec3(0, 0, 0); positions[0] = Vec3(0, 0, 0);
positions[1] = Vec3(0, 0, 0); positions[1] = Vec3(0, 0, 0);
DrudeLangevinIntegrator integ(temperature, 20.0, temperatureDrude, 20.0, 0.003); DrudeLangevinIntegrator integ(temperature, 20.0, temperatureDrude, 20.0, 0.003);
integ.setMaxDrudeDistance(maxDistance);
Platform& platform = Platform::getPlatformByName("OpenCL"); Platform& platform = Platform::getPlatformByName("OpenCL");
Context context(system, integ, platform); Context context(system, integ, platform);
context.setPositions(positions); context.setPositions(positions);
...@@ -84,12 +86,15 @@ void testSinglePair() { ...@@ -84,12 +86,15 @@ void testSinglePair() {
int numSteps = 10000; int numSteps = 10000;
for (int i = 0; i < numSteps; i++) { for (int i = 0; i < numSteps; i++) {
integ.step(10); integ.step(10);
State state = context.getState(State::Velocities); State state = context.getState(State::Velocities | State::Positions);
const vector<Vec3>& vel = state.getVelocities(); const vector<Vec3>& vel = state.getVelocities();
Vec3 velCM = vel[0]*(mass1/totalMass) + vel[1]*(mass2/totalMass); Vec3 velCM = vel[0]*(mass1/totalMass) + vel[1]*(mass2/totalMass);
keCM += 0.5*totalMass*velCM.dot(velCM); keCM += 0.5*totalMass*velCM.dot(velCM);
Vec3 velInternal = vel[0]-vel[1]; Vec3 velInternal = vel[0]-vel[1];
keInternal += 0.5*reducedMass*velInternal.dot(velInternal); keInternal += 0.5*reducedMass*velInternal.dot(velInternal);
Vec3 delta = state.getPositions()[0]-state.getPositions()[1];
double distance = sqrt(delta.dot(delta));
ASSERT(distance <= maxDistance*(1+1e-6));
} }
ASSERT_USUALLY_EQUAL_TOL(3*0.5*BOLTZ*temperature, keCM/numSteps, 0.1); ASSERT_USUALLY_EQUAL_TOL(3*0.5*BOLTZ*temperature, keCM/numSteps, 0.1);
ASSERT_USUALLY_EQUAL_TOL(3*0.5*BOLTZ*temperatureDrude, keInternal/numSteps, 0.01); ASSERT_USUALLY_EQUAL_TOL(3*0.5*BOLTZ*temperatureDrude, keInternal/numSteps, 0.01);
......
...@@ -75,6 +75,6 @@ SET_TARGET_PROPERTIES(${SHARED_TARGET} PROPERTIES LINK_FLAGS "${EXTRA_LINK_FLAGS ...@@ -75,6 +75,6 @@ SET_TARGET_PROPERTIES(${SHARED_TARGET} PROPERTIES LINK_FLAGS "${EXTRA_LINK_FLAGS
INSTALL(TARGETS ${SHARED_TARGET} DESTINATION ${CMAKE_INSTALL_PREFIX}/lib/plugins) INSTALL(TARGETS ${SHARED_TARGET} DESTINATION ${CMAKE_INSTALL_PREFIX}/lib/plugins)
IF(BUILD_TESTING) IF(BUILD_TESTING AND OPENMM_BUILD_REFERENCE_TESTS)
SUBDIRS (tests) SUBDIRS (tests)
ENDIF(BUILD_TESTING) ENDIF(BUILD_TESTING AND OPENMM_BUILD_REFERENCE_TESTS)
...@@ -235,7 +235,6 @@ void ReferenceIntegrateDrudeLangevinStepKernel::initialize(const System& system, ...@@ -235,7 +235,6 @@ void ReferenceIntegrateDrudeLangevinStepKernel::initialize(const System& system,
// Identify particle pairs and ordinary particles. // Identify particle pairs and ordinary particles.
set<int> particles; set<int> particles;
vector<RealOpenMM> particleMass;
for (int i = 0; i < system.getNumParticles(); i++) { for (int i = 0; i < system.getNumParticles(); i++) {
particles.insert(i); particles.insert(i);
double mass = system.getParticleMass(i); double mass = system.getParticleMass(i);
...@@ -325,6 +324,75 @@ void ReferenceIntegrateDrudeLangevinStepKernel::execute(ContextImpl& context, co ...@@ -325,6 +324,75 @@ void ReferenceIntegrateDrudeLangevinStepKernel::execute(ContextImpl& context, co
pos[i] = xPrime[i]; pos[i] = xPrime[i];
} }
} }
// Apply hard wall constraints.
const RealOpenMM maxDrudeDistance = integrator.getMaxDrudeDistance();
if (maxDrudeDistance > 0) {
const RealOpenMM hardwallscaleDrude = sqrt(kTDrude);
for (int i = 0; i < (int) pairParticles.size(); i++) {
int p1 = pairParticles[i].first;
int p2 = pairParticles[i].second;
RealVec delta = pos[p1]-pos[p2];
RealOpenMM r = sqrt(delta.dot(delta));
RealOpenMM rInv = 1/r;
if (rInv*maxDrudeDistance < 1.0) {
// The constraint has been violated, so make the inter-particle distance "bounce"
// off the hard wall.
if (rInv*maxDrudeDistance < 0.5)
throw OpenMMException("Drude particle moved too far beyond hard wall constraint");
RealVec bondDir = delta*rInv;
RealVec vel1 = vel[p1];
RealVec vel2 = vel[p2];
RealOpenMM mass1 = particleMass[p1];
RealOpenMM mass2 = particleMass[p2];
RealOpenMM deltaR = r-maxDrudeDistance;
RealOpenMM deltaT = dt;
RealOpenMM dotvr1 = vel1.dot(bondDir);
RealVec vb1 = bondDir*dotvr1;
RealVec vp1 = vel1-vb1;
if (mass2 == 0) {
// The parent particle is massless, so move only the Drude particle.
if (dotvr1 != 0.0)
deltaT = deltaR/abs(dotvr1);
if (deltaT > dt)
deltaT = dt;
dotvr1 = -dotvr1*hardwallscaleDrude/(abs(dotvr1)*sqrt(mass1));
RealOpenMM dr = -deltaR + deltaT*dotvr1;
pos[p1] += bondDir*dr;
vel[p1] = vp1 + bondDir*dotvr1;
}
else {
// Move both particles.
RealOpenMM invTotalMass = pairInvTotalMass[i];
RealOpenMM dotvr2 = vel2.dot(bondDir);
RealVec vb2 = bondDir*dotvr2;
RealVec vp2 = vel2-vb2;
RealOpenMM vbCMass = (mass1*dotvr1 + mass2*dotvr2)*invTotalMass;
dotvr1 -= vbCMass;
dotvr2 -= vbCMass;
if (dotvr1 != dotvr2)
deltaT = deltaR/abs(dotvr1-dotvr2);
if (deltaT > dt)
deltaT = dt;
RealOpenMM vBond = hardwallscaleDrude/sqrt(mass1);
dotvr1 = -dotvr1*vBond*mass2*invTotalMass/abs(dotvr1);
dotvr2 = -dotvr2*vBond*mass1*invTotalMass/abs(dotvr2);
RealOpenMM dr1 = -deltaR*mass2*invTotalMass + deltaT*dotvr1;
RealOpenMM dr2 = deltaR*mass1*invTotalMass + deltaT*dotvr2;
dotvr1 += vbCMass;
dotvr2 += vbCMass;
pos[p1] += bondDir*dr1;
pos[p2] += bondDir*dr2;
vel[p1] = vp1 + bondDir*dotvr1;
vel[p2] = vp2 + bondDir*dotvr2;
}
}
}
}
ReferenceVirtualSites::computePositions(context.getSystem(), pos); ReferenceVirtualSites::computePositions(context.getSystem(), pos);
data.time += integrator.getStepSize(); data.time += integrator.getStepSize();
data.stepCount++; data.stepCount++;
......
...@@ -115,6 +115,7 @@ private: ...@@ -115,6 +115,7 @@ private:
ReferencePlatform::PlatformData& data; ReferencePlatform::PlatformData& data;
std::vector<int> normalParticles; std::vector<int> normalParticles;
std::vector<std::pair<int, int> > pairParticles; std::vector<std::pair<int, int> > pairParticles;
std::vector<double> particleMass;
std::vector<double> particleInvMass; std::vector<double> particleInvMass;
std::vector<double> pairInvTotalMass; std::vector<double> pairInvTotalMass;
std::vector<double> pairInvReducedMass; std::vector<double> pairInvReducedMass;
...@@ -158,6 +159,7 @@ private: ...@@ -158,6 +159,7 @@ private:
std::vector<double> particleInvMass; std::vector<double> particleInvMass;
lbfgsfloatval_t *minimizerPos; lbfgsfloatval_t *minimizerPos;
lbfgs_parameter_t minimizerParams; lbfgs_parameter_t minimizerParams;
double maxDrudeDistance;
}; };
} // namespace OpenMM } // namespace OpenMM
......
...@@ -60,6 +60,7 @@ void testSinglePair() { ...@@ -60,6 +60,7 @@ void testSinglePair() {
const double mass2 = 0.1; const double mass2 = 0.1;
const double totalMass = mass1+mass2; const double totalMass = mass1+mass2;
const double reducedMass = (mass1*mass2)/(mass1+mass2); const double reducedMass = (mass1*mass2)/(mass1+mass2);
const double maxDistance = 0.05;
System system; System system;
system.addParticle(mass1); system.addParticle(mass1);
system.addParticle(mass2); system.addParticle(mass2);
...@@ -70,6 +71,7 @@ void testSinglePair() { ...@@ -70,6 +71,7 @@ void testSinglePair() {
positions[0] = Vec3(0, 0, 0); positions[0] = Vec3(0, 0, 0);
positions[1] = Vec3(0, 0, 0); positions[1] = Vec3(0, 0, 0);
DrudeLangevinIntegrator integ(temperature, 20.0, temperatureDrude, 20.0, 0.003); DrudeLangevinIntegrator integ(temperature, 20.0, temperatureDrude, 20.0, 0.003);
integ.setMaxDrudeDistance(maxDistance);
Platform& platform = Platform::getPlatformByName("Reference"); Platform& platform = Platform::getPlatformByName("Reference");
Context context(system, integ, platform); Context context(system, integ, platform);
context.setPositions(positions); context.setPositions(positions);
...@@ -84,12 +86,15 @@ void testSinglePair() { ...@@ -84,12 +86,15 @@ void testSinglePair() {
int numSteps = 10000; int numSteps = 10000;
for (int i = 0; i < numSteps; i++) { for (int i = 0; i < numSteps; i++) {
integ.step(10); integ.step(10);
State state = context.getState(State::Velocities); State state = context.getState(State::Velocities | State::Positions);
const vector<Vec3>& vel = state.getVelocities(); const vector<Vec3>& vel = state.getVelocities();
Vec3 velCM = vel[0]*(mass1/totalMass) + vel[1]*(mass2/totalMass); Vec3 velCM = vel[0]*(mass1/totalMass) + vel[1]*(mass2/totalMass);
keCM += 0.5*totalMass*velCM.dot(velCM); keCM += 0.5*totalMass*velCM.dot(velCM);
Vec3 velInternal = vel[0]-vel[1]; Vec3 velInternal = vel[0]-vel[1];
keInternal += 0.5*reducedMass*velInternal.dot(velInternal); keInternal += 0.5*reducedMass*velInternal.dot(velInternal);
Vec3 delta = state.getPositions()[0]-state.getPositions()[1];
double distance = sqrt(delta.dot(delta));
ASSERT(distance <= maxDistance*(1+1e-6));
} }
ASSERT_USUALLY_EQUAL_TOL(3*0.5*BOLTZ*temperature, keCM/numSteps, 0.1); ASSERT_USUALLY_EQUAL_TOL(3*0.5*BOLTZ*temperature, keCM/numSteps, 0.1);
ASSERT_USUALLY_EQUAL_TOL(3*0.5*BOLTZ*temperatureDrude, keInternal/numSteps, 0.01); ASSERT_USUALLY_EQUAL_TOL(3*0.5*BOLTZ*temperatureDrude, keInternal/numSteps, 0.01);
......
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