biotite 1.6.0__cp314-cp314-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (354) hide show
  1. biotite/__init__.py +18 -0
  2. biotite/application/__init__.py +69 -0
  3. biotite/application/application.py +276 -0
  4. biotite/application/autodock/__init__.py +12 -0
  5. biotite/application/autodock/app.py +500 -0
  6. biotite/application/blast/__init__.py +14 -0
  7. biotite/application/blast/alignment.py +92 -0
  8. biotite/application/blast/webapp.py +426 -0
  9. biotite/application/clustalo/__init__.py +12 -0
  10. biotite/application/clustalo/app.py +223 -0
  11. biotite/application/dssp/__init__.py +12 -0
  12. biotite/application/dssp/app.py +216 -0
  13. biotite/application/localapp.py +342 -0
  14. biotite/application/mafft/__init__.py +12 -0
  15. biotite/application/mafft/app.py +116 -0
  16. biotite/application/msaapp.py +363 -0
  17. biotite/application/muscle/__init__.py +13 -0
  18. biotite/application/muscle/app3.py +227 -0
  19. biotite/application/muscle/app5.py +163 -0
  20. biotite/application/sra/__init__.py +18 -0
  21. biotite/application/sra/app.py +447 -0
  22. biotite/application/tantan/__init__.py +12 -0
  23. biotite/application/tantan/app.py +199 -0
  24. biotite/application/util.py +77 -0
  25. biotite/application/viennarna/__init__.py +18 -0
  26. biotite/application/viennarna/rnaalifold.py +310 -0
  27. biotite/application/viennarna/rnafold.py +254 -0
  28. biotite/application/viennarna/rnaplot.py +208 -0
  29. biotite/application/viennarna/util.py +77 -0
  30. biotite/application/webapp.py +76 -0
  31. biotite/copyable.py +71 -0
  32. biotite/database/__init__.py +23 -0
  33. biotite/database/afdb/__init__.py +12 -0
  34. biotite/database/afdb/download.py +202 -0
  35. biotite/database/entrez/__init__.py +15 -0
  36. biotite/database/entrez/check.py +66 -0
  37. biotite/database/entrez/dbnames.py +101 -0
  38. biotite/database/entrez/download.py +224 -0
  39. biotite/database/entrez/key.py +44 -0
  40. biotite/database/entrez/query.py +263 -0
  41. biotite/database/error.py +16 -0
  42. biotite/database/pubchem/__init__.py +21 -0
  43. biotite/database/pubchem/download.py +259 -0
  44. biotite/database/pubchem/error.py +30 -0
  45. biotite/database/pubchem/query.py +819 -0
  46. biotite/database/pubchem/throttle.py +98 -0
  47. biotite/database/rcsb/__init__.py +13 -0
  48. biotite/database/rcsb/download.py +191 -0
  49. biotite/database/rcsb/query.py +963 -0
  50. biotite/database/uniprot/__init__.py +13 -0
  51. biotite/database/uniprot/check.py +40 -0
  52. biotite/database/uniprot/download.py +127 -0
  53. biotite/database/uniprot/query.py +292 -0
  54. biotite/file.py +244 -0
  55. biotite/interface/__init__.py +19 -0
  56. biotite/interface/openmm/__init__.py +20 -0
  57. biotite/interface/openmm/state.py +93 -0
  58. biotite/interface/openmm/system.py +227 -0
  59. biotite/interface/pymol/__init__.py +201 -0
  60. biotite/interface/pymol/cgo.py +346 -0
  61. biotite/interface/pymol/convert.py +185 -0
  62. biotite/interface/pymol/display.py +267 -0
  63. biotite/interface/pymol/object.py +1228 -0
  64. biotite/interface/pymol/shapes.py +178 -0
  65. biotite/interface/pymol/startup.py +169 -0
  66. biotite/interface/rdkit/__init__.py +19 -0
  67. biotite/interface/rdkit/mol.py +491 -0
  68. biotite/interface/version.py +94 -0
  69. biotite/interface/warning.py +19 -0
  70. biotite/sequence/__init__.py +84 -0
  71. biotite/sequence/align/__init__.py +199 -0
  72. biotite/sequence/align/alignment.py +763 -0
  73. biotite/sequence/align/banded.cp314-win_amd64.pyd +0 -0
  74. biotite/sequence/align/banded.pyx +652 -0
  75. biotite/sequence/align/buckets.py +71 -0
  76. biotite/sequence/align/cigar.py +425 -0
  77. biotite/sequence/align/kmeralphabet.cp314-win_amd64.pyd +0 -0
  78. biotite/sequence/align/kmeralphabet.pyx +595 -0
  79. biotite/sequence/align/kmersimilarity.cp314-win_amd64.pyd +0 -0
  80. biotite/sequence/align/kmersimilarity.pyx +233 -0
  81. biotite/sequence/align/kmertable.cp314-win_amd64.pyd +0 -0
  82. biotite/sequence/align/kmertable.pyx +3411 -0
  83. biotite/sequence/align/localgapped.cp314-win_amd64.pyd +0 -0
  84. biotite/sequence/align/localgapped.pyx +892 -0
  85. biotite/sequence/align/localungapped.cp314-win_amd64.pyd +0 -0
  86. biotite/sequence/align/localungapped.pyx +279 -0
  87. biotite/sequence/align/matrix.py +631 -0
  88. biotite/sequence/align/matrix_data/3Di.mat +24 -0
  89. biotite/sequence/align/matrix_data/BLOSUM100.mat +31 -0
  90. biotite/sequence/align/matrix_data/BLOSUM30.mat +31 -0
  91. biotite/sequence/align/matrix_data/BLOSUM35.mat +31 -0
  92. biotite/sequence/align/matrix_data/BLOSUM40.mat +31 -0
  93. biotite/sequence/align/matrix_data/BLOSUM45.mat +31 -0
  94. biotite/sequence/align/matrix_data/BLOSUM50.mat +31 -0
  95. biotite/sequence/align/matrix_data/BLOSUM50_13p.mat +25 -0
  96. biotite/sequence/align/matrix_data/BLOSUM50_14.3.mat +25 -0
  97. biotite/sequence/align/matrix_data/BLOSUM50_5.0.mat +25 -0
  98. biotite/sequence/align/matrix_data/BLOSUM55.mat +31 -0
  99. biotite/sequence/align/matrix_data/BLOSUM60.mat +31 -0
  100. biotite/sequence/align/matrix_data/BLOSUM62.mat +31 -0
  101. biotite/sequence/align/matrix_data/BLOSUM62_13p.mat +25 -0
  102. biotite/sequence/align/matrix_data/BLOSUM62_14.3.mat +25 -0
  103. biotite/sequence/align/matrix_data/BLOSUM62_5.0.mat +25 -0
  104. biotite/sequence/align/matrix_data/BLOSUM65.mat +31 -0
  105. biotite/sequence/align/matrix_data/BLOSUM70.mat +31 -0
  106. biotite/sequence/align/matrix_data/BLOSUM75.mat +31 -0
  107. biotite/sequence/align/matrix_data/BLOSUM80.mat +31 -0
  108. biotite/sequence/align/matrix_data/BLOSUM85.mat +31 -0
  109. biotite/sequence/align/matrix_data/BLOSUM90.mat +31 -0
  110. biotite/sequence/align/matrix_data/BLOSUMN.mat +31 -0
  111. biotite/sequence/align/matrix_data/CorBLOSUM49_5.0.mat +25 -0
  112. biotite/sequence/align/matrix_data/CorBLOSUM57_13p.mat +25 -0
  113. biotite/sequence/align/matrix_data/CorBLOSUM57_14.3.mat +25 -0
  114. biotite/sequence/align/matrix_data/CorBLOSUM61_5.0.mat +25 -0
  115. biotite/sequence/align/matrix_data/CorBLOSUM66_13p.mat +25 -0
  116. biotite/sequence/align/matrix_data/CorBLOSUM67_14.3.mat +25 -0
  117. biotite/sequence/align/matrix_data/DAYHOFF.mat +32 -0
  118. biotite/sequence/align/matrix_data/GONNET.mat +26 -0
  119. biotite/sequence/align/matrix_data/IDENTITY.mat +25 -0
  120. biotite/sequence/align/matrix_data/MATCH.mat +25 -0
  121. biotite/sequence/align/matrix_data/NUC.mat +25 -0
  122. biotite/sequence/align/matrix_data/PAM10.mat +34 -0
  123. biotite/sequence/align/matrix_data/PAM100.mat +34 -0
  124. biotite/sequence/align/matrix_data/PAM110.mat +34 -0
  125. biotite/sequence/align/matrix_data/PAM120.mat +34 -0
  126. biotite/sequence/align/matrix_data/PAM130.mat +34 -0
  127. biotite/sequence/align/matrix_data/PAM140.mat +34 -0
  128. biotite/sequence/align/matrix_data/PAM150.mat +34 -0
  129. biotite/sequence/align/matrix_data/PAM160.mat +34 -0
  130. biotite/sequence/align/matrix_data/PAM170.mat +34 -0
  131. biotite/sequence/align/matrix_data/PAM180.mat +34 -0
  132. biotite/sequence/align/matrix_data/PAM190.mat +34 -0
  133. biotite/sequence/align/matrix_data/PAM20.mat +34 -0
  134. biotite/sequence/align/matrix_data/PAM200.mat +34 -0
  135. biotite/sequence/align/matrix_data/PAM210.mat +34 -0
  136. biotite/sequence/align/matrix_data/PAM220.mat +34 -0
  137. biotite/sequence/align/matrix_data/PAM230.mat +34 -0
  138. biotite/sequence/align/matrix_data/PAM240.mat +34 -0
  139. biotite/sequence/align/matrix_data/PAM250.mat +34 -0
  140. biotite/sequence/align/matrix_data/PAM260.mat +34 -0
  141. biotite/sequence/align/matrix_data/PAM270.mat +34 -0
  142. biotite/sequence/align/matrix_data/PAM280.mat +34 -0
  143. biotite/sequence/align/matrix_data/PAM290.mat +34 -0
  144. biotite/sequence/align/matrix_data/PAM30.mat +34 -0
  145. biotite/sequence/align/matrix_data/PAM300.mat +34 -0
  146. biotite/sequence/align/matrix_data/PAM310.mat +34 -0
  147. biotite/sequence/align/matrix_data/PAM320.mat +34 -0
  148. biotite/sequence/align/matrix_data/PAM330.mat +34 -0
  149. biotite/sequence/align/matrix_data/PAM340.mat +34 -0
  150. biotite/sequence/align/matrix_data/PAM350.mat +34 -0
  151. biotite/sequence/align/matrix_data/PAM360.mat +34 -0
  152. biotite/sequence/align/matrix_data/PAM370.mat +34 -0
  153. biotite/sequence/align/matrix_data/PAM380.mat +34 -0
  154. biotite/sequence/align/matrix_data/PAM390.mat +34 -0
  155. biotite/sequence/align/matrix_data/PAM40.mat +34 -0
  156. biotite/sequence/align/matrix_data/PAM400.mat +34 -0
  157. biotite/sequence/align/matrix_data/PAM410.mat +34 -0
  158. biotite/sequence/align/matrix_data/PAM420.mat +34 -0
  159. biotite/sequence/align/matrix_data/PAM430.mat +34 -0
  160. biotite/sequence/align/matrix_data/PAM440.mat +34 -0
  161. biotite/sequence/align/matrix_data/PAM450.mat +34 -0
  162. biotite/sequence/align/matrix_data/PAM460.mat +34 -0
  163. biotite/sequence/align/matrix_data/PAM470.mat +34 -0
  164. biotite/sequence/align/matrix_data/PAM480.mat +34 -0
  165. biotite/sequence/align/matrix_data/PAM490.mat +34 -0
  166. biotite/sequence/align/matrix_data/PAM50.mat +34 -0
  167. biotite/sequence/align/matrix_data/PAM500.mat +34 -0
  168. biotite/sequence/align/matrix_data/PAM60.mat +34 -0
  169. biotite/sequence/align/matrix_data/PAM70.mat +34 -0
  170. biotite/sequence/align/matrix_data/PAM80.mat +34 -0
  171. biotite/sequence/align/matrix_data/PAM90.mat +34 -0
  172. biotite/sequence/align/matrix_data/PB.license +21 -0
  173. biotite/sequence/align/matrix_data/PB.mat +18 -0
  174. biotite/sequence/align/matrix_data/RBLOSUM52_5.0.mat +25 -0
  175. biotite/sequence/align/matrix_data/RBLOSUM59_13p.mat +25 -0
  176. biotite/sequence/align/matrix_data/RBLOSUM59_14.3.mat +25 -0
  177. biotite/sequence/align/matrix_data/RBLOSUM64_5.0.mat +25 -0
  178. biotite/sequence/align/matrix_data/RBLOSUM69_13p.mat +25 -0
  179. biotite/sequence/align/matrix_data/RBLOSUM69_14.3.mat +25 -0
  180. biotite/sequence/align/multiple.cp314-win_amd64.pyd +0 -0
  181. biotite/sequence/align/multiple.pyx +619 -0
  182. biotite/sequence/align/pairwise.cp314-win_amd64.pyd +0 -0
  183. biotite/sequence/align/pairwise.pyx +585 -0
  184. biotite/sequence/align/permutation.cp314-win_amd64.pyd +0 -0
  185. biotite/sequence/align/permutation.pyx +313 -0
  186. biotite/sequence/align/primes.txt +821 -0
  187. biotite/sequence/align/selector.cp314-win_amd64.pyd +0 -0
  188. biotite/sequence/align/selector.pyx +954 -0
  189. biotite/sequence/align/statistics.py +264 -0
  190. biotite/sequence/align/tracetable.cp314-win_amd64.pyd +0 -0
  191. biotite/sequence/align/tracetable.pxd +64 -0
  192. biotite/sequence/align/tracetable.pyx +370 -0
  193. biotite/sequence/alphabet.py +555 -0
  194. biotite/sequence/annotation.py +836 -0
  195. biotite/sequence/codec.cp314-win_amd64.pyd +0 -0
  196. biotite/sequence/codec.pyx +155 -0
  197. biotite/sequence/codon.py +476 -0
  198. biotite/sequence/codon_tables.txt +202 -0
  199. biotite/sequence/graphics/__init__.py +33 -0
  200. biotite/sequence/graphics/alignment.py +1101 -0
  201. biotite/sequence/graphics/color_schemes/3di_flower.json +48 -0
  202. biotite/sequence/graphics/color_schemes/autumn.json +51 -0
  203. biotite/sequence/graphics/color_schemes/blossom.json +51 -0
  204. biotite/sequence/graphics/color_schemes/clustalx_dna.json +11 -0
  205. biotite/sequence/graphics/color_schemes/clustalx_protein.json +28 -0
  206. biotite/sequence/graphics/color_schemes/flower.json +51 -0
  207. biotite/sequence/graphics/color_schemes/jalview_buried.json +31 -0
  208. biotite/sequence/graphics/color_schemes/jalview_hydrophobicity.json +31 -0
  209. biotite/sequence/graphics/color_schemes/jalview_prop_helix.json +31 -0
  210. biotite/sequence/graphics/color_schemes/jalview_prop_strand.json +31 -0
  211. biotite/sequence/graphics/color_schemes/jalview_prop_turn.json +31 -0
  212. biotite/sequence/graphics/color_schemes/jalview_taylor.json +28 -0
  213. biotite/sequence/graphics/color_schemes/jalview_zappo.json +28 -0
  214. biotite/sequence/graphics/color_schemes/ocean.json +51 -0
  215. biotite/sequence/graphics/color_schemes/pb_flower.json +40 -0
  216. biotite/sequence/graphics/color_schemes/rainbow_dna.json +11 -0
  217. biotite/sequence/graphics/color_schemes/rainbow_protein.json +30 -0
  218. biotite/sequence/graphics/color_schemes/spring.json +51 -0
  219. biotite/sequence/graphics/color_schemes/sunset.json +51 -0
  220. biotite/sequence/graphics/color_schemes/wither.json +51 -0
  221. biotite/sequence/graphics/colorschemes.py +170 -0
  222. biotite/sequence/graphics/dendrogram.py +231 -0
  223. biotite/sequence/graphics/features.py +544 -0
  224. biotite/sequence/graphics/logo.py +102 -0
  225. biotite/sequence/graphics/plasmid.py +712 -0
  226. biotite/sequence/io/__init__.py +12 -0
  227. biotite/sequence/io/fasta/__init__.py +22 -0
  228. biotite/sequence/io/fasta/convert.py +462 -0
  229. biotite/sequence/io/fasta/file.py +265 -0
  230. biotite/sequence/io/fastq/__init__.py +19 -0
  231. biotite/sequence/io/fastq/convert.py +117 -0
  232. biotite/sequence/io/fastq/file.py +507 -0
  233. biotite/sequence/io/genbank/__init__.py +17 -0
  234. biotite/sequence/io/genbank/annotation.py +269 -0
  235. biotite/sequence/io/genbank/file.py +573 -0
  236. biotite/sequence/io/genbank/metadata.py +336 -0
  237. biotite/sequence/io/genbank/sequence.py +173 -0
  238. biotite/sequence/io/general.py +201 -0
  239. biotite/sequence/io/gff/__init__.py +26 -0
  240. biotite/sequence/io/gff/convert.py +128 -0
  241. biotite/sequence/io/gff/file.py +449 -0
  242. biotite/sequence/phylo/__init__.py +36 -0
  243. biotite/sequence/phylo/nj.cp314-win_amd64.pyd +0 -0
  244. biotite/sequence/phylo/nj.pyx +221 -0
  245. biotite/sequence/phylo/tree.cp314-win_amd64.pyd +0 -0
  246. biotite/sequence/phylo/tree.pyx +1169 -0
  247. biotite/sequence/phylo/upgma.cp314-win_amd64.pyd +0 -0
  248. biotite/sequence/phylo/upgma.pyx +164 -0
  249. biotite/sequence/profile.py +561 -0
  250. biotite/sequence/search.py +117 -0
  251. biotite/sequence/seqtypes.py +720 -0
  252. biotite/sequence/sequence.py +373 -0
  253. biotite/setup_ccd.py +197 -0
  254. biotite/structure/__init__.py +135 -0
  255. biotite/structure/alphabet/__init__.py +25 -0
  256. biotite/structure/alphabet/encoder.py +332 -0
  257. biotite/structure/alphabet/encoder_weights_3di.kerasify +0 -0
  258. biotite/structure/alphabet/i3d.py +109 -0
  259. biotite/structure/alphabet/layers.py +86 -0
  260. biotite/structure/alphabet/pb.license +21 -0
  261. biotite/structure/alphabet/pb.py +170 -0
  262. biotite/structure/alphabet/unkerasify.py +128 -0
  263. biotite/structure/atoms.py +1596 -0
  264. biotite/structure/basepairs.py +1403 -0
  265. biotite/structure/bonds.cp314-win_amd64.pyd +0 -0
  266. biotite/structure/bonds.pyx +2036 -0
  267. biotite/structure/box.py +724 -0
  268. biotite/structure/celllist.cp314-win_amd64.pyd +0 -0
  269. biotite/structure/celllist.pyx +864 -0
  270. biotite/structure/chains.py +310 -0
  271. biotite/structure/charges.cp314-win_amd64.pyd +0 -0
  272. biotite/structure/charges.pyx +521 -0
  273. biotite/structure/compare.py +683 -0
  274. biotite/structure/density.py +109 -0
  275. biotite/structure/dotbracket.py +213 -0
  276. biotite/structure/error.py +39 -0
  277. biotite/structure/filter.py +646 -0
  278. biotite/structure/geometry.py +817 -0
  279. biotite/structure/graphics/__init__.py +13 -0
  280. biotite/structure/graphics/atoms.py +243 -0
  281. biotite/structure/graphics/rna.py +298 -0
  282. biotite/structure/hbond.py +426 -0
  283. biotite/structure/info/__init__.py +24 -0
  284. biotite/structure/info/atom_masses.json +121 -0
  285. biotite/structure/info/atoms.py +98 -0
  286. biotite/structure/info/bonds.py +149 -0
  287. biotite/structure/info/ccd.py +200 -0
  288. biotite/structure/info/components.bcif +0 -0
  289. biotite/structure/info/groups.py +128 -0
  290. biotite/structure/info/masses.py +121 -0
  291. biotite/structure/info/misc.py +137 -0
  292. biotite/structure/info/radii.py +267 -0
  293. biotite/structure/info/standardize.py +185 -0
  294. biotite/structure/integrity.py +213 -0
  295. biotite/structure/io/__init__.py +29 -0
  296. biotite/structure/io/dcd/__init__.py +13 -0
  297. biotite/structure/io/dcd/file.py +67 -0
  298. biotite/structure/io/general.py +243 -0
  299. biotite/structure/io/gro/__init__.py +14 -0
  300. biotite/structure/io/gro/file.py +343 -0
  301. biotite/structure/io/mol/__init__.py +20 -0
  302. biotite/structure/io/mol/convert.py +112 -0
  303. biotite/structure/io/mol/ctab.py +420 -0
  304. biotite/structure/io/mol/header.py +120 -0
  305. biotite/structure/io/mol/mol.py +149 -0
  306. biotite/structure/io/mol/sdf.py +940 -0
  307. biotite/structure/io/netcdf/__init__.py +13 -0
  308. biotite/structure/io/netcdf/file.py +64 -0
  309. biotite/structure/io/pdb/__init__.py +20 -0
  310. biotite/structure/io/pdb/convert.py +389 -0
  311. biotite/structure/io/pdb/file.py +1380 -0
  312. biotite/structure/io/pdb/hybrid36.cp314-win_amd64.pyd +0 -0
  313. biotite/structure/io/pdb/hybrid36.pyx +242 -0
  314. biotite/structure/io/pdbqt/__init__.py +15 -0
  315. biotite/structure/io/pdbqt/convert.py +113 -0
  316. biotite/structure/io/pdbqt/file.py +688 -0
  317. biotite/structure/io/pdbx/__init__.py +23 -0
  318. biotite/structure/io/pdbx/bcif.py +674 -0
  319. biotite/structure/io/pdbx/cif.py +1091 -0
  320. biotite/structure/io/pdbx/component.py +251 -0
  321. biotite/structure/io/pdbx/compress.py +362 -0
  322. biotite/structure/io/pdbx/convert.py +2122 -0
  323. biotite/structure/io/pdbx/encoding.cp314-win_amd64.pyd +0 -0
  324. biotite/structure/io/pdbx/encoding.pyx +1078 -0
  325. biotite/structure/io/trajfile.py +696 -0
  326. biotite/structure/io/trr/__init__.py +13 -0
  327. biotite/structure/io/trr/file.py +43 -0
  328. biotite/structure/io/util.py +38 -0
  329. biotite/structure/io/xtc/__init__.py +13 -0
  330. biotite/structure/io/xtc/file.py +43 -0
  331. biotite/structure/mechanics.py +72 -0
  332. biotite/structure/molecules.py +337 -0
  333. biotite/structure/pseudoknots.py +622 -0
  334. biotite/structure/rdf.py +245 -0
  335. biotite/structure/repair.py +302 -0
  336. biotite/structure/residues.py +716 -0
  337. biotite/structure/rings.py +452 -0
  338. biotite/structure/sasa.cp314-win_amd64.pyd +0 -0
  339. biotite/structure/sasa.pyx +322 -0
  340. biotite/structure/segments.py +328 -0
  341. biotite/structure/sequence.py +110 -0
  342. biotite/structure/spacegroups.json +1567 -0
  343. biotite/structure/spacegroups.license +26 -0
  344. biotite/structure/sse.py +306 -0
  345. biotite/structure/superimpose.py +511 -0
  346. biotite/structure/tm.py +581 -0
  347. biotite/structure/transform.py +736 -0
  348. biotite/structure/util.py +160 -0
  349. biotite/version.py +34 -0
  350. biotite/visualize.py +375 -0
  351. biotite-1.6.0.dist-info/METADATA +162 -0
  352. biotite-1.6.0.dist-info/RECORD +354 -0
  353. biotite-1.6.0.dist-info/WHEEL +4 -0
  354. biotite-1.6.0.dist-info/licenses/LICENSE.rst +30 -0
@@ -0,0 +1,817 @@
1
+ # This source code is part of the Biotite package and is distributed
2
+ # under the 3-Clause BSD License. Please see 'LICENSE.rst' for further
3
+ # information.
4
+
5
+ """
6
+ This module provides functions for geometric measurements between atoms
7
+ in a structure, mainly lengths and angles.
8
+ """
9
+
10
+ __name__ = "biotite.structure"
11
+ __author__ = "Patrick Kunzmann"
12
+ __all__ = [
13
+ "displacement",
14
+ "index_displacement",
15
+ "distance",
16
+ "index_distance",
17
+ "angle",
18
+ "index_angle",
19
+ "dihedral",
20
+ "index_dihedral",
21
+ "dihedral_backbone",
22
+ "dihedral_side_chain",
23
+ "centroid",
24
+ ]
25
+
26
+ import functools
27
+ import numpy as np
28
+ from biotite.structure.atoms import AtomArray, AtomArrayStack, coord
29
+ from biotite.structure.box import coord_to_fraction, fraction_to_coord, is_orthogonal
30
+ from biotite.structure.filter import filter_amino_acids, filter_canonical_amino_acids
31
+ from biotite.structure.residues import get_residue_starts
32
+ from biotite.structure.util import (
33
+ coord_for_atom_name_per_residue,
34
+ norm_vector,
35
+ vector_dot,
36
+ )
37
+
38
+ # The names of the atoms participating in chi angle
39
+ _CHI_ATOMS = {
40
+ "ARG": [
41
+ ("N", "CA", "CB", "CG"),
42
+ ("CA", "CB", "CG", "CD"),
43
+ ("CB", "CG", "CD", "NE"),
44
+ ("CG", "CD", "NE", "CZ"),
45
+ ],
46
+ "LEU": [
47
+ ("N", "CA", "CB", "CG"),
48
+ # By convention chi2 is defined using CD1 instead of CD2
49
+ ("CA", "CB", "CG", "CD1"),
50
+ ],
51
+ "VAL": [("N", "CA", "CB", "CG1")],
52
+ "ILE": [("N", "CA", "CB", "CG1"), ("CA", "CB", "CG1", "CD1")],
53
+ "MET": [
54
+ ("N", "CA", "CB", "CG"),
55
+ ("CA", "CB", "CG", "SD"),
56
+ ("CB", "CG", "SD", "CE"),
57
+ ],
58
+ "LYS": [
59
+ ("N", "CA", "CB", "CG"),
60
+ ("CA", "CB", "CG", "CD"),
61
+ ("CB", "CG", "CD", "CE"),
62
+ ("CG", "CD", "CE", "NZ"),
63
+ ],
64
+ "PHE": [
65
+ ("N", "CA", "CB", "CG"),
66
+ ("CA", "CB", "CG", "CD1"),
67
+ ],
68
+ "TRP": [
69
+ ("N", "CA", "CB", "CG"),
70
+ ("CA", "CB", "CG", "CD1"),
71
+ ],
72
+ "TYR": [
73
+ ("N", "CA", "CB", "CG"),
74
+ ("CA", "CB", "CG", "CD1"),
75
+ ],
76
+ "ASN": [("N", "CA", "CB", "CG"), ("CA", "CB", "CG", "OD1")],
77
+ "GLN": [
78
+ ("N", "CA", "CB", "CG"),
79
+ ("CA", "CB", "CG", "CD"),
80
+ ("CB", "CG", "CD", "OE1"),
81
+ ],
82
+ "ASP": [("N", "CA", "CB", "CG"), ("CA", "CB", "CG", "OD1")],
83
+ "GLU": [
84
+ ("N", "CA", "CB", "CG"),
85
+ ("CA", "CB", "CG", "CD"),
86
+ ("CB", "CG", "CD", "OE1"),
87
+ ],
88
+ "CYS": [("N", "CA", "CB", "SG")],
89
+ "HIS": [("N", "CA", "CB", "CG"), ("CA", "CB", "CG", "ND1")],
90
+ "PRO": [("N", "CA", "CB", "CG"), ("CA", "CB", "CG", "CD")],
91
+ "SER": [("N", "CA", "CB", "OG")],
92
+ "THR": [("N", "CA", "CB", "OG1")],
93
+ }
94
+
95
+
96
+ def displacement(atoms1, atoms2, box=None):
97
+ """
98
+ Measure the displacement vector, i.e. the vector difference, from
99
+ one array of atom coordinates to another array of coordinates.
100
+
101
+ Parameters
102
+ ----------
103
+ atoms1, atoms2 : ndarray, shape=(m,n,3) or ndarray, shape=(n,3) or ndarray, shape=(3,) or Atom or AtomArray or AtomArrayStack
104
+ The atoms to measure the displacement between.
105
+ The vector from `atoms1` to `atoms2` is measured.
106
+ The dimensions may vary.
107
+ Alternatively an ndarray containing the coordinates can be
108
+ provided.
109
+ Usual *NumPy* broadcasting rules apply.
110
+ box : ndarray, shape=(3,3) or shape=(m,3,3), optional
111
+ If this parameter is set, periodic boundary conditions are
112
+ taken into account (minimum-image convention), based on
113
+ the box vectors given with this parameter.
114
+ The shape *(m,3,3)* is only allowed, when the input coordinates
115
+ comprise multiple models.
116
+
117
+ Returns
118
+ -------
119
+ disp : ndarray, shape=(m,n,3) or ndarray, shape=(n,3) or ndarray, shape=(3,)
120
+ The displacement vector(s). The shape is equal to the shape of
121
+ the input `atoms` with the highest dimensionality.
122
+
123
+ See Also
124
+ --------
125
+ index_displacement : The same calculation, but using atom indices.
126
+ """
127
+ v1 = coord(atoms1)
128
+ v2 = coord(atoms2)
129
+ # Decide subtraction order based on shape, since an array can be
130
+ # only subtracted by an array with less dimensions
131
+ if len(v1.shape) <= len(v2.shape):
132
+ diff = v2 - v1
133
+ else:
134
+ diff = -(v1 - v2)
135
+
136
+ # Use minimum-image convention if box is given
137
+ if box is not None:
138
+ # Transform difference vector
139
+ # from coordinates into fractions of box vectors
140
+ # for faster calculation laster on
141
+ fractions = coord_to_fraction(diff, box)
142
+ # Move vectors into box
143
+ fractions = fractions % 1
144
+ # Check for each model if the box vectors are orthogonal
145
+ orthogonality = is_orthogonal(box)
146
+ disp = np.zeros(fractions.shape, dtype=diff.dtype)
147
+ if fractions.ndim == 1:
148
+ # Single atom
149
+ # Transform into two dimensions
150
+ # to match signature of '_displacement_xxx()'
151
+ fractions = fractions[np.newaxis, :]
152
+ disp = disp[np.newaxis, :]
153
+ if orthogonality:
154
+ _displacement_orthogonal_box(fractions, box, disp)
155
+ else:
156
+ _displacement_triclinic_box(
157
+ fractions.astype(diff.dtype, copy=False),
158
+ box.astype(diff.dtype, copy=False),
159
+ disp,
160
+ )
161
+ # Transform back
162
+ disp = disp[0]
163
+ if fractions.ndim == 2:
164
+ # Single model
165
+ if orthogonality:
166
+ _displacement_orthogonal_box(fractions, box, disp)
167
+ else:
168
+ _displacement_triclinic_box(
169
+ fractions.astype(diff.dtype, copy=False),
170
+ box.astype(diff.dtype, copy=False),
171
+ disp,
172
+ )
173
+ elif fractions.ndim == 3:
174
+ # Multiple models
175
+ # (Model count) x (Atom count)
176
+ for i in range(len(fractions)):
177
+ if box.ndim == 2:
178
+ box_for_model = box
179
+ orthogonality_for_model = orthogonality
180
+ elif box.ndim == 3:
181
+ box_for_model = box[i]
182
+ orthogonality_for_model = orthogonality[i]
183
+ else:
184
+ raise ValueError(f"{box.ndim} are to many box dimensions")
185
+ if orthogonality_for_model:
186
+ _displacement_orthogonal_box(fractions[i], box_for_model, disp[i])
187
+ else:
188
+ _displacement_triclinic_box(
189
+ fractions[i].astype(diff.dtype, copy=False),
190
+ box_for_model.astype(diff.dtype, copy=False),
191
+ disp[i],
192
+ )
193
+ else:
194
+ raise ValueError(f"{diff.shape} is an invalid shape for atom coordinates")
195
+ return disp
196
+
197
+ else:
198
+ return diff
199
+
200
+
201
+ def index_displacement(*args, **kwargs):
202
+ """
203
+ index_displacement(atoms, indices, periodic=False, box=None)
204
+
205
+ Measure the displacement, i.e. the vector difference, between pairs
206
+ of atoms.
207
+
208
+ The pairs refer to indices of a given atom array, whose pairwise
209
+ displacement should be calculated.
210
+ If an atom array stack is provided, the distances are calculated for
211
+ each frame/model.
212
+ In contrast to the :func:`distance()` function, this function is
213
+ able to take periodic boundary conditions into account.
214
+
215
+ Parameters
216
+ ----------
217
+ atoms : AtomArray or AtomArrayStack or ndarray, shape=(n,3) or shape=(m,n,3)
218
+ The atoms the `indices` parameter refers to.
219
+ The pairwise distances are calculated for these pairs.
220
+ Alternatively, the atom coordinates can be directly provided as
221
+ :class:`ndarray`.
222
+ indices : ndarray, shape=(k,2)
223
+ Pairs of indices that point to `atoms`.
224
+ The displacement is measured from ``indices[x,0]`` to
225
+ ``indices[x,1]``.
226
+ periodic : bool, optional
227
+ If set to true, periodic boundary conditions are taken into
228
+ account (minimum-image convention).
229
+ The `box` attribute of the `atoms` parameter is used for
230
+ calculation.
231
+ An alternative box can be provided via the `box` parameter.
232
+ By default, periodicity is ignored.
233
+ box : ndarray, shape=(3,3) or shape=(m,3,3), optional
234
+ If this parameter is set, the given box is used instead of the
235
+ `box` attribute of `atoms`.
236
+
237
+ Returns
238
+ -------
239
+ disp : ndarray, shape=(k,) or shape=(m,k)
240
+ The pairwise displacements.
241
+ If `atoms` is an atom array stack, The distances are
242
+ calculated for each model.
243
+
244
+ Warnings
245
+ --------
246
+ In case `periodic` is set to true and if the box is not orthorhombic
247
+ (at least one angle deviates from 90 degrees),
248
+ the calculation requires approximately 8 times as long as in the
249
+ orthorhombic case.
250
+ Furthermore, it is not guaranteed, that the lowest-distance periodic
251
+ copy is found for non-orthorhombic boxes; this is especially true
252
+ for heavily skewed boxes.
253
+
254
+ See Also
255
+ --------
256
+ displacement
257
+ """
258
+ return _call_non_index_function(displacement, 2, *args, **kwargs)
259
+
260
+
261
+ def distance(atoms1, atoms2, box=None):
262
+ """
263
+ Measure the euclidian distance between atoms.
264
+
265
+ Parameters
266
+ ----------
267
+ atoms1, atoms2 : ndarray or Atom or AtomArray or AtomArrayStack
268
+ The atoms to measure the distances between.
269
+ The dimensions may vary.
270
+ Alternatively an ndarray containing the coordinates can be
271
+ provided.
272
+ Usual *NumPy* broadcasting rules apply.
273
+ box : ndarray, shape=(3,3) or shape=(m,3,3), optional
274
+ If this parameter is set, periodic boundary conditions are
275
+ taken into account (minimum-image convention), based on
276
+ the box vectors given with this parameter.
277
+ The shape *(m,3,3)* is only allowed, when the input coordinates
278
+ comprise multiple models.
279
+
280
+ Returns
281
+ -------
282
+ dist : float or ndarray
283
+ The atom distances.
284
+ The shape is equal to the shape of the input `atoms` with the
285
+ highest dimensionality minus the last axis.
286
+
287
+ See Also
288
+ --------
289
+ index_distance : The same calculation, but using atom indices.
290
+ """
291
+ diff = displacement(atoms1, atoms2, box)
292
+ return np.sqrt(vector_dot(diff, diff))
293
+
294
+
295
+ def index_distance(*args, **kwargs):
296
+ """
297
+ index_distance(atoms, indices, periodic=False, box=None)
298
+
299
+ Measure the euclidian distance between pairs of atoms.
300
+
301
+ The pairs refer to indices of a given atom array, whose pairwise
302
+ distances should be calculated.
303
+ If an atom array stack is provided, the distances are calculated for
304
+ each frame/model.
305
+ In contrast to the :func:`distance()` function, this function is
306
+ able to take periodic boundary conditions into account.
307
+
308
+ Parameters
309
+ ----------
310
+ atoms : AtomArray or AtomArrayStack or ndarray, shape=(n,3) or shape=(m,n,3)
311
+ The atoms the `indices` parameter refers to.
312
+ The pairwise distances are calculated for these pairs.
313
+ Alternatively, the atom coordinates can be directly provided as
314
+ :class:`ndarray`.
315
+ indices : ndarray, shape=(k,2)
316
+ Pairs of indices that point to `atoms`.
317
+ periodic : bool, optional
318
+ If set to true, periodic boundary conditions are taken into
319
+ account (minimum-image convention).
320
+ The `box` attribute of the `atoms` parameter is used for
321
+ calculation.
322
+ An alternative box can be provided via the `box` parameter.
323
+ By default, periodicity is ignored.
324
+ box : ndarray, shape=(3,3) or shape=(m,3,3), optional
325
+ If this parameter is set, the given box is used instead of the
326
+ `box` attribute of `atoms`.
327
+
328
+ Returns
329
+ -------
330
+ dist : ndarray, shape=(k,) or shape=(m,k)
331
+ The pairwise distances.
332
+ If `atoms` is an atom array stack, The distances are
333
+ calculated for each model.
334
+
335
+ Warnings
336
+ --------
337
+ In case `periodic` is set to true and if the box is not orthorhombic
338
+ (at least one angle deviates from 90 degrees),
339
+ the calculation requires approximately 8 times as long as in the
340
+ orthorhombic case.
341
+ Furthermore, it is not guaranteed, that the lowest-distance periodic
342
+ copy is found for non-orthorhombic boxes; this is especially true
343
+ for heavily skewed boxes.
344
+
345
+ See Also
346
+ --------
347
+ distance
348
+ """
349
+ return _call_non_index_function(distance, 2, *args, **kwargs)
350
+
351
+
352
+ def angle(atoms1, atoms2, atoms3, box=None):
353
+ """
354
+ Measure the angle between 3 atoms.
355
+
356
+ Parameters
357
+ ----------
358
+ atoms1, atoms2, atoms3 : ndarray or Atom or AtomArray or AtomArrayStack
359
+ The atoms to measure the angle between. Alternatively an
360
+ ndarray containing the coordinates can be provided.
361
+ box : ndarray, shape=(3,3) or shape=(m,3,3), optional
362
+ If this parameter is set, periodic boundary conditions are
363
+ taken into account (minimum-image convention), based on
364
+ the box vectors given with this parameter.
365
+ The shape *(m,3,3)* is only allowed, when the input coordinates
366
+ comprise multiple models.
367
+
368
+ Returns
369
+ -------
370
+ angle : float or ndarray
371
+ The angle(s) between the atoms. The shape is equal to the shape
372
+ of the input `atoms` with the highest dimensionality minus the
373
+ last axis.
374
+
375
+ See Also
376
+ --------
377
+ index_angle : The same calculation, but using atom indices.
378
+ """
379
+ v1 = displacement(atoms1, atoms2, box)
380
+ v2 = displacement(atoms3, atoms2, box)
381
+ norm_vector(v1)
382
+ norm_vector(v2)
383
+ return np.arccos(vector_dot(v1, v2))
384
+
385
+
386
+ def index_angle(*args, **kwargs):
387
+ """
388
+ index_angle(atoms, indices, periodic=False, box=None)
389
+
390
+ Measure the angle between triples of atoms.
391
+
392
+ The triples refer to indices of a given atom array, whose triplewise
393
+ angles should be calculated.
394
+ If an atom array stack is provided, the distances are calculated for
395
+ each frame/model.
396
+
397
+ Parameters
398
+ ----------
399
+ atoms : AtomArray or AtomArrayStack or ndarray, shape=(n,3) or shape=(m,n,3)
400
+ The atoms the `indices` parameter refers to.
401
+ The triplewise distances are calculated for these pairs.
402
+ Alternatively, the atom coordinates can be directly provided as
403
+ :class:`ndarray`.
404
+ indices : ndarray, shape=(k,3)
405
+ Triples of indices that point to `atoms`.
406
+ periodic : bool, optional
407
+ If set to true, periodic boundary conditions are taken into
408
+ account (minimum-image convention).
409
+ The `box` attribute of the `atoms` parameter is used for
410
+ calculation.
411
+ An alternative box can be provided via the `box` parameter.
412
+ By default, periodicity is ignored.
413
+ box : ndarray, shape=(3,3) or shape=(m,3,3), optional
414
+ If this parameter is set, the given box is used instead of the
415
+ `box` attribute of `atoms`.
416
+
417
+ Returns
418
+ -------
419
+ angle : ndarray, shape=(k,) or shape=(m,k)
420
+ The triplewise angles.
421
+ If `atoms` is an atom array stack, The distances are
422
+ calculated for each model.
423
+
424
+ Warnings
425
+ --------
426
+ In case `periodic` is set to true and if the box is not orthorhombic
427
+ (at least one angle deviates from 90 degrees),
428
+ the calculation requires approximately 8 times as long as in the
429
+ orthorhombic case.
430
+ Furthermore, it is not guaranteed, that the lowest-distance periodic
431
+ copy is found for non-orthorhombic boxes; this is especially true
432
+ for heavily skewed boxes.
433
+
434
+ See Also
435
+ --------
436
+ angle
437
+ """
438
+ return _call_non_index_function(angle, 3, *args, **kwargs)
439
+
440
+
441
+ def dihedral(atoms1, atoms2, atoms3, atoms4, box=None):
442
+ """
443
+ Measure the dihedral angle between 4 atoms.
444
+
445
+ Parameters
446
+ ----------
447
+ atoms1, atoms2, atoms3, atoms4 : ndarray or Atom or AtomArray or AtomArrayStack
448
+ The atoms to measure the dihedral angle between.
449
+ Alternatively an ndarray containing the coordinates can be
450
+ provided.
451
+ box : ndarray, shape=(3,3) or shape=(m,3,3), optional
452
+ If this parameter is set, periodic boundary conditions are
453
+ taken into account (minimum-image convention), based on
454
+ the box vectors given with this parameter.
455
+ The shape *(m,3,3)* is only allowed, when the input coordinates
456
+ comprise multiple models.
457
+
458
+ Returns
459
+ -------
460
+ dihed : float or ndarray
461
+ The dihedral angle(s) between the atoms. The shape is equal to
462
+ the shape of the input `atoms` with the highest dimensionality
463
+ minus the last axis.
464
+
465
+ See Also
466
+ --------
467
+ index_dihedral : The same calculation, but using atom indices.
468
+ dihedral_backbone : Calculate the dihedral angle along a peptide backbone.
469
+ """
470
+ v1 = displacement(atoms1, atoms2, box)
471
+ v2 = displacement(atoms2, atoms3, box)
472
+ v3 = displacement(atoms3, atoms4, box)
473
+ norm_vector(v1)
474
+ norm_vector(v2)
475
+ norm_vector(v3)
476
+
477
+ n1 = np.cross(v1, v2)
478
+ n2 = np.cross(v2, v3)
479
+
480
+ # Calculation using atan2, to ensure the correct sign of the angle
481
+ x = vector_dot(n1, n2)
482
+ y = vector_dot(np.cross(n1, n2), v2)
483
+ return np.arctan2(y, x)
484
+
485
+
486
+ def index_dihedral(*args, **kwargs):
487
+ """
488
+ index_dihedral(atoms, indices, periodic=False, box=None)
489
+
490
+ Measure the dihedral angle between quadruples of atoms.
491
+
492
+ The triples refer to indices of a given atom array, whose
493
+ quadruplewise dihedral angles should be calculated.
494
+ If an atom array stack is provided, the distances are calculated for
495
+ each frame/model.
496
+
497
+ Parameters
498
+ ----------
499
+ atoms : AtomArray or AtomArrayStack or ndarray, shape=(n,3) or shape=(m,n,3)
500
+ The atoms the `indices` parameter refers to.
501
+ The quadruplewise dihedral angles are calculated for these
502
+ pairs.
503
+ Alternatively, the atom coordinates can be directly provided as
504
+ :class:`ndarray`.
505
+ indices : ndarray, shape=(k,4)
506
+ Quadruples of indices that point to `atoms`.
507
+ periodic : bool, optional
508
+ If set to true, periodic boundary conditions are taken into
509
+ account (minimum-image convention).
510
+ The `box` attribute of the `atoms` parameter is used for
511
+ calculation.
512
+ An alternative box can be provided via the `box` parameter.
513
+ By default, periodicity is ignored.
514
+ box : ndarray, shape=(3,3) or shape=(m,3,3), optional
515
+ If this parameter is set, the given box is used instead of the
516
+ `box` attribute of `atoms`.
517
+
518
+ Returns
519
+ -------
520
+ dihedral : ndarray, shape=(k,) or shape=(m,k)
521
+ The quadruplewise dihedral angles.
522
+ If `atoms` is an atom array stack, The distances are
523
+ calculated for each model.
524
+
525
+ Warnings
526
+ --------
527
+ In case `periodic` is set to true and if the box is not orthorhombic
528
+ (at least one angle deviates from 90 degrees),
529
+ the calculation requires approximately 8 times as long as in the
530
+ orthorhombic case.
531
+ Furthermore, it is not guaranteed, that the lowest-distance periodic
532
+ copy is found for non-orthorhombic boxes; this is especially true
533
+ for heavily skewed boxes.
534
+
535
+ See Also
536
+ --------
537
+ dihedral
538
+ dihedral_backbone
539
+ """
540
+ return _call_non_index_function(dihedral, 4, *args, **kwargs)
541
+
542
+
543
+ def dihedral_backbone(atom_array):
544
+ """
545
+ Measure the characteristic backbone dihedral angles of a chain.
546
+
547
+ Parameters
548
+ ----------
549
+ atom_array : AtomArray or AtomArrayStack
550
+ The protein structure to measure the dihedral angles for.
551
+ For missing backbone atoms the corresponding angles are `NaN`.
552
+
553
+ Returns
554
+ -------
555
+ phi, psi, omega : ndarray, shape=(m,n) or shape=(n,), dtype=float
556
+ An array containing the 3 backbone dihedral angles for every CA atom.
557
+ `phi` is not defined at the N-terminus, `psi` and `omega` are not defined at the
558
+ C-terminus.
559
+ In these places the arrays have *NaN* values.
560
+ If an :class:`AtomArrayStack` is given, the output angles are 2-dimensional,
561
+ the first dimension corresponds to the model number.
562
+ """
563
+ amino_acid_mask = filter_amino_acids(atom_array)
564
+
565
+ # Coordinates for dihedral angle calculation
566
+ coord_n, coord_ca, coord_c = coord_for_atom_name_per_residue(
567
+ atom_array,
568
+ ("N", "CA", "C"),
569
+ amino_acid_mask,
570
+ )
571
+ n_residues = coord_n.shape[-2]
572
+
573
+ # Coordinates for dihedral angle calculation
574
+ # Dim 0: Model index (only for atom array stacks)
575
+ # Dim 1: Angle index
576
+ # Dim 2: X, Y, Z coordinates
577
+ # Dim 3: Atoms involved in dihedral angle
578
+ if isinstance(atom_array, AtomArray):
579
+ angle_coord_shape: tuple[int, ...] = (n_residues, 3, 4)
580
+ elif isinstance(atom_array, AtomArrayStack):
581
+ angle_coord_shape = (atom_array.stack_depth(), n_residues, 3, 4)
582
+ coord_for_phi = np.full(angle_coord_shape, np.nan, dtype=np.float32)
583
+ coord_for_psi = np.full(angle_coord_shape, np.nan, dtype=np.float32)
584
+ coord_for_omg = np.full(angle_coord_shape, np.nan, dtype=np.float32)
585
+
586
+ # fmt: off
587
+ coord_for_phi[..., 1:, :, 0] = coord_c[..., 0:-1, :]
588
+ coord_for_phi[..., 1:, :, 1] = coord_n[..., 1:, :]
589
+ coord_for_phi[..., 1:, :, 2] = coord_ca[..., 1:, :]
590
+ coord_for_phi[..., 1:, :, 3] = coord_c[..., 1:, :]
591
+
592
+ coord_for_psi[..., 0:-1, :, 0] = coord_n[..., 0:-1, :]
593
+ coord_for_psi[..., 0:-1, :, 1] = coord_ca[..., 0:-1, :]
594
+ coord_for_psi[..., 0:-1, :, 2] = coord_c[..., 0:-1, :]
595
+ coord_for_psi[..., 0:-1, :, 3] = coord_n[..., 1:, :]
596
+
597
+ coord_for_omg[..., 0:-1, :, 0] = coord_ca[..., 0:-1, :]
598
+ coord_for_omg[..., 0:-1, :, 1] = coord_c[..., 0:-1, :]
599
+ coord_for_omg[..., 0:-1, :, 2] = coord_n[..., 1:, :]
600
+ coord_for_omg[..., 0:-1, :, 3] = coord_ca[..., 1:, :]
601
+ # fmt: on
602
+
603
+ phi = dihedral(
604
+ coord_for_phi[..., 0],
605
+ coord_for_phi[..., 1],
606
+ coord_for_phi[..., 2],
607
+ coord_for_phi[..., 3],
608
+ )
609
+ psi = dihedral(
610
+ coord_for_psi[..., 0],
611
+ coord_for_psi[..., 1],
612
+ coord_for_psi[..., 2],
613
+ coord_for_psi[..., 3],
614
+ )
615
+ omg = dihedral(
616
+ coord_for_omg[..., 0],
617
+ coord_for_omg[..., 1],
618
+ coord_for_omg[..., 2],
619
+ coord_for_omg[..., 3],
620
+ )
621
+
622
+ return phi, psi, omg
623
+
624
+
625
+ def dihedral_side_chain(atoms):
626
+ r"""
627
+ Measure the side chain :math:`\chi` dihedral angles of amino acid residues.
628
+
629
+ Parameters
630
+ ----------
631
+ atoms : AtomArray or AtomArrayStack
632
+ The protein structure to measure the side chain dihedral angles for.
633
+
634
+ Returns
635
+ -------
636
+ chi : ndarray, shape=(m, n, 4) or shape=(n, 4), dtype=float
637
+ An array containing the up to four side chain dihedral angles for every
638
+ amino acid residue.
639
+ Trailing :math:`\chi` angles that are not defined for an amino acid are filled
640
+ with :math:`NaN` values.
641
+ The same is True for all residues that are not canonical amino acids.
642
+
643
+ Notes
644
+ -----
645
+ By convention, the :math:`\chi_2` angle of leucine is defined using ``CD1``
646
+ instead of ``CD2``.
647
+
648
+ Examples
649
+ --------
650
+
651
+ >>> res_ids, res_names = get_residues(atom_array)
652
+ >>> dihedrals = dihedral_side_chain(atom_array)
653
+ >>> for res_id, res_name, dihedrals in zip(res_ids, res_names, dihedrals):
654
+ ... print(f"{res_name.capitalize()}{res_id:<2d}:", dihedrals)
655
+ Asn1 : [-1.180 -0.066 nan nan]
656
+ Leu2 : [0.923 1.866 nan nan]
657
+ Tyr3 : [-2.593 -1.487 nan nan]
658
+ Ile4 : [-0.781 -0.972 nan nan]
659
+ Gln5 : [-2.557 1.410 -1.776 nan]
660
+ Trp6 : [3.117 1.372 nan nan]
661
+ Leu7 : [-1.33 3.08 nan nan]
662
+ Lys8 : [ 1.320 1.734 3.076 -2.022]
663
+ Asp9 : [-1.623 0.909 nan nan]
664
+ Gly10: [nan nan nan nan]
665
+ Gly11: [nan nan nan nan]
666
+ Pro12: [-0.331 0.539 nan nan]
667
+ Ser13: [-1.067 nan nan nan]
668
+ Ser14: [-2.514 nan nan nan]
669
+ Gly15: [nan nan nan nan]
670
+ Arg16: [ 1.032 -3.063 1.541 -1.568]
671
+ Pro17: [ 0.522 -0.601 nan nan]
672
+ Pro18: [ 0.475 -0.577 nan nan]
673
+ Pro19: [ 0.561 -0.602 nan nan]
674
+ Ser20: [-1.055 nan nan nan]
675
+ """
676
+ is_multi_model = isinstance(atoms, AtomArrayStack)
677
+
678
+ chi_atoms = _all_chi_atoms()
679
+ res_names = atoms.res_name[get_residue_starts(atoms)]
680
+ chi_atom_coord = coord_for_atom_name_per_residue(
681
+ atoms, chi_atoms, filter_canonical_amino_acids(atoms)
682
+ )
683
+ chi_atoms_to_coord_index = {atom_name: i for i, atom_name in enumerate(chi_atoms)}
684
+
685
+ if is_multi_model:
686
+ shape = (atoms.stack_depth(), len(res_names), 4)
687
+ else:
688
+ shape = (len(res_names), 4)
689
+ chi_angles = np.full(shape, np.nan, dtype=np.float32)
690
+ for res_name, chi_atom_names_for_all_angles in _CHI_ATOMS.items():
691
+ res_mask = res_names == res_name
692
+ for chi_i, chi_atom_names in enumerate(chi_atom_names_for_all_angles):
693
+ dihedrals = dihedral(
694
+ chi_atom_coord[
695
+ chi_atoms_to_coord_index[chi_atom_names[0]], ..., res_mask, :
696
+ ],
697
+ chi_atom_coord[
698
+ chi_atoms_to_coord_index[chi_atom_names[1]], ..., res_mask, :
699
+ ],
700
+ chi_atom_coord[
701
+ chi_atoms_to_coord_index[chi_atom_names[2]], ..., res_mask, :
702
+ ],
703
+ chi_atom_coord[
704
+ chi_atoms_to_coord_index[chi_atom_names[3]], ..., res_mask, :
705
+ ],
706
+ )
707
+ if is_multi_model:
708
+ # Swap dimensions due to NumPy's behavior when using advanced indexing
709
+ # (https://numpy.org/devdocs/user/basics.indexing.html#combining-advanced-and-basic-indexing)
710
+ dihedrals = dihedrals.T
711
+ chi_angles[..., res_mask, chi_i] = dihedrals
712
+ return chi_angles
713
+
714
+
715
+ def centroid(atoms):
716
+ """
717
+ Measure the centroid of a structure.
718
+
719
+ Parameters
720
+ ----------
721
+ atoms : ndarray or AtomArray or AtomArrayStack
722
+ The structures to determine the centroid from.
723
+ Alternatively an ndarray containing the coordinates can be
724
+ provided.
725
+
726
+ Returns
727
+ -------
728
+ centroid : float or ndarray
729
+ The centroid of the structure(s). :class:`ndarray` is returned when
730
+ an :class:`AtomArrayStack` is given (centroid for each model).
731
+ """
732
+ return np.mean(coord(atoms), axis=-2)
733
+
734
+
735
+ def _call_non_index_function(
736
+ function, expected_amount, atoms, indices, periodic=False, box=None
737
+ ):
738
+ """
739
+ Call an `xxx()` function based on the parameters given to a
740
+ `index_xxx()` function.
741
+ """
742
+ if indices.shape[-1] != expected_amount:
743
+ raise ValueError(
744
+ f"Expected length {expected_amount} in the last dimension "
745
+ f"of the indices, but got length {indices.shape[-1]}"
746
+ )
747
+ coord_list = []
748
+ for i in range(expected_amount):
749
+ coord_list.append(coord(atoms)[..., indices[:, i], :])
750
+ if periodic:
751
+ if box is None:
752
+ if isinstance(atoms, (AtomArray, AtomArrayStack)):
753
+ box = atoms.box
754
+ else:
755
+ raise ValueError(
756
+ "If `atoms` are coordinates, the box must be set explicitly"
757
+ )
758
+ else:
759
+ box = None
760
+ return function(*coord_list, box)
761
+
762
+
763
+ def _displacement_orthogonal_box(fractions, box, disp):
764
+ """
765
+ Fill in the PBC-aware displacement vector for non-PBC-aware
766
+ displacements given as fractions of given box vectors.
767
+ """
768
+ # Fraction components are guaranteed to be positive
769
+ # Use fraction vector components with lower absolute
770
+ # -> new_vec[i] = vec[i] - 1 if vec[i] > 0.5 else vec[i]
771
+ fractions[fractions > 0.5] -= 1
772
+ disp[:] = fraction_to_coord(fractions, box)
773
+
774
+
775
+ def _displacement_triclinic_box(fractions, box, disp):
776
+ """
777
+ Fill in the PBC-aware displacement vector for non-PBC-aware
778
+ displacements given as fractions of given box vectors.
779
+ """
780
+ diffs = fraction_to_coord(fractions, box)
781
+ # Fraction components are guaranteed to be positive
782
+ # Test all 3 fraction vector components
783
+ # with positive and negative sign
784
+ # (i,j,k in {-1, 0})
785
+ # Hence, 8 periodic copies are tested
786
+ periodic_shift = []
787
+ for i in range(-1, 1):
788
+ for j in range(-1, 1):
789
+ for k in range(-1, 1):
790
+ x = i * box[0, 0] + j * box[1, 0] + k * box[2, 0]
791
+ y = i * box[0, 1] + j * box[1, 1] + k * box[2, 1]
792
+ z = i * box[0, 2] + j * box[1, 2] + k * box[2, 2]
793
+ periodic_shift.append([x, y, z])
794
+ periodic_shift = np.array(periodic_shift, dtype=disp.dtype)
795
+ # Create 8 periodically shifted variants for each atom
796
+ shifted_diffs = diffs[:, np.newaxis, :] + periodic_shift[np.newaxis, :, :]
797
+ # Find for each atom the periodically shifted variant with lowest
798
+ # distance
799
+ # Lowest squared distance -> lowest distance
800
+ sq_distance = vector_dot(shifted_diffs, shifted_diffs)
801
+ # for each given non-PBC-aware displacement find the PBC-aware
802
+ # displacement with the lowest distance
803
+ disp[:] = shifted_diffs[
804
+ np.arange(len(shifted_diffs)), np.argmin(sq_distance, axis=1)
805
+ ]
806
+
807
+
808
+ @functools.cache
809
+ def _all_chi_atoms():
810
+ """
811
+ Get the names of the atoms participating in any chi angle.
812
+ """
813
+ atom_names = set()
814
+ for angles in _CHI_ATOMS.values():
815
+ for angle in angles:
816
+ atom_names.update(angle)
817
+ return sorted(atom_names)