Réimplémentation du programme DSSP en Python

structure.py 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  1. import math
  2. import numpy as np
  3. class Structure:
  4. def __init__(self, res):
  5. self.res = res
  6. class Turn(Structure):
  7. def __init__(self, turn_type, res):
  8. self.turn_type = turn_type
  9. Structure.res = res
  10. class Bridge(Structure):
  11. def __init__(self, bridge_type, res_num, res_partner):
  12. self.bridge_type = bridge_type
  13. self.res_num = res_num
  14. self.res_partner = res_partner
  15. Structure.res = res_num
  16. class Helix(Structure):
  17. def __init__(self, residues, res_num):
  18. self.residues = residues
  19. self.res_num = res_num
  20. Structure.res = res_num
  21. def get_turns(residues):
  22. # TODO : prevent redondency of overlapping turns
  23. turns = {}
  24. for i,res in enumerate(residues):
  25. for j in range(3,6):
  26. if(i+j<len(residues)):
  27. if(res.h_bond(residues[i+j])<-0.5):
  28. print(j,"TURN", residues[i].resid, residues[i+j].resid)
  29. turns[i] = Turn(j,residues[i].resid)
  30. return(turns)
  31. def get_bridges(residues):
  32. bridges = {}
  33. bridge = {}
  34. strongest_bridge = {}
  35. for i in range(1,len(residues)-4):
  36. E_min = 0
  37. for j in range(i+2,len(residues)-1):
  38. # select triplet with the minimal energy
  39. if(residues[i-1].h_bond(residues[j])<-0.5
  40. and residues[j].h_bond(residues[i+1])<-0.5):
  41. bridge = {'res1':residues[i-1].h_bond(residues[j]),
  42. 'res2':residues[j].h_bond(residues[i+1]),
  43. 'ipos':residues[i].resid,
  44. 'jpos':residues[j].resid,
  45. 'btype':"para"}
  46. if(residues[j-1].h_bond(residues[i])<-0.5
  47. and residues[i].h_bond(residues[j+1])<-0.5):
  48. bridge = {'res1':residues[j-1].h_bond(residues[i]),
  49. 'res2':residues[i].h_bond(residues[j+1]),
  50. 'ipos':residues[i].resid,
  51. 'jpos':residues[j].resid,
  52. 'btype':"para"}
  53. if(residues[i].h_bond(residues[j])<-0.5
  54. and residues[j].h_bond(residues[i])<-0.5):
  55. bridge = {'res1':residues[i].h_bond(residues[j]),
  56. 'res2':residues[j].h_bond(residues[i]),
  57. 'ipos':residues[i].resid,
  58. 'jpos':residues[j].resid,
  59. 'btype':"anti"}
  60. if(residues[i-1].h_bond(residues[j+1])<-0.5
  61. and residues[j-1].h_bond(residues[i+1])<-0.5):
  62. bridge = {'res1':residues[i-1].h_bond(residues[j+1]),
  63. 'res2':residues[j-1].h_bond(residues[i+1]),
  64. 'ipos':residues[i].resid,
  65. 'jpos':residues[j].resid,
  66. 'btype':"anti"}
  67. if(bridge):
  68. if(bridge['res1']+bridge['res2']<E_min):
  69. E_min = bridge['res1']+bridge['res2']
  70. strongest_bridge = bridge
  71. bridge = {}
  72. coord_bridge = [i,j]
  73. # finally add the strongest bridge at i and j pos
  74. if(strongest_bridge):
  75. bridges[coord_bridge[0]] = (Bridge(strongest_bridge['btype'],
  76. strongest_bridge['ipos'],
  77. strongest_bridge['jpos']))
  78. bridges[coord_bridge[1]] = (Bridge(strongest_bridge['btype'],
  79. strongest_bridge['jpos'],
  80. strongest_bridge['ipos']))
  81. if(len(bridges)>0):
  82. return(bridges)
  83. else:
  84. return(False)
  85. # def get_bonds(residues):
  86. # for i,res1 in enumerate(residues):
  87. # E_min = 0
  88. # strongest_bridge = []
  89. # for j in range(-5,6):
  90. # if(i+j < len(residues)):
  91. # res2 = residues[i+j]
  92. # if(res1.h_bond(res2)<-0.5) and (res1.h_bond(res2)<E_min):
  93. # E_min = res1.h_bond(res2)
  94. # strongest_bridge = [res1, res2]
  95. # if(len(strongest_bridge)>0):
  96. # diff = strongest_bridge[0].resid - strongest_bridge[1].resid
  97. # if( abs (diff) > 1 and abs(diff)<=5):
  98. # print(diff)
  99. def get_bonds(residues):
  100. bonds = {}
  101. k = 0
  102. for i,res in enumerate(residues):
  103. E_min = 0
  104. for j in range(-5,-2):
  105. if(i+j<len(residues)):
  106. if(residues[i+j].h_bond(res)<-0.5):
  107. k+=1
  108. #if(res.h_bond(residues[i+j])<E_min):
  109. E_min = res.h_bond(residues[i+j])
  110. res_a = res
  111. res_b = residues[i+j]
  112. if j not in bonds:
  113. bonds[j] = []
  114. bonds[j].append([res_a.resid, res_b.resid])
  115. #turns[residues[i].resid] = Turn(j,residues[i].resid)
  116. for key in bonds.keys():
  117. print(key, len(bonds[key]))
  118. print("LH",k)
  119. def get_helix(residues, turns):
  120. i = 1
  121. helix = []
  122. while i <= len(residues):
  123. if(i in turns.keys() and i-1 in turns.keys()):
  124. k = 0
  125. temp_res = []
  126. while(i+k in turns):
  127. k+=1
  128. temp_res.append(residues[i])
  129. last_res_pos = residues[i].resid+k
  130. if(k>=1):
  131. helix_size=last_res_pos - residues[i].resid
  132. print(turns[i].turn_type,"- HELIX at", residues[i].resid)
  133. helix.append(Helix(temp_res,residues[i].resid))
  134. i = i+k
  135. else:
  136. i+=1
  137. return(helix)
  138. def get_ladders(bridges, residues):
  139. ladders = {}
  140. i = 1
  141. while i < len(residues):
  142. k = 1
  143. if i in bridges.keys():
  144. temp_bridges = [bridges[i]]
  145. while ((i+k in bridges.keys()) and
  146. (bridges[i].bridge_type == bridges[i+k].bridge_type)):
  147. temp_bridges.append(bridges[i+k])
  148. k+=1
  149. if k>1:
  150. ladders[i] = k-1
  151. print("ladder", bridges[i].res_num, bridges[i+k-1].res_num)
  152. ladders[i] = {'start':bridges[i].res_num,
  153. 'end':bridges[i+k-1].res_num,
  154. 'bridges':temp_bridges}
  155. i+=k-1
  156. else:
  157. i+=1
  158. return ladders
  159. def get_sheets(ladders):
  160. """
  161. Bridges between ladders.
  162. Check if 1 bridge between one ladder and one or more other ladders.
  163. Iterate over all residues of one ladder and check if bridge with other residues
  164. of the other ladders.
  165. """
  166. sheets = {}
  167. for ladder in ladders:
  168. for ladd2 in ladders:
  169. for bridge in ladders[ladder]['bridges']:
  170. if bridge.res_partner in res_list(ladders[ladd2]):
  171. print("ladder",ladders[ladder]['start'], ladders[ladder]['end'],"bridge",bridge.res_num, bridge.res_partner,
  172. "ladder 2",ladders[ladd2]['start'], ladders[ladd2]['end'])
  173. #print("stop ladder 2")
  174. print("stop ladder 1")
  175. def res_list(ladder):
  176. # TODO : method in ladder class
  177. l=[]
  178. for i in range(ladder['start'], ladder['end']):
  179. l.append(i)
  180. return(l)
  181. def get_bends(residues):
  182. bends = {}
  183. for i in range(2,len(residues)-2):
  184. angle = math.degrees(vector_angles(vectors_substr(position_vector(residues[i].atoms["CA"].coords),
  185. position_vector(residues[i-2].atoms["CA"].coords)),
  186. vectors_substr(position_vector(residues[i+2].atoms["CA"].coords),
  187. position_vector(residues[i].atoms["CA"].coords))))
  188. if(angle>70):
  189. print("angle", residues[i].resid, angle)
  190. bends[residues[i].resid] = angle
  191. return(bends)
  192. def vector_from_pos(a, b):
  193. xAB = b[0]-a[0]
  194. yAB = b[1]-a[1]
  195. zAB = b[2]-a[2]
  196. coord_AB = [xAB,yAB,zAB]
  197. return coord_AB
  198. def vector_norm(v):
  199. norm = math.sqrt(v[0]**2 + v[1]**2 + v[2]**2)
  200. return norm
  201. def dot_product(v1,v2):
  202. dot_product = v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2]
  203. return(dot_product)
  204. def position_vector(c):
  205. vector = vector_from_pos([0,0,0],c)
  206. return vector
  207. def vectors_substr(v1, v2):
  208. return ([v1[0]-v2[0],
  209. v1[1]-v2[1],
  210. v1[2]-v2[2]])
  211. def vector_angles(v1,v2):
  212. dot_prod = dot_product(v1,v2)
  213. norm_v1 = vector_norm(v1)
  214. norm_v2 = vector_norm(v2)
  215. term = dot_prod/(abs(norm_v1)*abs(norm_v2))
  216. rad_angle = math.acos(term)
  217. return rad_angle
  218. def calc_dihedral(u1, u2, u3, u4):
  219. """ Calculate dihedral angle method. From bioPython.PDB
  220. (adapted to np.array)
  221. Calculate the dihedral angle between 4 vectors
  222. representing 4 connected points. The angle is in
  223. [-pi, pi].
  224. Adapted function of dihedral_angle_from_coordinates.py
  225. by Eric Alcaide.
  226. Source : https://gist.github.com/EricAlcaide
  227. URL : https://gist.github.com/EricAlcaide/30d6bfdb8358d3a57d010c9a501fda56
  228. """
  229. #convert coords to numpy arrays
  230. u1 = np.array(u1)
  231. u2 = np.array(u2)
  232. u3 = np.array(u3)
  233. u4 = np.array(u4)
  234. a1 = u2 - u1
  235. a2 = u3 - u2
  236. a3 = u4 - u3
  237. v1 = np.cross(a1, a2)
  238. v1 = v1 / (v1 * v1).sum(-1)**0.5
  239. v2 = np.cross(a2, a3)
  240. v2 = v2 / (v2 * v2).sum(-1)**0.5
  241. porm = np.sign((v1 * a3).sum(-1))
  242. rad = np.arccos((v1*v2).sum(-1) / ((v1**2).sum(-1) * (v2**2).sum(-1))**0.5)
  243. if not porm == 0:
  244. rad = rad * porm
  245. deg_angle = rad*(180/math.pi)
  246. return deg_angle
  247. def get_chirality(residues):
  248. for i in range(1,len(residues)-2):
  249. chirality = {}
  250. angle = calc_dihedral(residues[i-1].atoms["CA"].coords,
  251. residues[i].atoms["CA"].coords,
  252. residues[i+1].atoms["CA"].coords,
  253. residues[i+2].atoms["CA"].coords)
  254. if(angle>0 and angle<=180):
  255. sign="+"
  256. print("chirality", residues[i].resid, "+", angle)
  257. if(angle<=0 and angle > -180):
  258. sign="-"
  259. print("chirality", residues[i].resid, "-", angle)
  260. chirality[residues[i].resid] = sign
  261. return chirality
  262. def get_phi_psi(residues):
  263. for i in range(len(residues)):
  264. if(i==0):
  265. phi = 360.0
  266. else:
  267. phi = calc_dihedral(residues[i-1].atoms["C"].coords,
  268. residues[i].atoms["N"].coords,
  269. residues[i].atoms["CA"].coords,
  270. residues[i].atoms["C"].coords)
  271. if(i==len(residues)-1):
  272. psi = 360.0
  273. else:
  274. psi = calc_dihedral(residues[i].atoms["N"].coords,
  275. residues[i].atoms["CA"].coords,
  276. residues[i].atoms["C"].coords,
  277. residues[i+1].atoms["N"].coords)
  278. print("ANGLES", i, phi, psi)
  279. def get_TCO(res1, res2):
  280. CO_res1 = vector_from_pos(res1.atoms["C"].coords,
  281. res1.atoms["O"].coords)
  282. CO_res2 = vector_from_pos(res2.atoms["C"].coords,
  283. res2.atoms["O"].coords)
  284. angle = vector_angles(CO_res1, CO_res2)
  285. return(math.cos(angle))