Commit 2dd09317 authored by Peter Eastman's avatar Peter Eastman
Browse files

ForceField support TIP4P and TIP5P

parent 274be989
...@@ -7,6 +7,7 @@ __kernel void calcCenterOfMassMomentum(int numAtoms, __global const float4* rest ...@@ -7,6 +7,7 @@ __kernel void calcCenterOfMassMomentum(int numAtoms, __global const float4* rest
float4 cm = 0.0f; float4 cm = 0.0f;
while (index < numAtoms) { while (index < numAtoms) {
float4 velocity = velm[index]; float4 velocity = velm[index];
if (velocity.w != 0.0)
cm.xyz += velocity.xyz/velocity.w; cm.xyz += velocity.xyz/velocity.w;
index += get_global_size(0); index += get_global_size(0);
} }
......
...@@ -269,7 +269,7 @@ ...@@ -269,7 +269,7 @@
<Atom name="H72" alt1="2H5M"/> <Atom name="H72" alt1="2H5M"/>
<Atom name="H73" alt1="3H5M"/> <Atom name="H73" alt1="3H5M"/>
</Residue> </Residue>
<Residue name="HOH" alt1="H20" alt2="WAT"> <Residue name="HOH" alt1="H20" alt2="WAT" alt3="SOL">
<Atom name="O" alt1="OW"/> <Atom name="O" alt1="OW"/>
<Atom name="H1" alt1="HW1"/> <Atom name="H1" alt1="HW1"/>
<Atom name="H2" alt1="HW2"/> <Atom name="H2" alt1="HW2"/>
......
<ForceField>
<AtomTypes>
<Type name="tip4p-O" class="OW" element="O" mass="15.99943"/>
<Type name="tip4p-H" class="HW" element="H" mass="1.007947"/>
<Type name="tip4p-M" class="MW" mass="0"/>
</AtomTypes>
<Residues>
<Residue name="HOH">
<Atom name="O" type="tip4p-O"/>
<Atom name="H1" type="tip4p-H"/>
<Atom name="H2" type="tip4p-H"/>
<Atom name="M" type="tip4p-M"/>
<VirtualSite type="average3" index="3" atom1="0" atom2="1" atom3="2" weight1="0.74397587" weight2="0.128012065" weight3="0.128012065"/>
<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-O" charge="0" sigma="0.315365" epsilon="0.64852"/>
<Atom type="tip4p-H" charge="0.52" sigma="1" epsilon="0"/>
<Atom type="tip4p-M" charge="-1.04" sigma="1" epsilon="0"/>
</NonbondedForce>
</ForceField>
<ForceField>
<AtomTypes>
<Type name="tip5p-O" class="OW" element="O" mass="15.99943"/>
<Type name="tip5p-H" class="HW" element="H" mass="1.007947"/>
<Type name="tip5p-M" class="MW" mass="0"/>
</AtomTypes>
<Residues>
<Residue name="HOH">
<Atom name="O" type="tip5p-O"/>
<Atom name="H1" type="tip5p-H"/>
<Atom name="H2" type="tip5p-H"/>
<Atom name="M1" type="tip5p-M"/>
<Atom name="M2" type="tip5p-M"/>
<VirtualSite type="outOfPlane" index="3" atom1="0" atom2="1" atom3="2" weight12="-0.34490826" weight13="-0.34490826" weightCross="-6.4437903"/>
<VirtualSite type="outOfPlane" index="4" atom1="0" atom2="1" atom3="2" weight12="-0.34490826" weight13="-0.34490826" weightCross="6.4437903"/>
<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="tip5p-O" charge="0" sigma="0.312" epsilon="0.66944"/>
<Atom type="tip5p-H" charge="0.241" sigma="1" epsilon="0"/>
<Atom type="tip5p-M" charge="-0.241" sigma="1" epsilon="0"/>
</NonbondedForce>
</ForceField>
...@@ -57,7 +57,10 @@ class ForceField(object): ...@@ -57,7 +57,10 @@ class ForceField(object):
if tree.getroot().find('AtomTypes') is not None: if tree.getroot().find('AtomTypes') is not None:
for type in tree.getroot().find('AtomTypes').findall('Type'): for type in tree.getroot().find('AtomTypes').findall('Type'):
self._atomTypes[type.attrib['name']] = (type.attrib['class'], float(type.attrib['mass']), elem.get_by_symbol(type.attrib['element'])) element = None
if 'element' in type.attrib:
element = elem.get_by_symbol(type.attrib['element'])
self._atomTypes[type.attrib['name']] = (type.attrib['class'], float(type.attrib['mass']), element)
# Load the residue templates. # Load the residue templates.
...@@ -68,6 +71,8 @@ class ForceField(object): ...@@ -68,6 +71,8 @@ class ForceField(object):
self._templates[resName] = template self._templates[resName] = template
for atom in residue.findall('Atom'): for atom in residue.findall('Atom'):
template.atoms.append(ForceField._TemplateAtomData(atom.attrib['name'], atom.attrib['type'], self._atomTypes[atom.attrib['type']][2])) 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'): for bond in residue.findall('Bond'):
b = (int(bond.attrib['from']), int(bond.attrib['to'])) b = (int(bond.attrib['from']), int(bond.attrib['to']))
template.bonds.append(b) template.bonds.append(b)
...@@ -79,6 +84,9 @@ class ForceField(object): ...@@ -79,6 +84,9 @@ class ForceField(object):
template.atoms[b].externalBonds += 1 template.atoms[b].externalBonds += 1
for template in self._templates.values(): for template in self._templates.values():
template.signature = _createResidueSignature([atom.element for atom in template.atoms]) template.signature = _createResidueSignature([atom.element for atom in template.atoms])
if template.signature is None:
sigString = None
else:
sigString = _signatureToString(template.signature) sigString = _signatureToString(template.signature)
if sigString in self._templateSignatures: if sigString in self._templateSignatures:
self._templateSignatures[sigString].append(template) self._templateSignatures[sigString].append(template)
...@@ -144,6 +152,7 @@ class ForceField(object): ...@@ -144,6 +152,7 @@ class ForceField(object):
def __init__(self): def __init__(self):
self.atomType = {} self.atomType = {}
self.atoms = [] self.atoms = []
self.virtualSites = {}
self.bonds = [] self.bonds = []
self.angles = [] self.angles = []
self.propers = [] self.propers = []
...@@ -156,6 +165,7 @@ class ForceField(object): ...@@ -156,6 +165,7 @@ class ForceField(object):
def __init__(self, name): def __init__(self, name):
self.name = name self.name = name
self.atoms = [] self.atoms = []
self.virtualSites = []
self.bonds = [] self.bonds = []
self.externalBonds = [] self.externalBonds = []
...@@ -176,6 +186,25 @@ class ForceField(object): ...@@ -176,6 +186,25 @@ class ForceField(object):
self.isConstrained = False self.isConstrained = False
self.length = 0.0 self.length = 0.0
class _VirtualSiteData:
"""Inner class used to encapsulate data about a virtual site."""
def __init__(self, node):
attrib = node.attrib
self.index = int(attrib['index'])
self.type = attrib['type']
if self.type == 'average2':
self.atoms = [int(attrib['atom1']), int(attrib['atom2'])]
self.weights = [float(attrib['weight1']), float(attrib['weight2'])]
elif self.type == 'average3':
self.atoms = [int(attrib['atom1']), int(attrib['atom2']), int(attrib['atom3'])]
self.weights = [float(attrib['weight1']), float(attrib['weight2']), float(attrib['weight3'])]
elif self.type == 'outOfPlane':
self.atoms = [int(attrib['atom1']), int(attrib['atom2']), int(attrib['atom3'])]
self.weights = [float(attrib['weight12']), float(attrib['weight13']), float(attrib['weightCross'])]
else:
raise ValueError('Unknown virtual site type: %s' % self.type)
self.atoms = [x-self.index for x in self.atoms]
def createSystem(self, topology, nonbondedMethod=NoCutoff, nonbondedCutoff=1.0*unit.nanometer, def createSystem(self, topology, nonbondedMethod=NoCutoff, nonbondedCutoff=1.0*unit.nanometer,
constraints=None, rigidWater=True, removeCMMotion=True, **args): constraints=None, rigidWater=True, removeCMMotion=True, **args):
"""Construct an OpenMM System representing a Topology with this force field. """Construct an OpenMM System representing a Topology with this force field.
...@@ -225,19 +254,31 @@ class ForceField(object): ...@@ -225,19 +254,31 @@ class ForceField(object):
for chain in topology.chains(): for chain in topology.chains():
for res in chain.residues(): for res in chain.residues():
signature = _signatureToString(_createResidueSignature([atom.element for atom in res.atoms()]))
template = None template = None
matches = None matches = None
sig = _createResidueSignature([atom.element for atom in res.atoms()])
if sig is not None:
signature = _signatureToString(sig)
if signature in self._templateSignatures: if signature in self._templateSignatures:
for t in self._templateSignatures[signature]: for t in self._templateSignatures[signature]:
matches = _matchResidue(res, t, bondedToAtom, atomIndices) matches = _matchResidue(res, t, bondedToAtom, atomIndices)
if matches is not None: if matches is not None:
template = t template = t
break break
if matches is None:
# Check templates involving virtual sites
for t in self._templateSignatures[None]:
matches = _matchResidue(res, t, bondedToAtom, atomIndices)
if matches is not None:
template = t
break
if matches is None: if matches is None:
raise ValueError('No template found for residue %d (%s)' % (res.index+1, res.name)) raise ValueError('No template found for residue %d (%s)' % (res.index+1, res.name))
for atom, match in zip(res.atoms(), matches): for atom, match in zip(res.atoms(), matches):
data.atomType[atom] = template.atoms[match].type data.atomType[atom] = template.atoms[match].type
for site in template.virtualSites:
if match == site.index:
data.virtualSites[atom] = site
# Create the System and add atoms # Create the System and add atoms
...@@ -338,6 +379,18 @@ class ForceField(object): ...@@ -338,6 +379,18 @@ class ForceField(object):
if atom1.residue.name == 'HOH' and atom2.residue.name == 'HOH' and atom3.residue.name == 'HOH': if atom1.residue.name == 'HOH' and atom2.residue.name == 'HOH' and atom3.residue.name == 'HOH':
data.isAngleConstrained[i] = True data.isAngleConstrained[i] = True
# Add virtual sites
for atom in data.virtualSites:
site = data.virtualSites[atom]
index = atomIndices[atom]
if site.type == 'average2':
sys.setVirtualSite(index, mm.TwoParticleAverageSite(index+site.atoms[0], index+site.atoms[1], site.weights[0], site.weights[1]))
elif site.type == 'average3':
sys.setVirtualSite(index, mm.ThreeParticleAverageSite(index+site.atoms[0], index+site.atoms[1], index+site.atoms[2], site.weights[0], site.weights[1], site.weights[2]))
elif site.type == 'outOfPlane':
sys.setVirtualSite(index, mm.OutOfPlaneSite(index+site.atoms[0], index+site.atoms[1], index+site.atoms[2], site.weights[0], site.weights[1], site.weights[2]))
# Add forces to the System # Add forces to the System
for force in self._forces: for force in self._forces:
...@@ -351,6 +404,8 @@ def _createResidueSignature(elements): ...@@ -351,6 +404,8 @@ def _createResidueSignature(elements):
"""Create a signature for a residue based on the elements of the atoms it contains.""" """Create a signature for a residue based on the elements of the atoms it contains."""
counts = {} counts = {}
for element in elements: for element in elements:
if element is None:
return None # This residue contains "atoms" (probably virtual sites) that should match any element
if element in counts: if element in counts:
counts[element] += 1 counts[element] += 1
else: else:
...@@ -382,6 +437,8 @@ def _matchResidue(res, template, bondedToAtom, atomIndices): ...@@ -382,6 +437,8 @@ def _matchResidue(res, template, bondedToAtom, atomIndices):
or None if it does not match the template or None if it does not match the template
""" """
atoms = list(res.atoms()) atoms = list(res.atoms())
if len(atoms) != len(template.atoms):
return None
residueAtomBonds = [] residueAtomBonds = []
templateAtomBonds = [] templateAtomBonds = []
matches = len(atoms)*[0] matches = len(atoms)*[0]
...@@ -410,7 +467,7 @@ def _findAtomMatches(atoms, template, bondedTo, externalBonds, matches, hasMatch ...@@ -410,7 +467,7 @@ def _findAtomMatches(atoms, template, bondedTo, externalBonds, matches, hasMatch
elem = atoms[position].element elem = atoms[position].element
for i in range(len(atoms)): for i in range(len(atoms)):
atom = template.atoms[i] atom = template.atoms[i]
if atom.element == elem 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 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]))
...@@ -869,9 +926,15 @@ class NonbondedGenerator: ...@@ -869,9 +926,15 @@ 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.
bondIndices = [] bondIndices = []
for bond in data.bonds: for bond in data.bonds:
bondIndices.append((bond.atom1, bond.atom2)) 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)))
force.createExceptionsFromBonds(bondIndices, self.coulomb14scale, self.lj14scale) force.createExceptionsFromBonds(bondIndices, self.coulomb14scale, self.lj14scale)
force.setNonbondedMethod(methodMap[nonbondedMethod]) force.setNonbondedMethod(methodMap[nonbondedMethod])
force.setCutoffDistance(nonbondedCutoff) force.setCutoffDistance(nonbondedCutoff)
......
...@@ -173,6 +173,7 @@ NO_OUTPUT_ARGS = [('LocalEnergyMinimizer', 'minimize', 'context'), ...@@ -173,6 +173,7 @@ NO_OUTPUT_ARGS = [('LocalEnergyMinimizer', 'minimize', 'context'),
# The list is the argument position(s). # The list is the argument position(s).
STEAL_OWNERSHIP = {("Platform", "registerPlatform") : [0], STEAL_OWNERSHIP = {("Platform", "registerPlatform") : [0],
("System", "addForce") : [0], ("System", "addForce") : [0],
("System", "setVirtualSite") : [1],
} }
# This is a list of units to attach to return values and method args. # This is a list of units to attach to return values and method args.
......
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