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