Commit 83ed602e authored by peastman's avatar peastman
Browse files

Merge pull request #797 from peastman/triclinic

C++ libraries support triclinic boxes
parents b51e05a8 050e1262
......@@ -1261,6 +1261,61 @@ Other Features
##############
Periodic Boundary Conditions
****************************
Many Force objects support periodic boundary conditions. They act as if space
were tiled with infinitely repeating copies of the system, then compute the
forces acting on a single copy based on the infinite periodic copies. In most
(but not all) cases, they apply a cutoff so that each particle only interacts
with a single copy of each other particle.
OpenMM supports triclinic periodic boxes. This means the periodicity is defined
by three vectors, :math:`\mathbf{a}`\ , :math:`\mathbf{b}`\ , and
:math:`\mathbf{c}`\ . Given a particle position, the infinite periodic copies
of that particle are generated by adding vectors of the form
:math:`i \mathbf{a}+j \mathbf{b}+k \mathbf{c}`\ , where :math:`i`\ ,
:math:`j`\ , and :math:`k` are arbitrary integers.
The periodic box vectors must be chosen to satisfy certain requirements.
Roughly speaking, :math:`\mathbf{a}`\ , :math:`\mathbf{b}`\ , and
:math:`\mathbf{c}` need to "mostly" correspond to the x, y, and z axes. They
must have the form
.. math::
\mathbf{a} = (a_x, 0, 0)
\mathbf{b} = (b_x, b_y, 0)
\mathbf{c} = (c_x, c_y, c_z)
It is always possible to put the box vectors into this form by rotating the
system until :math:`\mathbf{a}` is parallel to x and :math:`\mathbf{b}` lies in
the xy plane.
Furthermore, they must obey the following constraints:
.. math::
a_x > 0, b_y > 0, c_z > 0
a_x \ge 2 |b_x|
a_x \ge 2 |c_x|
b_y \ge 2 |c_y|
This effectively requires the box vectors to be specified in a particular
reduced form. By forming combinations of box vectors (a process known as
"lattice reduction"), it is always possible to put them in this form without
changing the periodic system they represent.
These requirements have an important consequence: the periodic unit cell can
always be treated as an axis-aligned rectangular box of size
:math:`(a_x, b_y, c_z)`\ . The remaining non-zero elements of the box vectors
cause the repeating copies of the system to be staggered relative to each other,
but they do not affect the shape or size of each copy. The volume of the unit
cell is simply given by :math:`a_x b_y c_z`\ .
LocalEnergyMinimizer
********************
......
......@@ -1225,10 +1225,10 @@ public:
* Begin computing the force and energy.
*
* @param io an object that coordinates data transfer
* @param periodicBoxSize the size of the periodic box (measured in nm)
* @param periodicBoxVectors the vectors defining the periodic box (measured in nm)
* @param includeEnergy true if potential energy should be computed
*/
virtual void beginComputation(IO& io, Vec3 periodicBoxSize, bool includeEnergy) = 0;
virtual void beginComputation(IO& io, const Vec3* periodicBoxVectors, bool includeEnergy) = 0;
/**
* Finish computing the force and energy.
*
......
......@@ -182,8 +182,9 @@ public:
* Set the vectors defining the axes of the periodic box (measured in nm). They will affect
* any Force that uses periodic boundary conditions.
*
* Currently, only rectangular boxes are supported. This means that a, b, and c must be aligned with the
* x, y, and z axes respectively. Future releases may support arbitrary triclinic boxes.
* Triclinic boxes are supported, but the vectors must satisfy certain requirements. In particular,
* a must point in the x direction, b must point "mostly" in the y direction, and c must point "mostly"
* in the z direction. See the documentation for details.
*
* @param a the vector defining the first edge of the periodic box
* @param b the vector defining the second edge of the periodic box
......
......@@ -203,9 +203,6 @@ public:
* created Context will have its box vectors set to these. They will affect
* any Force added to the System that uses periodic boundary conditions.
*
* Currently, only rectangular boxes are supported. This means that a, b, and c must be aligned with the
* x, y, and z axes respectively. Future releases may support arbitrary triclinic boxes.
*
* @param a on exit, this contains the vector defining the first edge of the periodic box
* @param b on exit, this contains the vector defining the second edge of the periodic box
* @param c on exit, this contains the vector defining the third edge of the periodic box
......@@ -216,8 +213,9 @@ public:
* created Context will have its box vectors set to these. They will affect
* any Force added to the System that uses periodic boundary conditions.
*
* Currently, only rectangular boxes are supported. This means that a, b, and c must be aligned with the
* x, y, and z axes respectively. Future releases may support arbitrary triclinic boxes.
* Triclinic boxes are supported, but the vectors must satisfy certain requirements. In particular,
* a must point in the x direction, b must point "mostly" in the y direction, and c must point "mostly"
* in the z direction. See the documentation for details.
*
* @param a the vector defining the first edge of the periodic box
* @param b the vector defining the second edge of the periodic box
......
......@@ -142,9 +142,6 @@ public:
* Get the vectors defining the axes of the periodic box (measured in nm). They will affect
* any Force that uses periodic boundary conditions.
*
* Currently, only rectangular boxes are supported. This means that a, b, and c must be aligned with the
* x, y, and z axes respectively. Future releases may support arbitrary triclinic boxes.
*
* @param a the vector defining the first edge of the periodic box
* @param b the vector defining the second edge of the periodic box
* @param c the vector defining the third edge of the periodic box
......@@ -154,8 +151,9 @@ public:
* Set the vectors defining the axes of the periodic box (measured in nm). They will affect
* any Force that uses periodic boundary conditions.
*
* Currently, only rectangular boxes are supported. This means that a, b, and c must be aligned with the
* x, y, and z axes respectively. Future releases may support arbitrary triclinic boxes.
* Triclinic boxes are supported, but the vectors must satisfy certain requirements. In particular,
* a must point in the x direction, b must point "mostly" in the y direction, and c must point "mostly"
* in the z direction. See the documentation for details.
*
* @param a the vector defining the first edge of the periodic box
* @param b the vector defining the second edge of the periodic box
......
......@@ -40,6 +40,7 @@
#include "openmm/VirtualSite.h"
#include "openmm/Context.h"
#include <algorithm>
#include <cmath>
#include <iostream>
#include <map>
#include <utility>
......@@ -235,10 +236,10 @@ void ContextImpl::getPeriodicBoxVectors(Vec3& a, Vec3& b, Vec3& c) {
void ContextImpl::setPeriodicBoxVectors(const Vec3& a, const Vec3& b, const Vec3& c) {
if (a[1] != 0.0 || a[2] != 0.0)
throw OpenMMException("First periodic box vector must be parallel to x.");
if (b[0] != 0.0 || b[2] != 0.0)
throw OpenMMException("Second periodic box vector must be parallel to y.");
if (c[0] != 0.0 || c[1] != 0.0)
throw OpenMMException("Third periodic box vector must be parallel to z.");
if (b[2] != 0.0)
throw OpenMMException("Second periodic box vector must be in the x-y plane.");
if (a[0] <= 0.0 || b[1] <= 0.0 || c[2] <= 0.0 || a[0] < 2*fabs(b[0]) || a[0] < 2*fabs(c[0]) || b[1] < 2*fabs(c[1]))
throw OpenMMException("Periodic box vectors must be in reduced form.");
updateStateDataKernel.getAs<UpdateStateDataKernel>().setPeriodicBoxVectors(*this, a, b, c);
}
......
......@@ -112,7 +112,9 @@ void MonteCarloAnisotropicBarostatImpl::updateContextState(ContextImpl& context)
Vec3 lengthScale(1.0, 1.0, 1.0);
lengthScale[axis] = newVolume/volume;
kernel.getAs<ApplyMonteCarloBarostatKernel>().scaleCoordinates(context, lengthScale[0], lengthScale[1], lengthScale[2]);
context.getOwner().setPeriodicBoxVectors(box[0]*lengthScale[0], box[1]*lengthScale[1], box[2]*lengthScale[2]);
context.getOwner().setPeriodicBoxVectors(Vec3(box[0][0]*lengthScale[0], box[0][1]*lengthScale[1], box[0][2]*lengthScale[2]),
Vec3(box[1][0]*lengthScale[0], box[1][1]*lengthScale[1], box[1][2]*lengthScale[2]),
Vec3(box[2][0]*lengthScale[0], box[2][1]*lengthScale[1], box[2][2]*lengthScale[2]));
// Compute the energy of the modified system.
......
......@@ -113,7 +113,9 @@ void MonteCarloMembraneBarostatImpl::updateContextState(ContextImpl& context) {
}
double deltaArea = box[0][0]*lengthScale[0]*box[1][1]*lengthScale[1] - box[0][0]*box[1][1];
kernel.getAs<ApplyMonteCarloBarostatKernel>().scaleCoordinates(context, lengthScale[0], lengthScale[1], lengthScale[2]);
context.getOwner().setPeriodicBoxVectors(box[0]*lengthScale[0], box[1]*lengthScale[1], box[2]*lengthScale[2]);
context.getOwner().setPeriodicBoxVectors(Vec3(box[0][0]*lengthScale[0], box[0][1]*lengthScale[1], box[0][2]*lengthScale[2]),
Vec3(box[1][0]*lengthScale[0], box[1][1]*lengthScale[1], box[1][2]*lengthScale[2]),
Vec3(box[2][0]*lengthScale[0], box[2][1]*lengthScale[1], box[2][2]*lengthScale[2]));
// Compute the energy of the modified system.
......
......@@ -6,7 +6,7 @@
* Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org. *
* *
* Portions copyright (c) 2008-2014 Stanford University and the Authors. *
* Portions copyright (c) 2008-2015 Stanford University and the Authors. *
* Authors: Peter Eastman *
* Contributors: *
* *
......@@ -98,6 +98,8 @@ void NonbondedForceImpl::initialize(ContextImpl& context) {
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("NonbondedForce: The cutoff distance cannot be greater than half the periodic box size.");
if (owner.getNonbondedMethod() == NonbondedForce::Ewald && (boxVectors[1][0] != 0.0 || boxVectors[2][0] != 0.0 || boxVectors[2][1] != 0))
throw OpenMMException("NonbondedForce: Ewald is not supported with non-rectangular boxes. Use PME instead.");
}
kernel.getAs<CalcNonbondedForceKernel>().initialize(context.getSystem(), owner);
}
......
......@@ -34,6 +34,7 @@
#include "openmm/System.h"
#include "openmm/VirtualSite.h"
#include "openmm/internal/AssertionUtilities.h"
#include <cmath>
using namespace OpenMM;
......@@ -111,10 +112,10 @@ void System::getDefaultPeriodicBoxVectors(Vec3& a, Vec3& b, Vec3& c) const {
void System::setDefaultPeriodicBoxVectors(const Vec3& a, const Vec3& b, const Vec3& c) {
if (a[1] != 0.0 || a[2] != 0.0)
throw OpenMMException("First periodic box vector must be parallel to x.");
if (b[0] != 0.0 || b[2] != 0.0)
throw OpenMMException("Second periodic box vector must be parallel to y.");
if (c[0] != 0.0 || c[1] != 0.0)
throw OpenMMException("Third periodic box vector must be parallel to z.");
if (b[2] != 0.0)
throw OpenMMException("Second periodic box vector must be in the x-y plane.");
if (a[0] <= 0.0 || b[1] <= 0.0 || c[2] <= 0.0 || a[0] < 2*fabs(b[0]) || a[0] < 2*fabs(c[0]) || b[1] < 2*fabs(c[1]))
throw OpenMMException("Periodic box vectors must be in reduced form.");
periodicBoxVectors[0] = a;
periodicBoxVectors[1] = b;
periodicBoxVectors[2] = c;
......
......@@ -51,9 +51,11 @@ private:
class ComputeForceTask;
class ThreadData;
int numParticles, numParticlesPerSet, numPerParticleParameters, numTypes;
bool useCutoff, usePeriodic, centralParticleMode;
bool useCutoff, usePeriodic, triclinic, centralParticleMode;
RealOpenMM cutoffDistance;
RealOpenMM periodicBoxSize[3];
float recipBoxSize[3];
RealVec periodicBoxVectors[3];
AlignedArray<fvec4> periodicBoxVec4;
CpuNeighborList* neighborList;
ThreadPool& threads;
std::vector<std::set<int> > exclusions;
......@@ -138,9 +140,9 @@ public:
* 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
* @param periodicBoxVectors the vectors defining the periodic box
*/
void setPeriodic(OpenMM::RealVec& boxSize);
void setPeriodic(RealVec* periodicBoxVectors);
/**
* Calculate the interaction.
......
......@@ -95,11 +95,11 @@ class CpuCustomNonbondedForce {
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
@param periodicBoxVectors the vectors defining the periodic box
--------------------------------------------------------------------------------------- */
void setPeriodic(OpenMM::RealVec& boxSize);
void setPeriodic(RealVec* periodicBoxVectors);
/**---------------------------------------------------------------------------------------
......@@ -127,8 +127,11 @@ private:
bool cutoff;
bool useSwitch;
bool periodic;
bool triclinic;
const CpuNeighborList* neighborList;
RealOpenMM periodicBoxSize[3];
float recipBoxSize[3];
RealVec periodicBoxVectors[3];
AlignedArray<fvec4> periodicBoxVec4;
RealOpenMM cutoffDistance, switchingDistance;
ThreadPool& threads;
const std::vector<std::set<int> > exclusions;
......
......@@ -33,6 +33,7 @@
* -------------------------------------------------------------------------- */
#include "AlignedArray.h"
#include "RealVec.h"
#include "windowsExportCpu.h"
#include "openmm/internal/ThreadPool.h"
#include <set>
......@@ -47,7 +48,7 @@ public:
class Voxels;
CpuNeighborList(int blockSize);
void computeNeighborList(int numAtoms, const AlignedArray<float>& atomLocations, const std::vector<std::set<int> >& exclusions,
const float* periodicBoxSize, bool usePeriodic, float maxDistance, ThreadPool& threads);
const RealVec* periodicBoxVectors, bool usePeriodic, float maxDistance, ThreadPool& threads);
int getNumBlocks() const;
const std::vector<int>& getSortedAtoms() const;
const std::vector<int>& getBlockNeighbors(int blockIndex) const;
......@@ -68,7 +69,7 @@ private:
Voxels* voxels;
const std::vector<std::set<int> >* exclusions;
const float* atomLocations;
const float* periodicBoxSize;
RealVec periodicBoxVectors[3];
int numAtoms;
bool usePeriodic;
float maxDistance;
......
......@@ -83,11 +83,11 @@ class CpuNonbondedForce {
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
@param periodicBoxVectors the vectors defining the periodic box
--------------------------------------------------------------------------------------- */
void setPeriodic(float* periodicBoxSize);
void setPeriodic(RealVec* periodicBoxVectors);
/**---------------------------------------------------------------------------------------
......@@ -161,11 +161,14 @@ protected:
bool cutoff;
bool useSwitch;
bool periodic;
bool triclinic;
bool ewald;
bool pme;
bool tableIsValid;
const CpuNeighborList* neighborList;
float periodicBoxSize[3];
float recipBoxSize[3];
RealVec periodicBoxVectors[3];
AlignedArray<fvec4> periodicBoxVec4;
float cutoffDistance, switchingDistance;
float krf, crf;
float alphaEwald;
......
/* Portions copyright (c) 2006-2013 Stanford University and Simbios.
/* Portions copyright (c) 2006-2014 Stanford University and Simbios.
* Contributors: Pande Group
*
* Permission is hereby granted, free of charge, to any person obtaining
......@@ -53,6 +53,12 @@ protected:
void calculateBlockIxn(int blockIndex, float* forces, double* totalEnergy, const fvec4& boxSize, const fvec4& invBoxSize);
/**
* Templatized implementation of calculateBlockIxn.
*/
template <bool TRICLINIC>
void calculateBlockIxnImpl(int blockIndex, float* forces, double* totalEnergy, const fvec4& boxSize, const fvec4& invBoxSize);
/**---------------------------------------------------------------------------------------
Calculate all the interactions for one atom block.
......@@ -65,10 +71,17 @@ protected:
void calculateBlockEwaldIxn(int blockIndex, float* forces, double* totalEnergy, const fvec4& boxSize, const fvec4& invBoxSize);
/**
* Templatized implementation of calculateBlockEwaldIxn.
*/
template <bool TRICLINIC>
void calculateBlockEwaldIxnImpl(int blockIndex, float* forces, double* totalEnergy, const fvec4& boxSize, const fvec4& invBoxSize);
/**
* Compute the displacement and squared distance between a collection of points, optionally using
* periodic boundary conditions.
*/
template <bool TRICLINIC>
void getDeltaR(const float* posI, const fvec4& x, const fvec4& y, const fvec4& z, fvec4& dx, fvec4& dy, fvec4& dz, fvec4& r2, bool periodic, const fvec4& boxSize, const fvec4& invBoxSize) const;
/**
......
/* Portions copyright (c) 2006-2013 Stanford University and Simbios.
/* Portions copyright (c) 2006-2014 Stanford University and Simbios.
* Contributors: Pande Group
*
* Permission is hereby granted, free of charge, to any person obtaining
......@@ -52,6 +52,12 @@ protected:
void calculateBlockIxn(int blockIndex, float* forces, double* totalEnergy, const fvec4& boxSize, const fvec4& invBoxSize);
/**
* Templatized implementation of calculateBlockIxn.
*/
template <bool TRICLINIC>
void calculateBlockIxnImpl(int blockIndex, float* forces, double* totalEnergy, const fvec4& boxSize, const fvec4& invBoxSize);
/**---------------------------------------------------------------------------------------
Calculate all the interactions for one atom block.
......@@ -64,10 +70,17 @@ protected:
void calculateBlockEwaldIxn(int blockIndex, float* forces, double* totalEnergy, const fvec4& boxSize, const fvec4& invBoxSize);
/**
* Templatized implementation of calculateBlockEwaldIxn.
*/
template <bool TRICLINIC>
void calculateBlockEwaldIxnImpl(int blockIndex, float* forces, double* totalEnergy, const fvec4& boxSize, const fvec4& invBoxSize);
/**
* Compute the displacement and squared distance between a collection of points, optionally using
* periodic boundary conditions.
*/
template <bool TRICLINIC>
void getDeltaR(const float* posI, const fvec8& x, const fvec8& y, const fvec8& z, fvec8& dx, fvec8& dy, fvec8& dz, fvec8& r2, bool periodic, const fvec4& boxSize, const fvec4& invBoxSize) const;
/**
......
......@@ -122,8 +122,7 @@ void CpuCustomManyParticleForce::calculateIxn(AlignedArray<float>& posq, RealOpe
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);
neighborList->computeNeighborList(numParticles, posq, exclusions, periodicBoxVectors, 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);
......@@ -159,8 +158,8 @@ void CpuCustomManyParticleForce::calculateIxn(AlignedArray<float>& posq, RealOpe
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);
fvec4 boxSize(periodicBoxVectors[0][0], periodicBoxVectors[1][1], periodicBoxVectors[2][2], 0);
fvec4 invBoxSize(recipBoxSize[0], recipBoxSize[1], recipBoxSize[2], 0);
float* forces = &(*threadForce)[threadIndex][0];
ThreadData& data = *threadData[threadIndex];
data.energy = 0;
......@@ -201,15 +200,25 @@ void CpuCustomManyParticleForce::setUseCutoff(RealOpenMM distance) {
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);
void CpuCustomManyParticleForce::setPeriodic(RealVec* periodicBoxVectors) {
assert(cutoff);
assert(periodicBoxVectors[0][0] >= 2.0*cutoffDistance);
assert(periodicBoxVectors[1][1] >= 2.0*cutoffDistance);
assert(periodicBoxVectors[2][2] >= 2.0*cutoffDistance);
usePeriodic = true;
periodicBoxSize[0] = boxSize[0];
periodicBoxSize[1] = boxSize[1];
periodicBoxSize[2] = boxSize[2];
this->periodicBoxVectors[0] = periodicBoxVectors[0];
this->periodicBoxVectors[1] = periodicBoxVectors[1];
this->periodicBoxVectors[2] = periodicBoxVectors[2];
recipBoxSize[0] = (float) (1.0/periodicBoxVectors[0][0]);
recipBoxSize[1] = (float) (1.0/periodicBoxVectors[1][1]);
recipBoxSize[2] = (float) (1.0/periodicBoxVectors[2][2]);
periodicBoxVec4.resize(3);
periodicBoxVec4[0] = fvec4(periodicBoxVectors[0][0], periodicBoxVectors[0][1], periodicBoxVectors[0][2], 0);
periodicBoxVec4[1] = fvec4(periodicBoxVectors[1][0], periodicBoxVectors[1][1], periodicBoxVectors[1][2], 0);
periodicBoxVec4[2] = fvec4(periodicBoxVectors[2][0], periodicBoxVectors[2][1], periodicBoxVectors[2][2], 0);
triclinic = (periodicBoxVectors[0][1] != 0.0 || periodicBoxVectors[0][2] != 0.0 ||
periodicBoxVectors[1][0] != 0.0 || periodicBoxVectors[1][2] != 0.0 ||
periodicBoxVectors[2][0] != 0.0 || periodicBoxVectors[2][1] != 0.0);
}
void CpuCustomManyParticleForce::loopOverInteractions(vector<int>& availableParticles, vector<int>& particleSet, int loopIndex, int startIndex,
......@@ -394,9 +403,16 @@ void CpuCustomManyParticleForce::calculateOneIxn(vector<int>& particleSet, RealO
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) {
if (triclinic) {
deltaR -= periodicBoxVec4[2]*floorf(deltaR[2]*recipBoxSize[2]+0.5f);
deltaR -= periodicBoxVec4[1]*floorf(deltaR[1]*recipBoxSize[1]+0.5f);
deltaR -= periodicBoxVec4[0]*floorf(deltaR[0]*recipBoxSize[0]+0.5f);
}
else {
fvec4 base = round(deltaR*invBoxSize)*boxSize;
deltaR = deltaR-base;
}
}
r2 = dot3(deltaR, deltaR);
}
......
......@@ -98,15 +98,25 @@ void CpuCustomNonbondedForce::setUseSwitchingFunction(RealOpenMM distance) {
switchingDistance = distance;
}
void CpuCustomNonbondedForce::setPeriodic(RealVec& boxSize) {
void CpuCustomNonbondedForce::setPeriodic(RealVec* periodicBoxVectors) {
assert(cutoff);
assert(boxSize[0] >= 2.0*cutoffDistance);
assert(boxSize[1] >= 2.0*cutoffDistance);
assert(boxSize[2] >= 2.0*cutoffDistance);
assert(periodicBoxVectors[0][0] >= 2.0*cutoffDistance);
assert(periodicBoxVectors[1][1] >= 2.0*cutoffDistance);
assert(periodicBoxVectors[2][2] >= 2.0*cutoffDistance);
periodic = true;
periodicBoxSize[0] = boxSize[0];
periodicBoxSize[1] = boxSize[1];
periodicBoxSize[2] = boxSize[2];
this->periodicBoxVectors[0] = periodicBoxVectors[0];
this->periodicBoxVectors[1] = periodicBoxVectors[1];
this->periodicBoxVectors[2] = periodicBoxVectors[2];
recipBoxSize[0] = (float) (1.0/periodicBoxVectors[0][0]);
recipBoxSize[1] = (float) (1.0/periodicBoxVectors[1][1]);
recipBoxSize[2] = (float) (1.0/periodicBoxVectors[2][2]);
periodicBoxVec4.resize(3);
periodicBoxVec4[0] = fvec4(periodicBoxVectors[0][0], periodicBoxVectors[0][1], periodicBoxVectors[0][2], 0);
periodicBoxVec4[1] = fvec4(periodicBoxVectors[1][0], periodicBoxVectors[1][1], periodicBoxVectors[1][2], 0);
periodicBoxVec4[2] = fvec4(periodicBoxVectors[2][0], periodicBoxVectors[2][1], periodicBoxVectors[2][2], 0);
triclinic = (periodicBoxVectors[0][1] != 0.0 || periodicBoxVectors[0][2] != 0.0 ||
periodicBoxVectors[1][0] != 0.0 || periodicBoxVectors[1][2] != 0.0 ||
periodicBoxVectors[2][0] != 0.0 || periodicBoxVectors[2][1] != 0.0);
}
......@@ -155,8 +165,8 @@ void CpuCustomNonbondedForce::threadComputeForce(ThreadPool& threads, int thread
ReferenceForce::setVariable(ReferenceForce::getVariablePointer(data.energyExpression, iter->first), iter->second);
ReferenceForce::setVariable(ReferenceForce::getVariablePointer(data.forceExpression, iter->first), iter->second);
}
fvec4 boxSize(periodicBoxSize[0], periodicBoxSize[1], periodicBoxSize[2], 0);
fvec4 invBoxSize((1/periodicBoxSize[0]), (1/periodicBoxSize[1]), (1/periodicBoxSize[2]), 0);
fvec4 boxSize(periodicBoxVectors[0][0], periodicBoxVectors[1][1], periodicBoxVectors[2][2], 0);
fvec4 invBoxSize(recipBoxSize[0], recipBoxSize[1], recipBoxSize[2], 0);
if (groupInteractions.size() > 0) {
// The user has specified interaction groups, so compute only the requested interactions.
......@@ -266,8 +276,15 @@ void CpuCustomNonbondedForce::calculateOneIxn(int ii, int jj, ThreadData& data,
void CpuCustomNonbondedForce::getDeltaR(const fvec4& posI, const fvec4& posJ, fvec4& deltaR, float& r2, const fvec4& boxSize, const fvec4& invBoxSize) const {
deltaR = posJ-posI;
if (periodic) {
if (triclinic) {
deltaR -= periodicBoxVec4[2]*floorf(deltaR[2]*recipBoxSize[2]+0.5f);
deltaR -= periodicBoxVec4[1]*floorf(deltaR[1]*recipBoxSize[1]+0.5f);
deltaR -= periodicBoxVec4[0]*floorf(deltaR[0]*recipBoxSize[0]+0.5f);
}
else {
fvec4 base = round(deltaR*invBoxSize)*boxSize;
deltaR = deltaR-base;
}
}
r2 = dot3(deltaR, deltaR);
}
......@@ -73,6 +73,11 @@ static RealVec& extractBoxSize(ContextImpl& context) {
return *(RealVec*) data->periodicBoxSize;
}
static RealVec* extractBoxVectors(ContextImpl& context) {
ReferencePlatform::PlatformData* data = reinterpret_cast<ReferencePlatform::PlatformData*>(context.getPlatformData());
return (RealVec*) data->periodicBoxVectors;
}
static ReferenceConstraints& extractConstraints(ContextImpl& context) {
ReferencePlatform::PlatformData* data = reinterpret_cast<ReferencePlatform::PlatformData*>(context.getPlatformData());
return *(ReferenceConstraints*) data->constraints;
......@@ -147,19 +152,36 @@ public:
AlignedArray<float>& posq = data.posq;
vector<RealVec>& posData = extractPositions(context);
RealVec boxSize = extractBoxSize(context);
double invBoxSize[3] = {1/boxSize[0], 1/boxSize[1], 1/boxSize[2]};
RealVec* boxVectors = extractBoxVectors(context);
double boxSize[3] = {boxVectors[0][0], boxVectors[1][1], boxVectors[2][2]};
double invBoxSize[3] = {1/boxVectors[0][0], 1/boxVectors[1][1], 1/boxVectors[2][2]};
bool triclinic = (boxVectors[0][1] != 0 || boxVectors[0][2] != 0 || boxVectors[1][0] != 0 || boxVectors[1][2] != 0 || boxVectors[2][0] != 0 || boxVectors[2][1] != 0);
int numParticles = context.getSystem().getNumParticles();
int numThreads = threads.getNumThreads();
int start = threadIndex*numParticles/numThreads;
int end = (threadIndex+1)*numParticles/numThreads;
if (data.isPeriodic)
for (int i = start; i < end; i++)
if (data.isPeriodic) {
if (triclinic) {
for (int i = start; i < end; i++) {
RealVec pos = posData[i];
pos -= boxVectors[2]*floor(pos[2]*invBoxSize[2]);
pos -= boxVectors[1]*floor(pos[1]*invBoxSize[1]);
pos -= boxVectors[0]*floor(pos[0]*invBoxSize[0]);
posq[4*i] = (float) pos[0];
posq[4*i+1] = (float) pos[1];
posq[4*i+2] = (float) pos[2];
}
}
else {
for (int i = start; i < end; i++) {
for (int j = 0; j < 3; j++) {
RealOpenMM x = posData[i][j];
double base = floor(x*invBoxSize[j])*boxSize[j];
posq[4*i+j] = (float) (x-base);
}
}
}
}
else
for (int i = start; i < end; i++) {
posq[4*i] = (float) posData[i][0];
......@@ -499,8 +521,7 @@ double CpuCalcNonbondedForceKernel::execute(ContextImpl& context, bool includeFo
AlignedArray<float>& posq = data.posq;
vector<RealVec>& posData = extractPositions(context);
vector<RealVec>& forceData = extractForces(context);
RealVec boxSize = extractBoxSize(context);
float floatBoxSize[3] = {(float) boxSize[0], (float) boxSize[1], (float) boxSize[2]};
RealVec* boxVectors = extractBoxVectors(context);
double energy = (includeReciprocal ? ewaldSelfEnergy : 0.0);
bool ewald = (nonbondedMethod == Ewald);
bool pme = (nonbondedMethod == PME);
......@@ -546,16 +567,17 @@ double CpuCalcNonbondedForceKernel::execute(ContextImpl& context, bool includeFo
}
}
if (needRecompute) {
neighborList->computeNeighborList(numParticles, posq, exclusions, floatBoxSize, data.isPeriodic, nonbondedCutoff+padding, data.threads);
neighborList->computeNeighborList(numParticles, posq, exclusions, boxVectors, data.isPeriodic, nonbondedCutoff+padding, data.threads);
lastPositions = posData;
}
nonbonded->setUseCutoff(nonbondedCutoff, *neighborList, rfDielectric);
}
if (data.isPeriodic) {
RealVec* boxVectors = extractBoxVectors(context);
double minAllowedSize = 1.999999*nonbondedCutoff;
if (boxSize[0] < minAllowedSize || boxSize[1] < minAllowedSize || boxSize[2] < minAllowedSize)
if (boxVectors[0][0] < minAllowedSize || boxVectors[1][1] < minAllowedSize || boxVectors[2][2] < minAllowedSize)
throw OpenMMException("The periodic box size has decreased to less than twice the nonbonded cutoff.");
nonbonded->setPeriodic(floatBoxSize);
nonbonded->setPeriodic(boxVectors);
}
if (ewald)
nonbonded->setUseEwald(ewaldAlpha, kmax[0], kmax[1], kmax[2]);
......@@ -569,8 +591,8 @@ double CpuCalcNonbondedForceKernel::execute(ContextImpl& context, bool includeFo
if (includeReciprocal) {
if (useOptimizedPme) {
PmeIO io(&posq[0], &data.threadForce[0][0], numParticles);
Vec3 periodicBoxSize(boxSize[0], boxSize[1], boxSize[2]);
optimizedPme.getAs<CalcPmeReciprocalForceKernel>().beginComputation(io, periodicBoxSize, includeEnergy);
Vec3 periodicBoxVectors[3] = {boxVectors[0], boxVectors[1], boxVectors[2]};
optimizedPme.getAs<CalcPmeReciprocalForceKernel>().beginComputation(io, periodicBoxVectors, includeEnergy);
nonbondedEnergy += optimizedPme.getAs<CalcPmeReciprocalForceKernel>().finishComputation(io);
}
else
......@@ -582,7 +604,7 @@ double CpuCalcNonbondedForceKernel::execute(ContextImpl& context, bool includeFo
ReferenceLJCoulomb14 nonbonded14;
refBondForce.calculateForce(num14, bonded14IndexArray, posData, bonded14ParamArray, forceData, includeEnergy ? &energy : NULL, nonbonded14);
if (data.isPeriodic)
energy += dispersionCoefficient/(boxSize[0]*boxSize[1]*boxSize[2]);
energy += dispersionCoefficient/(boxVectors[0][0]*boxVectors[1][1]*boxVectors[2][2]);
}
return energy;
}
......@@ -736,19 +758,18 @@ void CpuCalcCustomNonbondedForceKernel::initialize(const System& system, const C
double CpuCalcCustomNonbondedForceKernel::execute(ContextImpl& context, bool includeForces, bool includeEnergy) {
vector<RealVec>& posData = extractPositions(context);
vector<RealVec>& forceData = extractForces(context);
RealVec& box = extractBoxSize(context);
float floatBoxSize[3] = {(float) box[0], (float) box[1], (float) box[2]};
RealVec* boxVectors = extractBoxVectors(context);
double energy = 0;
bool periodic = (nonbondedMethod == CutoffPeriodic);
if (nonbondedMethod != NoCutoff) {
neighborList->computeNeighborList(numParticles, data.posq, exclusions, floatBoxSize, data.isPeriodic, nonbondedCutoff, data.threads);
neighborList->computeNeighborList(numParticles, data.posq, exclusions, boxVectors, data.isPeriodic, nonbondedCutoff, data.threads);
nonbonded->setUseCutoff(nonbondedCutoff, *neighborList);
}
if (periodic) {
double minAllowedSize = 2*nonbondedCutoff;
if (box[0] < minAllowedSize || box[1] < minAllowedSize || box[2] < minAllowedSize)
if (boxVectors[0][0] < minAllowedSize || boxVectors[1][1] < minAllowedSize || boxVectors[2][2] < minAllowedSize)
throw OpenMMException("The periodic box size has decreased to less than twice the nonbonded cutoff.");
nonbonded->setPeriodic(box);
nonbonded->setPeriodic(boxVectors);
}
bool globalParamsChanged = false;
for (int i = 0; i < (int) globalParameterNames.size(); i++) {
......@@ -767,7 +788,7 @@ double CpuCalcCustomNonbondedForceKernel::execute(ContextImpl& context, bool inc
longRangeCoefficient = CustomNonbondedForceImpl::calcLongRangeCorrection(*forceCopy, context.getOwner());
hasInitializedLongRangeCorrection = true;
}
energy += longRangeCoefficient/(box[0]*box[1]*box[2]);
energy += longRangeCoefficient/(boxVectors[0][0]*boxVectors[1][1]*boxVectors[2][2]);
return energy;
}
......@@ -975,13 +996,12 @@ void CpuCalcCustomGBForceKernel::initialize(const System& system, const CustomGB
double CpuCalcCustomGBForceKernel::execute(ContextImpl& context, bool includeForces, bool includeEnergy) {
vector<RealVec>& forceData = extractForces(context);
RealOpenMM energy = 0;
RealVec& box = extractBoxSize(context);
float floatBoxSize[3] = {(float) box[0], (float) box[1], (float) box[2]};
RealVec* boxVectors = extractBoxVectors(context);
if (data.isPeriodic)
ixn->setPeriodic(extractBoxSize(context));
if (nonbondedMethod != NoCutoff) {
vector<set<int> > noExclusions(numParticles);
neighborList->computeNeighborList(numParticles, data.posq, exclusions, floatBoxSize, data.isPeriodic, nonbondedCutoff, data.threads);
neighborList->computeNeighborList(numParticles, data.posq, exclusions, boxVectors, data.isPeriodic, nonbondedCutoff, data.threads);
ixn->setUseCutoff(nonbondedCutoff, *neighborList);
}
map<string, double> globalParameters;
......@@ -1038,6 +1058,7 @@ void CpuCalcCustomManyParticleForceKernel::initialize(const System& system, cons
ixn = new CpuCustomManyParticleForce(force, data.threads);
nonbondedMethod = CalcCustomManyParticleForceKernel::NonbondedMethod(force.getNonbondedMethod());
cutoffDistance = force.getCutoffDistance();
data.isPeriodic = (nonbondedMethod == CutoffPeriodic);
}
double CpuCalcCustomManyParticleForceKernel::execute(ContextImpl& context, bool includeForces, bool includeEnergy) {
......@@ -1045,11 +1066,11 @@ double CpuCalcCustomManyParticleForceKernel::execute(ContextImpl& context, bool
for (int i = 0; i < (int) globalParameterNames.size(); i++)
globalParameters[globalParameterNames[i]] = context.getParameter(globalParameterNames[i]);
if (nonbondedMethod == CutoffPeriodic) {
RealVec& box = extractBoxSize(context);
RealVec* boxVectors = extractBoxVectors(context);
double minAllowedSize = 2*cutoffDistance;
if (box[0] < minAllowedSize || box[1] < minAllowedSize || box[2] < minAllowedSize)
if (boxVectors[0][0] < minAllowedSize || boxVectors[1][1] < minAllowedSize || boxVectors[2][2] < minAllowedSize)
throw OpenMMException("The periodic box size has decreased to less than twice the nonbonded cutoff.");
ixn->setPeriodic(box);
ixn->setPeriodic(boxVectors);
}
double energy = 0;
ixn->calculateIxn(data.posq, particleParamArray, globalParameters, data.threadForce, includeForces, includeEnergy, energy);
......
This diff is collapsed.
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