double ReferenceIntegrateRPMDStepKernel::computeKineticEnergy(ContextImpl& context, const RPMDIntegrator& integrator) { const System& system = context.getSystem(); int numParticles = system.getNumParticles(); vector<RealVec>& velData = extractVelocities(context); double energy = 0.0; for (int i = 0; i < numParticles; ++i) { double mass = system.getParticleMass(i); if (mass > 0) { RealVec v = velData[i]; energy += mass*(v.dot(v)); } } return 0.5*energy; }
void ReferenceIntegrateDrudeSCFStepKernel::minimize(ContextImpl& context, double tolerance) { // Record the initial positions and determine a normalization constant for scaling the tolerance. vector<RealVec>& pos = extractPositions(context); int numDrudeParticles = drudeParticles.size(); double norm = 0.0; for (int i = 0; i < numDrudeParticles; i++) { RealVec p = pos[drudeParticles[i]]; minimizerPos[3*i] = p[0]; minimizerPos[3*i+1] = p[1]; minimizerPos[3*i+2] = p[2]; norm += p.dot(p); } norm /= numDrudeParticles; norm = (norm < 1 ? 1 : sqrt(norm)); minimizerParams.epsilon = tolerance/norm; // Perform the minimization. lbfgsfloatval_t fx; MinimizerData data(context, drudeParticles); lbfgs(numDrudeParticles*3, minimizerPos, &fx, evaluate, NULL, &data, &minimizerParams); }
double CpuCalcNonbondedForceKernel::execute(ContextImpl& context, bool includeForces, bool includeEnergy, bool includeDirect, bool includeReciprocal) { if (!hasInitializedPme) { hasInitializedPme = true; useOptimizedPme = false; if (nonbondedMethod == PME) { // If available, use the optimized PME implementation. try { optimizedPme = getPlatform().createKernel(CalcPmeReciprocalForceKernel::Name(), context); optimizedPme.getAs<CalcPmeReciprocalForceKernel>().initialize(gridSize[0], gridSize[1], gridSize[2], numParticles, ewaldAlpha); useOptimizedPme = true; } catch (OpenMMException& ex) { // The CPU PME plugin isn't available. } } } AlignedArray<float>& posq = data.posq; vector<RealVec>& posData = extractPositions(context); vector<RealVec>& forceData = extractForces(context); RealVec boxSize = extractBoxSize(context); float floatBoxSize[3] = {(float) boxSize[0], (float) boxSize[1], (float) boxSize[2]}; double energy = (includeReciprocal ? ewaldSelfEnergy : 0.0); bool ewald = (nonbondedMethod == Ewald); bool pme = (nonbondedMethod == PME); if (nonbondedMethod != NoCutoff) { // Determine whether we need to recompute the neighbor list. double padding = 0.15*nonbondedCutoff; bool needRecompute = false; double closeCutoff2 = 0.25*padding*padding; double farCutoff2 = 0.5*padding*padding; int maxNumMoved = numParticles/10; vector<int> moved; for (int i = 0; i < numParticles; i++) { RealVec delta = posData[i]-lastPositions[i]; double dist2 = delta.dot(delta); if (dist2 > closeCutoff2) { moved.push_back(i); if (dist2 > farCutoff2 || moved.size() > maxNumMoved) { needRecompute = true; break; } } } if (!needRecompute && moved.size() > 0) { // Some particles have moved further than half the padding distance. Look for pairs // that are missing from the neighbor list. int numMoved = moved.size(); double cutoff2 = nonbondedCutoff*nonbondedCutoff; double paddedCutoff2 = (nonbondedCutoff+padding)*(nonbondedCutoff+padding); for (int i = 1; i < numMoved && !needRecompute; i++) for (int j = 0; j < i; j++) { RealVec delta = posData[moved[i]]-posData[moved[j]]; if (delta.dot(delta) < cutoff2) { // These particles should interact. See if they are in the neighbor list. RealVec oldDelta = lastPositions[moved[i]]-lastPositions[moved[j]]; if (oldDelta.dot(oldDelta) > paddedCutoff2) { needRecompute = true; break; } } } } if (needRecompute) { neighborList->computeNeighborList(numParticles, posq, exclusions, floatBoxSize, data.isPeriodic, nonbondedCutoff+padding, data.threads); lastPositions = posData; } nonbonded->setUseCutoff(nonbondedCutoff, *neighborList, rfDielectric); } if (data.isPeriodic) { double minAllowedSize = 1.999999*nonbondedCutoff; if (boxSize[0] < minAllowedSize || boxSize[1] < minAllowedSize || boxSize[2] < minAllowedSize) throw OpenMMException("The periodic box size has decreased to less than twice the nonbonded cutoff."); nonbonded->setPeriodic(floatBoxSize); } if (ewald) nonbonded->setUseEwald(ewaldAlpha, kmax[0], kmax[1], kmax[2]); if (pme) nonbonded->setUsePME(ewaldAlpha, gridSize); if (useSwitchingFunction) nonbonded->setUseSwitchingFunction(switchingDistance); double nonbondedEnergy = 0; if (includeDirect) nonbonded->calculateDirectIxn(numParticles, &posq[0], posData, particleParams, exclusions, data.threadForce, includeEnergy ? &nonbondedEnergy : NULL, data.threads); if (includeReciprocal) { if (useOptimizedPme) { PmeIO io(&posq[0], &data.threadForce[0][0], numParticles); Vec3 periodicBoxSize(boxSize[0], boxSize[1], boxSize[2]); optimizedPme.getAs<CalcPmeReciprocalForceKernel>().beginComputation(io, periodicBoxSize, includeEnergy); nonbondedEnergy += optimizedPme.getAs<CalcPmeReciprocalForceKernel>().finishComputation(io); } else nonbonded->calculateReciprocalIxn(numParticles, &posq[0], posData, particleParams, exclusions, forceData, includeEnergy ? &nonbondedEnergy : NULL); } energy += nonbondedEnergy; if (includeDirect) { ReferenceBondForce refBondForce; ReferenceLJCoulomb14 nonbonded14; refBondForce.calculateForce(num14, bonded14IndexArray, posData, bonded14ParamArray, forceData, includeEnergy ? &energy : NULL, nonbonded14); if (data.isPeriodic) energy += dispersionCoefficient/(boxSize[0]*boxSize[1]*boxSize[2]); } return energy; }
double ReferenceCalcDrudeForceKernel::execute(ContextImpl& context, bool includeForces, bool includeEnergy) { vector<RealVec>& pos = extractPositions(context); vector<RealVec>& force = extractForces(context); int numParticles = particle.size(); double energy = 0; // Compute the interactions from the harmonic springs. for (int i = 0; i < numParticles; i++) { int p = particle[i]; int p1 = particle1[i]; int p2 = particle2[i]; int p3 = particle3[i]; int p4 = particle4[i]; RealOpenMM a1 = (p2 == -1 ? 1 : aniso12[i]); RealOpenMM a2 = (p3 == -1 || p4 == -1 ? 1 : aniso34[i]); RealOpenMM a3 = 3-a1-a2; RealOpenMM k3 = charge[i]*charge[i]/(polarizability[i]*a3); RealOpenMM k1 = charge[i]*charge[i]/(polarizability[i]*a1) - k3; RealOpenMM k2 = charge[i]*charge[i]/(polarizability[i]*a2) - k3; // Compute the isotropic force. RealVec delta = pos[p]-pos[p1]; RealOpenMM r2 = delta.dot(delta); energy += 0.5*k3*r2; force[p] -= delta*k3; force[p1] += delta*k3; // Compute the first anisotropic force. if (p2 != -1) { RealVec dir = pos[p1]-pos[p2]; RealOpenMM invDist = 1.0/sqrt(dir.dot(dir)); dir *= invDist; RealOpenMM rprime = dir.dot(delta); energy += 0.5*k1*rprime*rprime; RealVec f1 = dir*(k1*rprime); RealVec f2 = (delta-dir*rprime)*(k1*rprime*invDist); force[p] -= f1; force[p1] += f1-f2; force[p2] += f2; } // Compute the second anisotropic force. if (p3 != -1 && p4 != -1) { RealVec dir = pos[p3]-pos[p4]; RealOpenMM invDist = 1.0/sqrt(dir.dot(dir)); dir *= invDist; RealOpenMM rprime = dir.dot(delta); energy += 0.5*k2*rprime*rprime; RealVec f1 = dir*(k2*rprime); RealVec f2 = (delta-dir*rprime)*(k2*rprime*invDist); force[p] -= f1; force[p1] += f1; force[p3] -= f2; force[p4] += f2; } } // Compute the screened interaction between bonded dipoles. int numPairs = pair1.size(); for (int i = 0; i < numPairs; i++) { int dipole1 = pair1[i]; int dipole2 = pair2[i]; int dipole1Particles[] = {particle[dipole1], particle1[dipole1]}; int dipole2Particles[] = {particle[dipole2], particle1[dipole2]}; for (int j = 0; j < 2; j++) for (int k = 0; k < 2; k++) { int p1 = dipole1Particles[j]; int p2 = dipole2Particles[k]; RealOpenMM chargeProduct = charge[dipole1]*charge[dipole2]*(j == k ? 1 : -1); RealVec delta = pos[p1]-pos[p2]; RealOpenMM r = sqrt(delta.dot(delta)); RealOpenMM u = r*pairThole[i]/pow(polarizability[dipole1]*polarizability[dipole2], 1.0/6.0); RealOpenMM screening = 1.0 - (1.0+0.5*u)*exp(-u); energy += ONE_4PI_EPS0*chargeProduct*screening/r; RealVec f = delta*(ONE_4PI_EPS0*chargeProduct*screening/(r*r*r)); force[p1] += f; force[p2] -= f; } } return energy; }
void ReferenceIntegrateDrudeLangevinStepKernel::execute(ContextImpl& context, const DrudeLangevinIntegrator& integrator) { vector<RealVec>& pos = extractPositions(context); vector<RealVec>& vel = extractVelocities(context); vector<RealVec>& force = extractForces(context); // Update velocities of ordinary particles. const RealOpenMM vscale = exp(-integrator.getStepSize()*integrator.getFriction()); const RealOpenMM fscale = (1-vscale)/integrator.getFriction(); const RealOpenMM kT = BOLTZ*integrator.getTemperature(); const RealOpenMM noisescale = sqrt(2*kT*integrator.getFriction())*sqrt(0.5*(1-vscale*vscale)/integrator.getFriction()); for (int i = 0; i < (int) normalParticles.size(); i++) { int index = normalParticles[i]; RealOpenMM invMass = particleInvMass[index]; if (invMass != 0.0) { RealOpenMM sqrtInvMass = sqrt(invMass); for (int j = 0; j < 3; j++) vel[index][j] = vscale*vel[index][j] + fscale*invMass*force[index][j] + noisescale*sqrtInvMass*SimTKOpenMMUtilities::getNormallyDistributedRandomNumber(); } } // Update velocities of Drude particle pairs. const RealOpenMM vscaleDrude = exp(-integrator.getStepSize()*integrator.getDrudeFriction()); const RealOpenMM fscaleDrude = (1-vscaleDrude)/integrator.getDrudeFriction(); const RealOpenMM kTDrude = BOLTZ*integrator.getDrudeTemperature(); const RealOpenMM noisescaleDrude = sqrt(2*kTDrude*integrator.getDrudeFriction())*sqrt(0.5*(1-vscaleDrude*vscaleDrude)/integrator.getDrudeFriction()); for (int i = 0; i < (int) pairParticles.size(); i++) { int p1 = pairParticles[i].first; int p2 = pairParticles[i].second; RealOpenMM mass1fract = pairInvTotalMass[i]/particleInvMass[p1]; RealOpenMM mass2fract = pairInvTotalMass[i]/particleInvMass[p2]; RealOpenMM sqrtInvTotalMass = sqrt(pairInvTotalMass[i]); RealOpenMM sqrtInvReducedMass = sqrt(pairInvReducedMass[i]); RealVec cmVel = vel[p1]*mass1fract+vel[p2]*mass2fract; RealVec relVel = vel[p2]-vel[p1]; RealVec cmForce = force[p1]+force[p2]; RealVec relForce = force[p2]*mass1fract - force[p1]*mass2fract; for (int j = 0; j < 3; j++) { cmVel[j] = vscale*cmVel[j] + fscale*pairInvTotalMass[i]*cmForce[j] + noisescale*sqrtInvTotalMass*SimTKOpenMMUtilities::getNormallyDistributedRandomNumber(); relVel[j] = vscaleDrude*relVel[j] + fscaleDrude*pairInvReducedMass[i]*relForce[j] + noisescaleDrude*sqrtInvReducedMass*SimTKOpenMMUtilities::getNormallyDistributedRandomNumber(); } vel[p1] = cmVel-relVel*mass2fract; vel[p2] = cmVel+relVel*mass1fract; } // Update the particle positions. int numParticles = particleInvMass.size(); vector<RealVec> xPrime(numParticles); RealOpenMM dt = integrator.getStepSize(); for (int i = 0; i < numParticles; i++) if (particleInvMass[i] != 0.0) xPrime[i] = pos[i]+vel[i]*dt; // Apply constraints. extractConstraints(context).apply(pos, xPrime, particleInvMass, integrator.getConstraintTolerance()); // Record the constrained positions and velocities. RealOpenMM dtInv = 1.0/dt; for (int i = 0; i < numParticles; i++) { if (particleInvMass[i] != 0.0) { vel[i] = (xPrime[i]-pos[i])*dtInv; 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); data.time += integrator.getStepSize(); data.stepCount++; }