"""
Generate the vector features for pharmacophores from a rdkit conformer.
Adapted from rdkit:
https://github.com/rdkit/rdkit/blob/master/rdkit/Chem/Features/FeatDirUtilsRD.py
https://github.com/rdkit/rdkit/blob/master/rdkit/Chem/Features/ShowFeats.py
Changed to return anchor position and relative unit vector.
"""
from rdkit import Chem
from rdkit.Geometry.rdGeometry import Point3D
# pharmacophores
from rdkit.Chem.Features import FeatDirUtilsRD as FeatDirUtils
## Streamlined functions
[docs]
def GetAromaticFeatVects(conf, featAtoms, featLoc, return_both: bool = False, scale=1.0):
"""
Compute the direction vector for an aromatic feature
Changed: only return one vector, process later for visualization and scoring
Arguments
---------
conf : a conformer
featAtoms : list of atom IDs that make up the feature
featLoc : location of the aromatic feature specified as point3d
return_both : bool for whether to return both vectors or just one.
scale : the size of the direction vector
Returns
-------
Tuple
list of anchor position(s) as rdkit Point3D
list of relative unit vector(s) as rdkit Point3D
"""
head = featLoc
ats = [conf.GetAtomPosition(x) for x in featAtoms]
v1 = ats[0] - head
v2 = ats[1] - head
norm1 = v1.CrossProduct(v2)
norm1.Normalize()
norm1 *= scale
if return_both:
norm2 = Point3D(0, 0, 0) - norm1
return [head]*2, [norm1, norm2]
else:
return [head], [norm1]
[docs]
def GetDonorFeatVects(conf, featAtoms, scale=1., exclude=[]):
"""
Get vectors for hydrogen bond donors in the direction of the hydrogens.
Arguments
---------
conf : rdkit Mol object with a conformer.
featAtoms : list containing rdkit Atom object of atom attributed as a donor.
scale : float (default = 1.) length of direction vector.
exclude : list of atom indices that should not be included as a donatable H.
Returns
-------
Tuple
list of anchor position(s) as rdkit Point3D or None
list of relative unit vector(s) as rdkit Point3D or None
list of neighboring hydrogens or None
"""
assert len(featAtoms) == 1
aid = featAtoms[0]
mol = conf.GetOwningMol()
cpt = conf.GetAtomPosition(aid)
nbrs = mol.GetAtomWithIdx(aid).GetNeighbors()
hyd_nbrs = [] # hydrogens
vectors = []
for nbr in nbrs:
if nbr.GetAtomicNum() == 1:
if nbr.GetIdx() in exclude:
continue
hyd_nbrs.append(nbr)
hnid = nbr.GetIdx()
vec = conf.GetAtomPosition(hnid)
vec -= cpt
vec.Normalize()
vec *= scale
vectors.append(vec)
if hyd_nbrs != []:
return [cpt]*len(vectors), vectors, hyd_nbrs
else:
return None, None, None
[docs]
def GetAcceptorFeatVects(conf: Chem.rdchem.Mol,
featAtoms: Chem.rdchem.Atom,
scale: float = 1.0):
"""
Get the anchor positions and relative unit vectors of an acceptor atom.
Assumes HBA's are only O and N as defined by smarts_features.fdef.
If HBA is not one of those, then it assumes the atom has one lone pair.
Parameters
----------
conf : Chem.Mol
RDKit Mol object with a conformer.
featAtoms : list
List containing RDKit Atom object of atom attributed as an acceptor.
scale : float, optional
Length of direction vector. Default is 1.0.
Returns
-------
tuple
(list of anchor position(s) as RDKit Point3D or [None],
list of relative unit vector(s) as RDKit Point3D or [None])
"""
assert len(featAtoms) == 1
atom_id = featAtoms[0]
mol = conf.GetOwningMol()
atom = mol.GetAtomWithIdx(atom_id)
nbrs = atom.GetNeighbors()
num_lone_pairs = 3
if atom.GetAtomicNum() == 7: # N
num_lone_pairs = 1
elif atom.GetAtomicNum() == 8: # O
num_lone_pairs = 2
hydrogens = []
heavy = []
for nbr in nbrs:
if nbr.GetAtomicNum() == 1:
hydrogens.append(nbr)
else:
heavy.append(nbr)
cpt = conf.GetAtomPosition(atom_id)
if num_lone_pairs == 1:
if len(nbrs) == 1: # linear
# triple bond
v1 = conf.GetAtomPosition(nbrs[0].GetIdx())
v1 -= cpt
v1.Normalize()
v1 *= (-1.0 * scale)
return [cpt], [v1]
elif len(nbrs) == 2: # sp2
v1 = FeatDirUtils._findAvgVec(conf, cpt, nbrs)
v1 *= (-1.0 * scale)
return [cpt], [v1]
elif len(nbrs) == 3: # sp3
cpt = conf.GetAtomPosition(atom_id)
out = FeatDirUtils._GetTetrahedralFeatVect(conf, atom_id, scale)
if out != ():
v1 = out[0][1] - cpt # need to subtract out the center to be relative
v1.Normalize()
v1 *= scale
return [cpt], [v1]
else:
# Means that this is planar so likely not an acceptor, remove it after
return None, None
else:
return None, None
elif num_lone_pairs == 2:
heavy_nbr = heavy[0]
if len(nbrs) == 1: # sp2
for a in heavy_nbr.GetNeighbors():
if a.GetIdx() != atom_id:
heavy_nbr_nbr = a # heavy atom's neighbor that isn't the acceptor
break
pt1 = conf.GetAtomPosition(heavy_nbr_nbr.GetIdx())
v1 = conf.GetAtomPosition(heavy_nbr.GetIdx())
pt1 -= v1
v1 -= cpt
rotAxis = v1.CrossProduct(pt1)
rotAxis.Normalize()
bv1 = FeatDirUtils.ArbAxisRotation(120, rotAxis, v1)
bv1.Normalize()
bv1 *= scale
bv2 = FeatDirUtils.ArbAxisRotation(-120, rotAxis, v1)
bv2.Normalize()
bv2 *= scale
return [cpt]*2, [bv1, bv2]
if len(nbrs) == 2: # sp3
bvec = FeatDirUtils._findAvgVec(conf, cpt, nbrs)
bvec *= (-1.0 * scale)
# we will create two vectors by rotating bvec by half the tetrahedral angle in either directions
v1 = conf.GetAtomPosition(nbrs[0].GetIdx())
v1 -= cpt
v2 = conf.GetAtomPosition(nbrs[1].GetIdx())
v2 -= cpt
rotAxis = v1 - v2
rotAxis.Normalize()
bv1 = FeatDirUtils.ArbAxisRotation(54.5, rotAxis, bvec)
bv2 = FeatDirUtils.ArbAxisRotation(-54.5, rotAxis, bvec)
bv1.Normalize()
bv2.Normalize()
bv1 *= scale
bv2 *= scale
return [cpt]*2, [bv1, bv2]
else:
return None, None
elif num_lone_pairs == 3: # sp3 but do linear
# Just do opposite of single bond (i.e., F)
heavy_nbr = heavy[0]
if len(hydrogens) == 0:
v1 = conf.GetAtomPosition(heavy_nbr.GetIdx())
v1 -= cpt
v1.Normalize()
v1 *= (-1.0 * scale)
return [cpt], [v1]
else:
return None, None
[docs]
def GetHalogenFeatVects(conf: Chem.rdchem.Mol,
featAtoms: Chem.rdchem.Atom,
scale: float = 1.0):
"""
Get the anchor positions and relative unit vectors of a halogen atom.
Assumes only one connection.
Arguments
---------
conf : rdkit Mol object with a conformer.
featAtoms : list containing rdkit Atom object of atom attributed as an acceptor.
scale : float (default = 1.) length of direction vector.
Returns
-------
Tuple
list of anchor position(s) as rdkit Point3D or [None]
list of relative unit vector(s) as rdkit Point3D or [None]
"""
assert len(featAtoms) == 1
atom_id = featAtoms[0]
mol = conf.GetOwningMol()
atom = mol.GetAtomWithIdx(atom_id)
nbrs = atom.GetNeighbors()
cpt = conf.GetAtomPosition(atom_id)
# Just do opposite of single bond
heavy_nbr = nbrs[0]
v1 = conf.GetAtomPosition(heavy_nbr.GetIdx())
v1 -= cpt
v1.Normalize()
v1 *= (-1.0 * scale)
return [cpt], [v1]
###################################################
## Modular functions from rdkit that were edited ##
## No longer used, but potentially more general ###
###################################################
# Hydrogen Bond Donors
[docs]
def GetDonor1FeatVects_single(conf, featAtoms, scale=1.):
"""
Get the direction vectors for Donor of type 1.
Made to generate a single vector representation.
This is a donor with one heavy atom. It is not clear where we should we should be putting the
direction vector for this. It should probably be a cone. In this case we will just use the
direction vector from the donor atom to the heavy atom.
Changed: conditioning based on the number of hydrogens
1. If 1 hydrogen, vector should point in the direction of the hydrogen.
2. If 2 hydrogens, vector should point in a bisecting direction of the two hydrogens.
3. If 3 hydrogens, point in the direction of the bond.
Arguments
---------
conf - rdkit Mol object with conformer
featAtoms - list of atoms that are part of the feature
scale - float for length of the direction vector (default = 1.0)
Returns
-------
Tuple
anchor position as rdkit Point3D or None
relative unit vector(s) as rdkit Point3D or None
list of hydrogen rdkit Atom objects
"""
assert len(featAtoms) == 1
aid = featAtoms[0]
mol = conf.GetOwningMol()
nbrs = mol.GetAtomWithIdx(aid).GetNeighbors()
hyd_nbrs = [] # hydrogens
heavy_nbr = -1 # hnbr in rdkit
for nbr in nbrs:
if nbr.GetAtomicNum() == 1:
hyd_nbrs.append(nbr)
else:
heavy_nbr = nbr.GetIdx()
cpt = conf.GetAtomPosition(aid)
# Point in direction of hydrogen
if len(hyd_nbrs) == 1:
bvec = conf.GetAtomPosition(hyd_nbrs[0].GetIdx())
bvec -= cpt
bvec.Normalize()
bvec *= scale
return cpt, bvec, hyd_nbrs
# Point in "average" (bisected) direction of 2 hydrogens
elif len(hyd_nbrs) == 2:
bvec = FeatDirUtils._findAvgVec(conf, cpt, hyd_nbrs)
bvec *= scale
# Removed conditional for generating 2 vectors for sp3 oxygen to maintain one average vector
return cpt, bvec, hyd_nbrs
# Otherwise, vector is the one from donor atom to heavy atom. 3 hydrogens
cpt = conf.GetAtomPosition(aid)
v1 = conf.GetAtomPosition(heavy_nbr)
v1 -= cpt
v1.Normalize()
v1 *= (-1.0 * scale)
return cpt, v1, hyd_nbrs
[docs]
def GetDonor2FeatVects_single(conf, featAtoms, scale=1.):
"""
Get the direction vectors for Donor of type 2.
Made to generate a single vector representation.
This is a donor with two heavy atoms as neighbors. The atom may are may not have
hydrogen on it. Here are the situations with the neighbors that will be considered here
1. two heavy atoms and two hydrogens: we will assume a sp3 arrangement here
2. two heavy atoms and one hydrogen: this can either be sp2 or sp3
3. two heavy atoms and no hydrogens
Changed: conditioning based on the number of hydrogens
1. For case 1, point in the direction bisecting the two hydrogens.
2. For case 2, point in the direction of the hydrogen.
3. For case 3, no changes.
Arguments
---------
conf : rdkit Mol object with conformer
featAtoms : list of atoms that are part of the feature
scale : float for length of the direction vector (default = 1.0)
Returns
-------
Tuple
anchor position as rdkit Point3D or None
relative unit vector(s) as rdkit Point3D or None
list of hydrogen rdkit Atom objects
"""
assert len(featAtoms) == 1
aid = featAtoms[0]
mol = conf.GetOwningMol()
cpt = conf.GetAtomPosition(aid)
# find the two atoms that are neighbors of this atoms
nbrs = list(mol.GetAtomWithIdx(aid).GetNeighbors())
assert len(nbrs) >= 2
hydrogens = []
heavy = []
for nbr in nbrs:
if nbr.GetAtomicNum() == 1:
hydrogens.append(nbr)
else:
heavy.append(nbr)
# Case 3: not sure when this would be triggered
if len(nbrs) == 2:
# there should be no hydrogens in this case
assert len(hydrogens) == 0
# in this case the direction is the opposite of the average vector of the two neighbors
bvec = FeatDirUtils._findAvgVec(conf, cpt, heavy)
bvec *= (-1.0 * scale)
return cpt, bvec, None
# Case 2
if len(nbrs) == 3:
assert len(hydrogens) == 1
# this is a little more tricky we have to check if the hydrogen is in the plane of the
# two heavy atoms (i.e. sp2 arrangement) or out of plane (sp3 arrangement)
# One of the directions will be from hydrogen atom to the heavy atom
hid = hydrogens[0].GetIdx()
bvec = conf.GetAtomPosition(hid)
bvec -= cpt
bvec.Normalize()
bvec *= scale
if FeatDirUtils._checkPlanarity(conf, cpt, nbrs, tol=1.0e-2):
# only the hydrogen atom direction needs to be used
return cpt, bvec, hydrogens
# we have a non-planar configuration - we will assume sp3 and compute a second direction vector
# Changed since we constrain to 1 vector
return cpt, bvec, hydrogens
# Case 1
if len(nbrs) >= 4:
# Changed -> take the bisecting vector (average) to only use 1 vector
vecs = []
for hid in hydrogens:
hid = hid.GetIdx()
bvec = conf.GetAtomPosition(hid)
bvec -= cpt
vecs.append(bvec.Normalize())
bisec_vec = sum(*vecs)
bisec_vec.Normalize()
return cpt, bisec_vec, hydrogens
return None, None, None
[docs]
def GetDonor3FeatVects_single(conf, featAtoms, scale=1.0):
"""
Get the direction vectors for Donor of type 3.
Made to generate a single vector representation.
This is a donor with three heavy atoms as neighbors. We will assume
a tetrahedral arrangement of these neighbors. So the direction we are seeking
is the last fourth arm of the sp3 arrangement
Changed: Return anchor and relative unit vector tuple
Arguments
---------
conf : rdkit Mol object with conformer
featAtoms : list of atoms that are part of the feature
scale : float for length of the direction vector (default = 1.0)
Returns
-------
Tuple
anchor position as rdkit Point3D or None
relative unit vector(s) as rdkit Point3D or None
list of hydrogen rdkit Atom objects
"""
assert len(featAtoms) == 1
aid = featAtoms[0]
cpt = conf.GetAtomPosition(aid)
out = FeatDirUtils._GetTetrahedralFeatVect(conf, aid, scale)
nbrs = conf.GetAtomWithIdx(aid).GetNeighbors()
hydrogens = []
for nbr in nbrs:
if nbr.GetAtomicNum() == 1:
hydrogens.append(nbr)
if out != ():
if hydrogens == []:
hydrogens = None
return cpt, out[0][1] - cpt, hydrogens # need to subtract out the center
else:
# Means that this is planar so likely not an donor, remove it after
return None, None, None
# Hydrogen bond acceptors
[docs]
def GetAcceptor1FeatVects_single(conf, featAtoms, scale=1.):
"""
Get the direction vectors for Acceptor of type 1 (single vector representation).
This is an acceptor with one heavy atom neighbor. There are two possibilities:
- The bond to the heavy atom is a single bond (e.g. CO): We use the inversion
of this bond direction and mark it as a 'cone'.
- The bond to the heavy atom is a double bond (e.g. C=O): We have two possible
directions except in some special cases (e.g. SO2) where we use bond direction.
Notes
-----
Modified to condition on the number of hydrogens with methanamine fix:
- Case 1: If one hydrogen, vector points in the opposite direction of the bisection
of the acute angle formed by the heavy-acceptor-hydrogen. If two hydrogens,
assume sp3 and project in that lone-pair direction. If not tetrahedral,
return None.
- Case 2: Return the bisecting vector of the two lone-pairs.
Parameters
----------
conf : Chem.Mol
RDKit Mol object with conformer.
featAtoms : list
List of atoms that are part of the feature.
scale : float, optional
Length of the direction vector. Default is 1.0.
Returns
-------
tuple
(anchor position as RDKit Point3D or None,
relative unit vector(s) as RDKit Point3D or None)
"""
assert len(featAtoms) == 1
aid = featAtoms[0]
mol = conf.GetOwningMol()
nbrs = mol.GetAtomWithIdx(aid).GetNeighbors()
cpt = conf.GetAtomPosition(aid)
hyd_nbrs = [] # hydrogens
heavyAt = -1
# find the adjacent heavy atom and hydrogens
for nbr in nbrs:
if nbr.GetAtomicNum() == 1:
hyd_nbrs.append(nbr)
else:
heavyAt = nbr
# before this was ``>`` which doesn't make sense
singleBnd = mol.GetBondBetweenAtoms(aid, heavyAt.GetIdx()).GetBondType() == Chem.BondType.SINGLE
# special scale - if the heavy atom is a sulfur (we should proabably check phosphorous as well)
sulfur = heavyAt.GetAtomicNum() == 16
methanamine = mol.GetBondBetweenAtoms(aid, heavyAt.GetIdx()).GetBondType() == Chem.BondType.DOUBLE and len(hyd_nbrs) == 1
if singleBnd or sulfur or methanamine:
if len(hyd_nbrs) == 1:
bvec = FeatDirUtils._findAvgVec(conf, cpt, [heavyAt, hyd_nbrs[0]])
bvec *= (-1 * scale)
return cpt, bvec
elif len(hyd_nbrs) == 2:
out = FeatDirUtils._GetTetrahedralFeatVect(conf, aid, scale)
if out != ():
return cpt, out[0][1] - cpt # need to subtract out the center
else:
# Means that this is planar so likely not an acceptor, remove it after
return None, None
# Changed -> Assume sp2 (like the original code) but rather than get the two
# lone pair directions just do the vector along the double bond axis to use only one vector
v1 = conf.GetAtomPosition(heavyAt.GetIdx())
v1 -= cpt
v1.Normalize()
v1 *= (-1.0 * scale)
return cpt, v1
[docs]
def GetAcceptor2FeatVects_single(conf, featAtoms, scale=1.):
"""
Get the direction vectors for Acceptor of type 2.
Made to generate a single vector representation.
This is the acceptor with two adjacent heavy atoms. We will special case a few things here.
If the acceptor atom is an oxygen we will assume a sp3 hybridization
the acceptor directions (two of them)
reflect that configurations. Otherwise the direction vector in plane with the neighboring
heavy atoms
Changed: Only generate one vector
Rather than generating 2 vectors for sp3 oxygen, just keep the average vector.
Arguments
---------
conf : rdkit Mol object with conformer
featAtoms : list of atoms that are part of the feature
scale : float for length of the direction vector (default = 1.0)
Returns
-------
Tuple
anchor position as rdkit Point3D or None
relative unit vector(s) as rdkit Point3D or None
"""
assert len(featAtoms) == 1
aid = featAtoms[0]
cpt = conf.GetAtomPosition(aid)
mol = conf.GetOwningMol()
nbrs = list(mol.GetAtomWithIdx(aid).GetNeighbors())
hydrogens = []
heavy = []
for nbr in nbrs:
if nbr.GetAtomicNum() == 1:
hydrogens.append(nbr)
else:
heavy.append(nbr)
if len(hydrogens) == 0:
bvec = FeatDirUtils._findAvgVec(conf, cpt, heavy)
elif len(hydrogens) == 1:
bvec = FeatDirUtils._findAvgVec(conf, cpt, nbrs)
bvec *= (-1.0 * scale)
# Changed -- Removed conditional for generating 2 vectors for sp3 oxygen to maintain one average vector
return cpt, bvec
[docs]
def GetAcceptor3FeatVects_single(conf, featAtoms, scale=1.0):
"""
Get the direction vectors for Donor of type 3.
Made to generate a single vector representation.
This is a donor with three heavy atoms as neighbors. We will assume
a tetrahedral arrangement of these neighbors. So the direction we are seeking
is the last fourth arm of the sp3 arrangement
Changed: to return anchor and relative unit vector tuple
Arguments
---------
conf : rdkit Mol object with conformer
featAtoms : list of atoms that are part of the feature
scale : float for length of the direction vector (default = 1.0)
Returns
-------
Tuple
anchor position as rdkit Point3D or None
relative unit vector(s) as rdkit Point3D or None
"""
assert len(featAtoms) == 1
aid = featAtoms[0]
cpt = conf.GetAtomPosition(aid)
out = FeatDirUtils._GetTetrahedralFeatVect(conf, aid, scale)
if out != ():
return cpt, out[0][1] - cpt # need to subtract out the center to be relative
else:
# Means that this is planar so likely not an acceptor, remove it after
return None, None
################### MULTI-VECTOR ##################
## THIS IS MORE TRUTHFUL TO RDKIT IMPLEMENTATION ##
# Hydrogen bond acceptors
[docs]
def GetAcceptor1FeatVects(conf, featAtoms, scale=1.):
"""
Get the direction vectors for Acceptor of type 1 (multi-vector representation).
This is an acceptor with one heavy atom neighbor. There are two possibilities:
- The bond to the heavy atom is a single bond (e.g. CO): We use the inversion
of this bond direction and mark it as a 'cone'.
- The bond to the heavy atom is a double bond (e.g. C=O): We have two possible
directions except in some special cases (e.g. SO2) where we use bond direction.
Notes
-----
Modified to change return format, with fixes for methanamine and two vectors
for hydroxyls.
Parameters
----------
conf : Chem.Mol
RDKit Mol object with a conformer.
featAtoms : list
List containing RDKit Atom object of atom attributed as an acceptor.
scale : float, optional
Length of direction vector. Default is 1.0.
Returns
-------
tuple
(list of anchor position(s) as RDKit Point3D or None,
list of relative unit vector(s) as RDKit Point3D or None)
"""
assert len(featAtoms) == 1
aid = featAtoms[0]
mol = conf.GetOwningMol()
nbrs = mol.GetAtomWithIdx(aid).GetNeighbors()
cpt = conf.GetAtomPosition(aid)
hyd_nbrs = [] # hydrogens
heavyAt = -1
# find the adjacent heavy atom and hydrogens
for nbr in nbrs:
if nbr.GetAtomicNum() == 1:
hyd_nbrs.append(nbr)
else:
heavyAt = nbr
# before this was ``>`` which doesn't make sense
singleBnd = mol.GetBondBetweenAtoms(aid, heavyAt.GetIdx()).GetBondType() == Chem.BondType.SINGLE
# special scale - if the heavy atom is a sulfur (we should proabably check phosphorous as well)
sulfur = heavyAt.GetAtomicNum() == 16
methanamine = mol.GetBondBetweenAtoms(aid, heavyAt.GetIdx()).GetBondType() == Chem.BondType.DOUBLE and len(hyd_nbrs) == 1
if singleBnd or sulfur or methanamine:
if len(hyd_nbrs) == 0 or sulfur:
v1 = conf.GetAtomPosition(heavyAt.GetIdx())
v1 -= cpt
v1.Normalize()
v1 *= (-1.0 * scale)
return [cpt], [v1]
elif len(hyd_nbrs) == 1 and methanamine:
bvec = FeatDirUtils._findAvgVec(conf, cpt, [heavyAt, hyd_nbrs[0]])
bvec *= (-1 * scale)
return [cpt], [bvec]
elif len(hyd_nbrs) == 1 and singleBnd:
# hydroxyl group
bvec = FeatDirUtils._findAvgVec(conf, cpt, nbrs)
bvec *= (-1.0 * scale)
# assume sp3
# we will create two vectors by rotating bvec by half the tetrahedral angle in either directions
v1 = conf.GetAtomPosition(hyd_nbrs[0].GetIdx())
v1 -= cpt
v2 = conf.GetAtomPosition(heavyAt.GetIdx())
v2 -= cpt
rotAxis = v1 - v2
rotAxis.Normalize()
bv1 = FeatDirUtils.ArbAxisRotation(54.5, rotAxis, bvec)
bv2 = FeatDirUtils.ArbAxisRotation(-54.5, rotAxis, bvec)
return [cpt]*2, [bv1, bv2]
elif len(hyd_nbrs) == 2:
out = FeatDirUtils._GetTetrahedralFeatVect(conf, aid, scale)
if out != ():
return [cpt], [out[0][1] - cpt] # need to subtract out the center
else:
# Means that this is planar so likely not an acceptor, remove it after
return None, None
# ok in this case we will assume that
# heavy atom is sp2 hybridized and the direction vectors (two of them)
# are in the same plane, we will find this plane by looking for one
# of the neighbors of the heavy atom
hvNbrs = heavyAt.GetNeighbors()
hvNbr = -1
for nbr in hvNbrs:
if nbr.GetIdx() != aid:
hvNbr = nbr
break
pt1 = conf.GetAtomPosition(hvNbr.GetIdx())
v1 = conf.GetAtomPosition(heavyAt.GetIdx())
pt1 -= v1
v1 -= cpt
rotAxis = v1.CrossProduct(pt1)
rotAxis.Normalize()
bv1 = FeatDirUtils.ArbAxisRotation(120, rotAxis, v1)
bv1.Normalize()
bv1 *= scale
bv2 = FeatDirUtils.ArbAxisRotation(-120, rotAxis, v1)
bv2.Normalize()
bv2 *= scale
return [cpt]*2, [bv1, bv2]
[docs]
def GetAcceptor2FeatVects(conf, featAtoms, scale=1.):
"""
Get the direction vectors for Acceptor of type 2.
Made to generate a single vector representation.
This is the acceptor with two adjacent heavy atoms. We will special case a few things here.
If the acceptor atom is an oxygen we will assume a sp3 hybridization
the acceptor directions (two of them)
reflect that configurations. Otherwise the direction vector in plane with the neighboring
heavy atoms
Changed: return format
Arguments
---------
conf : rdkit Mol object with a conformer.
featAtoms : list containing rdkit Atom object of atom attributed as an acceptor.
scale : float (default = 1.) length of direction vector.
Returns
-------
Tuple
list of anchor position(s) as rdkit Point3D or None
list of relative unit vector(s) as rdkit Point3D or None
"""
assert len(featAtoms) == 1
aid = featAtoms[0]
cpt = conf.GetAtomPosition(aid)
mol = conf.GetOwningMol()
nbrs = list(mol.GetAtomWithIdx(aid).GetNeighbors())
hydrogens = []
heavy = []
for nbr in nbrs:
if nbr.GetAtomicNum() == 1:
hydrogens.append(nbr)
else:
heavy.append(nbr)
if len(hydrogens) == 0:
bvec = FeatDirUtils._findAvgVec(conf, cpt, heavy)
elif len(hydrogens) == 1:
bvec = FeatDirUtils._findAvgVec(conf, cpt, nbrs)
bvec *= (-1.0 * scale)
if mol.GetAtomWithIdx(aid).GetAtomicNum() == 8:
# assume sp3
# we will create two vectors by rotating bvec by half the tetrahedral angle in either directions
v1 = conf.GetAtomPosition(heavy[0].GetIdx())
v1 -= cpt
v2 = conf.GetAtomPosition(heavy[1].GetIdx())
v2 -= cpt
rotAxis = v1 - v2
rotAxis.Normalize()
bv1 = FeatDirUtils.ArbAxisRotation(54.5, rotAxis, bvec)
bv2 = FeatDirUtils.ArbAxisRotation(-54.5, rotAxis, bvec)
return [cpt]*2, [bv1, bv2]
return [cpt], [bvec]
[docs]
def GetAcceptor3FeatVects(conf, featAtoms, scale=1.0):
"""
Get the direction vectors for Donor of type 3.
Made to generate a single vector representation.
This is a donor with three heavy atoms as neighbors. We will assume
a tetrahedral arrangement of these neighbors. So the direction we are seeking
is the last fourth arm of the sp3 arrangement
Changed: return format
Arguments
---------
conf : rdkit Mol object with a conformer.
featAtoms : list containing rdkit Atom object of atom attributed as an acceptor.
scale : float (default = 1.) length of direction vector.
Returns
-------
Tuple
list of anchor position(s) as rdkit Point3D or None
list of relative unit vector(s) as rdkit Point3D or None
"""
assert len(featAtoms) == 1
aid = featAtoms[0]
cpt = conf.GetAtomPosition(aid)
out = FeatDirUtils._GetTetrahedralFeatVect(conf, aid, scale)
if out != ():
return [cpt], [out[0][1] - cpt] # need to subtract out the center to be relative
else:
# Means that this is planar so likely not an acceptor, remove it after
return None, None