Commit 604881dc authored by peastman's avatar peastman
Browse files

Merge pull request #604 from peastman/many

Created CustomManyParticleForce
parents f2eb95d0 1515e2bc
......@@ -2400,6 +2400,61 @@ must include an attribute called :code:`radius`\ .
CustomGBForce also allows you to define tabulated functions. See section
:ref:`tabulated-functions` for details.
<CustomManyParticleForce>
=========================
To add a CustomManyParticleForce to the System, include a tag that looks like this:
.. code-block:: xml
<CustomManyParticleForce particlesPerSet="3" permutationMode="UniqueCentralParticle"
bondCutoff="3" energy="scale*(distance(p1,p2)-r1)*(distance(p1,p3)-r1)">
<GlobalParameter name="scale" defaultValue="1"/>
<PerParticleParameter name="r"/>
<TypeFilter index="0" types="1,2"/>
<Atom type="0" r="0.31" filterType="0"/>
<Atom type="1" r="0.25" filterType="0"/>
<Atom type="2" r="0.33" filterType="1"/>
...
</CustomManyParticleForce>
The energy expression for the CustomManyParticleForce is specified by the
:code:`energy` attribute. This is a mathematical expression that gives the
energy of each pairwise interaction as a function of various particle coordinates,
distances, and angles. See the API documentation for details. :code:`particlesPerSet`
specifies the number of particles involved in the interaction and
:code:`permutationMode` specifies the permutation mode.
The expression may depend on an arbitrary list of global or per-atom
parameters. Use a :code:`<GlobalParameter>` tag to define a global
parameter, and a :code:`<PerAtomParameter>` tag to define a per-atom
parameter.
Exclusions are created automatically based on the :code:`bondCutoff` attribute.
After setting the nonbonded parameters for all atoms, the force field calls
:code:`createExclusionsFromBonds()` on the CustomManyParticleForce, passing in this
value as its argument. To avoid creating exclusions, set :code:`bondCutoff` to 0.
Type filters may be specified with a :code:`<TypeFilter>` tag. The :code:`index`
attribute specifies the index of the particle to apply the filter to, and
:code:`types` is a comma separated list of allowed types.
Each :code:`<Atom>` tag specifies the parameters for one atom type
(specified with the :code:`type` attribute) or atom class (specified with
the :code:`class` attribute). It is fine to mix these two methods, having
some tags specify a type and others specify a class. However you do it, you
must make sure that a unique set of parameters is defined for every atom type.
In addition, each :code:`<Atom>` tag must include the :code:`filterType`
attribute, which specifies the atom type for use in type filters.
The remaining attributes are the values to use for the per-atom parameters. All
per-atom parameters must be specified for every :code:`<Atom>` tag, and the
attribute name must match the name of the parameter. For instance, if there is
a per-atom parameter with the name radius, then every :code:`<Atom>` tag
must include an attribute called :code:`radius`\ .
CustomManyParticleForce also allows you to define tabulated functions. See section
:ref:`tabulated-functions` for details.
Writing Custom Expressions
==========================
......
......@@ -828,6 +828,54 @@ Parameters may be specified in two ways:
* Per-bond parameters are defined by specifying a value for each bond.
CustomManyParticleForce
***********************
CustomManyParticleForce is similar to CustomNonbondedForce in that it represents
a custom nonbonded interaction between particles, but it allows the interaction
to depend on more than two particles. This allows it to represent a wide range
of non-pairwise interactions. It is defined by specifying the number of
particles :math:`N` involved in the interaction and how the energy depends on
their positions. More specifically, it takes a user specified energy function
.. math::
E=f(\{x_i\},\{r_i\},\{\theta_i\},\{\phi_i\})
that may depend on an arbitrary set of positions {\ :math:`x_i`\ }, distances
{\ :math:`r_i`\ }, angles {\ :math:`\theta_i`\ }, and dihedral angles
{\ :math:`\phi_i`\ } from a particular set of :math:`N` particles.
Each distance, angle, or dihedral is defined by specifying a sequence of
particles chosen from among the particles in the set. A distance
variable is defined by two particles, and equals the distance between them. An
angle variable is defined by three particles, and equals the angle between them.
A dihedral variable is defined by four particles, and equals the angle between
the first and last particles about the axis formed by the middle two particles.
It is equal to zero when the first and last particles are on the same side of
the axis.
In addition to depending on positions, distances, angles, and dihedrals, the
energy may also depend on an arbitrary set of user defined parameters.
Parameters may be specified in two ways:
* Global parameters have a single, fixed value.
* Per-particle parameters are defined by specifying a value for each particle.
The energy function is evaluated one or more times for every unique set of
:math:`N` particles in the system. The exact number of times depends on the
*permutation mode*\ . A set of :math:`N` particles has :math:`N!` possible
permutations. In :code:`SinglePermutation` mode, the function is evaluated
for a single arbitrarily chosen one of those permutations. In
:code:`UniqueCentralParticle` mode, the function is evaluated for :math:`N` of
those permutations, once with each particle as the "central particle".
The number of times the energy function is evaluated can be further restricted
by specifying *type filters*\ . Each particle may have a "type" assigned to it,
and then each of the :math:`N` particles involved in an interaction may be
restricted to only a specified set of types. This provides a great deal of
flexibility in controlling which particles interact with each other.
CustomGBForce
*************
......
......@@ -44,6 +44,7 @@
#include "openmm/CustomHbondForce.h"
#include "openmm/CustomIntegrator.h"
#include "openmm/CustomNonbondedForce.h"
#include "openmm/CustomManyParticleForce.h"
#include "openmm/CustomTorsionForce.h"
#include "openmm/GBSAOBCForce.h"
#include "openmm/GBVIForce.h"
......@@ -826,6 +827,46 @@ public:
virtual void copyParametersToContext(ContextImpl& context, const CustomCompoundBondForce& force) = 0;
};
/**
* This kernel is invoked by CustomManyParticleForce to calculate the forces acting on the system and the energy of the system.
*/
class CalcCustomManyParticleForceKernel : public KernelImpl {
public:
enum NonbondedMethod {
NoCutoff = 0,
CutoffNonPeriodic = 1,
CutoffPeriodic = 2
};
static std::string Name() {
return "CalcCustomManyParticleForce";
}
CalcCustomManyParticleForceKernel(std::string name, const Platform& platform) : KernelImpl(name, platform) {
}
/**
* Initialize the kernel.
*
* @param system the System this kernel will be applied to
* @param force the CustomManyParticleForce this kernel will be used for
*/
virtual void initialize(const System& system, const CustomManyParticleForce& force) = 0;
/**
* Execute the kernel to calculate the forces and/or energy.
*
* @param context the context in which to execute this kernel
* @param includeForces true if forces should be calculated
* @param includeEnergy true if the energy should be calculated
* @return the potential energy due to the force
*/
virtual double execute(ContextImpl& context, bool includeForces, bool includeEnergy) = 0;
/**
* Copy changed parameters over to a context.
*
* @param context the context to copy parameters to
* @param force the CustomManyParticleForce to copy the parameters from
*/
virtual void copyParametersToContext(ContextImpl& context, const CustomManyParticleForce& force) = 0;
};
/**
* This kernel is invoked by VerletIntegrator to take one time step.
*/
......
......@@ -44,6 +44,7 @@
#include "openmm/CustomGBForce.h"
#include "openmm/CustomHbondForce.h"
#include "openmm/CustomIntegrator.h"
#include "openmm/CustomManyParticleForce.h"
#include "openmm/CustomNonbondedForce.h"
#include "openmm/Force.h"
#include "openmm/GBSAOBCForce.h"
......
#ifndef OPENMM_CUSTOMTHREEBODYFORCE_H_
#define OPENMM_CUSTOMTHREEBODYFORCE_H_
/* -------------------------------------------------------------------------- *
* OpenMM *
* -------------------------------------------------------------------------- *
* This is part of the OpenMM molecular simulation toolkit originating from *
* Simbios, the NIH National Center for Physics-Based Simulation of *
* Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org. *
* *
* Portions copyright (c) 2008-2014 Stanford University and the Authors. *
* Authors: Peter Eastman *
* Contributors: *
* *
* Permission is hereby granted, free of charge, to any person obtaining a *
* copy of this software and associated documentation files (the "Software"), *
* to deal in the Software without restriction, including without limitation *
* the rights to use, copy, modify, merge, publish, distribute, sublicense, *
* and/or sell copies of the Software, and to permit persons to whom the *
* Software is furnished to do so, subject to the following conditions: *
* *
* The above copyright notice and this permission notice shall be included in *
* all copies or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
* THE AUTHORS, CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, *
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR *
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE *
* USE OR OTHER DEALINGS IN THE SOFTWARE. *
* -------------------------------------------------------------------------- */
#include "Force.h"
#include "TabulatedFunction.h"
#include "internal/windowsExport.h"
#include <set>
#include <string>
#include <vector>
namespace OpenMM {
/**
* This class supports a wide variety of nonbonded N-particle interactions, where N is user specified. The
* interaction energy is determined by an arbitrary, user specified algebraic expression that is evaluated for
* every possible set of N particles in the system. It may depend on the positions of the individual particles,
* the distances between pairs of particles, the angles formed by sets of three particles, and the dihedral
* angles formed by sets of four particles.
*
* Be aware that the cost of evaluating an N-particle interaction increases very rapidly with N. Values larger
* than N=3 are rarely used.
*
* We refer to a set of particles for which the energy is being evaluated as p1, p2, p3, etc. The energy expression
* may depend on the following variables and functions:
*
* <ul>
* <li>x1, y1, z1, x2, y2, z2, etc.: The x, y, and z coordinates of the particle positions. For example, x1
* is the x coordinate of particle p1, and y3 is the y coordinate of particle p3.</li>
* <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>arbitrary global and per-particle parameters that you define.</li>
* </ul>
*
* To use this class, create a CustomManyParticleForce object, passing an algebraic expression to the constructor
* that defines the interaction energy of each set of particles. Then call addPerParticleParameter() to define per-particle
* parameters, and addGlobalParameter() to define global parameters. The values of per-particle parameters are specified as
* part of the system definition, while values of global parameters may be modified during a simulation by calling Context::setParameter().
*
* Next, call addParticle() once for each particle in the System to set the values of its per-particle parameters.
* The number of particles for which you set 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 a Context. After a particle has been added,
* you can modify its parameters by calling setParticleParameters(). This will have no effect on Contexts that already exist
* unless you call updateParametersInContext().
*
* Multi-particle interactions can be very expensive to evaluate, so they are usually used with a cutoff distance. The exact
* interpretation of the cutoff depends on the permutation mode, as discussed below.
*
* CustomManyParticleForce also lets you specify "exclusions", particular pairs of particles whose interactions should be
* omitted from force and energy calculations. This is most often used for particles that are bonded to each other.
* If you specify a pair of particles as an exclusion, <i>all</i> sets that include those two particles will be omitted.
*
* As an example, the following code creates a CustomManyParticleForce that implements an Axilrod-Teller potential. This
* is an interaction between three particles that depends on all three distances and angles formed by the particles.
*
* <tt><pre>CustomManyParticleForce* force = new CustomManyParticleForce(3,
* "C*(1+3*cos(theta1)*cos(theta2)*cos(theta3))/(r12*r13*r23)^3;"
* "theta1=angle(p1,p2,p3); theta2=angle(p2,p3,p1); theta3=angle(p3,p1,p2);"
* "r12=distance(p1,p2); r13=distance(p1,p3); r23=distance(p2,p3)");
* force->setPermutationMode(CustomManyParticleForce::SinglePermutation);
* </pre></tt>
*
* This force depends on one parameter, C. The following code defines it as a global parameter:
*
* <tt><pre>
* force->addGlobalParameter("C", 1.0);
* </pre></tt>
*
* Notice that the expression is symmetric with respect to the particles. It only depends on the products
* cos(theta1)*cos(theta2)*cos(theta3) and r12*r13*r23, both of which are unchanged if the labels p1, p2, and p3 are permuted.
* This is required because we specified SinglePermutation as the permutation mode. (This is the default, so we did not
* really need to set it, but doing so makes the example clearer.) In this mode, the expression is only evaluated once for
* each set of particles. No guarantee is made about which particle will be identified as p1, p2, etc. Therefore, the
* energy <i>must</i> be symmetric with respect to exchange of particles. Otherwise, the results would be undefined because
* permuting the labels would change the energy.
*
* Not all many-particle interactions work this way. Another common pattern is for the expression to describe an interaction
* between one central particle and other nearby particles. An example of this is the 3-particle piece of the Stillinger-Weber
* potential:
*
* <tt><pre>CustomManyParticleForce* force = new CustomManyParticleForce(3,
* "L*eps*(cos(theta1)+1/3)^2*exp(sigma*gamma/(r12-a*sigma))*exp(sigma*gamma/(r13-a*sigma));"
"r12 = distance(p1,p2); r13 = distance(p1,p3); theta1 = angle(p3,p1,p2)");
* force->setPermutationMode(CustomManyParticleForce::UniqueCentralParticle);
* </pre></tt>
*
* When the permutation mode is set to UniqueCentralParticle, particle p1 is treated as the central particle. For a set of
* N particles, the expression is evaluated N times, once with each particle as p1. The expression can therefore treat
* p1 differently from the other particles. Notice that it is still symmetric with respect to p2 and p3, however. There
* is no guarantee about how those labels will be assigned to particles.
*
* Distance cutoffs are applied in different ways depending on the permutation mode. In SinglePermutation mode, every particle
* in the set must be within the cutoff distance of every other particle. If <i>any</i> two particles are further apart than
* the cutoff distance, the interaction is skipped. In UniqueCentralParticle mode, each particle must be within the cutoff
* distance of the central particle, but not necessarily of all the other particles. The cutoff may therefore exclude a subset
* of the permutations of a set of particles.
*
* Another common situation is that some particles are fundamentally different from others, causing the expression to be
* inherently non-symmetric. An example would be a water model that involves three particles, two of which <i>must</i> be
* hydrogen and one of which <i>must</i> be oxygen. Cases like this can be implemented using particle types.
*
* A particle type is an integer that you specify when you call addParticle(). (If you omit the argument, it defaults
* to 0.) For the water model, you could specify 0 for all oxygen atoms and 1 for all hydrogen atoms. You can then
* call setTypeFilter() to specify the list of allowed types for each of the N particles involved in an interaction:
*
* <tt><pre>
* set&lt;int&gt; oxygenTypes, hydrogenTypes;
* oxygenTypes.insert(0);
* hydrogenTypes.insert(1);
* force->setTypeFilter(0, oxygenTypes);
* force->setTypeFilter(1, hydrogenTypes);
* force->setTypeFilter(2, hydrogenTypes);
* </pre></tt>
*
* This specifies that of the three particles in an interaction, p1 must be oxygen while p2 and p3 must be hydrogen.
* The energy expression will only be evaluated for triplets of particles that satisfy those requirements. It will
* still only be evaluated once for each triplet, so it must still be symmetric with respect to p2 and p3.
*
* Expressions may involve the operators + (add), - (subtract), * (multiply), / (divide), and ^ (power), and the following
* functions: sqrt, exp, log, sin, cos, sec, csc, tan, cot, asin, acos, atan, sinh, cosh, tanh, erf, erfc, min, max, abs, step, delta. All trigonometric functions
* are defined in radians, and log is the natural logarithm. step(x) = 0 if x is less than 0, 1 otherwise. delta(x) = 1 if x is 0, 0 otherwise.
* The names of per-particle parameters have the suffix "1", "2", etc. appended to them to indicate the values for the multiple interacting particles.
* For example, if you define a per-particle parameter called "charge", then the variable "charge2" is the charge of particle p2.
* As seen above, the expression may also involve intermediate quantities that are defined following the main expression, using ";" as a separator.
*
* In addition, you can call addTabulatedFunction() to define a new function based on tabulated values. You specify the function by
* creating a TabulatedFunction object. That function can then appear in the expression.
*/
class OPENMM_EXPORT CustomManyParticleForce : public Force {
public:
/**
* This is an enumeration of the different methods that may be used for handling long range nonbonded forces.
*/
enum NonbondedMethod {
/**
* No cutoff is applied to nonbonded interactions. The full set of interactions is computed exactly.
* This necessarily means that periodic boundary conditions cannot be used. This is the default.
*/
NoCutoff = 0,
/**
* Interactions are ignored if any two particles are further apart than the cutoff distance.
*/
CutoffNonPeriodic = 1,
/**
* Periodic boundary conditions are used, so that each particle interacts only with the nearest periodic copy of
* each other particle. Interactions are ignored if any two particles are further apart than the cutoff distance.
*/
CutoffPeriodic = 2,
};
/**
* This is an enumeration of the different modes for selecting which permutations of a set of particles to evaluate the
* interaction for.
*/
enum PermutationMode {
/**
* For any set of particles, the interaction is evaluated only once for a single permutation of the particles.
* There is no guarantee about which permutation will be used (aside from the requirement to satisfy type filters),
* so the expression must be symmetric. If cutoffs are used, then every particle in the set must be within the
* cutoff distance of every other particle.
*/
SinglePermutation = 0,
/**
* The interaction is treated as an interaction between one central particle (p1) and various other nearby particles
* (p2, p3, ...). For a set of N particles it will be evaluated N times, once with each particle as p1. The expression
* must be symmetric with respect to the other particles, but may treat p1 differently. If cutoffs are used, then
* every particle must be within the cutoff distance of p1.
*/
UniqueCentralParticle = 1
};
/**
* Create a CustomManyParticleForce.
*
* @param particlesPerSet the number of particles in each set for which the energy is evaluated
* @param energy an algebraic expression giving the interaction energy of each triplet as a function
* of particle positions, inter-particle distances, angles, and any global and per-particle parameters
*/
explicit CustomManyParticleForce(int particlesPerSet, const std::string& energy);
~CustomManyParticleForce();
/**
* Get the number of particles in each set for which the energy is evaluated
*/
int getNumParticlesPerSet() const {
return particlesPerSet;
}
/**
* Get the number of particles for which force field parameters have been defined.
*/
int getNumParticles() const {
return particles.size();
}
/**
* Get the number of particle pairs whose interactions should be excluded.
*/
int getNumExclusions() const {
return exclusions.size();
}
/**
* Get the number of per-particle parameters that the interaction depends on.
*/
int getNumPerParticleParameters() const {
return particleParameters.size();
}
/**
* Get the number of global parameters that the interaction depends on.
*/
int getNumGlobalParameters() const {
return globalParameters.size();
}
/**
* Get the number of tabulated functions that have been defined.
*/
int getNumTabulatedFunctions() const {
return functions.size();
}
/**
* Get the algebraic expression that gives the interaction energy of each bond
*/
const std::string& getEnergyFunction() const;
/**
* Set the algebraic expression that gives the interaction energy of each bond
*/
void setEnergyFunction(const std::string& energy);
/**
* Get the method used for handling long range nonbonded interactions.
*/
NonbondedMethod getNonbondedMethod() const;
/**
* Set the method used for handling long range nonbonded interactions.
*/
void setNonbondedMethod(NonbondedMethod method);
/**
* Get the mode that selects which permutations of a set of particles to evaluate the interaction for.
*/
PermutationMode getPermutationMode() const;
/**
* Set the mode that selects which permutations of a set of particles to evaluate the interaction for.
*/
void setPermutationMode(PermutationMode mode);
/**
* Get the cutoff distance (in nm) being used for nonbonded interactions. If the NonbondedMethod in use
* is NoCutoff, this value will have no effect.
*
* @return the cutoff distance, measured in nm
*/
double getCutoffDistance() const;
/**
* Set the cutoff distance (in nm) being used for nonbonded interactions. If the NonbondedMethod in use
* is NoCutoff, this value will have no effect.
*
* @param distance the cutoff distance, measured in nm
*/
void setCutoffDistance(double distance);
/**
* Add a new per-particle parameter that the interaction may depend on.
*
* @param name the name of the parameter
* @return the index of the parameter that was added
*/
int addPerParticleParameter(const std::string& name);
/**
* Get the name of a per-particle parameter.
*
* @param index the index of the parameter for which to get the name
* @return the parameter name
*/
const std::string& getPerParticleParameterName(int index) const;
/**
* Set the name of a per-particle parameter.
*
* @param index the index of the parameter for which to set the name
* @param name the name of the parameter
*/
void setPerParticleParameterName(int index, const std::string& name);
/**
* Add a new global parameter that the interaction may depend on.
*
* @param name the name of the parameter
* @param defaultValue the default value of the parameter
* @return the index of the parameter that was added
*/
int addGlobalParameter(const std::string& name, double defaultValue);
/**
* Get the name of a global parameter.
*
* @param index the index of the parameter for which to get the name
* @return the parameter name
*/
const std::string& getGlobalParameterName(int index) const;
/**
* Set the name of a global parameter.
*
* @param index the index of the parameter for which to set the name
* @param name the name of the parameter
*/
void setGlobalParameterName(int index, const std::string& name);
/**
* Get the default value of a global parameter.
*
* @param index the index of the parameter for which to get the default value
* @return the parameter default value
*/
double getGlobalParameterDefaultValue(int index) const;
/**
* Set the default value of a global parameter.
*
* @param index the index of the parameter for which to set the default value
* @param name the default value of the parameter
*/
void setGlobalParameterDefaultValue(int index, double defaultValue);
/**
* 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.
*
* @param parameters the list of parameters for the new particle
* @param type the type of the new particle
* @return the index of the particle that was added
*/
int addParticle(const std::vector<double>& parameters, int type=0);
/**
* Get the nonbonded force parameters for a particle.
*
* @param index the index of the particle for which to get parameters
* @param parameters the list of parameters for the specified particle
* @param type the type of the specified particle
*/
void getParticleParameters(int index, std::vector<double>& parameters, int& type) const;
/**
* Set the nonbonded force parameters for a particle.
*
* @param index the index of the particle for which to set parameters
* @param parameters the list of parameters for the specified particle
* @param type the type of the specified particle
*/
void setParticleParameters(int index, const std::vector<double>& parameters, int type);
/**
* Add a particle pair to the list of interactions that should be excluded.
*
* In many cases, you can use createExclusionsFromBonds() rather than adding each exclusion explicitly.
*
* @param particle1 the index of the first particle in the pair
* @param particle2 the index of the second particle in the pair
* @return the index of the exclusion that was added
*/
int addExclusion(int particle1, int particle2);
/**
* Get the particles in a pair whose interaction should be excluded.
*
* @param index the index of the exclusion for which to get particle indices
* @param particle1 the index of the first particle in the pair
* @param particle2 the index of the second particle in the pair
*/
void getExclusionParticles(int index, int& particle1, int& particle2) const;
/**
* Set the particles in a pair whose interaction should be excluded.
*
* @param index the index of the exclusion for which to set particle indices
* @param particle1 the index of the first particle in the pair
* @param particle2 the index of the second particle in the pair
*/
void setExclusionParticles(int index, int particle1, int particle2);
/**
* Identify exclusions based on the molecular topology. Particles which are separated by up to a specified number of
* bonds are added as exclusions.
*
* @param bonds the set of bonds based on which to construct exclusions. Each element specifies the indices of
* two particles that are bonded to each other.
* @param bondCutoff pairs of particles that are separated by this many bonds or fewer are added to the list of exclusions
*/
void createExclusionsFromBonds(const std::vector<std::pair<int, int> >& bonds, int bondCutoff);
/**
* Get the allowed particle types for one of the particles involved in the interaction.
* If this an empty set (the default), no filter is applied and all interactions are evaluated
* regardless of the type of the specified particle.
*
* @param index the index of the particle within the interaction (between 0 and getNumParticlesPerSet())
* @param types the allowed types for the specified particle
*/
void getTypeFilter(int index, std::set<int>& types) const;
/**
* Set the allowed particle types for one of the particles involved in the interaction.
* If this an empty set (the default), no filter is applied and all interactions are evaluated
* regardless of the type of the specified particle.
*
* @param index the index of the particle within the interaction (between 0 and getNumParticlesPerSet())
* @param types the allowed types for the specified particle
*/
void setTypeFilter(int index, const std::set<int>& types);
/**
* Add a tabulated function that may appear in the energy expression.
*
* @param name the name of the function as it appears in expressions
* @param function a TabulatedFunction object defining the function. The TabulatedFunction
* should have been created on the heap with the "new" operator. The
* Force takes over ownership of it, and deletes it when the Force itself is deleted.
* @return the index of the function that was added
*/
int addTabulatedFunction(const std::string& name, TabulatedFunction* function);
/**
* Get a const reference to a tabulated function that may appear in the energy expression.
*
* @param index the index of the function to get
* @return the TabulatedFunction object defining the function
*/
const TabulatedFunction& getTabulatedFunction(int index) const;
/**
* Get a reference to a tabulated function that may appear in the energy expression.
*
* @param index the index of the function to get
* @return the TabulatedFunction object defining the function
*/
TabulatedFunction& getTabulatedFunction(int index);
/**
* Get the name of a tabulated function that may appear in the energy expression.
*
* @param index the index of the function to get
* @return the name of the function as it appears in expressions
*/
const std::string& getTabulatedFunctionName(int index) const;
/**
* Update the per-particle parameters in a Context to match those stored in this Force object. This method provides
* an efficient method to update certain parameters in an existing Context without needing to reinitialize it.
* Simply call setParticleParameters() to modify this object's parameters, then call updateParametersInContext()
* to copy them over to the Context.
*
* This method has several limitations. The only information it updates is the values of per-particle parameters.
* All other aspects of the Force (the energy function, nonbonded method, cutoff distance, etc.) are unaffected and can
* only be changed by reinitializing the Context. Also, this method cannot be used to add new particles, only to change
* the parameters of existing ones.
*/
void updateParametersInContext(Context& context);
protected:
ForceImpl* createImpl() const;
private:
class ParticleInfo;
class ParticleParameterInfo;
class GlobalParameterInfo;
class ExclusionInfo;
class FunctionInfo;
int particlesPerSet;
NonbondedMethod nonbondedMethod;
PermutationMode permutationMode;
double cutoffDistance;
std::string energyExpression;
std::vector<ParticleParameterInfo> particleParameters;
std::vector<GlobalParameterInfo> globalParameters;
std::vector<ParticleInfo> particles;
std::vector<ExclusionInfo> exclusions;
std::vector<FunctionInfo> functions;
std::vector<std::set<int> > typeFilters;
};
/**
* This is an internal class used to record information about a particle.
* @private
*/
class CustomManyParticleForce::ParticleInfo {
public:
std::vector<double> parameters;
int type;
ParticleInfo() {
}
ParticleInfo(const std::vector<double>& parameters, int type) : parameters(parameters), type(type) {
}
};
/**
* This is an internal class used to record information about a per-particle parameter.
* @private
*/
class CustomManyParticleForce::ParticleParameterInfo {
public:
std::string name;
ParticleParameterInfo() {
}
ParticleParameterInfo(const std::string& name) : name(name) {
}
};
/**
* This is an internal class used to record information about a global parameter.
* @private
*/
class CustomManyParticleForce::GlobalParameterInfo {
public:
std::string name;
double defaultValue;
GlobalParameterInfo() {
}
GlobalParameterInfo(const std::string& name, double defaultValue) : name(name), defaultValue(defaultValue) {
}
};
/**
* This is an internal class used to record information about an exclusion.
* @private
*/
class CustomManyParticleForce::ExclusionInfo {
public:
int particle1, particle2;
ExclusionInfo() {
particle1 = particle2 = -1;
}
ExclusionInfo(int particle1, int particle2) :
particle1(particle1), particle2(particle2) {
}
};
/**
* This is an internal class used to record information about a tabulated function.
* @private
*/
class CustomManyParticleForce::FunctionInfo {
public:
std::string name;
TabulatedFunction* function;
FunctionInfo() {
}
FunctionInfo(const std::string& name, TabulatedFunction* function) : name(name), function(function) {
}
};
} // namespace OpenMM
#endif /*OPENMM_CUSTOMTHREEBODYFORCE_H_*/
#ifndef OPENMM_CUSTOMMANYPARTICLEFORCEIMPL_H_
#define OPENMM_CUSTOMMANYPARTICLEFORCEIMPL_H_
/* -------------------------------------------------------------------------- *
* OpenMM *
* -------------------------------------------------------------------------- *
* This is part of the OpenMM molecular simulation toolkit originating from *
* Simbios, the NIH National Center for Physics-Based Simulation of *
* Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org. *
* *
* Portions copyright (c) 2008-2014 Stanford University and the Authors. *
* Authors: Peter Eastman *
* Contributors: *
* *
* Permission is hereby granted, free of charge, to any person obtaining a *
* copy of this software and associated documentation files (the "Software"), *
* to deal in the Software without restriction, including without limitation *
* the rights to use, copy, modify, merge, publish, distribute, sublicense, *
* and/or sell copies of the Software, and to permit persons to whom the *
* Software is furnished to do so, subject to the following conditions: *
* *
* The above copyright notice and this permission notice shall be included in *
* all copies or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
* THE AUTHORS, CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, *
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR *
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE *
* USE OR OTHER DEALINGS IN THE SOFTWARE. *
* -------------------------------------------------------------------------- */
#include "ForceImpl.h"
#include "openmm/CustomManyParticleForce.h"
#include "openmm/Kernel.h"
#include "lepton/CustomFunction.h"
#include "lepton/ExpressionTreeNode.h"
#include "lepton/ParsedExpression.h"
#include <utility>
#include <map>
#include <string>
namespace OpenMM {
/**
* This is the internal implementation of CustomManyParticleForce.
*/
class OPENMM_EXPORT CustomManyParticleForceImpl : public ForceImpl {
public:
CustomManyParticleForceImpl(const CustomManyParticleForce& owner);
~CustomManyParticleForceImpl();
void initialize(ContextImpl& context);
const CustomManyParticleForce& getOwner() const {
return owner;
}
void updateContextState(ContextImpl& context) {
// This force field doesn't update the state directly.
}
double calcForcesAndEnergy(ContextImpl& context, bool includeForces, bool includeEnergy, int groups);
std::map<std::string, double> getDefaultParameters();
std::vector<std::string> getKernelNames();
void updateParametersInContext(ContextImpl& context);
/**
* This is a utility routine that parses the energy expression, identifies the angles and dihedrals
* in it, and replaces them with variables.
*
* @param force the CustomManyParticleForce to process
* @param functions definitions of custom function that may appear in the expression
* @param distances on exit, this will contain an entry for each distance used in the expression. The key is the name
* of the corresponding variable, and the value is the list of particle indices.
* @param angles on exit, this will contain an entry for each angle used in the expression. The key is the name
* of the corresponding variable, and the value is the list of particle indices.
* @param dihedrals on exit, this will contain an entry for each dihedral used in the expression. The key is the name
* of the corresponding variable, and the value is the list of particle indices.
* @return a Parsed expression for the energy
*/
static Lepton::ParsedExpression prepareExpression(const CustomManyParticleForce& force, const std::map<std::string, Lepton::CustomFunction*>& functions, std::map<std::string, std::vector<int> >& distances,
std::map<std::string, std::vector<int> >& angles, std::map<std::string, std::vector<int> >& dihedrals);
/**
* Analyze the type filters for a force and build a set of arrays that can be used for reordering the
* particles in an interaction.
*
* @param force the CustomManyParticleForce to process
* @param numTypes on exit, the number of unique particle types
* @param particleTypes on exit, this contains a type code for each particle. These codes are <i>not</i> necessarily the
* same as the types assigned by the force. They are guaranteed to be successive integers starting from 0,
* whereas the force may have used arbitrary integers.
* @param orderIndex on exit, this contains a lookup table for selecting the particle order for an interaction.
* orderIndex[t1+numTypes*t2+numTypes*numTypes*t3+...] is the index of the order to use, where t1, t2, etc. are the type codes
* of the particles involved in the interaction. If this equals -1, the interaction should be omitted.
* @param particleOrder on exit, particleOrder[i][j] tells which particle to use as the j'th particle, where i is the value found in orderIndex.
*/
static void buildFilterArrays(const CustomManyParticleForce& force, int& numTypes, std::vector<int>& particleTypes, std::vector<int>& orderIndex, std::vector<std::vector<int> >& particleOrder);
private:
class FunctionPlaceholder;
static Lepton::ExpressionTreeNode replaceFunctions(const Lepton::ExpressionTreeNode& node, std::map<std::string, int> atoms,
std::map<std::string, std::vector<int> >& distances, std::map<std::string, std::vector<int> >& angles,
std::map<std::string, std::vector<int> >& dihedrals);
static void generatePermutations(std::vector<int>& values, int numFixed, std::vector<std::vector<int> >& result);
const CustomManyParticleForce& owner;
Kernel kernel;
};
} // namespace OpenMM
#endif /*OPENMM_CUSTOMMANYPARTICLEFORCEIMPL_H_*/
......@@ -268,6 +268,12 @@ static inline float dot4(const fvec4& v1, const fvec4& v2) {
return vgetq_lane_f32(result, 0) + vgetq_lane_f32(result, 1) + vgetq_lane_f32(result, 2) + vgetq_lane_f32(result,3);
}
static inline fvec4 cross(const fvec4& v1, const fvec4& v2) {
return fvec4(v1[1]*v2[2] - v1[2]*v2[1],
v1[2]*v2[0] - v1[0]*v2[2],
v1[0]*v2[1] - v1[1]*v2[0], 0);
}
static inline void transpose(fvec4& v1, fvec4& v2, fvec4& v3, fvec4& v4) {
float32x4x2_t t1 = vuzpq_f32(v1, v3);
float32x4x2_t t2 = vuzpq_f32(v2, v4);
......
......@@ -255,6 +255,12 @@ static inline float dot4(const fvec4& v1, const fvec4& v2) {
return r[0]+r[1]+r[2]+r[3];
}
static inline fvec4 cross(const fvec4& v1, const fvec4& v2) {
__m128 temp = v2.val*__builtin_shufflevector(v1.val, v1.val, 2, 0, 1, 3) -
v1.val*__builtin_shufflevector(v2.val, v2.val, 2, 0, 1, 3);
return __builtin_shufflevector(temp, temp, 2, 0, 1, 3);
}
static inline void transpose(fvec4& v1, fvec4& v2, fvec4& v3, fvec4& v4) {
__m128 a1 = __builtin_shufflevector(v1.val, v2.val, 0, 4, 2, 6);
__m128 a2 = __builtin_shufflevector(v1.val, v2.val, 1, 5, 3, 7);
......
#ifndef OPENMM_VECTORIZE_SSE_H_
#define OPENMM_VECTORIZE_SSE_H_
/* -------------------------------------------------------------------------- *
* OpenMM *
* -------------------------------------------------------------------------- *
* This is part of the OpenMM molecular simulation toolkit originating from *
* Simbios, the NIH National Center for Physics-Based Simulation of *
* 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. *
* Authors: Peter Eastman *
* Contributors: *
* *
* Permission is hereby granted, free of charge, to any person obtaining a *
* copy of this software and associated documentation files (the "Software"), *
* to deal in the Software without restriction, including without limitation *
* the rights to use, copy, modify, merge, publish, distribute, sublicense, *
* and/or sell copies of the Software, and to permit persons to whom the *
* Software is furnished to do so, subject to the following conditions: *
* *
* The above copyright notice and this permission notice shall be included in *
* all copies or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
* THE AUTHORS, CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, *
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR *
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE *
* USE OR OTHER DEALINGS IN THE SOFTWARE. *
* -------------------------------------------------------------------------- */
#include <smmintrin.h>
#include "hardware.h"
// This file defines classes and functions to simplify vectorizing code with SSE.
/**
* Determine whether ivec4 and fvec4 are supported on this processor.
*/
static bool isVec4Supported() {
int cpuInfo[4];
cpuid(cpuInfo, 0);
if (cpuInfo[0] >= 1) {
cpuid(cpuInfo, 1);
return ((cpuInfo[2] & ((int) 1 << 19)) != 0);
}
return false;
}
class ivec4;
/**
* A four element vector of floats.
*/
class fvec4 {
public:
__m128 val;
fvec4() {}
fvec4(float v) : val(_mm_set1_ps(v)) {}
fvec4(float v1, float v2, float v3, float v4) : val(_mm_set_ps(v4, v3, v2, v1)) {}
fvec4(__m128 v) : val(v) {}
fvec4(const float* v) : val(_mm_loadu_ps(v)) {}
operator __m128() const {
return val;
}
float operator[](int i) const {
float result[4];
store(result);
return result[i];
}
void store(float* v) const {
_mm_storeu_ps(v, val);
}
fvec4 operator+(const fvec4& other) const {
return _mm_add_ps(val, other);
}
fvec4 operator-(const fvec4& other) const {
return _mm_sub_ps(val, other);
}
fvec4 operator*(const fvec4& other) const {
return _mm_mul_ps(val, other);
}
fvec4 operator/(const fvec4& other) const {
return _mm_div_ps(val, other);
}
void operator+=(const fvec4& other) {
val = _mm_add_ps(val, other);
}
void operator-=(const fvec4& other) {
val = _mm_sub_ps(val, other);
}
void operator*=(const fvec4& other) {
val = _mm_mul_ps(val, other);
}
void operator/=(const fvec4& other) {
val = _mm_div_ps(val, other);
}
fvec4 operator-() const {
return _mm_sub_ps(_mm_set1_ps(0.0f), val);
}
fvec4 operator&(const fvec4& other) const {
return _mm_and_ps(val, other);
}
fvec4 operator|(const fvec4& other) const {
return _mm_or_ps(val, other);
}
fvec4 operator==(const fvec4& other) const {
return _mm_cmpeq_ps(val, other);
}
fvec4 operator!=(const fvec4& other) const {
return _mm_cmpneq_ps(val, other);
}
fvec4 operator>(const fvec4& other) const {
return _mm_cmpgt_ps(val, other);
}
fvec4 operator<(const fvec4& other) const {
return _mm_cmplt_ps(val, other);
}
fvec4 operator>=(const fvec4& other) const {
return _mm_cmpge_ps(val, other);
}
fvec4 operator<=(const fvec4& other) const {
return _mm_cmple_ps(val, other);
}
operator ivec4() const;
};
/**
* A four element vector of ints.
*/
class ivec4 {
public:
__m128i val;
ivec4() {}
ivec4(int v) : val(_mm_set1_epi32(v)) {}
ivec4(int v1, int v2, int v3, int v4) : val(_mm_set_epi32(v4, v3, v2, v1)) {}
ivec4(__m128i v) : val(v) {}
ivec4(const int* v) : val(_mm_loadu_si128((const __m128i*) v)) {}
operator __m128i() const {
return val;
}
int operator[](int i) const {
int result[4];
store(result);
return result[i];
}
void store(int* v) const {
_mm_storeu_si128((__m128i*) v, val);
}
ivec4 operator+(const ivec4& other) const {
return _mm_add_epi32(val, other);
}
ivec4 operator-(const ivec4& other) const {
return _mm_sub_epi32(val, other);
}
ivec4 operator*(const ivec4& other) const {
return _mm_mullo_epi32(val, other);
}
void operator+=(const ivec4& other) {
val = _mm_add_epi32(val, other);
}
void operator-=(const ivec4& other) {
val = _mm_sub_epi32(val, other);
}
void operator*=(const ivec4& other) {
val = _mm_mullo_epi32(val, other);
}
ivec4 operator-() const {
return _mm_sub_epi32(_mm_set1_epi32(0), val);
}
ivec4 operator&(const ivec4& other) const {
return _mm_and_si128(val, other);
}
ivec4 operator|(const ivec4& other) const {
return _mm_or_si128(val, other);
}
ivec4 operator==(const ivec4& other) const {
return _mm_cmpeq_epi32(val, other);
}
ivec4 operator!=(const ivec4& other) const {
return _mm_xor_si128(*this==other, _mm_set1_epi32(0xFFFFFFFF));
}
ivec4 operator>(const ivec4& other) const {
return _mm_cmpgt_epi32(val, other);
}
ivec4 operator<(const ivec4& other) const {
return _mm_cmplt_epi32(val, other);
}
ivec4 operator>=(const ivec4& other) const {
return _mm_xor_si128(_mm_cmplt_epi32(val, other), _mm_set1_epi32(0xFFFFFFFF));
}
ivec4 operator<=(const ivec4& other) const {
return _mm_xor_si128(_mm_cmpgt_epi32(val, other), _mm_set1_epi32(0xFFFFFFFF));
}
operator fvec4() const;
};
// Conversion operators.
inline fvec4::operator ivec4() const {
return _mm_cvttps_epi32(val);
}
inline ivec4::operator fvec4() const {
return _mm_cvtepi32_ps(val);
}
// Functions that operate on fvec4s.
static inline fvec4 floor(const fvec4& v) {
return fvec4(_mm_floor_ps(v.val));
}
static inline fvec4 ceil(const fvec4& v) {
return fvec4(_mm_ceil_ps(v.val));
}
static inline fvec4 round(const fvec4& v) {
return fvec4(_mm_round_ps(v.val, _MM_FROUND_TO_NEAREST_INT));
}
static inline fvec4 min(const fvec4& v1, const fvec4& v2) {
return fvec4(_mm_min_ps(v1.val, v2.val));
}
static inline fvec4 max(const fvec4& v1, const fvec4& v2) {
return fvec4(_mm_max_ps(v1.val, v2.val));
}
static inline fvec4 abs(const fvec4& v) {
static const __m128 mask = _mm_castsi128_ps(_mm_set1_epi32(0x7FFFFFFF));
return fvec4(_mm_and_ps(v.val, mask));
}
static inline fvec4 sqrt(const fvec4& v) {
return fvec4(_mm_sqrt_ps(v.val));
}
static inline float dot3(const fvec4& v1, const fvec4& v2) {
return _mm_cvtss_f32(_mm_dp_ps(v1, v2, 0x71));
}
static inline float dot4(const fvec4& v1, const fvec4& v2) {
return _mm_cvtss_f32(_mm_dp_ps(v1, v2, 0xF1));
}
static inline void transpose(fvec4& v1, fvec4& v2, fvec4& v3, fvec4& v4) {
_MM_TRANSPOSE4_PS(v1, v2, v3, v4);
}
// Functions that operate on ivec4s.
static inline ivec4 min(const ivec4& v1, const ivec4& v2) {
return ivec4(_mm_min_epi32(v1.val, v2.val));
}
static inline ivec4 max(const ivec4& v1, const ivec4& v2) {
return ivec4(_mm_max_epi32(v1.val, v2.val));
}
static inline ivec4 abs(const ivec4& v) {
return ivec4(_mm_abs_epi32(v.val));
}
static inline bool any(const ivec4& v) {
return !_mm_test_all_zeros(v, _mm_set1_epi32(0xFFFFFFFF));
}
// Mathematical operators involving a scalar and a vector.
static inline fvec4 operator+(float v1, const fvec4& v2) {
return fvec4(v1)+v2;
}
static inline fvec4 operator-(float v1, const fvec4& v2) {
return fvec4(v1)-v2;
}
static inline fvec4 operator*(float v1, const fvec4& v2) {
return fvec4(v1)*v2;
}
static inline fvec4 operator/(float v1, const fvec4& v2) {
return fvec4(v1)/v2;
}
// Operations for blending fvec4s based on an ivec4.
static inline fvec4 blend(const fvec4& v1, const fvec4& v2, const ivec4& mask) {
return fvec4(_mm_blendv_ps(v1.val, v2.val, _mm_castsi128_ps(mask.val)));
}
#endif /*OPENMM_VECTORIZE_SSE_H_*/
#ifndef OPENMM_VECTORIZE_SSE_H_
#define OPENMM_VECTORIZE_SSE_H_
/* -------------------------------------------------------------------------- *
* OpenMM *
* -------------------------------------------------------------------------- *
* This is part of the OpenMM molecular simulation toolkit originating from *
* Simbios, the NIH National Center for Physics-Based Simulation of *
* 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. *
* Authors: Peter Eastman *
* Contributors: *
* *
* Permission is hereby granted, free of charge, to any person obtaining a *
* copy of this software and associated documentation files (the "Software"), *
* to deal in the Software without restriction, including without limitation *
* the rights to use, copy, modify, merge, publish, distribute, sublicense, *
* and/or sell copies of the Software, and to permit persons to whom the *
* Software is furnished to do so, subject to the following conditions: *
* *
* The above copyright notice and this permission notice shall be included in *
* all copies or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
* THE AUTHORS, CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, *
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR *
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE *
* USE OR OTHER DEALINGS IN THE SOFTWARE. *
* -------------------------------------------------------------------------- */
#include <smmintrin.h>
#include "hardware.h"
// This file defines classes and functions to simplify vectorizing code with SSE.
/**
* Determine whether ivec4 and fvec4 are supported on this processor.
*/
static bool isVec4Supported() {
int cpuInfo[4];
cpuid(cpuInfo, 0);
if (cpuInfo[0] >= 1) {
cpuid(cpuInfo, 1);
return ((cpuInfo[2] & ((int) 1 << 19)) != 0);
}
return false;
}
class ivec4;
/**
* A four element vector of floats.
*/
class fvec4 {
public:
__m128 val;
fvec4() {}
fvec4(float v) : val(_mm_set1_ps(v)) {}
fvec4(float v1, float v2, float v3, float v4) : val(_mm_set_ps(v4, v3, v2, v1)) {}
fvec4(__m128 v) : val(v) {}
fvec4(const float* v) : val(_mm_loadu_ps(v)) {}
operator __m128() const {
return val;
}
float operator[](int i) const {
float result[4];
store(result);
return result[i];
}
void store(float* v) const {
_mm_storeu_ps(v, val);
}
fvec4 operator+(const fvec4& other) const {
return _mm_add_ps(val, other);
}
fvec4 operator-(const fvec4& other) const {
return _mm_sub_ps(val, other);
}
fvec4 operator*(const fvec4& other) const {
return _mm_mul_ps(val, other);
}
fvec4 operator/(const fvec4& other) const {
return _mm_div_ps(val, other);
}
void operator+=(const fvec4& other) {
val = _mm_add_ps(val, other);
}
void operator-=(const fvec4& other) {
val = _mm_sub_ps(val, other);
}
void operator*=(const fvec4& other) {
val = _mm_mul_ps(val, other);
}
void operator/=(const fvec4& other) {
val = _mm_div_ps(val, other);
}
fvec4 operator-() const {
return _mm_sub_ps(_mm_set1_ps(0.0f), val);
}
fvec4 operator&(const fvec4& other) const {
return _mm_and_ps(val, other);
}
fvec4 operator|(const fvec4& other) const {
return _mm_or_ps(val, other);
}
fvec4 operator==(const fvec4& other) const {
return _mm_cmpeq_ps(val, other);
}
fvec4 operator!=(const fvec4& other) const {
return _mm_cmpneq_ps(val, other);
}
fvec4 operator>(const fvec4& other) const {
return _mm_cmpgt_ps(val, other);
}
fvec4 operator<(const fvec4& other) const {
return _mm_cmplt_ps(val, other);
}
fvec4 operator>=(const fvec4& other) const {
return _mm_cmpge_ps(val, other);
}
fvec4 operator<=(const fvec4& other) const {
return _mm_cmple_ps(val, other);
}
operator ivec4() const;
};
/**
* A four element vector of ints.
*/
class ivec4 {
public:
__m128i val;
ivec4() {}
ivec4(int v) : val(_mm_set1_epi32(v)) {}
ivec4(int v1, int v2, int v3, int v4) : val(_mm_set_epi32(v4, v3, v2, v1)) {}
ivec4(__m128i v) : val(v) {}
ivec4(const int* v) : val(_mm_loadu_si128((const __m128i*) v)) {}
operator __m128i() const {
return val;
}
int operator[](int i) const {
int result[4];
store(result);
return result[i];
}
void store(int* v) const {
_mm_storeu_si128((__m128i*) v, val);
}
ivec4 operator+(const ivec4& other) const {
return _mm_add_epi32(val, other);
}
ivec4 operator-(const ivec4& other) const {
return _mm_sub_epi32(val, other);
}
ivec4 operator*(const ivec4& other) const {
return _mm_mullo_epi32(val, other);
}
void operator+=(const ivec4& other) {
val = _mm_add_epi32(val, other);
}
void operator-=(const ivec4& other) {
val = _mm_sub_epi32(val, other);
}
void operator*=(const ivec4& other) {
val = _mm_mullo_epi32(val, other);
}
ivec4 operator-() const {
return _mm_sub_epi32(_mm_set1_epi32(0), val);
}
ivec4 operator&(const ivec4& other) const {
return _mm_and_si128(val, other);
}
ivec4 operator|(const ivec4& other) const {
return _mm_or_si128(val, other);
}
ivec4 operator==(const ivec4& other) const {
return _mm_cmpeq_epi32(val, other);
}
ivec4 operator!=(const ivec4& other) const {
return _mm_xor_si128(*this==other, _mm_set1_epi32(0xFFFFFFFF));
}
ivec4 operator>(const ivec4& other) const {
return _mm_cmpgt_epi32(val, other);
}
ivec4 operator<(const ivec4& other) const {
return _mm_cmplt_epi32(val, other);
}
ivec4 operator>=(const ivec4& other) const {
return _mm_xor_si128(_mm_cmplt_epi32(val, other), _mm_set1_epi32(0xFFFFFFFF));
}
ivec4 operator<=(const ivec4& other) const {
return _mm_xor_si128(_mm_cmpgt_epi32(val, other), _mm_set1_epi32(0xFFFFFFFF));
}
operator fvec4() const;
};
// Conversion operators.
inline fvec4::operator ivec4() const {
return _mm_cvttps_epi32(val);
}
inline ivec4::operator fvec4() const {
return _mm_cvtepi32_ps(val);
}
// Functions that operate on fvec4s.
static inline fvec4 floor(const fvec4& v) {
return fvec4(_mm_floor_ps(v.val));
}
static inline fvec4 ceil(const fvec4& v) {
return fvec4(_mm_ceil_ps(v.val));
}
static inline fvec4 round(const fvec4& v) {
return fvec4(_mm_round_ps(v.val, _MM_FROUND_TO_NEAREST_INT));
}
static inline fvec4 min(const fvec4& v1, const fvec4& v2) {
return fvec4(_mm_min_ps(v1.val, v2.val));
}
static inline fvec4 max(const fvec4& v1, const fvec4& v2) {
return fvec4(_mm_max_ps(v1.val, v2.val));
}
static inline fvec4 abs(const fvec4& v) {
static const __m128 mask = _mm_castsi128_ps(_mm_set1_epi32(0x7FFFFFFF));
return fvec4(_mm_and_ps(v.val, mask));
}
static inline fvec4 sqrt(const fvec4& v) {
return fvec4(_mm_sqrt_ps(v.val));
}
static inline float dot3(const fvec4& v1, const fvec4& v2) {
return _mm_cvtss_f32(_mm_dp_ps(v1, v2, 0x71));
}
static inline float dot4(const fvec4& v1, const fvec4& v2) {
return _mm_cvtss_f32(_mm_dp_ps(v1, v2, 0xF1));
}
static inline fvec4 cross(const fvec4& v1, const fvec4& v2) {
fvec4 temp = fvec4(_mm_mul_ps(v1, _mm_shuffle_ps(v2, v2, _MM_SHUFFLE(3, 0, 2, 1)))) -
fvec4(_mm_mul_ps(v2, _mm_shuffle_ps(v1, v1, _MM_SHUFFLE(3, 0, 2, 1))));
return _mm_shuffle_ps(temp, temp, _MM_SHUFFLE(3, 0, 2, 1));
}
static inline void transpose(fvec4& v1, fvec4& v2, fvec4& v3, fvec4& v4) {
_MM_TRANSPOSE4_PS(v1, v2, v3, v4);
}
// Functions that operate on ivec4s.
static inline ivec4 min(const ivec4& v1, const ivec4& v2) {
return ivec4(_mm_min_epi32(v1.val, v2.val));
}
static inline ivec4 max(const ivec4& v1, const ivec4& v2) {
return ivec4(_mm_max_epi32(v1.val, v2.val));
}
static inline ivec4 abs(const ivec4& v) {
return ivec4(_mm_abs_epi32(v.val));
}
static inline bool any(const ivec4& v) {
return !_mm_test_all_zeros(v, _mm_set1_epi32(0xFFFFFFFF));
}
// Mathematical operators involving a scalar and a vector.
static inline fvec4 operator+(float v1, const fvec4& v2) {
return fvec4(v1)+v2;
}
static inline fvec4 operator-(float v1, const fvec4& v2) {
return fvec4(v1)-v2;
}
static inline fvec4 operator*(float v1, const fvec4& v2) {
return fvec4(v1)*v2;
}
static inline fvec4 operator/(float v1, const fvec4& v2) {
return fvec4(v1)/v2;
}
// Operations for blending fvec4s based on an ivec4.
static inline fvec4 blend(const fvec4& v1, const fvec4& v2, const ivec4& mask) {
return fvec4(_mm_blendv_ps(v1.val, v2.val, _mm_castsi128_ps(mask.val)));
}
#endif /*OPENMM_VECTORIZE_SSE_H_*/
/* -------------------------------------------------------------------------- *
* OpenMM *
* -------------------------------------------------------------------------- *
* This is part of the OpenMM molecular simulation toolkit originating from *
* Simbios, the NIH National Center for Physics-Based Simulation of *
* Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org. *
* *
* Portions copyright (c) 2008-2014 Stanford University and the Authors. *
* Authors: Peter Eastman *
* Contributors: *
* *
* Permission is hereby granted, free of charge, to any person obtaining a *
* copy of this software and associated documentation files (the "Software"), *
* to deal in the Software without restriction, including without limitation *
* the rights to use, copy, modify, merge, publish, distribute, sublicense, *
* and/or sell copies of the Software, and to permit persons to whom the *
* Software is furnished to do so, subject to the following conditions: *
* *
* The above copyright notice and this permission notice shall be included in *
* all copies or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
* THE AUTHORS, CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, *
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR *
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE *
* USE OR OTHER DEALINGS IN THE SOFTWARE. *
* -------------------------------------------------------------------------- */
#include "openmm/Force.h"
#include "openmm/OpenMMException.h"
#include "openmm/CustomManyParticleForce.h"
#include "openmm/internal/AssertionUtilities.h"
#include "openmm/internal/CustomManyParticleForceImpl.h"
#include <cmath>
#include <map>
#include <set>
#include <sstream>
#include <utility>
using namespace OpenMM;
using namespace std;
CustomManyParticleForce::CustomManyParticleForce(int particlesPerSet, const string& energy) :
particlesPerSet(particlesPerSet), energyExpression(energy), nonbondedMethod(NoCutoff), permutationMode(SinglePermutation), cutoffDistance(1.0), typeFilters(particlesPerSet) {
}
CustomManyParticleForce::~CustomManyParticleForce() {
for (int i = 0; i < (int) functions.size(); i++)
delete functions[i].function;
}
const string& CustomManyParticleForce::getEnergyFunction() const {
return energyExpression;
}
void CustomManyParticleForce::setEnergyFunction(const string& energy) {
energyExpression = energy;
}
CustomManyParticleForce::NonbondedMethod CustomManyParticleForce::getNonbondedMethod() const {
return nonbondedMethod;
}
void CustomManyParticleForce::setNonbondedMethod(NonbondedMethod method) {
nonbondedMethod = method;
}
CustomManyParticleForce::PermutationMode CustomManyParticleForce::getPermutationMode() const {
return permutationMode;
}
void CustomManyParticleForce::setPermutationMode(PermutationMode mode) {
permutationMode = mode;
}
double CustomManyParticleForce::getCutoffDistance() const {
return cutoffDistance;
}
void CustomManyParticleForce::setCutoffDistance(double distance) {
cutoffDistance = distance;
}
int CustomManyParticleForce::addPerParticleParameter(const string& name) {
particleParameters.push_back(ParticleParameterInfo(name));
return particleParameters.size()-1;
}
const string& CustomManyParticleForce::getPerParticleParameterName(int index) const {
ASSERT_VALID_INDEX(index, particleParameters);
return particleParameters[index].name;
}
void CustomManyParticleForce::setPerParticleParameterName(int index, const string& name) {
ASSERT_VALID_INDEX(index, particleParameters);
particleParameters[index].name = name;
}
int CustomManyParticleForce::addGlobalParameter(const string& name, double defaultValue) {
globalParameters.push_back(GlobalParameterInfo(name, defaultValue));
return globalParameters.size()-1;
}
const string& CustomManyParticleForce::getGlobalParameterName(int index) const {
ASSERT_VALID_INDEX(index, globalParameters);
return globalParameters[index].name;
}
void CustomManyParticleForce::setGlobalParameterName(int index, const string& name) {
ASSERT_VALID_INDEX(index, globalParameters);
globalParameters[index].name = name;
}
double CustomManyParticleForce::getGlobalParameterDefaultValue(int index) const {
ASSERT_VALID_INDEX(index, globalParameters);
return globalParameters[index].defaultValue;
}
void CustomManyParticleForce::setGlobalParameterDefaultValue(int index, double defaultValue) {
ASSERT_VALID_INDEX(index, globalParameters);
globalParameters[index].defaultValue = defaultValue;
}
int CustomManyParticleForce::addParticle(const vector<double>& parameters, int type) {
particles.push_back(ParticleInfo(parameters, type));
return particles.size()-1;
}
void CustomManyParticleForce::getParticleParameters(int index, vector<double>& parameters, int& type) const {
ASSERT_VALID_INDEX(index, particles);
parameters = particles[index].parameters;
type = particles[index].type;
}
void CustomManyParticleForce::setParticleParameters(int index, const vector<double>& parameters, int type) {
ASSERT_VALID_INDEX(index, particles);
particles[index].parameters = parameters;
particles[index].type = type;
}
int CustomManyParticleForce::addExclusion(int particle1, int particle2) {
exclusions.push_back(ExclusionInfo(particle1, particle2));
return exclusions.size()-1;
}
void CustomManyParticleForce::getExclusionParticles(int index, int& particle1, int& particle2) const {
ASSERT_VALID_INDEX(index, exclusions);
particle1 = exclusions[index].particle1;
particle2 = exclusions[index].particle2;
}
void CustomManyParticleForce::setExclusionParticles(int index, int particle1, int particle2) {
ASSERT_VALID_INDEX(index, exclusions);
exclusions[index].particle1 = particle1;
exclusions[index].particle2 = particle2;
}
void CustomManyParticleForce::createExclusionsFromBonds(const vector<pair<int, int> >& bonds, int bondCutoff) {
if (bondCutoff < 1)
return;
vector<set<int> > exclusions(particles.size());
vector<set<int> > bonded12(exclusions.size());
for (int i = 0; i < (int) bonds.size(); ++i) {
int p1 = bonds[i].first;
int p2 = bonds[i].second;
exclusions[p1].insert(p2);
exclusions[p2].insert(p1);
bonded12[p1].insert(p2);
bonded12[p2].insert(p1);
}
for (int level = 0; level < bondCutoff-1; level++) {
vector<set<int> > currentExclusions = exclusions;
for (int i = 0; i < (int) particles.size(); i++) {
for (set<int>::const_iterator iter = currentExclusions[i].begin(); iter != currentExclusions[i].end(); ++iter)
exclusions[*iter].insert(bonded12[i].begin(), bonded12[i].end());
}
}
for (int i = 0; i < (int) exclusions.size(); ++i)
for (set<int>::const_iterator iter = exclusions[i].begin(); iter != exclusions[i].end(); ++iter)
if (*iter < i)
addExclusion(*iter, i);
}
void CustomManyParticleForce::getTypeFilter(int index, set<int>& types) const {
if (index < 0 || index >= particlesPerSet)
throw OpenMMException("CustomManyParticleForce: index to getTypeFilter out of range");
types = typeFilters[index];
}
void CustomManyParticleForce::setTypeFilter(int index, const set<int>& types) {
if (index < 0 || index >= particlesPerSet)
throw OpenMMException("CustomManyParticleForce: index to setTypeFilter out of range");
typeFilters[index] = types;
}
int CustomManyParticleForce::addTabulatedFunction(const string& name, TabulatedFunction* function) {
functions.push_back(FunctionInfo(name, function));
return functions.size()-1;
}
const TabulatedFunction& CustomManyParticleForce::getTabulatedFunction(int index) const {
ASSERT_VALID_INDEX(index, functions);
return *functions[index].function;
}
TabulatedFunction& CustomManyParticleForce::getTabulatedFunction(int index) {
ASSERT_VALID_INDEX(index, functions);
return *functions[index].function;
}
const string& CustomManyParticleForce::getTabulatedFunctionName(int index) const {
ASSERT_VALID_INDEX(index, functions);
return functions[index].name;
}
ForceImpl* CustomManyParticleForce::createImpl() const {
return new CustomManyParticleForceImpl(*this);
}
void CustomManyParticleForce::updateParametersInContext(Context& context) {
dynamic_cast<CustomManyParticleForceImpl&>(getImplInContext(context)).updateParametersInContext(getContextImpl(context));
}
/* -------------------------------------------------------------------------- *
* OpenMM *
* -------------------------------------------------------------------------- *
* This is part of the OpenMM molecular simulation toolkit originating from *
* Simbios, the NIH National Center for Physics-Based Simulation of *
* Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org. *
* *
* Portions copyright (c) 2008-2014 Stanford University and the Authors. *
* Authors: Peter Eastman *
* Contributors: *
* *
* Permission is hereby granted, free of charge, to any person obtaining a *
* copy of this software and associated documentation files (the "Software"), *
* to deal in the Software without restriction, including without limitation *
* the rights to use, copy, modify, merge, publish, distribute, sublicense, *
* and/or sell copies of the Software, and to permit persons to whom the *
* Software is furnished to do so, subject to the following conditions: *
* *
* The above copyright notice and this permission notice shall be included in *
* all copies or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
* THE AUTHORS, CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, *
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR *
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE *
* USE OR OTHER DEALINGS IN THE SOFTWARE. *
* -------------------------------------------------------------------------- */
#include "openmm/OpenMMException.h"
#include "openmm/internal/ContextImpl.h"
#include "openmm/internal/CustomManyParticleForceImpl.h"
#include "openmm/kernels.h"
#include "lepton/Operation.h"
#include "lepton/Parser.h"
#include <sstream>
using namespace OpenMM;
using Lepton::CustomFunction;
using Lepton::ExpressionTreeNode;
using Lepton::Operation;
using Lepton::ParsedExpression;
using std::map;
using std::pair;
using std::vector;
using std::set;
using std::string;
using std::stringstream;
/**
* This class serves as a placeholder for angles and dihedrals in expressions.
*/
class CustomManyParticleForceImpl::FunctionPlaceholder : public CustomFunction {
public:
int numArguments;
FunctionPlaceholder(int numArguments) : numArguments(numArguments) {
}
int getNumArguments() const {
return numArguments;
}
double evaluate(const double* arguments) const {
return 0.0;
}
double evaluateDerivative(const double* arguments, const int* derivOrder) const {
return 0.0;
}
CustomFunction* clone() const {
return new FunctionPlaceholder(numArguments);
}
};
CustomManyParticleForceImpl::CustomManyParticleForceImpl(const CustomManyParticleForce& owner) : owner(owner) {
}
CustomManyParticleForceImpl::~CustomManyParticleForceImpl() {
}
void CustomManyParticleForceImpl::initialize(ContextImpl& context) {
kernel = context.getPlatform().createKernel(CalcCustomManyParticleForceKernel::Name(), context);
// Check for errors in the specification of parameters and exclusions.
const System& system = context.getSystem();
if (owner.getNumParticles() != system.getNumParticles())
throw OpenMMException("CustomManyParticleForce must have exactly as many particles as the System it belongs to.");
vector<set<int> > exclusions(owner.getNumParticles());
vector<double> parameters;
int type;
int numParameters = owner.getNumPerParticleParameters();
for (int i = 0; i < owner.getNumParticles(); i++) {
owner.getParticleParameters(i, parameters, type);
if (parameters.size() != numParameters) {
stringstream msg;
msg << "CustomManyParticleForce: Wrong number of parameters for particle ";
msg << i;
throw OpenMMException(msg.str());
}
}
for (int i = 0; i < owner.getNumExclusions(); i++) {
int particle1, particle2;
owner.getExclusionParticles(i, particle1, particle2);
if (particle1 < 0 || particle1 >= owner.getNumParticles()) {
stringstream msg;
msg << "CustomManyParticleForce: Illegal particle index for an exclusion: ";
msg << particle1;
throw OpenMMException(msg.str());
}
if (particle2 < 0 || particle2 >= owner.getNumParticles()) {
stringstream msg;
msg << "CustomManyParticleForce: Illegal particle index for an exclusion: ";
msg << particle2;
throw OpenMMException(msg.str());
}
if (exclusions[particle1].count(particle2) > 0 || exclusions[particle2].count(particle1) > 0) {
stringstream msg;
msg << "CustomManyParticleForce: Multiple exclusions are specified for particles ";
msg << particle1;
msg << " and ";
msg << particle2;
throw OpenMMException(msg.str());
}
exclusions[particle1].insert(particle2);
exclusions[particle2].insert(particle1);
}
if (owner.getNonbondedMethod() == CustomManyParticleForce::CutoffPeriodic) {
Vec3 boxVectors[3];
system.getDefaultPeriodicBoxVectors(boxVectors[0], boxVectors[1], boxVectors[2]);
double cutoff = owner.getCutoffDistance();
if (cutoff > 0.5*boxVectors[0][0] || cutoff > 0.5*boxVectors[1][1] || cutoff > 0.5*boxVectors[2][2])
throw OpenMMException("CustomManyParticleForce: The cutoff distance cannot be greater than half the periodic box size.");
}
kernel.getAs<CalcCustomManyParticleForceKernel>().initialize(context.getSystem(), owner);
}
double CustomManyParticleForceImpl::calcForcesAndEnergy(ContextImpl& context, bool includeForces, bool includeEnergy, int groups) {
if ((groups&(1<<owner.getForceGroup())) != 0)
return kernel.getAs<CalcCustomManyParticleForceKernel>().execute(context, includeForces, includeEnergy);
return 0.0;
}
vector<string> CustomManyParticleForceImpl::getKernelNames() {
vector<string> names;
names.push_back(CalcCustomManyParticleForceKernel::Name());
return names;
}
map<string, double> CustomManyParticleForceImpl::getDefaultParameters() {
map<string, double> parameters;
for (int i = 0; i < owner.getNumGlobalParameters(); i++)
parameters[owner.getGlobalParameterName(i)] = owner.getGlobalParameterDefaultValue(i);
return parameters;
}
ParsedExpression CustomManyParticleForceImpl::prepareExpression(const CustomManyParticleForce& force, const map<string, CustomFunction*>& customFunctions, map<string, vector<int> >& distances,
map<string, vector<int> >& angles, map<string, vector<int> >& dihedrals) {
CustomManyParticleForceImpl::FunctionPlaceholder custom(1);
CustomManyParticleForceImpl::FunctionPlaceholder distance(2);
CustomManyParticleForceImpl::FunctionPlaceholder angle(3);
CustomManyParticleForceImpl::FunctionPlaceholder dihedral(4);
map<string, CustomFunction*> functions = customFunctions;
functions["distance"] = &distance;
functions["angle"] = &angle;
functions["dihedral"] = &dihedral;
ParsedExpression expression = Lepton::Parser::parse(force.getEnergyFunction(), functions);
map<string, int> atoms;
for (int i = 0; i < force.getNumParticlesPerSet(); i++) {
stringstream name;
name << 'p' << (i+1);
atoms[name.str()] = i;
}
return ParsedExpression(replaceFunctions(expression.getRootNode(), atoms, distances, angles, dihedrals)).optimize();
}
ExpressionTreeNode CustomManyParticleForceImpl::replaceFunctions(const ExpressionTreeNode& node, map<string, int> atoms,
map<string, vector<int> >& distances, map<string, vector<int> >& angles, map<string, vector<int> >& dihedrals) {
const Operation& op = node.getOperation();
if (op.getId() != Operation::CUSTOM || (op.getName() != "distance" && op.getName() != "angle" && op.getName() != "dihedral"))
{
// This is not an angle or dihedral, so process its children.
vector<ExpressionTreeNode> children;
for (int i = 0; i < (int) node.getChildren().size(); i++)
children.push_back(replaceFunctions(node.getChildren()[i], atoms, distances, angles, dihedrals));
return ExpressionTreeNode(op.clone(), children);
}
const Operation::Custom& custom = static_cast<const Operation::Custom&>(op);
// Identify the atoms this term is based on.
int numArgs = custom.getNumArguments();
vector<int> indices(numArgs);
for (int i = 0; i < numArgs; i++) {
map<string, int>::const_iterator iter = atoms.find(node.getChildren()[i].getOperation().getName());
if (iter == atoms.end())
throw OpenMMException("CustomManyParticleForce: Unknown particle '"+node.getChildren()[i].getOperation().getName()+"'");
indices[i] = iter->second;
}
// Select a name for the variable and add it to the appropriate map.
stringstream variable;
if (numArgs == 2)
variable << "distance";
else if (numArgs == 3)
variable << "angle";
else
variable << "dihedral";
for (int i = 0; i < numArgs; i++)
variable << indices[i];
string name = variable.str();
if (numArgs == 2)
distances[name] = indices;
else if (numArgs == 3)
angles[name] = indices;
else
dihedrals[name] = indices;
// Return a new node that represents it as a simple variable.
return ExpressionTreeNode(new Operation::Variable(name));
}
void CustomManyParticleForceImpl::updateParametersInContext(ContextImpl& context) {
kernel.getAs<CalcCustomManyParticleForceKernel>().copyParametersToContext(context, owner);
}
void CustomManyParticleForceImpl::buildFilterArrays(const CustomManyParticleForce& force, int& numTypes, vector<int>& particleTypes, vector<int>& orderIndex, vector<vector<int> >& particleOrder) {
// Build a canonical list of type codes.
int numParticles = force.getNumParticles();
int numParticlesPerSet = force.getNumParticlesPerSet();
particleTypes.resize(numParticles);
map<int, int> typeMap;
for (int i = 0; i < numParticles; i++) {
vector<double> params;
int type;
force.getParticleParameters(i, params, type);
map<int, int>::const_iterator element = typeMap.find(type);
if (element == typeMap.end()) {
int newType = typeMap.size();
typeMap[type] = newType;
particleTypes[i] = newType;
}
else
particleTypes[i] = element->second;
}
numTypes = typeMap.size();
int numIndices = 1;
for (int i = 0; i < numParticlesPerSet; i++)
numIndices *= numTypes;
orderIndex.resize(numIndices, 0);
// Find the allowed type codes for each particle in an interaction.
vector<set<int> > allowedTypes(numParticlesPerSet);
bool anyFilters = false;
for (int i = 0; i < numParticlesPerSet; i++) {
set<int> types;
force.getTypeFilter(i, types);
if (types.size() == 0)
for (int j = 0; j < numTypes; j++)
allowedTypes[i].insert(j);
else {
for (set<int>::const_iterator iter = types.begin(); iter != types.end(); ++iter)
if (typeMap.find(*iter) != typeMap.end())
allowedTypes[i].insert(typeMap[*iter]);
if (allowedTypes[i].size() < numTypes)
anyFilters = true;
}
}
// If there are no filters, reordering is unnecessary.
if (!anyFilters) {
particleOrder.resize(1);
particleOrder[0].resize(numParticlesPerSet);
for (int i = 0; i < numParticlesPerSet; i++)
particleOrder[0][i] = i;
return;
}
// Build a list of every possible permutation of the particles.
particleOrder.clear();
vector<int> values;
for (int i = 0; i < numParticlesPerSet; i++)
values.push_back(i);
generatePermutations(values, force.getPermutationMode() == CustomManyParticleForce::SinglePermutation ? 0 : 1, particleOrder);
int numOrders = particleOrder.size();
// Now we need to loop over every possible sequence of type codes, and for each one figure out which order to use.
for (int i = 0; i < numIndices; i++) {
vector<int> types(numParticlesPerSet);
int temp = i;
for (int j = 0; j < numParticlesPerSet; j++) {
types[j] = temp%numTypes;
temp /= numTypes;
}
// Loop over possible orders until we find one that matches the filters.
int order = -1;
for (int j = 0; j < numOrders && order == -1; j++) {
bool matches = true;
for (int k = 0; k < numParticlesPerSet && matches; k++)
if (allowedTypes[k].find(types[particleOrder[j][k]]) == allowedTypes[k].end())
matches = false;
if (matches)
order = j;
}
orderIndex[i] = order;
}
}
void CustomManyParticleForceImpl::generatePermutations(vector<int>& values, int numFixed, vector<vector<int> >& result) {
int numValues = values.size();
if (numFixed == numValues) {
result.push_back(values);
return;
}
for (int i = numFixed; i < numValues; i++) {
int v1 = values[numFixed];
int v2 = values[i];
values[numFixed] = v2;
values[i] = v1;
generatePermutations(values, numFixed+1, result);
values[numFixed] = v1;
values[i] = v2;
}
}
\ No newline at end of file
#ifndef OPENMM_COMPILEDEXPRESSIONSET_H_
#define OPENMM_COMPILEDEXPRESSIONSET_H_
/* -------------------------------------------------------------------------- *
* OpenMM *
* -------------------------------------------------------------------------- *
* This is part of the OpenMM molecular simulation toolkit originating from *
* Simbios, the NIH National Center for Physics-Based Simulation of *
* Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org. *
* *
* Portions copyright (c) 2014 Stanford University and the Authors. *
* Authors: Peter Eastman *
* Contributors: *
* *
* Permission is hereby granted, free of charge, to any person obtaining a *
* copy of this software and associated documentation files (the "Software"), *
* to deal in the Software without restriction, including without limitation *
* the rights to use, copy, modify, merge, publish, distribute, sublicense, *
* and/or sell copies of the Software, and to permit persons to whom the *
* Software is furnished to do so, subject to the following conditions: *
* *
* The above copyright notice and this permission notice shall be included in *
* all copies or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
* THE AUTHORS, CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, *
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR *
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE *
* USE OR OTHER DEALINGS IN THE SOFTWARE. *
* -------------------------------------------------------------------------- */
#include "lepton/CompiledExpression.h"
#include "windowsExportCpu.h"
#include <string>
#include <vector>
namespace OpenMM {
/**
* This class simplifies the management of a set of related CompiledExpressions that share variables.
*/
class OPENMM_EXPORT_CPU CompiledExpressionSet {
public:
CompiledExpressionSet();
/**
* Add a CompiledExpression to the set.
*/
void registerExpression(Lepton::CompiledExpression& expression);
/**
* Get the index of a particular variable.
*/
int getVariableIndex(const std::string& name);
/**
* Set the value of a variable on every CompiledExpression.
*
* @param index the index of the variable, as returned by getVariableIndex()
* @param value the value to set it to
*/
void setVariable(int index, double value);
private:
std::vector<Lepton::CompiledExpression*> expressions;
std::vector<std::string> variables;
std::vector<std::vector<double*> > variableReferences;
};
} // namespace OpenMM
#endif /*OPENMM_COMPILEDEXPRESSIONSET_H_*/
/* Portions copyright (c) 2009-2014 Stanford University and Simbios.
* Contributors: Peter Eastman
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject
* to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS, CONTRIBUTORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef OPENMM_CPU_CUSTOM_MANY_PARTICLE_FORCE_H__
#define OPENMM_CPU_CUSTOM_MANY_PARTICLE_FORCE_H__
#include "ReferenceForce.h"
#include "ReferenceBondIxn.h"
#include "CompiledExpressionSet.h"
#include "CpuNeighborList.h"
#include "openmm/CustomManyParticleForce.h"
#include "openmm/internal/ThreadPool.h"
#include "openmm/internal/vectorize.h"
#include "lepton/CompiledExpression.h"
#include "lepton/ParsedExpression.h"
#include <map>
#include <set>
#include <utility>
#include <vector>
namespace OpenMM {
class CpuCustomManyParticleForce {
private:
class ParticleTermInfo;
class DistanceTermInfo;
class AngleTermInfo;
class DihedralTermInfo;
class ComputeForceTask;
class ThreadData;
int numParticles, numParticlesPerSet, numPerParticleParameters, numTypes;
bool useCutoff, usePeriodic, centralParticleMode;
RealOpenMM cutoffDistance;
RealOpenMM periodicBoxSize[3];
CpuNeighborList* neighborList;
ThreadPool& threads;
std::vector<std::set<int> > exclusions;
std::vector<int> particleTypes;
std::vector<int> orderIndex;
std::vector<std::vector<int> > particleOrder;
std::vector<std::vector<int> > particleNeighbors;
std::vector<ThreadData*> threadData;
// The following variables are used to make information accessible to the individual threads.
float* posq;
RealOpenMM** particleParameters;
const std::map<std::string, double>* globalParameters;
std::vector<AlignedArray<float> >* threadForce;
bool includeForces, includeEnergy;
void* atomicCounter;
/**
* This routine contains the code executed by each thread.
*/
void threadComputeForce(ThreadPool& threads, int threadIndex);
/**
* This is called recursively to loop over all possible combination of a set of particles and evaluate the
* interaction for each one.
*/
void loopOverInteractions(std::vector<int>& availableParticles, std::vector<int>& particleSet, int loopIndex, int startIndex,
RealOpenMM** particleParameters, float* forces, ThreadData& data, const fvec4& boxSize, const fvec4& invBoxSize);
/**---------------------------------------------------------------------------------------
Calculate custom interaction for one set of particles
@param particleSet the indices of the particles
@param posq atom coordinates in float format
@param particleParameters particle parameter values (particleParameters[particleIndex][parameterIndex])
@param forces force array (forces added)
@param totalEnergy total energy
--------------------------------------------------------------------------------------- */
/**
* Calculate the interaction for one set of particles
*
* @param particleSet the indices of the particles
* @param particleParameters particle parameter values (particleParameters[particleIndex][parameterIndex])
* @param data information and workspace for the current thread
* @param boxSize the size of the periodic box
* @param invBoxSize the inverse size of the periodic box
*/
void calculateOneIxn(std::vector<int>& particleSet, RealOpenMM** particleParameters, float* forces, ThreadData& data, const fvec4& boxSize, const fvec4& invBoxSize);
/**
* Compute the displacement and squared distance between two points, optionally using
* periodic boundary conditions.
*/
void computeDelta(const fvec4& posI, const fvec4& posJ, fvec4& deltaR, float& r2, const fvec4& boxSize, const fvec4& invBoxSize) const;
static float computeAngle(const fvec4& vi, const fvec4& vj, float v2i, float v2j, float sign);
static float getDihedralAngleBetweenThreeVectors(const fvec4& v1, const fvec4& v2, const fvec4& v3, fvec4& cross1, fvec4& cross2, const fvec4& signVector);
public:
/**
* Create a new CpuCustomManyParticleForce.
*
* @param force the CustomManyParticleForce to create it for
* @param threads the thread pool to use
*/
CpuCustomManyParticleForce(const OpenMM::CustomManyParticleForce& force, ThreadPool& threads);
~CpuCustomManyParticleForce();
/**
* Set the force to use a cutoff.
*
* @param distance the cutoff distance
*/
void setUseCutoff(RealOpenMM distance);
/**
* Set the force to use periodic boundary conditions. This requires that a cutoff has
* already been set, and the smallest side of the periodic box is at least twice the cutoff
* distance.
*
* @param boxSize the X, Y, and Z widths of the periodic box
*/
void setPeriodic(OpenMM::RealVec& boxSize);
/**
* Calculate the interaction.
*
* @param posq atom coordinates in float format
* @param particleParameters particle parameter values (particleParameters[particleIndex][parameterIndex])
* @param globalParameters the values of global parameters
* @param threadForce the collection of arrays for each thread to add forces to
* @param includeForce whether to compute forces
* @param includeEnergy whether to compute energy
* @param energy the total energy is added to this
*/
void calculateIxn(AlignedArray<float>& posq, RealOpenMM** particleParameters, const std::map<std::string, double>& globalParameters,
std::vector<AlignedArray<float> >& threadForce, bool includeForces, bool includeEnergy, double& energy);
};
class CpuCustomManyParticleForce::ParticleTermInfo {
public:
std::string name;
int atom, component, variableIndex;
Lepton::CompiledExpression forceExpression;
ParticleTermInfo(const std::string& name, int atom, int component, const Lepton::CompiledExpression& forceExpression, ThreadData& data);
};
class CpuCustomManyParticleForce::DistanceTermInfo {
public:
std::string name;
int p1, p2, variableIndex;
Lepton::CompiledExpression forceExpression;
int delta;
float deltaSign;
DistanceTermInfo(const std::string& name, const std::vector<int>& atoms, const Lepton::CompiledExpression& forceExpression, ThreadData& data);
};
class CpuCustomManyParticleForce::AngleTermInfo {
public:
std::string name;
int p1, p2, p3, variableIndex;
Lepton::CompiledExpression forceExpression;
int delta1, delta2;
float delta1Sign, delta2Sign;
AngleTermInfo(const std::string& name, const std::vector<int>& atoms, const Lepton::CompiledExpression& forceExpression, ThreadData& data);
};
class CpuCustomManyParticleForce::DihedralTermInfo {
public:
std::string name;
int p1, p2, p3, p4, variableIndex;
Lepton::CompiledExpression forceExpression;
int delta1, delta2, delta3;
DihedralTermInfo(const std::string& name, const std::vector<int>& atoms, const Lepton::CompiledExpression& forceExpression, ThreadData& data);
};
class CpuCustomManyParticleForce::ThreadData {
public:
CompiledExpressionSet expressionSet;
Lepton::CompiledExpression energyExpression;
std::vector<std::vector<int> > particleParamIndices;
std::vector<int> permutedParticles;
std::vector<std::pair<int, int> > deltaPairs;
std::vector<ParticleTermInfo> particleTerms;
std::vector<DistanceTermInfo> distanceTerms;
std::vector<AngleTermInfo> angleTerms;
std::vector<DihedralTermInfo> dihedralTerms;
AlignedArray<fvec4> delta, cross1, cross2;
std::vector<float> normDelta;
std::vector<float> norm2Delta;
AlignedArray<fvec4> f;
double energy;
ThreadData(const CustomManyParticleForce& force, Lepton::ParsedExpression& energyExpr,
std::map<std::string, std::vector<int> >& distances, std::map<std::string, std::vector<int> >& angles, std::map<std::string, std::vector<int> >& dihedrals);
/**
* Request a pair of particles whose distance or displacement vector is needed in the computation.
*/
void requestDeltaPair(int p1, int p2, int& pairIndex, float& pairSign, bool allowReversed);
};
} // namespace OpenMM
#endif // OPENMM_CPU_CUSTOM_MANY_PARTICLE_FORCE_H__
......@@ -33,6 +33,7 @@
* -------------------------------------------------------------------------- */
#include "CpuBondForce.h"
#include "CpuCustomManyParticleForce.h"
#include "CpuCustomNonbondedForce.h"
#include "CpuGBSAOBCForce.h"
#include "CpuLangevinDynamics.h"
......@@ -302,6 +303,48 @@ private:
CpuGBSAOBCForce obc;
};
/**
* This kernel is invoked by CustomManyParticleForce to calculate the forces acting on the system and the energy of the system.
*/
class CpuCalcCustomManyParticleForceKernel : public CalcCustomManyParticleForceKernel {
public:
CpuCalcCustomManyParticleForceKernel(std::string name, const Platform& platform, CpuPlatform::PlatformData& data) : CalcCustomManyParticleForceKernel(name, platform),
data(data), ixn(NULL) {
}
~CpuCalcCustomManyParticleForceKernel();
/**
* Initialize the kernel.
*
* @param system the System this kernel will be applied to
* @param force the CustomManyParticleForce this kernel will be used for
*/
void initialize(const System& system, const CustomManyParticleForce& force);
/**
* Execute the kernel to calculate the forces and/or energy.
*
* @param context the context in which to execute this kernel
* @param includeForces true if forces should be calculated
* @param includeEnergy true if the energy should be calculated
* @return the potential energy due to the force
*/
double execute(ContextImpl& context, bool includeForces, bool includeEnergy);
/**
* Copy changed parameters over to a context.
*
* @param context the context to copy parameters to
* @param force the CustomManyParticleForce to copy the parameters from
*/
void copyParametersToContext(ContextImpl& context, const CustomManyParticleForce& force);
private:
CpuPlatform::PlatformData& data;
int numParticles;
RealOpenMM cutoffDistance;
RealOpenMM **particleParamArray;
CpuCustomManyParticleForce* ixn;
std::vector<std::string> globalParameterNames;
NonbondedMethod nonbondedMethod;
};
/**
* This kernel is invoked by LangevinIntegrator to take one time step.
*/
......
/* Portions copyright (c) 2014 Stanford University and Simbios.
* Contributors: Peter Eastman
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject
* to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS, CONTRIBUTORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "CompiledExpressionSet.h"
using namespace OpenMM;
using namespace Lepton;
using namespace std;
CompiledExpressionSet::CompiledExpressionSet() {
}
void CompiledExpressionSet::registerExpression(Lepton::CompiledExpression& expression) {
expressions.push_back(&expression);
for (int i = 0; i < (int) variables.size(); i++)
if (expression.getVariables().find(variables[i]) != expression.getVariables().end())
variableReferences[i].push_back(&expression.getVariableReference(variables[i]));
}
int CompiledExpressionSet::getVariableIndex(const std::string& name) {
for (int i = 0; i < (int) variables.size(); i++)
if (variables[i] == name)
return i;
int index = variables.size();
variables.push_back(name);
variableReferences.push_back(vector<double*>());
for (int i = 0; i < (int) expressions.size(); i++)
if (expressions[i]->getVariables().find(name) != expressions[i]->getVariables().end())
variableReferences[index].push_back(&expressions[i]->getVariableReference(name));
return index;
}
void CompiledExpressionSet::setVariable(int index, double value) {
for (int i = 0; i < (int) variableReferences[index].size(); i++)
*variableReferences[index][i] = value;
}
/* Portions copyright (c) 2009-2014 Stanford University and Simbios.
* Contributors: Peter Eastman
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject
* to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS, CONTRIBUTORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <string.h>
#include <sstream>
#include <utility>
#include "SimTKOpenMMCommon.h"
#include "SimTKOpenMMLog.h"
#include "SimTKOpenMMUtilities.h"
#include "ReferenceForce.h"
#include "CpuCustomManyParticleForce.h"
#include "ReferenceTabulatedFunction.h"
#include "openmm/internal/CustomManyParticleForceImpl.h"
#include "lepton/CustomFunction.h"
#include "gmx_atomic.h"
using namespace OpenMM;
using namespace std;
class CpuCustomManyParticleForce::ComputeForceTask : public ThreadPool::Task {
public:
ComputeForceTask(CpuCustomManyParticleForce& owner) : owner(owner) {
}
void execute(ThreadPool& threads, int threadIndex) {
owner.threadComputeForce(threads, threadIndex);
}
CpuCustomManyParticleForce& owner;
};
CpuCustomManyParticleForce::CpuCustomManyParticleForce(const CustomManyParticleForce& force, ThreadPool& threads) :
threads(threads), useCutoff(false), usePeriodic(false), neighborList(NULL) {
numParticles = force.getNumParticles();
numParticlesPerSet = force.getNumParticlesPerSet();
numPerParticleParameters = force.getNumPerParticleParameters();
centralParticleMode = (force.getPermutationMode() == CustomManyParticleForce::UniqueCentralParticle);
// Create custom functions for the tabulated functions.
map<string, Lepton::CustomFunction*> functions;
for (int i = 0; i < (int) force.getNumTabulatedFunctions(); i++)
functions[force.getTabulatedFunctionName(i)] = createReferenceTabulatedFunction(force.getTabulatedFunction(i));
// Parse the expression and create the objects used to calculate the interaction.
map<string, vector<int> > distances;
map<string, vector<int> > angles;
map<string, vector<int> > dihedrals;
Lepton::ParsedExpression energyExpr = CustomManyParticleForceImpl::prepareExpression(force, functions, distances, angles, dihedrals);
for (int i = 0; i < threads.getNumThreads(); i++)
threadData.push_back(new ThreadData(force, energyExpr, distances, angles, dihedrals));
if (force.getNonbondedMethod() != CustomManyParticleForce::NoCutoff)
setUseCutoff(force.getCutoffDistance());
// Delete the custom functions.
for (map<string, Lepton::CustomFunction*>::iterator iter = functions.begin(); iter != functions.end(); iter++)
delete iter->second;
// Record exclusions.
exclusions.resize(force.getNumParticles());
for (int i = 0; i < (int) force.getNumExclusions(); i++) {
int p1, p2;
force.getExclusionParticles(i, p1, p2);
exclusions[p1].insert(p2);
exclusions[p2].insert(p1);
}
// Record information about type filters.
CustomManyParticleForceImpl::buildFilterArrays(force, numTypes, particleTypes, orderIndex, particleOrder);
}
CpuCustomManyParticleForce::~CpuCustomManyParticleForce() {
if (neighborList != NULL)
delete neighborList;
for (int i = 0; i < (int) threadData.size(); i++)
delete threadData[i];
}
void CpuCustomManyParticleForce::calculateIxn(AlignedArray<float>& posq, RealOpenMM** particleParameters,
const map<string, double>& globalParameters, vector<AlignedArray<float> >& threadForce,
bool includeForces, bool includeEnergy, double& energy) {
// Record the parameters for the threads.
this->posq = &posq[0];
this->particleParameters = particleParameters;
this->globalParameters = &globalParameters;
this->threadForce = &threadForce;
this->includeForces = includeForces;
this->includeEnergy = includeEnergy;
gmx_atomic_t counter;
gmx_atomic_set(&counter, 0);
this->atomicCounter = &counter;
if (useCutoff) {
// Construct a neighbor list. We use CpuNeighborList to do this, but then copy the result
// into a new data structure. This is needed because in UniqueCentralParticle mode, the
// the neighbor list needs to include symmetric pairs.
particleNeighbors.resize(numParticles);
for (int i = 0; i < numParticles; i++)
particleNeighbors[i].clear();
float boxSizeFloat[] = {(float) periodicBoxSize[0], (float) periodicBoxSize[1], (float) periodicBoxSize[2]};
neighborList->computeNeighborList(numParticles, posq, exclusions, boxSizeFloat, usePeriodic, cutoffDistance, threads);
for (int blockIndex = 0; blockIndex < neighborList->getNumBlocks(); blockIndex++) {
const vector<int>& neighbors = neighborList->getBlockNeighbors(blockIndex);
const vector<char>& exclusions = neighborList->getBlockExclusions(blockIndex);
int numNeighbors = neighbors.size();
for (int i = 0; i < 4; i++) {
int p1 = neighborList->getSortedAtoms()[4*blockIndex+i];
for (int j = 0; j < numNeighbors; j++) {
if ((exclusions[j] & (1<<i)) == 0) {
int p2 = neighbors[j];
particleNeighbors[p1].push_back(p2);
if (centralParticleMode)
particleNeighbors[p2].push_back(p1);
}
}
}
}
}
// Signal the threads to start running and wait for them to finish.
ComputeForceTask task(*this);
threads.execute(task);
threads.waitForThreads();
// Combine the energies from all the threads.
if (includeEnergy) {
int numThreads = threads.getNumThreads();
for (int i = 0; i < numThreads; i++)
energy += threadData[i]->energy;
}
}
void CpuCustomManyParticleForce::threadComputeForce(ThreadPool& threads, int threadIndex) {
vector<int> particleIndices(numParticlesPerSet);
fvec4 boxSize(periodicBoxSize[0], periodicBoxSize[1], periodicBoxSize[2], 0);
fvec4 invBoxSize((1/periodicBoxSize[0]), (1/periodicBoxSize[1]), (1/periodicBoxSize[2]), 0);
float* forces = &(*threadForce)[threadIndex][0];
ThreadData& data = *threadData[threadIndex];
data.energy = 0;
for (map<string, double>::const_iterator iter = globalParameters->begin(); iter != globalParameters->end(); ++iter)
data.expressionSet.setVariable(data.expressionSet.getVariableIndex(iter->first), iter->second);
if (useCutoff) {
// Loop over interactions from the neighbor list.
while (true) {
int i = gmx_atomic_fetch_add(reinterpret_cast<gmx_atomic_t*>(atomicCounter), 1);
if (i >= numParticles)
break;
particleIndices[0] = i;
loopOverInteractions(particleNeighbors[i], particleIndices, 1, 0, particleParameters, forces, data, boxSize, invBoxSize);
}
}
else {
// Loop over all possible sets of particles.
vector<int> particles(numParticles);
for (int i = 0; i < numParticles; i++)
particles[i] = i;
while (true) {
int i = gmx_atomic_fetch_add(reinterpret_cast<gmx_atomic_t*>(atomicCounter), 1);
if (i >= numParticles)
break;
particleIndices[0] = i;
int startIndex = (centralParticleMode ? 0 : i+1);
loopOverInteractions(particles, particleIndices, 1, startIndex, particleParameters, forces, data, boxSize, invBoxSize);
}
}
}
void CpuCustomManyParticleForce::setUseCutoff(RealOpenMM distance) {
useCutoff = true;
cutoffDistance = distance;
if (neighborList == NULL)
neighborList = new CpuNeighborList(4);
}
void CpuCustomManyParticleForce::setPeriodic(RealVec& boxSize) {
assert(useCutoff);
assert(boxSize[0] >= 2.0*cutoffDistance);
assert(boxSize[1] >= 2.0*cutoffDistance);
assert(boxSize[2] >= 2.0*cutoffDistance);
usePeriodic = true;
periodicBoxSize[0] = boxSize[0];
periodicBoxSize[1] = boxSize[1];
periodicBoxSize[2] = boxSize[2];
}
void CpuCustomManyParticleForce::loopOverInteractions(vector<int>& availableParticles, vector<int>& particleSet, int loopIndex, int startIndex,
RealOpenMM** particleParameters, float* forces, ThreadData& data, const fvec4& boxSize, const fvec4& invBoxSize) {
int numParticles = availableParticles.size();
double cutoff2 = cutoffDistance*cutoffDistance;
int checkRange = (centralParticleMode ? 1 : loopIndex);
for (int i = startIndex; i < numParticles; i++) {
int particle = availableParticles[i];
// Check whether this particle can actually participate in interactions with the others found so far.
bool include = true;
if (useCutoff) {
fvec4 deltaR;
fvec4 pos1(posq+4*particle);
float r2;
for (int j = 0; j < checkRange && include; j++) {
fvec4 pos2(posq+4*particleSet[j]);
computeDelta(pos1, pos2, deltaR, r2, boxSize, invBoxSize);
include &= (r2 < cutoff2);
}
}
for (int j = 0; j < loopIndex && include; j++)
include &= (exclusions[particle].find(particleSet[j]) == exclusions[particle].end());
if (include) {
if (loopIndex > 0 && availableParticles[i] == particleSet[0])
continue;
particleSet[loopIndex] = availableParticles[i];
if (loopIndex == numParticlesPerSet-1)
calculateOneIxn(particleSet, particleParameters, forces, data, boxSize, invBoxSize);
else
loopOverInteractions(availableParticles, particleSet, loopIndex+1, i+1, particleParameters, forces, data, boxSize, invBoxSize);
}
}
}
void CpuCustomManyParticleForce::calculateOneIxn(vector<int>& particleSet, RealOpenMM** particleParameters, float* forces, ThreadData& data, const fvec4& boxSize, const fvec4& invBoxSize) {
// Select the ordering to use for the particles.
vector<int>& permutedParticles = data.permutedParticles;
if (particleOrder.size() == 1) {
// There are no filters, so we don't need to worry about ordering.
permutedParticles = particleSet;
}
else {
int index = 0;
for (int i = numParticlesPerSet-1; i >= 0; i--)
index = particleTypes[particleSet[i]]+numTypes*index;
int order = orderIndex[index];
if (order == -1)
return;
for (int i = 0; i < numParticlesPerSet; i++)
permutedParticles[i] = particleSet[particleOrder[order][i]];
}
// Record per-particle parameters.
CompiledExpressionSet& expressionSet = data.expressionSet;
for (int i = 0; i < numParticlesPerSet; i++)
for (int j = 0; j < numPerParticleParameters; j++)
expressionSet.setVariable(data.particleParamIndices[i][j], particleParameters[permutedParticles[i]][j]);
// Compute inter-particle deltas.
int numDeltas = data.deltaPairs.size();
AlignedArray<fvec4>& delta = data.delta;
AlignedArray<fvec4>& cross1 = data.cross1;
AlignedArray<fvec4>& cross2 = data.cross2;
vector<float>& normDelta = data.normDelta;
vector<float>& norm2Delta = data.norm2Delta;
for (int i = 0; i < numDeltas; i++) {
int p1 = permutedParticles[data.deltaPairs[i].first];
int p2 = permutedParticles[data.deltaPairs[i].second];
computeDelta(fvec4(posq+4*p1), fvec4(posq+4*p2), delta[i], norm2Delta[i], boxSize, invBoxSize);
normDelta[i] = sqrtf(norm2Delta[i]);
}
// Compute all of the variables the energy can depend on.
for (int i = 0; i < (int) data.particleTerms.size(); i++) {
const ParticleTermInfo& term = data.particleTerms[i];
expressionSet.setVariable(term.variableIndex, posq[4*permutedParticles[term.atom]+term.component]);
}
for (int i = 0; i < (int) data.distanceTerms.size(); i++) {
const DistanceTermInfo& term = data.distanceTerms[i];
expressionSet.setVariable(term.variableIndex, normDelta[term.delta]);
}
for (int i = 0; i < (int) data.angleTerms.size(); i++) {
const AngleTermInfo& term = data.angleTerms[i];
expressionSet.setVariable(term.variableIndex, computeAngle(delta[term.delta1], delta[term.delta2], norm2Delta[term.delta1], norm2Delta[term.delta2], term.delta1Sign*term.delta2Sign));
}
for (int i = 0; i < (int) data.dihedralTerms.size(); i++) {
const DihedralTermInfo& term = data.dihedralTerms[i];
expressionSet.setVariable(term.variableIndex, getDihedralAngleBetweenThreeVectors(delta[term.delta1], delta[term.delta2], delta[term.delta3], cross1[i], cross2[i], delta[term.delta1]));
}
if (includeForces) {
// Apply forces based on individual particle coordinates.
AlignedArray<fvec4>& f = data.f;
for (int i = 0; i < numParticlesPerSet; i++)
f[i] = fvec4(0.0f);
for (int i = 0; i < (int) data.particleTerms.size(); i++) {
const ParticleTermInfo& term = data.particleTerms[i];
float temp[4];
f[term.atom].store(temp);
temp[term.component] -= term.forceExpression.evaluate();
f[term.atom] = fvec4(temp);
}
// Apply forces based on distances.
for (int i = 0; i < (int) data.distanceTerms.size(); i++) {
const DistanceTermInfo& term = data.distanceTerms[i];
float dEdR = (float) (term.forceExpression.evaluate()*term.deltaSign/(normDelta[term.delta]));
fvec4 force = -dEdR*delta[term.delta];
f[term.p1] -= force;
f[term.p2] += force;
}
// Apply forces based on angles.
for (int i = 0; i < (int) data.angleTerms.size(); i++) {
const AngleTermInfo& term = data.angleTerms[i];
float dEdTheta = (float) term.forceExpression.evaluate();
fvec4 thetaCross = cross(delta[term.delta1], delta[term.delta2]);
float lengthThetaCross = sqrtf(dot3(thetaCross, thetaCross));
if (lengthThetaCross < 1.0e-6f)
lengthThetaCross = 1.0e-6f;
float termA = dEdTheta*term.delta2Sign/(norm2Delta[term.delta1]*lengthThetaCross);
float termC = -dEdTheta*term.delta1Sign/(norm2Delta[term.delta2]*lengthThetaCross);
fvec4 deltaCross1 = cross(delta[term.delta1], thetaCross);
fvec4 deltaCross2 = cross(delta[term.delta2], thetaCross);
fvec4 force1 = termA*deltaCross1;
fvec4 force3 = termC*deltaCross2;
fvec4 force2 = -(force1+force3);
f[term.p1] += force1;
f[term.p2] += force2;
f[term.p3] += force3;
}
// Apply forces based on dihedrals.
for (int i = 0; i < (int) data.dihedralTerms.size(); i++) {
const DihedralTermInfo& term = data.dihedralTerms[i];
float dEdTheta = (float) term.forceExpression.evaluate();
float normCross1 = dot3(cross1[i], cross1[i]);
float normBC = normDelta[term.delta2];
float forceFactors[4];
forceFactors[0] = (-dEdTheta*normBC)/normCross1;
float normCross2 = dot3(cross2[i], cross2[i]);
forceFactors[3] = (dEdTheta*normBC)/normCross2;
forceFactors[1] = dot3(delta[term.delta1], delta[term.delta2]);
forceFactors[1] /= norm2Delta[term.delta2];
forceFactors[2] = dot3(delta[term.delta3], delta[term.delta2]);
forceFactors[2] /= norm2Delta[term.delta2];
fvec4 force1 = forceFactors[0]*cross1[i];
fvec4 force4 = forceFactors[3]*cross2[i];
fvec4 s = forceFactors[1]*force1 - forceFactors[2]*force4;
f[term.p1] += force1;
f[term.p2] -= force1-s;
f[term.p3] -= force4+s;
f[term.p4] += force4;
}
// Store the forces.
for (int i = 0; i < numParticlesPerSet; i++) {
int index = permutedParticles[i];
(fvec4(forces+4*index)+f[i]).store(forces+4*index);
}
}
// Add the energy
if (includeEnergy)
data.energy += data.energyExpression.evaluate();
}
void CpuCustomManyParticleForce::computeDelta(const fvec4& posI, const fvec4& posJ, fvec4& deltaR, float& r2, const fvec4& boxSize, const fvec4& invBoxSize) const {
deltaR = posJ-posI;
if (usePeriodic) {
fvec4 base = round(deltaR*invBoxSize)*boxSize;
deltaR = deltaR-base;
}
r2 = dot3(deltaR, deltaR);
}
float CpuCustomManyParticleForce::computeAngle(const fvec4& vi, const fvec4& vj, float v2i, float v2j, float sign) {
float dot = dot3(vi, vj)*sign;
float cosine = dot/sqrtf(v2i*v2j);
if (cosine > 0.99f || cosine < -0.99f) {
// We're close to the singularity in acos(), so take the cross product and use asin() instead.
fvec4 cross12 = cross(vi, vj);
float scale = v2i*v2j;
float angle = asinf(sqrtf(dot3(cross12, cross12)/scale));
if (cosine < 0.0f)
angle = (float) (M_PI-angle);
return angle;
}
return acosf(cosine);
}
float CpuCustomManyParticleForce::getDihedralAngleBetweenThreeVectors(const fvec4& v1, const fvec4& v2, const fvec4& v3, fvec4& cross1, fvec4& cross2, const fvec4& signVector) {
cross1 = cross(v1, v2);
cross2 = cross(v2, v3);
float angle = computeAngle(cross1, cross2, dot3(cross1, cross1), dot3(cross2, cross2), 1.0f);
float dotProduct = dot3(signVector, cross2);
if (dotProduct < 0)
angle = -angle;
return angle;
}
CpuCustomManyParticleForce::ParticleTermInfo::ParticleTermInfo(const string& name, int atom, int component, const Lepton::CompiledExpression& forceExpression, ThreadData& data) :
name(name), atom(atom), component(component), forceExpression(forceExpression) {
variableIndex = data.expressionSet.getVariableIndex(name);
}
CpuCustomManyParticleForce::DistanceTermInfo::DistanceTermInfo(const string& name, const vector<int>& atoms, const Lepton::CompiledExpression& forceExpression, ThreadData& data) :
name(name), p1(atoms[0]), p2(atoms[1]), forceExpression(forceExpression) {
variableIndex = data.expressionSet.getVariableIndex(name);
data.requestDeltaPair(p1, p2, delta, deltaSign, true);
}
CpuCustomManyParticleForce::AngleTermInfo::AngleTermInfo(const string& name, const vector<int>& atoms, const Lepton::CompiledExpression& forceExpression, ThreadData& data) :
name(name), p1(atoms[0]), p2(atoms[1]), p3(atoms[2]), forceExpression(forceExpression) {
variableIndex = data.expressionSet.getVariableIndex(name);
data.requestDeltaPair(p1, p2,delta1, delta1Sign, true);
data.requestDeltaPair(p3, p2, delta2, delta2Sign, true);
}
CpuCustomManyParticleForce::DihedralTermInfo::DihedralTermInfo(const string& name, const vector<int>& atoms, const Lepton::CompiledExpression& forceExpression, ThreadData& data) :
name(name), p1(atoms[0]), p2(atoms[1]), p3(atoms[2]), p4(atoms[3]), forceExpression(forceExpression) {
variableIndex = data.expressionSet.getVariableIndex(name);
float sign;
data.requestDeltaPair(p2, p1, delta1, sign, false);
data.requestDeltaPair(p2, p3, delta2, sign, false);
data.requestDeltaPair(p4, p3, delta3, sign, false);
}
CpuCustomManyParticleForce::ThreadData::ThreadData(const CustomManyParticleForce& force, Lepton::ParsedExpression& energyExpr,
map<string, vector<int> >& distances, map<string, vector<int> >& angles, map<string, vector<int> >& dihedrals) {
int numParticlesPerSet = force.getNumParticlesPerSet();
int numPerParticleParameters = force.getNumPerParticleParameters();
particleParamIndices.resize(numParticlesPerSet);
permutedParticles.resize(numParticlesPerSet);
f.resize(numParticlesPerSet);
energyExpression = energyExpr.createCompiledExpression();
expressionSet.registerExpression(energyExpression);
// Differentiate the energy to get expressions for the force.
for (int i = 0; i < numParticlesPerSet; i++) {
stringstream xname, yname, zname;
xname << 'x' << (i+1);
yname << 'y' << (i+1);
zname << 'z' << (i+1);
particleTerms.push_back(CpuCustomManyParticleForce::ParticleTermInfo(xname.str(), i, 0, energyExpr.differentiate(xname.str()).optimize().createCompiledExpression(), *this));
particleTerms.push_back(CpuCustomManyParticleForce::ParticleTermInfo(yname.str(), i, 1, energyExpr.differentiate(yname.str()).optimize().createCompiledExpression(), *this));
particleTerms.push_back(CpuCustomManyParticleForce::ParticleTermInfo(zname.str(), i, 2, energyExpr.differentiate(zname.str()).optimize().createCompiledExpression(), *this));
for (int j = 0; j < numPerParticleParameters; j++) {
stringstream paramname;
paramname << force.getPerParticleParameterName(j) << (i+1);
particleParamIndices[i].push_back(expressionSet.getVariableIndex(paramname.str()));
}
}
for (map<string, vector<int> >::const_iterator iter = dihedrals.begin(); iter != dihedrals.end(); ++iter)
dihedralTerms.push_back(CpuCustomManyParticleForce::DihedralTermInfo(iter->first, iter->second, energyExpr.differentiate(iter->first).optimize().createCompiledExpression(), *this));
for (map<string, vector<int> >::const_iterator iter = distances.begin(); iter != distances.end(); ++iter)
distanceTerms.push_back(CpuCustomManyParticleForce::DistanceTermInfo(iter->first, iter->second, energyExpr.differentiate(iter->first).optimize().createCompiledExpression(), *this));
for (map<string, vector<int> >::const_iterator iter = angles.begin(); iter != angles.end(); ++iter)
angleTerms.push_back(CpuCustomManyParticleForce::AngleTermInfo(iter->first, iter->second, energyExpr.differentiate(iter->first).optimize().createCompiledExpression(), *this));
for (int i = 0; i < particleTerms.size(); i++)
expressionSet.registerExpression(particleTerms[i].forceExpression);
for (int i = 0; i < distanceTerms.size(); i++)
expressionSet.registerExpression(distanceTerms[i].forceExpression);
for (int i = 0; i < angleTerms.size(); i++)
expressionSet.registerExpression(angleTerms[i].forceExpression);
for (int i = 0; i < dihedralTerms.size(); i++)
expressionSet.registerExpression(dihedralTerms[i].forceExpression);
int numDeltas = deltaPairs.size();
delta.resize(numDeltas);
normDelta.resize(numDeltas);
norm2Delta.resize(numDeltas);
cross1.resize(numDeltas);
cross2.resize(numDeltas);
}
void CpuCustomManyParticleForce::ThreadData::requestDeltaPair(int p1, int p2, int& pairIndex, float& pairSign, bool allowReversed) {
for (int i = 0; i < (int) deltaPairs.size(); i++) {
if (deltaPairs[i].first == p1 && deltaPairs[i].second == p2) {
pairIndex = i;
pairSign = 1;
return;
}
if (deltaPairs[i].first == p2 && deltaPairs[i].second == p1 && allowReversed) {
pairIndex = i;
pairSign = -1;
return;
}
}
pairIndex = deltaPairs.size();
pairSign = 1;
deltaPairs.push_back(make_pair(p1, p2));
}
......@@ -49,6 +49,8 @@ KernelImpl* CpuKernelFactory::createKernelImpl(std::string name, const Platform&
return new CpuCalcNonbondedForceKernel(name, platform, data);
if (name == CalcCustomNonbondedForceKernel::Name())
return new CpuCalcCustomNonbondedForceKernel(name, platform, data);
if (name == CalcCustomManyParticleForceKernel::Name())
return new CpuCalcCustomManyParticleForceKernel(name, platform, data);
if (name == CalcGBSAOBCForceKernel::Name())
return new CpuCalcGBSAOBCForceKernel(name, platform, data);
if (name == IntegrateLangevinStepKernel::Name())
......
......@@ -835,6 +835,72 @@ void CpuCalcGBSAOBCForceKernel::copyParametersToContext(ContextImpl& context, co
obc.setParticleParameters(particleParams);
}
CpuCalcCustomManyParticleForceKernel::~CpuCalcCustomManyParticleForceKernel() {
if (particleParamArray != NULL) {
for (int i = 0; i < numParticles; i++)
delete[] particleParamArray[i];
delete[] particleParamArray;
}
if (ixn != NULL)
delete ixn;
}
void CpuCalcCustomManyParticleForceKernel::initialize(const System& system, const CustomManyParticleForce& force) {
// Build the arrays.
numParticles = system.getNumParticles();
int numParticleParameters = force.getNumPerParticleParameters();
particleParamArray = new double*[numParticles];
for (int i = 0; i < numParticles; i++)
particleParamArray[i] = new double[numParticleParameters];
for (int i = 0; i < numParticles; ++i) {
vector<double> parameters;
int type;
force.getParticleParameters(i, parameters, type);
for (int j = 0; j < numParticleParameters; j++)
particleParamArray[i][j] = parameters[j];
}
for (int i = 0; i < force.getNumGlobalParameters(); i++)
globalParameterNames.push_back(force.getGlobalParameterName(i));
ixn = new CpuCustomManyParticleForce(force, data.threads);
nonbondedMethod = CalcCustomManyParticleForceKernel::NonbondedMethod(force.getNonbondedMethod());
cutoffDistance = force.getCutoffDistance();
}
double CpuCalcCustomManyParticleForceKernel::execute(ContextImpl& context, bool includeForces, bool includeEnergy) {
map<string, double> globalParameters;
for (int i = 0; i < (int) globalParameterNames.size(); i++)
globalParameters[globalParameterNames[i]] = context.getParameter(globalParameterNames[i]);
if (nonbondedMethod == CutoffPeriodic) {
RealVec& box = extractBoxSize(context);
double minAllowedSize = 2*cutoffDistance;
if (box[0] < minAllowedSize || box[1] < minAllowedSize || box[2] < minAllowedSize)
throw OpenMMException("The periodic box size has decreased to less than twice the nonbonded cutoff.");
ixn->setPeriodic(box);
}
double energy = 0;
ixn->calculateIxn(data.posq, particleParamArray, globalParameters, data.threadForce, includeForces, includeEnergy, energy);
return energy;
}
void CpuCalcCustomManyParticleForceKernel::copyParametersToContext(ContextImpl& context, const CustomManyParticleForce& force) {
if (numParticles != force.getNumParticles())
throw OpenMMException("updateParametersInContext: The number of particles has changed");
// Record the values.
int numParameters = force.getNumPerParticleParameters();
vector<double> params;
for (int i = 0; i < numParticles; ++i) {
vector<double> parameters;
int type;
force.getParticleParameters(i, parameters, type);
for (int j = 0; j < numParameters; j++)
particleParamArray[i][j] = static_cast<RealOpenMM>(parameters[j]);
}
}
CpuIntegrateLangevinStepKernel::~CpuIntegrateLangevinStepKernel() {
if (dynamics)
delete dynamics;
......
......@@ -65,6 +65,7 @@ CpuPlatform::CpuPlatform() {
registerKernelFactory(CalcRBTorsionForceKernel::Name(), factory);
registerKernelFactory(CalcNonbondedForceKernel::Name(), factory);
registerKernelFactory(CalcCustomNonbondedForceKernel::Name(), factory);
registerKernelFactory(CalcCustomManyParticleForceKernel::Name(), factory);
registerKernelFactory(CalcGBSAOBCForceKernel::Name(), factory);
registerKernelFactory(IntegrateLangevinStepKernel::Name(), factory);
platformProperties.push_back(CpuThreads());
......
......@@ -59,7 +59,9 @@ public:
inverseMasses(inverseMasses), tolerance(tolerance), threadSettle(threadSettle) {
}
void execute(ThreadPool& threads, int threadIndex) {
threadSettle[threadIndex]->applyToVelocities(atomCoordinates, velocities, inverseMasses, tolerance);
if (threadIndex < threadSettle.size()) {
threadSettle[threadIndex]->applyToVelocities(atomCoordinates, velocities, inverseMasses, tolerance);
}
}
vector<OpenMM::RealVec>& atomCoordinates;
vector<OpenMM::RealVec>& velocities;
......
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