Commit 29e3fa57 authored by Peter Eastman's avatar Peter Eastman
Browse files

Redesigned the API for specifying exclusions and 1-4 interactions.

parent afd06645
......@@ -47,7 +47,7 @@ ENDIF(${CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT})
INCLUDE(Dart)
# SUBDIRS (platforms/reference/tests platforms/cuda)
SUBDIRS (tests)
# The source is organized into subdirectories, but we handle them all from
# this CMakeLists file rather than letting CMake visit them as SUBDIRS.
......
......@@ -226,11 +226,8 @@ public:
*
* @param system the System this kernel will be applied to
* @param force the NonbondedForce this kernel will be used for
* @param exclusions the i'th element lists the indices of all particles with which the i'th particle should not interact through
* nonbonded forces. Bonded 1-4 pairs are also included in this list, since they should be omitted from
* the standard nonbonded calculation.
*/
virtual void initialize(const System& system, const NonbondedForce& force, const std::vector<std::set<int> >& exclusions) = 0;
virtual void initialize(const System& system, const NonbondedForce& force) = 0;
/**
* Execute the kernel to calculate the forces.
*
......
......@@ -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) 2008 Stanford University and the Authors. *
* Portions copyright (c) 2008-2009 Stanford University and the Authors. *
* Authors: Peter Eastman *
* Contributors: *
* *
......@@ -35,6 +35,7 @@
#include "Force.h"
#include "Vec3.h"
#include <map>
#include <set>
#include <vector>
#include "internal/windowsExport.h"
......@@ -46,16 +47,22 @@ namespace OpenMM {
* periodic boundary conditions and cutoffs for long range interactions. Lennard-Jones interactions are
* calculated with the Lorentz-Bertelot combining rule: it uses the arithmetic mean of the sigmas and the
* geometric mean of the epsilons for the two interacting particles.
*
* If the System also contains a HarmonicBondForce, nonbonded interactions are automatically excluded between
* particles which are separated by three or fewer bonds. Most molecular force fields omit Coulomb and
* Lennard-Jones interactions between particles separated by one or two bonds, while using modified parameters
* for those separated by three bonds (known as "1-4 interactions"). This class lets you provide a list of
* 1-4 interactions to include in the potential, along with the parameters to use for each one.
*
* When creating a NonbondedForce, you specify the number of particles and the number of 1-4 interactions as
* arguments to the constructor. You then loop over them and call setParticleParameters() for each particle and
* setNonbond14Parameters() for each 1-4 interaction.
*
* To use this class, create a NonbondedForce object, then call addParticle() once for each particle in the
* System to define its parameters. The number of particles for which you define nonbonded parameters must
* be exactly equal to the number of particles in the System, or else an exception will be thrown when you
* try to create an OpenMMContext. After a particle has been added, you can modify its force field parameters
* by calling setParticleParameters().
*
* NonbondedForce also lets you specify "exceptions", particular pairs of particles whose interactions should be
* computed based on different parameters than those defined for the individual particles. This can be used to
* completely exclude certain interactions from the force calculation, or to alter how they interact with each other.
*
* Many molecular force fields omit Coulomb and Lennard-Jones interactions between particles separated by one
* or two bonds, while using modified parameters for those separated by three bonds (known as "1-4 interactions").
* This class provides a convenience method for this case called createExceptionsFromBonds(). You pass to it
* a list of bonds and the scale factors to use for 1-4 interactions. It identifies all pairs of particles which
* are separated by 1, 2, or 3 bonds, then automatically creates exceptions for them.
*/
class OPENMM_EXPORT NonbondedForce : public Force {
......@@ -81,28 +88,26 @@ public:
*/
CutoffPeriodic = 2,
/**
* Using Ewald summation method for long-range electrostatics.
* Periodic boundary conditions are used, and Ewald summation is used to compute the interaction of each particle
* with all periodic copies of every other particle.
*/
Ewald = 3
};
/**
* Create a NonbondedForce.
*
* @param numParticles the number of particles in the system
* @param numNonbonded14 the number of nonbonded 1-4 terms in the potential function
*/
NonbondedForce(int numParticles, int numNonbonded14);
NonbondedForce();
/**
* Get the number of particles in the system.
* Get the number of particles for which force field parameters have been defined.
*/
int getNumParticles() const {
return particles.size();
}
/**
* Get the number of nonbonded 1-4 terms in the potential function
* Get the number of special interactions that should be calculated differently from other interactions.
*/
int getNumNonbonded14() const {
return nb14s.size();
int getNumExceptions() const {
return exceptions.size();
}
/**
* Get the method used for handling long range nonbonded interactions.
......@@ -146,13 +151,25 @@ public:
* @param c the vector defining the third edge of the periodic box
*/
void setPeriodicBoxVectors(Vec3 a, Vec3 b, Vec3 c);
/**
* 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.
* For calculating the Lennard-Jones interaction between two particles, the arithmetic mean of the sigmas
* and the geometric mean of the epsilons for the two interacting particles is used (the Lorentz-Bertelot
* combining rule).
*
* @param charge the charge of the particle, measured in units of the proton charge
* @param sigma the sigma parameter of the Lennard-Jones potential (corresponding to the van der Waals radius of the particle), measured in nm
* @param epsilon the epsilon parameter of the Lennard-Jones potential (corresponding to the well depth of the van der Waals interaction), measured in kJ/mol
*/
void addParticle(double charge, double sigma, double epsilon);
/**
* Get the nonbonded force parameters for a particle.
*
*
* @param index the index of the particle for which to get parameters
* @param charge the charge of the particle, measured in units of the proton charge
* @param sigma the van der Waals radius of the particle (sigma in the Lennard Jones potential), measured in nm
* @param epsilon the well depth of the van der Waals interaction (epsilon in the Lennard Jones potential), measured in kJ/mol
* @param sigma the sigma parameter of the Lennard-Jones potential (corresponding to the van der Waals radius of the particle), measured in nm
* @param epsilon the epsilon parameter of the Lennard-Jones potential (corresponding to the well depth of the van der Waals interaction), measured in kJ/mol
*/
void getParticleParameters(int index, double& charge, double& sigma, double& epsilon) const;
/**
......@@ -162,40 +179,71 @@ public:
*
* @param index the index of the particle for which to set parameters
* @param charge the charge of the particle, measured in units of the proton charge
* @param sigma the van der Waals radius of the particle (sigma in the Lennard Jones potential), measured in nm
* @param epsilon the well depth of the van der Waals interaction (epsilon in the Lennard Jones potential), measured in kJ/mol
* @param sigma the sigma parameter of the Lennard-Jones potential (corresponding to the van der Waals radius of the particle), measured in nm
* @param epsilon the epsilon parameter of the Lennard-Jones potential (corresponding to the well depth of the van der Waals interaction), measured in kJ/mol
*/
void setParticleParameters(int index, double charge, double sigma, double epsilon);
/**
* Get the force field parameters for a nonbonded 1-4 term.
* Add an interaction to the list of exceptions that should be calculated differently from other interactions.
* If chargeProd and epsilon are both equal to 0, this will cause the interaction to be completely omitted from
* force and energy calculations.
*
* In many cases, you can use createExceptionsFromBonds() rather than adding each exception explicitly.
*
* @param particle1 the index of the first particle involved in the interaction
* @param particle2 the index of the second particle involved in the interaction
* @param chargeProd the scaled product of the atomic charges (i.e. the strength of the Coulomb interaction), measured in units of the proton charge squared
* @param sigma the sigma parameter of the Lennard-Jones potential (corresponding to the van der Waals radius of the particle), measured in nm
* @param epsilon the epsilon parameter of the Lennard-Jones potential (corresponding to the well depth of the van der Waals interaction), measured in kJ/mol
*/
void addException(int particle1, int particle2, double chargeProd, double sigma, double epsilon);
/**
* Get the force field parameters for an interaction that should be calculated differently from others.
*
* @param index the index of the interaction for which to get parameters
* @param particle1 the index of the first particle involved in the interaction
* @param particle2 the index of the second particle involved in the interaction
* @param chargeProd the scaled product of the atomic charges (i.e. the strength of the Coulomb interaction), measured in units of the proton charge squared
* @param sigma the van der Waals radius of the particle (sigma in the Lennard Jones potential), measured in nm
* @param epsilon the well depth of the van der Waals interaction (epsilon in the Lennard Jones potential), measured in kJ/mol
* @param sigma the sigma parameter of the Lennard-Jones potential (corresponding to the van der Waals radius of the particle), measured in nm
* @param epsilon the epsilon parameter of the Lennard-Jones potential (corresponding to the well depth of the van der Waals interaction), measured in kJ/mol
*/
void getNonbonded14Parameters(int index, int& particle1, int& particle2, double& chargeProd, double& sigma, double& epsilon) const;
void getExceptionParameters(int index, int& particle1, int& particle2, double& chargeProd, double& sigma, double& epsilon) const;
/**
* Set the force field parameters for a nonbonded 1-4 term.
* Set the force field parameters for an interaction that should be calculated differently from others.
* If chargeProd and epsilon are both equal to 0, this will cause the interaction to be completely omitted from
* force and energy calculations.
*
* @param index the index of the interaction for which to get parameters
* @param particle1 the index of the first particle involved in the interaction
* @param particle2 the index of the second particle involved in the interaction
* @param chargeProd the scaled product of the atomic charges (i.e. the strength of the Coulomb interaction), measured in units of the proton charge squared
* @param sigma the van der Waals radius of the particle (sigma in the Lennard Jones potential), measured in nm
* @param epsilon the well depth of the van der Waals interaction (epsilon in the Lennard Jones potential), measured in kJ/mol
* @param sigma the sigma parameter of the Lennard-Jones potential (corresponding to the van der Waals radius of the particle), measured in nm
* @param epsilon the epsilon parameter of the Lennard-Jones potential (corresponding to the well depth of the van der Waals interaction), measured in kJ/mol
*/
void setExceptionParameters(int index, int particle1, int particle2, double chargeProd, double sigma, double epsilon);
/**
* Identify exceptions based on the molecular topology. Particles which are separated by one or two bonds are set
* to not interact at all, while pairs of particles separated by three bonds (known as "1-4 interactions") have
* their Coulomb and Lennard-Jones interactions reduced by a fixed factor.
*
* @param bonds the set of bonds based on which to construct exceptions. Each element specifies the indices of
* two particles that are bonded to each other.
* @param coulomb14Scale pairs of particles separated by three bonds will have the strength of their Coulomb interaction
* multiplied by this factor
* @param lj14Scale pairs of particles separated by three bonds will have the strength of their Lennard-Jones interaction
* multiplied by this factor
*/
void setNonbonded14Parameters(int index, int particle1, int particle2, double chargeProd, double sigma, double epsilon);
void createExceptionsFromBonds(const std::vector<std::pair<int, int> >& bonds, double coulomb14Scale, double lj14Scale);
protected:
ForceImpl* createImpl();
private:
class ParticleInfo;
class NB14Info;
class ExceptionInfo;
NonbondedMethod nonbondedMethod;
double cutoffDistance;
Vec3 periodicBoxVectors[3];
void addExclusionsToSet(const std::vector<std::set<int> >& bonded12, std::set<int>& exclusions, int baseParticle, int fromParticle, int currentLevel) const;
// Retarded visual studio compiler complains about being unable to
// export private stl class members.
......@@ -205,7 +253,7 @@ private:
#pragma warning(disable:4251)
#endif
std::vector<ParticleInfo> particles;
std::vector<NB14Info> nb14s;
std::vector<ExceptionInfo> exceptions;
#if defined(_MSC_VER)
#pragma warning(pop)
#endif
......@@ -218,16 +266,22 @@ public:
ParticleInfo() {
charge = sigma = epsilon = 0.0;
}
ParticleInfo(double charge, double sigma, double epsilon) :
charge(charge), sigma(sigma), epsilon(epsilon) {
}
};
class NonbondedForce::NB14Info {
class NonbondedForce::ExceptionInfo {
public:
int particle1, particle2;
double chargeProd, sigma, epsilon;
NB14Info() {
ExceptionInfo() {
particle1 = particle2 = -1;
chargeProd = sigma = epsilon = 0.0;
}
ExceptionInfo(int particle1, int particle2, double chargeProd, double sigma, double epsilon) :
particle1(particle1), particle2(particle2), chargeProd(chargeProd), sigma(sigma), epsilon(epsilon) {
}
};
} // namespace OpenMM
......
......@@ -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) 2008 Stanford University and the Authors. *
* Portions copyright (c) 2008-2009 Stanford University and the Authors. *
* Authors: Peter Eastman *
* Contributors: *
* *
......@@ -63,8 +63,6 @@ public:
}
std::vector<std::string> getKernelNames();
private:
void findExclusions(const std::vector<std::vector<int> >& bondIndices, std::vector<std::set<int> >& exclusions, std::set<std::pair<int, int> >& bonded14Indices) const;
void addExclusionsToSet(const std::vector<std::set<int> >& bonded12, std::set<int>& exclusions, int baseParticle, int fromParticle, int currentLevel) const;
NonbondedForce& owner;
Kernel kernel;
};
......
......@@ -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) 2008 Stanford University and the Authors. *
* Portions copyright (c) 2008-2009 Stanford University and the Authors. *
* Authors: Peter Eastman *
* Contributors: *
* *
......@@ -33,11 +33,15 @@
#include "OpenMMException.h"
#include "NonbondedForce.h"
#include "internal/NonbondedForceImpl.h"
#include <cmath>
#include <utility>
using namespace OpenMM;
using std::pair;
using std::set;
using std::vector;
NonbondedForce::NonbondedForce(int numParticles, int numNonbonded14) : particles(numParticles), nb14s(numNonbonded14),
nonbondedMethod(NoCutoff), cutoffDistance(1.0) {
NonbondedForce::NonbondedForce() : nonbondedMethod(NoCutoff), cutoffDistance(1.0) {
periodicBoxVectors[0] = Vec3(2, 0, 0);
periodicBoxVectors[1] = Vec3(0, 2, 0);
periodicBoxVectors[2] = Vec3(0, 0, 2);
......@@ -77,6 +81,10 @@ void NonbondedForce::setPeriodicBoxVectors(Vec3 a, Vec3 b, Vec3 c) {
periodicBoxVectors[2] = c;
}
void NonbondedForce::addParticle(double charge, double sigma, double epsilon) {
particles.push_back(ParticleInfo(charge, sigma, epsilon));
}
void NonbondedForce::getParticleParameters(int index, double& charge, double& sigma, double& epsilon) const {
charge = particles[index].charge;
sigma = particles[index].sigma;
......@@ -89,22 +97,73 @@ void NonbondedForce::setParticleParameters(int index, double charge, double sigm
particles[index].epsilon = epsilon;
}
void NonbondedForce::getNonbonded14Parameters(int index, int& particle1, int& particle2, double& chargeProd, double& sigma, double& epsilon) const {
particle1 = nb14s[index].particle1;
particle2 = nb14s[index].particle2;
chargeProd = nb14s[index].chargeProd;
sigma = nb14s[index].sigma;
epsilon = nb14s[index].epsilon;
void NonbondedForce::addException(int particle1, int particle2, double chargeProd, double sigma, double epsilon) {
exceptions.push_back(ExceptionInfo(particle1, particle2, chargeProd, sigma, epsilon));
}
void NonbondedForce::getExceptionParameters(int index, int& particle1, int& particle2, double& chargeProd, double& sigma, double& epsilon) const {
particle1 = exceptions[index].particle1;
particle2 = exceptions[index].particle2;
chargeProd = exceptions[index].chargeProd;
sigma = exceptions[index].sigma;
epsilon = exceptions[index].epsilon;
}
void NonbondedForce::setNonbonded14Parameters(int index, int particle1, int particle2, double chargeProd, double sigma, double epsilon) {
nb14s[index].particle1 = particle1;
nb14s[index].particle2 = particle2;
nb14s[index].chargeProd = chargeProd;
nb14s[index].sigma = sigma;
nb14s[index].epsilon = epsilon;
void NonbondedForce::setExceptionParameters(int index, int particle1, int particle2, double chargeProd, double sigma, double epsilon) {
exceptions[index].particle1 = particle1;
exceptions[index].particle2 = particle2;
exceptions[index].chargeProd = chargeProd;
exceptions[index].sigma = sigma;
exceptions[index].epsilon = epsilon;
}
ForceImpl* NonbondedForce::createImpl() {
return new NonbondedForceImpl(*this);
}
void NonbondedForce::createExceptionsFromBonds(const vector<pair<int, int> >& bonds, double coulomb14Scale, double lj14Scale) {
// Find particles separated by 1, 2, or 3 bonds.
vector<set<int> > exclusions(particles.size());
vector<set<int> > bonded12(exclusions.size());
for (int i = 0; i < (int) bonds.size(); ++i) {
bonded12[bonds[i].first].insert(bonds[i].second);
bonded12[bonds[i].second].insert(bonds[i].first);
}
for (int i = 0; i < (int) exclusions.size(); ++i)
addExclusionsToSet(bonded12, exclusions[i], i, i, 2);
// Find particles separated by 1 or 2 bonds and create the exceptions.
for (int i = 0; i < (int) exclusions.size(); ++i) {
set<int> bonded13;
addExclusionsToSet(bonded12, bonded13, i, i, 1);
for (set<int>::const_iterator iter = exclusions[i].begin(); iter != exclusions[i].end(); ++iter)
if (*iter < i) {
if (bonded13.find(*iter) == bonded13.end()) {
// This is a 1-4 interaction.
const ParticleInfo& particle1 = particles[*iter];
const ParticleInfo& particle2 = particles[i];
const double chargeProd = coulomb14Scale*particle1.charge*particle2.charge;
const double sigma = 0.5*(particle1.sigma+particle2.sigma);
const double epsilon = lj14Scale*std::sqrt(particle1.epsilon*particle2.epsilon);
addException(*iter, i, chargeProd, sigma, epsilon);
}
else {
// This interaction should be completely excluded.
addException(*iter, i, 0.0, 1.0, 0.0);
}
}
}
}
void NonbondedForce::addExclusionsToSet(const vector<set<int> >& bonded12, set<int>& exclusions, int baseParticle, int fromParticle, int currentLevel) const {
for (set<int>::const_iterator iter = bonded12[fromParticle].begin(); iter != bonded12[fromParticle].end(); ++iter) {
if (*iter != baseParticle)
exclusions.insert(*iter);
if (currentLevel > 0)
addExclusionsToSet(bonded12, exclusions, baseParticle, *iter, currentLevel-1);
}
}
......@@ -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) 2008 Stanford University and the Authors. *
* Portions copyright (c) 2008-2009 Stanford University and the Authors. *
* Authors: Peter Eastman *
* Contributors: *
* *
......@@ -29,14 +29,17 @@
* USE OR OTHER DEALINGS IN THE SOFTWARE. *
* -------------------------------------------------------------------------- */
#include "OpenMMException.h"
#include "internal/OpenMMContextImpl.h"
#include "internal/NonbondedForceImpl.h"
#include "kernels.h"
#include <sstream>
using namespace OpenMM;
using std::pair;
using std::vector;
using std::set;
using std::stringstream;
NonbondedForceImpl::NonbondedForceImpl(NonbondedForce& owner) : owner(owner) {
}
......@@ -46,41 +49,41 @@ NonbondedForceImpl::~NonbondedForceImpl() {
void NonbondedForceImpl::initialize(OpenMMContextImpl& context) {
kernel = context.getPlatform().createKernel(CalcNonbondedForceKernel::Name(), context);
// See if the system contains a HarmonicBondForce. If so, use it to identify exclusions.
System& system = context.getSystem();
vector<set<int> > exclusions(owner.getNumParticles());
vector<vector<int> > bondIndices;
set<pair<int, int> > bonded14set;
for (int i = 0; i < system.getNumForces(); i++) {
if (dynamic_cast<HarmonicBondForce*>(&system.getForce(i)) != NULL) {
const HarmonicBondForce& force = dynamic_cast<const HarmonicBondForce&>(system.getForce(i));
bondIndices.resize(force.getNumBonds());
for (int j = 0; j < force.getNumBonds(); ++j) {
int particle1, particle2;
double length, k;
force.getBondParameters(j, particle1, particle2, length, k);
bondIndices[j].push_back(particle1);
bondIndices[j].push_back(particle2);
}
break;
}
}
// Also treat constrained distances as bonds.
// Check for errors in the specification of exceptions.
int numBonds = bondIndices.size();
bondIndices.resize(numBonds+system.getNumConstraints());
for (int j = 0; j < system.getNumConstraints(); j++) {
System& system = context.getSystem();
if (owner.getNumParticles() != system.getNumParticles())
throw OpenMMException("NonbondedForce must have exactly as many particles as the System it belongs to.");
vector<set<int> > exceptions(owner.getNumParticles());
for (int i = 0; i < owner.getNumExceptions(); i++) {
int particle1, particle2;
double distance;
system.getConstraintParameters(j, particle1, particle2, distance);
bondIndices[j+numBonds].push_back(particle1);
bondIndices[j+numBonds].push_back(particle2);
double chargeProd, sigma, epsilon;
owner.getExceptionParameters(i, particle1, particle2, chargeProd, sigma, epsilon);
if (particle1 < 0 || particle1 >= owner.getNumParticles()) {
stringstream msg;
msg << "NonbondedForce: Illegal particle index for an exception: ";
msg << particle1;
throw OpenMMException(msg.str());
}
if (particle2 < 0 || particle2 >= owner.getNumParticles()) {
stringstream msg;
msg << "NonbondedForce: Illegal particle index for an exception: ";
msg << particle2;
throw OpenMMException(msg.str());
}
if (exceptions[particle1].count(particle2) > 0 || exceptions[particle2].count(particle1) > 0) {
stringstream msg;
msg << "NonbondedForce: Multiple exceptions are specified for particles ";
msg << particle1;
msg << " and ";
msg << particle2;
throw OpenMMException(msg.str());
}
exceptions[particle1].insert(particle2);
exceptions[particle2].insert(particle1);
}
findExclusions(bondIndices, exclusions, bonded14set);
dynamic_cast<CalcNonbondedForceKernel&>(kernel.getImpl()).initialize(context.getSystem(), owner, exclusions);
dynamic_cast<CalcNonbondedForceKernel&>(kernel.getImpl()).initialize(context.getSystem(), owner);
}
void NonbondedForceImpl::calcForces(OpenMMContextImpl& context, Stream& forces) {
......@@ -96,30 +99,3 @@ std::vector<std::string> NonbondedForceImpl::getKernelNames() {
names.push_back(CalcNonbondedForceKernel::Name());
return names;
}
void NonbondedForceImpl::findExclusions(const vector<vector<int> >& bondIndices, vector<set<int> >& exclusions, set<pair<int, int> >& bonded14Indices) const {
vector<set<int> > bonded12(exclusions.size());
for (int i = 0; i < (int) bondIndices.size(); ++i) {
bonded12[bondIndices[i][0]].insert(bondIndices[i][1]);
bonded12[bondIndices[i][1]].insert(bondIndices[i][0]);
}
for (int i = 0; i < (int) exclusions.size(); ++i)
addExclusionsToSet(bonded12, exclusions[i], i, i, 2);
for (int i = 0; i < (int) exclusions.size(); ++i) {
set<int> bonded13;
addExclusionsToSet(bonded12, bonded13, i, i, 1);
for (set<int>::const_iterator iter = exclusions[i].begin(); iter != exclusions[i].end(); ++iter)
if (*iter < i && bonded13.find(*iter) == bonded13.end())
bonded14Indices.insert(pair<int, int> (*iter, i));
}
}
void NonbondedForceImpl::addExclusionsToSet(const vector<set<int> >& bonded12, set<int>& exclusions, int baseParticle, int fromParticle, int currentLevel) const {
for (set<int>::const_iterator iter = bonded12[fromParticle].begin(); iter != bonded12[fromParticle].end(); ++iter) {
if (*iter != baseParticle)
exclusions.insert(*iter);
if (currentLevel > 0)
addExclusionsToSet(bonded12, exclusions, baseParticle, *iter, currentLevel-1);
}
}
......@@ -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) 2008 Stanford University and the Authors. *
* Portions copyright (c) 2008-2009 Stanford University and the Authors. *
* Authors: Peter Eastman, Mark Friedrichs *
* Contributors: *
* *
......@@ -129,7 +129,7 @@ int BrookCalcNonbondedForceKernel::setLog( FILE* log ){
*
*/
void BrookCalcNonbondedForceKernel::initialize( const System& system, const NonbondedForce& force, const std::vector<std::set<int> >& exclusions ){
void BrookCalcNonbondedForceKernel::initialize( const System& system, const NonbondedForce& force ){
// ---------------------------------------------------------------------------------------
......@@ -172,7 +172,22 @@ void BrookCalcNonbondedForceKernel::initialize( const System& system, const Nonb
nonbondedParameters[ii].push_back( charge );
nonbondedParameters[ii].push_back( radius );
nonbondedParameters[ii].push_back( depth );
}
}
// Go through the exclusions.
std::vector<std::set<int> >& exclusions(_numberOfParticles);
std::vector<int> nb14s;
for (int i = 0; i < force.getNumExceptions(); i++) {
int particle1, particle2;
double chargeProd, sigma, epsilon;
force.getExceptionParameters(i, particle1, particle2, chargeProd, sigma, epsilon);
exclusions[particle1].insert(particle2);
exclusions[particle2].insert(particle1);
exclusions.push_back(pair<int, int>(particle1, particle2));
if (chargeProd != 0.0 || epsilon != 0.0)
nb14s.push_back(i);
}
brookNonBonded.setup( _numberOfParticles, nonbondedParameters, exclusions, getPlatform() );
_openMMBrookInterface.setTriggerForceKernel( this );
......@@ -188,7 +203,7 @@ void BrookCalcNonbondedForceKernel::initialize( const System& system, const Nonb
// nonbonded 14 ixns
initialize14Interactions( system, force );
initialize14Interactions( system, force, nb14s );
}
......@@ -200,7 +215,7 @@ void BrookCalcNonbondedForceKernel::initialize( const System& system, const Nonb
*
*/
void BrookCalcNonbondedForceKernel::initialize14Interactions( const System& system, const NonbondedForce& force ){
void BrookCalcNonbondedForceKernel::initialize14Interactions( const System& system, const NonbondedForce& force, const std::vector<int>& nb14s ){
// ---------------------------------------------------------------------------------------
......@@ -216,7 +231,7 @@ void BrookCalcNonbondedForceKernel::initialize14Interactions( const System& syst
// create _brookBondParameters object containing particle indices/parameters
int numberOf14Forces = force.getNumNonbonded14();
int numberOf14Forces = nb14s.size();
if( numberOf14Forces > 0 ){
_brookBondParameters = new BrookBondParameters( BondName, NumberOfParticlesInBond, NumberOfParametersInBond, numberOf14Forces, getLog() );
......@@ -229,7 +244,7 @@ void BrookCalcNonbondedForceKernel::initialize14Interactions( const System& syst
int particles[NumberOfParticlesInBond];
double parameters[NumberOfParametersInBond];
force.getNonbonded14Parameters( ii, particle1, particle2, charge, radius, depth );
force.getExceptionParameters( nb14s[ii], particle1, particle2, charge, radius, depth );
//(void) fprintf( log, "%s idx=%d [%d %d] [%f %f %f]\n", methodName.c_str(), ii, particle1, particle2, charge, radius, depth );
particles[0] = particle1;
......
......@@ -54,12 +54,9 @@ class BrookCalcNonbondedForceKernel : public CalcNonbondedForceKernel {
*
* @param system the System this kernel will be applied to
* @param force the NonbondedForce this kernel will be used for
* @param exclusions the i'th element lists the indices of all particles with which the i'th particle should not interact through
* nonbonded forces. Bonded 1-4 pairs are also included in this list, since they should be omitted from
* the standard nonbonded calculation.
*/
void initialize( const System& system, const NonbondedForce& force, const std::vector<std::set<int> >& exclusions );
void initialize( const System& system, const NonbondedForce& force );
/**
* Initialize the 14 ixns
......
......@@ -88,10 +88,10 @@ void testMotionRemoval( FILE* log ) {
bonds->setBondParameters(0, 2, 3, 2.0, 0.5);
system.addForce(bonds);
NonbondedForce* nonbonded = new NonbondedForce(numberOfParticles, 0);
NonbondedForce* nonbonded = new NonbondedForce();
for (int i = 0; i < numberOfParticles; ++i) {
system.setParticleMass(i, (double) (i+1) );
nonbonded->setParticleParameters(i, (i%2 == 0 ? 1.0 : -1.0), 1.0, 5.0);
nonbonded->addParticle((i%2 == 0 ? 1.0 : -1.0), 1.0, 5.0);
}
system.addForce(nonbonded);
......
......@@ -217,10 +217,10 @@ void testLangevinTemperature( FILE* log ){
System system(numberOfParticles, 0);
LangevinIntegrator integrator(temp, 0.2, 0.002);
NonbondedForce* forceField = new NonbondedForce(numberOfParticles, 0);
NonbondedForce* forceField = new NonbondedForce();
for (int i = 0; i < numberOfParticles; ++i){
system.setParticleMass(i, mass );
forceField->setParticleParameters(i, (i%2 == 0 ? 1.0 : -1.0), 1.0, 5.0);
forceField->addParticle((i%2 == 0 ? 1.0 : -1.0), 1.0, 5.0);
}
system.addForce(forceField);
......@@ -315,10 +315,10 @@ void testLangevinConstraints( FILE* log ){
System system( numParticles, numConstraints );
LangevinIntegrator integrator( temp, 2.0, 0.001 );
integrator.setConstraintTolerance(1e-5);
NonbondedForce* forceField = new NonbondedForce(numParticles, 0);
NonbondedForce* forceField = new NonbondedForce();
for (int i = 0; i < numParticles; ++i) {
system.setParticleMass(i, mass);
forceField->setParticleParameters(i, (i%2 == 0 ? 0.2 : -0.2), 0.5, 5.0);
forceField->addParticle((i%2 == 0 ? 0.2 : -0.2), 0.5, 5.0);
}
for (int i = 0; i < numConstraints; ++i){
system.setConstraintParameters(i, 2*i, 2*i+1, 1.0);
......
......@@ -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) 2008 Stanford University and the Authors. *
* Portions copyright (c) 2008-2009 Stanford University and the Authors. *
* Authors: Mark Friedrichs *
* Contributors: *
* *
......@@ -73,9 +73,9 @@ void testBrookCoulomb( FILE* log ){
// int index, double charge, double radius, double depth
NonbondedForce* forceField = new NonbondedForce( numberOfParticles, 0 );
forceField->setParticleParameters(0, 0.5, 1, 0);
forceField->setParticleParameters(1, -1.5, 1, 0);
NonbondedForce* forceField = new NonbondedForce();
forceField->addParticle(0.5, 1, 0);
forceField->addParticle(-1.5, 1, 0);
system.addForce(forceField);
//(void) fprintf( log, "%s: Calling context\n", methodName.c_str() );
......@@ -141,9 +141,9 @@ void testBrookLJ( FILE* log ){
// int index, double charge, double radius, double depth
NonbondedForce* forceField = new NonbondedForce( numberOfParticles, 0 );
forceField->setParticleParameters(0, 0, 1.2, 1);
forceField->setParticleParameters(1, 0, 1.4, 2);
NonbondedForce* forceField = new NonbondedForce();
forceField->addParticle(0, 1.2, 1);
forceField->addParticle(0, 1.4, 2);
system.addForce(forceField);
//(void) fprintf( log, "%s: Calling context\n", methodName.c_str() );
......@@ -208,13 +208,25 @@ void testBrookExclusionsAnd14( FILE* log ){
// int index, double charge, double radius, double depth
HarmonicBondForce* bonds = new HarmonicBondForce(4);
bonds->setBondParameters(0, 0, 1, 1, 0);
bonds->setBondParameters(1, 1, 2, 1, 0);
bonds->setBondParameters(2, 2, 3, 1, 0);
bonds->setBondParameters(3, 3, 4, 1, 0);
system.addForce(bonds);
NonbondedForce* nonbonded = new NonbondedForce( numberOfParticles, 2);
NonbondedForce* nonbonded = new NonbondedForce();
for (int i = 0; i < numberOfParticles; i++)
nonbonded->addParticle(0, 1.5, 0)
vector<pair<int, int> > bonds;
bonds.push_back(pair<int, int>(0, 1));
bonds.push_back(pair<int, int>(1, 2));
bonds.push_back(pair<int, int>(2, 3));
bonds.push_back(pair<int, int>(3, 4));
nonbonded->createExceptionsFromBonds(bonds, 0.0, 0.0);
int first14, second14;
for (int i = 0; i < nonbonded->getNumExceptions(); i++) {
int particle1, particle2;
double chargeProd, sigma, epsilon;
nonbonded->getExceptionParameters(i, particle1, particle2, chargeProd, sigma, epsilon);
if ((particle1 == 0 && particle2 == 3) || (particle1 == 3 && particle2 == 0))
first14 = i;
if ((particle1 == 1 && particle2 == 4) || (particle1 == 4 && particle2 == 1))
second14 = i;
}
system.addForce(nonbonded);
//(void) fprintf( log, "%s: Calling context\n", methodName.c_str() );
......@@ -240,8 +252,8 @@ void testBrookExclusionsAnd14( FILE* log ){
}
nonbonded->setParticleParameters(0, 0, 1.5, 1);
nonbonded->setParticleParameters(ii, 0, 1.5, 1);
nonbonded->setNonbonded14Parameters(0, 0, 3, 0, 1.5, ii == 3 ? 0.5 : 0.0);
nonbonded->setNonbonded14Parameters(1, 1, 4, 0, 1.5, 0.0);
nonbonded->setExceptionParameters(first14, 0, 3, 0, 1.5, ii == 3 ? 0.5 : 0.0);
nonbonded->setExceptionParameters(second14, 1, 4, 0, 1.5, 0.0);
positions[ii] = Vec3(r, 0, 0);
context.reinitialize();
......@@ -284,8 +296,8 @@ void testBrookExclusionsAnd14( FILE* log ){
nonbonded->setParticleParameters( 0, 2, 1.5, 0 );
nonbonded->setParticleParameters( ii, 2, 1.5, 0 );
nonbonded->setNonbonded14Parameters( 0, 0, 3, ii == 3 ? 4/1.2 : 0, 1.5, 0 );
nonbonded->setNonbonded14Parameters( 1, 1, 4, 0, 1.5, 0 );
nonbonded->setExceptionParameters( first14, 0, 3, ii == 3 ? 4/1.2 : 0, 1.5, 0 );
nonbonded->setExceptionParameters( second14, 1, 4, 0, 1.5, 0 );
context.reinitialize();
......
......@@ -183,10 +183,10 @@ void testVerletConstraints( FILE* log ){
System system(numParticles, numConstraints);
VerletIntegrator integrator(0.001);
integrator.setConstraintTolerance(1e-5);
NonbondedForce* forceField = new NonbondedForce(numParticles, 0);
NonbondedForce* forceField = new NonbondedForce();
for (int i = 0; i < numParticles; ++i) {
system.setParticleMass(i, mass );
forceField->setParticleParameters(i, (i%2 == 0 ? 0.2 : -0.2), 0.5, 5.0);
forceField->addParticle((i%2 == 0 ? 0.2 : -0.2), 0.5, 5.0);
}
for (int i = 0; i < numConstraints; ++i){
system.setConstraintParameters(i, 2*i, 2*i+1, 1.0);
......
......@@ -238,14 +238,27 @@ double CudaCalcRBTorsionForceKernel::executeEnergy(OpenMMContextImpl& context) {
CudaCalcNonbondedForceKernel::~CudaCalcNonbondedForceKernel() {
}
void CudaCalcNonbondedForceKernel::initialize(const System& system, const NonbondedForce& force, const std::vector<std::set<int> >& exclusions) {
void CudaCalcNonbondedForceKernel::initialize(const System& system, const NonbondedForce& force) {
if (data.primaryKernel == NULL)
data.primaryKernel = this;
data.hasNonbonded = true;
numParticles = force.getNumParticles();
num14 = force.getNumNonbonded14();
_gpuContext* gpu = data.gpu;
// Identify which exceptions are 1-4 interactions.
vector<pair<int, int> > exclusions;
vector<int> nb14s;
for (int i = 0; i < force.getNumExceptions(); i++) {
int particle1, particle2;
double chargeProd, sigma, epsilon;
force.getExceptionParameters(i, particle1, particle2, chargeProd, sigma, epsilon);
exclusions.push_back(pair<int, int>(particle1, particle2));
if (chargeProd != 0.0 || epsilon != 0.0)
nb14s.push_back(i);
}
num14 = nb14s.size();
// Initialize nonbonded interactions.
{
......@@ -262,9 +275,12 @@ void CudaCalcNonbondedForceKernel::initialize(const System& system, const Nonbon
q[i] = (float) charge;
c6[i] = (float) (4*depth*pow(radius, 6.0));
c12[i] = (float) (4*depth*pow(radius, 12.0));
exclusionList[i] = vector<int>(exclusions[i].begin(), exclusions[i].end());
exclusionList[i].push_back(i);
}
for (int i = 0; i < exclusions.size(); i++) {
exclusionList[exclusions[i].first].push_back(exclusions[i].second);
exclusionList[exclusions[i].second].push_back(exclusions[i].first);
}
CudaNonbondedMethod method = NO_CUTOFF;
if (force.getNonbondedMethod() != NonbondedForce::NoCutoff) {
gpuSetNonbondedCutoff(gpu, force.getCutoffDistance(), 78.3f);
......@@ -299,7 +315,7 @@ void CudaCalcNonbondedForceKernel::initialize(const System& system, const Nonbon
vector<float> q2(num14);
for (int i = 0; i < num14; i++) {
double charge, sig, eps;
force.getNonbonded14Parameters(i, particle1[i], particle2[i], charge, sig, eps);
force.getExceptionParameters(nb14s[i], particle1[i], particle2[i], charge, sig, eps);
c6[i] = (float) (4*eps*pow(sig, 6.0));
c12[i] = (float) (4*eps*pow(sig, 12.0));
q1[i] = (float) charge;
......
......@@ -215,11 +215,8 @@ public:
*
* @param system the System this kernel will be applied to
* @param force the NonbondedForce this kernel will be used for
* @param exclusions the i'th element lists the indices of all particles with which the i'th particle should not interact through
* nonbonded forces. Bonded 1-4 pairs are also included in this list, since they should be omitted from
* the standard nonbonded calculation.
*/
void initialize(const System& system, const NonbondedForce& force, const std::vector<std::set<int> >& exclusions);
void initialize(const System& system, const NonbondedForce& force);
/**
* Execute the kernel to calculate the forces.
*
......
......@@ -55,10 +55,10 @@ void testTemperature() {
CudaPlatform platform;
System system(numParticles, 0);
VerletIntegrator integrator(0.01);
NonbondedForce* forceField = new NonbondedForce(numParticles, 0);
NonbondedForce* forceField = new NonbondedForce();
for (int i = 0; i < numParticles; ++i) {
system.setParticleMass(i, 2.0);
forceField->setParticleParameters(i, (i%2 == 0 ? 1.0 : -1.0), 1.0, 5.0);
forceField->addParticle((i%2 == 0 ? 1.0 : -1.0), 1.0, 5.0);
}
system.addForce(forceField);
AndersenThermostat* thermstat = new AndersenThermostat(temp, collisionFreq);
......@@ -93,10 +93,10 @@ void testRandomSeed() {
CudaPlatform platform;
System system(numParticles, 0);
VerletIntegrator integrator(0.01);
NonbondedForce* forceField = new NonbondedForce(numParticles, 0);
NonbondedForce* forceField = new NonbondedForce();
for (int i = 0; i < numParticles; ++i) {
system.setParticleMass(i, 2.0);
forceField->setParticleParameters(i, (i%2 == 0 ? 1.0 : -1.0), 1.0, 5.0);
forceField->addParticle((i%2 == 0 ? 1.0 : -1.0), 1.0, 5.0);
}
system.addForce(forceField);
AndersenThermostat* thermostat = new AndersenThermostat(temp, collisionFreq);
......
......@@ -132,10 +132,10 @@ void testConstraints() {
System system(numParticles, numConstraints);
BrownianIntegrator integrator(temp, 2.0, 0.001);
integrator.setConstraintTolerance(1e-5);
NonbondedForce* forceField = new NonbondedForce(numParticles, 0);
NonbondedForce* forceField = new NonbondedForce();
for (int i = 0; i < numParticles; ++i) {
system.setParticleMass(i, 10.0);
forceField->setParticleParameters(i, (i%2 == 0 ? 0.2 : -0.2), 0.5, 5.0);
forceField->addParticle((i%2 == 0 ? 0.2 : -0.2), 0.5, 5.0);
}
system.setConstraintParameters(0, 0, 1, 1.0);
system.setConstraintParameters(1, 1, 2, 1.0);
......@@ -178,10 +178,10 @@ void testRandomSeed() {
CudaPlatform platform;
System system(numParticles, 0);
BrownianIntegrator integrator(temp, 2.0, 0.001);
NonbondedForce* forceField = new NonbondedForce(numParticles, 0);
NonbondedForce* forceField = new NonbondedForce();
for (int i = 0; i < numParticles; ++i) {
system.setParticleMass(i, 2.0);
forceField->setParticleParameters(i, (i%2 == 0 ? 1.0 : -1.0), 1.0, 5.0);
forceField->addParticle((i%2 == 0 ? 1.0 : -1.0), 1.0, 5.0);
}
system.addForce(forceField);
vector<Vec3> positions(numParticles);
......
......@@ -67,10 +67,10 @@ void testMotionRemoval(Integrator& integrator) {
HarmonicBondForce* bonds = new HarmonicBondForce(1);
bonds->setBondParameters(0, 2, 3, 2.0, 0.5);
system.addForce(bonds);
NonbondedForce* nonbonded = new NonbondedForce(numParticles, 0);
NonbondedForce* nonbonded = new NonbondedForce();
for (int i = 0; i < numParticles; ++i) {
system.setParticleMass(i, i+1);
nonbonded->setParticleParameters(i, (i%2 == 0 ? 1.0 : -1.0), 1.0, 5.0);
nonbonded->addParticle((i%2 == 0 ? 1.0 : -1.0), 1.0, 5.0);
}
system.addForce(nonbonded);
CMMotionRemover* remover = new CMMotionRemover();
......
......@@ -58,9 +58,9 @@ void testEwald() {
CudaPlatform platform;
System system(2, 0);
VerletIntegrator integrator(0.01);
NonbondedForce* nonbonded = new NonbondedForce(2, 0);
nonbonded->setParticleParameters(0, 1.0, 1, 0);
nonbonded->setParticleParameters(1, -1.0, 1, 0);
NonbondedForce* nonbonded = new NonbondedForce();
nonbonded->addParticle(1.0, 1, 0);
nonbonded->addParticle(-1.0, 1, 0);
nonbonded->setNonbondedMethod(NonbondedForce::Ewald);
const double cutoff = 2.0;
nonbonded->setCutoffDistance(cutoff);
......@@ -90,10 +90,10 @@ void testPeriodic() {
HarmonicBondForce* bonds = new HarmonicBondForce(1);
bonds->setBondParameters(0, 0, 1, 1, 0);
system.addForce(bonds);
NonbondedForce* nonbonded = new NonbondedForce(3, 0);
nonbonded->setParticleParameters(0, 1.0, 1, 0);
nonbonded->setParticleParameters(1, 1.0, 1, 0);
nonbonded->setParticleParameters(2, 1.0, 1, 0);
NonbondedForce* nonbonded = new NonbondedForce();
nonbonded->addParticle(1.0, 1, 0);
nonbonded->addParticle(1.0, 1, 0);
nonbonded->addParticle(1.0, 1, 0);
nonbonded->setNonbondedMethod(NonbondedForce::CutoffPeriodic);
const double cutoff = 2.0;
nonbonded->setCutoffDistance(cutoff);
......
......@@ -57,9 +57,9 @@ void testSingleParticle() {
system.setParticleMass(0, 2.0);
LangevinIntegrator integrator(0, 0.1, 0.01);
GBSAOBCForce* gbsa = new GBSAOBCForce(1);
NonbondedForce* nonbonded = new NonbondedForce(1, 0);
NonbondedForce* nonbonded = new NonbondedForce();
gbsa->setParticleParameters(0, 0.5, 0.15, 1);
nonbonded->setParticleParameters(0, 0.5, 1, 0);
nonbonded->addParticle(0.5, 1, 0);
system.addForce(gbsa);
system.addForce(nonbonded);
OpenMMContext context(system, integrator, platform);
......@@ -80,11 +80,11 @@ void testCutoffAndPeriodic() {
System system(2, 0);
LangevinIntegrator integrator(0, 0.1, 0.01);
GBSAOBCForce* gbsa = new GBSAOBCForce(2);
NonbondedForce* nonbonded = new NonbondedForce(2, 0);
NonbondedForce* nonbonded = new NonbondedForce();
gbsa->setParticleParameters(0, -1, 0.15, 1);
nonbonded->setParticleParameters(0, -1, 1, 0);
nonbonded->addParticle(-1, 1, 0);
gbsa->setParticleParameters(1, 1, 0.15, 1);
nonbonded->setParticleParameters(1, 1, 1, 0);
nonbonded->addParticle(1, 1, 0);
const double cutoffDistance = 3.0;
const double boxSize = 10.0;
nonbonded->setCutoffDistance(cutoffDistance);
......@@ -131,11 +131,11 @@ void testForce(int numParticles, NonbondedForce::NonbondedMethod method) {
System system(numParticles, 0);
LangevinIntegrator integrator(0, 0.1, 0.01);
GBSAOBCForce* gbsa = new GBSAOBCForce(numParticles);
NonbondedForce* nonbonded = new NonbondedForce(numParticles, 0);
NonbondedForce* nonbonded = new NonbondedForce();
for (int i = 0; i < numParticles; ++i) {
double charge = i%2 == 0 ? -1 : 1;
gbsa->setParticleParameters(i, charge, 0.15, 1);
nonbonded->setParticleParameters(i, charge, 1, 0);
nonbonded->addParticle(charge, 1, 0);
}
nonbonded->setNonbondedMethod(method);
nonbonded->setCutoffDistance(3.0);
......
......@@ -100,10 +100,10 @@ void testTemperature() {
CudaPlatform platform;
System system(numParticles, 0);
LangevinIntegrator integrator(temp, 2.0, 0.01);
NonbondedForce* forceField = new NonbondedForce(numParticles, 0);
NonbondedForce* forceField = new NonbondedForce();
for (int i = 0; i < numParticles; ++i) {
system.setParticleMass(i, 2.0);
forceField->setParticleParameters(i, (i%2 == 0 ? 1.0 : -1.0), 1.0, 5.0);
forceField->addParticle((i%2 == 0 ? 1.0 : -1.0), 1.0, 5.0);
}
system.addForce(forceField);
OpenMMContext context(system, integrator, platform);
......@@ -137,10 +137,10 @@ void testConstraints() {
System system(numParticles, numConstraints);
LangevinIntegrator integrator(temp, 2.0, 0.01);
integrator.setConstraintTolerance(1e-5);
NonbondedForce* forceField = new NonbondedForce(numParticles, 0);
NonbondedForce* forceField = new NonbondedForce();
for (int i = 0; i < numParticles; ++i) {
system.setParticleMass(i, 10.0);
forceField->setParticleParameters(i, (i%2 == 0 ? 0.2 : -0.2), 0.5, 5.0);
forceField->addParticle((i%2 == 0 ? 0.2 : -0.2), 0.5, 5.0);
}
system.setConstraintParameters(0, 0, 1, 1.0);
system.setConstraintParameters(1, 1, 2, 1.0);
......@@ -183,10 +183,10 @@ void testRandomSeed() {
CudaPlatform platform;
System system(numParticles, 0);
LangevinIntegrator integrator(temp, 2.0, 0.01);
NonbondedForce* forceField = new NonbondedForce(numParticles, 0);
NonbondedForce* forceField = new NonbondedForce();
for (int i = 0; i < numParticles; ++i) {
system.setParticleMass(i, 2.0);
forceField->setParticleParameters(i, (i%2 == 0 ? 1.0 : -1.0), 1.0, 5.0);
forceField->addParticle((i%2 == 0 ? 1.0 : -1.0), 1.0, 5.0);
}
system.addForce(forceField);
vector<Vec3> positions(numParticles);
......
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