Commit c7aa1d00 authored by kyleabeauchamp's avatar kyleabeauchamp
Browse files

Merge remote-tracking branch 'upstream/master' into vagrant

parents aea4e454 f7127d33
......@@ -61,7 +61,7 @@ void validateForce(System& system, vector<Vec3>& positions, double expectedEnerg
// Try moving each particle along each axis, and see if the energy changes by the correct amount.
double offset = 1e-3;
double offset = 1e-2;
for (int i = 0; i < system.getNumParticles(); i++)
for (int j = 0; j < 3; j++) {
vector<Vec3> offsetPos = positions;
......@@ -76,9 +76,9 @@ void validateForce(System& system, vector<Vec3>& positions, double expectedEnerg
}
void testSingleParticle() {
const double k = 1.5;
const double k = ONE_4PI_EPS0*1.5;
const double charge = 0.1;
const double alpha = charge*charge/k;
const double alpha = ONE_4PI_EPS0*charge*charge/k;
System system;
system.addParticle(1.0);
system.addParticle(1.0);
......@@ -92,9 +92,9 @@ void testSingleParticle() {
}
void testAnisotropicParticle() {
const double k = 1.5;
const double k = ONE_4PI_EPS0*1.5;
const double charge = 0.1;
const double alpha = charge*charge/k;
const double alpha = ONE_4PI_EPS0*charge*charge/k;
const double a1 = 0.8;
const double a2 = 1.1;
const double k1 = k/a1;
......@@ -124,9 +124,9 @@ double computeScreening(double r, double thole, double alpha1, double alpha2) {
}
void testThole() {
const double k = 1.5;
const double k = ONE_4PI_EPS0*1.5;
const double charge = 0.1;
const double alpha = charge*charge/k;
const double alpha = ONE_4PI_EPS0*charge*charge/k;
const double thole = 2.5;
System system;
system.addParticle(1.0);
......@@ -157,9 +157,9 @@ void testThole() {
}
void testChangingParameters() {
const double k = 1.5;
const double k = ONE_4PI_EPS0*1.5;
const double charge = 0.1;
const double alpha = charge*charge/k;
const double alpha = ONE_4PI_EPS0*charge*charge/k;
Platform& platform = Platform::getPlatformByName("CUDA");
// Create the system.
......@@ -184,9 +184,9 @@ void testChangingParameters() {
// Modify the parameters.
const double k2 = 2.2;
const double k2 = ONE_4PI_EPS0*2.2;
const double charge2 = 0.3;
const double alpha2 = charge2*charge2/k2;
const double alpha2 = ONE_4PI_EPS0*charge2*charge2/k2;
drude->setParticleParameters(0, 1, 0, -1, -1, -1, charge2, alpha2, 1, 1);
drude->updateParametersInContext(context);
state = context.getState(State::Energy);
......
......@@ -53,9 +53,9 @@ extern "C" OPENMM_EXPORT void registerDrudeCudaKernelFactories();
void testSinglePair() {
const double temperature = 300.0;
const double temperatureDrude = 10.0;
const double k = 1.5;
const double k = ONE_4PI_EPS0*1.5;
const double charge = 0.1;
const double alpha = charge*charge/k;
const double alpha = ONE_4PI_EPS0*charge*charge/k;
const double mass1 = 1.0;
const double mass2 = 0.1;
const double totalMass = mass1+mass2;
......@@ -132,7 +132,7 @@ void testWater() {
system.addConstraint(startIndex, startIndex+3, 0.09572);
system.addConstraint(startIndex+2, startIndex+3, 0.15139);
system.setVirtualSite(startIndex+4, new ThreeParticleAverageSite(startIndex, startIndex+2, startIndex+3, 0.786646558, 0.106676721, 0.106676721));
drude->addParticle(startIndex+1, startIndex, -1, -1, -1, -1.71636, 1.71636*1.71636/(100000*4.184), 1, 1);
drude->addParticle(startIndex+1, startIndex, -1, -1, -1, -1.71636, ONE_4PI_EPS0*1.71636*1.71636/(100000*4.184), 1, 1);
}
vector<Vec3> positions;
for (int i = 0; i < gridSize; i++)
......
......@@ -85,7 +85,7 @@ void testWater() {
system.addConstraint(startIndex, startIndex+3, 0.09572);
system.addConstraint(startIndex+2, startIndex+3, 0.15139);
system.setVirtualSite(startIndex+4, new ThreeParticleAverageSite(startIndex, startIndex+2, startIndex+3, 0.786646558, 0.106676721, 0.106676721));
drude->addParticle(startIndex+1, startIndex, -1, -1, -1, -1.71636, 1.71636*1.71636/(100000*4.184), 1, 1);
drude->addParticle(startIndex+1, startIndex, -1, -1, -1, -1.71636, ONE_4PI_EPS0*1.71636*1.71636/(100000*4.184), 1, 1);
}
vector<Vec3> positions;
for (int i = 0; i < gridSize; i++)
......
......@@ -131,9 +131,9 @@ void OpenCLCalcDrudeForceKernel::initialize(const System& system, const DrudeFor
double a1 = (atoms[i][2] == -1 ? 1 : aniso12);
double a2 = (atoms[i][3] == -1 || atoms[i][4] == -1 ? 1 : aniso34);
double a3 = 3-a1-a2;
double k3 = charge*charge/(polarizability*a3);
double k1 = charge*charge/(polarizability*a1) - k3;
double k2 = charge*charge/(polarizability*a2) - k3;
double k3 = ONE_4PI_EPS0*charge*charge/(polarizability*a3);
double k1 = ONE_4PI_EPS0*charge*charge/(polarizability*a1) - k3;
double k2 = ONE_4PI_EPS0*charge*charge/(polarizability*a2) - k3;
if (atoms[i][2] == -1) {
atoms[i][2] = 0;
k1 = 0;
......@@ -202,9 +202,9 @@ void OpenCLCalcDrudeForceKernel::copyParametersToContext(ContextImpl& context, c
double a1 = (p2 == -1 ? 1 : aniso12);
double a2 = (p3 == -1 || p4 == -1 ? 1 : aniso34);
double a3 = 3-a1-a2;
double k3 = charge*charge/(polarizability*a3);
double k1 = charge*charge/(polarizability*a1) - k3;
double k2 = charge*charge/(polarizability*a2) - k3;
double k3 = ONE_4PI_EPS0*charge*charge/(polarizability*a3);
double k1 = ONE_4PI_EPS0*charge*charge/(polarizability*a1) - k3;
double k2 = ONE_4PI_EPS0*charge*charge/(polarizability*a2) - k3;
if (p2 == -1)
k1 = 0;
if (p3 == -1 || p4 == -1)
......
......@@ -13,7 +13,7 @@ real u = drudeParams.x*r;
real screening = 1-(1+0.5f*u)*EXP(-u);
real pairEnergy = drudeParams.y*screening*rInv;
energy += pairEnergy;
real4 f = delta*(pairEnergy*rInv*rInv);
real4 f = delta*(drudeParams.y*rInv*rInv)*(screening*rInv-0.5f*(1+u)*EXP(-u)*drudeParams.x);
force1 += f;
force3 -= f;
......@@ -26,7 +26,7 @@ u = drudeParams.x*r;
screening = 1-(1+0.5f*u)*EXP(-u);
pairEnergy = -drudeParams.y*screening*rInv;
energy += pairEnergy;
f = delta*(pairEnergy*rInv*rInv);
f = delta*(-drudeParams.y*rInv*rInv)*(screening*rInv-0.5f*(1+u)*EXP(-u)*drudeParams.x);
force1 += f;
force4 -= f;
......@@ -39,7 +39,7 @@ u = drudeParams.x*r;
screening = 1-(1+0.5f*u)*EXP(-u);
pairEnergy = -drudeParams.y*screening*rInv;
energy += pairEnergy;
f = delta*(pairEnergy*rInv*rInv);
f = delta*(-drudeParams.y*rInv*rInv)*(screening*rInv-0.5f*(1+u)*EXP(-u)*drudeParams.x);
force2 += f;
force3 -= f;
......@@ -52,6 +52,6 @@ u = drudeParams.x*r;
screening = 1-(1+0.5f*u)*EXP(-u);
pairEnergy = drudeParams.y*screening*rInv;
energy += pairEnergy;
f = delta*(pairEnergy*rInv*rInv);
f = delta*(drudeParams.y*rInv*rInv)*(screening*rInv-0.5f*(1+u)*EXP(-u)*drudeParams.x);
force2 += f;
force4 -= f;
......@@ -61,7 +61,7 @@ void validateForce(System& system, vector<Vec3>& positions, double expectedEnerg
// Try moving each particle along each axis, and see if the energy changes by the correct amount.
double offset = 1e-3;
double offset = 1e-2;
for (int i = 0; i < system.getNumParticles(); i++)
for (int j = 0; j < 3; j++) {
vector<Vec3> offsetPos = positions;
......@@ -76,9 +76,9 @@ void validateForce(System& system, vector<Vec3>& positions, double expectedEnerg
}
void testSingleParticle() {
const double k = 1.5;
const double k = ONE_4PI_EPS0*1.5;
const double charge = 0.1;
const double alpha = charge*charge/k;
const double alpha = ONE_4PI_EPS0*charge*charge/k;
System system;
system.addParticle(1.0);
system.addParticle(1.0);
......@@ -92,9 +92,9 @@ void testSingleParticle() {
}
void testAnisotropicParticle() {
const double k = 1.5;
const double k = ONE_4PI_EPS0*1.5;
const double charge = 0.1;
const double alpha = charge*charge/k;
const double alpha = ONE_4PI_EPS0*charge*charge/k;
const double a1 = 0.8;
const double a2 = 1.1;
const double k1 = k/a1;
......@@ -124,9 +124,9 @@ double computeScreening(double r, double thole, double alpha1, double alpha2) {
}
void testThole() {
const double k = 1.5;
const double k = ONE_4PI_EPS0*1.5;
const double charge = 0.1;
const double alpha = charge*charge/k;
const double alpha = ONE_4PI_EPS0*charge*charge/k;
const double thole = 2.5;
System system;
system.addParticle(1.0);
......@@ -157,9 +157,9 @@ void testThole() {
}
void testChangingParameters() {
const double k = 1.5;
const double k = ONE_4PI_EPS0*1.5;
const double charge = 0.1;
const double alpha = charge*charge/k;
const double alpha = ONE_4PI_EPS0*charge*charge/k;
Platform& platform = Platform::getPlatformByName("OpenCL");
// Create the system.
......@@ -184,9 +184,9 @@ void testChangingParameters() {
// Modify the parameters.
const double k2 = 2.2;
const double k2 = ONE_4PI_EPS0*2.2;
const double charge2 = 0.3;
const double alpha2 = charge2*charge2/k2;
const double alpha2 = ONE_4PI_EPS0*charge2*charge2/k2;
drude->setParticleParameters(0, 1, 0, -1, -1, -1, charge2, alpha2, 1, 1);
drude->updateParametersInContext(context);
state = context.getState(State::Energy);
......
......@@ -53,9 +53,9 @@ extern "C" OPENMM_EXPORT void registerDrudeOpenCLKernelFactories();
void testSinglePair() {
const double temperature = 300.0;
const double temperatureDrude = 10.0;
const double k = 1.5;
const double k = ONE_4PI_EPS0*1.5;
const double charge = 0.1;
const double alpha = charge*charge/k;
const double alpha = ONE_4PI_EPS0*charge*charge/k;
const double mass1 = 1.0;
const double mass2 = 0.1;
const double totalMass = mass1+mass2;
......@@ -132,7 +132,7 @@ void testWater() {
system.addConstraint(startIndex, startIndex+3, 0.09572);
system.addConstraint(startIndex+2, startIndex+3, 0.15139);
system.setVirtualSite(startIndex+4, new ThreeParticleAverageSite(startIndex, startIndex+2, startIndex+3, 0.786646558, 0.106676721, 0.106676721));
drude->addParticle(startIndex+1, startIndex, -1, -1, -1, -1.71636, 1.71636*1.71636/(100000*4.184), 1, 1);
drude->addParticle(startIndex+1, startIndex, -1, -1, -1, -1.71636, ONE_4PI_EPS0*1.71636*1.71636/(100000*4.184), 1, 1);
}
vector<Vec3> positions;
for (int i = 0; i < gridSize; i++)
......
......@@ -85,7 +85,7 @@ void testWater() {
system.addConstraint(startIndex, startIndex+3, 0.09572);
system.addConstraint(startIndex+2, startIndex+3, 0.15139);
system.setVirtualSite(startIndex+4, new ThreeParticleAverageSite(startIndex, startIndex+2, startIndex+3, 0.786646558, 0.106676721, 0.106676721));
drude->addParticle(startIndex+1, startIndex, -1, -1, -1, -1.71636, 1.71636*1.71636/(100000*4.184), 1, 1);
drude->addParticle(startIndex+1, startIndex, -1, -1, -1, -1.71636, ONE_4PI_EPS0*1.71636*1.71636/(100000*4.184), 1, 1);
}
vector<Vec3> positions;
for (int i = 0; i < gridSize; i++)
......
......@@ -6,7 +6,7 @@
* Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org. *
* *
* Portions copyright (c) 2011-2013 Stanford University and the Authors. *
* Portions copyright (c) 2011-2014 Stanford University and the Authors. *
* Authors: Peter Eastman *
* Contributors: *
* *
......@@ -136,9 +136,9 @@ double ReferenceCalcDrudeForceKernel::execute(ContextImpl& context, bool include
RealOpenMM a1 = (p2 == -1 ? 1 : aniso12[i]);
RealOpenMM a2 = (p3 == -1 || p4 == -1 ? 1 : aniso34[i]);
RealOpenMM a3 = 3-a1-a2;
RealOpenMM k3 = charge[i]*charge[i]/(polarizability[i]*a3);
RealOpenMM k1 = charge[i]*charge[i]/(polarizability[i]*a1) - k3;
RealOpenMM k2 = charge[i]*charge[i]/(polarizability[i]*a2) - k3;
RealOpenMM k3 = ONE_4PI_EPS0*charge[i]*charge[i]/(polarizability[i]*a3);
RealOpenMM k1 = ONE_4PI_EPS0*charge[i]*charge[i]/(polarizability[i]*a1) - k3;
RealOpenMM k2 = ONE_4PI_EPS0*charge[i]*charge[i]/(polarizability[i]*a2) - k3;
// Compute the isotropic force.
......@@ -188,6 +188,7 @@ double ReferenceCalcDrudeForceKernel::execute(ContextImpl& context, bool include
int dipole2 = pair2[i];
int dipole1Particles[] = {particle[dipole1], particle1[dipole1]};
int dipole2Particles[] = {particle[dipole2], particle1[dipole2]};
RealOpenMM uscale = pairThole[i]/pow(polarizability[dipole1]*polarizability[dipole2], 1.0/6.0);
for (int j = 0; j < 2; j++)
for (int k = 0; k < 2; k++) {
int p1 = dipole1Particles[j];
......@@ -195,10 +196,10 @@ double ReferenceCalcDrudeForceKernel::execute(ContextImpl& context, bool include
RealOpenMM chargeProduct = charge[dipole1]*charge[dipole2]*(j == k ? 1 : -1);
RealVec delta = pos[p1]-pos[p2];
RealOpenMM r = sqrt(delta.dot(delta));
RealOpenMM u = r*pairThole[i]/pow(polarizability[dipole1]*polarizability[dipole2], 1.0/6.0);
RealOpenMM u = r*uscale;
RealOpenMM screening = 1.0 - (1.0+0.5*u)*exp(-u);
energy += ONE_4PI_EPS0*chargeProduct*screening/r;
RealVec f = delta*(ONE_4PI_EPS0*chargeProduct*screening/(r*r*r));
RealVec f = delta*(ONE_4PI_EPS0*chargeProduct/(r*r))*(screening/r-0.5*(1+u)*exp(-u)*uscale);
force[p1] += f;
force[p2] -= f;
}
......@@ -461,4 +462,4 @@ void ReferenceIntegrateDrudeSCFStepKernel::minimize(ContextImpl& context, double
lbfgsfloatval_t fx;
MinimizerData data(context, drudeParticles);
lbfgs(numDrudeParticles*3, minimizerPos, &fx, evaluate, NULL, &data, &minimizerParams);
}
\ No newline at end of file
}
......@@ -71,14 +71,14 @@ void validateForce(System& system, vector<Vec3>& positions, double expectedEnerg
offsetPos[i][j] = positions[i][j]+offset;
context.setPositions(offsetPos);
double e2 = context.getState(State::Energy | State::Forces).getPotentialEnergy();
ASSERT_EQUAL_TOL(state.getForces()[i][j], (e1-e2)/(2*offset), 1e-3);
ASSERT_EQUAL_TOL(state.getForces()[i][j], (e1-e2)/(2*offset), 1e-5);
}
}
void testSingleParticle() {
const double k = 1.5;
const double k = ONE_4PI_EPS0*1.5;
const double charge = 0.1;
const double alpha = charge*charge/k;
const double alpha = ONE_4PI_EPS0*charge*charge/k;
System system;
system.addParticle(1.0);
system.addParticle(1.0);
......@@ -92,9 +92,9 @@ void testSingleParticle() {
}
void testAnisotropicParticle() {
const double k = 1.5;
const double k = ONE_4PI_EPS0*1.5;
const double charge = 0.1;
const double alpha = charge*charge/k;
const double alpha = ONE_4PI_EPS0*charge*charge/k;
const double a1 = 0.8;
const double a2 = 1.1;
const double k1 = k/a1;
......@@ -124,9 +124,9 @@ double computeScreening(double r, double thole, double alpha1, double alpha2) {
}
void testThole() {
const double k = 1.5;
const double k = ONE_4PI_EPS0*1.5;
const double charge = 0.1;
const double alpha = charge*charge/k;
const double alpha = ONE_4PI_EPS0*charge*charge/k;
const double thole = 2.5;
System system;
system.addParticle(1.0);
......@@ -157,9 +157,9 @@ void testThole() {
}
void testChangingParameters() {
const double k = 1.5;
const double k = ONE_4PI_EPS0*1.5;
const double charge = 0.1;
const double alpha = charge*charge/k;
const double alpha = ONE_4PI_EPS0*charge*charge/k;
Platform& platform = Platform::getPlatformByName("Reference");
// Create the system.
......@@ -184,9 +184,9 @@ void testChangingParameters() {
// Modify the parameters.
const double k2 = 2.2;
const double k2 = ONE_4PI_EPS0*2.2;
const double charge2 = 0.3;
const double alpha2 = charge2*charge2/k2;
const double alpha2 = ONE_4PI_EPS0*charge2*charge2/k2;
drude->setParticleParameters(0, 1, 0, -1, -1, -1, charge2, alpha2, 1, 1);
drude->updateParametersInContext(context);
state = context.getState(State::Energy);
......
......@@ -53,9 +53,9 @@ extern "C" OPENMM_EXPORT void registerDrudeReferenceKernelFactories();
void testSinglePair() {
const double temperature = 300.0;
const double temperatureDrude = 10.0;
const double k = 1.5;
const double k = ONE_4PI_EPS0*1.5;
const double charge = 0.1;
const double alpha = charge*charge/k;
const double alpha = ONE_4PI_EPS0*charge*charge/k;
const double mass1 = 1.0;
const double mass2 = 0.1;
const double totalMass = mass1+mass2;
......@@ -132,7 +132,7 @@ void testWater() {
system.addConstraint(startIndex, startIndex+3, 0.09572);
system.addConstraint(startIndex+2, startIndex+3, 0.15139);
system.setVirtualSite(startIndex+4, new ThreeParticleAverageSite(startIndex, startIndex+2, startIndex+3, 0.786646558, 0.106676721, 0.106676721));
drude->addParticle(startIndex+1, startIndex, -1, -1, -1, -1.71636, 1.71636*1.71636/(100000*4.184), 1, 1);
drude->addParticle(startIndex+1, startIndex, -1, -1, -1, -1.71636, ONE_4PI_EPS0*1.71636*1.71636/(100000*4.184), 1, 1);
}
vector<Vec3> positions;
for (int i = 0; i < gridSize; i++)
......
......@@ -85,7 +85,7 @@ void testWater() {
system.addConstraint(startIndex, startIndex+3, 0.09572);
system.addConstraint(startIndex+2, startIndex+3, 0.15139);
system.setVirtualSite(startIndex+4, new ThreeParticleAverageSite(startIndex, startIndex+2, startIndex+3, 0.786646558, 0.106676721, 0.106676721));
drude->addParticle(startIndex+1, startIndex, -1, -1, -1, -1.71636, 1.71636*1.71636/(100000*4.184), 1, 1);
drude->addParticle(startIndex+1, startIndex, -1, -1, -1, -1.71636, ONE_4PI_EPS0*1.71636*1.71636/(100000*4.184), 1, 1);
}
vector<Vec3> positions;
for (int i = 0; i < gridSize; i++)
......
......@@ -55,12 +55,16 @@ foreach(SUBDIR ${SUBDIRS})
"${CMAKE_CURRENT_SOURCE_DIR}/${SUBDIR}/*.xml"
"${CMAKE_CURRENT_SOURCE_DIR}/${SUBDIR}/*.pdb"
"${CMAKE_CURRENT_SOURCE_DIR}/${SUBDIR}/*.prmtop"
"${CMAKE_CURRENT_SOURCE_DIR}/${SUBDIR}/*.prm"
"${CMAKE_CURRENT_SOURCE_DIR}/${SUBDIR}/*.inpcrd"
"${CMAKE_CURRENT_SOURCE_DIR}/${SUBDIR}/*.crd"
"${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}/*.top"
"${CMAKE_CURRENT_SOURCE_DIR}/${SUBDIR}/*.par"
"${CMAKE_CURRENT_SOURCE_DIR}/${SUBDIR}/*.str"
"${CMAKE_CURRENT_SOURCE_DIR}/${SUBDIR}/*psf"
"${CMAKE_CURRENT_SOURCE_DIR}/${SUBDIR}/charmm22.*"
)
......
......@@ -28,7 +28,7 @@ from desmonddmsfile import DesmondDMSFile
from checkpointreporter import CheckpointReporter
from charmmcrdfiles import CharmmCrdFile, CharmmRstFile
from charmmparameterset import CharmmParameterSet
from charmmpsffile import CharmmPsfFile
from charmmpsffile import CharmmPsfFile, CharmmPSFWarning
# Enumerated values
......
......@@ -13,7 +13,7 @@ Copyright (c) 2014 the Authors
Author: Jason M. Swails
Contributors:
Date: April 18, 2014
Date: July 17, 2014
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
......@@ -123,6 +123,8 @@ class CharmmParameterSet(object):
elif arg.endswith('.str'):
strs.append(arg)
elif arg.endswith('.inp'):
# Only consider the file name (since the directory is likely
# "toppar" and will screw up file type detection)
fname = os.path.split(arg)[1]
if 'par' in fname:
pars.append(arg)
......@@ -436,11 +438,29 @@ class CharmmParameterSet(object):
try:
at1 = words[0]
at2 = words[1]
emin = conv(words[2], float, 'NBFIX Emin')
emin = abs(conv(words[2], float, 'NBFIX Emin'))
rmin = conv(words[3], float, 'NBFIX Rmin')
try:
emin14 = abs(conv(words[4], float, 'NBFIX Emin 1-4'))
rmin14 = conv(words[5], float, 'NBFIX Rmin 1-4')
except IndexError:
emin14 = rmin14 = None
try:
self.atom_types_str[at1].add_nbfix(at2, rmin, emin,
rmin14, emin14)
self.atom_types_str[at2].add_nbfix(at1, rmin, emin,
rmin14, emin14)
except KeyError:
# Some stream files define NBFIX terms with an atom that
# is defined in another toppar file that does not
# necessarily have to be loaded. As a result, not every
# NBFIX found here will necessarily need to be applied.
# If we can't find a particular atom type, don't bother
# adding that nbfix and press on
pass
except IndexError:
raise CharmmFileError('Could not parse NBFIX terms.')
self.nbfix_types[(min(at1, at2), max(at1, at2))] = (emin, rmin)
self.nbfix_types[(min(at1, at2), max(at1, at2))] = (rmin, emin)
# Now we're done. Load the nonbonded types into the relevant AtomType
# instances. In order for this to work, all keys in nonbonded_types
# must be in the self.atom_types_str dict. Raise a RuntimeError if this
......
......@@ -995,11 +995,25 @@ class CharmmPsfFile(object):
u.kilojoule_per_mole)
ene_conv = dihe_frc_conv
# Create the system
# Create the system and determine if any of our atoms have NBFIX (and
# therefore requires a CustomNonbondedForce instead)
typenames = set()
system = mm.System()
if verbose: print('Adding particles...')
for atom in self.atom_list:
typenames.add(atom.type.name)
system.addParticle(atom.mass)
has_nbfix_terms = False
typenames = list(typenames)
try:
for i, typename in enumerate(typenames):
typ = params.atom_types_str[typename]
for j in range(i, len(typenames)):
if typenames[j] in typ.nbfix:
has_nbfix_terms = True
raise StopIteration
except StopIteration:
pass
# Set up the constraints
if verbose and (constraints is not None and not rigidWater):
print('Adding constraints...')
......@@ -1240,9 +1254,85 @@ class CharmmPsfFile(object):
# Add per-particle nonbonded parameters (LJ params)
sigma_scale = 2**(-1/6) * 2
for i, atm in enumerate(self.atom_list):
force.addParticle(atm.charge, sigma_scale*atm.type.rmin*length_conv,
abs(atm.type.epsilon*ene_conv))
if not has_nbfix_terms:
for atm in self.atom_list:
force.addParticle(atm.charge, sigma_scale*atm.type.rmin*length_conv,
abs(atm.type.epsilon*ene_conv))
else:
for atm in self.atom_list:
force.addParticle(atm.charge, 1.0, 0.0)
# Now add the custom nonbonded force that implements NBFIX. First
# thing we need to do is condense our number of types
lj_idx_list = [0 for atom in self.atom_list]
lj_radii, lj_depths = [], []
num_lj_types = 0
lj_type_list = []
for i, atom in enumerate(self.atom_list):
atom = atom.type
if lj_idx_list[i]: continue # already assigned
num_lj_types += 1
lj_idx_list[i] = num_lj_types
ljtype = (atom.rmin, atom.epsilon)
lj_type_list.append(atom)
lj_radii.append(atom.rmin)
lj_depths.append(atom.epsilon)
for j in range(i+1, len(self.atom_list)):
atom2 = self.atom_list[j].type
if lj_idx_list[j] > 0: continue # already assigned
if atom2 is atom:
lj_idx_list[j] = num_lj_types
elif not atom.nbfix:
# Only non-NBFIXed atom types can be compressed
ljtype2 = (atom2.rmin, atom2.epsilon)
if ljtype == ljtype2:
lj_idx_list[j] = num_lj_types
# Now everything is assigned. Create the A-coefficient and
# B-coefficient arrays
acoef = [0 for i in range(num_lj_types*num_lj_types)]
bcoef = acoef[:]
for i in range(num_lj_types):
for j in range(num_lj_types):
namej = lj_type_list[j].name
try:
rij, wdij, rij14, wdij14 = lj_type_list[i].nbfix[namej]
except KeyError:
rij = (lj_radii[i] + lj_radii[j]) * length_conv
wdij = sqrt(lj_depths[i] * lj_depths[j]) * ene_conv
else:
rij *= length_conv
wdij *= ene_conv
acoef[i+num_lj_types*j] = sqrt(wdij) * rij**6
bcoef[i+num_lj_types*j] = 2 * wdij * rij**6
cforce = mm.CustomNonbondedForce('(a/r6)^2-b/r6; r6=r^6;'
'a=acoef(type1, type2);'
'b=bcoef(type1, type2)')
cforce.addTabulatedFunction('acoef',
mm.Discrete2DFunction(num_lj_types, num_lj_types, acoef))
cforce.addTabulatedFunction('bcoef',
mm.Discrete2DFunction(num_lj_types, num_lj_types, bcoef))
cforce.addPerParticleParameter('type')
cforce.setForceGroup(self.NONBONDED_FORCE_GROUP)
if (nonbondedMethod is ff.PME or nonbondedMethod is ff.Ewald or
nonbondedMethod is ff.CutoffPeriodic):
cforce.setNonbondedMethod(cforce.CutoffPeriodic)
cforce.setCutoffDistance(nonbondedCutoff)
cforce.setUseLongRangeCorrection(True)
elif nonbondedMethod is ff.NoCutoff:
cforce.setNonbondedMethod(cforce.NoCutoff)
elif nonbondedMethod is ff.CutoffNonPeriodic:
cforce.setNonbondedMethod(cforce.CutoffNonPeriodic)
cforce.setCutoffDistance(nonbondedCutoff)
else:
raise ValueError('Unrecognized nonbonded method')
if switchDistance and nonbondedMethod is not ff.NoCutoff:
# make sure it's legal
if switchDistance >= nonbondedCutoff:
raise ValueError('switchDistance is too large compared '
'to the cutoff!')
cforce.setUseSwitchingFunction(True)
cforce.setSwitchingDistance(switchDistance)
for i in lj_idx_list:
cforce.addParticle((i - 1,)) # adjust for indexing from 0
# Add 1-4 interactions
excluded_atom_pairs = set() # save these pairs so we don't zero them out
......@@ -1283,6 +1373,13 @@ class CharmmPsfFile(object):
continue
force.addException(atom.idx, atom2.idx, 0.0, 0.1, 0.0)
system.addForce(force)
# If we needed a CustomNonbondedForce, map all of the exceptions from
# the NonbondedForce to the CustomNonbondedForce
if has_nbfix_terms:
for i in range(force.getNumExceptions()):
ii, jj, q, eps, sig = force.getExceptionParameters(i)
cforce.addExclusion(ii, jj)
system.addForce(cforce)
# Add GB model if we're doing one
if implicitSolvent is not None:
......
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -30,7 +30,7 @@
<Atom type="swm4ndp-OD" charge="-1.71636" sigma="1" epsilon="0"/>
</NonbondedForce>
<DrudeForce>
<Particle type1="swm4ndp-OD" type2="swm4ndp-O" charge="-1.71636" polarizability="7.040850e-6" thole="1.3"/>
<Particle type1="swm4ndp-OD" type2="swm4ndp-O" charge="-1.71636" polarizability="0.000978253" thole="1.3"/>
</DrudeForce>
</ForceField>
......@@ -100,7 +100,7 @@ class ForceField(object):
"""Load one or more XML files and create a ForceField object based on them.
Parameters:
- files A list of XML files defining the force field. Each entry may
- files (list) A list of XML files defining the force field. Each entry may
be an absolute file path, a path relative to the current working
directory, a path relative to this module's data subdirectory
(for built in force fields), or an open file-like object with a
......@@ -113,47 +113,59 @@ class ForceField(object):
self._forces = []
self._scripts = []
for file in files:
try:
# this handles either filenames or open file-like objects
tree = etree.parse(file)
except IOError:
tree = etree.parse(os.path.join(os.path.dirname(__file__), 'data', file))
root = tree.getroot()
# Load the atom types.
if tree.getroot().find('AtomTypes') is not None:
for type in tree.getroot().find('AtomTypes').findall('Type'):
self.registerAtomType(type.attrib)
# Load the residue templates.
if tree.getroot().find('Residues') is not None:
for residue in root.find('Residues').findall('Residue'):
resName = residue.attrib['name']
template = ForceField._TemplateData(resName)
for atom in residue.findall('Atom'):
template.atoms.append(ForceField._TemplateAtomData(atom.attrib['name'], atom.attrib['type'], self._atomTypes[atom.attrib['type']][2]))
for site in residue.findall('VirtualSite'):
template.virtualSites.append(ForceField._VirtualSiteData(site))
for bond in residue.findall('Bond'):
template.addBond(int(bond.attrib['from']), int(bond.attrib['to']))
for bond in residue.findall('ExternalBond'):
b = int(bond.attrib['from'])
template.externalBonds.append(b)
template.atoms[b].externalBonds += 1
self.registerResidueTemplate(template)
# Load force definitions
for child in root:
if child.tag in parsers:
parsers[child.tag](child, self)
# Load scripts
for node in tree.getroot().findall('Script'):
self.registerScript(node.text)
self.loadFile(file)
def loadFile(self, file):
"""Load an XML file and add the definitions from it to this FieldField.
Parameters:
- file (string or file) An XML file containing force field definitions. It may
be either an absolute file path, a path relative to the current working
directory, a path relative to this module's data subdirectory
(for built in force fields), or an open file-like object with a
read() method from which the forcefield XML data can be loaded.
"""
try:
# this handles either filenames or open file-like objects
tree = etree.parse(file)
except IOError:
tree = etree.parse(os.path.join(os.path.dirname(__file__), 'data', file))
root = tree.getroot()
# Load the atom types.
if tree.getroot().find('AtomTypes') is not None:
for type in tree.getroot().find('AtomTypes').findall('Type'):
self.registerAtomType(type.attrib)
# Load the residue templates.
if tree.getroot().find('Residues') is not None:
for residue in root.find('Residues').findall('Residue'):
resName = residue.attrib['name']
template = ForceField._TemplateData(resName)
for atom in residue.findall('Atom'):
template.atoms.append(ForceField._TemplateAtomData(atom.attrib['name'], atom.attrib['type'], self._atomTypes[atom.attrib['type']][2]))
for site in residue.findall('VirtualSite'):
template.virtualSites.append(ForceField._VirtualSiteData(site))
for bond in residue.findall('Bond'):
template.addBond(int(bond.attrib['from']), int(bond.attrib['to']))
for bond in residue.findall('ExternalBond'):
b = int(bond.attrib['from'])
template.externalBonds.append(b)
template.atoms[b].externalBonds += 1
self.registerResidueTemplate(template)
# Load force definitions
for child in root:
if child.tag in parsers:
parsers[child.tag](child, self)
# Load scripts
for node in tree.getroot().findall('Script'):
self.registerScript(node.text)
def getGenerators(self):
"""Get the list of all registered generators."""
......@@ -240,6 +252,7 @@ class ForceField(object):
def __init__(self):
self.atomType = {}
self.atoms = []
self.excludeAtomWith = []
self.virtualSites = {}
self.bonds = []
self.angles = []
......@@ -302,6 +315,10 @@ class ForceField(object):
self.localPos = [float(attrib['p1']), float(attrib['p2']), float(attrib['p3'])]
else:
raise ValueError('Unknown virtual site type: %s' % self.type)
if 'excludeWith' in attrib:
self.excludeWith = int(attrib['excludeWith'])
else:
self.excludeWith = self.atoms[0]
def createSystem(self, topology, nonbondedMethod=NoCutoff, nonbondedCutoff=1.0*unit.nanometer,
constraints=None, rigidWater=True, removeCMMotion=True, hydrogenMass=None, **args):
......@@ -324,6 +341,8 @@ class ForceField(object):
"""
data = ForceField._SystemData()
data.atoms = list(topology.atoms())
for atom in data.atoms:
data.excludeAtomWith.append([])
# Make a list of all bonds
......@@ -363,7 +382,7 @@ class ForceField(object):
data.atomType[atom] = template.atoms[match].type
for site in template.virtualSites:
if match == site.index:
data.virtualSites[atom] = (site, [matchAtoms[i].index for i in site.atoms])
data.virtualSites[atom] = (site, [matchAtoms[i].index for i in site.atoms], matchAtoms[site.excludeWith].index)
# Create the System and add atoms
......@@ -478,8 +497,9 @@ class ForceField(object):
# Add virtual sites
for atom in data.virtualSites:
(site, atoms) = data.virtualSites[atom]
(site, atoms, excludeWith) = data.virtualSites[atom]
index = atom.index
data.excludeAtomWith[excludeWith].append(index)
if site.type == 'average2':
sys.setVirtualSite(index, mm.TwoParticleAverageSite(atoms[0], atoms[1], site.weights[0], site.weights[1]))
elif site.type == 'average3':
......@@ -895,7 +915,7 @@ class PeriodicTorsionGenerator:
for (t2, t3, t4) in itertools.permutations(((type2, 1), (type3, 2), (type4, 3))):
if t2[0] in types2 and t3[0] in types3 and t4[0] in types4:
# Workaround to be more consistent with AMBER. It uses wildcards to define most of its
# impropers, which leaves the ordering ambigous. It then follows some bizarre rules
# impropers, which leaves the ordering ambiguous. It then follows some bizarre rules
# to pick the order.
a1 = torsion[t2[1]]
a2 = torsion[t3[1]]
......@@ -991,18 +1011,22 @@ class RBTorsionGenerator:
if type1 in types1:
for (t2, t3, t4) in itertools.permutations(((type2, 1), (type3, 2), (type4, 3))):
if t2[0] in types2 and t3[0] in types3 and t4[0] in types4:
# Workaround to be more consistent with AMBER. It uses wildcards to define most of its
# impropers, which leaves the ordering ambigous. It then follows some bizarre rules
# to pick the order.
a1 = torsion[t2[1]]
a2 = torsion[t3[1]]
e1 = data.atoms[a1].element
e2 = data.atoms[a2].element
if e1 == e2 and a1 > a2:
(a1, a2) = (a2, a1)
elif e1 != elem.carbon and (e2 == elem.carbon or e1.mass < e2.mass):
(a1, a2) = (a2, a1)
force.addTorsion(a1, a2, torsion[0], torsion[t4[1]], tordef.c[0], tordef.c[1], tordef.c[2], tordef.c[3], tordef.c[4], tordef.c[5])
if wildcard in (types1, types2, types3, types4):
# Workaround to be more consistent with AMBER. It uses wildcards to define most of its
# impropers, which leaves the ordering ambiguous. It then follows some bizarre rules
# to pick the order.
a1 = torsion[t2[1]]
a2 = torsion[t3[1]]
e1 = data.atoms[a1].element
e2 = data.atoms[a2].element
if e1 == e2 and a1 > a2:
(a1, a2) = (a2, a1)
elif e1 != elem.carbon and (e2 == elem.carbon or e1.mass < e2.mass):
(a1, a2) = (a2, a1)
force.addTorsion(a1, a2, torsion[0], torsion[t4[1]], tordef.c[0], tordef.c[1], tordef.c[2], tordef.c[3], tordef.c[4], tordef.c[5])
else:
# There are no wildcards, so the order is unambiguous.
force.addTorsion(torsion[0], torsion[t2[1]], torsion[t3[1]], torsion[t4[1]], tordef.c[0], tordef.c[1], tordef.c[2], tordef.c[3], tordef.c[4], tordef.c[5])
done = True
break
......@@ -1157,33 +1181,33 @@ class NonbondedGenerator:
sys.addForce(force)
def postprocessSystem(self, sys, data, args):
# Create exceptions based on bonds, virtual sites, and Drude particles.
# Create exceptions based on bonds.
bondIndices = []
for bond in data.bonds:
bondIndices.append((bond.atom1, bond.atom2))
# If a virtual site does *not* share exclusions with another atom, add a bond between it and its first parent atom.
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))
(site, atoms, excludeWith) = data.virtualSites[data.atoms[i]]
if excludeWith is None:
bondIndices.append((i, site.getParticle(0)))
# Certain particles, such as lone pairs and Drude particles, share exclusions with a parent atom.
# If the parent atom does not interact with an atom, the child particle does not either.
for atom1, atom2 in bondIndices:
for child1 in data.excludeAtomWith[atom1]:
bondIndices.append((child1, atom2))
for child2 in data.excludeAtomWith[atom2]:
bondIndices.append((child1, child2))
for child2 in data.excludeAtomWith[atom2]:
bondIndices.append((atom1, child2))
# Create the exceptions.
nonbonded = [f for f in sys.getForces() if isinstance(f, mm.NonbondedForce)][0]
nonbonded.createExceptionsFromBonds(bondIndices, self.coulomb14scale, self.lj14scale)
......@@ -1519,18 +1543,22 @@ class CustomTorsionGenerator:
if type1 in types1:
for (t2, t3, t4) in itertools.permutations(((type2, 1), (type3, 2), (type4, 3))):
if t2[0] in types2 and t3[0] in types3 and t4[0] in types4:
# Workaround to be more consistent with AMBER. It uses wildcards to define most of its
# impropers, which leaves the ordering ambigous. It then follows some bizarre rules
# to pick the order.
a1 = torsion[t2[1]]
a2 = torsion[t3[1]]
e1 = data.atoms[a1].element
e2 = data.atoms[a2].element
if e1 == e2 and a1 > a2:
(a1, a2) = (a2, a1)
elif e1 != elem.carbon and (e2 == elem.carbon or e1.mass < e2.mass):
(a1, a2) = (a2, a1)
force.addTorsion(a1, a2, torsion[0], torsion[t4[1]], tordef.paramValues)
if wildcard in (types1, types2, types3, types4):
# Workaround to be more consistent with AMBER. It uses wildcards to define most of its
# impropers, which leaves the ordering ambiguous. It then follows some bizarre rules
# to pick the order.
a1 = torsion[t2[1]]
a2 = torsion[t3[1]]
e1 = data.atoms[a1].element
e2 = data.atoms[a2].element
if e1 == e2 and a1 > a2:
(a1, a2) = (a2, a1)
elif e1 != elem.carbon and (e2 == elem.carbon or e1.mass < e2.mass):
(a1, a2) = (a2, a1)
force.addTorsion(a1, a2, torsion[0], torsion[t4[1]], tordef.paramValues)
else:
# There are no wildcards, so the order is unambiguous.
force.addTorsion(torsion[0], torsion[t2[1]], torsion[t3[1]], torsion[t4[1]], tordef.paramValues)
done = True
break
......@@ -4290,8 +4318,6 @@ class DrudeGenerator:
# Add Drude particles.
drudeMap = {}
parentMap = {}
for atom in data.atoms:
t = data.atomType[atom]
if t in self.typeMap:
......@@ -4308,9 +4334,8 @@ class DrudeGenerator:
p[2] = atom2.index
elif values[3] is not None and type2 in values[3]:
p[3] = atom2.index
drudeIndex = force.addParticle(atom.index, p[0], p[1], p[2], p[3], values[4], values[5], values[6], values[7])
drudeMap[atom.index] = p[0]
parentMap[p[0]] = (atom.index, drudeIndex)
force.addParticle(atom.index, p[0], p[1], p[2], p[3], values[4], values[5], values[6], values[7])
data.excludeAtomWith[p[0]].append(atom.index)
sys.addForce(force)
def postprocessSystem(self, sys, data, args):
......@@ -4323,14 +4348,14 @@ class DrudeGenerator:
particleMap[drude.getParticleParameters(i)[0]] = i
for i in range(nonbonded.getNumExceptions()):
(particle1, particle2, charge, sigma, epsilon) = nonbonded.getExceptionParameters(i)
if charge == 0 and epsilon == 0:
if charge._value == 0 and epsilon._value == 0:
# This is an exclusion.
if particle1 in particleMap and particle2 in particleMap:
# It connects two Drude particles, so add a screened pair.
drude1 = particleMap[particle1]
drude2 = particleMap[particle2]
type1 = data.atomType[data.atoms[drude1]]
type2 = data.atomType[data.atoms[drude2]]
type1 = data.atomType[data.atoms[particle1]]
type2 = data.atomType[data.atoms[particle2]]
thole1 = self.typeMap[type1][8]
thole2 = self.typeMap[type2][8]
drude.addScreenedPair(drude1, drude2, thole1+thole2)
......
......@@ -52,6 +52,7 @@ except:
import simtk.unit as units
import simtk.openmm
from simtk.openmm.app import element as elem
from simtk.openmm.vec3 import Vec3
import customgbforces as customgb
#=============================================================================================
......@@ -116,6 +117,7 @@ class PrmtopLoader(object):
self._flags=[]
self._raw_format={}
self._raw_data={}
self._has_nbfix_terms = False
fIn=open(inFilename)
for line in fIn:
......@@ -218,28 +220,16 @@ class PrmtopLoader(object):
try:
return self._massList
except AttributeError:
pass
self._massList=[]
raw_masses=self._raw_data['MASS']
for ii in range(self.getNumAtoms()):
self._massList.append(float(raw_masses[ii]))
self._massList = self._massList
return self._massList
self._massList = [float(x) for x in self._raw_data['MASS']]
return self._massList
def getCharges(self):
"""Return a list of atomic charges in the system"""
try:
return self._chargeList
except AttributeError:
pass
self._chargeList=[]
raw_charges=self._raw_data['CHARGE']
for ii in range(self.getNumAtoms()):
self._chargeList.append(float(raw_charges[ii])/18.2223)
self._chargeList = self._chargeList
return self._chargeList
self._chargeList = [float(x)/18.2223 for x in self._raw_data['CHARGE']]
return self._chargeList
def getAtomName(self, iAtom):
"""Return the atom name for iAtom"""
......@@ -254,11 +244,8 @@ class PrmtopLoader(object):
try:
return self._atomTypeIndexes
except AttributeError:
pass
self._atomTypeIndexes=[]
for atomTypeIndex in self._raw_data['ATOM_TYPE_INDEX']:
self._atomTypeIndexes.append(int(atomTypeIndex))
return self._atomTypeIndexes
self._atomTypeIndexes = [int(x) for x in self._raw_data['ATOM_TYPE_INDEX']]
return self._atomTypeIndexes
def getAtomType(self, iAtom):
"""Return the AMBER atom type for iAtom"""
......@@ -275,11 +262,11 @@ class PrmtopLoader(object):
def getResidueLabel(self, iAtom=None, iRes=None):
"""Return residue label for iAtom OR iRes"""
if iRes==None and iAtom==None:
if iRes is None and iAtom is None:
raise Exception("only specify iRes or iAtom, not both")
if iRes!=None and iAtom!=None:
if iRes is not None and iAtom is not None:
raise Exception("iRes or iAtom must be set")
if iRes!=None:
if iRes is not None:
return self._raw_data['RESIDUE_LABEL'][iRes]
else:
return self.getResidueLabel(iRes=self._getResiduePointer(iAtom))
......@@ -306,6 +293,9 @@ class PrmtopLoader(object):
elements of the Lennard-Jones A and B coefficient matrices are found,
NbfixPresent exception is raised
"""
if self._has_nbfix_terms:
raise NbfixPresent('Off-diagonal Lennard-Jones elements found. '
'Cannot determine LJ parameters for individual atoms.')
try:
return self._nonbondTerms
except AttributeError:
......@@ -344,13 +334,13 @@ class PrmtopLoader(object):
b = float(self._raw_data['LENNARD_JONES_BCOEF'][index])
if a == 0 or b == 0:
if a != 0 or b != 0 or (wdij != 0 and rij != 0):
del self._nonbondTerms
self._has_nbfix_terms = True
raise NbfixPresent('Off-diagonal Lennard-Jones elements'
' found. Cannot determine LJ '
'parameters for individual atoms.')
elif (abs((a - (wdij * rij ** 12)) / a) > 1e-6 or
abs((b - (2 * wdij * rij**6)) / b) > 1e-6):
del self._nonbondTerms
self._has_nbfix_terms = True
raise NbfixPresent('Off-diagonal Lennard-Jones elements '
'found. Cannot determine LJ parameters '
'for individual atoms.')
......@@ -712,6 +702,10 @@ def readAmberSystem(prmtop_filename=None, prmtop_loader=None, shake=None, gbmode
warnings.warn("1-4 scaling parameters in topology file are being ignored. "
"This is not recommended unless you know what you are doing.")
has_1264 = 'LENNARD_JONES_CCOEF' in prmtop._raw_data.keys()
if has_1264:
parm_ccoef = [float(x) for x in prmtop._raw_data['LENNARD_JONES_CCOEF']]
# Use pyopenmm implementation of OpenMM by default.
if mm is None:
mm = simtk.openmm
......@@ -875,31 +869,52 @@ def readAmberSystem(prmtop_filename=None, prmtop_loader=None, shake=None, gbmode
idx = nbidx[numTypes*i+j] - 1
acoef[i+numTypes*j] = sqrt(parm_acoef[idx]) * afac
bcoef[i+numTypes*j] = parm_bcoef[idx] * bfac
cforce = mm.CustomNonbondedForce('(a/r6)^2-b/r6; r6=r^6;'
'a=acoef(type1, type2);'
'b=bcoef(type1, type2);')
if has_1264:
cfac = ene_conv * length_conv**4
ccoef = [0 for i in range(numTypes*numTypes)]
for i in range(numTypes):
for j in range(numTypes):
idx = nbidx[numTypes*i+j] - 1
ccoef[i+numTypes*j] = parm_ccoef[idx] * cfac
cforce = mm.CustomNonbondedForce('(a/r6)^2-b/r6-c/r^4; r6=r^6;'
'a=acoef(type1, type2);'
'b=bcoef(type1, type2);'
'c=ccoef(type1, type2);')
else:
cforce = mm.CustomNonbondedForce('(a/r6)^2-b/r6; r6=r^6;'
'a=acoef(type1, type2);'
'b=bcoef(type1, type2);')
cforce.addTabulatedFunction('acoef',
mm.Discrete2DFunction(numTypes, numTypes, acoef))
cforce.addTabulatedFunction('bcoef',
mm.Discrete2DFunction(numTypes, numTypes, bcoef))
if has_1264:
cforce.addTabulatedFunction('ccoef',
mm.Discrete2DFunction(numTypes, numTypes, ccoef))
cforce.addPerParticleParameter('type')
for atom in prmtop._getAtomTypeIndexes():
cforce.addParticle((atom-1,))
# Now set the various properties based on the NonbondedForce object
if nonbondedMethod in ('PME', 'Ewald', 'CutoffPeriodic'):
cforce.setNonbondedMethod(cforce.CutoffPeriodic)
cforce.setCutoffDistance(nonbondedCutoff)
elif nonbondedMethod == 'CutoffNonPeriodic':
cforce.setNonbondedMethod(cforce.CutoffNonPeriodic)
cforce.setCutoffDistance(nonbondedCutoff)
elif nonbondedMethod == 'NoCutoff':
cforce.setNonbondedMethod(cforce.NoCutoff)
else:
raise ValueError('Unrecognized cutoff option %s' % nonbondedMethod)
else:
for (charge, (rVdw, epsilon)) in zip(prmtop.getCharges(), nonbondTerms):
sigma = rVdw * sigmaScale
force.addParticle(charge, sigma, epsilon)
if has_1264:
numTypes = prmtop.getNumTypes()
nbidx = [int(x) for x in prmtop._raw_data['NONBONDED_PARM_INDEX']]
ccoef = [0 for i in range(numTypes*numTypes)]
ene_conv = units.kilocalories_per_mole.conversion_factor_to(units.kilojoules_per_mole)
length_conv = units.angstroms.conversion_factor_to(units.nanometers)
cfac = ene_conv * length_conv**4
for i in range(numTypes):
for j in range(numTypes):
idx = nbidx[numTypes*i+j] - 1
ccoef[i+numTypes*j] = parm_ccoef[idx] * cfac
cforce = mm.CustomNonbondedForce('-c/r^4; c=ccoef(type1, type2)')
cforce.addTabulatedFunction('ccoef',
mm.Discrete2DFunction(numTypes, numTypes, ccoef))
cforce.addPerParticleParameter('type')
for atom in prmtop._getAtomTypeIndexes():
cforce.addParticle((atom-1,))
# Add 1-4 Interactions
......@@ -925,10 +940,23 @@ def readAmberSystem(prmtop_filename=None, prmtop_loader=None, shake=None, gbmode
# Copy the exceptions as exclusions to the CustomNonbondedForce if we have
# NBFIX terms
if nbfix:
if nbfix or has_1264:
for i in range(force.getNumExceptions()):
ii, jj, chg, sig, eps = force.getExceptionParameters(i)
cforce.addExclusion(ii, jj)
# Now set the various properties based on the NonbondedForce object
if nonbondedMethod in ('PME', 'Ewald', 'CutoffPeriodic'):
cforce.setNonbondedMethod(cforce.CutoffPeriodic)
cforce.setCutoffDistance(nonbondedCutoff)
cforce.setUseLongRangeCorrection(True)
elif nonbondedMethod == 'CutoffNonPeriodic':
cforce.setNonbondedMethod(cforce.CutoffNonPeriodic)
cforce.setCutoffDistance(nonbondedCutoff)
elif nonbondedMethod == 'NoCutoff':
cforce.setNonbondedMethod(cforce.NoCutoff)
else:
raise ValueError('Unrecognized cutoff option %s' % nonbondedMethod)
# Add this force to the system
system.addForce(cforce)
system.addForce(force)
......@@ -1190,9 +1218,9 @@ class AmberAsciiRestart(object):
if hasbox:
boxVectors = np.zeros((3, 3), np.float32)
else:
coordinates = [[0.0, 0.0, 0.0] for i in range(self.natom)]
coordinates = [Vec3(0.0, 0.0, 0.0) for i in range(self.natom)]
if hasvels:
velocities = [[0.0, 0.0, 0.0] for i in range(self.natom)]
velocities = [Vec3(0.0, 0.0, 0.0) for i in range(self.natom)]
if hasbox:
boxVectors = [[0.0, 0.0, 0.0] for i in range(3)]
......@@ -1202,14 +1230,16 @@ class AmberAsciiRestart(object):
idx = 0
for i in range(startline, endline):
line = lines[i]
coordinates[idx][0] = float(line[ 0:12])
coordinates[idx][1] = float(line[12:24])
coordinates[idx][2] = float(line[24:36])
x = float(line[ 0:12])
y = float(line[12:24])
z = float(line[24:36])
coordinates[idx] = Vec3(x, y, z)
idx += 1
if idx < self.natom:
coordinates[idx][0] = float(line[36:48])
coordinates[idx][1] = float(line[48:60])
coordinates[idx][2] = float(line[60:72])
x = float(line[36:48])
y = float(line[48:60])
z = float(line[60:72])
coordinates[idx] = Vec3(x, y, z)
idx += 1
self.coordinates = units.Quantity(coordinates, units.angstroms)
startline = endline
......@@ -1219,14 +1249,16 @@ class AmberAsciiRestart(object):
idx = 0
for i in range(startline, endline):
line = lines[i]
velocities[idx][0] = float(line[ 0:12]) * VELSCALE
velocities[idx][1] = float(line[12:24]) * VELSCALE
velocities[idx][2] = float(line[24:36]) * VELSCALE
x = float(line[ 0:12]) * VELSCALE
y = float(line[12:24]) * VELSCALE
z = float(line[24:36]) * VELSCALE
velocities[idx] = Vec3(x, y, z)
idx += 1
if idx < self.natom:
velocities[idx][0] = float(line[36:48]) * VELSCALE
velocities[idx][1] = float(line[48:60]) * VELSCALE
velocities[idx][2] = float(line[60:72]) * VELSCALE
x = float(line[36:48]) * VELSCALE
y = float(line[48:60]) * VELSCALE
z = float(line[60:72]) * VELSCALE
velocities[idx] = Vec3(x, y, z)
idx += 1
startline = endline
self.velocities = units.Quantity(velocities,
......@@ -1241,7 +1273,7 @@ class AmberAsciiRestart(object):
lengths = tmp[:3]
angles = tmp[3:]
_box_vectors_from_lengths_angles(lengths, angles, boxVectors)
self.boxVectors = units.Quantity(boxVectors, units.angstroms)
self.boxVectors = [units.Quantity(Vec3(*x), units.angstrom) for x in boxVectors]
class AmberNetcdfRestart(object):
"""
......@@ -1324,11 +1356,11 @@ class AmberNetcdfRestart(object):
# They are already numpy -- convert to list if we don't want numpy
if not asNumpy:
self.coordinates = self.coordinates.tolist()
self.coordinates = [Vec3(*x) for x in self.coordinates]
if self.velocities is not None:
self.velocities = self.velocities.tolist()
self.velocities = [Vec3(*x) for x in self.velocities]
if self.boxVectors is not None:
self.boxVectors = self.boxVectors.tolist()
self.boxVectors = [Vec3(*x) for x in self.boxVectors]
# Now add the units
self.coordinates = units.Quantity(self.coordinates, units.angstroms)
......@@ -1336,7 +1368,7 @@ class AmberNetcdfRestart(object):
self.velocities = units.Quantity(self.velocities,
units.angstroms/units.picoseconds)
if self.boxVectors is not None:
self.boxVectors = units.Quantity(self.boxVectors, units.angstroms)
self.boxVectors = [units.Quantity(x, units.angstroms) for x in self.boxVectors]
self.time = units.Quantity(self.time, units.picosecond)
def _box_vectors_from_lengths_angles(lengths, angles, boxVectors):
......@@ -1426,6 +1458,7 @@ def readAmberCoordinates(filename, asNumpy=False):
try:
crdfile = AmberAsciiRestart(filename)
except TypeError:
raise
raise TypeError('Problem parsing %s as an ASCII Amber restart file'
% filename)
except (IndexError, ValueError):
......
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