Commit 3b6925ae authored by Andy Simmonett's avatar Andy Simmonett Committed by GitHub
Browse files

Merge pull request #1 from peastman/ljpme

Cleanup to LJ PME code
parents 5a8a8aa9 f7a102fb
......@@ -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: *
* *
......@@ -37,6 +37,7 @@
#include <map>
#include <set>
#include <string>
#include <utility>
#include <vector>
#ifdef LEPTON_USE_JIT
#include "asmjit.h"
......@@ -73,6 +74,12 @@ public:
* to set the value of the variable before calling evaluate().
*/
double& getVariableReference(const std::string& name);
/**
* You can optionally specify the memory locations from which the values of variables should be read.
* This is useful, for example, when several expressions all use the same variable. You can then set
* the value of that variable in one place, and it will be seen by all of them.
*/
void setVariableLocations(std::map<std::string, double*>& variableLocations);
/**
* Evaluate the expression. The values of all variables should have been set before calling this.
*/
......@@ -82,6 +89,8 @@ private:
CompiledExpression(const ParsedExpression& expression);
void compileExpression(const ExpressionTreeNode& node, std::vector<std::pair<ExpressionTreeNode, int> >& temps);
int findTempIndex(const ExpressionTreeNode& node, std::vector<std::pair<ExpressionTreeNode, int> >& temps);
std::map<std::string, double*> variablePointers;
std::vector<std::pair<double*, double*> > variablesToCopy;
std::vector<std::vector<int> > arguments;
std::vector<int> target;
std::vector<Operation*> operation;
......
......@@ -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 Stanford University and the Authors. *
* Portions copyright (c) 2013-2016 Stanford University and the Authors. *
* Authors: Peter Eastman *
* Contributors: *
* *
......@@ -77,10 +77,7 @@ CompiledExpression& CompiledExpression::operator=(const CompiledExpression& expr
operation.resize(expression.operation.size());
for (int i = 0; i < (int) operation.size(); i++)
operation[i] = expression.operation[i]->clone();
#ifdef LEPTON_USE_JIT
if (workspace.size() > 0)
generateJitCode();
#endif
setVariableLocations(variablePointers);
return *this;
}
......@@ -138,16 +135,41 @@ const set<string>& CompiledExpression::getVariables() const {
}
double& CompiledExpression::getVariableReference(const string& name) {
map<string, double*>::iterator pointer = variablePointers.find(name);
if (pointer != variablePointers.end())
return *pointer->second;
map<string, int>::iterator index = variableIndices.find(name);
if (index == variableIndices.end())
throw Exception("getVariableReference: Unknown variable '"+name+"'");
return workspace[index->second];
}
void CompiledExpression::setVariableLocations(map<string, double*>& variableLocations) {
variablePointers = variableLocations;
#ifdef LEPTON_USE_JIT
// Rebuild the JIT code.
if (workspace.size() > 0)
generateJitCode();
#else
// Make a list of all variables we will need to copy before evaluating the expression.
variablesToCopy.clear();
for (map<string, int>::const_iterator iter = variableIndices.begin(); iter != variableIndices.end(); ++iter) {
map<string, double*>::iterator pointer = variablePointers.find(iter->first);
if (pointer != variablePointers.end())
variablesToCopy.push_back(make_pair(&workspace[iter->second], pointer->second));
}
#endif
}
double CompiledExpression::evaluate() const {
#ifdef LEPTON_USE_JIT
return ((double (*)()) jitCode)();
#else
for (int i = 0; i < variablesToCopy.size(); i++)
*variablesToCopy[i].first = *variablesToCopy[i].second;
// Loop over the operations and evaluate each one.
for (int step = 0; step < operation.size(); step++) {
......@@ -176,16 +198,16 @@ void CompiledExpression::generateJitCode() {
vector<X86XmmVar> workspaceVar(workspace.size());
for (int i = 0; i < (int) workspaceVar.size(); i++)
workspaceVar[i] = c.newXmmVar(kX86VarTypeXmmSd);
X86GpVar workspacePointer(c);
X86GpVar argsPointer(c);
c.mov(workspacePointer, imm_ptr(&workspace[0]));
c.mov(argsPointer, imm_ptr(&argValues[0]));
// Load the arguments into variables.
for (set<string>::const_iterator iter = variableNames.begin(); iter != variableNames.end(); ++iter) {
map<string, int>::iterator index = variableIndices.find(*iter);
c.movsd(workspaceVar[index->second], x86::ptr(workspacePointer, 8*index->second, 0));
X86GpVar variablePointer(c);
c.mov(variablePointer, imm_ptr(&getVariableReference(index->first)));
c.movsd(workspaceVar[index->second], x86::ptr(variablePointer, 0, 0));
}
// Make a list of all constants that will be needed for evaluation.
......
......@@ -555,7 +555,8 @@ public:
CutoffNonPeriodic = 1,
CutoffPeriodic = 2,
Ewald = 3,
PME = 4
PME = 4,
LJPME = 5
};
static std::string Name() {
return "CalcNonbondedForce";
......@@ -596,6 +597,15 @@ public:
* @param nz the number of grid points along the Z axis
*/
virtual void getPMEParameters(double& alpha, int& nx, int& ny, int& nz) const = 0;
/**
* Get the parameters being used for the dispersion terms in LJPME.
*
* @param alpha the separation parameter
* @param nx the number of grid points along the X axis
* @param ny the number of grid points along the Y axis
* @param nz the number of grid points along the Z axis
*/
virtual void getLJPMEParameters(double& alpha, int& nx, int& ny, int& nz) const = 0;
};
/**
......@@ -1335,6 +1345,57 @@ public:
};
/**
* This kernel performs the dispersion reciprocal space calculation for LJPME. In most cases, this
* calculation is done directly by CalcNonbondedForceKernel so this kernel is unneeded.
* In some cases it may want to outsource the work to a different kernel. In particular,
* GPU based platforms sometimes use a CPU based implementation provided by a separate
* plugin.
*/
class CalcDispersionPmeReciprocalForceKernel : public KernelImpl {
public:
class IO;
static std::string Name() {
return "CalcDispersionPmeReciprocalForce";
}
CalcDispersionPmeReciprocalForceKernel(std::string name, const Platform& platform) : KernelImpl(name, platform) {
}
/**
* Initialize the kernel.
*
* @param gridx the x size of the PME grid
* @param gridy the y size of the PME grid
* @param gridz the z size of the PME grid
* @param numParticles the number of particles in the system
* @param alpha the Ewald blending parameter
*/
virtual void initialize(int gridx, int gridy, int gridz, int numParticles, double alpha) = 0;
/**
* Begin computing the force and energy.
*
* @param io an object that coordinates data transfer
* @param periodicBoxVectors the vectors defining the periodic box (measured in nm)
* @param includeEnergy true if potential energy should be computed
*/
virtual void beginComputation(IO& io, const Vec3* periodicBoxVectors, bool includeEnergy) = 0;
/**
* Finish computing the force and energy.
*
* @param io an object that coordinates data transfer
* @return the potential energy due to the PME reciprocal space interactions
*/
virtual double finishComputation(IO& io) = 0;
/**
* Get the parameters being used for PME.
*
* @param alpha the separation parameter
* @param nx the number of grid points along the X axis
* @param ny the number of grid points along the Y axis
* @param nz the number of grid points along the Z axis
*/
virtual void getPMEParameters(double& alpha, int& nx, int& ny, int& nz) const = 0;
};
} // namespace OpenMM
#endif /*OPENMM_KERNELS_H_*/
......@@ -104,7 +104,7 @@ void Platform::setPropertyDefaultValue(const string& property, const string& val
propertyName = deprecatedPropertyReplacements.find(property)->second;
for (int i = 0; i < (int) platformProperties.size(); i++)
if (platformProperties[i] == propertyName) {
defaultProperties[property] = value;
defaultProperties[propertyName] = value;
return;
}
throw OpenMMException("setPropertyDefaultValue: Illegal property name");
......
......@@ -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) 2015 Stanford University and the Authors. *
* Portions copyright (c) 2015-2016 Stanford University and the Authors. *
* Authors: Peter Eastman *
* Contributors: *
* *
......@@ -171,6 +171,16 @@ protected:
* The implementation returns the union of all kernel names required by all Integrators that have been added.
*/
std::vector<std::string> getKernelNames();
/**
* This will be called by the Context when the user modifies aspects of the context state, such
* as positions, velocities, or parameters. This gives the Integrator a chance to discard cached
* information. This is <i>only</i> called when the user modifies information using methods of the Context
* object. It is <i>not</i> called when a ForceImpl object modifies state information in its updateContextState()
* method (unless the ForceImpl calls a Context method to perform the modification).
*
* @param changed this specifies what aspect of the Context was changed
*/
void stateChanged(State::DataType changed);
/**
* Compute the kinetic energy of the system at the current time.
*
......
......@@ -57,7 +57,7 @@ namespace OpenMM {
* <li>distance(p1, p2): the distance between particles p1 and p2 (where "p1" and "p2" may be replaced by the names
* of whichever particles you want to calculate the distance between).</li>
* <li>angle(p1, p2, p3): the angle formed by the three specified particles.</li>
* <li>dihedral(p1, p2, p3, p4): the dihedral angle formed by the four specified particles.</li>
* <li>dihedral(p1, p2, p3, p4): the dihedral angle formed by the four specified particles, guaranteed to be in the range [-pi,+pi].</li>
* </ul>
*
* The expression also may involve tabulated functions, and may depend on arbitrary
......
......@@ -52,6 +52,7 @@ namespace OpenMM {
* part of the system definition, while values of global parameters may be modified during a simulation by calling Context::setParameter().
* Finally, call addTorsion() once for each torsion. After an torsion has been added, you can modify its parameters by calling setTorsionParameters().
* This will have no effect on Contexts that already exist unless you call updateParametersInContext().
* theta is guaranteed to be in the range [-pi,+pi]
*
* As an example, the following code creates a CustomTorsionForce that implements a harmonic potential:
*
......
......@@ -191,7 +191,7 @@ public:
* @returns true if force uses PBC and false otherwise
*/
bool usesPeriodicBoundaryConditions() const {
return true;
return false;
}
protected:
ForceImpl* createImpl() const;
......
......@@ -147,7 +147,7 @@ public:
* @returns true if force uses PBC and false otherwise
*/
bool usesPeriodicBoundaryConditions() const {
return true;
return false;
}
protected:
ForceImpl* createImpl() const;
......
......@@ -244,7 +244,7 @@ public:
* @returns true if force uses PBC and false otherwise
*/
bool usesPeriodicBoundaryConditions() const {
return true;
return false;
}
protected:
ForceImpl* createImpl() const;
......
......@@ -109,7 +109,12 @@ public:
* Periodic boundary conditions are used, and Particle-Mesh Ewald (PME) summation is used to compute the interaction of each particle
* with all periodic copies of every other particle.
*/
PME = 4
PME = 4,
/**
* Periodic boundary conditions are used, and Particle-Mesh Ewald (PME) summation is used to compute the interaction of each particle
* with all periodic copies of every other particle for both electrostatics and dispersion. No switching is used for either interaction.
*/
LJPME = 5
};
/**
* Create a NonbondedForce.
......@@ -207,6 +212,16 @@ public:
* @param[out] nz the number of grid points along the Z axis
*/
void getPMEParameters(double& alpha, int& nx, int& ny, int& nz) const;
/**
* Get the parameters to use for dispersion term in LJ-PME calculations. If alpha is 0 (the default),
* these parameters are ignored and instead their values are chosen based on the Ewald error tolerance.
*
* @param[out] alpha the separation parameter
* @param[out] nx the number of dispersion grid points along the X axis
* @param[out] ny the number of dispersion grid points along the Y axis
* @param[out] nz the number of dispersion grid points along the Z axis
*/
void getLJPMEParameters(double& alpha, int& nx, int& ny, int& nz) const;
/**
* Set the parameters to use for PME calculations. If alpha is 0 (the default), these parameters are
* ignored and instead their values are chosen based on the Ewald error tolerance.
......@@ -217,6 +232,16 @@ public:
* @param nz the number of grid points along the Z axis
*/
void setPMEParameters(double alpha, int nx, int ny, int nz);
/**
* Set the parameters to use for the dispersion term in LJPME calculations. If alpha is 0 (the default),
* these parameters are ignored and instead their values are chosen based on the Ewald error tolerance.
*
* @param alpha the separation parameter
* @param nx the number of grid points along the X axis
* @param ny the number of grid points along the Y axis
* @param nz the number of grid points along the Z axis
*/
void setLJPMEParameters(double alpha, int nx, int ny, int nz);
/**
* Get the parameters being used for PME in a particular Context. Because some platforms have restrictions
* on the allowed grid sizes, the values that are actually used may be slightly different from those
......@@ -230,6 +255,19 @@ public:
* @param[out] nz the number of grid points along the Z axis
*/
void getPMEParametersInContext(const Context& context, double& alpha, int& nx, int& ny, int& nz) const;
/**
* Get the PME parameters being used for the dispersion term for LJPME in a particular Context. Because some
* platforms have restrictions on the allowed grid sizes, the values that are actually used may be slightly different
* from those specified with setPMEParameters(), or the standard values calculated based on the Ewald error tolerance.
* See the manual for details.
*
* @param context the Context for which to get the parameters
* @param[out] alpha the separation parameter
* @param[out] nx the number of grid points along the X axis
* @param[out] ny the number of grid points along the Y axis
* @param[out] nz the number of grid points along the Z axis
*/
void getLJPMEParametersInContext(const Context& context, double& alpha, int& nx, int& ny, int& nz) const;
/**
* Add the nonbonded force parameters for a particle. This should be called once for each particle
* in the System. When it is called for the i'th time, it specifies the parameters for the i'th particle.
......@@ -382,9 +420,9 @@ private:
class ParticleInfo;
class ExceptionInfo;
NonbondedMethod nonbondedMethod;
double cutoffDistance, switchingDistance, rfDielectric, ewaldErrorTol, alpha;
double cutoffDistance, switchingDistance, rfDielectric, ewaldErrorTol, alpha, dalpha;
bool useSwitchingFunction, useDispersionCorrection;
int recipForceGroup, nx, ny, nz;
int recipForceGroup, nx, ny, nz, dnx, dny, dnz;
void addExclusionsToSet(const std::vector<std::set<int> >& bonded12, std::set<int>& exclusions, int baseParticle, int fromParticle, int currentLevel) const;
std::vector<ParticleInfo> particles;
std::vector<ExceptionInfo> exceptions;
......
......@@ -59,6 +59,9 @@ class OPENMM_EXPORT TabulatedFunction {
public:
virtual ~TabulatedFunction() {
}
/**
* @deprecated This will be removed in a future release.
*/
virtual TabulatedFunction* Copy() const = 0;
};
......@@ -99,6 +102,8 @@ public:
void setFunctionParameters(const std::vector<double>& values, double min, double max);
/**
* Create a deep copy of the tabulated function.
*
* @deprecated This will be removed in a future release.
*/
Continuous1DFunction* Copy() const;
private:
......@@ -158,6 +163,8 @@ public:
void setFunctionParameters(int xsize, int ysize, const std::vector<double>& values, double xmin, double xmax, double ymin, double ymax);
/**
* Create a deep copy of the tabulated function
*
* @deprecated This will be removed in a future release.
*/
Continuous2DFunction* Copy() const;
private:
......@@ -233,6 +240,8 @@ public:
void setFunctionParameters(int xsize, int ysize, int zsize, const std::vector<double>& values, double xmin, double xmax, double ymin, double ymax, double zmin, double zmax);
/**
* Create a deep copy of the tabulated function
*
* @deprecated This will be removed in a future release.
*/
Continuous3DFunction* Copy() const;
private:
......@@ -268,6 +277,8 @@ public:
void setFunctionParameters(const std::vector<double>& values);
/**
* Create a deep copy of the tabulated function
*
* @deprecated This will be removed in a future release.
*/
Discrete1DFunction* Copy() const;
private:
......@@ -310,6 +321,8 @@ public:
void setFunctionParameters(int xsize, int ysize, const std::vector<double>& values);
/**
* Create a deep copy of the tabulated function
*
* @deprecated This will be removed in a future release.
*/
Discrete2DFunction* Copy() const;
private:
......@@ -356,6 +369,8 @@ public:
void setFunctionParameters(int xsize, int ysize, int zsize, const std::vector<double>& values);
/**
* Create a deep copy of the tabulated function
*
* @deprecated This will be removed in a future release.
*/
Discrete3DFunction* Copy() const;
private:
......
......@@ -252,6 +252,10 @@ public:
void integratorDeleted() {
integratorIsDeleted = true;
}
/**
* Notify the integrator that some aspect of the system has changed, and cached information should be discarded.
*/
void systemChanged();
/**
* This is the routine that actually computes the list of molecules returned by getMolecules(). Normally
* you should never call it. It is exposed here because the same logic is useful to other classes too.
......
......@@ -65,6 +65,7 @@ public:
std::vector<std::string> getKernelNames();
void updateParametersInContext(ContextImpl& context);
void getPMEParameters(double& alpha, int& nx, int& ny, int& nz) const;
void getLJPMEParameters(double& alpha, int& nx, int& ny, int& nz) const;
/**
* This is a utility routine that calculates the values to use for alpha and kmax when using
* Ewald summation.
......@@ -74,7 +75,7 @@ public:
* This is a utility routine that calculates the values to use for alpha and grid size when using
* Particle Mesh Ewald.
*/
static void calcPMEParameters(const System& system, const NonbondedForce& force, double& alpha, int& xsize, int& ysize, int& zsize);
static void calcPMEParameters(const System& system, const NonbondedForce& force, double& alpha, int& xsize, int& ysize, int& zsize, bool lj);
/**
* Compute the coefficient which, when divided by the periodic box volume, gives the
* long range dispersion correction to the energy.
......
......@@ -158,4 +158,5 @@ void CMAPTorsionForceImpl::calcMapDerivatives(int size, const vector<double>& en
void CMAPTorsionForceImpl::updateParametersInContext(ContextImpl& context) {
kernel.getAs<CalcCMAPTorsionForceKernel>().copyParametersToContext(context, owner);
context.systemChanged();
}
......@@ -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) 2015 Stanford University and the Authors. *
* Portions copyright (c) 2015-2016 Stanford University and the Authors. *
* Authors: Peter Eastman *
* Contributors: *
* *
......@@ -120,6 +120,11 @@ vector<string> CompoundIntegrator::getKernelNames() {
return kernels;
}
void CompoundIntegrator::stateChanged(State::DataType changed) {
for (int i = 0; i < integrators.size(); i++)
integrators[i]->stateChanged(changed);
}
double CompoundIntegrator::computeKineticEnergy() {
return integrators[currentIntegrator]->computeKineticEnergy();
}
......@@ -56,12 +56,13 @@ const static char CHECKPOINT_MAGIC_BYTES[] = "OpenMM Binary Checkpoint\n";
ContextImpl::ContextImpl(Context& owner, const System& system, Integrator& integrator, Platform* platform, const map<string, string>& properties) :
owner(owner), system(system), integrator(integrator), hasInitializedForces(false), hasSetPositions(false), integratorIsDeleted(false),
lastForceGroups(-1), platform(platform), platformData(NULL) {
if (system.getNumParticles() == 0)
int numParticles = system.getNumParticles();
if (numParticles == 0)
throw OpenMMException("Cannot create a Context for a System with no particles");
// Check for errors in virtual sites and massless particles.
for (int i = 0; i < system.getNumParticles(); i++) {
for (int i = 0; i < numParticles; i++) {
if (system.isVirtualSite(i)) {
if (system.getParticleMass(i) != 0.0)
throw OpenMMException("Virtual site has nonzero mass");
......@@ -71,14 +72,23 @@ ContextImpl::ContextImpl(Context& owner, const System& system, Integrator& integ
throw OpenMMException("A virtual site cannot depend on another virtual site");
}
}
set<pair<int, int> > constraintAtoms;
for (int i = 0; i < system.getNumConstraints(); i++) {
int particle1, particle2;
double distance;
system.getConstraintParameters(i, particle1, particle2, distance);
if (particle1 == particle2)
throw OpenMMException("A constraint cannot connect a particle to itself");
if (particle1 < 0 || particle2 < 0 || particle1 >= numParticles || particle2 >= numParticles)
throw OpenMMException("Illegal particle index in constraint");
double mass1 = system.getParticleMass(particle1);
double mass2 = system.getParticleMass(particle2);
if ((mass1 == 0.0 && mass2 != 0.0) || (mass2 == 0.0 && mass1 != 0.0))
throw OpenMMException("A constraint cannot involve a massless particle");
pair<int, int> atoms = make_pair(min(particle1, particle2), max(particle1, particle2));
if (constraintAtoms.find(atoms) != constraintAtoms.end())
throw OpenMMException("The System has two constraints between the same atoms. This will produce a singular constraint matrix.");
constraintAtoms.insert(atoms);
}
// Validate the list of properties.
......@@ -170,7 +180,7 @@ ContextImpl::ContextImpl(Context& owner, const System& system, Integrator& integ
for (size_t i = 0; i < forceImpls.size(); ++i)
forceImpls[i]->initialize(*this);
integrator.initialize(*this);
updateStateDataKernel.getAs<UpdateStateDataKernel>().setVelocities(*this, vector<Vec3>(system.getNumParticles()));
updateStateDataKernel.getAs<UpdateStateDataKernel>().setVelocities(*this, vector<Vec3>(numParticles));
}
ContextImpl::~ContextImpl() {
......@@ -259,10 +269,14 @@ void ContextImpl::setPeriodicBoxVectors(const Vec3& a, const Vec3& b, const Vec3
}
void ContextImpl::applyConstraints(double tol) {
if (!hasSetPositions)
throw OpenMMException("Particle positions have not been set");
applyConstraintsKernel.getAs<ApplyConstraintsKernel>().apply(*this, tol);
}
void ContextImpl::applyVelocityConstraints(double tol) {
if (!hasSetPositions)
throw OpenMMException("Particle positions have not been set");
applyConstraintsKernel.getAs<ApplyConstraintsKernel>().applyToVelocities(*this, tol);
}
......@@ -460,3 +474,7 @@ void ContextImpl::loadCheckpoint(istream& stream) {
updateStateDataKernel.getAs<UpdateStateDataKernel>().loadCheckpoint(*this, stream);
hasSetPositions = true;
}
void ContextImpl::systemChanged() {
integrator.stateChanged(State::Energy);
}
......@@ -108,4 +108,5 @@ map<string, double> CustomAngleForceImpl::getDefaultParameters() {
void CustomAngleForceImpl::updateParametersInContext(ContextImpl& context) {
kernel.getAs<CalcCustomAngleForceKernel>().copyParametersToContext(context, owner);
context.systemChanged();
}
......@@ -113,4 +113,5 @@ vector<pair<int, int> > CustomBondForceImpl::getBondedParticles() const {
void CustomBondForceImpl::updateParametersInContext(ContextImpl& context) {
kernel.getAs<CalcCustomBondForceKernel>().copyParametersToContext(context, owner);
context.systemChanged();
}
......@@ -246,6 +246,7 @@ void CustomCentroidBondForceImpl::addBondsBetweenGroups(int group1, int group2,
void CustomCentroidBondForceImpl::updateParametersInContext(ContextImpl& context) {
kernel.getAs<CalcCustomCentroidBondForceKernel>().copyParametersToContext(context, owner);
context.systemChanged();
}
void CustomCentroidBondForceImpl::computeNormalizedWeights(const CustomCentroidBondForce& force, const System& system, vector<vector<double> >& weights) {
......
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