Réimplémentation du programme DSSP en Python

atom.py 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  1. import math
  2. from structure import *
  3. class Atom:
  4. def dist_atoms(self, atom2):
  5. return(math.sqrt((self.coord_x-atom2.coord_x)**2 +
  6. (self.coord_y-atom2.coord_y)**2 +
  7. (self.coord_z-atom2.coord_z)**2))
  8. def __init__(self, atom_id, atom_name, res_name, chain_id,
  9. res_seq_nb, insertion_code, coordinates):
  10. self.atom_id = atom_id
  11. self.atom_name = atom_name
  12. self.res_name = res_name
  13. self.chain_id = chain_id
  14. self.res_seq_nb = res_seq_nb
  15. self.insertion_code = insertion_code
  16. self.coord_x = coordinates[0]
  17. self.coord_y = coordinates[1]
  18. self.coord_z = coordinates[2]
  19. self.coords = coordinates
  20. class Residue:
  21. def __init__(self, atoms_list, indice):
  22. self.atoms = {}
  23. for atom in atoms_list:
  24. self.atoms[atom.atom_name] = atom
  25. self.resid = atom.res_seq_nb
  26. self.res_name = atom.res_name
  27. self.chain_id = atom.chain_id
  28. self.insertion_code = atom.insertion_code
  29. self.indice = indice
  30. def h_bond(self, res2):
  31. if("H" not in res2.atoms.keys()):
  32. return(False)
  33. # dimensionnal factor, in kcal/mole
  34. f = 332
  35. # partial charges
  36. q1 = 0.42
  37. q2 = 0.20
  38. # distance between O-N atoms, in angströms
  39. r_ON = self.atoms["O"].dist_atoms(res2.atoms["N"])
  40. # distance between C-H atoms, in angströms
  41. r_CH = self.atoms["C"].dist_atoms(res2.atoms["H"])
  42. # distance between O-H atoms, in angströms
  43. r_OH = self.atoms["O"].dist_atoms(res2.atoms["H"])
  44. # distance between C-N atoms, in angströms
  45. r_CN = self.atoms["C"].dist_atoms(res2.atoms["N"])
  46. # electrostatic interaction energy, in kcal/mole
  47. E = q1*q2*(1/r_ON + 1/r_CH - 1/r_OH - 1/r_CN)*f
  48. return(E)
  49. def get_turns(self, residues):
  50. """
  51. Get all the turns from a specific residue.
  52. """
  53. turns = {}
  54. i = residues.index(self)
  55. k = 0
  56. for j in range(3,6):
  57. if(i+j<len(residues)):
  58. if(self.h_bond(residues[i+j])<-0.5):
  59. k = j
  60. if k != 0:
  61. #print(k,"TURN", residues[i].resid, residues[i+k].resid)
  62. return Turn(k,residues[i].resid)
  63. return False
  64. def get_bridges(self, residues):
  65. bridges = {}
  66. bridge = {}
  67. strongest_bridge = {}
  68. i = residues.index(self)
  69. if(i >= 1 and i < len(residues)-4):
  70. E_min = 0
  71. for j in range(i+2,len(residues)-1):
  72. # select triplet with the minimal energy
  73. if(residues[i-1].h_bond(residues[j])<-0.5
  74. and residues[j].h_bond(residues[i+1])<-0.5):
  75. bridge = {'res1':residues[i-1].h_bond(residues[j]),
  76. 'res2':residues[j].h_bond(residues[i+1]),
  77. 'ipos':residues[i].resid,
  78. 'jpos':residues[j].resid,
  79. 'btype':"para"}
  80. if(residues[j-1].h_bond(residues[i])<-0.5
  81. and residues[i].h_bond(residues[j+1])<-0.5):
  82. bridge = {'res1':residues[j-1].h_bond(residues[i]),
  83. 'res2':residues[i].h_bond(residues[j+1]),
  84. 'ipos':residues[i].resid,
  85. 'jpos':residues[j].resid,
  86. 'btype':"para"}
  87. if(residues[i].h_bond(residues[j])<-0.5
  88. and residues[j].h_bond(residues[i])<-0.5):
  89. bridge = {'res1':residues[i].h_bond(residues[j]),
  90. 'res2':residues[j].h_bond(residues[i]),
  91. 'ipos':residues[i].resid,
  92. 'jpos':residues[j].resid,
  93. 'btype':"anti"}
  94. if(residues[i-1].h_bond(residues[j+1])<-0.5
  95. and residues[j-1].h_bond(residues[i+1])<-0.5):
  96. bridge = {'res1':residues[i-1].h_bond(residues[j+1]),
  97. 'res2':residues[j-1].h_bond(residues[i+1]),
  98. 'ipos':residues[i].resid,
  99. 'jpos':residues[j].resid,
  100. 'btype':"anti"}
  101. if(bridge):
  102. if(bridge['res1']+bridge['res2']<E_min):
  103. E_min = bridge['res1']+bridge['res2']
  104. strongest_bridge = bridge
  105. bridge = {}
  106. coord_bridge = [i,j]
  107. # finally add the strongest bridge at i and j pos
  108. if(strongest_bridge):
  109. bridges[coord_bridge[0]] = (Bridge(strongest_bridge['btype'],
  110. strongest_bridge['ipos'],
  111. strongest_bridge['jpos']))
  112. bridges[coord_bridge[1]] = (Bridge(strongest_bridge['btype'],
  113. strongest_bridge['jpos'],
  114. strongest_bridge['ipos']))
  115. if(len(bridges)>0):
  116. return(bridges[coord_bridge[0]])
  117. else:
  118. return(False)
  119. def get_helix(self, residues):
  120. """
  121. Return if there is an helix at a given residue,
  122. as well as its type.
  123. """
  124. i = residues.index(self)
  125. # if there are no turns or it is the first residue, skip
  126. if i == 0:
  127. return False
  128. if(self.get_turns(residues) and residues[i-1].get_turns(residues)):
  129. print(self.get_turns(residues).turn_type,"- HELIX at", residues[i].resid)
  130. return(self.get_turns(residues).turn_type, residues[i].resid)
  131. return(False)
  132. def get_ladder(self, residues):
  133. #ladders = {}
  134. i = residues.index(self)
  135. if i != 0:
  136. if self.get_bridges(residues):
  137. if (residues[i-1].get_bridges(residues)):
  138. local_bridge = self.get_bridges(residues)
  139. consec_bridge = residues[i-1].get_bridges(residues)
  140. if local_bridge.bridge_type == consec_bridge.bridge_type:
  141. #print("ladder", consec_bridge.res_num, local_bridge.res_num)
  142. ladder = {'start':consec_bridge.res_num,
  143. 'end':local_bridge.res_num,
  144. 'bridges':[consec_bridge, local_bridge]}
  145. return ladder
  146. return False
  147. def get_bridges(residues):
  148. bridges = {}
  149. bridge = {}
  150. strongest_bridge = {}
  151. for i in range(1,len(residues)-4):
  152. E_min = 0
  153. for j in range(i+2,len(residues)-1):
  154. # select triplet with the minimal energy
  155. if(residues[i-1].h_bond(residues[j])<-0.5
  156. and residues[j].h_bond(residues[i+1])<-0.5):
  157. bridge = {'res1':residues[i-1].h_bond(residues[j]),
  158. 'res2':residues[j].h_bond(residues[i+1]),
  159. 'ipos':residues[i].resid,
  160. 'jpos':residues[j].resid,
  161. 'i':residues[i].indice,
  162. 'j':residues[j].indice,
  163. 'btype':"para"}
  164. if(residues[j-1].h_bond(residues[i])<-0.5
  165. and residues[i].h_bond(residues[j+1])<-0.5):
  166. bridge = {'res1':residues[j-1].h_bond(residues[i]),
  167. 'res2':residues[i].h_bond(residues[j+1]),
  168. 'ipos':residues[i].resid,
  169. 'jpos':residues[j].resid,
  170. 'i':residues[i].indice,
  171. 'j':residues[j].indice,
  172. 'btype':"para"}
  173. if(residues[i].h_bond(residues[j])<-0.5
  174. and residues[j].h_bond(residues[i])<-0.5):
  175. bridge = {'res1':residues[i].h_bond(residues[j]),
  176. 'res2':residues[j].h_bond(residues[i]),
  177. 'ipos':residues[i].resid,
  178. 'jpos':residues[j].resid,
  179. 'i':residues[i].indice,
  180. 'j':residues[j].indice,
  181. 'btype':"anti"}
  182. if(residues[i-1].h_bond(residues[j+1])<-0.5
  183. and residues[j-1].h_bond(residues[i+1])<-0.5):
  184. bridge = {'res1':residues[i-1].h_bond(residues[j+1]),
  185. 'res2':residues[j-1].h_bond(residues[i+1]),
  186. 'ipos':residues[i].resid,
  187. 'jpos':residues[j].resid,
  188. 'i':residues[i].indice,
  189. 'j':residues[j].indice,
  190. 'btype':"anti"}
  191. if(bridge):
  192. if(bridge['res1']+bridge['res2']<E_min):
  193. E_min = bridge['res1']+bridge['res2']
  194. strongest_bridge = bridge
  195. coord_bridge = [i,j]
  196. bridge = {}
  197. # finally add the strongest bridge at i and j pos
  198. if(strongest_bridge):
  199. bridges[strongest_bridge['i']] = (Bridge(strongest_bridge['btype'],
  200. strongest_bridge['ipos'],
  201. strongest_bridge['jpos'],
  202. [strongest_bridge['i'],
  203. strongest_bridge['j']]))
  204. bridges[strongest_bridge['j']] = (Bridge(strongest_bridge['btype'],
  205. strongest_bridge['jpos'],
  206. strongest_bridge['ipos'],
  207. [strongest_bridge['i'],
  208. strongest_bridge['j']]))
  209. if(len(bridges)>0):
  210. return(bridges)
  211. else:
  212. return(False)
  213. def get_ladders(bridges, residues):
  214. ladders = {}
  215. i = 1
  216. while i < len(residues):
  217. k = 1
  218. if i in bridges.keys():
  219. temp_bridges = [bridges[i]]
  220. while ((i+k in bridges.keys()) and
  221. (bridges[i].bridge_type == bridges[i+k].bridge_type)):
  222. temp_bridges.append(bridges[i+k])
  223. k+=1
  224. if k>1:
  225. #print("ladder", bridges[i].res_num, bridges[i+k-1].res_num)
  226. ladders[i] = {'start':bridges[i].res_num,
  227. 'end':bridges[i+k-1].res_num,
  228. 'bridges':temp_bridges,
  229. 'i':i,
  230. 'j':i+k-1}
  231. i+=k-1
  232. else:
  233. i+=1
  234. return ladders
  235. def connected_ladders(ladd_1, ladd_2):
  236. links = []
  237. for bridge in ladd_1['bridges']:
  238. if bridge.res_partner in res_list(ladd_2):
  239. return([ladd_1['i'], ladd_1['j'], bridge.i, bridge.j,
  240. ladd_2['i'], ladd_2['j']])
  241. return False
  242. def get_sheets(ladders):
  243. """
  244. Bridges between ladders.
  245. Check if 1 bridge between one ladder and one or more other ladders.
  246. Iterate over all residues of one ladder and check if bridge with other residues
  247. of the other ladders.
  248. """
  249. ladds = [ elem for elem in ladders.values() ]
  250. sheets = {}
  251. i = 0
  252. while i < len(ladds):
  253. j = i+1
  254. ladd = ladds[i]
  255. ladd1 = ladds[i]
  256. ladd2 = ladds[j]
  257. while j < len(ladds):
  258. print(connected_ladders(ladd1, ladd2))
  259. if connected_ladders(ladd1, ladd2)!=False:
  260. ladd1 = ladd2
  261. #print(connected_ladders(ladd1, ladd2)[4])
  262. ladd2 = ladders[connected_ladders(ladd1, ladd2)[4]]
  263. j+=1
  264. print(ladd['i'], ladd2['i'])
  265. i+=1
  266. # for ladder in ladders:
  267. # for ladd2 in ladders:
  268. # if connected_ladders(ladders[ladder], ladders[ladd2]):
  269. # pass
  270. #print("ladder",ladders[ladder]['i'], ladders[ladder]['j'],"bridge",bridge.i, bridge.j,
  271. # "ladder 2",ladders[ladd2]['i'], ladders[ladd2]['j'])
  272. def res_list(ladder):
  273. # TODO : method in ladder class
  274. l=[]
  275. for i in range(ladder['start'], ladder['end']):
  276. l.append(i)
  277. return(l)
  278. def get_bends(residues):
  279. bends = {}
  280. for i in range(2,len(residues)-2):
  281. angle = math.degrees(vector_angles(vectors_substr(position_vector(residues[i].atoms["CA"].coords),
  282. position_vector(residues[i-2].atoms["CA"].coords)),
  283. vectors_substr(position_vector(residues[i+2].atoms["CA"].coords),
  284. position_vector(residues[i].atoms["CA"].coords))))
  285. if(angle>70):
  286. print("angle", residues[i].resid, angle)
  287. bends[residues[i].resid] = angle
  288. return(bends)
  289. def get_chirality(residues):
  290. for i in range(1,len(residues)-2):
  291. chirality = {}
  292. angle = calc_dihedral(residues[i-1].atoms["CA"].coords,
  293. residues[i].atoms["CA"].coords,
  294. residues[i+1].atoms["CA"].coords,
  295. residues[i+2].atoms["CA"].coords)
  296. if(angle>0 and angle<=180):
  297. sign="+"
  298. print("chirality", residues[i].resid, "+", angle)
  299. if(angle<=0 and angle > -180):
  300. sign="-"
  301. print("chirality", residues[i].resid, "-", angle)
  302. chirality[residues[i].resid] = sign
  303. return chirality
  304. def get_phi_psi(residues):
  305. for i in range(len(residues)):
  306. if(i==0):
  307. phi = 360.0
  308. else:
  309. phi = calc_dihedral(residues[i-1].atoms["C"].coords,
  310. residues[i].atoms["N"].coords,
  311. residues[i].atoms["CA"].coords,
  312. residues[i].atoms["C"].coords)
  313. if(i==len(residues)-1):
  314. psi = 360.0
  315. else:
  316. psi = calc_dihedral(residues[i].atoms["N"].coords,
  317. residues[i].atoms["CA"].coords,
  318. residues[i].atoms["C"].coords,
  319. residues[i+1].atoms["N"].coords)
  320. print("ANGLES", i, phi, psi)
  321. def get_TCO(res1, res2):
  322. CO_res1 = vector_from_pos(res1.atoms["C"].coords,
  323. res1.atoms["O"].coords)
  324. CO_res2 = vector_from_pos(res2.atoms["C"].coords,
  325. res2.atoms["O"].coords)
  326. angle = vector_angles(CO_res1, CO_res2)
  327. return(math.cos(angle))