Commit bceea302 authored by Peter Eastman's avatar Peter Eastman
Browse files

Merge remote-tracking branch 'origin/master' into qc

parents 2e6397b7 a1b2641c
...@@ -524,6 +524,47 @@ void testReordering() { ...@@ -524,6 +524,47 @@ void testReordering() {
} }
} }
/**
* Test a System where multiple virtual sites are all calculated from the same particles.
*/
void testOverlappingSites() {
System system;
system.addParticle(1.0);
system.addParticle(1.0);
system.addParticle(1.0);
NonbondedForce* nonbonded = new NonbondedForce();
system.addForce(nonbonded);
nonbonded->addParticle(1.0, 0.0, 0.0);
nonbonded->addParticle(-0.5, 0.0, 0.0);
nonbonded->addParticle(-0.5, 0.0, 0.0);
vector<Vec3> positions;
positions.push_back(Vec3(0, 0, 0));
positions.push_back(Vec3(10, 0, 0));
positions.push_back(Vec3(0, 10, 0));
for (int i = 0; i < 20; i++) {
system.addParticle(0.0);
double u = 0.1*((i+1)%4);
double v = 0.05*i;
system.setVirtualSite(3+i, new ThreeParticleAverageSite(0, 1, 2, u, v, 1-u-v));
nonbonded->addParticle(i%2 == 0 ? -1.0 : 1.0, 0.0, 0.0);
positions.push_back(Vec3());
}
VerletIntegrator i1(0.002);
VerletIntegrator i2(0.002);
Context c1(system, i1, Platform::getPlatformByName("Reference"));
Context c2(system, i2, platform);
c1.setPositions(positions);
c2.setPositions(positions);
c1.applyConstraints(0.0001);
c2.applyConstraints(0.0001);
State s1 = c1.getState(State::Positions | State::Forces);
State s2 = c2.getState(State::Positions | State::Forces);
for (int i = 0; i < system.getNumParticles(); i++)
ASSERT_EQUAL_VEC(s1.getPositions()[i], s2.getPositions()[i], 1e-5);
for (int i = 0; i < 3; i++)
ASSERT_EQUAL_VEC(s1.getForces()[i], s2.getForces()[i], 1e-5);
}
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {
try { try {
if (argc > 1) if (argc > 1)
...@@ -535,6 +576,7 @@ int main(int argc, char* argv[]) { ...@@ -535,6 +576,7 @@ int main(int argc, char* argv[]) {
testLocalCoordinates(); testLocalCoordinates();
testConservationLaws(); testConservationLaws();
testReordering(); testReordering();
testOverlappingSites();
} }
catch(const exception& e) { catch(const exception& e) {
cout << "exception: " << e.what() << endl; cout << "exception: " << e.what() << endl;
......
...@@ -126,13 +126,15 @@ public: ...@@ -126,13 +126,15 @@ public:
* Set the torsion-torsion grid at the specified index * Set the torsion-torsion grid at the specified index
* *
* @param index the index of the torsion-torsion for which to get parameters * @param index the index of the torsion-torsion for which to get parameters
* @param grid grid * @param grid either 3 or 6 values may be specified per grid point. If the derivatives
* are omitted, they are calculated automatically by fitting a 2D spline to
* the energies.
* grid[x][y][0] = x value * grid[x][y][0] = x value
* grid[x][y][1] = y value * grid[x][y][1] = y value
* grid[x][y][2] = function value * grid[x][y][2] = energy
* grid[x][y][3] = dfdx value * grid[x][y][3] = dEdx value
* grid[x][y][4] = dfdy value * grid[x][y][4] = dEdy value
* grid[x][y][5] = dfd(xy) value * grid[x][y][5] = dEd(xy) value
*/ */
void setTorsionTorsionGrid(int index, const std::vector<std::vector<std::vector<double> > >& grid); void setTorsionTorsionGrid(int index, const std::vector<std::vector<std::vector<double> > >& grid);
...@@ -181,29 +183,7 @@ public: ...@@ -181,29 +183,7 @@ public:
_spacing[0] = _spacing[1] = 1.0; _spacing[0] = _spacing[1] = 1.0;
} }
TorsionTorsionGridInfo(const TorsionTorsionGrid& grid) { TorsionTorsionGridInfo(const TorsionTorsionGrid& grid);
_grid.resize(grid.size());
for(unsigned int kk = 0; kk < grid.size(); kk++){
_grid[kk].resize(grid[kk].size());
for(unsigned int jj = 0; jj < grid[kk].size(); jj++){
_grid[kk][jj].resize(grid[kk][jj].size());
for(unsigned int ii = 0; ii < grid[kk][jj].size(); ii++){
_grid[kk][jj][ii] = grid[kk][jj][ii];
}
}
}
_startValues[0] = _grid[0][0][0];
_startValues[1] = _grid[0][0][1];
_spacing[0] = static_cast<double>(_grid.size()-1)/360.0;
_spacing[1] = static_cast<double>(grid.size()-1)/360.0;
_size[0] = static_cast<int>(grid.size());
_size[1] = static_cast<int>(grid[0].size());
}
const TorsionTorsionGrid& getTorsionTorsionGrid(void) const { const TorsionTorsionGrid& getTorsionTorsionGrid(void) const {
return _grid; return _grid;
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Biological Structures at Stanford, funded under the NIH Roadmap for * * Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org. * * Medical Research, grant U54 GM072970. See https://simtk.org. *
* * * *
* Portions copyright (c) 2008-2009 Stanford University and the Authors. * * Portions copyright (c) 2008-2014 Stanford University and the Authors. *
* Authors: * * Authors: *
* Contributors: * * Contributors: *
* * * *
...@@ -33,8 +33,10 @@ ...@@ -33,8 +33,10 @@
#include "openmm/OpenMMException.h" #include "openmm/OpenMMException.h"
#include "openmm/AmoebaTorsionTorsionForce.h" #include "openmm/AmoebaTorsionTorsionForce.h"
#include "openmm/internal/AmoebaTorsionTorsionForceImpl.h" #include "openmm/internal/AmoebaTorsionTorsionForceImpl.h"
#include "openmm/internal/SplineFitter.h"
using namespace OpenMM; using namespace OpenMM;
using namespace std;
AmoebaTorsionTorsionForce::AmoebaTorsionTorsionForce() { AmoebaTorsionTorsionForce::AmoebaTorsionTorsionForce() {
} }
...@@ -82,3 +84,102 @@ void AmoebaTorsionTorsionForce::setTorsionTorsionGrid(int index, const TorsionTo ...@@ -82,3 +84,102 @@ void AmoebaTorsionTorsionForce::setTorsionTorsionGrid(int index, const TorsionTo
ForceImpl* AmoebaTorsionTorsionForce::createImpl() const { ForceImpl* AmoebaTorsionTorsionForce::createImpl() const {
return new AmoebaTorsionTorsionForceImpl(*this); return new AmoebaTorsionTorsionForceImpl(*this);
} }
AmoebaTorsionTorsionForce::TorsionTorsionGridInfo::TorsionTorsionGridInfo(const TorsionTorsionGrid& grid) {
if (grid[0][0][0] != grid[1][0][0])
_grid = grid;
else {
// We need to transpose the grid.
int xsize = grid[0].size();
int ysize = grid.size();
_grid.resize(xsize);
for (int i = 0; i < xsize; i++) {
_grid[i].resize(ysize);
for (int j = 0; j < ysize; j++)
_grid[i][j] = grid[j][i];
}
}
_startValues[0] = _grid[0][0][0];
_startValues[1] = _grid[0][0][1];
_spacing[0] = static_cast<double>(_grid.size()-1)/360.0;
_spacing[1] = static_cast<double>(_grid.size()-1)/360.0;
_size[0] = static_cast<int>(_grid.size());
_size[1] = static_cast<int>(_grid[0].size());
if (_grid[0][0].size() == 3) {
// We need to compute the derivatives ourselves. First determine if the grid is periodic.
int xsize = _size[0];
int ysize = _size[1];
bool periodic = true;
for (int i = 0; i < xsize; i++)
if (_grid[i][0][2] != _grid[i][ysize-1][2])
periodic = false;
for (int i = 0; i < ysize; i++)
if (_grid[0][i][2] != _grid[xsize-1][i][2])
periodic = false;
// Compute derivatives with respect to the first angle.
vector<double> x(xsize), y(ysize);
for (int i = 0; i < xsize; i++)
x[i] = _grid[i][0][0];
for (int i = 0; i < ysize; i++)
y[i] = _grid[0][i][1];
vector<double> d1(xsize*ysize), d2(xsize*ysize), d12(xsize*ysize);
vector<double> t(xsize), deriv(xsize);
for (int i = 0; i < ysize; i++) {
for (int j = 0; j < xsize; j++)
t[j] = _grid[j][i][2];
if (periodic)
SplineFitter::createPeriodicSpline(x, t, deriv);
else
SplineFitter::createNaturalSpline(x, t, deriv);
for (int j = 0; j < xsize; j++)
d1[j+xsize*i] = SplineFitter::evaluateSplineDerivative(x, t, deriv, x[j]);
}
// Compute derivatives with respect to the second angle.
t.resize(ysize);
deriv.resize(ysize);
for (int i = 0; i < xsize; i++) {
for (int j = 0; j < ysize; j++)
t[j] = _grid[i][j][2];
if (periodic)
SplineFitter::createPeriodicSpline(y, t, deriv);
else
SplineFitter::createNaturalSpline(y, t, deriv);
for (int j = 0; j < ysize; j++)
d2[i+xsize*j] = SplineFitter::evaluateSplineDerivative(y, t, deriv, y[j]);
}
// Compute cross derivatives.
t.resize(xsize);
deriv.resize(xsize);
for (int i = 0; i < ysize; i++) {
for (int j = 0; j < xsize; j++)
t[j] = d2[j+xsize*i];
if (periodic)
SplineFitter::createPeriodicSpline(x, t, deriv);
else
SplineFitter::createNaturalSpline(x, t, deriv);
for (int j = 0; j < xsize; j++)
d12[j+xsize*i] = SplineFitter::evaluateSplineDerivative(x, t, deriv, x[j]);
}
// Add the derivatives to the grid.
for (int i = 0; i < xsize; i++)
for (int j = 0; j < ysize; j++) {
_grid[i][j].push_back(d1[i+xsize*j]);
_grid[i][j].push_back(d2[i+xsize*j]);
_grid[i][j].push_back(d12[i+xsize*j]);
}
}
}
...@@ -49,7 +49,7 @@ extern "C" OPENMM_EXPORT void registerAmoebaReferenceKernelFactories(); ...@@ -49,7 +49,7 @@ extern "C" OPENMM_EXPORT void registerAmoebaReferenceKernelFactories();
const double TOL = 1e-4; const double TOL = 1e-4;
TorsionTorsionGrid& getTorsionGrid( int gridIndex ) { TorsionTorsionGrid getTorsionGrid(int gridIndex, bool includeDerivs) {
static double grid[4][625][6] = { static double grid[4][625][6] = {
{ {
...@@ -2557,16 +2557,14 @@ static double grid[4][625][6] = { ...@@ -2557,16 +2557,14 @@ static double grid[4][625][6] = {
{ 165.0000, 180.0000, -0.182999000E+01, 0.377952854E-01, 0.233583295E-01, -0.109828932E-02 }, { 165.0000, 180.0000, -0.182999000E+01, 0.377952854E-01, 0.233583295E-01, -0.109828932E-02 },
{ 180.0000, 180.0000, -0.146854000E+01, 0.491175487E-02, 0.195601580E-02, -0.163177030E-02 } } }; { 180.0000, 180.0000, -0.146854000E+01, 0.491175487E-02, 0.195601580E-02, -0.163177030E-02 } } };
// static std::vector< std::vector< std::vector<double> > > TorsionTorsionGrid int elementCount = (includeDerivs ? 6 : 3);
static std::vector<TorsionTorsionGrid> grids; std::vector<TorsionTorsionGrid> grids(4);
if( grids.size() == 0 ){
grids.resize(4);
for( int ii = 0; ii < 4; ii++ ){ for( int ii = 0; ii < 4; ii++ ){
grids[ii].resize( 25 ); grids[ii].resize( 25 );
for( int jj = 0; jj < 25; jj++ ){ for( int jj = 0; jj < 25; jj++ ){
grids[ii][jj].resize(25); grids[ii][jj].resize(25);
for( int kk = 0; kk < 25; kk++ ){ for( int kk = 0; kk < 25; kk++ ){
grids[ii][jj][kk].resize(6); grids[ii][jj][kk].resize(elementCount);
} }
} }
int index = 0; int index = 0;
...@@ -2574,18 +2572,17 @@ static double grid[4][625][6] = { ...@@ -2574,18 +2572,17 @@ static double grid[4][625][6] = {
for( int kk = 0; kk < 25; kk++ ){ for( int kk = 0; kk < 25; kk++ ){
int jjIndex = static_cast<int>(((grid[ii][index][0] + 180.0)/15.0)+1.0e-05); int jjIndex = static_cast<int>(((grid[ii][index][0] + 180.0)/15.0)+1.0e-05);
int kkIndex = static_cast<int>(((grid[ii][index][1] + 180.0)/15.0)+1.0e-05); int kkIndex = static_cast<int>(((grid[ii][index][1] + 180.0)/15.0)+1.0e-05);
for( int ll = 0; ll < 6; ll++ ){ for( int ll = 0; ll < elementCount; ll++ ){
grids[ii][kk][jj][ll] = grid[ii][index][ll]; grids[ii][kk][jj][ll] = grid[ii][index][ll];
} }
index++; index++;
} }
} }
} }
}
return grids[gridIndex]; return grids[gridIndex];
} }
void testTorsionTorsion( FILE* log, int systemId ) { void testTorsionTorsion(int systemId, bool includeDerivs) {
System system; System system;
int numberOfParticles = 6; int numberOfParticles = 6;
...@@ -2645,11 +2642,11 @@ void testTorsionTorsion( FILE* log, int systemId ) { ...@@ -2645,11 +2642,11 @@ void testTorsionTorsion( FILE* log, int systemId ) {
expectedEnergy = -3.372536909E+00; expectedEnergy = -3.372536909E+00;
} }
amoebaTorsionTorsionForce->addTorsionTorsion( 0, 1, 2, 3, 4, chiralCheckAtomIndex, 0 ); amoebaTorsionTorsionForce->addTorsionTorsion(0, 1, 2, 3, 4, chiralCheckAtomIndex, 0);
amoebaTorsionTorsionForce->setTorsionTorsionGrid( 0, getTorsionGrid( gridIndex ) ); amoebaTorsionTorsionForce->setTorsionTorsionGrid(0, getTorsionGrid(gridIndex, includeDerivs));
system.addForce(amoebaTorsionTorsionForce); system.addForce(amoebaTorsionTorsionForce);
Context context(system, integrator, Platform::getPlatformByName( "Reference")); Context context(system, integrator, Platform::getPlatformByName("Reference"));
context.setPositions(positions); context.setPositions(positions);
State state = context.getState(State::Forces | State::Energy); State state = context.getState(State::Forces | State::Energy);
...@@ -2662,18 +2659,6 @@ void testTorsionTorsion( FILE* log, int systemId ) { ...@@ -2662,18 +2659,6 @@ void testTorsionTorsion( FILE* log, int systemId ) {
forces[ii][2] *= conversion; forces[ii][2] *= conversion;
} }
#ifdef AMOEBA_DEBUG
if( log ){
(void) fprintf( log, "computeAmoebaTorsionTorsionForces: expected energy=%14.7e %14.7e\n", expectedEnergy, state.getPotentialEnergy() );
for( unsigned int ii = 0; ii < forces.size(); ii++ ){
(void) fprintf( log, "%6u [%14.7e %14.7e %14.7e] [%14.7e %14.7e %14.7e]\n", ii,
expectedForces[ii][0], expectedForces[ii][1], expectedForces[ii][2],
forces[ii][0], forces[ii][1], forces[ii][2] );
}
(void) fflush( log );
}
#endif
double tolerance = 1.0e-03; double tolerance = 1.0e-03;
for( unsigned int ii = 0; ii < forces.size(); ii++ ){ for( unsigned int ii = 0; ii < forces.size(); ii++ ){
ASSERT_EQUAL_VEC( expectedForces[ii], forces[ii], tolerance ); ASSERT_EQUAL_VEC( expectedForces[ii], forces[ii], tolerance );
...@@ -2687,10 +2672,8 @@ int main( int numberOfArguments, char* argv[] ) { ...@@ -2687,10 +2672,8 @@ int main( int numberOfArguments, char* argv[] ) {
try { try {
std::cout << "TestReferenceAmoebaTorsionTorsionForce running test..." << std::endl; std::cout << "TestReferenceAmoebaTorsionTorsionForce running test..." << std::endl;
registerAmoebaReferenceKernelFactories(); registerAmoebaReferenceKernelFactories();
//registerAmoebaCudaKernelFactories(); testTorsionTorsion(1, true);
testTorsionTorsion(1, false);
FILE* log = NULL;
testTorsionTorsion( log, 1 );
} catch(const std::exception& e) { } catch(const std::exception& e) {
std::cout << "exception: " << e.what() << std::endl; std::cout << "exception: " << e.what() << std::endl;
std::cout << "FAIL - ERROR. Test failed." << std::endl; std::cout << "FAIL - ERROR. Test failed." << std::endl;
......
...@@ -33,8 +33,8 @@ ...@@ -33,8 +33,8 @@
#include "openmm/Context.h" #include "openmm/Context.h"
#include "openmm/OpenMMException.h" #include "openmm/OpenMMException.h"
#include "openmm/internal/ContextImpl.h" #include "openmm/internal/ContextImpl.h"
#include "openmm/internal/OSRngSeed.h"
#include "openmm/DrudeKernels.h" #include "openmm/DrudeKernels.h"
#include <cmath>
#include <ctime> #include <ctime>
#include <string> #include <string>
...@@ -49,7 +49,7 @@ DrudeLangevinIntegrator::DrudeLangevinIntegrator(double temperature, double fric ...@@ -49,7 +49,7 @@ DrudeLangevinIntegrator::DrudeLangevinIntegrator(double temperature, double fric
setDrudeFriction(drudeFrictionCoeff); setDrudeFriction(drudeFrictionCoeff);
setStepSize(stepSize); setStepSize(stepSize);
setConstraintTolerance(1e-5); setConstraintTolerance(1e-5);
setRandomNumberSeed((int) time(NULL)); setRandomNumberSeed(osrngseed());
} }
void DrudeLangevinIntegrator::initialize(ContextImpl& contextRef) { void DrudeLangevinIntegrator::initialize(ContextImpl& contextRef) {
......
...@@ -33,10 +33,10 @@ ...@@ -33,10 +33,10 @@
#include "openmm/Context.h" #include "openmm/Context.h"
#include "openmm/OpenMMException.h" #include "openmm/OpenMMException.h"
#include "openmm/internal/ContextImpl.h" #include "openmm/internal/ContextImpl.h"
#include "openmm/internal/OSRngSeed.h"
#include "openmm/RpmdKernels.h" #include "openmm/RpmdKernels.h"
#include "SimTKOpenMMRealType.h" #include "SimTKOpenMMRealType.h"
#include <cmath> #include <cmath>
#include <ctime>
#include <string> #include <string>
using namespace OpenMM; using namespace OpenMM;
...@@ -48,7 +48,7 @@ RPMDIntegrator::RPMDIntegrator(int numCopies, double temperature, double frictio ...@@ -48,7 +48,7 @@ RPMDIntegrator::RPMDIntegrator(int numCopies, double temperature, double frictio
setFriction(frictionCoeff); setFriction(frictionCoeff);
setStepSize(stepSize); setStepSize(stepSize);
setConstraintTolerance(1e-5); setConstraintTolerance(1e-5);
setRandomNumberSeed((int) time(NULL)); setRandomNumberSeed(osrngseed());
} }
RPMDIntegrator::RPMDIntegrator(int numCopies, double temperature, double frictionCoeff, double stepSize) : RPMDIntegrator::RPMDIntegrator(int numCopies, double temperature, double frictionCoeff, double stepSize) :
...@@ -57,7 +57,7 @@ RPMDIntegrator::RPMDIntegrator(int numCopies, double temperature, double frictio ...@@ -57,7 +57,7 @@ RPMDIntegrator::RPMDIntegrator(int numCopies, double temperature, double frictio
setFriction(frictionCoeff); setFriction(frictionCoeff);
setStepSize(stepSize); setStepSize(stepSize);
setConstraintTolerance(1e-5); setConstraintTolerance(1e-5);
setRandomNumberSeed((int) time(NULL)); setRandomNumberSeed(osrngseed());
} }
void RPMDIntegrator::initialize(ContextImpl& contextRef) { void RPMDIntegrator::initialize(ContextImpl& contextRef) {
......
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
* -------------------------------------------------------------------------- */ * -------------------------------------------------------------------------- */
#include "openmm/internal/AssertionUtilities.h" #include "openmm/internal/AssertionUtilities.h"
#include "openmm/CustomNonbondedForce.h"
#include "openmm/NonbondedForce.h" #include "openmm/NonbondedForce.h"
#include "openmm/OpenMMException.h" #include "openmm/OpenMMException.h"
#include <iostream> #include <iostream>
...@@ -166,10 +167,62 @@ void testReplaceExceptions() { ...@@ -166,10 +167,62 @@ void testReplaceExceptions() {
ASSERT(charge == 5.0); ASSERT(charge == 5.0);
} }
/**
* This is the same as testFindExceptions(), except it tests adding exclusions to a CustomNonbondedForce.
*/
void testFindCustomExclusions() {
CustomNonbondedForce nonbonded("r");
vector<pair<int, int> > bonds;
vector<double> params;
for (int i = 0; i < NUM_ATOMS; i++)
nonbonded.addParticle(params);
// loop over all main-chain atoms (even numbered atoms)
for (int i = 0; i < NUM_ATOMS-1; i += 2)
{
// side-chain bonds
bonds.push_back(pair<int, int>(i, i+1));
// main-chain bonds
if (i < NUM_ATOMS-2) // penultimate atom (NUM_ATOMS-2) has no subsequent main-chain atom
bonds.push_back(pair<int, int>(i, i+2));
}
nonbonded.createExclusionsFromBonds(bonds, 3);
// Build lists of the expected exclusions.
vector<set<int> > expectedExclusions(NUM_ATOMS);
int totalExclusions = 0;
for (int i = 0; i < NUM_ATOMS; i += 2) {
addAtomsToExclusions(i, i+1, expectedExclusions, totalExclusions);
addAtomsToExclusions(i, i+2, expectedExclusions, totalExclusions);
addAtomsToExclusions(i, i+3, expectedExclusions, totalExclusions);
addAtomsToExclusions(i, i+4, expectedExclusions, totalExclusions);
addAtomsToExclusions(i+1, i+2, expectedExclusions, totalExclusions);
addAtomsToExclusions(i, i+5, expectedExclusions, totalExclusions);
addAtomsToExclusions(i, i+6, expectedExclusions, totalExclusions);
addAtomsToExclusions(i+1, i+3, expectedExclusions, totalExclusions);
addAtomsToExclusions(i+1, i+4, expectedExclusions, totalExclusions);
}
for (int i = 0; i < nonbonded.getNumExclusions(); i++) {
int particle1, particle2;
nonbonded.getExclusionParticles(i, particle1, particle2);
}
// Compare them to the exceptions that were generated.
ASSERT_EQUAL(totalExclusions, nonbonded.getNumExclusions());
for (int i = 0; i < nonbonded.getNumExclusions(); i++) {
int particle1, particle2;
nonbonded.getExclusionParticles(i, particle1, particle2);
ASSERT(expectedExclusions[particle1].find(particle2) != expectedExclusions[particle1].end());
}
}
int main() { int main() {
try { try {
testFindExceptions(); testFindExceptions();
testReplaceExceptions(); testReplaceExceptions();
testFindCustomExclusions();
} }
catch(const exception& e) { catch(const exception& e) {
cout << "exception: " << e.what() << endl; cout << "exception: " << e.what() << endl;
......
...@@ -55,8 +55,12 @@ foreach(SUBDIR ${SUBDIRS}) ...@@ -55,8 +55,12 @@ foreach(SUBDIR ${SUBDIRS})
"${CMAKE_CURRENT_SOURCE_DIR}/${SUBDIR}/*.xml" "${CMAKE_CURRENT_SOURCE_DIR}/${SUBDIR}/*.xml"
"${CMAKE_CURRENT_SOURCE_DIR}/${SUBDIR}/*.pdb" "${CMAKE_CURRENT_SOURCE_DIR}/${SUBDIR}/*.pdb"
"${CMAKE_CURRENT_SOURCE_DIR}/${SUBDIR}/*.prmtop" "${CMAKE_CURRENT_SOURCE_DIR}/${SUBDIR}/*.prmtop"
"${CMAKE_CURRENT_SOURCE_DIR}/${SUBDIR}/*.parm7"
"${CMAKE_CURRENT_SOURCE_DIR}/${SUBDIR}/*.rst7"
"${CMAKE_CURRENT_SOURCE_DIR}/${SUBDIR}/*.ncrst"
"${CMAKE_CURRENT_SOURCE_DIR}/${SUBDIR}/*.dms" "${CMAKE_CURRENT_SOURCE_DIR}/${SUBDIR}/*.dms"
"${CMAKE_CURRENT_SOURCE_DIR}/${SUBDIR}/*.top" "${CMAKE_CURRENT_SOURCE_DIR}/${SUBDIR}/*.top"
"${CMAKE_CURRENT_SOURCE_DIR}/${SUBDIR}/*.par"
"${CMAKE_CURRENT_SOURCE_DIR}/${SUBDIR}/*psf" "${CMAKE_CURRENT_SOURCE_DIR}/${SUBDIR}/*psf"
"${CMAKE_CURRENT_SOURCE_DIR}/${SUBDIR}/charmm22.*" "${CMAKE_CURRENT_SOURCE_DIR}/${SUBDIR}/charmm22.*"
) )
......
...@@ -6,9 +6,9 @@ Simbios, the NIH National Center for Physics-Based Simulation of ...@@ -6,9 +6,9 @@ Simbios, the NIH National Center for Physics-Based Simulation of
Biological Structures at Stanford, funded under the NIH Roadmap for Biological Structures at Stanford, funded under the NIH Roadmap for
Medical Research, grant U54 GM072970. See https://simtk.org. Medical Research, grant U54 GM072970. See https://simtk.org.
Portions copyright (c) 2012 Stanford University and the Authors. Portions copyright (c) 2012-2014 Stanford University and the Authors.
Authors: Peter Eastman Authors: Peter Eastman
Contributors: Contributors: Jason Swails
Permission is hereby granted, free of charge, to any person obtaining a Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"), copy of this software and associated documentation files (the "Software"),
...@@ -31,51 +31,55 @@ USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -31,51 +31,55 @@ USE OR OTHER DEALINGS IN THE SOFTWARE.
__author__ = "Peter Eastman" __author__ = "Peter Eastman"
__version__ = "1.0" __version__ = "1.0"
from functools import wraps
from simtk.openmm.app.internal import amber_file_parser from simtk.openmm.app.internal import amber_file_parser
from simtk.unit import Quantity, nanometers, picoseconds from simtk.unit import Quantity, nanometers, picoseconds
import warnings
try: try:
import numpy import numpy as np
except: except:
pass np = None
def numpy_protector(func):
"""
Decorator to emit useful error messages if users try to request numpy
processing if numpy is not available. Raises ImportError if numpy could not
be found
"""
@wraps(func)
def wrapper(self, asNumpy=False):
if asNumpy and np is None:
raise ImportError('Could not import numpy. Cannot set asNumpy=True')
return func(self, asNumpy=asNumpy)
return wrapper
class AmberInpcrdFile(object): class AmberInpcrdFile(object):
"""AmberInpcrdFile parses an AMBER inpcrd file and loads the data stored in it.""" """AmberInpcrdFile parses an AMBER inpcrd file and loads the data stored in it."""
def __init__(self, file, loadVelocities=False, loadBoxVectors=False): def __init__(self, file, loadVelocities=None, loadBoxVectors=None):
"""Load an inpcrd file. """Load an inpcrd file.
An inpcrd file contains atom positions and, optionally, velocities and periodic box dimensions. An inpcrd file contains atom positions and, optionally, velocities and periodic box dimensions.
Unfortunately, it is sometimes impossible to determine from the file itself exactly what data
it contains. You therefore must specify in advance what data to load. It is stored into this
object's "positions", "velocities", and "boxVectors" fields.
Parameters: Parameters:
- file (string) the name of the file to load - file (string) the name of the file to load
- loadVelocities (boolean=False) whether to load velocities from the file - loadVelocities (boolean=None) deprecated. Velocities are loaded automatically if present
- loadBoxVectors (boolean=False) whether to load the periodic box vectors - loadBoxVectors (boolean=None) deprecated. Box vectors are loaded automatically if present
""" """
results = amber_file_parser.readAmberCoordinates(file, read_velocities=loadVelocities, read_box=loadBoxVectors) self.file = file
if loadVelocities: if loadVelocities is not None or loadBoxVectors is not None:
## The atom positions read from the inpcrd file warnings.warn('loadVelocities and loadBoxVectors have been '
self.positions = results[0] 'deprecated. velocities and box information '
if loadBoxVectors: 'is loaded automatically if the inpcrd file contains '
## The periodic box vectors read from the inpcrd file 'them.', DeprecationWarning)
self.boxVectors = results[1] results = amber_file_parser.readAmberCoordinates(file)
## The atom velocities read from the inpcrd file self.positions, self.velocities, self.boxVectors = results
self.velocities = results[2] # Cached numpy arrays
else:
self.velocities = results[1]
elif loadBoxVectors:
self.positions = results[0]
self.boxVectors = results[1]
else:
self.positions = results
self._numpyPositions = None self._numpyPositions = None
if loadVelocities:
self._numpyVelocities = None self._numpyVelocities = None
if loadBoxVectors:
self._numpyBoxVectors = None self._numpyBoxVectors = None
@numpy_protector
def getPositions(self, asNumpy=False): def getPositions(self, asNumpy=False):
"""Get the atomic positions. """Get the atomic positions.
...@@ -84,34 +88,40 @@ class AmberInpcrdFile(object): ...@@ -84,34 +88,40 @@ class AmberInpcrdFile(object):
""" """
if asNumpy: if asNumpy:
if self._numpyPositions is None: if self._numpyPositions is None:
self._numpyPositions = Quantity(numpy.array(self.positions.value_in_unit(nanometers)), nanometers) self._numpyPositions = Quantity(np.array(self.positions.value_in_unit(nanometers)), nanometers)
return self._numpyPositions return self._numpyPositions
return self.positions return self.positions
@numpy_protector
def getVelocities(self, asNumpy=False): def getVelocities(self, asNumpy=False):
"""Get the atomic velocities. """Get the atomic velocities.
Parameters: Parameters:
- asNumpy (boolean=False) if true, the vectors are returned as numpy arrays instead of Vec3s - asNumpy (boolean=False) if true, the vectors are returned as numpy arrays instead of Vec3s
""" """
if self.velocities is None:
raise AttributeError('velocities not found in %s' % self.file)
if asNumpy: if asNumpy:
if self._numpyVelocities is None: if self._numpyVelocities is None:
self._numpyVelocities = Quantity(numpy.array(self.velocities.value_in_unit(nanometers/picoseconds)), nanometers/picoseconds) self._numpyVelocities = Quantity(np.array(self.velocities.value_in_unit(nanometers/picoseconds)), nanometers/picoseconds)
return self._numpyVelocities return self._numpyVelocities
return self.velocities return self.velocities
@numpy_protector
def getBoxVectors(self, asNumpy=False): def getBoxVectors(self, asNumpy=False):
"""Get the periodic box vectors. """Get the periodic box vectors.
Parameters: Parameters:
- asNumpy (boolean=False) if true, the values are returned as a numpy array instead of a list of Vec3s - asNumpy (boolean=False) if true, the values are returned as a numpy array instead of a list of Vec3s
""" """
if self.boxVectors is None:
raise AttributeError('Box information not found in %s' % self.file)
if asNumpy: if asNumpy:
if self._numpyBoxVectors is None: if self._numpyBoxVectors is None:
self._numpyBoxVectors = [] self._numpyBoxVectors = []
self._numpyBoxVectors.append(Quantity(numpy.array(self.boxVectors[0].value_in_unit(nanometers)), nanometers)) self._numpyBoxVectors.append(Quantity(np.array(self.boxVectors[0].value_in_unit(nanometers)), nanometers))
self._numpyBoxVectors.append(Quantity(numpy.array(self.boxVectors[1].value_in_unit(nanometers)), nanometers)) self._numpyBoxVectors.append(Quantity(np.array(self.boxVectors[1].value_in_unit(nanometers)), nanometers))
self._numpyBoxVectors.append(Quantity(numpy.array(self.boxVectors[2].value_in_unit(nanometers)), nanometers)) self._numpyBoxVectors.append(Quantity(np.array(self.boxVectors[2].value_in_unit(nanometers)), nanometers))
return self._numpyBoxVectors return self._numpyBoxVectors
return self.boxVectors return self.boxVectors
...@@ -13,7 +13,7 @@ Copyright (c) 2014 the Authors ...@@ -13,7 +13,7 @@ Copyright (c) 2014 the Authors
Author: Jason Deckman Author: Jason Deckman
Contributors: Jason M. Swails Contributors: Jason M. Swails
Date: April 19, 2014 Date: June 6, 2014
Permission is hereby granted, free of charge, to any person obtaining a Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"), copy of this software and associated documentation files (the "Software"),
...@@ -105,7 +105,7 @@ class CharmmCrdFile(object): ...@@ -105,7 +105,7 @@ class CharmmCrdFile(object):
self.resname.append(line[2]) self.resname.append(line[2])
self.attype.append(line[3]) self.attype.append(line[3])
pos = Vec3(float(line[4]), float(line[5]), float(line[6])) pos = Vec3(float(line[4]), float(line[5]), float(line[6]))
self.positions.append(pos * u.angstroms) self.positions.append(pos)
self.segid.append(line[7]) self.segid.append(line[7])
self.resid.append(int(line[8])) self.resid.append(int(line[8]))
self.weighting.append(float(line[9])) self.weighting.append(float(line[9]))
...@@ -120,6 +120,10 @@ class CharmmCrdFile(object): ...@@ -120,6 +120,10 @@ class CharmmCrdFile(object):
except (ValueError, IndexError), e: except (ValueError, IndexError), e:
raise CharmmFileError('Error parsing CHARMM coordinate file') raise CharmmFileError('Error parsing CHARMM coordinate file')
# Apply units to the positions now. Do it this way to allow for
# (possible) numpy functionality in the future.
self.positions = u.Quantity(self.positions, u.angstroms)
class CharmmRstFile(object): class CharmmRstFile(object):
""" """
Reads and parses data, velocities and coordinates from a CHARMM restart Reads and parses data, velocities and coordinates from a CHARMM restart
...@@ -209,8 +213,9 @@ class CharmmRstFile(object): ...@@ -209,8 +213,9 @@ class CharmmRstFile(object):
self.velocities = [v * ONE_TIMESCALE for v in self.velocities] self.velocities = [v * ONE_TIMESCALE for v in self.velocities]
# Add units to positions and velocities # Add units to positions and velocities
self.positions *= u.angstroms self.positions = u.Quantity(self.positions, u.angstroms)
self.velocities *= u.angstroms / u.picoseconds self.positionsold = u.Quantity(self.positionsold, u.angstroms)
self.velocities = u.Quantity(self.velocities, u.angstroms/u.picoseconds)
def _scan(self, handle, str, r=0): # read lines in file until str is found def _scan(self, handle, str, r=0): # read lines in file until str is found
scanning = True scanning = True
......
...@@ -241,6 +241,10 @@ class CharmmParameterSet(object): ...@@ -241,6 +241,10 @@ class CharmmParameterSet(object):
if line.startswith('HBOND'): if line.startswith('HBOND'):
section = None section = None
continue continue
# It seems like files? sections? can be terminated with 'END'
if line.startswith('END'): # should this be case-insensitive?
section = None
continue
# If we have no section, skip # If we have no section, skip
if section is None: continue if section is None: continue
# Now handle each section specifically # Now handle each section specifically
......
...@@ -90,6 +90,7 @@ class CharmmPsfFile(object): ...@@ -90,6 +90,7 @@ class CharmmPsfFile(object):
- bond_list - bond_list
- angle_list - angle_list
- dihedral_list - dihedral_list
- dihedral_parameter_list
- improper_list - improper_list
- cmap_list - cmap_list
- donor_list # hbonds donors? - donor_list # hbonds donors?
...@@ -349,6 +350,7 @@ class CharmmPsfFile(object): ...@@ -349,6 +350,7 @@ class CharmmPsfFile(object):
self.bond_list = bond_list self.bond_list = bond_list
self.angle_list = angle_list self.angle_list = angle_list
self.dihedral_list = dihedral_list self.dihedral_list = dihedral_list
self.dihedral_parameter_list = TrackedList()
self.improper_list = improper_list self.improper_list = improper_list
self.cmap_list = cmap_list self.cmap_list = cmap_list
self.donor_list = donor_list self.donor_list = donor_list
...@@ -599,9 +601,9 @@ class CharmmPsfFile(object): ...@@ -599,9 +601,9 @@ class CharmmPsfFile(object):
central atoms in impropers) and see if that matches. Wild-cards central atoms in impropers) and see if that matches. Wild-cards
will apply ONLY if specific parameters cannot be found. will apply ONLY if specific parameters cannot be found.
- This method will expand the dihedral_list attribute by adding a - This method will expand the dihedral_parameter_list attribute by
separate Dihedral object for each term for types that have a adding a separate Dihedral object for each term for types that
multi-term expansion have a multi-term expansion
""" """
# First load the atom types # First load the atom types
types_are_int = False types_are_int = False
...@@ -643,12 +645,9 @@ class CharmmPsfFile(object): ...@@ -643,12 +645,9 @@ class CharmmPsfFile(object):
self.urey_bradley_list.append(ub) self.urey_bradley_list.append(ub)
except KeyError: except KeyError:
raise MissingParameter('Missing angle type for %r' % ang) raise MissingParameter('Missing angle type for %r' % ang)
# Next load all of the dihedrals. This is a little trickier since we # Next load all of the dihedrals.
# need to back up the existing dihedral list and replace it with a self.dihedral_parameter_list = TrackedList()
# longer one that has only one Fourier term per Dihedral instance. for dih in self.dihedral_list:
dihedral_list = self.dihedral_list
self.dihedral_list = TrackedList()
for dih in dihedral_list:
# Store the atoms # Store the atoms
a1, a2, a3, a4 = dih.atom1, dih.atom2, dih.atom3, dih.atom4 a1, a2, a3, a4 = dih.atom1, dih.atom2, dih.atom3, dih.atom4
at1, at2, at3, at4 = a1.attype, a2.attype, a3.attype, a4.attype at1, at2, at3, at4 = a1.attype, a2.attype, a3.attype, a4.attype
...@@ -662,14 +661,14 @@ class CharmmPsfFile(object): ...@@ -662,14 +661,14 @@ class CharmmPsfFile(object):
'%r' % dih) '%r' % dih)
dtlist = parmset.dihedral_types[key] dtlist = parmset.dihedral_types[key]
for i, dt in enumerate(dtlist): for i, dt in enumerate(dtlist):
self.dihedral_list.append(Dihedral(a1, a2, a3, a4, dt)) self.dihedral_parameter_list.append(Dihedral(a1,a2,a3,a4,dt))
# See if we include the end-group interactions for this # See if we include the end-group interactions for this
# dihedral. We do IFF it is the last or only dihedral term and # dihedral. We do IFF it is the last or only dihedral term and
# it is NOT in the angle/bond partners # it is NOT in the angle/bond partners
if i != len(dtlist) - 1: if i != len(dtlist) - 1:
self.dihedral_list[-1].end_groups_active = False self.dihedral_parameter_list[-1].end_groups_active = False
elif a1 in a4.bond_partners or a1 in a4.angle_partners: elif a1 in a4.bond_partners or a1 in a4.angle_partners:
self.dihedral_list[-1].end_groups_active = False self.dihedral_parameter_list[-1].end_groups_active = False
# Now do the impropers # Now do the impropers
for imp in self.improper_list: for imp in self.improper_list:
# Store the atoms # Store the atoms
...@@ -755,6 +754,12 @@ class CharmmPsfFile(object): ...@@ -755,6 +754,12 @@ class CharmmPsfFile(object):
- alpha, beta, gamma (floats, optional) : Angles between the - alpha, beta, gamma (floats, optional) : Angles between the
periodic cells. periodic cells.
""" """
try:
# Since we are setting the box, delete the cached box lengths if we
# have them to make sure they are recomputed if desired.
del self._boxLengths
except AttributeError:
pass
self.box_vectors = _box_vectors_from_lengths_angles(a, b, c, self.box_vectors = _box_vectors_from_lengths_angles(a, b, c,
alpha, beta, gamma) alpha, beta, gamma)
# If we already have a _topology instance, then we have possibly changed # If we already have a _topology instance, then we have possibly changed
...@@ -952,8 +957,6 @@ class CharmmPsfFile(object): ...@@ -952,8 +957,6 @@ class CharmmPsfFile(object):
- flexibleConstraints (bool=True) Are our constraints flexible or not? - flexibleConstraints (bool=True) Are our constraints flexible or not?
- verbose (bool=False) Optionally prints out a running progress report - verbose (bool=False) Optionally prints out a running progress report
""" """
# back up the dihedral list
dihedral_list = self.dihedral_list
# Load the parameter set # Load the parameter set
self.loadParameters(params.condense()) self.loadParameters(params.condense())
hasbox = self.topology.getUnitCellDimensions() is not None hasbox = self.topology.getUnitCellDimensions() is not None
...@@ -1103,7 +1106,7 @@ class CharmmPsfFile(object): ...@@ -1103,7 +1106,7 @@ class CharmmPsfFile(object):
if verbose: print('Adding torsions...') if verbose: print('Adding torsions...')
force = mm.PeriodicTorsionForce() force = mm.PeriodicTorsionForce()
force.setForceGroup(self.DIHEDRAL_FORCE_GROUP) force.setForceGroup(self.DIHEDRAL_FORCE_GROUP)
for tor in self.dihedral_list: for tor in self.dihedral_parameter_list:
force.addTorsion(tor.atom1.idx, tor.atom2.idx, tor.atom3.idx, force.addTorsion(tor.atom1.idx, tor.atom2.idx, tor.atom3.idx,
tor.atom4.idx, tor.dihedral_type.per, tor.atom4.idx, tor.dihedral_type.per,
tor.dihedral_type.phase*pi/180, tor.dihedral_type.phase*pi/180,
...@@ -1235,7 +1238,7 @@ class CharmmPsfFile(object): ...@@ -1235,7 +1238,7 @@ class CharmmPsfFile(object):
# Add 1-4 interactions # Add 1-4 interactions
excluded_atom_pairs = set() # save these pairs so we don't zero them out excluded_atom_pairs = set() # save these pairs so we don't zero them out
sigma_scale = 2**(-1/6) sigma_scale = 2**(-1/6)
for tor in self.dihedral_list: for tor in self.dihedral_parameter_list:
# First check to see if atoms 1 and 4 are already excluded because # First check to see if atoms 1 and 4 are already excluded because
# they are 1-2 or 1-3 pairs (would happen in 6-member rings or # they are 1-2 or 1-3 pairs (would happen in 6-member rings or
# fewer). Then check that they're not already added as exclusions # fewer). Then check that they're not already added as exclusions
...@@ -1355,9 +1358,6 @@ class CharmmPsfFile(object): ...@@ -1355,9 +1358,6 @@ class CharmmPsfFile(object):
# Cache our system for easy access # Cache our system for easy access
self._system = system self._system = system
# Restore the dihedral list to allow reparametrization later
self.dihedral_list = dihedral_list
return system return system
@property @property
...@@ -1422,9 +1422,30 @@ class CharmmPsfFile(object): ...@@ -1422,9 +1422,30 @@ class CharmmPsfFile(object):
@property @property
def boxLengths(self): def boxLengths(self):
""" Return tuple of 3 units """ """ Return tuple of 3 units """
try:
# See if we have a cached version
return self._boxLengths
except AttributeError:
pass
if self.box_vectors is not None: if self.box_vectors is not None:
return (self.box_vectors[0][0], self.box_vectors[0][1], # Get the lengths of each vector
self.box_vectors[0][2]) if u.is_quantity(self.box_vectors):
# Unlikely -- each vector is like a quantity
vecs = self.box_vectors.value_in_unit(u.nanometers)
elif u.is_quantity(self.box_vectors[0]):
# Assume all box vectors are quantities
vecs = [x.value_in_unit(u.nanometers) for x in self.box_vectors]
else:
# Assume nanometers
vecs = self.box_vectors
a = sqrt(vecs[0][0]*vecs[0][0] + vecs[0][1]*vecs[0][1] +
vecs[0][2]*vecs[0][2])
b = sqrt(vecs[1][0]*vecs[1][0] + vecs[1][1]*vecs[1][1] +
vecs[1][2]*vecs[1][2])
c = sqrt(vecs[2][0]*vecs[2][0] + vecs[2][1]*vecs[2][1] +
vecs[2][2]*vecs[2][2])
self._boxLengths = (a, b, c) * u.nanometers
return self._boxLengths
return None return None
@boxLengths.setter @boxLengths.setter
...@@ -1440,6 +1461,12 @@ class CharmmPsfFile(object): ...@@ -1440,6 +1461,12 @@ class CharmmPsfFile(object):
@boxVectors.setter @boxVectors.setter
def boxVectors(self, stuff): def boxVectors(self, stuff):
""" Sets the box vectors """ """ Sets the box vectors """
try:
# We may be changing the box, so delete the cached box lengths to
# make sure they are recomputed if desired
del self._boxLengths
except AttributeError:
pass
self.box_vectors = stuff self.box_vectors = stuff
def deleteCmap(self): def deleteCmap(self):
......
This diff is collapsed.
This diff is collapsed.
<ForceField>
<Info>
<DateGenerated>2014-05-28</DateGenerated>
<Reference>Lee-Ping Wang, Todd J. Martinez and Vijay S. Pande. Building force fields - an automatic, systematic and reproducible approach. Journal of Physical Chemistry Letters, 2014, 5, pp 1885-1891. DOI:10.1021/jz500737m</Reference>
</Info>
<AtomTypes>
<Type name="tip3p-fb-O" class="OW" element="O" mass="15.99943"/>
<Type name="tip3p-fb-H" class="HW" element="H" mass="1.007947"/>
</AtomTypes>
<Residues>
<Residue name="HOH">
<Atom name="O" type="tip3p-fb-O"/>
<Atom name="H1" type="tip3p-fb-H"/>
<Atom name="H2" type="tip3p-fb-H"/>
<Bond from="0" to="1"/>
<Bond from="0" to="2"/>
</Residue>
</Residues>
<HarmonicBondForce>
<Bond class1="OW" class2="HW" length="0.101181082494" k="462750.4"/>
</HarmonicBondForce>
<HarmonicAngleForce>
<Angle class1="HW" class2="OW" class3="HW" angle="1.88754640288" k="836.8"/>
</HarmonicAngleForce>
<NonbondedForce coulomb14scale="0.833333" lj14scale="0.5">
<Atom type="tip3p-fb-O" charge="-0.848448690103" sigma="0.317796456355" epsilon="0.652143528104" />
<Atom type="tip3p-fb-H" charge="0.4242243450515" sigma="1" epsilon="0" />
</NonbondedForce>
</ForceField>
<ForceField>
<Info>
<DateGenerated>2014-05-28</DateGenerated>
<Reference>Lee-Ping Wang, Todd J. Martinez and Vijay S. Pande. Building force fields - an automatic, systematic and reproducible approach. Journal of Physical Chemistry Letters, 2014, 5, pp 1885-1891. DOI:10.1021/jz500737m</Reference>
</Info>
<AtomTypes>
<Type name="tip4p-fb-O" class="OW" element="O" mass="15.99943"/>
<Type name="tip4p-fb-H" class="HW" element="H" mass="1.007947"/>
<Type name="tip4p-fb-M" class="MW" mass="0"/>
</AtomTypes>
<Residues>
<Residue name="HOH">
<Atom name="O" type="tip4p-fb-O"/>
<Atom name="H1" type="tip4p-fb-H"/>
<Atom name="H2" type="tip4p-fb-H"/>
<Atom name="M" type="tip4p-fb-M"/>
<VirtualSite type="average3" index="3" atom1="0" atom2="1" atom3="2" weight1="8.203146574531e-01" weight2="8.984267127345e-02" weight3="8.984267127345e-02" />
<Bond from="0" to="1"/>
<Bond from="0" to="2"/>
</Residue>
</Residues>
<HarmonicBondForce>
<Bond class1="OW" class2="HW" length="0.09572" k="462750.4"/>
</HarmonicBondForce>
<HarmonicAngleForce>
<Angle class1="HW" class2="OW" class3="HW" angle="1.82421813418" k="836.8"/>
</HarmonicAngleForce>
<NonbondedForce coulomb14scale="0.833333" lj14scale="0.5">
<Atom type="tip4p-fb-O" charge="0" sigma="3.165552430462e-01" epsilon="7.492790213533e-01" />
<Atom type="tip4p-fb-H" charge="5.258681106763e-01" sigma="1" epsilon="0" />
<Atom type="tip4p-fb-M" charge="-1.0517362213526e+00" sigma="1" epsilon="0" />
</NonbondedForce>
</ForceField>
...@@ -6,7 +6,7 @@ Simbios, the NIH National Center for Physics-Based Simulation of ...@@ -6,7 +6,7 @@ Simbios, the NIH National Center for Physics-Based Simulation of
Biological Structures at Stanford, funded under the NIH Roadmap for Biological Structures at Stanford, funded under the NIH Roadmap for
Medical Research, grant U54 GM072970. See https://simtk.org. Medical Research, grant U54 GM072970. See https://simtk.org.
Portions copyright (c) 2012-2013 Stanford University and the Authors. Portions copyright (c) 2012-2014 Stanford University and the Authors.
Authors: Peter Eastman, Mark Friedrichs Authors: Peter Eastman, Mark Friedrichs
Contributors: Contributors:
...@@ -1485,6 +1485,104 @@ class CustomTorsionGenerator: ...@@ -1485,6 +1485,104 @@ class CustomTorsionGenerator:
parsers["CustomTorsionForce"] = CustomTorsionGenerator.parseElement parsers["CustomTorsionForce"] = CustomTorsionGenerator.parseElement
## @private
class CustomNonbondedGenerator:
"""A CustomNonbondedGenerator constructs a CustomNonbondedForce."""
def __init__(self, energy, bondCutoff):
self.energy = energy
self.bondCutoff = bondCutoff
self.typeMap = {}
self.globalParams = {}
self.perParticleParams = []
self.functions = []
@staticmethod
def parseElement(element, ff):
generator = CustomNonbondedGenerator(element.attrib['energy'], int(element.attrib['bondCutoff']))
ff._forces.append(generator)
for param in element.findall('GlobalParameter'):
generator.globalParams[param.attrib['name']] = float(param.attrib['defaultValue'])
for param in element.findall('PerParticleParameter'):
generator.perParticleParams.append(param.attrib['name'])
for atom in element.findall('Atom'):
types = ff._findAtomTypes(atom, 1)
if None not in types:
values = [float(atom.attrib[param]) for param in generator.perParticleParams]
for t in types[0]:
generator.typeMap[t] = values
def createForce(self, sys, data, nonbondedMethod, nonbondedCutoff, args):
methodMap = {NoCutoff:mm.CustomNonbondedForce.NoCutoff,
CutoffNonPeriodic:mm.CustomNonbondedForce.CutoffNonPeriodic,
CutoffPeriodic:mm.CustomNonbondedForce.CutoffPeriodic}
if nonbondedMethod not in methodMap:
raise ValueError('Illegal nonbonded method for CustomNonbondedForce')
force = mm.CustomNonbondedForce(self.energy)
for param in self.globalParams:
force.addGlobalParameter(param, self.globalParams[param])
for param in self.perParticleParams:
force.addPerParticleParameter(param)
for (name, type, values, params) in self.functions:
if type == 'Continuous1D':
force.addTabulatedFunction(name, mm.Continuous1DFunction(values, params['min'], params['max']))
elif type == 'Continuous2D':
force.addTabulatedFunction(name, mm.Continuous2DFunction(params['xsize'], params['ysize'], values, params['xmin'], params['xmax'], params['ymin'], params['ymax']))
elif type == 'Continuous3D':
force.addTabulatedFunction(name, mm.Continuous2DFunction(params['xsize'], params['ysize'], params['zsize'], values, params['xmin'], params['xmax'], params['ymin'], params['ymax'], params['zmin'], params['zmax']))
elif type == 'Discrete1D':
force.addTabulatedFunction(name, mm.Discrete1DFunction(values))
elif type == 'Discrete2D':
force.addTabulatedFunction(name, mm.Discrete2DFunction(params['xsize'], params['ysize'], values))
elif type == 'Discrete3D':
force.addTabulatedFunction(name, mm.Discrete2DFunction(params['xsize'], params['ysize'], params['zsize'], values))
for atom in data.atoms:
t = data.atomType[atom]
if t in self.typeMap:
values = self.typeMap[t]
force.addParticle(self.typeMap[t])
else:
raise ValueError('No CustomNonbonded parameters defined for atom type '+t)
force.setNonbondedMethod(methodMap[nonbondedMethod])
force.setCutoffDistance(nonbondedCutoff)
sys.addForce(force)
def postprocessSystem(self, sys, data, args):
# Create exceptions based on bonds, virtual sites, and Drude particles.
if self.bondCutoff == 0:
return
bondIndices = []
for bond in data.bonds:
bondIndices.append((bond.atom1, bond.atom2))
for i in range(sys.getNumParticles()):
if sys.isVirtualSite(i):
site = sys.getVirtualSite(i)
for j in range(site.getNumParticles()):
bondIndices.append((i, site.getParticle(j)))
drude = [f for f in sys.getForces() if isinstance(f, mm.DrudeForce)]
if len(drude) > 0:
drude = drude[0]
# For purposes of creating exceptions, a Drude particle is "bonded" to anything
# its parent atom is bonded to.
drudeMap = {}
for i in range(drude.getNumParticles()):
params = drude.getParticleParameters(i)
drudeMap[params[1]] = params[0]
for atom1, atom2 in bondIndices:
drude1 = drudeMap[atom1] if atom1 in drudeMap else None
drude2 = drudeMap[atom2] if atom2 in drudeMap else None
if drude1 is not None:
bondIndices.append((drude1, atom2))
if drude2 is not None:
bondIndices.append((drude1, drude2))
if drude2 is not None:
bondIndices.append((atom1, drude2))
nonbonded = [f for f in sys.getForces() if isinstance(f, mm.CustomNonbondedForce)][0]
nonbonded.createExclusionsFromBonds(bondIndices, self.bondCutoff)
parsers["CustomNonbondedForce"] = CustomNonbondedGenerator.parseElement
## @private ## @private
class CustomGBGenerator: class CustomGBGenerator:
"""A CustomGBGenerator constructs a CustomGBForce.""" """A CustomGBGenerator constructs a CustomGBForce."""
...@@ -1493,7 +1591,6 @@ class CustomGBGenerator: ...@@ -1493,7 +1591,6 @@ class CustomGBGenerator:
self.typeMap = {} self.typeMap = {}
self.globalParams = {} self.globalParams = {}
self.perParticleParams = [] self.perParticleParams = []
self.paramValues = []
self.computedValues = [] self.computedValues = []
self.energyTerms = [] self.energyTerms = []
self.functions = [] self.functions = []
...@@ -2517,6 +2614,7 @@ class AmoebaTorsionTorsionGenerator: ...@@ -2517,6 +2614,7 @@ class AmoebaTorsionTorsionGenerator:
gridRow.append(float(gridEntry.attrib['angle1'])) gridRow.append(float(gridEntry.attrib['angle1']))
gridRow.append(float(gridEntry.attrib['angle2'])) gridRow.append(float(gridEntry.attrib['angle2']))
gridRow.append(float(gridEntry.attrib['f'])) gridRow.append(float(gridEntry.attrib['f']))
if 'fx' in gridEntry.attrib:
gridRow.append(float(gridEntry.attrib['fx'])) gridRow.append(float(gridEntry.attrib['fx']))
gridRow.append(float(gridEntry.attrib['fy'])) gridRow.append(float(gridEntry.attrib['fy']))
gridRow.append(float(gridEntry.attrib['fxy'])) gridRow.append(float(gridEntry.attrib['fxy']))
......
...@@ -6,7 +6,7 @@ Simbios, the NIH National Center for Physics-Based Simulation of ...@@ -6,7 +6,7 @@ Simbios, the NIH National Center for Physics-Based Simulation of
Biological Structures at Stanford, funded under the NIH Roadmap for Biological Structures at Stanford, funded under the NIH Roadmap for
Medical Research, grant U54 GM072970. See https://simtk.org. Medical Research, grant U54 GM072970. See https://simtk.org.
Portions copyright (c) 2012-2013 Stanford University and the Authors. Portions copyright (c) 2012-2014 Stanford University and the Authors.
Authors: Peter Eastman Authors: Peter Eastman
Contributors: Contributors:
...@@ -745,6 +745,12 @@ class GromacsTopFile(object): ...@@ -745,6 +745,12 @@ class GromacsTopFile(object):
atom1params = nb.getParticleParameters(baseAtomIndex+atoms[0]) atom1params = nb.getParticleParameters(baseAtomIndex+atoms[0])
atom2params = nb.getParticleParameters(baseAtomIndex+atoms[1]) atom2params = nb.getParticleParameters(baseAtomIndex+atoms[1])
exceptions.append((baseAtomIndex+atoms[0], baseAtomIndex+atoms[1], atom1params[0]*atom2params[0]*fudgeQQ, params[0], params[1])) exceptions.append((baseAtomIndex+atoms[0], baseAtomIndex+atoms[1], atom1params[0]*atom2params[0]*fudgeQQ, params[0], params[1]))
for fields in moleculeType.exclusions:
atoms = [int(x)-1 for x in fields]
for atom in atoms[1:]:
if atom > atoms[0]:
exceptions.append((baseAtomIndex+atoms[0], baseAtomIndex+atom, 0, 0, 0))
# Create nonbonded exceptions. # Create nonbonded exceptions.
......
...@@ -421,8 +421,10 @@ class ResidueList(list): ...@@ -421,8 +421,10 @@ class ResidueList(list):
if self._last_residue is None: if self._last_residue is None:
res = self._last_residue = Residue(resname, resnum) res = self._last_residue = Residue(resname, resnum)
list.append(self, res) list.append(self, res)
elif self._last_residue != (resname, resnum): elif (self._last_residue != (resname, resnum) or
if self._last_residue.idx == resnum: system != self._last_residue.system):
if (self._last_residue.idx == resnum and
system == self._last_residue.system):
lresname = self._last_residue.resname lresname = self._last_residue.resname
warnings.warn('Residue %d split into separate residues %s ' warnings.warn('Residue %d split into separate residues %s '
'and %s' % (resnum, lresname, resname), 'and %s' % (resnum, lresname, resname),
...@@ -1063,7 +1065,6 @@ class TrackedList(list): ...@@ -1063,7 +1065,6 @@ class TrackedList(list):
__delitem__ = _tracking(list.__delitem__) __delitem__ = _tracking(list.__delitem__)
append = _tracking(list.append) append = _tracking(list.append)
extend = _tracking(list.extend) extend = _tracking(list.extend)
__delslice__ = _tracking(list.__delslice__)
__setitem__ = _tracking(list.__setitem__) __setitem__ = _tracking(list.__setitem__)
# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
......
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