Source code for mordred.surface_area._sasa
from __future__ import division
from collections import defaultdict
import numpy as np
from ._mesh import SphereMesh
from .._util import atoms_to_numpy
from .._atomic_property import GetAtomicNumber, vdw_radii
[docs]
class SurfaceArea(object):
r"""calculate solvent accessible surface area.
:type radiuses: np.ndarray(dtype=float, shape=(N,))
:param radiuses: atomic radius + solvent radius vector
:type xyzs: np.ndarray(dtype=float, shape=(N, 3))
:param xyzs: atomic position matrix
:type level: int
:param level: mesh level. subdivide icosahedron n-1 times.
.. math::
N_{\rm points} = 5 \times 4^{level} - 8
"""
def __init__(self, radiuses, xyzs, level=4):
self.rads = radiuses
self.rads2 = radiuses ** 2
self.xyzs = xyzs
self._gen_neighbor_list()
self.sphere = SphereMesh(level).vertices.T
def _gen_neighbor_list(self):
r = self.rads[:, np.newaxis] + self.rads
d = np.sqrt(np.sum((self.xyzs[:, np.newaxis] - self.xyzs) ** 2, axis=2))
ns = defaultdict(list)
for i, j in np.transpose(np.nonzero(d <= r)):
if i == j:
continue
ns[i].append((j, d[i, j]))
for _, l in ns.items():
l.sort(key=lambda i: i[1])
self.neighbors = ns
[docs]
def atomic_sa(self, i):
r"""Calculate atomic surface area.
:type i: int
:param i: atom index
:rtype: float
"""
sa = 4.0 * np.pi * self.rads2[i]
neighbors = self.neighbors.get(i)
if neighbors is None:
return sa
XYZi = self.xyzs[i, np.newaxis].T
sphere = self.sphere * self.rads[i] + XYZi
N = sphere.shape[1]
for j, _ in neighbors:
XYZj = self.xyzs[j, np.newaxis].T
d2 = (sphere - XYZj) ** 2
mask = (d2[0] + d2[1] + d2[2]) > self.rads2[j]
sphere = np.compress(mask, sphere, axis=1)
return sa * sphere.shape[1] / N
[docs]
def surface_area(self):
r"""Calculate all atomic surface area.
:rtype: [float]
"""
return [self.atomic_sa(i) for i in range(len(self.rads))]
[docs]
@classmethod
def from_mol(cls, mol, conformer=-1, solvent_radius=1.4, level=4):
r"""Construct SurfaceArea from rdkit Mol type.
:type mol: rdkit.Chem.Mol
:param mol: input molecule
:type conformer: int
:param conformer: conformer id
:type solvent_radius: float
:param solvent_radius: solvent radius
:type level: int
:param level: mesh level
:rtype: SurfaceArea
"""
rs = atoms_to_numpy(lambda a: vdw_radii[a.GetAtomicNum()] + solvent_radius, mol)
conf = mol.GetConformer(conformer)
ps = np.array([list(conf.GetAtomPosition(i)) for i in range(mol.GetNumAtoms())])
return cls(rs, ps, level)
[docs]
@classmethod
def from_pdb(cls, pdb, solvent_radius=1.4, level=3):
try:
from Bio.PDB import PDBParser
except ImportError:
raise ImportError("There isn't biopython package.")
rs = []
coords = []
for atom in PDBParser().get_structure("", pdb).get_atoms():
rs.append(vdw_radii[GetAtomicNumber(atom.element)] + solvent_radius)
coords.append(atom.coord)
return cls(np.array(rs), np.array(coords), level)