Commit c5ad2202 authored by peastman's avatar peastman
Browse files

ForceField supports DrudeForce

parent 6185317a
...@@ -158,18 +158,20 @@ class ForceField(object): ...@@ -158,18 +158,20 @@ class ForceField(object):
if typeAttrib in attrib: if typeAttrib in attrib:
raise ValueError('Tag specifies both a type and a class for the same atom: '+etree.tostring(node)) raise ValueError('Tag specifies both a type and a class for the same atom: '+etree.tostring(node))
if attrib[classAttrib] not in self._atomClasses: if attrib[classAttrib] not in self._atomClasses:
return None # Unknown atom class types.append(None) # Unknown atom class
else:
types.append(self._atomClasses[attrib[classAttrib]]) types.append(self._atomClasses[attrib[classAttrib]])
else: else:
if typeAttrib not in attrib or attrib[typeAttrib] not in self._atomTypes: if typeAttrib not in attrib or attrib[typeAttrib] not in self._atomTypes:
return None # Unknown atom type types.append(None) # Unknown atom type
else:
types.append([attrib[typeAttrib]]) types.append([attrib[typeAttrib]])
return types return types
def _parseTorsion(self, node): def _parseTorsion(self, node):
"""Parse the node defining a torsion.""" """Parse the node defining a torsion."""
types = self._findAtomTypes(node, 4) types = self._findAtomTypes(node, 4)
if types is None: if None in types:
return None return None
torsion = PeriodicTorsion(types) torsion = PeriodicTorsion(types)
attrib = node.attrib attrib = node.attrib
...@@ -416,6 +418,12 @@ class ForceField(object): ...@@ -416,6 +418,12 @@ class ForceField(object):
if removeCMMotion: if removeCMMotion:
sys.addForce(mm.CMMotionRemover()) sys.addForce(mm.CMMotionRemover())
# Let generators do postprocessing
for force in self._forces:
if 'postprocessSystem' in dir(force):
force.postprocessSystem(sys, data, args)
# Execute scripts found in the XML files. # Execute scripts found in the XML files.
for script in self._scripts: for script in self._scripts:
...@@ -483,9 +491,10 @@ def _findAtomMatches(atoms, template, bondedTo, externalBonds, matches, hasMatch ...@@ -483,9 +491,10 @@ def _findAtomMatches(atoms, template, bondedTo, externalBonds, matches, hasMatch
if position == len(atoms): if position == len(atoms):
return True return True
elem = atoms[position].element elem = atoms[position].element
name = atoms[position].name
for i in range(len(atoms)): for i in range(len(atoms)):
atom = template.atoms[i] atom = template.atoms[i]
if (atom.element == elem or atom.element is None) and not hasMatch[i] and len(atom.bondedTo) == len(bondedTo[position]) and atom.externalBonds == externalBonds[position]: if (atom.element == elem or (atom.element is None and atom.name == name)) and not hasMatch[i] and len(atom.bondedTo) == len(bondedTo[position]) and atom.externalBonds == externalBonds[position]:
# See if the bonds for this identification are consistent # See if the bonds for this identification are consistent
allBondsMatch = all((bonded > position or matches[bonded] in atom.bondedTo for bonded in bondedTo[position])) allBondsMatch = all((bonded > position or matches[bonded] in atom.bondedTo for bonded in bondedTo[position]))
...@@ -521,7 +530,7 @@ class HarmonicBondGenerator: ...@@ -521,7 +530,7 @@ class HarmonicBondGenerator:
ff._forces.append(generator) ff._forces.append(generator)
for bond in element.findall('Bond'): for bond in element.findall('Bond'):
types = ff._findAtomTypes(bond, 2) types = ff._findAtomTypes(bond, 2)
if types is not None: if None not in types:
generator.types1.append(types[0]) generator.types1.append(types[0])
generator.types2.append(types[1]) generator.types2.append(types[1])
generator.length.append(float(bond.attrib['length'])) generator.length.append(float(bond.attrib['length']))
...@@ -569,7 +578,7 @@ class HarmonicAngleGenerator: ...@@ -569,7 +578,7 @@ class HarmonicAngleGenerator:
ff._forces.append(generator) ff._forces.append(generator)
for angle in element.findall('Angle'): for angle in element.findall('Angle'):
types = ff._findAtomTypes(angle, 3) types = ff._findAtomTypes(angle, 3)
if types is not None: if None not in types:
generator.types1.append(types[0]) generator.types1.append(types[0])
generator.types2.append(types[1]) generator.types2.append(types[1])
generator.types3.append(types[2]) generator.types3.append(types[2])
...@@ -748,11 +757,11 @@ class RBTorsionGenerator: ...@@ -748,11 +757,11 @@ class RBTorsionGenerator:
ff._forces.append(generator) ff._forces.append(generator)
for torsion in element.findall('Proper'): for torsion in element.findall('Proper'):
types = ff._findAtomTypes(torsion, 4) types = ff._findAtomTypes(torsion, 4)
if types is not None: if None not in types:
generator.proper.append(RBTorsion(types, [float(torsion.attrib['c'+str(i)]) for i in range(6)])) generator.proper.append(RBTorsion(types, [float(torsion.attrib['c'+str(i)]) for i in range(6)]))
for torsion in element.findall('Improper'): for torsion in element.findall('Improper'):
types = ff._findAtomTypes(torsion, 4) types = ff._findAtomTypes(torsion, 4)
if types is not None: if None not in types:
generator.improper.append(RBTorsion(types, [float(torsion.attrib['c'+str(i)]) for i in range(6)])) generator.improper.append(RBTorsion(types, [float(torsion.attrib['c'+str(i)]) for i in range(6)]))
def createForce(self, sys, data, nonbondedMethod, nonbondedCutoff, args): def createForce(self, sys, data, nonbondedMethod, nonbondedCutoff, args):
...@@ -850,7 +859,7 @@ class CMAPTorsionGenerator: ...@@ -850,7 +859,7 @@ class CMAPTorsionGenerator:
generator.maps.append(values) generator.maps.append(values)
for torsion in element.findall('Torsion'): for torsion in element.findall('Torsion'):
types = ff._findAtomTypes(torsion, 5) types = ff._findAtomTypes(torsion, 5)
if types is not None: if None not in types:
generator.torsions.append(CMAPTorsion(types, int(torsion.attrib['map']))) generator.torsions.append(CMAPTorsion(types, int(torsion.attrib['map'])))
def createForce(self, sys, data, nonbondedMethod, nonbondedCutoff, args): def createForce(self, sys, data, nonbondedMethod, nonbondedCutoff, args):
...@@ -931,7 +940,7 @@ class NonbondedGenerator: ...@@ -931,7 +940,7 @@ class NonbondedGenerator:
raise ValueError('Found multiple NonbondedForce tags with different 1-4 scales') raise ValueError('Found multiple NonbondedForce tags with different 1-4 scales')
for atom in element.findall('Atom'): for atom in element.findall('Atom'):
types = ff._findAtomTypes(atom, 1) types = ff._findAtomTypes(atom, 1)
if types is not None: if None not in types:
values = (float(atom.attrib['charge']), float(atom.attrib['sigma']), float(atom.attrib['epsilon'])) values = (float(atom.attrib['charge']), float(atom.attrib['sigma']), float(atom.attrib['epsilon']))
for t in types[0]: for t in types[0]:
generator.typeMap[t] = values generator.typeMap[t] = values
...@@ -952,7 +961,14 @@ class NonbondedGenerator: ...@@ -952,7 +961,14 @@ class NonbondedGenerator:
force.addParticle(values[0], values[1], values[2]) force.addParticle(values[0], values[1], values[2])
else: else:
raise ValueError('No nonbonded parameters defined for atom type '+t) raise ValueError('No nonbonded parameters defined for atom type '+t)
# Create exceptions based on bonds and virtual sites. force.setNonbondedMethod(methodMap[nonbondedMethod])
force.setCutoffDistance(nonbondedCutoff)
if 'ewaldErrorTolerance' in args:
force.setEwaldErrorTolerance(args['ewaldErrorTolerance'])
sys.addForce(force)
def postprocessSystem(self, sys, data, args):
# Create exceptions based on bonds, virtual sites, and Drude particles.
bondIndices = [] bondIndices = []
for bond in data.bonds: for bond in data.bonds:
bondIndices.append((bond.atom1, bond.atom2)) bondIndices.append((bond.atom1, bond.atom2))
...@@ -961,12 +977,26 @@ class NonbondedGenerator: ...@@ -961,12 +977,26 @@ class NonbondedGenerator:
site = sys.getVirtualSite(i) site = sys.getVirtualSite(i)
for j in range(site.getNumParticles()): for j in range(site.getNumParticles()):
bondIndices.append((i, site.getParticle(j))) bondIndices.append((i, site.getParticle(j)))
force.createExceptionsFromBonds(bondIndices, self.coulomb14scale, self.lj14scale) drude = [f for f in sys.getForces() if isinstance(f, mm.DrudeForce)]
force.setNonbondedMethod(methodMap[nonbondedMethod]) if len(drude) > 0:
force.setCutoffDistance(nonbondedCutoff) drude = drude[0]
if 'ewaldErrorTolerance' in args: # For purposes of creating exceptions, a Drude particle is "bonded" to anything
force.setEwaldErrorTolerance(args['ewaldErrorTolerance']) # its parent atom is bonded to.
sys.addForce(force) 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.NonbondedForce)][0]
nonbonded.createExceptionsFromBonds(bondIndices, self.coulomb14scale, self.lj14scale)
parsers["NonbondedForce"] = NonbondedGenerator.parseElement parsers["NonbondedForce"] = NonbondedGenerator.parseElement
...@@ -989,7 +1019,7 @@ class GBSAOBCGenerator: ...@@ -989,7 +1019,7 @@ class GBSAOBCGenerator:
generator = existing[0] generator = existing[0]
for atom in element.findall('Atom'): for atom in element.findall('Atom'):
types = ff._findAtomTypes(atom, 1) types = ff._findAtomTypes(atom, 1)
if types is not None: if None not in types:
values = (float(atom.attrib['charge']), float(atom.attrib['radius']), float(atom.attrib['scale'])) values = (float(atom.attrib['charge']), float(atom.attrib['radius']), float(atom.attrib['scale']))
for t in types[0]: for t in types[0]:
generator.typeMap[t] = values generator.typeMap[t] = values
...@@ -1046,7 +1076,7 @@ class GBVIGenerator: ...@@ -1046,7 +1076,7 @@ class GBVIGenerator:
ff._forces.append(generator) ff._forces.append(generator)
for atom in element.findall('Atom'): for atom in element.findall('Atom'):
types = ff._findAtomTypes(atom, 1) types = ff._findAtomTypes(atom, 1)
if types is not None: if None not in types:
values = (float(atom.attrib['charge']), float(atom.attrib['radius']), float(atom.attrib['gamma'])) values = (float(atom.attrib['charge']), float(atom.attrib['radius']), float(atom.attrib['gamma']))
for t in types[0]: for t in types[0]:
generator.typeMap[t] = values generator.typeMap[t] = values
...@@ -1129,7 +1159,7 @@ class CustomBondGenerator: ...@@ -1129,7 +1159,7 @@ class CustomBondGenerator:
generator.perBondParams.append(param.attrib['name']) generator.perBondParams.append(param.attrib['name'])
for bond in element.findall('Bond'): for bond in element.findall('Bond'):
types = ff._findAtomTypes(bond, 2) types = ff._findAtomTypes(bond, 2)
if types is not None: if None not in types:
generator.types1.append(types[0]) generator.types1.append(types[0])
generator.types2.append(types[1]) generator.types2.append(types[1])
generator.paramValues.append([float(bond.attrib[param]) for param in generator.perBondParams]) generator.paramValues.append([float(bond.attrib[param]) for param in generator.perBondParams])
...@@ -1177,7 +1207,7 @@ class CustomAngleGenerator: ...@@ -1177,7 +1207,7 @@ class CustomAngleGenerator:
generator.perAngleParams.append(param.attrib['name']) generator.perAngleParams.append(param.attrib['name'])
for angle in element.findall('Angle'): for angle in element.findall('Angle'):
types = ff._findAtomTypes(angle, 3) types = ff._findAtomTypes(angle, 3)
if types is not None: if None not in types:
generator.types1.append(types[0]) generator.types1.append(types[0])
generator.types2.append(types[1]) generator.types2.append(types[1])
generator.types3.append(types[2]) generator.types3.append(types[2])
...@@ -1238,11 +1268,11 @@ class CustomTorsionGenerator: ...@@ -1238,11 +1268,11 @@ class CustomTorsionGenerator:
generator.perTorsionParams.append(param.attrib['name']) generator.perTorsionParams.append(param.attrib['name'])
for torsion in element.findall('Proper'): for torsion in element.findall('Proper'):
types = ff._findAtomTypes(torsion, 4) types = ff._findAtomTypes(torsion, 4)
if types is not None: if None not in types:
generator.proper.append(CustomTorsion(types, [float(torsion.attrib[param]) for param in generator.perTorsionParams])) generator.proper.append(CustomTorsion(types, [float(torsion.attrib[param]) for param in generator.perTorsionParams]))
for torsion in element.findall('Improper'): for torsion in element.findall('Improper'):
types = ff._findAtomTypes(torsion, 4) types = ff._findAtomTypes(torsion, 4)
if types is not None: if None not in types:
generator.improper.append(CustomTorsion(types, [float(torsion.attrib[param]) for param in generator.perTorsionParams])) generator.improper.append(CustomTorsion(types, [float(torsion.attrib[param]) for param in generator.perTorsionParams]))
def createForce(self, sys, data, nonbondedMethod, nonbondedCutoff, args): def createForce(self, sys, data, nonbondedMethod, nonbondedCutoff, args):
...@@ -1329,7 +1359,7 @@ class CustomGBGenerator: ...@@ -1329,7 +1359,7 @@ class CustomGBGenerator:
generator.perParticleParams.append(param.attrib['name']) generator.perParticleParams.append(param.attrib['name'])
for atom in element.findall('Atom'): for atom in element.findall('Atom'):
types = ff._findAtomTypes(atom, 1) types = ff._findAtomTypes(atom, 1)
if types is not None: if None not in types:
values = [float(atom.attrib[param]) for param in generator.perParticleParams] values = [float(atom.attrib[param]) for param in generator.perParticleParams]
for t in types[0]: for t in types[0]:
generator.typeMap[t] = values generator.typeMap[t] = values
...@@ -1431,7 +1461,7 @@ class AmoebaBondGenerator: ...@@ -1431,7 +1461,7 @@ class AmoebaBondGenerator:
forceField._forces.append(generator) forceField._forces.append(generator)
for bond in element.findall('Bond'): for bond in element.findall('Bond'):
types = forceField._findAtomTypes(bond, 2) types = forceField._findAtomTypes(bond, 2)
if types is not None: if None not in types:
generator.types1.append(types[0]) generator.types1.append(types[0])
generator.types2.append(types[1]) generator.types2.append(types[1])
generator.length.append(float(bond.attrib['length'])) generator.length.append(float(bond.attrib['length']))
...@@ -1544,7 +1574,7 @@ class AmoebaAngleGenerator: ...@@ -1544,7 +1574,7 @@ class AmoebaAngleGenerator:
forceField._forces.append(generator) forceField._forces.append(generator)
for angle in element.findall('Angle'): for angle in element.findall('Angle'):
types = forceField._findAtomTypes(angle, 3) types = forceField._findAtomTypes(angle, 3)
if types is not None: if None not in types:
generator.types1.append(types[0]) generator.types1.append(types[0])
generator.types2.append(types[1]) generator.types2.append(types[1])
...@@ -2029,7 +2059,7 @@ class AmoebaTorsionGenerator: ...@@ -2029,7 +2059,7 @@ class AmoebaTorsionGenerator:
for torsion in element.findall('Torsion'): for torsion in element.findall('Torsion'):
types = forceField._findAtomTypes(torsion, 4) types = forceField._findAtomTypes(torsion, 4)
if types is not None: if None not in types:
generator.types1.append(types[0]) generator.types1.append(types[0])
generator.types2.append(types[1]) generator.types2.append(types[1])
...@@ -2140,7 +2170,7 @@ class AmoebaPiTorsionGenerator: ...@@ -2140,7 +2170,7 @@ class AmoebaPiTorsionGenerator:
for piTorsion in element.findall('PiTorsion'): for piTorsion in element.findall('PiTorsion'):
types = forceField._findAtomTypes(piTorsion, 2) types = forceField._findAtomTypes(piTorsion, 2)
if types is not None: if None not in types:
generator.types1.append(types[0]) generator.types1.append(types[0])
generator.types2.append(types[1]) generator.types2.append(types[1])
generator.k.append(float(piTorsion.attrib['k'])) generator.k.append(float(piTorsion.attrib['k']))
...@@ -2259,7 +2289,7 @@ class AmoebaTorsionTorsionGenerator: ...@@ -2259,7 +2289,7 @@ class AmoebaTorsionTorsionGenerator:
for torsionTorsion in element.findall('TorsionTorsion'): for torsionTorsion in element.findall('TorsionTorsion'):
types = forceField._findAtomTypes(torsionTorsion, 5) types = forceField._findAtomTypes(torsionTorsion, 5)
if types is not None: if None not in types:
generator.types1.append(types[0]) generator.types1.append(types[0])
generator.types2.append(types[1]) generator.types2.append(types[1])
...@@ -2496,7 +2526,7 @@ class AmoebaStretchBendGenerator: ...@@ -2496,7 +2526,7 @@ class AmoebaStretchBendGenerator:
for stretchBend in element.findall('StretchBend'): for stretchBend in element.findall('StretchBend'):
types = forceField._findAtomTypes(stretchBend, 3) types = forceField._findAtomTypes(stretchBend, 3)
if types is not None: if None not in types:
generator.types1.append(types[0]) generator.types1.append(types[0])
generator.types2.append(types[1]) generator.types2.append(types[1])
...@@ -2653,7 +2683,7 @@ class AmoebaVdwGenerator: ...@@ -2653,7 +2683,7 @@ class AmoebaVdwGenerator:
for atom in element.findall('Vdw'): for atom in element.findall('Vdw'):
types = forceField._findAtomTypes(atom, 1) types = forceField._findAtomTypes(atom, 1)
if types is not None: if None not in types:
values = [float(atom.attrib['sigma']), float(atom.attrib['epsilon']), float(atom.attrib['reduction'])] values = [float(atom.attrib['sigma']), float(atom.attrib['epsilon']), float(atom.attrib['reduction'])]
...@@ -2926,7 +2956,7 @@ class AmoebaMultipoleGenerator: ...@@ -2926,7 +2956,7 @@ class AmoebaMultipoleGenerator:
for atom in element.findall('Multipole'): for atom in element.findall('Multipole'):
types = forceField._findAtomTypes(atom, 1) types = forceField._findAtomTypes(atom, 1)
if types is not None: if None not in types:
# k-indices not provided default to 0 # k-indices not provided default to 0
...@@ -2983,7 +3013,7 @@ class AmoebaMultipoleGenerator: ...@@ -2983,7 +3013,7 @@ class AmoebaMultipoleGenerator:
for atom in element.findall('Polarize'): for atom in element.findall('Polarize'):
types = forceField._findAtomTypes(atom, 1) types = forceField._findAtomTypes(atom, 1)
if types is not None: if None not in types:
classIndex = atom.attrib['type'] classIndex = atom.attrib['type']
polarizability = float(atom.attrib['polarizability']) polarizability = float(atom.attrib['polarizability'])
...@@ -3511,7 +3541,7 @@ class AmoebaWcaDispersionGenerator: ...@@ -3511,7 +3541,7 @@ class AmoebaWcaDispersionGenerator:
for atom in element.findall('WcaDispersion'): for atom in element.findall('WcaDispersion'):
types = forceField._findAtomTypes(atom, 1) types = forceField._findAtomTypes(atom, 1)
if types is not None: if None not in types:
values = [float(atom.attrib['radius']), float(atom.attrib['epsilon'])] values = [float(atom.attrib['radius']), float(atom.attrib['epsilon'])]
for t in types[0]: for t in types[0]:
...@@ -3855,7 +3885,7 @@ class AmoebaUreyBradleyGenerator: ...@@ -3855,7 +3885,7 @@ class AmoebaUreyBradleyGenerator:
forceField._forces.append(generator) forceField._forces.append(generator)
for bond in element.findall('UreyBradley'): for bond in element.findall('UreyBradley'):
types = forceField._findAtomTypes(bond, 3) types = forceField._findAtomTypes(bond, 3)
if types is not None: if None not in types:
generator.types1.append(types[0]) generator.types1.append(types[0])
generator.types2.append(types[1]) generator.types2.append(types[1])
...@@ -3899,3 +3929,87 @@ class AmoebaUreyBradleyGenerator: ...@@ -3899,3 +3929,87 @@ class AmoebaUreyBradleyGenerator:
parsers["AmoebaUreyBradleyForce"] = AmoebaUreyBradleyGenerator.parseElement parsers["AmoebaUreyBradleyForce"] = AmoebaUreyBradleyGenerator.parseElement
#============================================================================================= #=============================================================================================
## @private
class DrudeGenerator:
"""A DrudeGenerator constructs a DrudeForce."""
def __init__(self):
self.typeMap = {}
@staticmethod
def parseElement(element, ff):
existing = [f for f in ff._forces if isinstance(f, DrudeGenerator)]
if len(existing) == 0:
generator = DrudeGenerator()
ff._forces.append(generator)
else:
# Multiple <DrudeForce> tags were found, probably in different files. Simply add more types to the existing one.
generator = existing[0]
for particle in element.findall('Particle'):
types = ff._findAtomTypes(particle, 5)
if None not in types[:2]:
aniso12 = 0.0
aniso34 = 0.0
if 'aniso12' in particle.attrib:
aniso12 = float(particle.attrib['aniso12'])
if 'aniso34' in particle.attrib:
aniso34 = float(particle.attrib['aniso34'])
values = (types[1], types[2], types[3], types[4], float(particle.attrib['charge']), float(particle.attrib['polarizability']), aniso12, aniso34, float(particle.attrib['thole']))
for t in types[0]:
generator.typeMap[t] = values
def createForce(self, sys, data, nonbondedMethod, nonbondedCutoff, args):
force = mm.DrudeForce()
if not any(isinstance(f, mm.NonbondedForce) for f in sys.getForces()):
raise ValueError('<DrudeForce> must come after <NonbondedForce> in XML file')
# Add Drude particles.
drudeMap = {}
parentMap = {}
for atom in data.atoms:
t = data.atomType[atom]
if t in self.typeMap:
# Find other atoms in the residue that affect the Drude particle.
p = [-1, -1, -1, -1]
values = self.typeMap[t]
for atom2 in atom.residue.atoms():
type2 = data.atomType[atom2]
if type2 in values[0]:
p[0] = atom2.index
elif values[1] is not None and type2 in values[1]:
p[1] = atom2.index
elif values[2] is not None and type2 in values[2]:
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)
sys.addForce(force)
def postprocessSystem(self, sys, data, args):
# For every nonbonded exclusion between Drude particles, add a screened pair.
drude = [f for f in sys.getForces() if isinstance(f, mm.DrudeForce)][0]
nonbonded = [f for f in sys.getForces() if isinstance(f, mm.NonbondedForce)][0]
particleMap = {}
for i in range(drude.getNumParticles()):
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:
# 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]]
thole1 = self.typeMap[type1][8]
thole2 = self.typeMap[type2][8]
drude.addScreenedPair(drude1, drude2, thole1+thole2)
parsers["DrudeForce"] = DrudeGenerator.parseElement
\ No newline at end of file
...@@ -34,7 +34,7 @@ __author__ = "Peter Eastman" ...@@ -34,7 +34,7 @@ __author__ = "Peter Eastman"
__version__ = "1.0" __version__ = "1.0"
from simtk.openmm.app import Topology, PDBFile, ForceField from simtk.openmm.app import Topology, PDBFile, ForceField
from simtk.openmm.app.forcefield import HAngles, _createResidueSignature, _matchResidue from simtk.openmm.app.forcefield import HAngles, _createResidueSignature, _matchResidue, DrudeGenerator
from simtk.openmm.app.topology import Residue from simtk.openmm.app.topology import Residue
from simtk.openmm.vec3 import Vec3 from simtk.openmm.vec3 import Vec3
from simtk.openmm import System, Context, NonbondedForce, VerletIntegrator, LocalEnergyMinimizer from simtk.openmm import System, Context, NonbondedForce, VerletIntegrator, LocalEnergyMinimizer
...@@ -817,6 +817,15 @@ class Modeller(object): ...@@ -817,6 +817,15 @@ class Modeller(object):
bondedToAtomNoEP[atom1.index].add(atom2.index) bondedToAtomNoEP[atom1.index].add(atom2.index)
bondedToAtomNoEP[atom2.index].add(atom1.index) bondedToAtomNoEP[atom2.index].add(atom1.index)
# If the force field has a DrudeForce, record the types of Drude particles and their parents since we'll
# need them for picking particle positions.
drudeTypeMap = {}
for force in forcefield._forces:
if isinstance(force, DrudeGenerator):
for type in force.typeMap:
drudeTypeMap[type] = force.typeMap[type][0]
# Create the new Topology. # Create the new Topology.
newTopology = Topology() newTopology = Topology()
...@@ -894,6 +903,12 @@ class Modeller(object): ...@@ -894,6 +903,12 @@ class Modeller(object):
position = site.weights[0]*templateAtomPositions[index+site.atoms[0]] + site.weights[1]*templateAtomPositions[index+site.atoms[1]] + site.weights[2]*templateAtomPositions[index+site.atoms[2]] position = site.weights[0]*templateAtomPositions[index+site.atoms[0]] + site.weights[1]*templateAtomPositions[index+site.atoms[1]] + site.weights[2]*templateAtomPositions[index+site.atoms[2]]
elif site.type == 'outOfPlane': elif site.type == 'outOfPlane':
position = site.weights[0]*templateAtomPositions[index+site.atoms[0]] + site.weights[1]*templateAtomPositions[index+site.atoms[1]] + site.weights[2]*templateAtomPositions[index+site.atoms[2]] position = site.weights[0]*templateAtomPositions[index+site.atoms[0]] + site.weights[1]*templateAtomPositions[index+site.atoms[1]] + site.weights[2]*templateAtomPositions[index+site.atoms[2]]
if position is None and atom.type in drudeTypeMap:
# This is a Drude particle. Put it on top of its parent atom.
for atom2, pos in zip(template.atoms, templateAtomPositions):
if atom2.type in drudeTypeMap[atom.type]:
position = deepcopy(pos)
if position is None: if position is None:
# We couldn't figure out the correct position. As a wild guess, just put it at the center of the residue # We couldn't figure out the correct position. As a wild guess, just put it at the center of the residue
# and hope that energy minimization will fix it. # and hope that energy minimization will fix it.
......
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