void CudaIntegrateRPMDStepKernel::computeForces(ContextImpl& context) { // Compute forces from all groups that didn't have a specified contraction. for (int i = 0; i < numCopies; i++) { void* copyToContextArgs[] = {&velocities->getDevicePointer(), &cu.getVelm().getDevicePointer(), &positions->getDevicePointer(), &cu.getPosq().getDevicePointer(), &cu.getAtomIndexArray().getDevicePointer(), &i}; cu.executeKernel(copyToContextKernel, copyToContextArgs, cu.getNumAtoms()); context.computeVirtualSites(); Vec3 initialBox[3]; context.getPeriodicBoxVectors(initialBox[0], initialBox[1], initialBox[2]); context.updateContextState(); Vec3 finalBox[3]; context.getPeriodicBoxVectors(finalBox[0], finalBox[1], finalBox[2]); if (initialBox[0] != finalBox[0] || initialBox[1] != finalBox[1] || initialBox[2] != finalBox[2]) throw OpenMMException("Standard barostats cannot be used with RPMDIntegrator. Use RPMDMonteCarloBarostat instead."); context.calcForcesAndEnergy(true, false, groupsNotContracted); void* copyFromContextArgs[] = {&cu.getForce().getDevicePointer(), &forces->getDevicePointer(), &cu.getVelm().getDevicePointer(), &velocities->getDevicePointer(), &cu.getPosq().getDevicePointer(), &positions->getDevicePointer(), &cu.getAtomIndexArray().getDevicePointer(), &i}; cu.executeKernel(copyFromContextKernel, copyFromContextArgs, cu.getNumAtoms()); } // Now loop over contractions and compute forces from them. for (map<int, int>::const_iterator iter = groupsByCopies.begin(); iter != groupsByCopies.end(); ++iter) { int copies = iter->first; int groupFlags = iter->second; // Find the contracted positions. void* contractPosArgs[] = {&positions->getDevicePointer(), &contractedPositions->getDevicePointer()}; cu.executeKernel(positionContractionKernels[copies], contractPosArgs, numParticles*numCopies, workgroupSize); // Compute forces. for (int i = 0; i < copies; i++) { void* copyToContextArgs[] = {&velocities->getDevicePointer(), &cu.getVelm().getDevicePointer(), &contractedPositions->getDevicePointer(), &cu.getPosq().getDevicePointer(), &cu.getAtomIndexArray().getDevicePointer(), &i}; cu.executeKernel(copyToContextKernel, copyToContextArgs, cu.getNumAtoms()); context.computeVirtualSites(); context.calcForcesAndEnergy(true, false, groupFlags); void* copyFromContextArgs[] = {&cu.getForce().getDevicePointer(), &contractedForces->getDevicePointer(), &cu.getVelm().getDevicePointer(), &velocities->getDevicePointer(), &cu.getPosq().getDevicePointer(), &contractedPositions->getDevicePointer(), &cu.getAtomIndexArray().getDevicePointer(), &i}; cu.executeKernel(copyFromContextKernel, copyFromContextArgs, cu.getNumAtoms()); } // Apply the forces to the original copies. void* contractForceArgs[] = {&forces->getDevicePointer(), &contractedForces->getDevicePointer()}; cu.executeKernel(forceContractionKernels[copies], contractForceArgs, numParticles*numCopies, workgroupSize); } if (groupsByCopies.size() > 0) { // Ensure the Context contains the positions from the last copy, since we'll assume that later. int i = numCopies-1; void* copyToContextArgs[] = {&velocities->getDevicePointer(), &cu.getVelm().getDevicePointer(), &positions->getDevicePointer(), &cu.getPosq().getDevicePointer(), &cu.getAtomIndexArray().getDevicePointer(), &i}; cu.executeKernel(copyToContextKernel, copyToContextArgs, cu.getNumAtoms()); } }
void ReferenceCustomDynamics::update(ContextImpl& context, int numberOfAtoms, vector<RealVec>& atomCoordinates, vector<RealVec>& velocities, vector<RealVec>& forces, vector<RealOpenMM>& masses, map<string, RealOpenMM>& globals, vector<vector<RealVec> >& perDof, bool& forcesAreValid, RealOpenMM tolerance) { if (invalidatesForces.size() == 0) initialize(context, masses, globals); int numSteps = stepType.size(); globals.insert(context.getParameters().begin(), context.getParameters().end()); for (map<string, RealOpenMM>::const_iterator iter = globals.begin(); iter != globals.end(); ++iter) expressionSet.setVariable(expressionSet.getVariableIndex(iter->first), iter->second); oldPos = atomCoordinates; // Loop over steps and execute them. for (int step = 0; step < numSteps; ) { if ((needsForces[step] || needsEnergy[step]) && (!forcesAreValid || context.getLastForceGroups() != forceGroupFlags[step])) { // Recompute forces and/or energy. bool computeForce = needsForces[step] || computeBothForceAndEnergy[step]; bool computeEnergy = needsEnergy[step] || computeBothForceAndEnergy[step]; recordChangedParameters(context, globals); RealOpenMM e = context.calcForcesAndEnergy(computeForce, computeEnergy, forceGroupFlags[step]); if (computeEnergy) { energy = e; context.getEnergyParameterDerivatives(energyParamDerivs); } forcesAreValid = true; } // Execute the step. int nextStep = step+1; switch (stepType[step]) { case CustomIntegrator::ComputeGlobal: { uniform = SimTKOpenMMUtilities::getUniformlyDistributedRandomNumber(); gaussian = SimTKOpenMMUtilities::getNormallyDistributedRandomNumber(); RealOpenMM result = stepExpressions[step][0].evaluate(); globals[stepVariable[step]] = result; expressionSet.setVariable(stepVariableIndex[step], result); break; } case CustomIntegrator::ComputePerDof: { vector<RealVec>* results = NULL; if (stepVariableIndex[step] == xIndex) results = &atomCoordinates; else if (stepVariableIndex[step] == vIndex) results = &velocities; else { for (int j = 0; j < integrator.getNumPerDofVariables(); j++) if (stepVariableIndex[step] == perDofVariableIndex[j]) results = &perDof[j]; } if (results == NULL) throw OpenMMException("Illegal per-DOF output variable: "+stepVariable[step]); computePerDof(numberOfAtoms, *results, atomCoordinates, velocities, forces, masses, perDof, stepExpressions[step][0]); break; } case CustomIntegrator::ComputeSum: { computePerDof(numberOfAtoms, sumBuffer, atomCoordinates, velocities, forces, masses, perDof, stepExpressions[step][0]); RealOpenMM sum = 0.0; for (int j = 0; j < numberOfAtoms; j++) if (masses[j] != 0.0) sum += sumBuffer[j][0]+sumBuffer[j][1]+sumBuffer[j][2]; globals[stepVariable[step]] = sum; expressionSet.setVariable(stepVariableIndex[step], sum); break; } case CustomIntegrator::ConstrainPositions: { getReferenceConstraintAlgorithm()->apply(oldPos, atomCoordinates, inverseMasses, tolerance); oldPos = atomCoordinates; break; } case CustomIntegrator::ConstrainVelocities: { getReferenceConstraintAlgorithm()->applyToVelocities(oldPos, velocities, inverseMasses, tolerance); break; } case CustomIntegrator::UpdateContextState: { recordChangedParameters(context, globals); context.updateContextState(); globals.insert(context.getParameters().begin(), context.getParameters().end()); for (map<string, RealOpenMM>::const_iterator iter = globals.begin(); iter != globals.end(); ++iter) expressionSet.setVariable(expressionSet.getVariableIndex(iter->first), iter->second); break; } case CustomIntegrator::IfBlockStart: { if (!evaluateCondition(step)) nextStep = blockEnd[step]+1; break; } case CustomIntegrator::WhileBlockStart: { if (!evaluateCondition(step)) nextStep = blockEnd[step]+1; break; } case CustomIntegrator::BlockEnd: { if (blockEnd[step] != -1) nextStep = blockEnd[step]; // Return to the start of a while block. break; } } if (invalidatesForces[step]) forcesAreValid = false; step = nextStep; } ReferenceVirtualSites::computePositions(context.getSystem(), atomCoordinates); incrementTimeStep(); recordChangedParameters(context, globals); }
void ReferenceCustomDynamics::update(ContextImpl& context, int numberOfAtoms, vector<RealVec>& atomCoordinates, vector<RealVec>& velocities, vector<RealVec>& forces, vector<RealOpenMM>& masses, map<string, RealOpenMM>& globals, vector<vector<RealVec> >& perDof, bool& forcesAreValid, RealOpenMM tolerance) { int numSteps = stepType.size(); globals.insert(context.getParameters().begin(), context.getParameters().end()); oldPos = atomCoordinates; if (invalidatesForces.size() == 0) { // Some initialization can't be done in the constructor, since we need a ContextImpl from which to get the list of // Context parameters. Instead, we do it the first time this method is called. vector<int> forceGroup; vector<vector<Lepton::ParsedExpression> > expressions; CustomIntegratorUtilities::analyzeComputations(context, integrator, expressions, comparisons, blockEnd, invalidatesForces, needsForces, needsEnergy, computeBothForceAndEnergy, forceGroup); stepExpressions.resize(expressions.size()); for (int i = 0; i < numSteps; i++) { for (int j = 0; j < (int) expressions[i].size(); j++) stepExpressions[i].push_back(expressions[i][j].createProgram()); if (stepType[i] == CustomIntegrator::BeginWhileBlock) blockEnd[blockEnd[i]] = i; // Record where to branch back to. } // Record the variable names and flags for the force and energy in each step. forceGroupFlags.resize(numSteps, -1); forceName.resize(numSteps, "f"); energyName.resize(numSteps, "energy"); vector<string> forceGroupName; vector<string> energyGroupName; for (int i = 0; i < 32; i++) { stringstream fname; fname << "f" << i; forceGroupName.push_back(fname.str()); stringstream ename; ename << "energy" << i; energyGroupName.push_back(ename.str()); } for (int i = 0; i < numSteps; i++) { if (needsForces[i] && forceGroup[i] > -1) forceName[i] = forceGroupName[forceGroup[i]]; if (needsEnergy[i] && forceGroup[i] > -1) energyName[i] = energyGroupName[forceGroup[i]]; if (forceGroup[i] > -1) forceGroupFlags[i] = 1<<forceGroup[i]; } // Build the list of inverse masses. inverseMasses.resize(numberOfAtoms); for (int i = 0; i < numberOfAtoms; i++) { if (masses[i] == 0.0) inverseMasses[i] = 0.0; else inverseMasses[i] = 1.0/masses[i]; } } // Loop over steps and execute them. for (int step = 0; step < numSteps; ) { if ((needsForces[step] || needsEnergy[step]) && (!forcesAreValid || context.getLastForceGroups() != forceGroupFlags[step])) { // Recompute forces and/or energy. bool computeForce = needsForces[step] || computeBothForceAndEnergy[step]; bool computeEnergy = needsEnergy[step] || computeBothForceAndEnergy[step]; recordChangedParameters(context, globals); RealOpenMM e = context.calcForcesAndEnergy(computeForce, computeEnergy, forceGroupFlags[step]); if (computeEnergy) energy = e; forcesAreValid = true; } globals[energyName[step]] = energy; // Execute the step. int nextStep = step+1; switch (stepType[step]) { case CustomIntegrator::ComputeGlobal: { map<string, RealOpenMM> variables = globals; variables["uniform"] = SimTKOpenMMUtilities::getUniformlyDistributedRandomNumber(); variables["gaussian"] = SimTKOpenMMUtilities::getNormallyDistributedRandomNumber(); globals[stepVariable[step]] = stepExpressions[step][0].evaluate(variables); break; } case CustomIntegrator::ComputePerDof: { vector<RealVec>* results = NULL; if (stepVariable[step] == "x") results = &atomCoordinates; else if (stepVariable[step] == "v") results = &velocities; else { for (int j = 0; j < integrator.getNumPerDofVariables(); j++) if (stepVariable[step] == integrator.getPerDofVariableName(j)) results = &perDof[j]; } if (results == NULL) throw OpenMMException("Illegal per-DOF output variable: "+stepVariable[step]); computePerDof(numberOfAtoms, *results, atomCoordinates, velocities, forces, masses, globals, perDof, stepExpressions[step][0], forceName[step]); break; } case CustomIntegrator::ComputeSum: { computePerDof(numberOfAtoms, sumBuffer, atomCoordinates, velocities, forces, masses, globals, perDof, stepExpressions[step][0], forceName[step]); RealOpenMM sum = 0.0; for (int j = 0; j < numberOfAtoms; j++) if (masses[j] != 0.0) sum += sumBuffer[j][0]+sumBuffer[j][1]+sumBuffer[j][2]; globals[stepVariable[step]] = sum; break; } case CustomIntegrator::ConstrainPositions: { getReferenceConstraintAlgorithm()->apply(oldPos, atomCoordinates, inverseMasses, tolerance); oldPos = atomCoordinates; break; } case CustomIntegrator::ConstrainVelocities: { getReferenceConstraintAlgorithm()->applyToVelocities(oldPos, velocities, inverseMasses, tolerance); break; } case CustomIntegrator::UpdateContextState: { recordChangedParameters(context, globals); context.updateContextState(); globals.insert(context.getParameters().begin(), context.getParameters().end()); break; } case CustomIntegrator::BeginIfBlock: { if (!evaluateCondition(step, globals)) nextStep = blockEnd[step]+1; break; } case CustomIntegrator::BeginWhileBlock: { if (!evaluateCondition(step, globals)) nextStep = blockEnd[step]+1; break; } case CustomIntegrator::EndBlock: { if (blockEnd[step] != -1) nextStep = blockEnd[step]; // Return to the start of a while block. break; } } if (invalidatesForces[step]) forcesAreValid = false; step = nextStep; } ReferenceVirtualSites::computePositions(context.getSystem(), atomCoordinates); incrementTimeStep(); recordChangedParameters(context, globals); }
void ReferenceCustomDynamics::update(ContextImpl& context, int numberOfAtoms, vector<RealVec>& atomCoordinates, vector<RealVec>& velocities, vector<RealVec>& forces, vector<RealOpenMM>& masses, map<string, RealOpenMM>& globals, vector<vector<RealVec> >& perDof, bool& forcesAreValid, RealOpenMM tolerance){ int numSteps = stepType.size(); globals.insert(context.getParameters().begin(), context.getParameters().end()); oldPos = atomCoordinates; if (invalidatesForces.size() == 0) { // The first time this is called, work out when to recompute forces and energy. First build a // list of every step that invalidates the forces. invalidatesForces.resize(numSteps, false); needsForces.resize(numSteps, false); needsEnergy.resize(numSteps, false); forceGroup.resize(numSteps, -2); forceName.resize(numSteps, "f"); energyName.resize(numSteps, "energy"); set<string> affectsForce; affectsForce.insert("x"); for (vector<ForceImpl*>::const_iterator iter = context.getForceImpls().begin(); iter != context.getForceImpls().end(); ++iter) { const map<string, double> params = (*iter)->getDefaultParameters(); for (map<string, double>::const_iterator param = params.begin(); param != params.end(); ++param) affectsForce.insert(param->first); } for (int i = 0; i < numSteps; i++) invalidatesForces[i] = (stepType[i] == CustomIntegrator::ConstrainPositions || affectsForce.find(stepVariable[i]) != affectsForce.end()); // Make a list of which steps require valid forces or energy to be known. vector<string> forceGroupName; vector<string> energyGroupName; for (int i = 0; i < 32; i++) { stringstream fname; fname << "f" << i; forceGroupName.push_back(fname.str()); stringstream ename; ename << "energy" << i; energyGroupName.push_back(ename.str()); } for (int i = 0; i < numSteps; i++) { if (stepType[i] == CustomIntegrator::ComputeGlobal || stepType[i] == CustomIntegrator::ComputePerDof || stepType[i] == CustomIntegrator::ComputeSum) { for (int j = 0; j < stepExpression[i].getNumOperations(); j++) { const Lepton::Operation& op = stepExpression[i].getOperation(j); if (op.getId() == Lepton::Operation::VARIABLE) { if (op.getName() == "energy") { if (forceGroup[i] != -2) throw OpenMMException("A single computation step cannot depend on multiple force groups"); needsEnergy[i] = true; forceGroup[i] = -1; } else if (op.getName().substr(0, 6) == "energy") { for (int k = 0; k < (int) energyGroupName.size(); k++) if (op.getName() == energyGroupName[k]) { if (forceGroup[i] != -2) throw OpenMMException("A single computation step cannot depend on multiple force groups"); needsForces[i] = true; forceGroup[i] = 1<<k; energyName[i] = energyGroupName[k]; break; } } else if (op.getName() == "f") { if (forceGroup[i] != -2) throw OpenMMException("A single computation step cannot depend on multiple force groups"); needsForces[i] = true; forceGroup[i] = -1; } else if (op.getName()[0] == 'f') { for (int k = 0; k < (int) forceGroupName.size(); k++) if (op.getName() == forceGroupName[k]) { if (forceGroup[i] != -2) throw OpenMMException("A single computation step cannot depend on multiple force groups"); needsForces[i] = true; forceGroup[i] = 1<<k; forceName[i] = forceGroupName[k]; break; } } } } } } // Build the list of inverse masses. inverseMasses.resize(numberOfAtoms); for (int i = 0; i < numberOfAtoms; i++) { if (masses[i] == 0.0) inverseMasses[i] = 0.0; else inverseMasses[i] = 1.0/masses[i]; } } // Loop over steps and execute them. for (int i = 0; i < numSteps; i++) { if ((needsForces[i] || needsEnergy[i]) && (!forcesAreValid || context.getLastForceGroups() != forceGroup[i])) { // Recompute forces and or energy. Figure out what is actually needed // between now and the next time they get invalidated again. bool computeForce = false, computeEnergy = false; for (int j = i; ; j++) { if (needsForces[j]) computeForce = true; if (needsEnergy[j]) computeEnergy = true; if (invalidatesForces[j]) break; if (j == numSteps-1) j = -1; if (j == i-1) break; } recordChangedParameters(context, globals); RealOpenMM e = context.calcForcesAndEnergy(computeForce, computeEnergy, forceGroup[i]); if (computeEnergy) energy = e; forcesAreValid = true; } globals[energyName[i]] = energy; // Execute the step. switch (stepType[i]) { case CustomIntegrator::ComputeGlobal: { map<string, RealOpenMM> variables = globals; variables["uniform"] = SimTKOpenMMUtilities::getUniformlyDistributedRandomNumber(); variables["gaussian"] = SimTKOpenMMUtilities::getNormallyDistributedRandomNumber(); globals[stepVariable[i]] = stepExpression[i].evaluate(variables); break; } case CustomIntegrator::ComputePerDof: { vector<RealVec>* results = NULL; if (stepVariable[i] == "x") results = &atomCoordinates; else if (stepVariable[i] == "v") results = &velocities; else { for (int j = 0; j < integrator.getNumPerDofVariables(); j++) if (stepVariable[i] == integrator.getPerDofVariableName(j)) results = &perDof[j]; } if (results == NULL) throw OpenMMException("Illegal per-DOF output variable: "+stepVariable[i]); computePerDof(numberOfAtoms, *results, atomCoordinates, velocities, forces, masses, globals, perDof, stepExpression[i], forceName[i]); break; } case CustomIntegrator::ComputeSum: { computePerDof(numberOfAtoms, sumBuffer, atomCoordinates, velocities, forces, masses, globals, perDof, stepExpression[i], forceName[i]); RealOpenMM sum = 0.0; for (int j = 0; j < numberOfAtoms; j++) if (masses[j] != 0.0) sum += sumBuffer[j][0]+sumBuffer[j][1]+sumBuffer[j][2]; globals[stepVariable[i]] = sum; break; } case CustomIntegrator::ConstrainPositions: { getReferenceConstraintAlgorithm()->apply(oldPos, atomCoordinates, inverseMasses, tolerance); oldPos = atomCoordinates; break; } case CustomIntegrator::ConstrainVelocities: { getReferenceConstraintAlgorithm()->applyToVelocities(oldPos, velocities, inverseMasses, tolerance); break; } case CustomIntegrator::UpdateContextState: { recordChangedParameters(context, globals); context.updateContextState(); globals.insert(context.getParameters().begin(), context.getParameters().end()); } } if (invalidatesForces[i]) forcesAreValid = false; } ReferenceVirtualSites::computePositions(context.getSystem(), atomCoordinates); incrementTimeStep(); recordChangedParameters(context, globals); }
void ReferenceIntegrateRPMDStepKernel::computeForces(ContextImpl& context, const RPMDIntegrator& integrator) { const int totalCopies = positions.size(); const int numParticles = positions[0].size(); vector<RealVec>& pos = extractPositions(context); vector<RealVec>& vel = extractVelocities(context); vector<RealVec>& f = extractForces(context); // Compute forces from all groups that didn't have a specified contraction. for (int i = 0; i < totalCopies; i++) { pos = positions[i]; vel = velocities[i]; context.computeVirtualSites(); Vec3 initialBox[3]; context.getPeriodicBoxVectors(initialBox[0], initialBox[1], initialBox[2]); context.updateContextState(); Vec3 finalBox[3]; context.getPeriodicBoxVectors(finalBox[0], finalBox[1], finalBox[2]); if (initialBox[0] != finalBox[0] || initialBox[1] != finalBox[1] || initialBox[2] != finalBox[2]) { // A barostat was applied during updateContextState(). Adjust the particle positions in all the // other copies to match this one. for (int j = 0; j < numParticles; j++) { Vec3 delta = pos[j]-positions[i][j]; for (int k = 0; k < totalCopies; k++) if (k != i) positions[k][j] += delta; } } positions[i] = pos; velocities[i] = vel; context.calcForcesAndEnergy(true, false, groupsNotContracted); forces[i] = f; } // Now loop over contractions and compute forces from them. for (map<int, int>::const_iterator iter = groupsByCopies.begin(); iter != groupsByCopies.end(); ++iter) { int copies = iter->first; int groupFlags = iter->second; fftpack* shortFFT = contractionFFT[copies]; // Find the contracted positions. vector<t_complex> q(totalCopies); const RealOpenMM scale1 = 1.0/totalCopies; for (int particle = 0; particle < numParticles; particle++) { for (int component = 0; component < 3; component++) { // Transform to the frequency domain, set high frequency components to zero, and transform back. for (int k = 0; k < totalCopies; k++) q[k] = t_complex(positions[k][particle][component], 0.0); fftpack_exec_1d(fft, FFTPACK_FORWARD, &q[0], &q[0]); if (copies > 1) { int start = (copies+1)/2; int end = totalCopies-copies+start; for (int k = end; k < totalCopies; k++) q[k-(totalCopies-copies)] = q[k]; fftpack_exec_1d(shortFFT, FFTPACK_BACKWARD, &q[0], &q[0]); } for (int k = 0; k < copies; k++) contractedPositions[k][particle][component] = scale1*q[k].re; } } // Compute forces. for (int i = 0; i < copies; i++) { pos = contractedPositions[i]; context.computeVirtualSites(); context.calcForcesAndEnergy(true, false, groupFlags); contractedForces[i] = f; } // Apply the forces to the original copies. const RealOpenMM scale2 = 1.0/copies; for (int particle = 0; particle < numParticles; particle++) { for (int component = 0; component < 3; component++) { // Transform to the frequency domain, pad with zeros, and transform back. for (int k = 0; k < copies; k++) q[k] = t_complex(contractedForces[k][particle][component], 0.0); if (copies > 1) fftpack_exec_1d(shortFFT, FFTPACK_FORWARD, &q[0], &q[0]); int start = (copies+1)/2; int end = totalCopies-copies+start; for (int k = end; k < totalCopies; k++) q[k] = q[k-(totalCopies-copies)]; for (int k = start; k < end; k++) q[k] = t_complex(0, 0); fftpack_exec_1d(fft, FFTPACK_BACKWARD, &q[0], &q[0]); for (int k = 0; k < totalCopies; k++) forces[k][particle][component] += scale2*q[k].re; } } } }