Spaces:
No application file
No application file
File size: 4,437 Bytes
b7731cd |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 |
# Copyright (C) 2002, 2004 Thomas Hamelryck ([email protected])
# All rights reserved.
#
# This file is part of the Biopython distribution and governed by your
# choice of the "Biopython License Agreement" or the "BSD 3-Clause License".
# Please see the LICENSE file that should have been included as part of this
# package.
"""Fast atom neighbor lookup using a KD tree (implemented in C)."""
import numpy
from Bio.PDB.PDBExceptions import PDBException
from Bio.PDB.Selection import unfold_entities, entity_levels, uniqueify
class NeighborSearch:
"""Class for neighbor searching.
This class can be used for two related purposes:
1. To find all atoms/residues/chains/models/structures within radius
of a given query position.
2. To find all atoms/residues/chains/models/structures that are within
a fixed radius of each other.
NeighborSearch makes use of the KDTree class implemented in C for speed.
"""
def __init__(self, atom_list, bucket_size=10):
"""Create the object.
Arguments:
- atom_list - list of atoms. This list is used in the queries.
It can contain atoms from different structures.
- bucket_size - bucket size of KD tree. You can play around
with this to optimize speed if you feel like it.
"""
from Bio.PDB.kdtrees import KDTree
self.atom_list = atom_list
# get the coordinates
coord_list = [a.get_coord() for a in atom_list]
# to Nx3 array of type float
self.coords = numpy.array(coord_list, dtype="d")
assert bucket_size > 1
assert self.coords.shape[1] == 3
self.kdt = KDTree(self.coords, bucket_size)
# Private
def _get_unique_parent_pairs(self, pair_list):
# translate a list of (entity, entity) tuples to
# a list of (parent entity, parent entity) tuples,
# thereby removing duplicate (parent entity, parent entity)
# pairs.
# o pair_list - a list of (entity, entity) tuples
parent_pair_list = []
for (e1, e2) in pair_list:
p1 = e1.get_parent()
p2 = e2.get_parent()
if p1 == p2:
continue
elif p1 < p2:
parent_pair_list.append((p1, p2))
else:
parent_pair_list.append((p2, p1))
return uniqueify(parent_pair_list)
# Public
def search(self, center, radius, level="A"):
"""Neighbor search.
Return all atoms/residues/chains/models/structures
that have at least one atom within radius of center.
What entity level is returned (e.g. atoms or residues)
is determined by level (A=atoms, R=residues, C=chains,
M=models, S=structures).
Arguments:
- center - Numeric array
- radius - float
- level - char (A, R, C, M, S)
"""
if level not in entity_levels:
raise PDBException(f"{level}: Unknown level")
center = numpy.require(center, dtype="d", requirements="C")
if center.shape != (3,):
raise Exception("Expected a 3-dimensional NumPy array")
points = self.kdt.search(center, radius)
atom_list = [self.atom_list[point.index] for point in points]
if level == "A":
return atom_list
else:
return unfold_entities(atom_list, level)
def search_all(self, radius, level="A"):
"""All neighbor search.
Search all entities that have atoms pairs within
radius.
Arguments:
- radius - float
- level - char (A, R, C, M, S)
"""
if level not in entity_levels:
raise PDBException(f"{level}: Unknown level")
neighbors = self.kdt.neighbor_search(radius)
atom_list = self.atom_list
atom_pair_list = []
for neighbor in neighbors:
i1 = neighbor.index1
i2 = neighbor.index2
a1 = atom_list[i1]
a2 = atom_list[i2]
atom_pair_list.append((a1, a2))
if level == "A":
# return atoms
return atom_pair_list
next_level_pair_list = atom_pair_list
for next_level in ["R", "C", "M", "S"]:
next_level_pair_list = self._get_unique_parent_pairs(next_level_pair_list)
if level == next_level:
return next_level_pair_list
|