Files
propka/Source/ligand.py
2012-11-15 17:47:47 +01:00

404 lines
11 KiB
Python
Executable File

#!/usr/bin/python
import sys, Source.calculations
from Source.vector_algebra import *
all_sybyl_types = [
'C.3', # carbon sp3
'H', # hydrogen
'C.2', # carbon sp2
'H.spc', # hydrogen in Single Point Charge (SPC) water model
'C.1', # carbon sp
'H.t3p', # hydrogen in Transferable intermolecular Potential (TIP3P) water model
'C.ar', # carbon aromatic
'LP', # lone pair
'C.cat', # carbocation (C+) used only in a guadinium group
'Du', # dummy atom
'N.3', # nitrogen sp3
'Du.C', # dummy carbon
'N.2', # nitrogen sp2
'Any', # any atom
'N.1', # nitrogen sp
'Hal', # halogen
'N.ar', # nitrogen aromatic
'Het', # heteroatom = N, O, S, P
'N.am', # nitrogen amide
'Hev', # heavy atom (non hydrogen)
'N.pl3', # nitrogen trigonal planar
'Li', # lithium
'N.4', # nitrogen sp3 positively charged
'Na', # sodium
'O.3', # oxygen sp3
'Mg', # magnesium
'O.2', # oxygen sp2
'Al', # aluminum
'O.co2', # oxygen in carboxylate and phosphate groups
'Si', # silicon
'O.spc', # oxygen in Single Point Charge (SPC) water model
'K', # potassium
'O.t3p', # oxygen in Transferable Intermolecular Potential (TIP3P) water model
'Ca', # calcium
'S.3', # sulfur sp3
'Cr.th', # chromium (tetrahedral)
'S.2', # sulfur sp2
'Cr.oh', # chromium (octahedral)
'S.O', # sulfoxide sulfur
'Mn', # manganese
'S.O2', # sulfone sulfur
'Fe', # iron
'P.3', # phosphorous sp3
'Co.oh', # cobalt (octahedral)
'F', # fluorine
'Cu', # copper
'Cl', # chlorine
'Zn', # zinc
'Br', # bromine
'Se', # selenium
'I', # iodine
'Mo', # molybdenum
'Sn'] # tin
#propka_input_types = ['1P','1N','2P','2N']
#for type in all_sybyl_types:
# temp = type.replace('.','')
# if len(temp)>3:
# temp = temp[0:3]
# propka_input_types.append(temp)
#
#for t in propka_input_types:
# print (t)
propka_input_types = [
'1P',
'1N',
'2P',
'2N',
'C3',
'H',
'C2',
'Hsp',
'C1',
'Ht3',
'Car',
'LP',
'Cca',
'Du',
'N3',
'DuC',
'N2',
'Any',
'N1',
'Hal',
'Nar',
'Het',
'Nam',
'Hev',
'Npl',
'Li',
'N4',
'Na',
'O3',
'Mg',
'O2',
'Al',
'Oco',
'Si',
'Osp',
'K',
'Ot3',
'Ca',
'S3',
'Crt',
'S2',
'Cro',
'SO',
'Mn',
'SO2',
'Fe',
'P3',
'Coo',
'F',
'Cu',
'Cl',
'Zn',
'Br',
'Se',
'I',
'Mo',
'Sn']
max_C_double_bond = 1.3
max_C_triple_bond = 1.2
max_C_double_bond_squared = max_C_double_bond*max_C_double_bond
max_C_triple_bond_squared = max_C_triple_bond*max_C_triple_bond
def assign_sybyl_type(atom):
# check if we already have assigned a name to this atom
if atom.sybyl_assigned:
#print(atom.name,'already assigned')
return
# find some properties of the atom
ring_atoms = is_ring_member(atom)
aromatic = is_aromatic_ring(ring_atoms)
planar = is_planar(atom)
bonded_elements = {}
for i in range(len(atom.bonded_atoms)):
bonded_elements[i]=atom.bonded_atoms[i].element
# Aromatic carbon/nitrogen
if aromatic:
for ra in ring_atoms:
if ra.element in ['C','N']:
set_type(ra, ra.element+'.ar')
return
# check for amide
if atom.element in ['O','N','C']:
O=None
N=None
C=None
# oxygen, nitrogen
if atom.element in ['O','N']:
for b in atom.get_bonded_elements('C'):
for bb in b.bonded_atoms:
if (bb.element =='N' and atom.element == 'O'):
O=atom
C=b
N=bb
elif (bb.element =='O' and atom.element == 'N'):
N=atom
C=b
O=bb
# carbon
if atom.element == 'C':
nitrogens = atom.get_bonded_elements('N')
oxygens = atom.get_bonded_elements('O')
if len(nitrogens)==1 and len(oxygens)==1:
C = atom
N = nitrogens[0]
O = oxygens[0]
if C and N and O:
# make sure that the Nitrogen is not aromatic and that it has two heavy atom bonds
if not is_aromatic_ring(is_ring_member(N)) and len(N.get_bonded_heavy_atoms())==2:
set_type(N,'N.am')
set_type(C,'C.2')
set_type(O,'O.2')
return
if atom.element=='C':
# check for carboxyl
if len(atom.bonded_atoms)==3 and list(bonded_elements.values()).count('O')==2:
i1 = list(bonded_elements.values()).index('O')
i2 = list(bonded_elements.values()).index('O',i1+1)
if len(atom.bonded_atoms[i1].bonded_atoms)==1 and len(atom.bonded_atoms[i2].bonded_atoms)==1:
set_type(atom.bonded_atoms[i1],'O.co2-')
set_type(atom.bonded_atoms[i2],'O.co2')
set_type(atom,'C.2')
return
# sp carbon
if len(atom.bonded_atoms)<=2:
for b in atom.bonded_atoms:
if Source.calculations.squared_distance(atom, b) < max_C_triple_bond_squared:
set_type(atom,'C.1')
set_type(b,b.element+'.1')
if atom.sybyl_assigned:
return
# sp2 carbon
if planar:
set_type(atom,'C.2')
# check for N.pl3
for b in atom.bonded_atoms:
if b.element=='N':
if len(b.bonded_atoms)<3 or is_planar(b):
set_type(b,'N.pl3')
return
# sp3 carbon
set_type(atom, 'C.3')
return
# Nitrogen
if atom.element == 'N':
# check for planar N
if len(atom.bonded_atoms)==1:
if is_planar(atom.bonded_atoms[0]):
set_type(atom,'N.pl3')
return
if planar:
set_type(atom,'N.pl3')
return
set_type(atom,'N.3')
return
# Oxygen
if atom.element == 'O':
set_type(atom,'O.3')
if len(atom.bonded_atoms) == 1:
# check for carboxyl
if atom.bonded_atoms[0].element == 'C':
the_carbon = atom.bonded_atoms[0]
if len(the_carbon.bonded_atoms)==3 and the_carbon.count_bonded_elements('O')==2:
[O1,O2] = the_carbon.get_bonded_elements('O')
if len(O1.bonded_atoms)==1 and len(O2.bonded_atoms)==1:
set_type(O1,'O.co2-')
set_type(O2,'O.co2')
set_type(the_carbon,'C.2')
return
# check for X=O
if Source.calculations.squared_distance(atom, atom.bonded_atoms[0]) < max_C_double_bond_squared:
set_type(atom,'O.2')
if atom.bonded_atoms[0].element=='C':
set_type(atom.bonded_atoms[0],'C.2')
return
# Sulphur
if atom.element == 'S':
# check for SO2
if list(bonded_elements.values()).count('O')==2:
i1 = list(bonded_elements.values()).index('O')
i2 = list(bonded_elements.values()).index('O',i1+1)
set_type(atom.bonded_atoms[i1],'O.2')
set_type(atom.bonded_atoms[i2],'O.2')
set_type(atom,'S.o2')
return
# check for SO4
if list(bonded_elements.values()).count('O')==4:
no_O2 = 0
for i in range(len(atom.bonded_atoms)):
if len(atom.bonded_atoms[i].bonded_atoms)==1 and no_O2<2:
set_type(atom.bonded_atoms[i],'O.2')
no_O2+=1
else:
set_type(atom.bonded_atoms[i],'O.3')
set_type(atom,'S.3')
return
# Phosphorus
if atom.element == 'P':
set_type(atom,'P.3')
# check for phosphate group
bonded_oxygens = atom.get_bonded_elements('O')
for o in bonded_oxygens: set_type(o,'O.3')
# if len(bonded_oxygens)>=3:
# # find oxygens only bonded to current phosphorus
# bonded_oxygens_1 = [o for o in bonded_oxygens if len(o.get_bonded_heavy_atoms())==1]
# # find the closest oxygen ...
# closest_oxygen = min(bonded_oxygens_1,
# key= lambda o:Source.calculations.squared_distance(atom,o))
# # ... and set it to O.2
# set_type(closest_oxygen,'O.2')
return
element = atom.element.capitalize()
set_type(atom,element)
# print('Using element as type for %s'%atom.element)
return
def is_ring_member(atom):
return identify_ring(atom,atom,0,[])
def identify_ring(this_atom, original_atom, number, past_atoms):
number+=1
past_atoms=past_atoms+[this_atom]
return_atoms = []
if number > 10:
return return_atoms
for atom in this_atom.get_bonded_heavy_atoms():
if atom == original_atom and number>2:
return past_atoms
if atom not in past_atoms:
these_return_atoms = identify_ring(atom, original_atom, number, past_atoms)
if len(these_return_atoms) > 0:
if len(return_atoms)>len(these_return_atoms) or len(return_atoms)==0:
return_atoms = these_return_atoms
return return_atoms
def set_type(atom,type):
#print(atom, '->',type)
atom.sybyl_type = type
atom.sybyl_assigned=True
return
def is_planar(atom):
""" Finds out if atom forms a plane together with its bonded atoms"""
atoms = [atom]+atom.bonded_atoms
return are_atoms_planar(atoms)
def are_atoms_planar(atoms):
if len(atoms)==0:
return False
if len(atoms)<4:
return False
v1 = vector(atom1=atoms[0], atom2=atoms[1])
v2 = vector(atom1=atoms[0], atom2=atoms[2])
n = (v1**v2).rescale(1.0)
margin = 0.20
for b in atoms[3:]:
v = vector(atom1=atoms[0], atom2=b).rescale(1.0)
#print(atoms[0],abs(v*n) )
if abs(v*n)>margin:
return False
return True
def is_aromatic_ring(atoms):
if len(atoms)<5:
return False
for i in range(len(atoms)):
if not are_atoms_planar(atoms[i:]+atoms[:i]):
return False
return True