CombFold / scripts /libs /utils_pdb.py
wuxing0105's picture
Upload folder using huggingface_hub
8efb4bd verified
Raw
History Blame Contribute Delete
6.59 kB
from functools import lru_cache
from typing import Tuple, List
import Bio.PDB
import numpy as np
import scipy.spatial
from libs.utils_classes import INTERFACE_MIN_ATOM_DIST, SubunitPdbInfo
@lru_cache(5)
def get_pdb_model_readonly(pdb_path: str):
pdb_parser = Bio.PDB.PDBParser(QUIET=True)
pdb_struct = pdb_parser.get_structure("original_pdb", pdb_path)
assert len(list(pdb_struct)) == 1, f"Too many models! {pdb_path}"
return next(iter(pdb_struct))
def get_pdb_model_no_cache(pdb_path: str):
pdb_parser = Bio.PDB.PDBParser(QUIET=True)
pdb_struct = pdb_parser.get_structure("original_pdb", pdb_path)
assert len(list(pdb_struct)) == 1, f"Too many models! {pdb_path}"
return next(iter(pdb_struct))
def extract_chains(pdb_path: str, output_path: str, chains_to_save: List[str]):
pdb_parser = Bio.PDB.PDBParser(QUIET=True)
pdb_struct = pdb_parser.get_structure("original_pdb", pdb_path)
assert len(list(pdb_struct)) == 1, "can't extract if more than one model"
model = next(iter(pdb_struct))
chains = list(model.get_chains())
for chain_id in chains_to_save:
assert len([c for c in chains if c.id == chain_id]) == 1, f"Missing chain {chain_id} from {pdb_path}"
for chain in chains:
if chain.id not in chains_to_save:
model.detach_child(chain.id)
io = Bio.PDB.PDBIO()
io.set_structure(pdb_struct)
io.save(output_path)
def extract_pdb_info(pdb_path: str, pdb_info: SubunitPdbInfo, output_path: str):
pdb_parser = Bio.PDB.PDBParser(QUIET=True)
pdb_struct = pdb_parser.get_structure("original_pdb", pdb_path)
assert len(list(pdb_struct)) == 1, "can't extract if more than one model"
model = next(iter(pdb_struct))
chains = list(model.get_chains())
assert len([c for c in chains if c.id == pdb_info.chain_id]) == 1, f"Missing chain {pdb_info.chain_id}"
for chain in chains:
if chain.id != pdb_info.chain_id:
model.detach_child(chain.id)
res_to_remove = []
for res in model.get_residues():
if not (pdb_info.chain_residue_id <= res.id[1] < pdb_info.chain_residue_id + pdb_info.length):
res_to_remove.append(res)
for res in res_to_remove:
res.parent.detach_child(res.id)
io = Bio.PDB.PDBIO()
io.set_structure(pdb_struct)
io.save(output_path)
def copy_pdb_rename_chain(original_pdb_path: str, new_chain_name: str, output_path: str):
pdb_parser = Bio.PDB.PDBParser(QUIET=True)
pdb_struct = pdb_parser.get_structure("original_pdb", original_pdb_path)
model = next(iter(pdb_struct))
chains = list(model.get_chains())
assert len(chains) == 1
assert len(new_chain_name) == 1, f"Bad chain name provided {new_chain_name}"
chains[0].id = new_chain_name
io = Bio.PDB.PDBIO()
io.set_structure(pdb_struct)
io.save(output_path)
def copy_pdb_add_offset(original_pdb_path: str, offset: int, output_path: str):
pdb_parser = Bio.PDB.PDBParser(QUIET=True)
pdb_struct = pdb_parser.get_structure("original_pdb", original_pdb_path)
model = next(iter(pdb_struct))
chains = list(model.get_chains())
assert len(chains) == 1
# prevent "already used for a sibling of this entity" error
for res in chains[0].get_residues():
res.id = (res.id[0], res.id[1] + 10**4, res.id[2])
for res in chains[0].get_residues():
res.id = (res.id[0], res.id[1] - 10**4 + offset, res.id[2])
io = Bio.PDB.PDBIO()
io.set_structure(pdb_struct)
io.save(output_path)
def copy_pdb_set_start_offset(original_pdb_path: str, offset: int, output_path: str):
pdb_parser = Bio.PDB.PDBParser(QUIET=True)
pdb_struct = pdb_parser.get_structure("original_pdb", original_pdb_path)
model = next(iter(pdb_struct))
current_offset = min([res.id[1] for res in model.get_residues()])
copy_pdb_add_offset(original_pdb_path, offset - current_offset, output_path)
def is_chains_coords_close(chain1_ca: np.ndarray, chain2_ca: np.ndarray):
if len(chain1_ca) == 0 or len(chain2_ca) == 0:
return False
return np.min(scipy.spatial.distance.cdist(chain1_ca, chain2_ca)) < INTERFACE_MIN_ATOM_DIST
def are_subunits_close_in_pdb(pdb_path: str, subunit1: SubunitPdbInfo, subunit2: SubunitPdbInfo):
model = get_pdb_model_readonly(pdb_path)
chains = list(model.get_chains())
chain1 = [i for i in chains if i.get_id() == subunit1.chain_id][0]
chain2 = [i for i in chains if i.get_id() == subunit2.chain_id][0]
chain1_ca = np.array([res['CA'].coord for res in chain1.get_residues() if 'CA' in res
and subunit1.chain_residue_id <= res.get_id()[1]
<= subunit1.chain_residue_id + subunit1.length])
chain2_ca = np.array([res['CA'].coord for res in chain2.get_residues() if 'CA' in res
and subunit2.chain_residue_id <= res.get_id()[1]
<= subunit2.chain_residue_id + subunit2.length])
assert len(chain1_ca) > 0 and len(chain2_ca) > 0, f"No CA atoms found {subunit1} {subunit2} {pdb_path}"
return is_chains_coords_close(chain1_ca, chain2_ca)
def get_interface_res_ids(pdb_path: str, subunit1: SubunitPdbInfo, subunit2: SubunitPdbInfo) \
-> Tuple[List[int], List[int]]:
model = get_pdb_model_readonly(pdb_path)
chains = list(model.get_chains())
chain1 = [i for i in chains if i.get_id() == subunit1.chain_id][0]
chain2 = [i for i in chains if i.get_id() == subunit2.chain_id][0]
chain1_res = [res for res in chain1.get_residues() if 'CA' in res and subunit1.chain_residue_id <= res.get_id()[1]
<= subunit1.chain_residue_id + subunit1.length]
chain2_res = [res for res in chain2.get_residues() if 'CA' in res and subunit2.chain_residue_id <= res.get_id()[1]
<= subunit2.chain_residue_id + subunit2.length]
chain1_ca = np.array([res['CA'].coord for res in chain1_res])
chain2_ca = np.array([res['CA'].coord for res in chain2_res])
close_residues = np.argwhere(scipy.spatial.distance.cdist(chain1_ca, chain2_ca) < INTERFACE_MIN_ATOM_DIST)
chain1_interface, chain2_interface = set(), set()
for i, j in close_residues:
chain1_interface.add(chain1_res[i].id[1] - subunit1.chain_residue_id)
chain2_interface.add(chain2_res[j].id[1] - subunit2.chain_residue_id)
return sorted(list(chain1_interface)), sorted(list(chain2_interface))