Commit 24e12c12 by Peter Eastman

Added UI for adding missing residues. Added option to delete chains.

parent 892470c1
......@@ -69,7 +69,7 @@ def _overlayPoints(points1, points2):
if len(points1) == 0:
return (Vec3(0, 0, 0), np.identity(3), Vec3(0, 0, 0))
if len(points1) == 1:
return (points1[0], np.identity(3), -points2[0])
return (points1[0], np.identity(3), -1*points2[0])
# Compute centroids.
......@@ -98,6 +98,7 @@ class PDBFixer(object):
self.topology = self.pdb.topology
self.positions = self.pdb.positions
self.centroid = unit.sum(self.positions)/len(self.positions)
self._structureChains = list(self.structure.iter_chains())
# Load the templates.
......@@ -126,8 +127,8 @@ class PDBFixer(object):
# Insert missing residues here.
insertHere = [r[2] for r in self.missingResidues if r[0] == chain.index and r[1] == indexInChain]
if len(insertHere) > 0:
if (chain.index, indexInChain) in self.missingResidues:
insertHere = self.missingResidues[(chain.index, indexInChain)]
endPosition = self._computeResidueCenter(residue)
if indexInChain > 0:
startPosition = self._computeResidueCenter(chainResidues[indexInChain-1])
......@@ -180,8 +181,8 @@ class PDBFixer(object):
# If this is the end of the chain, add any missing residues that come after it.
if residue == chainResidues[-1]:
insertHere = [r[2] for r in self.missingResidues if r[0] == chain.index and r[1] > indexInChain]
if residue == chainResidues[-1] and (chain.index, indexInChain+1) in self.missingResidues:
insertHere = self.missingResidues[(chain.index, indexInChain+1)]
if len(insertHere) > 0:
startPosition = self._computeResidueCenter(residue)
outward = startPosition-self.centroid
......@@ -241,6 +242,14 @@ class PDBFixer(object):
templatePosition = template.positions[atom.index].value_in_unit(unit.nanometer)
newPositions.append(mm.Vec3(*np.dot(rotate, templatePosition))*unit.nanometer+translate)
def removeChains(self, chainIndices):
modeller = app.Modeller(self.topology, self.positions)
allChains = list(self.topology.chains())
modeller.delete(allChains[i] for i in chainIndices)
self.topology = modeller.topology
self.positions = modeller.positions
self._structureChains = [self._structureChains[i] for i in range(len(self._structureChains)) if i not in chainIndices]
def findNonstandardResidues(self):
self.nonstandardResidues = [r for r in self.topology.residues() if r.name in substitutions]
......@@ -266,7 +275,7 @@ class PDBFixer(object):
self.positions = modeller.positions
def findMissingResidues(self):
chains = [c for c in self.structure.iter_chains() if any(atom.record_name == 'ATOM' for atom in c.iter_atoms())]
chains = [c for c in self._structureChains if any(atom.record_name == 'ATOM' for atom in c.iter_atoms())]
chainWithGaps = {}
# Find the sequence of each chain, with gaps for missing residues.
......@@ -299,8 +308,8 @@ class PDBFixer(object):
# Now build the list of residues to add.
self.missingResidues = []
for structChain, topChain in zip(self.structure.iter_chains(), self.pdb.topology.chains()):
self.missingResidues = {}
for structChain, topChain in zip(self._structureChains, self.pdb.topology.chains()):
if structChain in chainSequence:
offset = chainOffset[structChain]
sequence = chainSequence[structChain].residues
......@@ -308,7 +317,10 @@ class PDBFixer(object):
index = 0
for i in range(len(sequence)):
if i < offset or i >= len(gappedSequence)+offset or gappedSequence[i-offset] is None:
self.missingResidues.append((topChain.index, index, sequence[i]))
key = (topChain.index, index)
if key not in self.missingResidues:
self.missingResidues[key] = []
self.missingResidues[key].append(sequence[i])
else:
index += 1
......@@ -337,7 +349,7 @@ class PDBFixer(object):
# Add missing terminal atoms.
terminals = []
if residue == chainResidues[-1] and not any(r[0] == chain.index and r[1] >= len(chainResidues) for r in self.missingResidues):
if residue == chainResidues[-1] and (chain.index, len(chainResidues)) not in self.missingResidues:
templateNames = set(atom.name for atom in template.topology.atoms())
if 'OXT' not in atomNames and all(name in templateNames for name in ['C', 'O', 'CA']):
terminals.append('OXT')
......
......@@ -10,7 +10,20 @@ def startPageCallback(parameters, handler):
global fixer
pdb = PdbStructure(parameters['pdbfile'].value.splitlines())
fixer = PDBFixer(pdb)
displayConvertResiduesPage()
displayDeleteChainsPage()
def deleteChainsPageCallback(parameters, handler):
numChains = len(list(fixer.topology.chains()))
deleteIndices = [i for i in range(numChains) if 'include'+str(i) not in parameters]
fixer.removeChains(deleteIndices)
displayAddResiduesPage()
def addResiduesPageCallback(parameters, handler):
keys = [key for key in sorted(fixer.missingResidues)]
for i, key in enumerate(keys):
if 'add'+str(i) not in parameters:
del fixer.missingResidues[key]
displayMissingAtomsPage()
def convertResiduesPageCallback(parameters, handler):
fixer.nonstandardResidues = [residue for i, residue in enumerate(fixer.nonstandardResidues) if 'convert'+str(i) in parameters]
......@@ -47,9 +60,75 @@ PDB File: <input type="file" name="pdbfile"/>
</html>
""")
def displayDeleteChainsPage():
uiserver.setCallback(deleteChainsPageCallback)
numChains = len(list(fixer.topology.chains()))
if numChains < 2:
displayAddResiduesPage()
return
table = ""
proteinResidues = ['ALA', 'ASN', 'CYS', 'GLU', 'HIS', 'LEU', 'MET', 'PRO', 'THR', 'TYR.pdb ARG', 'ASP', 'GLN', 'GLY', 'ILE', 'LYS', 'PHE', 'SER', 'TRP', 'VAL']
rnaResidues = ['A', 'G', 'C', 'U']
dnaResidues = ['DA', 'DG', 'DC', 'DT']
for i, chain in enumerate(fixer.topology.chains()):
residues = list(r.name for r in chain.residues())
if any(r in proteinResidues for r in residues):
content = "Protein"
elif any(r in rnaResidues for r in residues):
content = "RNA"
elif any(r in dnaResidues for r in residues):
content = "DNA"
else:
content = ', '.join(set(residues))
table += ' <tr><td>%d</td><td>%d</td><td>%s</td><td><input type="checkbox" name="include%d" checked></td></tr>\n' % (chain.index+1, len(residues), content, i)
uiserver.setContent("""
<html>
<head><title>PDB Fixer</title></head>
<body>
This PDB file contains %d chains. Select which ones to include.
<p>
<form method="post" action="/">
<table border="1">
<tr><th>Chain</th><th># Residues</th><th>Content</th><th>Include?</th></tr>
%s
</table>
<p>
<input type="submit" value="Continue"/>
</form>
</body>
<html>
""" % (numChains, table))
def displayAddResiduesPage():
uiserver.setCallback(addResiduesPageCallback)
fixer.findMissingResidues()
if len(fixer.missingResidues) == 0:
displayConvertResiduesPage()
return
table = ""
for i, key in enumerate(sorted(fixer.missingResidues)):
residues = fixer.missingResidues[key]
table += ' <tr><td>%d</td><td>%d to %d</td><td>%s</td><td><input type="checkbox" name="add%d" checked></td></tr>\n' % (key[0]+1, key[1]+1, key[1]+len(residues), ', '.join(residues), i)
uiserver.setContent("""
<html>
<head><title>PDB Fixer</title></head>
<body>
The SEQRES records in this PDB file include residues that are missing from the atom data section. Do you want to add the missing residues?
<p>
<form method="post" action="/">
<table border="1">
<tr><th>Chain</th><th>Residue Positions</th><th>Sequence</th><th>Add?</th></tr>
%s
</table>
<p>
<input type="submit" value="Continue"/>
</form>
</body>
<html>
""" % table)
def displayConvertResiduesPage():
uiserver.setCallback(convertResiduesPageCallback)
fixer.findMissingResidues()
fixer.findNonstandardResidues()
if len(fixer.nonstandardResidues) == 0:
displayMissingAtomsPage()
......@@ -105,7 +184,7 @@ def displayMissingAtomsPage():
<body>
The following residues are missing heavy atoms, which will be added.
<p>
<form method="get" action="/">
<form method="post" action="/">
<table border="1">
<tr><th>Chain</th><th>Residue</th><th>Missing Atoms</th></tr>
%s
......@@ -125,7 +204,7 @@ def displayDownloadPage():
<body>
The fixed PDB file is ready to download.
<p>
<form method="get" action="/">
<form method="post" action="/">
<input type="submit" name="download" value="Download"/>
<input type="submit" name="newfile" value="Process Another File"/>
</form>
......
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