biotite 1.5.0__cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.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.

Potentially problematic release.


This version of biotite might be problematic. Click here for more details.

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 +428 -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 +197 -0
  35. biotite/database/entrez/__init__.py +15 -0
  36. biotite/database/entrez/check.py +60 -0
  37. biotite/database/entrez/dbnames.py +101 -0
  38. biotite/database/entrez/download.py +228 -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 +258 -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 +161 -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 +126 -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 +490 -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 +702 -0
  73. biotite/sequence/align/banded.cpython-312-x86_64-linux-gnu.so +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.cpython-312-x86_64-linux-gnu.so +0 -0
  78. biotite/sequence/align/kmeralphabet.pyx +595 -0
  79. biotite/sequence/align/kmersimilarity.cpython-312-x86_64-linux-gnu.so +0 -0
  80. biotite/sequence/align/kmersimilarity.pyx +233 -0
  81. biotite/sequence/align/kmertable.cpython-312-x86_64-linux-gnu.so +0 -0
  82. biotite/sequence/align/kmertable.pyx +3411 -0
  83. biotite/sequence/align/localgapped.cpython-312-x86_64-linux-gnu.so +0 -0
  84. biotite/sequence/align/localgapped.pyx +892 -0
  85. biotite/sequence/align/localungapped.cpython-312-x86_64-linux-gnu.so +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.cpython-312-x86_64-linux-gnu.so +0 -0
  181. biotite/sequence/align/multiple.pyx +619 -0
  182. biotite/sequence/align/pairwise.cpython-312-x86_64-linux-gnu.so +0 -0
  183. biotite/sequence/align/pairwise.pyx +585 -0
  184. biotite/sequence/align/permutation.cpython-312-x86_64-linux-gnu.so +0 -0
  185. biotite/sequence/align/permutation.pyx +313 -0
  186. biotite/sequence/align/primes.txt +821 -0
  187. biotite/sequence/align/selector.cpython-312-x86_64-linux-gnu.so +0 -0
  188. biotite/sequence/align/selector.pyx +954 -0
  189. biotite/sequence/align/statistics.py +264 -0
  190. biotite/sequence/align/tracetable.cpython-312-x86_64-linux-gnu.so +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.cpython-312-x86_64-linux-gnu.so +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 +283 -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.cpython-312-x86_64-linux-gnu.so +0 -0
  244. biotite/sequence/phylo/nj.pyx +221 -0
  245. biotite/sequence/phylo/tree.cpython-312-x86_64-linux-gnu.so +0 -0
  246. biotite/sequence/phylo/tree.pyx +1169 -0
  247. biotite/sequence/phylo/upgma.cpython-312-x86_64-linux-gnu.so +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 +1562 -0
  264. biotite/structure/basepairs.py +1403 -0
  265. biotite/structure/bonds.cpython-312-x86_64-linux-gnu.so +0 -0
  266. biotite/structure/bonds.pyx +2036 -0
  267. biotite/structure/box.py +724 -0
  268. biotite/structure/celllist.cpython-312-x86_64-linux-gnu.so +0 -0
  269. biotite/structure/celllist.pyx +864 -0
  270. biotite/structure/chains.py +310 -0
  271. biotite/structure/charges.cpython-312-x86_64-linux-gnu.so +0 -0
  272. biotite/structure/charges.pyx +520 -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 +591 -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 +425 -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.cpython-312-x86_64-linux-gnu.so +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 +2113 -0
  323. biotite/structure/io/pdbx/encoding.cpython-312-x86_64-linux-gnu.so +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 +451 -0
  338. biotite/structure/sasa.cpython-312-x86_64-linux-gnu.so +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.5.0.dist-info/METADATA +162 -0
  352. biotite-1.5.0.dist-info/RECORD +354 -0
  353. biotite-1.5.0.dist-info/WHEEL +6 -0
  354. biotite-1.5.0.dist-info/licenses/LICENSE.rst +30 -0
