"...ssh:/git@developer.sourcefind.cn:2222/tsoc/openmm.git" did not exist on "f5eea2199fa16bbda8f2e6d622328f1c87871f40"
Commit fbf193fe authored by peastman's avatar peastman Committed by GitHub
Browse files

Merge pull request #1778 from GrossfieldLab/improved-gromacs

Improved gromacs
parents 792a9c07 4dcdf1d6
...@@ -69,11 +69,14 @@ def _is_gro_coord(line): ...@@ -69,11 +69,14 @@ def _is_gro_coord(line):
@param[in] line The line to be tested @param[in] line The line to be tested
""" """
sline = line.split() # data lines are fixed field
if len(sline) == 6 or len(sline) == 9: fields = []
return all([_isint(sline[2]), _isfloat(sline[3]), _isfloat(sline[4]), _isfloat(sline[5])]) fields.append(line[16:20].strip()) # atom number
elif len(sline) == 5 or len(sline) == 8: fields.append(line[21:28].strip()) # x coord
return all([_isint(line[15:20]), _isfloat(sline[2]), _isfloat(sline[3]), _isfloat(sline[4])]) fields.append(line[29:36].strip()) # y coord
fields.append(line[37:44].strip()) # z coord
if (all([f != '' for f in fields])): # check for empty fields
return all([_isint(fields[0]), _isfloat(fields[1]), _isfloat(fields[2]), _isfloat(fields[3])])
else: else:
return 0 return 0
......
...@@ -258,11 +258,16 @@ class GromacsTopFile(object): ...@@ -258,11 +258,16 @@ class GromacsTopFile(object):
def _processDefaults(self, line): def _processDefaults(self, line):
"""Process the [ defaults ] line.""" """Process the [ defaults ] line."""
fields = line.split() fields = line.split()
if len(fields) < 4: if len(fields) < 5:
raise ValueError('Too few fields in [ defaults ] line: '+line) # fudgeLJ and fudgeQQ not specified, assumed 1.0 by default
if len(fields) == 3:
fields.append(1.0)
fields.append(1.0)
else:
raise ValueError('Too few fields in [ defaults ] line: '+line)
if fields[0] != '1': if fields[0] != '1':
raise ValueError('Unsupported nonbonded type: '+fields[0]) raise ValueError('Unsupported nonbonded type: '+fields[0])
if fields[1] != '2': if not (fields[1] == '2' or fields[1] == '1'):
raise ValueError('Unsupported combination rule: '+fields[1]) raise ValueError('Unsupported combination rule: '+fields[1])
if fields[2].lower() == 'no': if fields[2].lower() == 'no':
self._genpairs = False self._genpairs = False
...@@ -570,7 +575,7 @@ class GromacsTopFile(object): ...@@ -570,7 +575,7 @@ class GromacsTopFile(object):
The solvent dielectric constant to use in the implicit solvent The solvent dielectric constant to use in the implicit solvent
model. model.
ewaldErrorTolerance : float=0.0005 ewaldErrorTolerance : float=0.0005
The error tolerance to use if nonbondedMethod is Ewald, PME, or LJPME. The error tolerance to use if nonbondedMethod is Ewald, PME or LJPME.
removeCMMotion : boolean=True removeCMMotion : boolean=True
If true, a CMMotionRemover will be added to the System If true, a CMMotionRemover will be added to the System
hydrogenMass : mass=None hydrogenMass : mass=None
...@@ -593,6 +598,11 @@ class GromacsTopFile(object): ...@@ -593,6 +598,11 @@ class GromacsTopFile(object):
raise ValueError('Illegal nonbonded method for a non-periodic system') raise ValueError('Illegal nonbonded method for a non-periodic system')
nb = mm.NonbondedForce() nb = mm.NonbondedForce()
sys.addForce(nb) sys.addForce(nb)
if self._defaults[1] == '1':
lj = mm.CustomNonbondedForce('sqrt(A1*A2)/r^12-sqrt(C1*C2)/r^6')
lj.addPerParticleParameter('C')
lj.addPerParticleParameter('A')
sys.addForce(lj)
if implicitSolvent is OBC2: if implicitSolvent is OBC2:
gb = mm.GBSAOBCForce() gb = mm.GBSAOBCForce()
gb.setSoluteDielectric(soluteDielectric) gb.setSoluteDielectric(soluteDielectric)
...@@ -610,8 +620,10 @@ class GromacsTopFile(object): ...@@ -610,8 +620,10 @@ class GromacsTopFile(object):
mapIndices = {} mapIndices = {}
bondIndices = [] bondIndices = []
topologyAtoms = list(self.topology.atoms()) topologyAtoms = list(self.topology.atoms())
exceptions = [] exclusions = []
pairs = []
fudgeQQ = float(self._defaults[4]) fudgeQQ = float(self._defaults[4])
fudgeLJ = float(self._defaults[3])
# Build a lookup table to let us process dihedrals more quickly. # Build a lookup table to let us process dihedrals more quickly.
...@@ -748,7 +760,7 @@ class GromacsTopFile(object): ...@@ -748,7 +760,7 @@ class GromacsTopFile(object):
dihedralType = fields[4] dihedralType = fields[4]
reversedTypes = types[::-1]+(dihedralType,) reversedTypes = types[::-1]+(dihedralType,)
types = types+(dihedralType,) types = types+(dihedralType,)
if (dihedralType in ('1', '2', '4', '9') and len(fields) > 7) or (dihedralType == '3' and len(fields) > 10): if (dihedralType in ('1', '4', '9') and len(fields) > 7) or (dihedralType == '3' and len(fields) > 10) or (dihedralType == '2' and len(fields) > 6):
paramsList = [fields] paramsList = [fields]
else: else:
# Look for a matching dihedral type. # Look for a matching dihedral type.
...@@ -776,13 +788,16 @@ class GromacsTopFile(object): ...@@ -776,13 +788,16 @@ class GromacsTopFile(object):
elif dihedralType == '2': elif dihedralType == '2':
# Harmonic torsion # Harmonic torsion
k = float(params[6]) k = float(params[6])
phi0 = float(params[5])
if k != 0: if k != 0:
if harmonicTorsion is None: if harmonicTorsion is None:
harmonicTorsion = mm.CustomTorsionForce('0.5*k*(theta-theta0)^2') harmonicTorsion = mm.CustomTorsionForce('0.5*k*(thetap-theta0)^2; thetap = step(-(theta-theta0+pi))*2*pi+theta+step(theta-theta0-pi)*(-2*pi); pi = %.15g' % math.pi)
harmonicTorsion.addPerTorsionParameter('theta0') harmonicTorsion.addPerTorsionParameter('theta0')
harmonicTorsion.addPerTorsionParameter('k') harmonicTorsion.addPerTorsionParameter('k')
sys.addForce(harmonicTorsion) sys.addForce(harmonicTorsion)
harmonicTorsion.addTorsion(baseAtomIndex+atoms[0], baseAtomIndex+atoms[1], baseAtomIndex+atoms[2], baseAtomIndex+atoms[3], (float(params[5])*degToRad, k)) # map phi0 into correct space
phi0 = phi0 - 360 if phi0 > 180 else phi0
harmonicTorsion.addTorsion(baseAtomIndex+atoms[0], baseAtomIndex+atoms[1], baseAtomIndex+atoms[2], baseAtomIndex+atoms[3], (phi0*degToRad, k))
else: else:
# RB Torsion # RB Torsion
c = [float(x) for x in params[5:11]] c = [float(x) for x in params[5:11]]
...@@ -825,11 +840,18 @@ class GromacsTopFile(object): ...@@ -825,11 +840,18 @@ class GromacsTopFile(object):
for fields in moleculeType.atoms: for fields in moleculeType.atoms:
params = self._atomTypes[fields[1]] params = self._atomTypes[fields[1]]
if len(fields) > 6: if len(fields) > 6:
q = float(fields[6]) q = float(fields[6])
else: else:
q = float(params[4]) q = float(params[4])
nb.addParticle(q, float(params[6]), float(params[7]))
if self._defaults[1] == '1':
nb.addParticle(q, 1.0, 0.0)
lj.addParticle([float(params[6]), float(params[7])])
elif self._defaults[1] == '2':
nb.addParticle(q, float(params[6]), float(params[7]))
if implicitSolvent is OBC2: if implicitSolvent is OBC2:
if fields[1] not in self._implicitTypes: if fields[1] not in self._implicitTypes:
raise ValueError('No implicit solvent parameters specified for atom type: '+fields[1]) raise ValueError('No implicit solvent parameters specified for atom type: '+fields[1])
...@@ -857,19 +879,37 @@ class GromacsTopFile(object): ...@@ -857,19 +879,37 @@ class GromacsTopFile(object):
continue # We'll use the automatically generated parameters continue # We'll use the automatically generated parameters
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])) pairs.append((baseAtomIndex+atoms[0], baseAtomIndex+atoms[1], atom1params[0]*atom2params[0]*fudgeQQ, params[0], params[1]))
for fields in moleculeType.exclusions: for fields in moleculeType.exclusions:
atoms = [int(x)-1 for x in fields] atoms = [int(x)-1 for x in fields]
for atom in atoms[1:]: for atom in atoms[1:]:
if atom > atoms[0]: if atom > atoms[0]:
exceptions.append((baseAtomIndex+atoms[0], baseAtomIndex+atom, 0, 0, 0)) exclusions.append((baseAtomIndex+atoms[0], baseAtomIndex+atom))
# Create nonbonded exceptions. # Create nonbonded exceptions.
nb.createExceptionsFromBonds(bondIndices, fudgeQQ, float(self._defaults[3])) nb.createExceptionsFromBonds(bondIndices, fudgeQQ, fudgeLJ)
for exception in exceptions: for exclusion in exclusions:
nb.addException(exception[0], exception[1], exception[2], float(exception[3]), float(exception[4]), True) nb.addException(exclusion[0], exclusion[1], 0.0, 1.0, 0.0, True)
if self._defaults[1] == '1':
# We're using a CustomNonbondedForce for LJ interactions, so also create a CustomBondForce
# to handle the exceptions.
pair_bond = mm.CustomBondForce('138.935456*q/r-C/r^6+A/r^12')
pair_bond.addPerBondParameter('q')
pair_bond.addPerBondParameter('C')
pair_bond.addPerBondParameter('A')
sys.addForce(pair_bond)
lj.createExclusionsFromBonds(bondIndices, 3)
for pair in pairs:
nb.addException(pair[0], pair[1], pair[2], 1.0, 0.0, True)
pair_bond.addBond(pair[0], pair[1], [pair[2],float(pair[3]), float(pair[4])])
for exclusion in exclusions:
lj.addExclusion(exclusion[0], exclusion[1])
elif self._defaults[1] == '2':
for pair in pairs:
nb.addException(pair[0], pair[1], pair[2], float(pair[3]), float(pair[4]), True)
# Finish configuring the NonbondedForce. # Finish configuring the NonbondedForce.
...@@ -882,6 +922,15 @@ class GromacsTopFile(object): ...@@ -882,6 +922,15 @@ class GromacsTopFile(object):
nb.setNonbondedMethod(methodMap[nonbondedMethod]) nb.setNonbondedMethod(methodMap[nonbondedMethod])
nb.setCutoffDistance(nonbondedCutoff) nb.setCutoffDistance(nonbondedCutoff)
nb.setEwaldErrorTolerance(ewaldErrorTolerance) nb.setEwaldErrorTolerance(ewaldErrorTolerance)
if self._defaults[1] == '1':
methodMap = {ff.NoCutoff:mm.CustomNonbondedForce.NoCutoff,
ff.CutoffNonPeriodic:mm.CustomNonbondedForce.CutoffNonPeriodic,
ff.CutoffPeriodic:mm.CustomNonbondedForce.CutoffPeriodic,
ff.Ewald:mm.CustomNonbondedForce.CutoffPeriodic,
ff.PME:mm.CustomNonbondedForce.CutoffPeriodic,
ff.LJPME:mm.CustomNonbondedForce.CutoffPeriodic}
lj.setNonbondedMethod(methodMap[nonbondedMethod])
lj.setCutoffDistance(nonbondedCutoff)
# Adjust masses. # Adjust masses.
......
...@@ -51,6 +51,18 @@ class TestGromacsTopFile(unittest.TestCase): ...@@ -51,6 +51,18 @@ class TestGromacsTopFile(unittest.TestCase):
ene = context.getState(getEnergy=True, groups=2**1).getPotentialEnergy() ene = context.getState(getEnergy=True, groups=2**1).getPotentialEnergy()
self.assertAlmostEqual(ene.value_in_unit(kilojoules_per_mole), 341.6905133582857) self.assertAlmostEqual(ene.value_in_unit(kilojoules_per_mole), 341.6905133582857)
def test_SMOG(self):
""" Test to ensure that SMOG models can be run without problems """
top = GromacsTopFile('systems/2ci2.pdb.top')
gro = GromacsGroFile('systems/2ci2.pdb.gro')
system = top.createSystem()
context = Context(system, VerletIntegrator(1*femtosecond),
Platform.getPlatformByName('Reference'))
context.setPositions(gro.positions)
ene = context.getState(getEnergy=True).getPotentialEnergy()
self.assertAlmostEqual(ene.value_in_unit(kilojoules_per_mole), -346.940915296)
def test_Cutoff(self): def test_Cutoff(self):
"""Test to make sure the nonbondedCutoff parameter is passed correctly.""" """Test to make sure the nonbondedCutoff parameter is passed correctly."""
......
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment