Réimplémentation du programme DSSP en Python

atom.py 18KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460
  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.res_letter = self.get_amino_letter(atom.res_name)
  28. self.chain_id = atom.chain_id
  29. self.insertion_code = atom.insertion_code
  30. self.indice = indice
  31. def get_amino_letter(self, res_name):
  32. code3 = ['ALA', 'ARG', 'ASN', 'ASP', 'CYS', 'GLU', 'GLN', 'GLY',
  33. 'HIS', 'ILE', 'LEU', 'LYS', 'MET', 'PHE', 'PRO', 'SER',
  34. 'THR', 'TRP', 'TYR', 'VAL']
  35. code1 = ['A','R','N','D','C','E','Q','G','H','I','L','K','M','F','P',
  36. 'S','T','W','Y','V']
  37. return code1[code3.index(res_name)]
  38. def h_bond(self, res2):
  39. if("H" not in res2.atoms.keys()):
  40. return(False)
  41. # dimensionnal factor, in kcal/mole
  42. f = 332
  43. # partial charges
  44. q1 = 0.42
  45. q2 = 0.20
  46. # distance between O-N atoms, in angströms
  47. r_ON = self.atoms["O"].dist_atoms(res2.atoms["N"])
  48. # distance between C-H atoms, in angströms
  49. r_CH = self.atoms["C"].dist_atoms(res2.atoms["H"])
  50. # distance between O-H atoms, in angströms
  51. r_OH = self.atoms["O"].dist_atoms(res2.atoms["H"])
  52. # distance between C-N atoms, in angströms
  53. r_CN = self.atoms["C"].dist_atoms(res2.atoms["N"])
  54. # electrostatic interaction energy, in kcal/mole
  55. E = q1*q2*(1/r_ON + 1/r_CH - 1/r_OH - 1/r_CN)*f
  56. return(E)
  57. def get_turns(self, residues):
  58. """
  59. Get all the turns from a specific residue.
  60. """
  61. turns = {}
  62. i = residues.index(self)
  63. k = 0
  64. for j in range(3,6):
  65. if(i+j<len(residues)):
  66. if(self.h_bond(residues[i+j])<-0.5):
  67. k = j
  68. if k != 0:
  69. #print(k,"TURN", residues[i].resid, residues[i+k].resid)
  70. return Turn(k,residues[i].resid)
  71. return False
  72. def get_bends(self, residues):
  73. i = residues.index(self)
  74. if i >=2 and i <len(residues)-2:
  75. angle = math.degrees(vector_angles(vectors_substr(position_vector(residues[i].atoms["CA"].coords),
  76. position_vector(residues[i-2].atoms["CA"].coords)),
  77. vectors_substr(position_vector(residues[i+2].atoms["CA"].coords),
  78. position_vector(residues[i].atoms["CA"].coords))))
  79. return angle
  80. else:
  81. return 360.0
  82. def get_bridges(self, residues):
  83. bridges = {}
  84. bridge = {}
  85. strongest_bridge = {}
  86. i = residues.index(self)
  87. if(i >= 1 and i < len(residues)-4):
  88. E_min = 0
  89. for j in range(i+2,len(residues)-1):
  90. # select triplet with the minimal energy
  91. if(residues[i-1].h_bond(residues[j])<-0.5
  92. and residues[j].h_bond(residues[i+1])<-0.5):
  93. bridge = {'res1':residues[i-1].h_bond(residues[j]),
  94. 'res2':residues[j].h_bond(residues[i+1]),
  95. 'ipos':residues[i].resid,
  96. 'jpos':residues[j].resid,
  97. 'btype':"para"}
  98. if(residues[j-1].h_bond(residues[i])<-0.5
  99. and residues[i].h_bond(residues[j+1])<-0.5):
  100. bridge = {'res1':residues[j-1].h_bond(residues[i]),
  101. 'res2':residues[i].h_bond(residues[j+1]),
  102. 'ipos':residues[i].resid,
  103. 'jpos':residues[j].resid,
  104. 'btype':"para"}
  105. if(residues[i].h_bond(residues[j])<-0.5
  106. and residues[j].h_bond(residues[i])<-0.5):
  107. bridge = {'res1':residues[i].h_bond(residues[j]),
  108. 'res2':residues[j].h_bond(residues[i]),
  109. 'ipos':residues[i].resid,
  110. 'jpos':residues[j].resid,
  111. 'btype':"anti"}
  112. if(residues[i-1].h_bond(residues[j+1])<-0.5
  113. and residues[j-1].h_bond(residues[i+1])<-0.5):
  114. bridge = {'res1':residues[i-1].h_bond(residues[j+1]),
  115. 'res2':residues[j-1].h_bond(residues[i+1]),
  116. 'ipos':residues[i].resid,
  117. 'jpos':residues[j].resid,
  118. 'btype':"anti"}
  119. if(bridge):
  120. if(bridge['res1']+bridge['res2']<E_min):
  121. E_min = bridge['res1']+bridge['res2']
  122. strongest_bridge = bridge
  123. bridge = {}
  124. coord_bridge = [i,j]
  125. # finally add the strongest bridge at i and j pos
  126. if(strongest_bridge):
  127. bridges[coord_bridge[0]] = (Bridge(strongest_bridge['btype'],
  128. strongest_bridge['ipos'],
  129. strongest_bridge['jpos']))
  130. bridges[coord_bridge[1]] = (Bridge(strongest_bridge['btype'],
  131. strongest_bridge['jpos'],
  132. strongest_bridge['ipos']))
  133. if(len(bridges)>0):
  134. return(bridges[coord_bridge[0]])
  135. else:
  136. return(False)
  137. def get_helix(self, residues):
  138. """
  139. Return if there is an helix at a given residue,
  140. as well as its type.
  141. """
  142. i = residues.index(self)
  143. # if there are no turns or it is the first residue, skip
  144. if i == 0:
  145. return False
  146. if(self.get_turns(residues) and residues[i-1].get_turns(residues)):
  147. print(self.get_turns(residues).turn_type,"- HELIX at", residues[i].resid)
  148. return(self.get_turns(residues).turn_type, residues[i].resid)
  149. return(False)
  150. def get_ladder(self, residues):
  151. #ladders = {}
  152. i = residues.index(self)
  153. if i != 0:
  154. if self.get_bridges(residues):
  155. if (residues[i-1].get_bridges(residues)):
  156. local_bridge = self.get_bridges(residues)
  157. consec_bridge = residues[i-1].get_bridges(residues)
  158. if local_bridge.bridge_type == consec_bridge.bridge_type:
  159. #print("ladder", consec_bridge.res_num, local_bridge.res_num)
  160. ladder = {'start':consec_bridge.res_num,
  161. 'end':local_bridge.res_num,
  162. 'bridges':[consec_bridge, local_bridge]}
  163. return ladder
  164. return False
  165. def get_tco(self, residues):
  166. i = residues.index(self)
  167. if(i!=0):
  168. res2 = residues[i-1]
  169. CO_res1 = vector_from_pos(self.atoms["C"].coords,
  170. self.atoms["O"].coords)
  171. CO_res2 = vector_from_pos(res2.atoms["C"].coords,
  172. res2.atoms["O"].coords)
  173. angle = vector_angles(CO_res1, CO_res2)
  174. else:
  175. angle = math.pi/2
  176. return(math.cos(angle))
  177. def get_chirality(self, residues):
  178. i = residues.index(self)
  179. if (i >=1 and i < len(residues)-2):
  180. chirality = {}
  181. angle = calc_dihedral(residues[i-1].atoms["CA"].coords,
  182. residues[i].atoms["CA"].coords,
  183. residues[i+1].atoms["CA"].coords,
  184. residues[i+2].atoms["CA"].coords)
  185. if(angle>0 and angle<=180):
  186. sign="+"
  187. if(angle<=0 and angle > -180):
  188. sign="-"
  189. else:
  190. angle = 360.0
  191. sign = ''
  192. return [angle, sign]
  193. def get_phi_psi(self, residues):
  194. i = residues.index(self)
  195. if(i==0):
  196. phi = 360.0
  197. else:
  198. phi = calc_dihedral(residues[i-1].atoms["C"].coords,
  199. residues[i].atoms["N"].coords,
  200. residues[i].atoms["CA"].coords,
  201. residues[i].atoms["C"].coords)
  202. if(i==len(residues)-1):
  203. psi = 360.0
  204. else:
  205. psi = calc_dihedral(residues[i].atoms["N"].coords,
  206. residues[i].atoms["CA"].coords,
  207. residues[i].atoms["C"].coords,
  208. residues[i+1].atoms["N"].coords)
  209. return((phi, psi))
  210. def get_bridges(residues):
  211. bridges = {}
  212. bridge = {}
  213. strongest_bridge = {}
  214. for i in range(1,len(residues)-4):
  215. E_min = 0
  216. for j in range(i+2,len(residues)-1):
  217. # select triplet with the minimal energy
  218. if(residues[i-1].h_bond(residues[j])<-0.5
  219. and residues[j].h_bond(residues[i+1])<-0.5):
  220. bridge = {'res1':residues[i-1].h_bond(residues[j]),
  221. 'res2':residues[j].h_bond(residues[i+1]),
  222. 'ipos':residues[i].resid,
  223. 'jpos':residues[j].resid,
  224. 'i':residues[i].indice,
  225. 'j':residues[j].indice,
  226. 'btype':"para"}
  227. if(residues[j-1].h_bond(residues[i])<-0.5
  228. and residues[i].h_bond(residues[j+1])<-0.5):
  229. bridge = {'res1':residues[j-1].h_bond(residues[i]),
  230. 'res2':residues[i].h_bond(residues[j+1]),
  231. 'ipos':residues[i].resid,
  232. 'jpos':residues[j].resid,
  233. 'i':residues[i].indice,
  234. 'j':residues[j].indice,
  235. 'btype':"para"}
  236. if(residues[i].h_bond(residues[j])<-0.5
  237. and residues[j].h_bond(residues[i])<-0.5):
  238. bridge = {'res1':residues[i].h_bond(residues[j]),
  239. 'res2':residues[j].h_bond(residues[i]),
  240. 'ipos':residues[i].resid,
  241. 'jpos':residues[j].resid,
  242. 'i':residues[i].indice,
  243. 'j':residues[j].indice,
  244. 'btype':"anti"}
  245. if(residues[i-1].h_bond(residues[j+1])<-0.5
  246. and residues[j-1].h_bond(residues[i+1])<-0.5):
  247. bridge = {'res1':residues[i-1].h_bond(residues[j+1]),
  248. 'res2':residues[j-1].h_bond(residues[i+1]),
  249. 'ipos':residues[i].resid,
  250. 'jpos':residues[j].resid,
  251. 'i':residues[i].indice,
  252. 'j':residues[j].indice,
  253. 'btype':"anti"}
  254. if(bridge):
  255. if(bridge['res1']+bridge['res2']<E_min):
  256. E_min = bridge['res1']+bridge['res2']
  257. strongest_bridge = bridge
  258. coord_bridge = [i,j]
  259. bridge = {}
  260. # finally add the strongest bridge at i and j pos
  261. if(strongest_bridge):
  262. bridges[strongest_bridge['i']] = (Bridge(strongest_bridge['btype'],
  263. strongest_bridge['ipos'],
  264. strongest_bridge['jpos'],
  265. [strongest_bridge['i'],
  266. strongest_bridge['j']]))
  267. bridges[strongest_bridge['j']] = (Bridge(strongest_bridge['btype'],
  268. strongest_bridge['jpos'],
  269. strongest_bridge['ipos'],
  270. [strongest_bridge['i'],
  271. strongest_bridge['j']]))
  272. if(len(bridges)>0):
  273. return(bridges)
  274. else:
  275. return(False)
  276. def get_ladders(bridges, residues):
  277. ladders = {}
  278. i = 1
  279. while i < len(residues):
  280. k = 1
  281. if i in bridges.keys():
  282. temp_bridges = [bridges[i]]
  283. while ((i+k in bridges.keys()) and
  284. (bridges[i].bridge_type == bridges[i+k].bridge_type)):
  285. temp_bridges.append(bridges[i+k])
  286. k+=1
  287. if k>1:
  288. #print("ladder", bridges[i].res_num, bridges[i+k-1].res_num)
  289. ladders[i] = {'start':bridges[i].res_num,
  290. 'end':bridges[i+k-1].res_num,
  291. 'bridges':temp_bridges,
  292. 'i':i,
  293. 'j':i+k-1}
  294. i+=k-1
  295. else:
  296. i+=1
  297. return ladders
  298. def connected_ladders(ladd_1, ladd_2):
  299. links = []
  300. for bridge in ladd_1['bridges']:
  301. if bridge.res_partner in res_list(ladd_2):
  302. # return([ladd_1['i'], ladd_1['j'], bridge.i, bridge.j,
  303. # ladd_2['i'], ladd_2['j']])
  304. return ladd_2
  305. return False
  306. def get_sheets(ladders):
  307. """
  308. Bridges between ladders.
  309. Check if 1 bridge between one ladder and one or more other ladders.
  310. Iterate over all residues of one ladder and check if bridge with other residues
  311. of the other ladders.
  312. """
  313. ladds = [ elem for elem in ladders.values() ]
  314. sheets = {}
  315. corresp = {}
  316. for ladd1 in ladds:
  317. for ladd2 in ladds:
  318. if connected_ladders(ladd1, ladd2)!=False:
  319. corresp_list = [ elem for elem in corresp.keys() ]
  320. if ladd1['i'] not in corresp_list and ladd2['i'] not in corresp_list:
  321. ind = len(sheets.keys())
  322. sheets[ind] = []
  323. sheets[ind].append(ladd1)
  324. sheets[ind].append(ladd2)
  325. corresp[ladd1['i']] = ind
  326. corresp[ladd2['i']] = ind
  327. elif ladd2 not in corresp_list and ladd1 in corresp_list:
  328. sheets[corresp[ladd1['i']]].append(ladd2)
  329. corresp[ladd2['i']] = corresp[ladd1['i']]
  330. elif ladd1 not in corresp_list and ladd2 in corresp_list:
  331. sheets[corresp[ladd2['i']]].append(ladd1)
  332. corresp[ladd1['i']] = corresp[ladd2['i']]
  333. return sheets
  334. def get_sheets2(ladders):
  335. """
  336. Bridges between ladders.
  337. Check if 1 bridge between one ladder and one or more other ladders.
  338. Iterate over all residues of one ladder and check if bridge with other residues
  339. of the other ladders.
  340. """
  341. sheets = {}
  342. for ladder in ladders:
  343. for ladd2 in ladders:
  344. if connected_ladders(ladders[ladder], ladders[ladd2]):
  345. pass
  346. print("ladder",ladders[ladder]['i'], ladders[ladder]['j'],"bridge",bridge.i, bridge.j,
  347. "ladder 2",ladders[ladd2]['i'], ladders[ladd2]['j'])
  348. def res_list(ladder):
  349. # TODO : method in ladder class
  350. l=[]
  351. for i in range(ladder['start'], ladder['end']):
  352. l.append(i)
  353. return(l)
  354. def get_bends(residues):
  355. bends = {}
  356. for i in range(2,len(residues)-2):
  357. angle = math.degrees(vector_angles(vectors_substr(position_vector(residues[i].atoms["CA"].coords),
  358. position_vector(residues[i-2].atoms["CA"].coords)),
  359. vectors_substr(position_vector(residues[i+2].atoms["CA"].coords),
  360. position_vector(residues[i].atoms["CA"].coords))))
  361. if(angle>70):
  362. print("angle", residues[i].resid, angle)
  363. bends[residues[i].resid] = angle
  364. return(bends)
  365. def get_chirality(residues):
  366. for i in range(1,len(residues)-2):
  367. chirality = {}
  368. angle = calc_dihedral(residues[i-1].atoms["CA"].coords,
  369. residues[i].atoms["CA"].coords,
  370. residues[i+1].atoms["CA"].coords,
  371. residues[i+2].atoms["CA"].coords)
  372. if(angle>0 and angle<=180):
  373. sign="+"
  374. print("chirality", residues[i].resid, "+", angle)
  375. if(angle<=0 and angle > -180):
  376. sign="-"
  377. print("chirality", residues[i].resid, "-", angle)
  378. chirality[residues[i].resid] = sign
  379. return chirality
  380. def get_phi_psi(residues):
  381. for i in range(len(residues)):
  382. if(i==0):
  383. phi = 360.0
  384. else:
  385. phi = calc_dihedral(residues[i-1].atoms["C"].coords,
  386. residues[i].atoms["N"].coords,
  387. residues[i].atoms["CA"].coords,
  388. residues[i].atoms["C"].coords)
  389. if(i==len(residues)-1):
  390. psi = 360.0
  391. else:
  392. psi = calc_dihedral(residues[i].atoms["N"].coords,
  393. residues[i].atoms["CA"].coords,
  394. residues[i].atoms["C"].coords,
  395. residues[i+1].atoms["N"].coords)
  396. print("ANGLES", i, phi, psi)
  397. def get_TCO(res1, res2):
  398. CO_res1 = vector_from_pos(res1.atoms["C"].coords,
  399. res1.atoms["O"].coords)
  400. CO_res2 = vector_from_pos(res2.atoms["C"].coords,
  401. res2.atoms["O"].coords)
  402. angle = vector_angles(CO_res1, CO_res2)
  403. return(math.cos(angle))