@@ -0,0 +1,864 @@
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 allows efficient search of atoms in a defined radius around
7
+ a location.
8
+ """
9
+
10
+ __name__ = "biotite.structure"
11
+ __author__ = "Patrick Kunzmann"
12
+ __all__ = ["CellList"]
13
+
14
+ cimport cython
15
+ cimport numpy as np
16
+ from libc.stdlib cimport realloc, malloc, free
17
+
18
+ import numpy as np
19
+ from .atoms import coord as to_coord
20
+ from .atoms import AtomArrayStack
21
+ from .box import repeat_box_coord, move_inside_box
22
+
23
+ ctypedef np.uint64_t ptr
24
+ ctypedef np.float32_t float32
25
+ ctypedef np.uint8_t uint8
26
+
27
+
28
+ cdef class CellList:
29
+ """
30
+ __init__(atom_array, cell_size, periodic=False, box=None, selection=None)
31
+
32
+ This class enables the efficient search of atoms in vicinity of a
33
+ defined location.
34
+
35
+ This class stores the indices of an atom array in virtual "cells",
36
+ each corresponding to a specific coordinate interval.
37
+ If the atoms in vicinity of a specific location are searched, only
38
+ the atoms in the relevant cells are checked.
39
+ Effectively this decreases the operation time for finding atoms
40
+ with a maximum distance to given coordinates from *O(n)* to *O(1)*,
41
+ after the :class:`CellList` has been created.
42
+ Therefore a :class:`CellList` saves calculation time in those
43
+ cases, where vicinity is checked for multiple locations.
44
+
45
+ Parameters
46
+ ----------
47
+ atom_array : AtomArray or ndarray, dtype=float, shape=(n,3)
48
+ The :class:`AtomArray` to create the :class:`CellList` for.
49
+ Alternatively the atom coordinates are accepted directly.
50
+ In this case `box` must be set, if `periodic` is true.
51
+ cell_size : float
52
+ The coordinate interval each cell has for x, y and z axis.
53
+ The amount of cells depends on the range of coordinates in the
54
+ `atom_array` and the `cell_size`.
55
+ periodic : bool, optional
56
+ If true, the cell list considers periodic copies of atoms.
57
+ The periodicity is based on the `box` attribute of `atom_array`.
58
+ box : ndarray, dtype=float, shape=(3,3), optional
59
+ If provided, the periodicity is based on this parameter instead
60
+ of the :attr:`box` attribute of `atom_array`.
61
+ Only has an effect, if `periodic` is ``True``.
62
+ selection : ndarray, dtype=bool, shape=(n,), optional
63
+ If provided, only the atoms masked by this array are stored in
64
+ the cell list. However, the indices stored in the cell list
65
+ will still refer to the original unfiltered `atom_array`.
66
+
67
+ Examples
68
+ --------
69
+
70
+ >>> cell_list = CellList(atom_array, cell_size=5)
71
+ >>> near_atoms = atom_array[cell_list.get_atoms(np.array([1,2,3]), radius=7.0)]
72
+ """
73
+
74
+ # The atom coordinates
75
+ cdef float32[:,:] _coord
76
+ # A boolean mask that covers the selected atoms
77
+ cdef uint8[:] _selection
78
+ cdef bint _has_selection
79
+ # The cells to store the coordinates in; an ndarray of pointers
80
+ cdef ptr[:,:,:] _cells
81
+ # The amount elements in each C-array in '_cells'
82
+ cdef int[:,:,:] _cell_length
83
+ # The maximum value of '_cell_length' over all cells,
84
+ # required for worst case assumption on size of output arrays
85
+ cdef int _max_cell_length
86
+ # The length of the cell in each direction (x,y,z)
87
+ cdef float _cellsize
88
+ # The minimum and maximum coordinates for all atoms
89
+ # Used as origin ('_min_coord' is at _cells[0,0,0])
90
+ # and for bound checks
91
+ cdef float32[:] _min_coord
92
+ cdef float32[:] _max_coord
93
+ # Indicates whether the cell list takes periodicity into account
94
+ cdef bint _periodic
95
+ cdef np.ndarray _box
96
+ # The length of the array before appending periodic copies
97
+ # if 'periodic' is true
98
+ cdef int _orig_length
99
+ cdef float32[:] _orig_min_coord
100
+ cdef float32[:] _orig_max_coord
101
+
102
+
103
+ @cython.initializedcheck(False)
104
+ @cython.boundscheck(False)
105
+ @cython.wraparound(False)
106
+ def __cinit__(self, atom_array not None, float cell_size,
107
+ bint periodic=False, box=None, np.ndarray selection=None):
108
+ cdef float32 x, y, z
109
+ cdef int i, j, k
110
+ cdef int atom_array_i
111
+ cdef int* cell_ptr = NULL
112
+ cdef int length
113
+
114
+ if isinstance(atom_array, AtomArrayStack):
115
+ raise TypeError("Expected 'AtomArray' but got 'AtomArrayStack'")
116
+ coord = to_coord(atom_array)
117
+ # the length of the array before appending periodic copies
118
+ # if 'periodic' is true
119
+ self._orig_length = coord.shape[0]
120
+ self._box = None
121
+ if selection is None:
122
+ _check_coord(coord)
123
+ else:
124
+ _check_coord(coord[selection])
125
+
126
+ if periodic:
127
+ if box is not None:
128
+ self._box = box
129
+ elif atom_array.box is not None:
130
+ if atom_array.box.shape != (3,3):
131
+ raise ValueError(
132
+ "Box has invalid shape"
133
+ )
134
+ self._box = atom_array.box
135
+ else:
136
+ raise ValueError(
137
+ "AtomArray must have a box to enable periodicity"
138
+ )
139
+ if np.isnan(self._box).any():
140
+ raise ValueError("Box contains NaN values")
141
+ coord = move_inside_box(coord, self._box)
142
+ coord, indices = repeat_box_coord(coord, self._box)
143
+
144
+ if self._has_initialized_cells():
145
+ raise Exception("Duplicate call of constructor")
146
+ self._cells = None
147
+ if cell_size <= 0:
148
+ raise ValueError("Cell size must be greater than 0")
149
+ self._periodic = periodic
150
+ self._coord = coord.astype(np.float32, copy=False)
151
+ self._cellsize = cell_size
152
+ # calculate how many cells are required for each dimension
153
+ min_coord = np.nanmin(coord, axis=0).astype(np.float32)
154
+ max_coord = np.nanmax(coord, axis=0).astype(np.float32)
155
+ self._min_coord = min_coord
156
+ self._max_coord = max_coord
157
+ cell_count = (((max_coord - min_coord) / cell_size) +1).astype(int)
158
+ if self._periodic:
159
+ self._orig_min_coord = np.nanmin(coord[:self._orig_length], axis=0) \
160
+ .astype(np.float32)
161
+ self._orig_max_coord = np.nanmax(coord[:self._orig_length], axis=0) \
162
+ .astype(np.float32)
163
+
164
+ # ndarray of pointers to C-arrays
165
+ # containing indices to atom array
166
+ self._cells = np.zeros(cell_count, dtype=np.uint64)
167
+ # Stores the length of the C-arrays
168
+ self._cell_length = np.zeros(cell_count, dtype=np.int32)
169
+
170
+ # Prepare selection
171
+ if selection is not None:
172
+ self._has_selection = True
173
+ self._selection = np.frombuffer(selection, dtype=np.uint8)
174
+ if self._selection.shape[0] != self._orig_length:
175
+ raise IndexError(
176
+ f"Atom array has length {self._orig_length}, "
177
+ f"but selection has length {self._selection.shape[0]}"
178
+ )
179
+ else:
180
+ self._has_selection = False
181
+
182
+ # Fill cells
183
+ for atom_array_i in range(self._coord.shape[0]):
184
+ # Only put selected atoms into cell list
185
+ if not self._has_selection \
186
+ or self._selection[atom_array_i % self._orig_length]:
187
+ x = self._coord[atom_array_i, 0]
188
+ y = self._coord[atom_array_i, 1]
189
+ z = self._coord[atom_array_i, 2]
190
+ # Get cell indices for coordinates
191
+ self._get_cell_index(x, y, z, &i, &j, &k)
192
+ # Increment cell length and reallocate
193
+ length = self._cell_length[i,j,k] + 1
194
+ cell_ptr = <int*>self._cells[i,j,k]
195
+ cell_ptr = <int*>realloc(cell_ptr, length * sizeof(int))
196
+ if not cell_ptr:
197
+ raise MemoryError()
198
+ # Potentially increase max cell length
199
+ if length > self._max_cell_length:
200
+ self._max_cell_length = length
201
+ # Store atom array index in respective cell
202
+ cell_ptr[length-1] = atom_array_i
203
+ # Store new cell pointer and length
204
+ self._cell_length[i,j,k] = length
205
+ self._cells[i,j,k] = <ptr> cell_ptr
206
+
207
+
208
+ def __dealloc__(self):
209
+ if self._has_initialized_cells():
210
+ deallocate_ptrs(self._cells)
211
+
212
+
213
+ @cython.initializedcheck(False)
214
+ @cython.boundscheck(False)
215
+ @cython.wraparound(False)
216
+ def create_adjacency_matrix(self, float32 threshold_distance):
217
+ """
218
+ create_adjacency_matrix(threshold_distance)
219
+
220
+ Create an adjacency matrix for the atoms in this cell list.
221
+
222
+ An adjacency matrix depicts which atoms *i* and *j* have
223
+ a distance lower than a given threshold distance.
224
+ The values in the adjacency matrix ``m`` are
225
+ ``m[i,j] = 1 if distance(i,j) <= threshold else 0``
226
+
227
+ Parameters
228
+ ----------
229
+ threshold_distance : float
230
+ The threshold distance. All atom pairs that have a distance
231
+ lower than this value are indicated by ``True`` values in
232
+ the resulting matrix.
233
+
234
+ Returns
235
+ -------
236
+ matrix : ndarray, dtype=bool, shape=(n,n)
237
+ An *n x n* adjacency matrix.
238
+ If a `selection` was given to the constructor of the
239
+ :class:`CellList`, the rows and columns corresponding to
240
+ atoms, that are not masked by the selection, have all
241
+ elements set to ``False``.
242
+
243
+ Notes
244
+ -----
245
+ The highest performance is achieved when the the cell size is
246
+ equal to the threshold distance. However, this is purely
247
+ optinal: The resulting adjacency matrix is the same for every
248
+ cell size.
249
+
250
+ Although the adjacency matrix should be symmetric in most cases,
251
+ it may occur that ``m[i,j] != m[j,i]``, when ``distance(i,j)``
252
+ is very close to the `threshold_distance` due to numerical
253
+ errors.
254
+ The matrix can be symmetrized with ``numpy.maximum(a, a.T)``.
255
+
256
+ Examples
257
+ --------
258
+ Create adjacency matrix for CA atoms in a structure:
259
+
260
+ >>> atom_array = atom_array[atom_array.atom_name == "CA"]
261
+ >>> cell_list = CellList(atom_array, 5)
262
+ >>> matrix = cell_list.create_adjacency_matrix(5)
263
+ """
264
+ if threshold_distance < 0:
265
+ raise ValueError("Threshold must be a positive value")
266
+ cdef int i=0
267
+
268
+ # Get atom position for all original positions
269
+ # (no periodic copies)
270
+ coord = np.asarray(self._coord[:self._orig_length])
271
+
272
+ if self._has_selection:
273
+ selection = np.asarray(self._selection, dtype=bool)
274
+ # Create matrix with all elements set to False
275
+ matrix = np.zeros(
276
+ (self._orig_length, self._orig_length), dtype=bool
277
+ )
278
+ # Set only those rows that belong to masked atoms
279
+ matrix[selection, :] = self.get_atoms(
280
+ coord[selection], threshold_distance, as_mask=True
281
+ )
282
+ return matrix
283
+ else:
284
+ return self.get_atoms(coord, threshold_distance, as_mask=True)
285
+
286
+
287
+ @cython.initializedcheck(False)
288
+ @cython.boundscheck(False)
289
+ @cython.wraparound(False)
290
+ def get_atoms(self, np.ndarray coord, radius, bint as_mask=False):
291
+ """
292
+ get_atoms(coord, radius, as_mask=False)
293
+
294
+ Find atoms with a maximum distance from given coordinates.
295
+
296
+ Parameters
297
+ ----------
298
+ coord : ndarray, dtype=float, shape=(3,) or shape=(m,3)
299
+ The central coordinates, around which the atoms are
300
+ searched.
301
+ If a single position is given, the indices of atoms in its
302
+ radius are returned.
303
+ Multiple positions (2-D :class:`ndarray`) have a vectorized
304
+ behavior:
305
+ Each row in the resulting :class:`ndarray` contains the
306
+ indices for the corresponding position.
307
+ Since the positions may have different amounts of adjacent
308
+ atoms, trailing `-1` values are used to indicate nonexisting
309
+ indices.
310
+ radius : float or ndarray, shape=(n,), dtype=float, optional
311
+ The radius around `coord`, in which the atoms are searched,
312
+ i.e. all atoms in `radius` distance to `coord` are returned.
313
+ Either a single radius can be given as scalar, or individual
314
+ radii for each position in `coord` can be provided as
315
+ :class:`ndarray`.
316
+ as_mask : bool, optional
317
+ If true, the result is returned as boolean mask, instead
318
+ of an index array.
319
+
320
+ Returns
321
+ -------
322
+ indices : ndarray, dtype=int32, shape=(p,) or shape=(m,p)
323
+ The indices of the atom array, where the atoms are in the
324
+ defined `radius` around `coord`.
325
+ If `coord` contains multiple positions, this return value is
326
+ two-dimensional with trailing `-1` values for empty values.
327
+ Only returned with `as_mask` set to false.
328
+ mask : ndarray, dtype=bool, shape=(m,n) or shape=(n,)
329
+ Same as `indices`, but as boolean mask.
330
+ The values are true for atoms in the atom array,
331
+ that are in the defined vicinity.
332
+ Only returned with `as_mask` set to true.
333
+
334
+ See Also
335
+ --------
336
+ get_atoms_in_cells
337
+
338
+ Notes
339
+ -----
340
+ In case of a :class:`CellList` with `periodic` set to `True`:
341
+ If more than one periodic copy of an atom is within the
342
+ threshold radius, the returned `indices` array contains the
343
+ corresponding index multiple times.
344
+ Please use ``numpy.unique()``, if this is undesireable.
345
+
346
+ Examples
347
+ --------
348
+ Get adjacent atoms for a single position:
349
+
350
+ >>> cell_list = CellList(atom_array, 3)
351
+ >>> pos = np.array([1.0, 2.0, 3.0])
352
+ >>> indices = cell_list.get_atoms(pos, radius=2.0)
353
+ >>> print(indices)
354
+ [102 104 112]
355
+ >>> print(atom_array[indices])
356
+ A 6 TRP CE3 C 0.779 0.524 2.812
357
+ A 6 TRP CZ3 C 1.439 0.433 4.053
358
+ A 6 TRP HE3 H -0.299 0.571 2.773
359
+ >>> indices = cell_list.get_atoms(pos, radius=3.0)
360
+ >>> print(atom_array[indices])
361
+ A 6 TRP CD2 C 1.508 0.564 1.606
362
+ A 6 TRP CE3 C 0.779 0.524 2.812
363
+ A 6 TRP CZ3 C 1.439 0.433 4.053
364
+ A 6 TRP HE3 H -0.299 0.571 2.773
365
+ A 6 TRP HZ3 H 0.862 0.400 4.966
366
+ A 3 TYR CZ C -0.639 3.053 5.043
367
+ A 3 TYR HH H 1.187 3.395 5.567
368
+ A 19 PRO HD2 H 0.470 3.937 1.260
369
+ A 6 TRP CE2 C 2.928 0.515 1.710
370
+ A 6 TRP CH2 C 2.842 0.407 4.120
371
+ A 18 PRO HA H 2.719 3.181 1.316
372
+ A 18 PRO HB3 H 2.781 3.223 3.618
373
+ A 18 PRO CB C 3.035 4.190 3.187
374
+
375
+ Get adjacent atoms for mutliple positions:
376
+
377
+ >>> cell_list = CellList(atom_array, 3)
378
+ >>> pos = np.array([[1.0,2.0,3.0], [2.0,3.0,4.0], [3.0,4.0,5.0]])
379
+ >>> indices = cell_list.get_atoms(pos, radius=3.0)
380
+ >>> print(indices)
381
+ [[ 99 102 104 112 114 45 55 290 101 105 271 273 268 -1 -1]
382
+ [104 114 45 46 55 44 54 105 271 273 265 268 269 272 275]
383
+ [ 46 55 273 268 269 272 274 275 -1 -1 -1 -1 -1 -1 -1]]
384
+ >>> # Convert to list of arrays and remove trailing -1
385
+ >>> indices = [row[row != -1] for row in indices]
386
+ >>> for row in indices:
387
+ ... print(row)
388
+ [ 99 102 104 112 114 45 55 290 101 105 271 273 268]
389
+ [104 114 45 46 55 44 54 105 271 273 265 268 269 272 275]
390
+ [ 46 55 273 268 269 272 274 275]
391
+ """
392
+ cdef int i=0, j=0
393
+ cdef int array_i = 0
394
+ cdef int max_array_length = 0
395
+ cdef int coord_index
396
+ cdef float32 x1, y1, z1, x2, y2, z2
397
+ cdef float32 sq_dist
398
+ cdef float32 sq_radius
399
+ cdef float32[:] sq_radii
400
+ cdef np.ndarray cell_radii
401
+
402
+ cdef int[:,:] all_indices
403
+ cdef int[:,:] indices
404
+ cdef float32[:,:] coord_v
405
+
406
+ if len(coord) == 0:
407
+ return _empty_result(as_mask)
408
+
409
+ # Handle periodicity for the input coordinates
410
+ if self._periodic:
411
+ coord = move_inside_box(coord, self._box)
412
+ # Convert input parameters into a uniform format
413
+ coord, radius, is_multi_coord, is_multi_radius \
414
+ = _prepare_vectorization(coord, radius, np.float32)
415
+ if is_multi_radius:
416
+ sq_radii = radius * radius
417
+ cell_radii = np.ceil(radius / self._cellsize).astype(np.int32)
418
+ else:
419
+ # All radii are equal
420
+ sq_radii = np.full(
421
+ len(coord), radius[0]*radius[0], dtype=np.float32
422
+ )
423
+ cell_radii = np.full(
424
+ len(coord),
425
+ int(np.ceil(radius[0] / self._cellsize)),
426
+ dtype=np.int32
427
+ )
428
+
429
+ # Get indices for adjacent atoms, based on a cell radius
430
+ all_indices = self._get_atoms_in_cells(
431
+ coord, cell_radii, is_multi_radius
432
+ )
433
+ # These have to be narrowed down in the next step
434
+ # using the Euclidian distance
435
+
436
+ # Filter all indices from all_indices
437
+ # where squared distance is smaller than squared radius
438
+ # Using the squared distance is computationally cheaper than
439
+ # calculating the sqaure root for every distance
440
+ indices = np.full(
441
+ (all_indices.shape[0], all_indices.shape[1]), -1, dtype=np.int32
442
+ )
443
+ coord_v = coord
444
+ for i in range(all_indices.shape[0]):
445
+ sq_radius = sq_radii[i]
446
+ x1 = coord_v[i,0]
447
+ y1 = coord_v[i,1]
448
+ z1 = coord_v[i,2]
449
+ array_i = 0
450
+ for j in range(all_indices.shape[1]):
451
+ coord_index = all_indices[i,j]
452
+ if coord_index != -1:
453
+ x2 = self._coord[coord_index, 0]
454
+ y2 = self._coord[coord_index, 1]
455
+ z2 = self._coord[coord_index, 2]
456
+ sq_dist = squared_distance(x1, y1, z1, x2, y2, z2)
457
+ if sq_dist <= sq_radius:
458
+ indices[i, array_i] = coord_index
459
+ array_i += 1
460
+ if array_i > max_array_length:
461
+ max_array_length = array_i
462
+
463
+ return self._post_process(
464
+ np.asarray(indices)[:, :max_array_length],
465
+ as_mask, is_multi_coord
466
+ )
467
+
468
+
469
+ @cython.boundscheck(False)
470
+ @cython.wraparound(False)
471
+ def get_atoms_in_cells(self, np.ndarray coord,
472
+ cell_radius=1, bint as_mask=False):
473
+ """
474
+ get_atoms_in_cells(coord, cell_radius=1, as_mask=False)
475
+
476
+ Find atoms with a maximum cell distance from given
477
+ coordinates.
478
+
479
+ Instead of using the radius as maximum euclidian distance to the
480
+ given coordinates,
481
+ the radius is measured as the amount of cells:
482
+ A radius of 0 means, that only the atoms in the same cell
483
+ as the given coordinates are considered. A radius of 1 means,
484
+ that the atoms indices from this cell and the 8 surrounding
485
+ cells are returned and so forth.
486
+ This is more efficient than `get_atoms()`.
487
+
488
+ Parameters
489
+ ----------
490
+ coord : ndarray, dtype=float, shape=(3,) or shape=(m,3)
491
+ The central coordinates, around which the atoms are
492
+ searched.
493
+ If a single position is given, the indices of atoms in its
494
+ cell radius are returned.
495
+ Multiple positions (2-D :class:`ndarray`) have a vectorized
496
+ behavior:
497
+ Each row in the resulting :class:`ndarray` contains the
498
+ indices for the corresponding position.
499
+ Since the positions may have different amounts of adjacent
500
+ atoms, trailing `-1` values are used to indicate nonexisting
501
+ indices.
502
+ cell_radius : int or ndarray, shape=(n,), dtype=int, optional
503
+ The radius around `coord` (in amount of cells), in which
504
+ the atoms are searched. This does not correspond to the
505
+ Euclidian distance used in `get_atoms()`. In this case, all
506
+ atoms in the cell corresponding to `coord` and in adjacent
507
+ cells are returned.
508
+ Either a single radius can be given as scalar, or individual
509
+ radii for each position in `coord` can be provided as
510
+ :class:`ndarray`.
511
+ By default atoms are searched in the cell of `coord`
512
+ and directly adjacent cells (cell_radius = 1).
513
+ as_mask : bool, optional
514
+ If true, the result is returned as boolean mask, instead
515
+ of an index array.
516
+
517
+ Returns
518
+ -------
519
+ indices : ndarray, dtype=int32, shape=(p,) or shape=(m,p)
520
+ The indices of the atom array, where the atoms are in the
521
+ defined `radius` around `coord`.
522
+ If `coord` contains multiple positions, this return value is
523
+ two-dimensional with trailing `-1` values for empty values.
524
+ Only returned with `as_mask` set to false.
525
+ mask : ndarray, dtype=bool, shape=(m,n) or shape=(n,)
526
+ Same as `indices`, but as boolean mask.
527
+ The values are true for atoms in the atom array,
528
+ that are in the defined vicinity.
529
+ Only returned with `as_mask` set to true.
530
+
531
+ See Also
532
+ --------
533
+ get_atoms
534
+
535
+ Notes
536
+ -----
537
+ In case of a :class:`CellList` with `periodic` set to `True`:
538
+ If more than one periodic copy of an atom is within the
539
+ threshold radius, the returned `indices` array contains the
540
+ corresponding index multiple times.
541
+ Please use ``numpy.unique()``, if this is undesireable.
542
+ """
543
+ # This function is a thin wrapper around the private method
544
+ # with the same name, with addition of handling periodicty
545
+ # and the ability to return a mask instead of indices
546
+
547
+ if len(coord) == 0:
548
+ return _empty_result(as_mask)
549
+
550
+ # Handle periodicity for the input coordinates
551
+ if self._periodic:
552
+ coord = move_inside_box(coord, self._box)
553
+ # Convert input parameters into a uniform format
554
+ coord, cell_radius, is_multi_coord, is_multi_radius \
555
+ = _prepare_vectorization(coord, cell_radius, np.int32)
556
+ # Get adjacent atom indices
557
+ array_indices = self._get_atoms_in_cells(
558
+ coord, cell_radius, is_multi_radius
559
+ )
560
+ return self._post_process(array_indices, as_mask, is_multi_coord)
561
+
562
+
563
+ @cython.boundscheck(False)
564
+ @cython.wraparound(False)
565
+ def _get_atoms_in_cells(self,
566
+ np.ndarray coord,
567
+ np.ndarray cell_radii,
568
+ bint is_multi_radius):
569
+ """
570
+ Get the indices of atoms in `cell_radii` adjacency of `coord`.
571
+
572
+ Parameters
573
+ ----------
574
+ coord : ndarray, dtype=float32, shape=(n,3)
575
+ The position to find adjacent atoms for.
576
+ cell_radii : ndarray, dtype=int32, shape=(n)
577
+ The radius for each position.
578
+ is_multi_radius : bool
579
+ True indicates, that all values in `cell_radii` are the
580
+ same.
581
+
582
+ Returns
583
+ -------
584
+ array_indices : ndarray, dtype=int32, shape=(m,p)
585
+ Indices of adjancent atoms.
586
+ """
587
+
588
+ cdef int max_cell_radius
589
+ if is_multi_radius:
590
+ max_cell_radius = np.max(cell_radii)
591
+ else:
592
+ # All radii are equal
593
+ max_cell_radius = cell_radii[0]
594
+ # Worst case assumption on index array length requirement:
595
+ # At maximum, the amount of adjacent atoms can only be the
596
+ # maximum amount of atoms per cell times the amount of cells
597
+ # Since the cells extend in 3 dimensions the amount of cells is
598
+ # (2*r + 1)**3
599
+ cdef int length = (2*max_cell_radius + 1)**3 * self._max_cell_length
600
+ array_indices = np.full((len(coord), length), -1, dtype=np.int32)
601
+ # Fill index array
602
+ cdef int max_array_length \
603
+ = self._find_adjacent_atoms(coord, array_indices, cell_radii)
604
+ return array_indices[:, :max_array_length]
605
+
606
+
607
+ @cython.boundscheck(False)
608
+ @cython.wraparound(False)
609
+ cdef int _find_adjacent_atoms(self,
610
+ float32[:,:] coord,
611
+ int[:,:] indices,
612
+ int[:] cell_radius):
613
+ """
614
+ This method fills the given empty index array
615
+ with actual indices of adjacent atoms.
616
+
617
+ Since the length of 'indices' (second dimension) is
618
+ the worst case assumption, this method returns the actual
619
+ required length, i.e. the highest length of all arrays
620
+ in this 'array of arrays'.
621
+ """
622
+ cdef int length
623
+ cdef int* list_ptr
624
+ cdef float32 x, y,z
625
+ cdef int i=0, j=0, k=0
626
+ cdef int adj_i, adj_j, adj_k
627
+ cdef int pos_i, array_i, cell_i
628
+ cdef int max_array_length = 0
629
+ cdef int cell_r
630
+
631
+ cdef ptr[:,:,:] cells = self._cells
632
+ cdef int[:,:,:] cell_length = self._cell_length
633
+ cdef uint8[:] finite_mask = (
634
+ np.isfinite(np.asarray(coord)).all(axis=-1).astype(np.uint8, copy=False)
635
+ )
636
+
637
+ for pos_i in range(coord.shape[0]):
638
+ if not finite_mask[pos_i]:
639
+ # For non-finite coordinates, there are no adjacent atoms
640
+ continue
641
+ array_i = 0
642
+ cell_r = cell_radius[pos_i]
643
+ x = coord[pos_i, 0]
644
+ y = coord[pos_i, 1]
645
+ z = coord[pos_i, 2]
646
+ self._get_cell_index(x, y, z, &i, &j, &k)
647
+ # Look into cells of the indices and adjacent cells
648
+ # in all 3 dimensions
649
+
650
+ for adj_i in range(i-cell_r, i+cell_r+1):
651
+ if (adj_i >= 0 and adj_i < cells.shape[0]):
652
+ for adj_j in range(j-cell_r, j+cell_r+1):
653
+ if (adj_j >= 0 and adj_j < cells.shape[1]):
654
+ for adj_k in range(k-cell_r, k+cell_r+1):
655
+ if (adj_k >= 0 and adj_k < cells.shape[2]):
656
+ # Fill index array
657
+ # with indices in cell
658
+ list_ptr = <int*>cells[adj_i, adj_j, adj_k]
659
+ length = cell_length[adj_i, adj_j, adj_k]
660
+ for cell_i in range(length):
661
+ indices[pos_i, array_i] = \
662
+ list_ptr[cell_i]
663
+ array_i += 1
664
+ if array_i > max_array_length:
665
+ max_array_length = array_i
666
+ return max_array_length
667
+
668
+
669
+ @cython.boundscheck(False)
670
+ @cython.wraparound(False)
671
+ def _post_process(self,
672
+ np.ndarray indices,
673
+ bint as_mask,
674
+ bint is_multi_coord):
675
+ """
676
+ Post process the resulting indices of adjacent atoms,
677
+ including periodicity handling and optional conversion into a
678
+ boolean matrix.
679
+ """
680
+ # Handle periodicity for the output indices
681
+ if self._periodic:
682
+ # Map indices of repeated coordinates to original
683
+ # coordinates, i.e. the coordinates in the central box
684
+ # -> Remainder of dividing index by original array length
685
+ # Furthermore this ensures, that the indices have valid
686
+ # values for '_as_mask()'
687
+ indices[indices != -1] %= self._orig_length
688
+ if as_mask:
689
+ matrix = self._as_mask(indices)
690
+ if is_multi_coord:
691
+ return matrix
692
+ else:
693
+ return matrix[0]
694
+ else:
695
+ if is_multi_coord:
696
+ return indices
697
+ else:
698
+ return indices[0]
699
+
700
+
701
+ @cython.initializedcheck(False)
702
+ @cython.boundscheck(False)
703
+ @cython.wraparound(False)
704
+ @cython.cdivision(True)
705
+ cdef inline void _get_cell_index(self, float32 x, float32 y, float32 z,
706
+ int* i, int* j, int* k):
707
+ i[0] = <int>((x - self._min_coord[0]) / self._cellsize)
708
+ j[0] = <int>((y - self._min_coord[1]) / self._cellsize)
709
+ k[0] = <int>((z - self._min_coord[2]) / self._cellsize)
710
+
711
+ @cython.initializedcheck(False)
712
+ @cython.boundscheck(False)
713
+ @cython.wraparound(False)
714
+ cdef inline bint _check_coord(self, float32 x, float32 y, float32 z):
715
+ if x < self._min_coord[0] or x > self._max_coord[0]:
716
+ return False
717
+ if y < self._min_coord[1] or y > self._max_coord[1]:
718
+ return False
719
+ if z < self._min_coord[2] or z > self._max_coord[2]:
720
+ return False
721
+ return True
722
+
723
+ @cython.initializedcheck(False)
724
+ @cython.boundscheck(False)
725
+ @cython.wraparound(False)
726
+ cdef np.ndarray _as_mask(self, int[:,:] indices):
727
+ cdef int i,j
728
+ cdef int index
729
+ cdef uint8[:,:] matrix = np.zeros(
730
+ (indices.shape[0], self._orig_length), dtype=np.uint8
731
+ )
732
+ # Fill matrix
733
+ for i in range(indices.shape[0]):
734
+ for j in range(indices.shape[1]):
735
+ index = indices[i,j]
736
+ if index == -1:
737
+ # End of list -> jump to next position
738
+ break
739
+ matrix[i, index] = True
740
+ return np.asarray(matrix, dtype=bool)
741
+
742
+ cdef inline bint _has_initialized_cells(self):
743
+ # Memoryviews are not initialized on class creation
744
+ # This method checks if the _cells memoryview was initialized
745
+ # and is not None
746
+ try:
747
+ if self._cells is not None:
748
+ return True
749
+ else:
750
+ return False
751
+ except AttributeError:
752
+ return False
753
+
754
+
755
+ def _check_coord(coord):
756
+ """
757
+ Perform checks on validity of coordinates.
758
+ """
759
+ if coord.ndim != 2:
760
+ raise ValueError("Coordinates must have shape (n,3)")
761
+ if coord.shape[0] == 0:
762
+ raise ValueError("Coordinates must not be empty")
763
+ if coord.shape[1] != 3:
764
+ raise ValueError("Coordinates must have form (x,y,z)")
765
+ if not np.isfinite(coord).all():
766
+ raise ValueError("Coordinates contain non-finite values")
767
+
768
+
769
+ def _empty_result(as_mask):
770
+ """
771
+ Create return value for :func:`get_atoms()` and
772
+ :func:`get_atoms_in_cells()`, if no coordinates are given.
773
+ """
774
+ if as_mask:
775
+ return np.array([], dtype=bool)
776
+ else:
777
+ return np.array([], dtype=np.int32)
778
+
779
+
780
+ def _prepare_vectorization(np.ndarray coord, radius, radius_dtype):
781
+ """
782
+ Since `get_atoms()` and `get_atoms_in_cells()`, may take different
783
+ amount of dimensions for the coordinates and the radius to enable
784
+ vectorized compuation, each of these functions would need to handle
785
+ the different cases.
786
+
787
+ This function converts the input radius and coordinates into a
788
+ uniform format and also return, whether single/multiple
789
+ radii/coordinates were given.
790
+
791
+ The shapes before and after conversion are:
792
+
793
+ - coord: (3, ), radius: scalar -> coord: (1,3), radius: (1,)
794
+ - coord: (n,3), radius: scalar -> coord: (n,3), radius: (n,)
795
+ - coord: (n,3), radius: (n, ) -> coord: (n,3), radius: (n,)
796
+
797
+ Thes resulting values have the same dimensionality for all cases and
798
+ can be handeled uniformly by `get_atoms()` and
799
+ `get_atoms_in_cells()`.
800
+ """
801
+ cdef bint is_multi_coord
802
+ cdef bint is_multi_radius
803
+
804
+ if coord.ndim == 1 and coord.shape[0] == 3:
805
+ # Single position
806
+ coord = coord[np.newaxis, :].astype(np.float32, copy=False)
807
+ is_multi_coord = False
808
+ elif coord.ndim == 2 and coord.shape[1] == 3:
809
+ # Multiple positions
810
+ coord = coord.astype(np.float32, copy=False)
811
+ is_multi_coord = True
812
+ else:
813
+ raise ValueError(
814
+ f"Invalid shape for input coordinates"
815
+ )
816
+
817
+ if isinstance(radius, np.ndarray):
818
+ # Multiple radii
819
+ # Check whether amount of coordinates match amount of radii
820
+ if not is_multi_coord:
821
+ raise ValueError(
822
+ "Cannot accept array of radii, if a single position is given"
823
+ )
824
+ if radius.ndim != 1:
825
+ raise ValueError("Array of radii must be one-dimensional")
826
+ if radius.shape[0] != coord.shape[0]:
827
+ raise ValueError(
828
+ f"Amount of radii ({radius.shape[0]}) "
829
+ f"and coordinates ({coord.shape[0]}) are not equal"
830
+ )
831
+ if (radius < 0).any():
832
+ raise ValueError("Radii must be a positive values")
833
+ radius = radius.astype(radius_dtype, copy=False)
834
+ is_multi_radius = True
835
+ else:
836
+ # Single radius
837
+ if radius < 0:
838
+ raise ValueError("Radius must be a positive value")
839
+ # If only a single integer is given,
840
+ # create numpy array filled with identical values
841
+ # with the same length as the coordiantes
842
+ radius = np.full(coord.shape[0], radius, dtype=radius_dtype)
843
+ is_multi_radius = False
844
+
845
+ return coord, radius, is_multi_coord, is_multi_radius
846
+
847
+
848
+ cdef inline void deallocate_ptrs(ptr[:,:,:] ptrs):
849
+ cdef int i, j, k
850
+ cdef int* cell_ptr
851
+ # Free cell pointers
852
+ for i in range(ptrs.shape[0]):
853
+ for j in range(ptrs.shape[1]):
854
+ for k in range(ptrs.shape[2]):
855
+ cell_ptr = <int*>ptrs[i,j,k]
856
+ free(cell_ptr)
857
+
858
+
859
+ cdef inline float32 squared_distance(float32 x1, float32 y1, float32 z1,
860
+ float32 x2, float32 y2, float32 z2):
861
+ cdef float32 diff_x = x2 - x1
862
+ cdef float32 diff_y = y2 - y1
863
+ cdef float32 diff_z = z2 - z1
864
+ return diff_x*diff_x + diff_y*diff_y + diff_z*diff_z