Commit 820a6baa authored by Peter Eastman's avatar Peter Eastman
Browse files

When computing kinetic energy, make sure the shifted velocities are...

When computing kinetic energy, make sure the shifted velocities are perpendicular to the constraints
parent 01afee8b
...@@ -643,6 +643,7 @@ CudaIntegrationUtilities::CudaIntegrationUtilities(CudaContext& context, const S ...@@ -643,6 +643,7 @@ CudaIntegrationUtilities::CudaIntegrationUtilities(CudaContext& context, const S
vsiteForceKernel = context.getKernel(module, "distributeVirtualSiteForces"); vsiteForceKernel = context.getKernel(module, "distributeVirtualSiteForces");
numVsites = num2Avg+num3Avg+numOutOfPlane; numVsites = num2Avg+num3Avg+numOutOfPlane;
randomKernel = context.getKernel(module, "generateRandomNumbers"); randomKernel = context.getKernel(module, "generateRandomNumbers");
timeShiftKernel = context.getKernel(module, "timeShiftVelocities");
} }
CudaIntegrationUtilities::~CudaIntegrationUtilities() { CudaIntegrationUtilities::~CudaIntegrationUtilities() {
...@@ -858,38 +859,46 @@ void CudaIntegrationUtilities::loadCheckpoint(istream& stream) { ...@@ -858,38 +859,46 @@ void CudaIntegrationUtilities::loadCheckpoint(istream& stream) {
double CudaIntegrationUtilities::computeKineticEnergy(double timeShift) { double CudaIntegrationUtilities::computeKineticEnergy(double timeShift) {
int numParticles = context.getNumAtoms(); int numParticles = context.getNumAtoms();
int paddedNumParticles = context.getPaddedNumAtoms(); if (timeShift != 0) {
long long* force = (long long*) context.getPinnedBuffer(); float timeShiftFloat = (float) timeShift;
context.getForce().download(force); void* timeShiftPtr = (context.getUseDoublePrecision() ? (void*) &timeShift : (void*) &timeShiftFloat);
double forceScale = timeShift/0x100000000LL;
// Copy the velocities into the posDelta array while we temporarily modify them.
context.getVelm().copyTo(*posDelta);
// Apply the time shift.
void* args[] = {&context.getVelm().getDevicePointer(), &context.getForce().getDevicePointer(), timeShiftPtr};
context.executeKernel(timeShiftKernel, args, numParticles);
applyConstraints(true, 1e-4);
}
// Compute the kinetic energy.
double energy = 0.0; double energy = 0.0;
if (context.getUseDoublePrecision() || context.getUseMixedPrecision()) { if (context.getUseDoublePrecision() || context.getUseMixedPrecision()) {
vector<double4> velm; vector<double4> velm;
context.getVelm().download(velm); context.getVelm().download(velm);
for (int i = 0; i < numParticles; i++) { for (int i = 0; i < numParticles; i++) {
double4 v = velm[i]; double4 v = velm[i];
if (v.w != 0) { if (v.w != 0)
double scale = forceScale*v.w;
v.x += scale*force[i];
v.y += scale*force[i+paddedNumParticles];
v.z += scale*force[i+paddedNumParticles*2];
energy += (v.x*v.x+v.y*v.y+v.z*v.z)/v.w; energy += (v.x*v.x+v.y*v.y+v.z*v.z)/v.w;
} }
} }
}
else { else {
vector<float4> velm; vector<float4> velm;
context.getVelm().download(velm); context.getVelm().download(velm);
for (int i = 0; i < numParticles; i++) { for (int i = 0; i < numParticles; i++) {
double4 v = make_double4(velm[i].x, velm[i].y, velm[i].z, velm[i].w); float4 v = velm[i];
if (v.w != 0) { if (v.w != 0)
double scale = forceScale*v.w;
v.x += scale*force[i];
v.y += scale*force[i+paddedNumParticles];
v.z += scale*force[i+paddedNumParticles*2];
energy += (v.x*v.x+v.y*v.y+v.z*v.z)/v.w; energy += (v.x*v.x+v.y*v.y+v.z*v.z)/v.w;
} }
} }
}
// Restore the velocities.
if (timeShift != 0)
posDelta->copyTo(context.getVelm());
return 0.5*energy; return 0.5*energy;
} }
...@@ -122,7 +122,7 @@ private: ...@@ -122,7 +122,7 @@ private:
CUfunction ccmaMultiplyKernel; CUfunction ccmaMultiplyKernel;
CUfunction ccmaUpdateKernel; CUfunction ccmaUpdateKernel;
CUfunction vsitePositionKernel, vsiteForceKernel; CUfunction vsitePositionKernel, vsiteForceKernel;
CUfunction randomKernel; CUfunction randomKernel, timeShiftKernel;
CudaArray* posDelta; CudaArray* posDelta;
CudaArray* settleAtoms; CudaArray* settleAtoms;
CudaArray* settleParams; CudaArray* settleParams;
......
...@@ -798,3 +798,19 @@ extern "C" __global__ void distributeVirtualSiteForces(const real4* __restrict__ ...@@ -798,3 +798,19 @@ extern "C" __global__ void distributeVirtualSiteForces(const real4* __restrict__
addForce(atoms.w, force, fp3); addForce(atoms.w, force, fp3);
} }
} }
/**
* Apply a time shift to the velocities before computing kinetic energy.
*/
extern "C" __global__ void timeShiftVelocities(mixed4* __restrict__ velm, const long long* __restrict__ force, real timeShift) {
const mixed scale = timeShift/(mixed) 0x100000000;
for (int index = blockIdx.x*blockDim.x+threadIdx.x; index < NUM_ATOMS; index += blockDim.x*gridDim.x) {
mixed4 velocity = velm[index];
if (velocity.w != 0.0) {
velocity.x += scale*force[index]*velocity.w;
velocity.y += scale*force[index+PADDED_NUM_ATOMS]*velocity.w;
velocity.z += scale*force[index+PADDED_NUM_ATOMS*2]*velocity.w;
velm[index] = velocity;
}
}
}
\ No newline at end of file
...@@ -120,6 +120,13 @@ OpenCLIntegrationUtilities::OpenCLIntegrationUtilities(OpenCLContext& context, c ...@@ -120,6 +120,13 @@ OpenCLIntegrationUtilities::OpenCLIntegrationUtilities(OpenCLContext& context, c
stepSize->upload(step); stepSize->upload(step);
} }
// Create the time shift kernel for calculating kinetic energy.
map<string, string> timeShiftDefines;
timeShiftDefines["NUM_ATOMS"] = context.intToString(system.getNumParticles());
cl::Program utilitiesProgram = context.createProgram(OpenCLKernelSources::integrationUtilities, timeShiftDefines);
timeShiftKernel = cl::Kernel(utilitiesProgram, "timeShiftVelocities");
// Create kernels for enforcing constraints. // Create kernels for enforcing constraints.
map<string, string> velocityDefines; map<string, string> velocityDefines;
...@@ -961,54 +968,48 @@ void OpenCLIntegrationUtilities::loadCheckpoint(istream& stream) { ...@@ -961,54 +968,48 @@ void OpenCLIntegrationUtilities::loadCheckpoint(istream& stream) {
double OpenCLIntegrationUtilities::computeKineticEnergy(double timeShift) { double OpenCLIntegrationUtilities::computeKineticEnergy(double timeShift) {
int numParticles = context.getNumAtoms(); int numParticles = context.getNumAtoms();
double energy = 0.0; if (timeShift != 0) {
if (context.getUseDoublePrecision()) { // Copy the velocities into the posDelta array while we temporarily modify them.
mm_double4* force = (mm_double4*) context.getPinnedBuffer();
context.getForce().download(force); context.getVelm().copyTo(*posDelta);
vector<mm_double4> velm;
context.getVelm().download(velm); // Apply the time shift.
for (int i = 0; i < numParticles; i++) {
mm_double4 v = velm[i]; timeShiftKernel.setArg<cl::Buffer>(0, context.getVelm().getDeviceBuffer());
if (v.w != 0) { timeShiftKernel.setArg<cl::Buffer>(1, context.getForce().getDeviceBuffer());
double scale = timeShift*v.w; if (context.getUseDoublePrecision() || context.getUseMixedPrecision())
v.x += scale*force[i].x; timeShiftKernel.setArg<cl_double>(2, timeShift);
v.y += scale*force[i].y; else
v.z += scale*force[i].z; timeShiftKernel.setArg<cl_float>(2, (cl_float) timeShift);
energy += (v.x*v.x+v.y*v.y+v.z*v.z)/v.w; context.executeKernel(timeShiftKernel, numParticles);
} applyConstraints(true, 1e-4);
}
} }
else if (context.getUseMixedPrecision()) {
mm_float4* force = (mm_float4*) context.getPinnedBuffer(); // Compute the kinetic energy.
context.getForce().download(force);
double energy = 0.0;
if (context.getUseDoublePrecision() || context.getUseMixedPrecision()) {
vector<mm_double4> velm; vector<mm_double4> velm;
context.getVelm().download(velm); context.getVelm().download(velm);
for (int i = 0; i < numParticles; i++) { for (int i = 0; i < numParticles; i++) {
mm_double4 v = velm[i]; mm_double4 v = velm[i];
if (v.w != 0) { if (v.w != 0)
double scale = timeShift*v.w;
v.x += scale*force[i].x;
v.y += scale*force[i].y;
v.z += scale*force[i].z;
energy += (v.x*v.x+v.y*v.y+v.z*v.z)/v.w; energy += (v.x*v.x+v.y*v.y+v.z*v.z)/v.w;
} }
} }
}
else { else {
mm_float4* force = (mm_float4*) context.getPinnedBuffer();
context.getForce().download(force);
vector<mm_float4> velm; vector<mm_float4> velm;
context.getVelm().download(velm); context.getVelm().download(velm);
for (int i = 0; i < numParticles; i++) { for (int i = 0; i < numParticles; i++) {
mm_double4 v = mm_double4(velm[i].x, velm[i].y, velm[i].z, velm[i].w); mm_float4 v = velm[i];
if (v.w != 0) { if (v.w != 0)
double scale = timeShift*v.w;
v.x += scale*force[i].x;
v.y += scale*force[i].y;
v.z += scale*force[i].z;
energy += (v.x*v.x+v.y*v.y+v.z*v.z)/v.w; energy += (v.x*v.x+v.y*v.y+v.z*v.z)/v.w;
} }
} }
}
// Restore the velocities.
if (timeShift != 0)
posDelta->copyTo(context.getVelm());
return 0.5*energy; return 0.5*energy;
} }
...@@ -122,7 +122,7 @@ private: ...@@ -122,7 +122,7 @@ private:
cl::Kernel ccmaMultiplyKernel; cl::Kernel ccmaMultiplyKernel;
cl::Kernel ccmaPosUpdateKernel, ccmaVelUpdateKernel; cl::Kernel ccmaPosUpdateKernel, ccmaVelUpdateKernel;
cl::Kernel vsitePositionKernel, vsiteForceKernel; cl::Kernel vsitePositionKernel, vsiteForceKernel;
cl::Kernel randomKernel; cl::Kernel randomKernel, timeShiftKernel;
OpenCLArray* posDelta; OpenCLArray* posDelta;
OpenCLArray* settleAtoms; OpenCLArray* settleAtoms;
OpenCLArray* settleParams; OpenCLArray* settleParams;
......
/**
* Apply a time shift to the velocities before computing kinetic energy.
*/
__kernel void timeShiftVelocities(__global mixed4* restrict velm, __global const real4* restrict force, real timeShift) {
for (int index = get_global_id(0); index < NUM_ATOMS; index += get_global_size(0)) {
mixed4 velocity = velm[index];
if (velocity.w != 0.0) {
velocity.xyz += timeShift*force[index].xyz*velocity.w;
velm[index] = velocity;
}
}
}
\ No newline at end of file
...@@ -149,16 +149,38 @@ static void findAnglesForCCMA(const System& system, vector<ReferenceCCMAAlgorith ...@@ -149,16 +149,38 @@ static void findAnglesForCCMA(const System& system, vector<ReferenceCCMAAlgorith
* Compute the kinetic energy of the system, possibly shifting the velocities in time to account * Compute the kinetic energy of the system, possibly shifting the velocities in time to account
* for a leapfrog integrator. * for a leapfrog integrator.
*/ */
static double computeShiftedKineticEnergy(ContextImpl& context, vector<double>& masses, double timeShift) { static double computeShiftedKineticEnergy(ContextImpl& context, vector<double>& masses, double timeShift, ReferenceConstraintAlgorithm* constraints) {
vector<RealVec>& posData = extractPositions(context);
vector<RealVec>& velData = extractVelocities(context); vector<RealVec>& velData = extractVelocities(context);
vector<RealVec>& forceData = extractForces(context); vector<RealVec>& forceData = extractForces(context);
double energy = 0.0; int numParticles = context.getSystem().getNumParticles();
for (int i = 0; i < (int) masses.size(); ++i) {
if (masses[i] > 0) { // Compute the shifted velocities.
RealVec v = velData[i]+forceData[i]*(timeShift/masses[i]);
energy += masses[i]*(v.dot(v)); vector<RealVec> shiftedVel(numParticles);
for (int i = 0; i < numParticles; ++i) {
if (masses[i] > 0)
shiftedVel[i] = velData[i]+forceData[i]*(timeShift/masses[i]);
else
shiftedVel[i] = velData[i];
} }
// Apply constraints to them.
if (constraints != NULL) {
vector<double> inverseMasses(numParticles);
for (int i = 0; i < numParticles; i++)
inverseMasses[i] = (masses[i] == 0 ? 0 : 1/masses[i]);
constraints->setTolerance(1e-4);
constraints->applyToVelocities(numParticles, posData, shiftedVel, inverseMasses);
} }
// Compute the kinetic energy.
double energy = 0.0;
for (int i = 0; i < numParticles; ++i)
if (masses[i] > 0)
energy += masses[i]*(shiftedVel[i].dot(shiftedVel[i]));
return 0.5*energy; return 0.5*energy;
} }
...@@ -1707,7 +1729,7 @@ void ReferenceIntegrateVerletStepKernel::execute(ContextImpl& context, const Ver ...@@ -1707,7 +1729,7 @@ void ReferenceIntegrateVerletStepKernel::execute(ContextImpl& context, const Ver
} }
double ReferenceIntegrateVerletStepKernel::computeKineticEnergy(ContextImpl& context, const VerletIntegrator& integrator) { double ReferenceIntegrateVerletStepKernel::computeKineticEnergy(ContextImpl& context, const VerletIntegrator& integrator) {
return computeShiftedKineticEnergy(context, masses, 0.5*integrator.getStepSize()); return computeShiftedKineticEnergy(context, masses, 0.5*integrator.getStepSize(), constraints);
} }
ReferenceIntegrateLangevinStepKernel::~ReferenceIntegrateLangevinStepKernel() { ReferenceIntegrateLangevinStepKernel::~ReferenceIntegrateLangevinStepKernel() {
...@@ -1775,7 +1797,7 @@ void ReferenceIntegrateLangevinStepKernel::execute(ContextImpl& context, const L ...@@ -1775,7 +1797,7 @@ void ReferenceIntegrateLangevinStepKernel::execute(ContextImpl& context, const L
} }
double ReferenceIntegrateLangevinStepKernel::computeKineticEnergy(ContextImpl& context, const LangevinIntegrator& integrator) { double ReferenceIntegrateLangevinStepKernel::computeKineticEnergy(ContextImpl& context, const LangevinIntegrator& integrator) {
return computeShiftedKineticEnergy(context, masses, 0.5*integrator.getStepSize()); return computeShiftedKineticEnergy(context, masses, 0.5*integrator.getStepSize(), constraints);
} }
ReferenceIntegrateBrownianStepKernel::~ReferenceIntegrateBrownianStepKernel() { ReferenceIntegrateBrownianStepKernel::~ReferenceIntegrateBrownianStepKernel() {
...@@ -1842,7 +1864,7 @@ void ReferenceIntegrateBrownianStepKernel::execute(ContextImpl& context, const B ...@@ -1842,7 +1864,7 @@ void ReferenceIntegrateBrownianStepKernel::execute(ContextImpl& context, const B
} }
double ReferenceIntegrateBrownianStepKernel::computeKineticEnergy(ContextImpl& context, const BrownianIntegrator& integrator) { double ReferenceIntegrateBrownianStepKernel::computeKineticEnergy(ContextImpl& context, const BrownianIntegrator& integrator) {
return computeShiftedKineticEnergy(context, masses, 0); return computeShiftedKineticEnergy(context, masses, 0, constraints);
} }
ReferenceIntegrateVariableLangevinStepKernel::~ReferenceIntegrateVariableLangevinStepKernel() { ReferenceIntegrateVariableLangevinStepKernel::~ReferenceIntegrateVariableLangevinStepKernel() {
...@@ -1910,7 +1932,7 @@ double ReferenceIntegrateVariableLangevinStepKernel::execute(ContextImpl& contex ...@@ -1910,7 +1932,7 @@ double ReferenceIntegrateVariableLangevinStepKernel::execute(ContextImpl& contex
} }
double ReferenceIntegrateVariableLangevinStepKernel::computeKineticEnergy(ContextImpl& context, const VariableLangevinIntegrator& integrator) { double ReferenceIntegrateVariableLangevinStepKernel::computeKineticEnergy(ContextImpl& context, const VariableLangevinIntegrator& integrator) {
return computeShiftedKineticEnergy(context, masses, 0.5*integrator.getStepSize()); return computeShiftedKineticEnergy(context, masses, 0.5*integrator.getStepSize(), constraints);
} }
ReferenceIntegrateVariableVerletStepKernel::~ReferenceIntegrateVariableVerletStepKernel() { ReferenceIntegrateVariableVerletStepKernel::~ReferenceIntegrateVariableVerletStepKernel() {
...@@ -1972,7 +1994,7 @@ double ReferenceIntegrateVariableVerletStepKernel::execute(ContextImpl& context, ...@@ -1972,7 +1994,7 @@ double ReferenceIntegrateVariableVerletStepKernel::execute(ContextImpl& context,
} }
double ReferenceIntegrateVariableVerletStepKernel::computeKineticEnergy(ContextImpl& context, const VariableVerletIntegrator& integrator) { double ReferenceIntegrateVariableVerletStepKernel::computeKineticEnergy(ContextImpl& context, const VariableVerletIntegrator& integrator) {
return computeShiftedKineticEnergy(context, masses, 0.5*integrator.getStepSize()); return computeShiftedKineticEnergy(context, masses, 0.5*integrator.getStepSize(), constraints);
} }
ReferenceIntegrateCustomStepKernel::~ReferenceIntegrateCustomStepKernel() { ReferenceIntegrateCustomStepKernel::~ReferenceIntegrateCustomStepKernel() {
......
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