Commit 8df54762 authored by Lee-Ping Wang's avatar Lee-Ping Wang
Browse files

Merge branch 'master' of github.com:leeping/openmm

parents 3cb25ad8 59854c5e
/* Portions copyright (c) 2006 Stanford University and Simbios.
* Contributors: Pande Group
*
* 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 __ReferenceConstraint_H__
#define __ReferenceConstraint_H__
// ---------------------------------------------------------------------------------------
class ReferenceConstraint {
private:
public:
/**---------------------------------------------------------------------------------------
Constructor
--------------------------------------------------------------------------------------- */
ReferenceConstraint( );
/**---------------------------------------------------------------------------------------
Destructor
--------------------------------------------------------------------------------------- */
~ReferenceConstraint( );
};
class ReferenceShakeConstraint : public ReferenceConstraint {
private:
// force heavy atom into index 0
int _atomIndices[2];
RealOpenMM _inverseMasses[2];
RealOpenMM _constraintDistance;
public:
/**---------------------------------------------------------------------------------------
Constructor
@param atomIndex1 atom index 1
@param atomIndex2 atom index 2
@param distance distance constraint
@param inverseMass1 inverseMass of atom 1
@param inverseMass2 inverseMass of atom 2
--------------------------------------------------------------------------------------- */
ReferenceShakeConstraint( int atomIndex1, int atomIndex2, RealOpenMM distance,
RealOpenMM inverseMass1, RealOpenMM inverseMass2 );
/**---------------------------------------------------------------------------------------
Destructor
--------------------------------------------------------------------------------------- */
~ReferenceShakeConstraint( );
/**---------------------------------------------------------------------------------------
Get constraint distance
@return distance
--------------------------------------------------------------------------------------- */
RealOpenMM getConstraintDistance( void );
/**---------------------------------------------------------------------------------------
Get inverse mass of heavy atom
@return inverse mass
--------------------------------------------------------------------------------------- */
RealOpenMM getHeavyAtomInverseMass( void );
/**---------------------------------------------------------------------------------------
Get inverse mass of light atom
@return inverse mass
--------------------------------------------------------------------------------------- */
RealOpenMM getLightAtomInverseMass( void );
/**---------------------------------------------------------------------------------------
Get index of heavy atom
@return index
--------------------------------------------------------------------------------------- */
int getHeavyAtomIndex( void );
/**---------------------------------------------------------------------------------------
Get index of light atom
@return index
--------------------------------------------------------------------------------------- */
int getLightAtomIndex( void );
};
// ---------------------------------------------------------------------------------------
#endif // __ReferenceShakeConstraint_H__
/* Portions copyright (c) 2009 Stanford University and Simbios.
/* Portions copyright (c) 2009-2013 Stanford University and Simbios.
* Contributors: Peter Eastman
*
* Permission is hereby granted, free of charge, to any person obtaining
......@@ -36,54 +36,34 @@ public:
virtual ~ReferenceConstraintAlgorithm() {};
/**---------------------------------------------------------------------------------------
Get the constraint tolerance
--------------------------------------------------------------------------------------- */
/**
* Get the constraint tolerance.
*/
virtual RealOpenMM getTolerance() const = 0;
/**---------------------------------------------------------------------------------------
Set the constraint tolerance
--------------------------------------------------------------------------------------- */
/**
* Set the constraint tolerance.
*/
virtual void setTolerance(RealOpenMM tolerance) = 0;
/**---------------------------------------------------------------------------------------
Apply constraint algorithm
@param numberOfAtoms number of atoms
@param atomCoordinates atom coordinates
@param atomCoordinatesP atom coordinates prime
@param inverseMasses 1/mass
@return SimTKOpenMMCommon::DefaultReturn if converge; else
return SimTKOpenMMCommon::ErrorReturn
--------------------------------------------------------------------------------------- */
virtual int apply(int numberOfAtoms, std::vector<OpenMM::RealVec>& atomCoordinates,
/**
* Apply the constraint algorithm.
*
* @param atomCoordinates the original atom coordinates
* @param atomCoordinatesP the new atom coordinates
* @param inverseMasses 1/mass
*/
virtual void apply(std::vector<OpenMM::RealVec>& atomCoordinates,
std::vector<OpenMM::RealVec>& atomCoordinatesP, std::vector<RealOpenMM>& inverseMasses) = 0;
/**---------------------------------------------------------------------------------------
Apply constraint algorithm to velocities.
@param numberOfAtoms number of atoms
@param atomCoordinates atom coordinates
@param velocities atom velocities
@param inverseMasses 1/mass
@return SimTKOpenMMCommon::DefaultReturn if converge; else
return SimTKOpenMMCommon::ErrorReturn
--------------------------------------------------------------------------------------- */
virtual int applyToVelocities(int numberOfAtoms, std::vector<OpenMM::RealVec>& atomCoordinates,
/**
* Apply the constraint algorithm to velocities.
*
* @param atomCoordinates the atom coordinates
* @param atomCoordinatesP the velocities to modify
* @param inverseMasses 1/mass
*/
virtual void applyToVelocities(std::vector<OpenMM::RealVec>& atomCoordinates,
std::vector<OpenMM::RealVec>& velocities, std::vector<RealOpenMM>& inverseMasses) = 0;
};
......
#ifndef OPENMM_REFERENCECONSTRAINTS_H_
#define OPENMM_REFERENCECONSTRAINTS_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 "ReferenceConstraintAlgorithm.h"
#include "ReferenceCCMAAlgorithm.h"
#include "ReferenceSETTLEAlgorithm.h"
#include "openmm/System.h"
namespace OpenMM {
/**
* This class uses multiple algorithms to apply constraints as efficiently as possible. It identifies clusters
* of three atoms that can be handled by SETTLE, and creates a ReferenceSETTLEAlgorithm object to handle them.
* It then creates a ReferenceCCMAAlgorithm object to handle any remaining constraints.
*/
class OPENMM_EXPORT ReferenceConstraints : public ReferenceConstraintAlgorithm {
public:
ReferenceConstraints(const System& system, RealOpenMM tolerance);
~ReferenceConstraints();
/**
* Get the constraint tolerance.
*/
RealOpenMM getTolerance() const;
/**
* Set the constraint tolerance.
*/
void setTolerance(RealOpenMM tolerance);
/**
* Apply the constraint algorithm.
*
* @param atomCoordinates the original atom coordinates
* @param atomCoordinatesP the new atom coordinates
* @param inverseMasses 1/mass
*/
void apply(std::vector<OpenMM::RealVec>& atomCoordinates, std::vector<OpenMM::RealVec>& atomCoordinatesP, std::vector<RealOpenMM>& inverseMasses);
/**
* Apply the constraint algorithm to velocities.
*
* @param atomCoordinates the atom coordinates
* @param atomCoordinatesP the velocities to modify
* @param inverseMasses 1/mass
*/
void applyToVelocities(std::vector<OpenMM::RealVec>& atomCoordinates, std::vector<OpenMM::RealVec>& velocities, std::vector<RealOpenMM>& inverseMasses);
private:
RealOpenMM tolerance;
ReferenceCCMAAlgorithm* ccma;
ReferenceSETTLEAlgorithm* settle;
};
} // namespace OpenMM
#endif /*OPENMM_REFERENCECONSTRAINTS_H_*/
/* Portions copyright (c) 2010 Stanford University and Simbios.
/* Portions copyright (c) 2010-2013 Stanford University and Simbios.
* Contributors: Peter Eastman
*
* Permission is hereby granted, free of charge, to any person obtaining
......@@ -25,17 +25,20 @@
#define __ReferenceCustomAngleIxn_H__
#include "ReferenceBondIxn.h"
#include "lepton/ExpressionProgram.h"
#include "lepton/CompiledExpression.h"
// ---------------------------------------------------------------------------------------
class ReferenceCustomAngleIxn : public ReferenceBondIxn {
private:
Lepton::ExpressionProgram energyExpression;
Lepton::ExpressionProgram forceExpression;
std::vector<std::string> paramNames;
std::map<std::string, double> globalParameters;
Lepton::CompiledExpression energyExpression;
Lepton::CompiledExpression forceExpression;
std::vector<double*> energyParams;
std::vector<double*> forceParams;
double* energyTheta;
double* forceTheta;
int numParameters;
public:
......@@ -45,7 +48,7 @@ class ReferenceCustomAngleIxn : public ReferenceBondIxn {
--------------------------------------------------------------------------------------- */
ReferenceCustomAngleIxn(const Lepton::ExpressionProgram& energyExpression, const Lepton::ExpressionProgram& forceExpression,
ReferenceCustomAngleIxn(const Lepton::CompiledExpression& energyExpression, const Lepton::CompiledExpression& forceExpression,
const std::vector<std::string>& parameterNames, std::map<std::string, double> globalParameters);
/**---------------------------------------------------------------------------------------
......
/* Portions copyright (c) 2009 Stanford University and Simbios.
/* Portions copyright (c) 2009-2013 Stanford University and Simbios.
* Contributors: Peter Eastman
*
* Permission is hereby granted, free of charge, to any person obtaining
......@@ -26,17 +26,20 @@
#define __ReferenceCustomBondIxn_H__
#include "ReferenceBondIxn.h"
#include "lepton/ExpressionProgram.h"
#include "lepton/CompiledExpression.h"
// ---------------------------------------------------------------------------------------
class ReferenceCustomBondIxn : public ReferenceBondIxn {
private:
Lepton::ExpressionProgram energyExpression;
Lepton::ExpressionProgram forceExpression;
std::vector<std::string> paramNames;
std::map<std::string, double> globalParameters;
Lepton::CompiledExpression energyExpression;
Lepton::CompiledExpression forceExpression;
std::vector<double*> energyParams;
std::vector<double*> forceParams;
double* energyR;
double* forceR;
int numParameters;
public:
......@@ -46,7 +49,7 @@ class ReferenceCustomBondIxn : public ReferenceBondIxn {
--------------------------------------------------------------------------------------- */
ReferenceCustomBondIxn(const Lepton::ExpressionProgram& energyExpression, const Lepton::ExpressionProgram& forceExpression,
ReferenceCustomBondIxn(const Lepton::CompiledExpression& energyExpression, const Lepton::CompiledExpression& forceExpression,
const std::vector<std::string>& parameterNames, std::map<std::string, double> globalParameters);
/**---------------------------------------------------------------------------------------
......
/* Portions copyright (c) 2009 Stanford University and Simbios.
/* Portions copyright (c) 2009-2013 Stanford University and Simbios.
* Contributors: Peter Eastman
*
* Permission is hereby granted, free of charge, to any person obtaining
......@@ -26,19 +26,26 @@
#define __ReferenceCustomExternalIxn_H__
#include "ReferenceCustomExternalIxn.h"
#include "lepton/ExpressionProgram.h"
#include "lepton/CompiledExpression.h"
// ---------------------------------------------------------------------------------------
class ReferenceCustomExternalIxn {
private:
Lepton::ExpressionProgram energyExpression;
Lepton::ExpressionProgram forceExpressionX;
Lepton::ExpressionProgram forceExpressionY;
Lepton::ExpressionProgram forceExpressionZ;
std::vector<std::string> paramNames;
std::map<std::string, double> globalParameters;
Lepton::CompiledExpression energyExpression;
Lepton::CompiledExpression forceExpressionX;
Lepton::CompiledExpression forceExpressionY;
Lepton::CompiledExpression forceExpressionZ;
std::vector<double*> energyParams;
std::vector<double*> forceXParams;
std::vector<double*> forceYParams;
std::vector<double*> forceZParams;
double *energyX, *energyY, *energyZ;
double *forceXX, *forceXY, *forceXZ;
double *forceYX, *forceYY, *forceYZ;
double *forceZX, *forceZY, *forceZZ;
int numParameters;
public:
......@@ -48,8 +55,8 @@ class ReferenceCustomExternalIxn {
--------------------------------------------------------------------------------------- */
ReferenceCustomExternalIxn(const Lepton::ExpressionProgram& energyExpression, const Lepton::ExpressionProgram& forceExpressionX,
const Lepton::ExpressionProgram& forceExpressionY, const Lepton::ExpressionProgram& forceExpressionZ,
ReferenceCustomExternalIxn(const Lepton::CompiledExpression& energyExpression, const Lepton::CompiledExpression& forceExpressionX,
const Lepton::CompiledExpression& forceExpressionY, const Lepton::CompiledExpression& forceExpressionZ,
const std::vector<std::string>& parameterNames, std::map<std::string, double> globalParameters);
/**---------------------------------------------------------------------------------------
......
/* Portions copyright (c) 2009 Stanford University and Simbios.
/* Portions copyright (c) 2009-2013 Stanford University and Simbios.
* Contributors: Peter Eastman
*
* Permission is hereby granted, free of charge, to any person obtaining
......@@ -27,7 +27,7 @@
#include "ReferencePairIxn.h"
#include "ReferenceNeighborList.h"
#include "lepton/ExpressionProgram.h"
#include "lepton/CompiledExpression.h"
#include <map>
#include <set>
#include <utility>
......@@ -45,10 +45,13 @@ class ReferenceCustomNonbondedIxn {
const OpenMM::NeighborList* neighborList;
RealOpenMM periodicBoxSize[3];
RealOpenMM cutoffDistance, switchingDistance;
Lepton::ExpressionProgram energyExpression;
Lepton::ExpressionProgram forceExpression;
Lepton::CompiledExpression energyExpression;
Lepton::CompiledExpression forceExpression;
std::vector<std::string> paramNames;
std::vector<std::string> particleParamNames;
std::vector<double*> energyParticleParams;
std::vector<double*> forceParticleParams;
double* energyR;
double* forceR;
std::vector<std::pair<std::set<int>, std::set<int> > > interactionGroups;
/**---------------------------------------------------------------------------------------
......@@ -65,9 +68,8 @@ class ReferenceCustomNonbondedIxn {
--------------------------------------------------------------------------------------- */
void calculateOneIxn( int atom1, int atom2, std::vector<OpenMM::RealVec>& atomCoordinates,
std::map<std::string, double>& variables, std::vector<OpenMM::RealVec>& forces,
RealOpenMM* energyByAtom, RealOpenMM* totalEnergy ) const;
void calculateOneIxn( int atom1, int atom2, std::vector<OpenMM::RealVec>& atomCoordinates, std::vector<OpenMM::RealVec>& forces,
RealOpenMM* energyByAtom, RealOpenMM* totalEnergy );
public:
......@@ -78,7 +80,7 @@ class ReferenceCustomNonbondedIxn {
--------------------------------------------------------------------------------------- */
ReferenceCustomNonbondedIxn(const Lepton::ExpressionProgram& energyExpression, const Lepton::ExpressionProgram& forceExpression,
ReferenceCustomNonbondedIxn(const Lepton::CompiledExpression& energyExpression, const Lepton::CompiledExpression& forceExpression,
const std::vector<std::string>& parameterNames);
/**---------------------------------------------------------------------------------------
......@@ -153,7 +155,7 @@ class ReferenceCustomNonbondedIxn {
void calculatePairIxn( int numberOfAtoms, std::vector<OpenMM::RealVec>& atomCoordinates,
RealOpenMM** atomParameters, std::vector<std::set<int> >& exclusions,
RealOpenMM* fixedParameters, const std::map<std::string, double>& globalParameters,
std::vector<OpenMM::RealVec>& forces, RealOpenMM* energyByAtom, RealOpenMM* totalEnergy ) const;
std::vector<OpenMM::RealVec>& forces, RealOpenMM* energyByAtom, RealOpenMM* totalEnergy );
// ---------------------------------------------------------------------------------------
......
/* Portions copyright (c) 2010 Stanford University and Simbios.
/* Portions copyright (c) 2010-2013 Stanford University and Simbios.
* Contributors: Peter Eastman
*
* Permission is hereby granted, free of charge, to any person obtaining
......@@ -25,17 +25,20 @@
#define __ReferenceCustomTorsionIxn_H__
#include "ReferenceBondIxn.h"
#include "lepton/ExpressionProgram.h"
#include "lepton/CompiledExpression.h"
// ---------------------------------------------------------------------------------------
class ReferenceCustomTorsionIxn : public ReferenceBondIxn {
private:
Lepton::ExpressionProgram energyExpression;
Lepton::ExpressionProgram forceExpression;
std::vector<std::string> paramNames;
std::map<std::string, double> globalParameters;
Lepton::CompiledExpression energyExpression;
Lepton::CompiledExpression forceExpression;
std::vector<double*> energyParams;
std::vector<double*> forceParams;
double* energyTheta;
double* forceTheta;
int numParameters;
public:
......@@ -45,7 +48,7 @@ class ReferenceCustomTorsionIxn : public ReferenceBondIxn {
--------------------------------------------------------------------------------------- */
ReferenceCustomTorsionIxn(const Lepton::ExpressionProgram& energyExpression, const Lepton::ExpressionProgram& forceExpression,
ReferenceCustomTorsionIxn(const Lepton::CompiledExpression& energyExpression, const Lepton::CompiledExpression& forceExpression,
const std::vector<std::string>& parameterNames, std::map<std::string, double> globalParameters);
/**---------------------------------------------------------------------------------------
......
/* Portions copyright (c) 2006 Stanford University and Simbios.
/* Portions copyright (c) 2006-2013 Stanford University and Simbios.
* Contributors: Pande Group
*
* Permission is hereby granted, free of charge, to any person obtaining
......@@ -25,6 +24,7 @@
#ifndef __ReferenceForce_H__
#define __ReferenceForce_H__
#include "lepton/CompiledExpression.h"
#include "openmm/internal/windowsExport.h"
// ---------------------------------------------------------------------------------------
......@@ -109,6 +109,18 @@ class OPENMM_EXPORT ReferenceForce {
static void getDeltaROnly( const RealOpenMM* atomCoordinatesI, const RealOpenMM* atomCoordinatesJ,
RealOpenMM* deltaR );
/**
* Get a pointer to the memory for setting a variable in a CompiledExpression. If the expression
* does not use the specified variable, return NULL.
*/
static double* getVariablePointer(Lepton::CompiledExpression& expression, const std::string& name);
/**
* Set the value of a variable in a CompiledExpression, using the pointer that was returned by getVariablePointer().
*/
static void setVariable(double* pointer, double value);
};
// ---------------------------------------------------------------------------------------
......
......@@ -36,6 +36,7 @@
#include "openmm/kernels.h"
#include "SimTKOpenMMRealType.h"
#include "ReferenceNeighborList.h"
#include "lepton/CompiledExpression.h"
#include "lepton/ExpressionProgram.h"
class CpuObc;
......@@ -316,7 +317,7 @@ private:
int numBonds;
int **bondIndexArray;
RealOpenMM **bondParamArray;
Lepton::ExpressionProgram energyExpression, forceExpression;
Lepton::CompiledExpression energyExpression, forceExpression;
std::vector<std::string> parameterNames, globalParameterNames;
};
......@@ -392,7 +393,7 @@ private:
int numAngles;
int **angleIndexArray;
RealOpenMM **angleParamArray;
Lepton::ExpressionProgram energyExpression, forceExpression;
Lepton::CompiledExpression energyExpression, forceExpression;
std::vector<std::string> parameterNames, globalParameterNames;
};
......@@ -534,7 +535,7 @@ private:
int numTorsions;
int **torsionIndexArray;
RealOpenMM **torsionParamArray;
Lepton::ExpressionProgram energyExpression, forceExpression;
Lepton::CompiledExpression energyExpression, forceExpression;
std::vector<std::string> parameterNames, globalParameterNames;
};
......@@ -621,7 +622,7 @@ private:
CustomNonbondedForce* forceCopy;
std::map<std::string, double> globalParamValues;
std::vector<std::set<int> > exclusions;
Lepton::ExpressionProgram energyExpression, forceExpression;
Lepton::CompiledExpression energyExpression, forceExpression;
std::vector<std::string> parameterNames, globalParameterNames;
std::vector<std::pair<std::set<int>, std::set<int> > > interactionGroups;
NonbondedMethod nonbondedMethod;
......@@ -781,7 +782,7 @@ private:
int numParticles;
std::vector<int> particles;
RealOpenMM **particleParamArray;
Lepton::ExpressionProgram energyExpression, forceExpressionX, forceExpressionY, forceExpressionZ;
Lepton::CompiledExpression energyExpression, forceExpressionX, forceExpressionY, forceExpressionZ;
std::vector<std::string> parameterNames, globalParameterNames;
};
......
......@@ -54,11 +54,11 @@ pme_t;
*/
int
pme_init(pme_t * ppme,
RealOpenMM ewaldcoeff,
RealOpenMM ewaldcoeff,
int natoms,
const int ngrid[3],
const int ngrid[3],
int pme_order,
RealOpenMM epsilon_r);
RealOpenMM epsilon_r);
/*
* Evaluate reciprocal space PME energy and forces.
......@@ -75,9 +75,9 @@ pme_init(pme_t * ppme,
*/
int
pme_exec(pme_t pme,
std::vector<OpenMM::RealVec>& atomCoordinates,
std::vector<OpenMM::RealVec>& forces,
RealOpenMM ** atomParameters,
const std::vector<OpenMM::RealVec>& atomCoordinates,
std::vector<OpenMM::RealVec>& forces,
const std::vector<RealOpenMM>& charges,
const RealOpenMM periodicBoxSize[3],
RealOpenMM * energy,
RealOpenMM pme_virial[3][3]);
......
......@@ -25,6 +25,7 @@
#ifndef __ReferencePairIxn_H__
#define __ReferencePairIxn_H__
#include "RealVec.h"
#include "openmm/internal/windowsExport.h"
// ---------------------------------------------------------------------------------------
......
#ifndef OPENMM_REFERENCESETTLEALGORITHM_H_
#define OPENMM_REFERENCESETTLEALGORITHM_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 "ReferenceConstraintAlgorithm.h"
#include <vector>
namespace OpenMM {
/**
* This implements the SETTLE algorithm for constraining water molecules.
*/
class OPENMM_EXPORT ReferenceSETTLEAlgorithm : public ReferenceConstraintAlgorithm {
public:
ReferenceSETTLEAlgorithm(const std::vector<int>& atom1, const std::vector<int>& atom2, const std::vector<int>& atom3,
const std::vector<RealOpenMM>& distance1, const std::vector<RealOpenMM>& distance2, std::vector<RealOpenMM>& masses, RealOpenMM tolerance);
/**
* Get the constraint tolerance.
*/
RealOpenMM getTolerance() const;
/**
* Set the constraint tolerance.
*/
void setTolerance(RealOpenMM tolerance);
/**
* Apply the constraint algorithm.
*
* @param atomCoordinates the original atom coordinates
* @param atomCoordinatesP the new atom coordinates
* @param inverseMasses 1/mass
*/
void apply(std::vector<OpenMM::RealVec>& atomCoordinates, std::vector<OpenMM::RealVec>& atomCoordinatesP, std::vector<RealOpenMM>& inverseMasses);
/**
* Apply the constraint algorithm to velocities.
*
* @param atomCoordinates the atom coordinates
* @param atomCoordinatesP the velocities to modify
* @param inverseMasses 1/mass
*/
void applyToVelocities(std::vector<OpenMM::RealVec>& atomCoordinates, std::vector<OpenMM::RealVec>& velocities, std::vector<RealOpenMM>& inverseMasses);
private:
RealOpenMM tolerance;
std::vector<int> atom1;
std::vector<int> atom2;
std::vector<int> atom3;
std::vector<RealOpenMM> distance1;
std::vector<RealOpenMM> distance2;
std::vector<RealOpenMM> masses;
};
} // namespace OpenMM
#endif /*OPENMM_REFERENCESETTLEALGORITHM_H_*/
......@@ -38,6 +38,7 @@
#include "ReferenceBrownianDynamics.h"
#include "ReferenceCCMAAlgorithm.h"
#include "ReferenceCMAPTorsionIxn.h"
#include "ReferenceConstraints.h"
#include "ReferenceCustomAngleIxn.h"
#include "ReferenceCustomBondIxn.h"
#include "ReferenceCustomCompoundBondIxn.h"
......@@ -132,20 +133,6 @@ static RealVec& extractBoxSize(ContextImpl& context) {
return *(RealVec*) data->periodicBoxSize;
}
static void findAnglesForCCMA(const System& system, vector<ReferenceCCMAAlgorithm::AngleInfo>& angles) {
for (int i = 0; i < system.getNumForces(); i++) {
const HarmonicAngleForce* force = dynamic_cast<const HarmonicAngleForce*>(&system.getForce(i));
if (force != NULL) {
for (int j = 0; j < force->getNumAngles(); j++) {
int atom1, atom2, atom3;
double angle, k;
force->getAngleParameters(j, atom1, atom2, atom3, angle, k);
angles.push_back(ReferenceCCMAAlgorithm::AngleInfo(atom1, atom2, atom3, (RealOpenMM)angle));
}
}
}
}
/**
* Compute the kinetic energy of the system, possibly shifting the velocities in time to account
* for a leapfrog integrator.
......@@ -173,7 +160,7 @@ static double computeShiftedKineticEnergy(ContextImpl& context, vector<double>&
for (int i = 0; i < numParticles; i++)
inverseMasses[i] = (masses[i] == 0 ? 0 : 1/masses[i]);
constraints->setTolerance(1e-4);
constraints->applyToVelocities(numParticles, posData, shiftedVel, inverseMasses);
constraints->applyToVelocities(posData, shiftedVel, inverseMasses);
}
// Compute the kinetic energy.
......@@ -315,17 +302,16 @@ void ReferenceApplyConstraintsKernel::initialize(const System& system) {
masses[i] = static_cast<RealOpenMM>(system.getParticleMass(i));
inverseMasses[i] = 1.0/masses[i];
}
numConstraints = system.getNumConstraints();
constraintIndices.resize(numConstraints);
constraintDistances.resize(numConstraints);
for (int i = 0; i < numConstraints; ++i) {
for (int i = 0; i < system.getNumConstraints(); ++i) {
int particle1, particle2;
double distance;
system.getConstraintParameters(i, particle1, particle2, distance);
constraintIndices[i].first = particle1;
constraintIndices[i].second = particle2;
constraintDistances[i] = static_cast<RealOpenMM>(distance);
if (system.getParticleMass(particle1) != 0 || system.getParticleMass(particle2) != 0) {
constraintIndices.push_back(make_pair(particle1, particle2));
constraintDistances.push_back(distance);
}
}
numConstraints = constraintIndices.size();
}
ReferenceApplyConstraintsKernel::~ReferenceApplyConstraintsKernel() {
......@@ -334,27 +320,21 @@ ReferenceApplyConstraintsKernel::~ReferenceApplyConstraintsKernel() {
}
void ReferenceApplyConstraintsKernel::apply(ContextImpl& context, double tol) {
if (constraints == NULL) {
vector<ReferenceCCMAAlgorithm::AngleInfo> angles;
findAnglesForCCMA(context.getSystem(), angles);
constraints = new ReferenceCCMAAlgorithm(context.getSystem().getNumParticles(), numConstraints, constraintIndices, constraintDistances, masses, angles, tol);
}
if (constraints == NULL)
constraints = new ReferenceConstraints(context.getSystem(), (RealOpenMM) tol);
vector<RealVec>& positions = extractPositions(context);
constraints->setTolerance(tol);
constraints->apply(data.numParticles, positions, positions, inverseMasses);
constraints->apply(positions, positions, inverseMasses);
ReferenceVirtualSites::computePositions(context.getSystem(), positions);
}
void ReferenceApplyConstraintsKernel::applyToVelocities(ContextImpl& context, double tol) {
if (constraints == NULL) {
vector<ReferenceCCMAAlgorithm::AngleInfo> angles;
findAnglesForCCMA(context.getSystem(), angles);
constraints = new ReferenceCCMAAlgorithm(context.getSystem().getNumParticles(), numConstraints, constraintIndices, constraintDistances, masses, angles, tol);
}
if (constraints == NULL)
constraints = new ReferenceConstraints(context.getSystem(), (RealOpenMM) tol);
vector<RealVec>& positions = extractPositions(context);
vector<RealVec>& velocities = extractVelocities(context);
constraints->setTolerance(tol);
constraints->applyToVelocities(data.numParticles, positions, velocities, inverseMasses);
constraints->applyToVelocities(positions, velocities, inverseMasses);
}
void ReferenceVirtualSitesKernel::initialize(const System& system) {
......@@ -440,8 +420,8 @@ void ReferenceCalcCustomBondForceKernel::initialize(const System& system, const
// Parse the expression used to calculate the force.
Lepton::ParsedExpression expression = Lepton::Parser::parse(force.getEnergyFunction()).optimize();
energyExpression = expression.createProgram();
forceExpression = expression.differentiate("r").optimize().createProgram();
energyExpression = expression.createCompiledExpression();
forceExpression = expression.differentiate("r").createCompiledExpression();
for (int i = 0; i < numParameters; i++)
parameterNames.push_back(force.getPerBondParameterName(i));
for (int i = 0; i < force.getNumGlobalParameters(); i++)
......@@ -554,8 +534,8 @@ void ReferenceCalcCustomAngleForceKernel::initialize(const System& system, const
// Parse the expression used to calculate the force.
Lepton::ParsedExpression expression = Lepton::Parser::parse(force.getEnergyFunction()).optimize();
energyExpression = expression.createProgram();
forceExpression = expression.differentiate("theta").optimize().createProgram();
energyExpression = expression.createCompiledExpression();
forceExpression = expression.differentiate("theta").createCompiledExpression();
for (int i = 0; i < numParameters; i++)
parameterNames.push_back(force.getPerAngleParameterName(i));
for (int i = 0; i < force.getNumGlobalParameters(); i++)
......@@ -764,8 +744,8 @@ void ReferenceCalcCustomTorsionForceKernel::initialize(const System& system, con
// Parse the expression used to calculate the force.
Lepton::ParsedExpression expression = Lepton::Parser::parse(force.getEnergyFunction()).optimize();
energyExpression = expression.createProgram();
forceExpression = expression.differentiate("theta").optimize().createProgram();
energyExpression = expression.createCompiledExpression();
forceExpression = expression.differentiate("theta").createCompiledExpression();
for (int i = 0; i < numParameters; i++)
parameterNames.push_back(force.getPerTorsionParameterName(i));
for (int i = 0; i < force.getNumGlobalParameters(); i++)
......@@ -1048,8 +1028,8 @@ void ReferenceCalcCustomNonbondedForceKernel::initialize(const System& system, c
// Parse the various expressions used to calculate the force.
Lepton::ParsedExpression expression = Lepton::Parser::parse(force.getEnergyFunction(), functions).optimize();
energyExpression = expression.createProgram();
forceExpression = expression.differentiate("r").optimize().createProgram();
energyExpression = expression.createCompiledExpression();
forceExpression = expression.differentiate("r").createCompiledExpression();
for (int i = 0; i < numParameters; i++)
parameterNames.push_back(force.getPerParticleParameterName(i));
for (int i = 0; i < force.getNumGlobalParameters(); i++) {
......@@ -1446,10 +1426,10 @@ void ReferenceCalcCustomExternalForceKernel::initialize(const System& system, co
// Parse the expression used to calculate the force.
Lepton::ParsedExpression expression = Lepton::Parser::parse(force.getEnergyFunction()).optimize();
energyExpression = expression.createProgram();
forceExpressionX = expression.differentiate("x").optimize().createProgram();
forceExpressionY = expression.differentiate("y").optimize().createProgram();
forceExpressionZ = expression.differentiate("z").optimize().createProgram();
energyExpression = expression.createCompiledExpression();
forceExpressionX = expression.differentiate("x").createCompiledExpression();
forceExpressionY = expression.differentiate("y").createCompiledExpression();
forceExpressionZ = expression.differentiate("z").createCompiledExpression();
for (int i = 0; i < numParameters; i++)
parameterNames.push_back(force.getPerParticleParameterName(i));
for (int i = 0; i < force.getNumGlobalParameters(); i++)
......@@ -1715,20 +1695,19 @@ void ReferenceIntegrateVerletStepKernel::initialize(const System& system, const
masses.resize(numParticles);
for (int i = 0; i < numParticles; ++i)
masses[i] = static_cast<RealOpenMM>(system.getParticleMass(i));
numConstraints = system.getNumConstraints();
vector<pair<int, int> > constraintIndices(numConstraints);
vector<RealOpenMM> constraintDistances(numConstraints);
for (int i = 0; i < numConstraints; ++i) {
vector<pair<int, int> > constraintIndices;
vector<RealOpenMM> constraintDistances;
for (int i = 0; i < system.getNumConstraints(); ++i) {
int particle1, particle2;
double distance;
system.getConstraintParameters(i, particle1, particle2, distance);
constraintIndices[i].first = particle1;
constraintIndices[i].second = particle2;
constraintDistances[i] = static_cast<RealOpenMM>(distance);
if (system.getParticleMass(particle1) != 0 || system.getParticleMass(particle2) != 0) {
constraintIndices.push_back(make_pair(particle1, particle2));
constraintDistances.push_back(distance);
}
}
vector<ReferenceCCMAAlgorithm::AngleInfo> angles;
findAnglesForCCMA(system, angles);
constraints = new ReferenceCCMAAlgorithm(system.getNumParticles(), numConstraints, constraintIndices, constraintDistances, masses, angles, (RealOpenMM)integrator.getConstraintTolerance());
numConstraints = constraintIndices.size();
constraints = new ReferenceConstraints(system, (RealOpenMM) integrator.getConstraintTolerance());
}
void ReferenceIntegrateVerletStepKernel::execute(ContextImpl& context, const VerletIntegrator& integrator) {
......@@ -1767,21 +1746,20 @@ void ReferenceIntegrateLangevinStepKernel::initialize(const System& system, cons
masses.resize(numParticles);
for (int i = 0; i < numParticles; ++i)
masses[i] = static_cast<RealOpenMM>(system.getParticleMass(i));
numConstraints = system.getNumConstraints();
vector<pair<int, int> > constraintIndices(numConstraints);
vector<RealOpenMM> constraintDistances(numConstraints);
for (int i = 0; i < numConstraints; ++i) {
vector<pair<int, int> > constraintIndices;
vector<RealOpenMM> constraintDistances;
for (int i = 0; i < system.getNumConstraints(); ++i) {
int particle1, particle2;
double distance;
system.getConstraintParameters(i, particle1, particle2, distance);
constraintIndices[i].first = particle1;
constraintIndices[i].second = particle2;
constraintDistances[i] = static_cast<RealOpenMM>(distance);
if (system.getParticleMass(particle1) != 0 || system.getParticleMass(particle2) != 0) {
constraintIndices.push_back(make_pair(particle1, particle2));
constraintDistances.push_back(distance);
}
}
numConstraints = constraintIndices.size();
SimTKOpenMMUtilities::setRandomNumberSeed((unsigned int) integrator.getRandomNumberSeed());
vector<ReferenceCCMAAlgorithm::AngleInfo> angles;
findAnglesForCCMA(system, angles);
constraints = new ReferenceCCMAAlgorithm(system.getNumParticles(), numConstraints, constraintIndices, constraintDistances, masses, angles, (RealOpenMM)integrator.getConstraintTolerance());
constraints = new ReferenceConstraints(system, (RealOpenMM) integrator.getConstraintTolerance());
}
void ReferenceIntegrateLangevinStepKernel::execute(ContextImpl& context, const LangevinIntegrator& integrator) {
......@@ -1829,21 +1807,20 @@ void ReferenceIntegrateBrownianStepKernel::initialize(const System& system, cons
masses.resize(numParticles);
for (int i = 0; i < numParticles; ++i)
masses[i] = static_cast<RealOpenMM>(system.getParticleMass(i));
numConstraints = system.getNumConstraints();
vector<pair<int, int> > constraintIndices(numConstraints);
vector<RealOpenMM> constraintDistances(numConstraints);
for (int i = 0; i < numConstraints; ++i) {
vector<pair<int, int> > constraintIndices;
vector<RealOpenMM> constraintDistances;
for (int i = 0; i < system.getNumConstraints(); ++i) {
int particle1, particle2;
double distance;
system.getConstraintParameters(i, particle1, particle2, distance);
constraintIndices[i].first = particle1;
constraintIndices[i].second = particle2;
constraintDistances[i] = static_cast<RealOpenMM>(distance);
if (system.getParticleMass(particle1) != 0 || system.getParticleMass(particle2) != 0) {
constraintIndices.push_back(make_pair(particle1, particle2));
constraintDistances.push_back(distance);
}
}
numConstraints = constraintIndices.size();
SimTKOpenMMUtilities::setRandomNumberSeed((unsigned int) integrator.getRandomNumberSeed());
vector<ReferenceCCMAAlgorithm::AngleInfo> angles;
findAnglesForCCMA(system, angles);
constraints = new ReferenceCCMAAlgorithm(system.getNumParticles(), numConstraints, constraintIndices, constraintDistances, masses, angles, (RealOpenMM)integrator.getConstraintTolerance());
constraints = new ReferenceConstraints(system, (RealOpenMM) integrator.getConstraintTolerance());
}
void ReferenceIntegrateBrownianStepKernel::execute(ContextImpl& context, const BrownianIntegrator& integrator) {
......@@ -1890,21 +1867,20 @@ void ReferenceIntegrateVariableLangevinStepKernel::initialize(const System& syst
masses.resize(numParticles);
for (int i = 0; i < numParticles; ++i)
masses[i] = static_cast<RealOpenMM>(system.getParticleMass(i));
numConstraints = system.getNumConstraints();
vector<pair<int, int> > constraintIndices(numConstraints);
vector<RealOpenMM> constraintDistances(numConstraints);
for (int i = 0; i < numConstraints; ++i) {
vector<pair<int, int> > constraintIndices;
vector<RealOpenMM> constraintDistances;
for (int i = 0; i < system.getNumConstraints(); ++i) {
int particle1, particle2;
double distance;
system.getConstraintParameters(i, particle1, particle2, distance);
constraintIndices[i].first = particle1;
constraintIndices[i].second = particle2;
constraintDistances[i] = static_cast<RealOpenMM>(distance);
if (system.getParticleMass(particle1) != 0 || system.getParticleMass(particle2) != 0) {
constraintIndices.push_back(make_pair(particle1, particle2));
constraintDistances.push_back(distance);
}
}
numConstraints = constraintIndices.size();
SimTKOpenMMUtilities::setRandomNumberSeed((unsigned int) integrator.getRandomNumberSeed());
vector<ReferenceCCMAAlgorithm::AngleInfo> angles;
findAnglesForCCMA(system, angles);
constraints = new ReferenceCCMAAlgorithm(system.getNumParticles(), numConstraints, constraintIndices, constraintDistances, masses, angles, (RealOpenMM)integrator.getConstraintTolerance());
constraints = new ReferenceConstraints(system, (RealOpenMM) integrator.getConstraintTolerance());
}
double ReferenceIntegrateVariableLangevinStepKernel::execute(ContextImpl& context, const VariableLangevinIntegrator& integrator, double maxTime) {
......@@ -1952,20 +1928,19 @@ void ReferenceIntegrateVariableVerletStepKernel::initialize(const System& system
masses.resize(numParticles);
for (int i = 0; i < numParticles; ++i)
masses[i] = static_cast<RealOpenMM>(system.getParticleMass(i));
numConstraints = system.getNumConstraints();
vector<pair<int, int> > constraintIndices(numConstraints);
vector<RealOpenMM> constraintDistances(numConstraints);
for (int i = 0; i < numConstraints; ++i) {
vector<pair<int, int> > constraintIndices;
vector<RealOpenMM> constraintDistances;
for (int i = 0; i < system.getNumConstraints(); ++i) {
int particle1, particle2;
double distance;
system.getConstraintParameters(i, particle1, particle2, distance);
constraintIndices[i].first = particle1;
constraintIndices[i].second = particle2;
constraintDistances[i] = static_cast<RealOpenMM>(distance);
if (system.getParticleMass(particle1) != 0 || system.getParticleMass(particle2) != 0) {
constraintIndices.push_back(make_pair(particle1, particle2));
constraintDistances.push_back(distance);
}
}
vector<ReferenceCCMAAlgorithm::AngleInfo> angles;
findAnglesForCCMA(system, angles);
constraints = new ReferenceCCMAAlgorithm(system.getNumParticles(), numConstraints, constraintIndices, constraintDistances, masses, angles, (RealOpenMM)integrator.getConstraintTolerance());
numConstraints = constraintIndices.size();
constraints = new ReferenceConstraints(system, (RealOpenMM) integrator.getConstraintTolerance());
}
double ReferenceIntegrateVariableVerletStepKernel::execute(ContextImpl& context, const VariableVerletIntegrator& integrator, double maxTime) {
......@@ -2008,17 +1983,18 @@ void ReferenceIntegrateCustomStepKernel::initialize(const System& system, const
masses.resize(numParticles);
for (int i = 0; i < numParticles; ++i)
masses[i] = static_cast<RealOpenMM>(system.getParticleMass(i));
numConstraints = system.getNumConstraints();
vector<pair<int, int> > constraintIndices(numConstraints);
vector<RealOpenMM> constraintDistances(numConstraints);
for (int i = 0; i < numConstraints; ++i) {
vector<pair<int, int> > constraintIndices;
vector<RealOpenMM> constraintDistances;
for (int i = 0; i < system.getNumConstraints(); ++i) {
int particle1, particle2;
double distance;
system.getConstraintParameters(i, particle1, particle2, distance);
constraintIndices[i].first = particle1;
constraintIndices[i].second = particle2;
constraintDistances[i] = static_cast<RealOpenMM>(distance);
if (system.getParticleMass(particle1) != 0 || system.getParticleMass(particle2) != 0) {
constraintIndices.push_back(make_pair(particle1, particle2));
constraintDistances.push_back(distance);
}
}
numConstraints = constraintIndices.size();
perDofValues.resize(integrator.getNumPerDofVariables());
for (int i = 0; i < (int) perDofValues.size(); i++)
perDofValues[i].resize(numParticles);
......@@ -2026,9 +2002,8 @@ void ReferenceIntegrateCustomStepKernel::initialize(const System& system, const
// Create the computation objects.
dynamics = new ReferenceCustomDynamics(system.getNumParticles(), integrator);
vector<ReferenceCCMAAlgorithm::AngleInfo> angles;
findAnglesForCCMA(system, angles);
constraints = new ReferenceCCMAAlgorithm(system.getNumParticles(), numConstraints, constraintIndices, constraintDistances, masses, angles, (RealOpenMM)integrator.getConstraintTolerance());
SimTKOpenMMUtilities::setRandomNumberSeed((unsigned int) integrator.getRandomNumberSeed());
constraints = new ReferenceConstraints(system, (RealOpenMM) integrator.getConstraintTolerance());
dynamics->setReferenceConstraintAlgorithm(constraints);
}
......
/* Portions copyright (c) 2006-2012 Stanford University and Simbios.
/* Portions copyright (c) 2006-2013 Stanford University and Simbios.
* Contributors: Peter Eastman, Pande Group
*
* Permission is hereby granted, free of charge, to any person obtaining
......@@ -161,8 +161,8 @@ void ReferenceBrownianDynamics::update(const OpenMM::System& system, vector<Real
}
}
ReferenceConstraintAlgorithm* referenceConstraintAlgorithm = getReferenceConstraintAlgorithm();
if( referenceConstraintAlgorithm )
referenceConstraintAlgorithm->apply( numberOfAtoms, atomCoordinates, xPrime, inverseMasses );
if (referenceConstraintAlgorithm)
referenceConstraintAlgorithm->apply(atomCoordinates, xPrime, inverseMasses);
// Update the positions and velocities.
......
/* Portions copyright (c) 2006-2009 Stanford University and Simbios.
/* Portions copyright (c) 2006-2013 Stanford University and Simbios.
* Contributors: Peter Eastman, Pande Group
*
* Permission is hereby granted, free of charge, to any person obtaining
......@@ -31,6 +31,7 @@
#include "ReferenceCCMAAlgorithm.h"
#include "ReferenceDynamics.h"
#include "quern.h"
#include "openmm/OpenMMException.h"
#include "openmm/Vec3.h"
#include <map>
......@@ -38,501 +39,269 @@ using std::map;
using std::pair;
using std::vector;
using std::set;
using OpenMM::OpenMMException;
using OpenMM::Vec3;
using OpenMM::RealVec;
/**---------------------------------------------------------------------------------------
ReferenceCCMAAlgorithm constructor
@param numberOfAtoms number of atoms
@param numberOfConstraints number of constraints
@param atomIndices atom indices for contraints
@param distance distances for constraints
@param masses atom masses
@param angles angle force field terms
@param tolerance constraint tolerance
--------------------------------------------------------------------------------------- */
ReferenceCCMAAlgorithm::ReferenceCCMAAlgorithm( int numberOfAtoms,
ReferenceCCMAAlgorithm::ReferenceCCMAAlgorithm(int numberOfAtoms,
int numberOfConstraints,
const vector<pair<int, int> >& atomIndices,
const vector<RealOpenMM>& distance,
vector<RealOpenMM>& masses,
vector<AngleInfo>& angles,
RealOpenMM tolerance){
// ---------------------------------------------------------------------------------------
// static const char* methodName = "\nReferenceCCMAAlgorithm::ReferenceCCMAAlgorithm";
static const RealOpenMM zero = 0.0;
static const RealOpenMM one = 1.0;
static const RealOpenMM two = 2.0;
static const RealOpenMM three = 3.0;
static const RealOpenMM oneM = -1.0;
static const int threeI = 3;
// ---------------------------------------------------------------------------------------
_numberOfConstraints = numberOfConstraints;
_atomIndices = atomIndices;
_distance = distance;
_maximumNumberOfIterations = 150;
_tolerance = tolerance;
_hasInitializedMasses = false;
// work arrays
if (_numberOfConstraints > 0) {
_r_ij.resize(numberOfConstraints);
_d_ij2 = SimTKOpenMMUtilities::allocateOneDRealOpenMMArray( numberOfConstraints, NULL, 1, zero, "dij_2" );
_distanceTolerance = SimTKOpenMMUtilities::allocateOneDRealOpenMMArray( numberOfConstraints, NULL, 1, zero, "distanceTolerance" );
_reducedMasses = SimTKOpenMMUtilities::allocateOneDRealOpenMMArray( numberOfConstraints, NULL, 1, zero, "reducedMasses" );
}
if (numberOfConstraints > 0)
{
// Compute the constraint coupling matrix
vector<vector<int> > atomAngles(numberOfAtoms);
for (int i = 0; i < (int) angles.size(); i++)
atomAngles[angles[i].atom2].push_back(i);
vector<vector<pair<int, double> > > matrix(numberOfConstraints);
for (int j = 0; j < numberOfConstraints; j++) {
for (int k = 0; k < numberOfConstraints; k++) {
if (j == k) {
matrix[j].push_back(pair<int, double>(j, 1.0));
continue;
}
double scale;
int atomj0 = _atomIndices[j].first;
int atomj1 = _atomIndices[j].second;
int atomk0 = _atomIndices[k].first;
int atomk1 = _atomIndices[k].second;
RealOpenMM invMass0 = one/masses[atomj0];
RealOpenMM invMass1 = one/masses[atomj1];
int atoma, atomb, atomc;
if (atomj0 == atomk0) {
atoma = atomj1;
atomb = atomj0;
atomc = atomk1;
scale = invMass0/(invMass0+invMass1);
}
else if (atomj1 == atomk1) {
atoma = atomj0;
atomb = atomj1;
atomc = atomk0;
scale = invMass1/(invMass0+invMass1);
}
else if (atomj0 == atomk1) {
atoma = atomj1;
atomb = atomj0;
atomc = atomk0;
scale = invMass0/(invMass0+invMass1);
}
else if (atomj1 == atomk0) {
atoma = atomj0;
atomb = atomj1;
atomc = atomk1;
scale = invMass1/(invMass0+invMass1);
}
else
continue; // These constraints are not connected.
// Look for a third constraint forming a triangle with these two.
bool foundConstraint = false;
for (int other = 0; other < numberOfConstraints; other++) {
if ((_atomIndices[other].first == atoma && _atomIndices[other].second == atomc) || (_atomIndices[other].first == atomc && _atomIndices[other].second == atoma)) {
double d1 = _distance[j];
double d2 = _distance[k];
double d3 = _distance[other];
matrix[j].push_back(pair<int, double>(k, scale*(d1*d1+d2*d2-d3*d3)/(2.0*d1*d2)));
foundConstraint = true;
break;
}
}
if (!foundConstraint) {
// We didn't find one, so look for an angle force field term.
const vector<int>& angleCandidates = atomAngles[atomb];
for (vector<int>::const_iterator iter = angleCandidates.begin(); iter != angleCandidates.end(); iter++) {
const AngleInfo& angle = angles[*iter];
if ((angle.atom1 == atoma && angle.atom3 == atomc) || (angle.atom3 == atoma && angle.atom1 == atomc)) {
matrix[j].push_back(pair<int, double>(k, scale*cos(angle.angle)));
break;
}
}
}
}
}
// Invert it using QR.
vector<int> matrixRowStart;
vector<int> matrixColIndex;
vector<double> matrixValue;
for (int i = 0; i < numberOfConstraints; i++) {
matrixRowStart.push_back(matrixValue.size());
for (int j = 0; j < (int) matrix[i].size(); j++) {
pair<int, double> element = matrix[i][j];
matrixColIndex.push_back(element.first);
matrixValue.push_back(element.second);
}
}
matrixRowStart.push_back(matrixValue.size());
int *qRowStart, *qColIndex, *rRowStart, *rColIndex;
double *qValue, *rValue;
QUERN_compute_qr(numberOfConstraints, numberOfConstraints, &matrixRowStart[0], &matrixColIndex[0], &matrixValue[0], NULL,
&qRowStart, &qColIndex, &qValue, &rRowStart, &rColIndex, &rValue);
vector<double> rhs(numberOfConstraints);
_matrix.resize(numberOfConstraints);
for (int i = 0; i < numberOfConstraints; i++) {
// Extract column i of the inverse matrix.
for (int j = 0; j < numberOfConstraints; j++)
rhs[j] = (i == j ? 1.0 : 0.0);
QUERN_multiply_with_q_transpose(numberOfConstraints, qRowStart, qColIndex, qValue, &rhs[0]);
QUERN_solve_with_r(numberOfConstraints, rRowStart, rColIndex, rValue, &rhs[0], &rhs[0]);
for (int j = 0; j < numberOfConstraints; j++) {
double value = rhs[j]*_distance[i]/_distance[j];
if (FABS((RealOpenMM)value) > 0.02)
_matrix[j].push_back(pair<int, RealOpenMM>(i, (RealOpenMM) value));
}
}
QUERN_free_result(qRowStart, qColIndex, qValue);
QUERN_free_result(rRowStart, rColIndex, rValue);
}
}
/**---------------------------------------------------------------------------------------
RealOpenMM tolerance) {
_numberOfConstraints = numberOfConstraints;
_atomIndices = atomIndices;
_distance = distance;
ReferenceCCMAAlgorithm destructor
_maximumNumberOfIterations = 150;
_tolerance = tolerance;
_hasInitializedMasses = false;
--------------------------------------------------------------------------------------- */
ReferenceCCMAAlgorithm::~ReferenceCCMAAlgorithm( ){
// ---------------------------------------------------------------------------------------
// static const char* methodName = "\nReferenceCCMAAlgorithm::~ReferenceCCMAAlgorithm";
// ---------------------------------------------------------------------------------------
// work arrays
if (_numberOfConstraints > 0) {
SimTKOpenMMUtilities::freeOneDRealOpenMMArray( _d_ij2, "d_ij2" );
SimTKOpenMMUtilities::freeOneDRealOpenMMArray( _distanceTolerance, "distanceTolerance" );
SimTKOpenMMUtilities::freeOneDRealOpenMMArray( _reducedMasses, "reducedMasses" );
_r_ij.resize(numberOfConstraints);
_d_ij2 = SimTKOpenMMUtilities::allocateOneDRealOpenMMArray(numberOfConstraints, NULL, 1, 0.0, "dij_2");
_distanceTolerance = SimTKOpenMMUtilities::allocateOneDRealOpenMMArray(numberOfConstraints, NULL, 1, 0.0, "distanceTolerance");
_reducedMasses = SimTKOpenMMUtilities::allocateOneDRealOpenMMArray(numberOfConstraints, NULL, 1, 0.0, "reducedMasses");
}
if (numberOfConstraints > 0)
{
// Compute the constraint coupling matrix
vector<vector<int> > atomAngles(numberOfAtoms);
for (int i = 0; i < (int) angles.size(); i++)
atomAngles[angles[i].atom2].push_back(i);
vector<vector<pair<int, double> > > matrix(numberOfConstraints);
for (int j = 0; j < numberOfConstraints; j++) {
for (int k = 0; k < numberOfConstraints; k++) {
if (j == k) {
matrix[j].push_back(pair<int, double>(j, 1.0));
continue;
}
double scale;
int atomj0 = _atomIndices[j].first;
int atomj1 = _atomIndices[j].second;
int atomk0 = _atomIndices[k].first;
int atomk1 = _atomIndices[k].second;
RealOpenMM invMass0 = 1/masses[atomj0];
RealOpenMM invMass1 = 1/masses[atomj1];
int atoma, atomb, atomc;
if (atomj0 == atomk0) {
atoma = atomj1;
atomb = atomj0;
atomc = atomk1;
scale = invMass0/(invMass0+invMass1);
}
else if (atomj1 == atomk1) {
atoma = atomj0;
atomb = atomj1;
atomc = atomk0;
scale = invMass1/(invMass0+invMass1);
}
else if (atomj0 == atomk1) {
atoma = atomj1;
atomb = atomj0;
atomc = atomk0;
scale = invMass0/(invMass0+invMass1);
}
else if (atomj1 == atomk0) {
atoma = atomj0;
atomb = atomj1;
atomc = atomk1;
scale = invMass1/(invMass0+invMass1);
}
else
continue; // These constraints are not connected.
// Look for a third constraint forming a triangle with these two.
bool foundConstraint = false;
for (int other = 0; other < numberOfConstraints; other++) {
if ((_atomIndices[other].first == atoma && _atomIndices[other].second == atomc) || (_atomIndices[other].first == atomc && _atomIndices[other].second == atoma)) {
double d1 = _distance[j];
double d2 = _distance[k];
double d3 = _distance[other];
matrix[j].push_back(pair<int, double>(k, scale*(d1*d1+d2*d2-d3*d3)/(2.0*d1*d2)));
foundConstraint = true;
break;
}
}
if (!foundConstraint) {
// We didn't find one, so look for an angle force field term.
const vector<int>& angleCandidates = atomAngles[atomb];
for (vector<int>::const_iterator iter = angleCandidates.begin(); iter != angleCandidates.end(); iter++) {
const AngleInfo& angle = angles[*iter];
if ((angle.atom1 == atoma && angle.atom3 == atomc) || (angle.atom3 == atoma && angle.atom1 == atomc)) {
matrix[j].push_back(pair<int, double>(k, scale*cos(angle.angle)));
break;
}
}
}
}
}
// Invert it using QR.
vector<int> matrixRowStart;
vector<int> matrixColIndex;
vector<double> matrixValue;
for (int i = 0; i < numberOfConstraints; i++) {
matrixRowStart.push_back(matrixValue.size());
for (int j = 0; j < (int) matrix[i].size(); j++) {
pair<int, double> element = matrix[i][j];
matrixColIndex.push_back(element.first);
matrixValue.push_back(element.second);
}
}
matrixRowStart.push_back(matrixValue.size());
int *qRowStart, *qColIndex, *rRowStart, *rColIndex;
double *qValue, *rValue;
QUERN_compute_qr(numberOfConstraints, numberOfConstraints, &matrixRowStart[0], &matrixColIndex[0], &matrixValue[0], NULL,
&qRowStart, &qColIndex, &qValue, &rRowStart, &rColIndex, &rValue);
vector<double> rhs(numberOfConstraints);
_matrix.resize(numberOfConstraints);
for (int i = 0; i < numberOfConstraints; i++) {
// Extract column i of the inverse matrix.
for (int j = 0; j < numberOfConstraints; j++)
rhs[j] = (i == j ? 1.0 : 0.0);
QUERN_multiply_with_q_transpose(numberOfConstraints, qRowStart, qColIndex, qValue, &rhs[0]);
QUERN_solve_with_r(numberOfConstraints, rRowStart, rColIndex, rValue, &rhs[0], &rhs[0]);
for (int j = 0; j < numberOfConstraints; j++) {
double value = rhs[j]*_distance[i]/_distance[j];
if (FABS((RealOpenMM)value) > 0.02)
_matrix[j].push_back(pair<int, RealOpenMM>(i, (RealOpenMM) value));
}
}
QUERN_free_result(qRowStart, qColIndex, qValue);
QUERN_free_result(rRowStart, rColIndex, rValue);
}
}
/**---------------------------------------------------------------------------------------
Get number of constraints
@return number of constraints
--------------------------------------------------------------------------------------- */
int ReferenceCCMAAlgorithm::getNumberOfConstraints( void ) const {
// ---------------------------------------------------------------------------------------
// static const char* methodName = "\nReferenceCCMAAlgorithm::getNumberOfConstraints";
// ---------------------------------------------------------------------------------------
return _numberOfConstraints;
ReferenceCCMAAlgorithm::~ReferenceCCMAAlgorithm() {
if (_numberOfConstraints > 0) {
SimTKOpenMMUtilities::freeOneDRealOpenMMArray(_d_ij2, "d_ij2");
SimTKOpenMMUtilities::freeOneDRealOpenMMArray(_distanceTolerance, "distanceTolerance");
SimTKOpenMMUtilities::freeOneDRealOpenMMArray(_reducedMasses, "reducedMasses");
}
}
/**---------------------------------------------------------------------------------------
Get maximum number of iterations
@return maximum number of iterations
--------------------------------------------------------------------------------------- */
int ReferenceCCMAAlgorithm::getMaximumNumberOfIterations( void ) const {
// ---------------------------------------------------------------------------------------
// static const char* methodName = "\nReferenceCCMAAlgorithm::getMaximumNumberOfIterations";
// ---------------------------------------------------------------------------------------
return _maximumNumberOfIterations;
int ReferenceCCMAAlgorithm::getNumberOfConstraints() const {
return _numberOfConstraints;
}
/**---------------------------------------------------------------------------------------
Set maximum number of iterations
@param maximumNumberOfIterations new maximum number of iterations
--------------------------------------------------------------------------------------- */
void ReferenceCCMAAlgorithm::setMaximumNumberOfIterations( int maximumNumberOfIterations ){
// ---------------------------------------------------------------------------------------
// static const char* methodName = "\nReferenceCCMAAlgorithm::setMaximumNumberOfIterations";
// ---------------------------------------------------------------------------------------
_maximumNumberOfIterations = maximumNumberOfIterations;
int ReferenceCCMAAlgorithm::getMaximumNumberOfIterations() const {
return _maximumNumberOfIterations;
}
/**---------------------------------------------------------------------------------------
Get tolerance
@return tolerance
--------------------------------------------------------------------------------------- */
RealOpenMM ReferenceCCMAAlgorithm::getTolerance( void ) const {
// ---------------------------------------------------------------------------------------
// static const char* methodName = "\nReferenceCCMAAlgorithm::getTolerance";
// ---------------------------------------------------------------------------------------
return _tolerance;
void ReferenceCCMAAlgorithm::setMaximumNumberOfIterations(int maximumNumberOfIterations) {
_maximumNumberOfIterations = maximumNumberOfIterations;
}
/**---------------------------------------------------------------------------------------
Set tolerance
@param tolerance new tolerance
--------------------------------------------------------------------------------------- */
void ReferenceCCMAAlgorithm::setTolerance( RealOpenMM tolerance ){
// ---------------------------------------------------------------------------------------
// static const char* methodName = "\nReferenceCCMAAlgorithm::setTolerance";
// ---------------------------------------------------------------------------------------
_tolerance = tolerance;;
RealOpenMM ReferenceCCMAAlgorithm::getTolerance() const {
return _tolerance;
}
/**---------------------------------------------------------------------------------------
Apply CCMA algorithm
@param numberOfAtoms number of atoms
@param atomCoordinates atom coordinates
@param atomCoordinatesP atom coordinates prime
@param inverseMasses 1/mass
@return SimTKOpenMMCommon::DefaultReturn if converge; else
return SimTKOpenMMCommon::ErrorReturn
--------------------------------------------------------------------------------------- */
void ReferenceCCMAAlgorithm::setTolerance(RealOpenMM tolerance) {
_tolerance = tolerance;
}
int ReferenceCCMAAlgorithm::apply( int numberOfAtoms, vector<RealVec>& atomCoordinates,
void ReferenceCCMAAlgorithm::apply(vector<RealVec>& atomCoordinates,
vector<RealVec>& atomCoordinatesP,
vector<RealOpenMM>& inverseMasses ){
return applyConstraints(numberOfAtoms, atomCoordinates, atomCoordinatesP, inverseMasses, false);
vector<RealOpenMM>& inverseMasses) {
applyConstraints(atomCoordinates, atomCoordinatesP, inverseMasses, false);
}
/**---------------------------------------------------------------------------------------
Apply constraint algorithm to velocities.
@param numberOfAtoms number of atoms
@param atomCoordinates atom coordinates
@param velocities atom velocities
@param inverseMasses 1/mass
@return SimTKOpenMMCommon::DefaultReturn if converge; else
return SimTKOpenMMCommon::ErrorReturn
--------------------------------------------------------------------------------------- */
int ReferenceCCMAAlgorithm::applyToVelocities(int numberOfAtoms, std::vector<OpenMM::RealVec>& atomCoordinates,
void ReferenceCCMAAlgorithm::applyToVelocities(std::vector<OpenMM::RealVec>& atomCoordinates,
std::vector<OpenMM::RealVec>& velocities, std::vector<RealOpenMM>& inverseMasses) {
return applyConstraints(numberOfAtoms, atomCoordinates, velocities, inverseMasses, true);
applyConstraints(atomCoordinates, velocities, inverseMasses, true);
}
int ReferenceCCMAAlgorithm::applyConstraints(int numberOfAtoms, vector<RealVec>& atomCoordinates,
void ReferenceCCMAAlgorithm::applyConstraints(vector<RealVec>& atomCoordinates,
vector<RealVec>& atomCoordinatesP,
vector<RealOpenMM>& inverseMasses, bool constrainingVelocities){
// ---------------------------------------------------------------------------------------
static const char* methodName = "\nReferenceCCMAAlgorithm::apply";
static const RealOpenMM zero = 0.0;
static const RealOpenMM one = 1.0;
static const RealOpenMM two = 2.0;
static const RealOpenMM half = 0.5;
static const RealOpenMM epsilon6 = (RealOpenMM) 1.0e-06;
// ---------------------------------------------------------------------------------------
// temp arrays
vector<RealVec>& r_ij = _r_ij;
RealOpenMM* d_ij2 = _d_ij2;
RealOpenMM* reducedMasses = _reducedMasses;
// calculate reduced masses on 1st pass
if( !_hasInitializedMasses ){
_hasInitializedMasses = true;
for( int ii = 0; ii < _numberOfConstraints; ii++ ){
int atomI = _atomIndices[ii].first;
int atomJ = _atomIndices[ii].second;
reducedMasses[ii] = half/( inverseMasses[atomI] + inverseMasses[atomJ] );
}
}
// setup: r_ij for each (i,j) constraint
RealOpenMM tolerance = getTolerance();
tolerance *= two;
for( int ii = 0; ii < _numberOfConstraints; ii++ ){
int atomI = _atomIndices[ii].first;
int atomJ = _atomIndices[ii].second;
r_ij[ii] = atomCoordinates[atomI] - atomCoordinates[atomJ];
d_ij2[ii] = r_ij[ii].dot(r_ij[ii]);
}
RealOpenMM lowerTol = one-two*getTolerance()+getTolerance()*getTolerance();
RealOpenMM upperTol = one+two*getTolerance()+getTolerance()*getTolerance();
// main loop
int iterations = 0;
int numberConverged = 0;
vector<RealOpenMM> constraintDelta(_numberOfConstraints);
vector<RealOpenMM> tempDelta(_numberOfConstraints);
while (iterations < getMaximumNumberOfIterations()) {
numberConverged = 0;
for( int ii = 0; ii < _numberOfConstraints; ii++ ){
int atomI = _atomIndices[ii].first;
int atomJ = _atomIndices[ii].second;
RealVec rp_ij = atomCoordinatesP[atomI] - atomCoordinatesP[atomJ];
if (constrainingVelocities) {
RealOpenMM rrpr = rp_ij.dot(r_ij[ii]);
constraintDelta[ii] = -2*reducedMasses[ii]*rrpr/d_ij2[ii];
if (fabs(constraintDelta[ii]) <= getTolerance())
numberConverged++;
}
else {
RealOpenMM rp2 = rp_ij.dot(rp_ij);
RealOpenMM dist2 = _distance[ii]*_distance[ii];
RealOpenMM diff = dist2 - rp2;
constraintDelta[ii] = zero;
RealOpenMM rrpr = DOT3( rp_ij, r_ij[ii] );
if( rrpr < d_ij2[ii]*epsilon6 ){
std::stringstream message;
message << iterations <<" "<<atomI<<" "<<atomJ<< " Error: sign of rrpr < 0?\n";
SimTKOpenMMLog::printMessage( message );
} else {
constraintDelta[ii] = reducedMasses[ii]*diff/rrpr;
}
if (rp2 >= lowerTol*dist2 && rp2 <= upperTol*dist2)
numberConverged++;
}
}
if( numberConverged == _numberOfConstraints )
break;
iterations++;
if (_matrix.size() > 0) {
for (int i = 0; i < _numberOfConstraints; i++) {
RealOpenMM sum = 0.0;
for (int j = 0; j < (int) _matrix[i].size(); j++) {
pair<int, RealOpenMM> element = _matrix[i][j];
sum += element.second*constraintDelta[element.first];
}
tempDelta[i] = sum;
}
constraintDelta = tempDelta;
}
for( int ii = 0; ii < _numberOfConstraints; ii++ ){
int atomI = _atomIndices[ii].first;
int atomJ = _atomIndices[ii].second;
RealVec dr = r_ij[ii]*constraintDelta[ii];
atomCoordinatesP[atomI] += dr*inverseMasses[atomI];
atomCoordinatesP[atomJ] -= dr*inverseMasses[atomJ];
}
}
return (numberConverged == _numberOfConstraints ? SimTKOpenMMCommon::DefaultReturn : SimTKOpenMMCommon::ErrorReturn);
}
/**---------------------------------------------------------------------------------------
Report any violated constriants
@param numberOfAtoms number of atoms
@param atomCoordinates atom coordinates
@param message report
@return number of violated constraints
--------------------------------------------------------------------------------------- */
int ReferenceCCMAAlgorithm::reportCCMA( int numberOfAtoms, vector<RealVec>& atomCoordinates,
std::stringstream& message ){
// ---------------------------------------------------------------------------------------
static const char* methodName = "\nReferenceCCMAAlgorithm::reportCCMA";
static const RealOpenMM zero = 0.0;
static const RealOpenMM one = 1.0;
static const RealOpenMM two = 2.0;
static const RealOpenMM three = 3.0;
static const RealOpenMM oneM = -1.0;
static const RealOpenMM half = 0.5;
// ---------------------------------------------------------------------------------------
int numberOfConstraints = getNumberOfConstraints();
// loop over constraints calculating distance and comparing to
// expected distance -- report any contraints that are violated
int numberConverged = 0;
RealOpenMM tolerance = getTolerance();
for( int ii = 0; ii < _numberOfConstraints; ii++ ){
int atomI = _atomIndices[ii].first;
int atomJ = _atomIndices[ii].second;
RealOpenMM rp2 = zero;
for( int jj = 0; jj < 3; jj++ ){
rp2 += (atomCoordinates[atomI][jj] - atomCoordinates[atomJ][jj])*(atomCoordinates[atomI][jj] - atomCoordinates[atomJ][jj]);
}
RealOpenMM diff = FABS( rp2 - (_distance[ii]*_distance[ii]) );
if( diff > tolerance ){
message << ii << " constraint violated: " << atomI << " " << atomJ << "] d=" << SQRT( rp2 ) << " " << rp2 << " d0=" << _distance[ii];
message << " diff=" << diff;
message << " [" << atomCoordinates[atomI][0] << " " << atomCoordinates[atomI][1] << " " << atomCoordinates[atomI][2] << "] ";
message << " [" << atomCoordinates[atomJ][0] << " " << atomCoordinates[atomJ][1] << " " << atomCoordinates[atomJ][2] << "] ";
message << "\n";
} else {
numberConverged++;
}
}
vector<RealOpenMM>& inverseMasses, bool constrainingVelocities) {
// temp arrays
vector<RealVec>& r_ij = _r_ij;
RealOpenMM* d_ij2 = _d_ij2;
RealOpenMM* reducedMasses = _reducedMasses;
// calculate reduced masses on 1st pass
if (!_hasInitializedMasses) {
_hasInitializedMasses = true;
for (int ii = 0; ii < _numberOfConstraints; ii++) {
int atomI = _atomIndices[ii].first;
int atomJ = _atomIndices[ii].second;
reducedMasses[ii] = 0.5/(inverseMasses[atomI] + inverseMasses[atomJ]);
}
}
return (numberOfConstraints-numberConverged);
// setup: r_ij for each (i,j) constraint
RealOpenMM tolerance = getTolerance()*2;
for (int ii = 0; ii < _numberOfConstraints; ii++) {
int atomI = _atomIndices[ii].first;
int atomJ = _atomIndices[ii].second;
r_ij[ii] = atomCoordinates[atomI] - atomCoordinates[atomJ];
d_ij2[ii] = r_ij[ii].dot(r_ij[ii]);
}
RealOpenMM lowerTol = 1-2*getTolerance()+getTolerance()*getTolerance();
RealOpenMM upperTol = 1+2*getTolerance()+getTolerance()*getTolerance();
// main loop
int iterations = 0;
int numberConverged = 0;
vector<RealOpenMM> constraintDelta(_numberOfConstraints);
vector<RealOpenMM> tempDelta(_numberOfConstraints);
while (iterations < getMaximumNumberOfIterations()) {
numberConverged = 0;
for (int ii = 0; ii < _numberOfConstraints; ii++) {
int atomI = _atomIndices[ii].first;
int atomJ = _atomIndices[ii].second;
RealVec rp_ij = atomCoordinatesP[atomI] - atomCoordinatesP[atomJ];
if (constrainingVelocities) {
RealOpenMM rrpr = rp_ij.dot(r_ij[ii]);
constraintDelta[ii] = -2*reducedMasses[ii]*rrpr/d_ij2[ii];
if (fabs(constraintDelta[ii]) <= getTolerance())
numberConverged++;
}
else {
RealOpenMM rp2 = rp_ij.dot(rp_ij);
RealOpenMM dist2 = _distance[ii]*_distance[ii];
RealOpenMM diff = dist2 - rp2;
constraintDelta[ii] = 0;
RealOpenMM rrpr = DOT3(rp_ij, r_ij[ii]);
constraintDelta[ii] = reducedMasses[ii]*diff/rrpr;
if (rp2 >= lowerTol*dist2 && rp2 <= upperTol*dist2)
numberConverged++;
}
}
if (numberConverged == _numberOfConstraints)
break;
iterations++;
if (_matrix.size() > 0) {
for (int i = 0; i < _numberOfConstraints; i++) {
RealOpenMM sum = 0.0;
for (int j = 0; j < (int) _matrix[i].size(); j++) {
pair<int, RealOpenMM> element = _matrix[i][j];
sum += element.second*constraintDelta[element.first];
}
tempDelta[i] = sum;
}
constraintDelta = tempDelta;
}
for (int ii = 0; ii < _numberOfConstraints; ii++) {
int atomI = _atomIndices[ii].first;
int atomJ = _atomIndices[ii].second;
RealVec dr = r_ij[ii]*constraintDelta[ii];
atomCoordinatesP[atomI] += dr*inverseMasses[atomI];
atomCoordinatesP[atomJ] -= dr*inverseMasses[atomJ];
}
}
}
/* Portions copyright (c) 2006 Stanford University and Simbios.
* Contributors: Pande Group
*
* 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 "SimTKOpenMMCommon.h"
#include "SimTKOpenMMLog.h"
#include "SimTKOpenMMUtilities.h"
#include "ReferenceConstraint.h"
#include "ReferenceDynamics.h"
/**---------------------------------------------------------------------------------------
ReferenceConstraint constructor
--------------------------------------------------------------------------------------- */
ReferenceConstraint::ReferenceConstraint( ){
// ---------------------------------------------------------------------------------------
//static const char* methodName = "\nReferenceConstraint::ReferenceConstraint";
// ---------------------------------------------------------------------------------------
}
/**---------------------------------------------------------------------------------------
ReferenceConstraint destructor
--------------------------------------------------------------------------------------- */
ReferenceConstraint::~ReferenceConstraint( ){
// ---------------------------------------------------------------------------------------
// static const char* methodName = "\nReferenceConstraint::~ReferenceConstraint";
// ---------------------------------------------------------------------------------------
}
/**---------------------------------------------------------------------------------------
ReferenceShakeConstraint constructor
@param numberOfAtoms number of atoms
@param deltaT delta t for dynamics
@param tau viscosity
@param temperature temperature
--------------------------------------------------------------------------------------- */
ReferenceShakeConstraint::ReferenceShakeConstraint( int atomIndex1, int atomIndex2,
RealOpenMM constraintDistance,
RealOpenMM inverseMass1,
RealOpenMM inverseMass2 ) : ReferenceConstraint( ) {
// ---------------------------------------------------------------------------------------
static const char* methodName = "\nReferenceShakeConstraint::ReferenceShakeConstraint";
// ---------------------------------------------------------------------------------------
// insure heavy atom is in 0 slot
if( inverseMass1 > inverseMass2 ){
int temp = atomIndex1;
atomIndex1 = atomIndex2;
atomIndex2 = temp;
RealOpenMM tempR = inverseMass1;
inverseMass1 = inverseMass2;
inverseMass2 = tempR;
}
_atomIndices[0] = atomIndex1;
_atomIndices[1] = atomIndex2;
_inverseMasses[0] = inverseMass1;
_inverseMasses[1] = inverseMass2;
_constraintDistance = constraintDistance;
}
/**---------------------------------------------------------------------------------------
ReferenceShakeConstraint destructor
--------------------------------------------------------------------------------------- */
ReferenceShakeConstraint::~ReferenceShakeConstraint( ){
// ---------------------------------------------------------------------------------------
// static const char* methodName = "\nReferenceShakeConstraint::~ReferenceShakeConstraint";
// ---------------------------------------------------------------------------------------
}
/**---------------------------------------------------------------------------------------
Get constraint distance
@return constraintDistance
--------------------------------------------------------------------------------------- */
RealOpenMM ReferenceShakeConstraint::getConstraintDistance( void ){
// ---------------------------------------------------------------------------------------
// static const char* methodName = "\nReferenceShakeConstraint::getConstraintDistance";
// ---------------------------------------------------------------------------------------
return _constraintDistance;
}
/**---------------------------------------------------------------------------------------
Get inverse mass of heavy atom
@return inverse mass
--------------------------------------------------------------------------------------- */
RealOpenMM ReferenceShakeConstraint::getHeavyAtomInverseMass( void ){
// ---------------------------------------------------------------------------------------
// static const char* methodName = "\nReferenceShakeConstraint::getHeavyAtomInverseMass";
// ---------------------------------------------------------------------------------------
return _inverseMasses[0];
}
/**---------------------------------------------------------------------------------------
Get inverse mass of light atom
@return inverse mass
--------------------------------------------------------------------------------------- */
RealOpenMM ReferenceShakeConstraint::getLightAtomInverseMass( void ){
// ---------------------------------------------------------------------------------------
// static const char* methodName = "\nReferenceShakeConstraint::getLightAtomInverseMass";
// ---------------------------------------------------------------------------------------
return _inverseMasses[1];
}
/**---------------------------------------------------------------------------------------
Get index of heavy atom
@return index
--------------------------------------------------------------------------------------- */
int ReferenceShakeConstraint::getHeavyAtomIndex( void ){
// ---------------------------------------------------------------------------------------
// static const char* methodName = "\nReferenceShakeConstraint::getHeavyAtomIndex";
// ---------------------------------------------------------------------------------------
return _atomIndices[0];
}
/**---------------------------------------------------------------------------------------
Get index of light atom
@return index
--------------------------------------------------------------------------------------- */
int ReferenceShakeConstraint::getLightAtomIndex( void ){
// ---------------------------------------------------------------------------------------
// static const char* methodName = "\nReferenceShakeConstraint::getLightAtomIndex";
// ---------------------------------------------------------------------------------------
return _atomIndices[1];
}
/* -------------------------------------------------------------------------- *
* 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 "ReferenceConstraints.h"
#include "openmm/HarmonicAngleForce.h"
#include "openmm/OpenMMException.h"
#include <map>
#include <utility>
#include <vector>
using namespace OpenMM;
using namespace std;
ReferenceConstraints::ReferenceConstraints(const System& system, RealOpenMM tolerance) : ccma(NULL), settle(NULL) {
this->tolerance = tolerance;
int numParticles = system.getNumParticles();
vector<RealOpenMM> masses(numParticles);
for (int i = 0; i < numParticles; ++i)
masses[i] = (RealOpenMM) system.getParticleMass(i);
// Record the set of constraints and how many constraints each atom is involved in.
vector<int> atom1;
vector<int> atom2;
vector<double> distance;
vector<int> constraintCount(numParticles, 0);
for (int i = 0; i < system.getNumConstraints(); i++) {
int p1, p2;
double d;
system.getConstraintParameters(i, p1, p2, d);
if (masses[p1] != 0 || masses[p2] != 0) {
atom1.push_back(p1);
atom2.push_back(p2);
distance.push_back(d);
constraintCount[p1]++;
constraintCount[p2]++;
}
}
// Identify clusters of three atoms that can be treated with SETTLE. First, for every
// atom that might be part of such a cluster, make a list of the two other atoms it is
// connected to.
vector<map<int, float> > settleConstraints(numParticles);
for (int i = 0; i < (int)atom1.size(); i++) {
if (constraintCount[atom1[i]] == 2 && constraintCount[atom2[i]] == 2) {
settleConstraints[atom1[i]][atom2[i]] = (float) distance[i];
settleConstraints[atom2[i]][atom1[i]] = (float) distance[i];
}
}
// Now remove the ones that don't actually form closed loops of three atoms.
vector<int> settleClusters;
for (int i = 0; i < (int)settleConstraints.size(); i++) {
if (settleConstraints[i].size() == 2) {
int partner1 = settleConstraints[i].begin()->first;
int partner2 = (++settleConstraints[i].begin())->first;
if (settleConstraints[partner1].size() != 2 || settleConstraints[partner2].size() != 2 ||
settleConstraints[partner1].find(partner2) == settleConstraints[partner1].end())
settleConstraints[i].clear();
else if (i < partner1 && i < partner2)
settleClusters.push_back(i);
}
else
settleConstraints[i].clear();
}
// Record the SETTLE clusters.
vector<bool> isSettleAtom(numParticles, false);
int numSETTLE = settleClusters.size();
if (numSETTLE > 0) {
vector<int> atom1(numSETTLE);
vector<int> atom2(numSETTLE);
vector<int> atom3(numSETTLE);
vector<RealOpenMM> distance1(numSETTLE);
vector<RealOpenMM> distance2(numSETTLE);
for (int i = 0; i < numSETTLE; i++) {
int p1 = settleClusters[i];
int p2 = settleConstraints[p1].begin()->first;
int p3 = (++settleConstraints[p1].begin())->first;
float dist12 = settleConstraints[p1].find(p2)->second;
float dist13 = settleConstraints[p1].find(p3)->second;
float dist23 = settleConstraints[p2].find(p3)->second;
if (dist12 == dist13) {
// p1 is the central atom
atom1[i] = p1;
atom2[i] = p2;
atom3[i] = p3;
distance1[i] = dist12;
distance2[i] = dist23;
}
else if (dist12 == dist23) {
// p2 is the central atom
atom1[i] = p2;
atom2[i] = p1;
atom3[i] = p3;
distance1[i] = dist12;
distance2[i] = dist13;
}
else if (dist13 == dist23) {
// p3 is the central atom
atom1[i] = p3;
atom2[i] = p1;
atom3[i] = p2;
distance1[i] = dist13;
distance2[i] = dist12;
}
else
throw OpenMMException("Two of the three distances constrained with SETTLE must be the same.");
isSettleAtom[p1] = true;
isSettleAtom[p2] = true;
isSettleAtom[p3] = true;
}
settle = new ReferenceSETTLEAlgorithm(atom1, atom2, atom3, distance1, distance2, masses, tolerance);
}
// All other constraints are handled with CCMA.
vector<int> ccmaConstraints;
for (unsigned i = 0; i < atom1.size(); i++)
if (!isSettleAtom[atom1[i]])
ccmaConstraints.push_back(i);
int numCCMA = ccmaConstraints.size();
if (numCCMA > 0) {
// Record particles and distances for CCMA.
vector<pair<int, int> > ccmaIndices(numCCMA);
vector<RealOpenMM> ccmaDistance(numCCMA);
for (int i = 0; i < numCCMA; i++) {
int index = ccmaConstraints[i];
ccmaIndices[i] = make_pair(atom1[index], atom2[index]);
ccmaDistance[i] = distance[index];
}
// Look up angles for CCMA.
vector<ReferenceCCMAAlgorithm::AngleInfo> angles;
for (int i = 0; i < system.getNumForces(); i++) {
const HarmonicAngleForce* force = dynamic_cast<const HarmonicAngleForce*>(&system.getForce(i));
if (force != NULL) {
for (int j = 0; j < force->getNumAngles(); j++) {
int atom1, atom2, atom3;
double angle, k;
force->getAngleParameters(j, atom1, atom2, atom3, angle, k);
angles.push_back(ReferenceCCMAAlgorithm::AngleInfo(atom1, atom2, atom3, (RealOpenMM) angle));
}
}
}
// Create the CCMA object.
ccma = new ReferenceCCMAAlgorithm(numParticles, numCCMA, ccmaIndices, ccmaDistance, masses, angles, tolerance);
}
}
ReferenceConstraints::~ReferenceConstraints() {
if (ccma != NULL)
delete ccma;
if (settle != NULL)
delete settle;
}
RealOpenMM ReferenceConstraints::getTolerance() const {
return tolerance;
}
void ReferenceConstraints::setTolerance(RealOpenMM tolerance) {
this->tolerance = tolerance;
if (ccma != NULL)
ccma->setTolerance(tolerance);
if (settle != NULL)
settle->setTolerance(tolerance);
}
void ReferenceConstraints::apply(vector<OpenMM::RealVec>& atomCoordinates, vector<OpenMM::RealVec>& atomCoordinatesP, vector<RealOpenMM>& inverseMasses) {
if (ccma != NULL)
ccma->apply(atomCoordinates, atomCoordinatesP, inverseMasses);
if (settle != NULL)
settle->apply(atomCoordinates, atomCoordinatesP, inverseMasses);
}
void ReferenceConstraints::applyToVelocities(vector<OpenMM::RealVec>& atomCoordinates, vector<OpenMM::RealVec>& velocities, vector<RealOpenMM>& inverseMasses) {
if (ccma != NULL)
ccma->applyToVelocities(atomCoordinates, velocities, inverseMasses);
if (settle != NULL)
settle->applyToVelocities(atomCoordinates, velocities, inverseMasses);
}
/* Portions copyright (c) 2010 Stanford University and Simbios.
/* Portions copyright (c) 2010-2013 Stanford University and Simbios.
* Contributors: Peter Eastman
*
* Permission is hereby granted, free of charge, to any person obtaining
......@@ -39,16 +39,21 @@ using namespace std;
--------------------------------------------------------------------------------------- */
ReferenceCustomAngleIxn::ReferenceCustomAngleIxn(const Lepton::ExpressionProgram& energyExpression,
const Lepton::ExpressionProgram& forceExpression, const vector<string>& parameterNames, map<string, double> globalParameters) :
energyExpression(energyExpression), forceExpression(forceExpression), paramNames(parameterNames), globalParameters(globalParameters) {
// ---------------------------------------------------------------------------------------
// static const char* methodName = "\nReferenceCustomAngleIxn::ReferenceCustomAngleIxn";
// ---------------------------------------------------------------------------------------
ReferenceCustomAngleIxn::ReferenceCustomAngleIxn(const Lepton::CompiledExpression& energyExpression,
const Lepton::CompiledExpression& forceExpression, const vector<string>& parameterNames, map<string, double> globalParameters) :
energyExpression(energyExpression), forceExpression(forceExpression) {
energyTheta = ReferenceForce::getVariablePointer(this->energyExpression, "theta");
forceTheta = ReferenceForce::getVariablePointer(this->forceExpression, "theta");
numParameters = parameterNames.size();
for (int i = 0; i < (int) numParameters; i++) {
energyParams.push_back(ReferenceForce::getVariablePointer(this->energyExpression, parameterNames[i]));
forceParams.push_back(ReferenceForce::getVariablePointer(this->forceExpression, parameterNames[i]));
}
for (map<string, double>::const_iterator iter = globalParameters.begin(); iter != globalParameters.end(); ++iter) {
ReferenceForce::setVariable(ReferenceForce::getVariablePointer(this->energyExpression, iter->first), iter->second);
ReferenceForce::setVariable(ReferenceForce::getVariablePointer(this->forceExpression, iter->first), iter->second);
}
}
/**---------------------------------------------------------------------------------------
......@@ -91,9 +96,10 @@ void ReferenceCustomAngleIxn::calculateBondIxn( int* atomIndices,
static const RealOpenMM one = 1.0;
RealOpenMM deltaR[2][ReferenceForce::LastDeltaRIndex];
map<string, double> variables = globalParameters;
for (int i = 0; i < (int) paramNames.size(); ++i)
variables[paramNames[i]] = parameters[i];
for (int i = 0; i < numParameters; i++) {
ReferenceForce::setVariable(energyParams[i], parameters[i]);
ReferenceForce::setVariable(forceParams[i], parameters[i]);
}
// ---------------------------------------------------------------------------------------
......@@ -118,12 +124,13 @@ void ReferenceCustomAngleIxn::calculateBondIxn( int* atomIndices,
angle = PI_M;
else
angle = ACOS(cosine);
variables["theta"] = angle;
ReferenceForce::setVariable(energyTheta, angle);
ReferenceForce::setVariable(forceTheta, angle);
// Compute the force and energy, and apply them to the atoms.
RealOpenMM energy = (RealOpenMM) energyExpression.evaluate(variables);
RealOpenMM dEdR = (RealOpenMM) forceExpression.evaluate(variables);
RealOpenMM energy = (RealOpenMM) energyExpression.evaluate();
RealOpenMM dEdR = (RealOpenMM) forceExpression.evaluate();
RealOpenMM termA = dEdR/(deltaR[0][ReferenceForce::R2Index]*rp);
RealOpenMM termC = -dEdR/(deltaR[1][ReferenceForce::R2Index]*rp);
......
/* Portions copyright (c) 2009 Stanford University and Simbios.
/* Portions copyright (c) 2009-2013 Stanford University and Simbios.
* Contributors: Peter Eastman
*
* Permission is hereby granted, free of charge, to any person obtaining
......@@ -40,16 +40,20 @@ using namespace OpenMM;
--------------------------------------------------------------------------------------- */
ReferenceCustomBondIxn::ReferenceCustomBondIxn(const Lepton::ExpressionProgram& energyExpression,
const Lepton::ExpressionProgram& forceExpression, const vector<string>& parameterNames, map<string, double> globalParameters) :
energyExpression(energyExpression), forceExpression(forceExpression), paramNames(parameterNames), globalParameters(globalParameters) {
// ---------------------------------------------------------------------------------------
// static const char* methodName = "\nReferenceCustomBondIxn::ReferenceCustomBondIxn";
// ---------------------------------------------------------------------------------------
ReferenceCustomBondIxn::ReferenceCustomBondIxn(const Lepton::CompiledExpression& energyExpression,
const Lepton::CompiledExpression& forceExpression, const vector<string>& parameterNames, map<string, double> globalParameters) :
energyExpression(energyExpression), forceExpression(forceExpression) {
energyR = ReferenceForce::getVariablePointer(this->energyExpression, "r");
forceR = ReferenceForce::getVariablePointer(this->forceExpression, "r");
numParameters = parameterNames.size();
for (int i = 0; i < (int) numParameters; i++) {
energyParams.push_back(ReferenceForce::getVariablePointer(this->energyExpression, parameterNames[i]));
forceParams.push_back(ReferenceForce::getVariablePointer(this->forceExpression, parameterNames[i]));
}
for (map<string, double>::const_iterator iter = globalParameters.begin(); iter != globalParameters.end(); ++iter) {
ReferenceForce::setVariable(ReferenceForce::getVariablePointer(this->energyExpression, iter->first), iter->second);
ReferenceForce::setVariable(ReferenceForce::getVariablePointer(this->forceExpression, iter->first), iter->second);
}
}
/**---------------------------------------------------------------------------------------
......@@ -95,9 +99,10 @@ void ReferenceCustomBondIxn::calculateBondIxn( int* atomIndices,
static const RealOpenMM half = 0.5;
RealOpenMM deltaR[ReferenceForce::LastDeltaRIndex];
map<string, double> variables = globalParameters;
for (int i = 0; i < (int) paramNames.size(); ++i)
variables[paramNames[i]] = parameters[i];
for (int i = 0; i < numParameters; i++) {
ReferenceForce::setVariable(energyParams[i], parameters[i]);
ReferenceForce::setVariable(forceParams[i], parameters[i]);
}
// ---------------------------------------------------------------------------------------
......@@ -106,8 +111,10 @@ void ReferenceCustomBondIxn::calculateBondIxn( int* atomIndices,
int atomAIndex = atomIndices[0];
int atomBIndex = atomIndices[1];
ReferenceForce::getDeltaR( atomCoordinates[atomAIndex], atomCoordinates[atomBIndex], deltaR );
variables["r"] = deltaR[ReferenceForce::RIndex];
RealOpenMM dEdR = (RealOpenMM) forceExpression.evaluate(variables);
ReferenceForce::setVariable(energyR, deltaR[ReferenceForce::RIndex]);
ReferenceForce::setVariable(forceR, deltaR[ReferenceForce::RIndex]);
RealOpenMM dEdR = (RealOpenMM) forceExpression.evaluate();
dEdR = deltaR[ReferenceForce::RIndex] > zero ? (dEdR/deltaR[ReferenceForce::RIndex]) : zero;
forces[atomAIndex][0] += dEdR*deltaR[ReferenceForce::XIndex];
......@@ -119,5 +126,5 @@ void ReferenceCustomBondIxn::calculateBondIxn( int* atomIndices,
forces[atomBIndex][2] -= dEdR*deltaR[ReferenceForce::ZIndex];
if (totalEnergy != NULL)
*totalEnergy += (RealOpenMM) energyExpression.evaluate(variables);
*totalEnergy += (RealOpenMM) energyExpression.evaluate();
}
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