modules/chempy/place.py (294 lines of code) (raw):
#A* -------------------------------------------------------------------
#B* This file contains source code for the PyMOL computer program
#C* copyright 1998-2000 by Warren Lyford Delano of DeLano Scientific.
#D* -------------------------------------------------------------------
#E* It is unlawful to modify or remove this copyright notice.
#F* -------------------------------------------------------------------
#G* Please see the accompanying LICENSE file for further information.
#H* -------------------------------------------------------------------
#I* Additional authors of this source file include:
#-*
#-*
#-*
#Z* -------------------------------------------------------------------
#
#
#
from __future__ import print_function
from . import bond_amber
from chempy.cpv import *
from chempy import feedback
import chempy.models
TET_TAN = 1.41
TRI_TAN = 1.732
#------------------------------------------------------------------------------
def find_known_secondary(model,anchor,known_list):
at = model.atom[anchor]
h_list = []
for id in known_list:
for b in model.bond[id]:
atx2 = b.index[0]
if atx2 == id:
atx2 = b.index[1]
if atx2 != anchor: # another bonded atom, not achor
at2 = model.atom[atx2]
if at2.has('coord'):
if at2.symbol != 'H':
return (id,atx2)
else:
h_list.append((id,atx2))
if len(h_list): # only return hydrogen as a last resort
return h_list[0]
return None
#------------------------------------------------------------------------------
def simple_unknowns(model,bondfield=bond_amber):
if feedback['actions']:
print(" "+str(__name__)+": placing unknowns...")
# this can be used to build hydrogens and would robably work for
# acyclic carbons as well
if not isinstance(model, chempy.models.Connected):
raise ValueError('model is not a "Connected" model object')
if model.nAtom:
if not model.index:
model.update_index()
idx = model.index
last_count = -1
while 1:
need = [ [], [], [], [] ]
bnd_len = bondfield.length
# find known atoms with missing neighbors, and keep track of the neighbors
for a in model.atom:
if a.has('coord'):
miss = []
know = []
atx1 = idx[id(a)]
bnd = model.bond[atx1]
for b in bnd:
atx2 = b.index[0]
if atx2 == atx1:
atx2 = b.index[1]
at2 = model.atom[atx2]
if not at2.has('coord'):
miss.append(atx2)
else:
know.append(atx2)
c = len(miss)
if c:
need[c-1].append((atx1,miss,know))
for a in need[0]: # missing only one atom
atx1 = a[0]
at1 = model.atom[atx1]
atx2 = a[1][0]
at2 = model.atom[atx2]
know = a[2]
if at1.text_type in bondfield.nonlinear:
near = find_known_secondary(model,atx1,know)
if near:
at3 = model.atom[near[0]]
if at3.text_type in bondfield.planer: # Phenolic hydrogens, etc.
at4 = model.atom[near[1]]
d1 = sub(at1.coord,at3.coord)
p0 = normalize(d1)
d2 = sub(at4.coord,at3.coord)
p1 = normalize(cross_product(d2,p0))
p2 = normalize(cross_product(p0,p1))
v = scale(p2,TRI_TAN)
v = normalize(add(p0,v))
at2.coord = add(at1.coord,scale(v,
bnd_len[(at1.text_type,at2.text_type)]))
else: # Ser, Cys, Thr hydroxyl hydrogens
at4 = model.atom[near[1]]
d2 = sub(at3.coord,at4.coord)
v = normalize(d2)
at2.coord = add(at1.coord,scale(v,
bnd_len[(at1.text_type,at2.text_type)]))
elif len(know):
d2 = [1.0,0,0]
at3 = model.atom[know[0]]
p0 = normalize(sub(at1.coord,at3.coord))
p1 = normalize(cross_product(d2,p0))
v = scale(p1,TET_TAN)
v = normalize(add(p0,v))
at2.coord = add(at1.coord,scale(v,
bnd_len[(at1.text_type,at2.text_type)]))
else:
at2.coord = random_sphere(at1.coord,
bnd_len[(at1.text_type,at2.text_type)])
elif len(know): # linear sum...amide, tbu, etc
v = [0.0,0.0,0.0]
for b in know:
d = sub(at1.coord,model.atom[b].coord)
v = add(v,normalize(d))
v = normalize(v)
at2.coord = add(at1.coord,scale(v,
bnd_len[(at1.text_type,at2.text_type)]))
else:
at2.coord = random_sphere(at1.coord,
bnd_len[(at1.text_type,at2.text_type)])
for a in need[1]: # missing two atoms
atx1 = a[0]
at1 = model.atom[atx1]
atx2 = a[1][0]
at2 = model.atom[atx2]
know = a[2]
if at1.text_type in bondfield.planer: # guanido, etc
near = find_known_secondary(model,atx1,know)
if near: # 1-4 present
at3 = model.atom[near[0]]
at4 = model.atom[near[1]]
d1 = sub(at1.coord,at3.coord)
p0 = normalize(d1)
d2 = sub(at4.coord,at3.coord)
p1 = normalize(cross_product(d2,p0))
p2 = normalize(cross_product(p0,p1))
v = scale(p2,TRI_TAN)
v = normalize(add(p0,v))
at2.coord = add(at1.coord,scale(v,
bnd_len[(at1.text_type,at2.text_type)]))
at2 = model.atom[a[1][1]]
v = scale(p2,-TRI_TAN)
v = normalize(add(p0,v))
at2.coord = add(at1.coord,scale(v,
bnd_len[(at1.text_type,at2.text_type)]))
elif len(know): # no 1-4 found
d2 = [1.0,0,0]
at3 = model.atom[know[0]]
d1 = sub(at1.coord,at3.coord)
p0 = normalize(d1)
p1 = normalize(cross_product(d2,p0))
p2 = normalize(cross_product(p0,p1))
v = scale(p2,TRI_TAN)
v = normalize(add(p0,v))
at2.coord = add(at1.coord,scale(v,
bnd_len[(at1.text_type,at2.text_type)]))
at2 = model.atom[a[1][1]]
v = scale(p2,-TRI_TAN)
v = normalize(add(p0,v))
at2.coord = add(at1.coord,scale(v,
bnd_len[(at1.text_type,at2.text_type)]))
else:
at2.coord = random_sphere(at1.coord,
bnd_len[(at1.text_type,at2.text_type)])
elif len(know)>=2: # simple tetrahedral
at3 = model.atom[know[0]]
at4 = model.atom[know[1]]
v = [0.0,0.0,0.0]
d1 = sub(at1.coord,at3.coord)
d2 = sub(at1.coord,at4.coord)
v = add(normalize(d1),normalize(d2))
p0 = normalize(v)
p1 = normalize(cross_product(d2,p0))
v = scale(p1,TET_TAN)
v = normalize(add(p0,v))
at2.coord = add(at1.coord,scale(v,
bnd_len[(at1.text_type,at2.text_type)]))
at2 = model.atom[a[1][1]]
v = scale(p1,-TET_TAN)
v = normalize(add(p0,v))
at2.coord = add(at1.coord,scale(v,
bnd_len[(at1.text_type,at2.text_type)]))
else:
if len(know): # sulfonamide?
d2 = [1.0,0,0]
at3 = model.atom[know[0]]
d1 = sub(at1.coord,at3.coord)
p0 = normalize(d1)
p1 = normalize(cross_product(d2,p0))
v = scale(p1,TET_TAN)
v = normalize(add(p0,v))
at2.coord = add(at1.coord,scale(v,
bnd_len[(at1.text_type,at2.text_type)]))
else: # blind
at2.coord = random_sphere(at1.coord,
bnd_len[(at1.text_type,at2.text_type)])
# 2013-08-14 added by thomas
raise NotImplementedError("FIXME: at3 unassigned")
at4=at2
at2=model.atom[a[1][1]]
v = [0.0,0.0,0.0]
d1 = sub(at1.coord,at3.coord)
d2 = sub(at1.coord,at4.coord)
v = add(normalize(d1),normalize(d2))
p0 = normalize(v)
p1 = normalize(cross_product(d2,p0))
v = scale(p1,TET_TAN)
v = normalize(add(p0,v))
at2.coord = add(at1.coord,scale(v,
bnd_len[(at1.text_type,at2.text_type)]))
for a in need[2]: # missing 3 atoms
atx1 = a[0]
at1 = model.atom[atx1]
atx2 = a[1][0]
at2 = model.atom[atx2]
know = a[2]
near = find_known_secondary(model,atx1,know)
if near: # 1-4 present
at3 = model.atom[near[0]]
at4 = model.atom[near[1]]
d1 = sub(at1.coord,at3.coord)
p0 = normalize(d1)
d2 = sub(at4.coord,at3.coord)
p1 = normalize(cross_product(d2,p0))
p2 = normalize(cross_product(p0,p1))
v = scale(p2,-TET_TAN)
v = normalize(add(p0,v))
at2.coord = add(at1.coord,scale(v,
bnd_len[(at1.text_type,at2.text_type)]))
at4 = at2
at2 = model.atom[a[1][1]]
d1 = sub(at1.coord,at3.coord)
d2 = sub(at1.coord,at4.coord)
v = add(normalize(d1),normalize(d2))
p0 = normalize(v)
p1 = normalize(cross_product(d2,p0))
v = scale(p1,TET_TAN)
v = normalize(add(p0,v))
at2.coord = add(at1.coord,scale(v,
bnd_len[(at1.text_type,at2.text_type)]))
at2 = model.atom[a[1][2]]
v = scale(p1,-TET_TAN)
v = normalize(add(p0,v))
at2.coord = add(at1.coord,scale(v,
bnd_len[(at1.text_type,at2.text_type)]))
elif len(know): # fall-back
d2 = [1.0,0,0]
at3 = model.atom[know[0]]
# 2013-08-14 added by thomas, not sure if this is correct
d1 = sub(at1.coord,at3.coord)
p0 = normalize(d1)
p1 = normalize(cross_product(d2,p0))
v = scale(p1,TET_TAN)
v = normalize(add(p0,v))
at2.coord = add(at1.coord,scale(v,
bnd_len[(at1.text_type,at2.text_type)]))
at4=at2
at2=model.atom[a[1][1]]
v = [0.0,0.0,0.0]
d1 = sub(at1.coord,at3.coord)
d2 = sub(at1.coord,at4.coord)
v = add(normalize(d1),normalize(d2))
p0 = normalize(v)
p1 = normalize(cross_product(d2,p0))
v = scale(p1,TET_TAN)
v = normalize(add(p0,v))
at2.coord = add(at1.coord,scale(v,
bnd_len[(at1.text_type,at2.text_type)]))
at2=model.atom[a[1][2]]
v = scale(p1,-TET_TAN)
v = normalize(add(p0,v))
at2.coord = add(at1.coord,scale(v,
bnd_len[(at1.text_type,at2.text_type)]))
else: # worst case: add one and get rest next time around
at2.coord=random_sphere(at2.coord,
bnd_len[(at1.text_type,at2.text_type)])
for a in need[3]: # missing 4 atoms
atx1 = a[0]
at1 = model.atom[atx1]
atx2 = a[1][0]
at2 = model.atom[atx2]
# add coordinate and get the rest next time around
at2.coord=random_sphere(at2.coord,
bnd_len[(at1.text_type,at2.text_type)])
c = 0
for a in model.atom:
if not a.has('coord'):
c = c + 1
if not c:
break;
if c==last_count:
break;
last_count = c
#------------------------------------------------------------------------------
def test_random():
'''
This is a simple test function which drops most coordinates from a
polypeptide and tries to reposition them with simple_unknowns().
Works fine to position hydrogens, fails to position other atoms.
'''
import random
from pymol import cmd
from chempy.champ import assign
cmd.fab('ACDEFGHIKLMNPQRSTVWY', 'm0')
assign.amber99()
for i in range(100):
m = cmd.get_model('m0').convert_to_connected()
for a in m.atom:
if random.random() < 0.8:
del a.coord
simple_unknowns(m)
cmd.load_model(m.convert_to_indexed(), 'm' + str(i + 1))