Réimplémentation du programme DSSP en Python

structure.py 9.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  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[residues[i].resid] = 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[i-1].h_bond(residues[j])+
  47. # residues[j].h_bond(residues[i+1]))<E_min:
  48. # E_min = residues[i-1].h_bond(residues[j])
  49. # +residues[j].h_bond(residues[i+1])
  50. # bridge_type = "para"
  51. if(residues[j-1].h_bond(residues[i])<-0.5
  52. and residues[i].h_bond(residues[j+1])<-0.5):
  53. bridge = {'res1':residues[j-1].h_bond(residues[i]),
  54. 'res2':residues[i].h_bond(residues[j+1]),
  55. 'ipos':residues[i].resid,
  56. 'jpos':residues[j].resid,
  57. 'btype':"para"}
  58. # if(residues[j-1].h_bond(residues[i])+
  59. # residues[i].h_bond(residues[j+1]))<E_min:
  60. # E_min = residues[j-1].h_bond(residues[i])
  61. # +residues[i].h_bond(residues[j+1])
  62. # bridge_type = "para"
  63. if(residues[i].h_bond(residues[j])<-0.5
  64. and residues[j].h_bond(residues[i])<-0.5):
  65. bridge = {'res1':residues[i].h_bond(residues[j]),
  66. 'res2':residues[j].h_bond(residues[i]),
  67. 'ipos':residues[i].resid,
  68. 'jpos':residues[j].resid,
  69. 'btype':"anti"}
  70. # if(residues[i].h_bond(residues[j])+
  71. # residues[j].h_bond(residues[i]))<E_min:
  72. # E_min = residues[i].h_bond(residues[j])
  73. # +residues[j].h_bond(residues[i])
  74. # bridge_type = "anti"
  75. if(residues[i-1].h_bond(residues[j+1])<-0.5
  76. and residues[j-1].h_bond(residues[i+1])<-0.5):
  77. bridge = {'res1':residues[i-1].h_bond(residues[j+1]),
  78. 'res2':residues[j-1].h_bond(residues[i+1]),
  79. 'ipos':residues[i].resid,
  80. 'jpos':residues[j].resid,
  81. 'btype':"anti"}
  82. # if(residues[i-1].h_bond(residues[j+1])+
  83. # residues[j-1].h_bond(residues[i+1]))<E_min:
  84. # E_min = residues[i-1].h_bond(residues[j+1])
  85. # +residues[j-1].h_bond(residues[i+1])
  86. # bridge_type = "anti"
  87. if(bridge):
  88. if(bridge['res1']+bridge['res2']<E_min):
  89. E_min = bridge['res1']+bridge['res2']
  90. strongest_bridge = bridge
  91. bridge = {}
  92. # finally add the strongest bridge at i and j pos
  93. if(strongest_bridge):
  94. bridges[strongest_bridge['ipos']] = (Bridge(strongest_bridge['btype'],
  95. strongest_bridge['ipos'],
  96. strongest_bridge['jpos']))
  97. bridges[strongest_bridge['jpos']] = (Bridge(strongest_bridge['btype'],
  98. strongest_bridge['jpos'],
  99. strongest_bridge['ipos']))
  100. if(len(bridges)>0):
  101. return(bridges)
  102. else:
  103. return(False)
  104. def get_helix(residues, turns):
  105. i = 1
  106. helix = []
  107. while i <= len(residues):
  108. if(i in turns.keys() and i-1 in turns.keys()):
  109. k = 0
  110. temp_res = []
  111. while(i+k in turns):
  112. k+=1
  113. temp_res.append(residues[i])
  114. if(k>2):
  115. print(k,"- HELIX at", residues[i].resid)
  116. helix.append(Helix(temp_res,residues[i].resid))
  117. i = i+k
  118. else:
  119. i+=1
  120. return(helix)
  121. def get_ladders(bridges):
  122. ladders = {}
  123. i = 1
  124. while i < len(bridges):
  125. k = 1
  126. if i in bridges.keys():
  127. temp_bridges = [bridges[i]]
  128. while ((i+k in bridges.keys()) and
  129. (bridges[i].bridge_type == bridges[i+k].bridge_type)):
  130. temp_bridges.append(bridges[i+k])
  131. k+=1
  132. if k>1:
  133. ladders[bridges[i].res_num] = k-1
  134. print("ladder", bridges[i].res_num, bridges[i+k-1].res_num)
  135. ladders[bridges[i].res_num] = {'start':bridges[i].res_num,
  136. 'end':bridges[i+k-1].res_num,
  137. 'bridges':temp_bridges}
  138. i+=k-1
  139. else:
  140. i+=1
  141. return ladders
  142. def get_sheets(ladders):
  143. """
  144. Bridges between ladders.
  145. Check if 1 bridge between one ladder and one or more other ladders.
  146. Iterate over all residues of one ladder and check if bridge with other residues
  147. of the other ladders.
  148. """
  149. for ladder in ladders:
  150. for bridge in ladders[ladder]['bridges']:
  151. for ladd2 in ladders:
  152. if bridge.res_partner in res_list(ladders[ladd2]):
  153. print("ladder",ladders[ladder]['start'], ladders[ladder]['end'],"bridge",bridge.res_num, bridge.res_partner,
  154. "ladder 2",ladders[ladd2]['start'], ladders[ladd2]['end'])
  155. def res_list(ladder):
  156. # TODO : method in ladder class
  157. l=[]
  158. for i in range(ladder['start'], ladder['end']):
  159. l.append(i)
  160. return(l)
  161. def get_bends(residues):
  162. bends = {}
  163. for i in range(2,len(residues)-2):
  164. angle = vector_angles(vectors_substr(position_vector(residues[i].atoms["CA"].coords),
  165. position_vector(residues[i-2].atoms["CA"].coords)),
  166. vectors_substr(position_vector(residues[i+2].atoms["CA"].coords),
  167. position_vector(residues[i].atoms["CA"].coords)))
  168. if(angle>70):
  169. print("angle", residues[i].resid, angle)
  170. bends[residues[i].resid] = angle
  171. return(bends)
  172. def vector_from_pos(a, b):
  173. xAB = b[0]-a[0]
  174. yAB = b[1]-a[1]
  175. zAB = b[2]-a[2]
  176. coord_AB = [xAB,yAB,zAB]
  177. return coord_AB
  178. def vector_norm(v):
  179. norm = math.sqrt(v[0]**2 + v[1]**2 + v[2]**2)
  180. return norm
  181. def dot_product(v1,v2):
  182. dot_product = v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2]
  183. return(dot_product)
  184. def position_vector(c):
  185. vector = vector_from_pos([0,0,0],c)
  186. return vector
  187. def vectors_substr(v1, v2):
  188. return ([v1[0]-v2[0],
  189. v1[1]-v2[1],
  190. v1[2]-v2[2]])
  191. def vector_angles(v1,v2):
  192. dot_prod = dot_product(v1,v2)
  193. norm_v1 = vector_norm(v1)
  194. norm_v2 = vector_norm(v2)
  195. term = dot_prod/(abs(norm_v1)*abs(norm_v2))
  196. rad_angle = math.acos(term)
  197. deg_angle = rad_angle*(180/math.pi)
  198. return deg_angle
  199. def calc_dihedral(u1, u2, u3, u4):
  200. """ Calculate dihedral angle method. From bioPython.PDB
  201. (adapted to np.array)
  202. Calculate the dihedral angle between 4 vectors
  203. representing 4 connected points. The angle is in
  204. [-pi, pi].
  205. Adapted function of dihedral_angle_from_coordinates.py
  206. by Eric Alcaide.
  207. Source : https://gist.github.com/EricAlcaide
  208. URL : https://gist.github.com/EricAlcaide/30d6bfdb8358d3a57d010c9a501fda56
  209. """
  210. #convert coords to numpy arrays
  211. u1 = np.array(u1)
  212. u2 = np.array(u2)
  213. u3 = np.array(u3)
  214. u4 = np.array(u4)
  215. a1 = u2 - u1
  216. a2 = u3 - u2
  217. a3 = u4 - u3
  218. v1 = np.cross(a1, a2)
  219. v1 = v1 / (v1 * v1).sum(-1)**0.5
  220. v2 = np.cross(a2, a3)
  221. v2 = v2 / (v2 * v2).sum(-1)**0.5
  222. porm = np.sign((v1 * a3).sum(-1))
  223. rad = np.arccos((v1*v2).sum(-1) / ((v1**2).sum(-1) * (v2**2).sum(-1))**0.5)
  224. if not porm == 0:
  225. rad = rad * porm
  226. deg_angle = rad*(180/math.pi)
  227. return deg_angle
  228. def get_chirality(residues):
  229. for i in range(1,len(residues)-2):
  230. chirality = {}
  231. angle = calc_dihedral(residues[i-1].atoms["CA"].coords,
  232. residues[i].atoms["CA"].coords,
  233. residues[i+1].atoms["CA"].coords,
  234. residues[i+2].atoms["CA"].coords)
  235. if(angle>0 and angle<=180):
  236. sign="+"
  237. print("chirality", residues[i].resid, "+", angle)
  238. if(angle<=0 and angle > -180):
  239. sign="-"
  240. print("chirality", residues[i].resid, "-", angle)
  241. chirality[residues[i].resid] = sign
  242. return chirality