Commit a773f1c7 authored by John Chodera (MSKCC)'s avatar John Chodera (MSKCC)
Browse files

Add methods to get unmatched residues without forcefield templates.

parent 39120086
......@@ -569,6 +569,89 @@ class ForceField(object):
break
return [template, matches]
def getUnmatchedResidues(self, topology):
"""Return a list of Residue objects from specified topology for which no forcefield templates are available.
Parameters
----------
topology : Topology
The Topology for which to create a System
Returns
-------
unmatched_residues : list of Residue
List of residue templates from `topology` for which no forcefield residue templates are available.
Note that multiple instances of the same residue appearing at different points in the topology may be returned.
This method may be of use in generating missing residue templates or diagnosing parameterization failures.
"""
# Find the template matching each residue, compiling a list of residues for which no templates are available.
unmatched_residues = list() # list of unmatched residues
for chain in topology.chains():
for res in chain.residues():
# Attempt to match one of the existing templates.
[template, matches] = self._getResidueTemplateMatches(res, bondedToAtom)
if matches is None:
# No existing templates match.
unmatched_residues.append(res)
return unmatched_residues
def getUniqueUnmatchedResidues(self, topology):
"""Returns a unique list of Residue objects from specified topology for which no forcefield templates are available.
Parameters
----------
topology : Topology
The Topology for which to create a System
Returns
-------
unmatched_residues : list of Residue
List of residue templates from `topology` for which no forcefield residue templates are available.
Note that only a single instances each missing residue type will be returned.
This method may be of use in generating missing residue templates.
"""
# Get a non-unique list of unmatched residues.
unmatched_residues = self.getUnmatchedResidues(topology)
# Record which atoms are bonded to each other atom
bondedToAtom = []
for atom in topology.atoms():
bondedToAtom.append(set())
for bond in topology.bonds():
bondedToAtom[bond.atom1.index].add(bond.atom2.index)
bondedToAtom[bond.atom2.index].add(bond.atom1.index)
# Generate a unique list of unmatched residues by comparing fingerprints.
unique_unmatched_residues = list()
signatures = set()
for residue in unmatched_residues:
signature = _createResidueSignature([ atom.element for atom in residue.atoms() ])
is_unique = True
if signature in signatures:
# Signature is the same as an existing residue; check connectivity.
template = ForceField._TemplateData(residue.name)
for atom in residue.atoms():
template.atoms.append(ForceField._TemplateAtomData(atom.name, None, atom.element))
for (atom1,atom2) in residue.internal_bonds():
template.addBondByName(atom1.name, atom2.name)
residue_atoms = [ atom for atom in residue.atoms() ]
for (atom1,atom2) in residue.external_bonds():
if atom1 in residue_atoms:
template.addExternalBondByName(atom1.name)
elif atom2 in residue_atoms:
template.addExternalBondByName(atom2.name)
for check_residue in unique_unmatched_residues:
matches = _matchResidue(check_residue, template, bondedToAtom)
if matches is not None:
is_unique = False
if is_unique:
# Residue is unique.
unique_unmatched_residues.append(residue)
signatures.add(signature)
return unique_unmatched_residues
def createSystem(self, topology, nonbondedMethod=NoCutoff, nonbondedCutoff=1.0*unit.nanometer,
constraints=None, rigidWater=True, removeCMMotion=True, hydrogenMass=None, **args):
"""Construct an OpenMM System representing a Topology with this force field.
......
......@@ -347,6 +347,55 @@ class TestForceField(unittest.TestCase):
system = forcefield.createSystem(pdb.topology, nonbondedMethod=test['nonbondedMethod'])
# TODO: Test energies are finite?
def test_getUnmatchedResidues(self):
"""Test retrieval of list of residues for which no templates are available."""
#
# Test where we generate parameters for only a ligand.
#
# Load the PDB file.
pdb = PDBFile(os.path.join('systems', 'T4-lysozyme-L99A-p-xylene-implicit.pdb'))
# Create a ForceField object.
forcefield = ForceField('amber99sb.xml', 'tip3p.xml')
# Get list of unmatched residues.
unmatched_residues = forcefield.getUnmatchedResidues(pdb.topology)
# Check results.
self.assertEqual(len(unmatched_residues), 1)
self.assertEqual(unmatched_residues[0].name, 'TMP')
self.assertEqual(unmatched_residues[0].id, 163)
# Load the PDB file.
pdb = PDBFile(os.path.join('systems', 'ala_ala_ala.pdb'))
# Create a ForceField object.
forcefield = ForceField('tip3p.xml')
# Get list of unmatched residues.
unmatched_residues = forcefield.getUnmatchedResidues(pdb.topology)
# Check results.
self.assertEqual(len(unmatched_residues), 3)
self.assertEqual(unmatched_residues[0].name, 'ALA')
self.assertEqual(unmatched_residues[0].chain, 'X')
self.assertEqual(unmatched_residues[0].id, 1)
def test_getUniqueUnmatchedResidues(self):
"""Test retrieval of list of residues for which no templates are available."""
#
# Test where we generate parameters for only a ligand.
#
# Load the PDB file.
pdb = PDBFile(os.path.join('systems', 'nacl-water.pdb'))
# Create a ForceField object.
forcefield = ForceField('tip3p.xml')
# Get list of unmatched residues.
unmatched_residues = forcefield.getUnmatchedResidues(pdb.topology)
unique_unmatched_residues = forcefield.getUniqueUnmatchedResidues(pdb.topology)
# Check results.
self.assertEqual(len(unmatched_residues), 14)
self.assertEqual(len(unique_unmatched_residues), 2)
unique_names = set([ residue.name for residue in unique_unmatched_residues ])
self.assertTrue('NA' in unique_names)
self.assertTrue('CL' in unique_names)
class AmoebaTestForceField(unittest.TestCase):
"""Test the ForceField.createSystem() method with the AMOEBA forcefield."""
......@@ -436,4 +485,3 @@ class AmoebaTestForceField(unittest.TestCase):
if __name__ == '__main__':
unittest.main()
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