Commit a4d327f5 authored by peastman's avatar peastman
Browse files

Merge pull request #1480 from peastman/neighborlist

CPU platform shares neighbor list between forces
parents 61d15a2a 57812e24
......@@ -9,7 +9,7 @@
* Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org. *
* *
* Portions copyright (c) 2013 Stanford University and the Authors. *
* Portions copyright (c) 2013-2016 Stanford University and the Authors. *
* Authors: Peter Eastman *
* Contributors: *
* *
......@@ -90,6 +90,7 @@ public:
private:
CpuPlatform::PlatformData& data;
Kernel referenceKernel;
std::vector<RealVec> lastPositions;
};
/**
......@@ -267,9 +268,7 @@ private:
bool useSwitchingFunction, useOptimizedPme, hasInitializedPme;
std::vector<std::set<int> > exclusions;
std::vector<std::pair<float, float> > particleParams;
std::vector<RealVec> lastPositions;
NonbondedMethod nonbondedMethod;
CpuNeighborList* neighborList;
CpuNonbondedForce* nonbonded;
Kernel optimizedPme;
CpuBondForce bondForce;
......@@ -317,7 +316,6 @@ private:
std::vector<std::string> parameterNames, globalParameterNames;
std::vector<std::pair<std::set<int>, std::set<int> > > interactionGroups;
NonbondedMethod nonbondedMethod;
CpuNeighborList* neighborList;
CpuCustomNonbondedForce* nonbonded;
};
......@@ -403,7 +401,6 @@ private:
std::vector<OpenMM::CustomGBForce::ComputationType> valueTypes;
std::vector<OpenMM::CustomGBForce::ComputationType> energyTypes;
NonbondedMethod nonbondedMethod;
CpuNeighborList* neighborList;
};
/**
......
......@@ -9,7 +9,7 @@
* Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org. *
* *
* Portions copyright (c) 2013-2015 Stanford University and the Authors. *
* Portions copyright (c) 2013-2016 Stanford University and the Authors. *
* Authors: Peter Eastman *
* Contributors: *
* *
......@@ -51,6 +51,7 @@ public:
void computeNeighborList(int numAtoms, const AlignedArray<float>& atomLocations, const std::vector<std::set<int> >& exclusions,
const RealVec* periodicBoxVectors, bool usePeriodic, float maxDistance, ThreadPool& threads);
int getNumBlocks() const;
int getBlockSize() const;
const std::vector<int>& getSortedAtoms() const;
const std::vector<int>& getBlockNeighbors(int blockIndex) const;
const std::vector<char>& getBlockExclusions(int blockIndex) const;
......
......@@ -34,6 +34,7 @@
#include "AlignedArray.h"
#include "CpuRandom.h"
#include "CpuNeighborList.h"
#include "ReferencePlatform.h"
#include "openmm/internal/ContextImpl.h"
#include "openmm/internal/ThreadPool.h"
......@@ -80,12 +81,18 @@ private:
class CpuPlatform::PlatformData {
public:
PlatformData(int numParticles, int numThreads);
~PlatformData();
void requestNeighborList(double cutoffDistance, double padding, bool useExclusions, std::vector<std::set<int> >& exclusionList);
AlignedArray<float> posq;
std::vector<AlignedArray<float> > threadForce;
ThreadPool threads;
bool isPeriodic;
CpuRandom random;
std::map<std::string, std::string> propertyValues;
CpuNeighborList* neighborList;
double cutoff, paddedCutoff;
bool anyExclusions;
std::vector<std::set<int> > exclusions;
};
} // namespace OpenMM
......
/* Portions copyright (c) 2009-2014 Stanford University and Simbios.
/* Portions copyright (c) 2009-2016 Stanford University and Simbios.
* Contributors: Peter Eastman
*
* Permission is hereby granted, free of charge, to any person obtaining
......@@ -294,12 +294,13 @@ void CpuCustomGBForce::calculateParticlePairValue(int index, ThreadData& data, i
int blockIndex = gmx_atomic_fetch_add(reinterpret_cast<gmx_atomic_t*>(atomicCounter), 1);
if (blockIndex >= neighborList->getNumBlocks())
break;
const int* blockAtom = &neighborList->getSortedAtoms()[4*blockIndex];
const int blockSize = neighborList->getBlockSize();
const int* blockAtom = &neighborList->getSortedAtoms()[blockSize*blockIndex];
const vector<int>& neighbors = neighborList->getBlockNeighbors(blockIndex);
const vector<char>& blockExclusions = neighborList->getBlockExclusions(blockIndex);
for (int i = 0; i < (int) neighbors.size(); i++) {
int first = neighbors[i];
for (int k = 0; k < 4; k++) {
for (int k = 0; k < blockSize; k++) {
if ((blockExclusions[i] & (1<<k)) == 0) {
int second = blockAtom[k];
if (useExclusions && exclusions[first].find(second) != exclusions[first].end())
......@@ -379,12 +380,13 @@ void CpuCustomGBForce::calculateParticlePairEnergyTerm(int index, ThreadData& da
int blockIndex = gmx_atomic_fetch_add(reinterpret_cast<gmx_atomic_t*>(atomicCounter), 1);
if (blockIndex >= neighborList->getNumBlocks())
break;
const int* blockAtom = &neighborList->getSortedAtoms()[4*blockIndex];
const int blockSize = neighborList->getBlockSize();
const int* blockAtom = &neighborList->getSortedAtoms()[blockSize*blockIndex];
const vector<int>& neighbors = neighborList->getBlockNeighbors(blockIndex);
const vector<char>& blockExclusions = neighborList->getBlockExclusions(blockIndex);
for (int i = 0; i < (int) neighbors.size(); i++) {
int first = neighbors[i];
for (int k = 0; k < 4; k++) {
for (int k = 0; k < blockSize; k++) {
if ((blockExclusions[i] & (1<<k)) == 0) {
int second = blockAtom[k];
if (useExclusions && exclusions[first].find(second) != exclusions[first].end())
......@@ -460,12 +462,13 @@ void CpuCustomGBForce::calculateChainRuleForces(ThreadData& data, int numAtoms,
int blockIndex = gmx_atomic_fetch_add(reinterpret_cast<gmx_atomic_t*>(atomicCounter), 1);
if (blockIndex >= neighborList->getNumBlocks())
break;
const int* blockAtom = &neighborList->getSortedAtoms()[4*blockIndex];
const int blockSize = neighborList->getBlockSize();
const int* blockAtom = &neighborList->getSortedAtoms()[blockSize*blockIndex];
const vector<int>& neighbors = neighborList->getBlockNeighbors(blockIndex);
const vector<char>& blockExclusions = neighborList->getBlockExclusions(blockIndex);
for (int i = 0; i < (int) neighbors.size(); i++) {
int first = neighbors[i];
for (int k = 0; k < 4; k++) {
for (int k = 0; k < blockSize; k++) {
if ((blockExclusions[i] & (1<<k)) == 0) {
int second = blockAtom[k];
bool isExcluded = (exclusions[first].find(second) != exclusions[first].end());
......
/* Portions copyright (c) 2009-2014 Stanford University and Simbios.
/* Portions copyright (c) 2009-2016 Stanford University and Simbios.
* Contributors: Peter Eastman
*
* Permission is hereby granted, free of charge, to any person obtaining
......@@ -190,7 +190,8 @@ void CpuCustomNonbondedForce::threadComputeForce(ThreadPool& threads, int thread
int blockIndex = gmx_atomic_fetch_add(reinterpret_cast<gmx_atomic_t*>(atomicCounter), 1);
if (blockIndex >= neighborList->getNumBlocks())
break;
const int* blockAtom = &neighborList->getSortedAtoms()[4*blockIndex];
const int blockSize = neighborList->getBlockSize();
const int* blockAtom = &neighborList->getSortedAtoms()[blockSize*blockIndex];
const vector<int>& neighbors = neighborList->getBlockNeighbors(blockIndex);
const vector<char>& exclusions = neighborList->getBlockExclusions(blockIndex);
for (int i = 0; i < (int) neighbors.size(); i++) {
......@@ -199,7 +200,7 @@ void CpuCustomNonbondedForce::threadComputeForce(ThreadPool& threads, int thread
ReferenceForce::setVariable(data.energyParticleParams[j*2], atomParameters[first][j]);
ReferenceForce::setVariable(data.forceParticleParams[j*2], atomParameters[first][j]);
}
for (int k = 0; k < 4; k++) {
for (int k = 0; k < blockSize; k++) {
if ((exclusions[i] & (1<<k)) == 0) {
int second = blockAtom[k];
for (int j = 0; j < (int) paramNames.size(); j++) {
......
......@@ -6,7 +6,7 @@
* Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org. *
* *
* Portions copyright (c) 2013-2015 Stanford University and the Authors. *
* Portions copyright (c) 2013-2016 Stanford University and the Authors. *
* Authors: Peter Eastman *
* Contributors: *
* *
......@@ -230,6 +230,7 @@ CpuCalcForcesAndEnergyKernel::CpuCalcForcesAndEnergyKernel(std::string name, con
void CpuCalcForcesAndEnergyKernel::initialize(const System& system) {
referenceKernel.getAs<ReferenceCalcForcesAndEnergyKernel>().initialize(system);
lastPositions.resize(system.getNumParticles(), Vec3(1e10, 1e10, 1e10));
}
void CpuCalcForcesAndEnergyKernel::beginComputation(ContextImpl& context, bool includeForce, bool includeEnergy, int groups) {
......@@ -237,11 +238,60 @@ void CpuCalcForcesAndEnergyKernel::beginComputation(ContextImpl& context, bool i
// Convert positions to single precision and clear the forces.
InitForceTask task(context.getSystem().getNumParticles(), context, data);
int numParticles = context.getSystem().getNumParticles();
InitForceTask task(numParticles, context, data);
data.threads.execute(task);
data.threads.waitForThreads();
if (!task.positionsValid)
throw OpenMMException("Particle coordinate is nan");
// Determine whether we need to recompute the neighbor list.
if (data.neighborList != NULL) {
double padding = data.paddedCutoff-data.cutoff;;
bool needRecompute = false;
double closeCutoff2 = 0.25*padding*padding;
double farCutoff2 = 0.5*padding*padding;
int maxNumMoved = numParticles/10;
vector<int> moved;
vector<RealVec>& posData = extractPositions(context);
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 = data.cutoff*data.cutoff;
double paddedCutoff2 = data.paddedCutoff*data.paddedCutoff;
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) {
data.neighborList->computeNeighborList(numParticles, data.posq, data.exclusions, extractBoxVectors(context), data.isPeriodic, data.paddedCutoff, data.threads);
lastPositions = posData;
}
}
}
double CpuCalcForcesAndEnergyKernel::finishComputation(ContextImpl& context, bool includeForce, bool includeEnergy, int groups, bool& valid) {
......@@ -473,15 +523,11 @@ CpuNonbondedForce* createCpuNonbondedForceVec4();
CpuNonbondedForce* createCpuNonbondedForceVec8();
CpuCalcNonbondedForceKernel::CpuCalcNonbondedForceKernel(string name, const Platform& platform, CpuPlatform::PlatformData& data) : CalcNonbondedForceKernel(name, platform),
data(data), bonded14IndexArray(NULL), bonded14ParamArray(NULL), hasInitializedPme(false), neighborList(NULL), nonbonded(NULL) {
if (isVec8Supported()) {
neighborList = new CpuNeighborList(8);
data(data), bonded14IndexArray(NULL), bonded14ParamArray(NULL), hasInitializedPme(false), nonbonded(NULL) {
if (isVec8Supported())
nonbonded = createCpuNonbondedForceVec8();
}
else {
neighborList = new CpuNeighborList(4);
else
nonbonded = createCpuNonbondedForceVec4();
}
}
CpuCalcNonbondedForceKernel::~CpuCalcNonbondedForceKernel() {
......@@ -495,8 +541,6 @@ CpuCalcNonbondedForceKernel::~CpuCalcNonbondedForceKernel() {
}
if (nonbonded != NULL)
delete nonbonded;
if (neighborList != NULL)
delete neighborList;
}
void CpuCalcNonbondedForceKernel::initialize(const System& system, const NonbondedForce& force) {
......@@ -556,6 +600,7 @@ void CpuCalcNonbondedForceKernel::initialize(const System& system, const Nonbond
if (nonbondedMethod == NoCutoff)
useSwitchingFunction = false;
else {
data.requestNeighborList(nonbondedCutoff, 0.25*nonbondedCutoff, true, exclusions);
useSwitchingFunction = force.getUseSwitchingFunction();
switchingDistance = force.getSwitchingDistance();
}
......@@ -578,7 +623,6 @@ void CpuCalcNonbondedForceKernel::initialize(const System& system, const Nonbond
dispersionCoefficient = NonbondedForceImpl::calcDispersionCorrection(system, force);
else
dispersionCoefficient = 0.0;
lastPositions.resize(numParticles, Vec3(1e10, 1e10, 1e10));
data.isPeriodic = (nonbondedMethod == CutoffPeriodic || nonbondedMethod == Ewald || nonbondedMethod == PME);
}
......@@ -605,53 +649,8 @@ double CpuCalcNonbondedForceKernel::execute(ContextImpl& context, bool includeFo
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.25*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, boxVectors, data.isPeriodic, nonbondedCutoff+padding, data.threads);
lastPositions = posData;
}
nonbonded->setUseCutoff(nonbondedCutoff, *neighborList, rfDielectric);
}
if (nonbondedMethod != NoCutoff)
nonbonded->setUseCutoff(nonbondedCutoff, *data.neighborList, rfDielectric);
if (data.isPeriodic) {
RealVec* boxVectors = extractBoxVectors(context);
double minAllowedSize = 1.999999*nonbondedCutoff;
......@@ -748,7 +747,7 @@ void CpuCalcNonbondedForceKernel::getPMEParameters(double& alpha, int& nx, int&
}
CpuCalcCustomNonbondedForceKernel::CpuCalcCustomNonbondedForceKernel(string name, const Platform& platform, CpuPlatform::PlatformData& data) :
CalcCustomNonbondedForceKernel(name, platform), data(data), forceCopy(NULL), neighborList(NULL), nonbonded(NULL) {
CalcCustomNonbondedForceKernel(name, platform), data(data), forceCopy(NULL), nonbonded(NULL) {
}
CpuCalcCustomNonbondedForceKernel::~CpuCalcCustomNonbondedForceKernel() {
......@@ -757,8 +756,6 @@ CpuCalcCustomNonbondedForceKernel::~CpuCalcCustomNonbondedForceKernel() {
delete[] particleParamArray[i];
delete[] particleParamArray;
}
if (neighborList != NULL)
delete neighborList;
if (nonbonded != NULL)
delete nonbonded;
if (forceCopy != NULL)
......@@ -795,7 +792,7 @@ void CpuCalcCustomNonbondedForceKernel::initialize(const System& system, const C
if (nonbondedMethod == NoCutoff)
useSwitchingFunction = false;
else {
neighborList = new CpuNeighborList(4);
data.requestNeighborList(nonbondedCutoff, 0.25*nonbondedCutoff, true, exclusions);
useSwitchingFunction = force.getUseSwitchingFunction();
switchingDistance = force.getSwitchingDistance();
}
......@@ -861,10 +858,8 @@ double CpuCalcCustomNonbondedForceKernel::execute(ContextImpl& context, bool inc
RealVec* boxVectors = extractBoxVectors(context);
double energy = 0;
bool periodic = (nonbondedMethod == CutoffPeriodic);
if (nonbondedMethod != NoCutoff) {
neighborList->computeNeighborList(numParticles, data.posq, exclusions, boxVectors, data.isPeriodic, nonbondedCutoff, data.threads);
nonbonded->setUseCutoff(nonbondedCutoff, *neighborList);
}
if (nonbondedMethod != NoCutoff)
nonbonded->setUseCutoff(nonbondedCutoff, *data.neighborList);
if (periodic) {
double minAllowedSize = 2*nonbondedCutoff;
if (boxVectors[0][0] < minAllowedSize || boxVectors[1][1] < minAllowedSize || boxVectors[2][2] < minAllowedSize)
......@@ -972,8 +967,6 @@ CpuCalcCustomGBForceKernel::~CpuCalcCustomGBForceKernel() {
delete[] particleParamArray[i];
delete[] particleParamArray;
}
if (neighborList != NULL)
delete neighborList;
if (ixn != NULL)
delete ixn;
}
......@@ -1021,10 +1014,8 @@ void CpuCalcCustomGBForceKernel::initialize(const System& system, const CustomGB
globalParameterNames.push_back(force.getGlobalParameterName(i));
nonbondedMethod = CalcCustomGBForceKernel::NonbondedMethod(force.getNonbondedMethod());
nonbondedCutoff = (RealOpenMM) force.getCutoffDistance();
if (nonbondedMethod == NoCutoff)
neighborList = NULL;
else
neighborList = new CpuNeighborList(4);
if (nonbondedMethod != NoCutoff)
data.requestNeighborList(nonbondedCutoff, 0.25*nonbondedCutoff, true, exclusions);
// Create custom functions for the tabulated functions.
......@@ -1121,8 +1112,7 @@ double CpuCalcCustomGBForceKernel::execute(ContextImpl& context, bool includeFor
ixn->setPeriodic(extractBoxSize(context));
if (nonbondedMethod != NoCutoff) {
vector<set<int> > noExclusions(numParticles);
neighborList->computeNeighborList(numParticles, data.posq, exclusions, boxVectors, data.isPeriodic, nonbondedCutoff, data.threads);
ixn->setUseCutoff(nonbondedCutoff, *neighborList);
ixn->setUseCutoff(nonbondedCutoff, *data.neighborList);
}
map<string, double> globalParameters;
for (int i = 0; i < (int) globalParameterNames.size(); i++)
......
......@@ -508,6 +508,10 @@ int CpuNeighborList::getNumBlocks() const {
return sortedAtoms.size()/blockSize;
}
int CpuNeighborList::getBlockSize() const {
return blockSize;
}
const std::vector<int>& CpuNeighborList::getSortedAtoms() const {
return sortedAtoms;
}
......
......@@ -34,6 +34,7 @@
#include "CpuKernels.h"
#include "CpuSETTLE.h"
#include "ReferenceConstraints.h"
#include "openmm/OpenMMException.h"
#include "openmm/internal/hardware.h"
#include "openmm/internal/vectorize.h"
#include <sstream>
......@@ -135,7 +136,8 @@ const CpuPlatform::PlatformData& CpuPlatform::getPlatformData(const ContextImpl&
return *contextData[&context];
}
CpuPlatform::PlatformData::PlatformData(int numParticles, int numThreads) : posq(4*numParticles), threads(numThreads) {
CpuPlatform::PlatformData::PlatformData(int numParticles, int numThreads) : posq(4*numParticles), threads(numThreads),
neighborList(NULL), cutoff(0.0), paddedCutoff(0.0), anyExclusions(false) {
numThreads = threads.getNumThreads();
threadForce.resize(numThreads);
for (int i = 0; i < numThreads; i++)
......@@ -145,3 +147,27 @@ CpuPlatform::PlatformData::PlatformData(int numParticles, int numThreads) : posq
threadsProperty << numThreads;
propertyValues[CpuThreads()] = threadsProperty.str();
}
CpuPlatform::PlatformData::~PlatformData() {
if (neighborList != NULL)
delete neighborList;
}
bool isVec8Supported();
void CpuPlatform::PlatformData::requestNeighborList(double cutoffDistance, double padding, bool useExclusions, vector<set<int> >& exclusionList) {
if (neighborList == NULL)
neighborList = new CpuNeighborList(isVec8Supported() ? 8 : 4);
if (cutoffDistance > cutoff)
cutoff = cutoffDistance;
if (cutoffDistance+padding > paddedCutoff)
paddedCutoff = cutoffDistance+padding;
if (useExclusions) {
if (anyExclusions && exclusions != exclusionList)
throw OpenMMException("All Forces must have identical exclusions");
else {
exclusions = exclusionList;
anyExclusions = true;
}
}
}
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