OneScience-Group/SurfDock
025
1# Copyright 2021 DeepMind Technologies Limited2#3# Licensed under the Apache License, Version 2.0 (the "License");4# you may not use this file except in compliance with the License.5# You may obtain a copy of the License at6#7# http://www.apache.org/licenses/LICENSE-2.08#9# Unless required by applicable law or agreed to in writing, software10# distributed under the License is distributed on an "AS IS" BASIS,11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.12# See the License for the specific language governing permissions and13# limitations under the License.14 15"""Cleans up a PDB file using pdbfixer in preparation for OpenMM simulations.16fix_pdb uses a third-party tool. We also support fixing some additional edge17cases like removing chains of length one (see clean_structure).18"""19import io20 21from pdbfixer import PDBFixer22from openmm import app23from openmm.app import element24from openmm.app.internal import pdbstructure25 26def pdb_to_structure(pdb_str):27 handle = io.StringIO(pdb_str)28 return pdbstructure.PdbStructure(handle)29 30def fix_pdb(pdbfile, alterations_info):31 """Apply pdbfixer to the contents of a PDB file; return a PDB string result.32 1) Replaces nonstandard residues.33 2) Removes heterogens (non protein residues) including water.34 3) Adds missing residues and missing atoms within existing residues.35 4) Adds hydrogens assuming pH=7.0.36 5) KeepIds is currently true, so the fixer must keep the existing chain and37 residue identifiers. This will fail for some files in wider PDB that have38 invalid IDs.39 Args:40 pdbfile: Input PDB file handle.41 alterations_info: A dict that will store details of changes made.42 Returns:43 A PDB string representing the fixed structure.44 """45 fixer = PDBFixer(pdbfile)46 fixer.findNonstandardResidues()47 alterations_info['nonstandard_residues'] = fixer.nonstandardResidues48 fixer.replaceNonstandardResidues()49 _remove_heterogens(fixer, alterations_info, keep_water=False)50 fixer.findMissingResidues()51 alterations_info['missing_residues'] = fixer.missingResidues52 fixer.findMissingAtoms()53 alterations_info['missing_heavy_atoms'] = fixer.missingAtoms54 alterations_info['missing_terminals'] = fixer.missingTerminals55 fixer.addMissingAtoms(seed=0)56 fixer.addMissingHydrogens()57 out_handle = io.StringIO()58 app.PDBFile.writeFile(fixer.topology, fixer.positions, out_handle,59 keepIds=True)60 return out_handle.getvalue()61 62def clean_structure(pdb_structure, alterations_info):63 """Applies additional fixes to an OpenMM structure, to handle edge cases.64 Args:65 pdb_structure: An OpenMM structure to modify and fix.66 alterations_info: A dict that will store details of changes made.67 """68 _replace_met_se(pdb_structure, alterations_info)69 _remove_chains_of_length_one(pdb_structure, alterations_info)70 71 72def _remove_heterogens(fixer, alterations_info, keep_water):73 """Removes the residues that Pdbfixer considers to be heterogens.74 Args:75 fixer: A Pdbfixer instance.76 alterations_info: A dict that will store details of changes made.77 keep_water: If True, water (HOH) is not considered to be a heterogen.78 """79 initial_resnames = set()80 for chain in fixer.topology.chains():81 for residue in chain.residues():82 initial_resnames.add(residue.name)83 fixer.removeHeterogens(keepWater=keep_water)84 final_resnames = set()85 for chain in fixer.topology.chains():86 for residue in chain.residues():87 final_resnames.add(residue.name)88 alterations_info['removed_heterogens'] = (89 initial_resnames.difference(final_resnames))90 91 92def _replace_met_se(pdb_structure, alterations_info):93 """Replace the Se in any MET residues that were not marked as modified."""94 modified_met_residues = []95 for res in pdb_structure.iter_residues():96 name = res.get_name_with_spaces().strip()97 if name == 'MET':98 s_atom = res.get_atom('SD')99 if s_atom.element_symbol == 'Se':100 s_atom.element_symbol = 'S'101 s_atom.element = element.get_by_symbol('S')102 modified_met_residues.append(s_atom.residue_number)103 alterations_info['Se_in_MET'] = modified_met_residues104 105 106def _remove_chains_of_length_one(pdb_structure, alterations_info):107 """Removes chains that correspond to a single amino acid.108 A single amino acid in a chain is both N and C terminus. There is no force109 template for this case.110 Args:111 pdb_structure: An OpenMM pdb_structure to modify and fix.112 alterations_info: A dict that will store details of changes made.113 """114 removed_chains = {}115 for model in pdb_structure.iter_models():116 valid_chains = [c for c in model.iter_chains() if len(c) > 1]117 invalid_chain_ids = [c.chain_id for c in model.iter_chains() if len(c) <= 1]118 model.chains = valid_chains119 for chain_id in invalid_chain_ids:120 model.chains_by_id.pop(chain_id)121 removed_chains[model.number] = invalid_chain_ids122 alterations_info['removed_chains'] = removed_chains