biotite 0.41.1__cp310-cp310-macosx_10_16_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 (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-310-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-310-darwin.so +0 -0
  61. biotite/sequence/align/kmeralphabet.pyx +574 -0
  62. biotite/sequence/align/kmersimilarity.cpython-310-darwin.so +0 -0
  63. biotite/sequence/align/kmersimilarity.pyx +233 -0
  64. biotite/sequence/align/kmertable.cpython-310-darwin.so +0 -0
  65. biotite/sequence/align/kmertable.pyx +3400 -0
  66. biotite/sequence/align/localgapped.cpython-310-darwin.so +0 -0
  67. biotite/sequence/align/localgapped.pyx +892 -0
  68. biotite/sequence/align/localungapped.cpython-310-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-310-darwin.so +0 -0
  161. biotite/sequence/align/multiple.pyx +620 -0
  162. biotite/sequence/align/pairwise.cpython-310-darwin.so +0 -0
  163. biotite/sequence/align/pairwise.pyx +587 -0
  164. biotite/sequence/align/permutation.cpython-310-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-310-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-310-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-310-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-310-darwin.so +0 -0
  223. biotite/sequence/phylo/nj.pyx +221 -0
  224. biotite/sequence/phylo/tree.cpython-310-darwin.so +0 -0
  225. biotite/sequence/phylo/tree.pyx +1169 -0
  226. biotite/sequence/phylo/upgma.cpython-310-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-310-darwin.so +0 -0
  236. biotite/structure/bonds.pyx +1933 -0
  237. biotite/structure/box.py +592 -0
  238. biotite/structure/celllist.cpython-310-darwin.so +0 -0
  239. biotite/structure/celllist.pyx +849 -0
  240. biotite/structure/chains.py +298 -0
  241. biotite/structure/charges.cpython-310-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-310-darwin.so +0 -0
  279. biotite/structure/io/mmtf/convertarray.pyx +341 -0
  280. biotite/structure/io/mmtf/convertfile.cpython-310-darwin.so +0 -0
  281. biotite/structure/io/mmtf/convertfile.pyx +501 -0
  282. biotite/structure/io/mmtf/decode.cpython-310-darwin.so +0 -0
  283. biotite/structure/io/mmtf/decode.pyx +152 -0
  284. biotite/structure/io/mmtf/encode.cpython-310-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-310-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-310-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-310-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,1455 @@
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 contains the main types of the ``structure`` subpackage:
7
+ :class:`Atom`, :class:`AtomArray` and :class:`AtomArrayStack`.
8
+ """
9
+
10
+ __name__ = "biotite.structure"
11
+ __author__ = "Patrick Kunzmann"
12
+ __all__ = ["Atom", "AtomArray", "AtomArrayStack",
13
+ "array", "stack", "repeat", "from_template", "coord"]
14
+
15
+ import numbers
16
+ import abc
17
+ import numpy as np
18
+ from .bonds import BondList
19
+ from ..copyable import Copyable
20
+
21
+
22
+ class _AtomArrayBase(Copyable, metaclass=abc.ABCMeta):
23
+ """
24
+ Private base class for :class:`AtomArray` and
25
+ :class:`AtomArrayStack`.
26
+ It implements functionality for annotation arrays and also
27
+ rudimentarily for coordinates.
28
+ """
29
+
30
+ def __init__(self, length):
31
+ """
32
+ Create the annotation arrays
33
+ """
34
+ self._annot = {}
35
+ self._array_length = length
36
+ self._coord = None
37
+ self._bonds = None
38
+ self._box = None
39
+ self.add_annotation("chain_id", dtype="U4")
40
+ self.add_annotation("res_id", dtype=int)
41
+ self.add_annotation("ins_code", dtype="U1")
42
+ self.add_annotation("res_name", dtype="U5")
43
+ self.add_annotation("hetero", dtype=bool)
44
+ self.add_annotation("atom_name", dtype="U6")
45
+ self.add_annotation("element", dtype="U2")
46
+
47
+ def array_length(self):
48
+ """
49
+ Get the length of the atom array.
50
+
51
+ This value is equivalent to the length of each annotation array.
52
+ For :class:`AtomArray` it is the same as ``len(array)``.
53
+
54
+ Returns
55
+ -------
56
+ length : int
57
+ Length of the array(s).
58
+ """
59
+ return self._array_length
60
+
61
+ @property
62
+ @abc.abstractmethod
63
+ def shape(self):
64
+ """
65
+ Tuple of array dimensions.
66
+
67
+ This property contains the current shape of the object.
68
+
69
+ Returns
70
+ -------
71
+ shape : tuple of int
72
+ Shape of the object.
73
+ """
74
+ return
75
+
76
+ def add_annotation(self, category, dtype):
77
+ """
78
+ Add an annotation category, if not already existing.
79
+
80
+ Initially the new annotation is filled with the *zero*
81
+ representation of the given type.
82
+
83
+ Parameters
84
+ ----------
85
+ category : str
86
+ The annotation category to be added.
87
+ dtype : type or str
88
+ A type instance or a valid *NumPy* *dtype* string.
89
+ Defines the type of the annotation
90
+
91
+ See Also
92
+ --------
93
+ set_annotation
94
+ """
95
+ if category not in self._annot:
96
+ self._annot[str(category)] = np.zeros(self._array_length,
97
+ dtype=dtype)
98
+
99
+ def del_annotation(self, category):
100
+ """
101
+ Removes an annotation category.
102
+
103
+ Parameters
104
+ ----------
105
+ category : str
106
+ The annotation category to be removed.
107
+ """
108
+ if category in self._annot:
109
+ del self._annot[str(category)]
110
+
111
+ def get_annotation(self, category):
112
+ """
113
+ Return an annotation array.
114
+
115
+ Parameters
116
+ ----------
117
+ category : str
118
+ The annotation category to be returned.
119
+
120
+ Returns
121
+ -------
122
+ array : ndarray
123
+ The annotation array.
124
+ """
125
+ if category not in self._annot:
126
+ raise ValueError(
127
+ f"Annotation category '{category}' is not existing"
128
+ )
129
+ return self._annot[category]
130
+
131
+ def set_annotation(self, category, array):
132
+ """
133
+ Set an annotation array. If the annotation category does not
134
+ exist yet, the category is created.
135
+
136
+ Parameters
137
+ ----------
138
+ category : str
139
+ The annotation category to be set.
140
+ array : ndarray or None
141
+ The new value of the annotation category. The size of the
142
+ array must be the same as the array length.
143
+ """
144
+ if len(array) != self._array_length:
145
+ raise IndexError(
146
+ f"Expected array length {self._array_length}, "
147
+ f"but got {len(array)}"
148
+ )
149
+ if category in self._annot:
150
+ # Keep the dtype if the annotation already exists
151
+ self._annot[category] = np.asarray(
152
+ array, dtype=self._annot[category].dtype
153
+ )
154
+ else:
155
+ self._annot[category] = np.asarray(array)
156
+
157
+ def get_annotation_categories(self):
158
+ """
159
+ Return a list containing all annotation array categories.
160
+
161
+ Returns
162
+ -------
163
+ categories : list
164
+ The list containing the names of each annotation array.
165
+ """
166
+ return list(self._annot.keys())
167
+
168
+ def _subarray(self, index):
169
+ # Index is one dimensional (boolean mask, index array)
170
+ new_coord = self._coord[..., index, :]
171
+ new_length = new_coord.shape[-2]
172
+ if isinstance(self, AtomArray):
173
+ new_object = AtomArray(new_length)
174
+ elif isinstance(self, AtomArrayStack):
175
+ new_depth = new_coord.shape[-3]
176
+ new_object = AtomArrayStack(new_depth, new_length)
177
+ new_object._coord = new_coord
178
+ if self._bonds is not None:
179
+ new_object._bonds = self._bonds[index]
180
+ if self._box is not None:
181
+ new_object._box = self._box
182
+ for annotation in self._annot:
183
+ new_object._annot[annotation] = (self._annot[annotation]
184
+ .__getitem__(index))
185
+ return new_object
186
+
187
+ def _set_element(self, index, atom):
188
+ try:
189
+ if isinstance(index, (numbers.Integral, np.ndarray)):
190
+ for name in self._annot:
191
+ self._annot[name][index] = atom._annot[name]
192
+ self._coord[..., index, :] = atom.coord
193
+ else:
194
+ raise TypeError(
195
+ f"Index must be integer, not '{type(index).__name__}'"
196
+ )
197
+ except KeyError:
198
+ raise KeyError("The annotations of the 'Atom' are incompatible")
199
+
200
+ def _del_element(self, index):
201
+ if isinstance(index, numbers.Integral):
202
+ for name in self._annot:
203
+ self._annot[name] = np.delete(self._annot[name], index, axis=0)
204
+ self._coord = np.delete(self._coord, index, axis=-2)
205
+ self._array_length = self._coord.shape[-2]
206
+ if self._bonds is not None:
207
+ mask = np.ones(self._bonds.get_atom_count(), dtype=bool)
208
+ mask[index] = False
209
+ self._bonds = self._bonds[mask]
210
+ else:
211
+ raise TypeError(
212
+ f"Index must be integer, not '{type(index).__name__}'"
213
+ )
214
+
215
+ def equal_annotations(self, item):
216
+ """
217
+ Check, if this object shares equal annotation arrays with the
218
+ given :class:`AtomArray` or :class:`AtomArrayStack`.
219
+
220
+ Parameters
221
+ ----------
222
+ item : AtomArray or AtomArrayStack
223
+ The object to compare the annotation arrays with.
224
+
225
+ Returns
226
+ -------
227
+ equality : bool
228
+ True, if the annotation arrays are equal.
229
+ """
230
+ if not isinstance(item, _AtomArrayBase):
231
+ return False
232
+ if not self.equal_annotation_categories(item):
233
+ return False
234
+ for name in self._annot:
235
+ if not np.array_equal(self._annot[name], item._annot[name]):
236
+ return False
237
+ return True
238
+
239
+ def equal_annotation_categories(self, item):
240
+ """
241
+ Check, if this object shares equal annotation array categories
242
+ with the given :class:`AtomArray` or :class:`AtomArrayStack`.
243
+
244
+ Parameters
245
+ ----------
246
+ item : AtomArray or AtomArrayStack
247
+ The object to compare the annotation arrays with.
248
+
249
+ Returns
250
+ -------
251
+ equality : bool
252
+ True, if the annotation array names are equal.
253
+ """
254
+ return sorted(self._annot.keys()) == sorted(item._annot.keys())
255
+
256
+ def __getattr__(self, attr):
257
+ """
258
+ If the attribute is an annotation, the annotation is returned
259
+ from the dictionary.
260
+ Exposes coordinates.
261
+ """
262
+ if attr == "coord":
263
+ return self._coord
264
+ if attr == "bonds":
265
+ return self._bonds
266
+ if attr == "box":
267
+ return self._box
268
+ # Call method of 'object' superclass to avoid infinite recursive
269
+ # calls of '__getattr__()'
270
+ elif attr in super().__getattribute__("_annot"):
271
+ return self._annot[attr]
272
+ else:
273
+ raise AttributeError(
274
+ f"'{type(self).__name__}' object has no attribute '{attr}'"
275
+ )
276
+
277
+ def __setattr__(self, attr, value):
278
+ """
279
+ If the attribute is an annotation, the :attr:`value` is saved
280
+ to the annotation in the dictionary.
281
+ Exposes coordinates.
282
+ :attr:`value` must have same length as :func:`array_length()`.
283
+ """
284
+ if attr == "coord":
285
+ if not isinstance(value, np.ndarray):
286
+ raise TypeError("Value must be ndarray of floats")
287
+ if isinstance(self, AtomArray):
288
+ if value.ndim != 2:
289
+ raise ValueError(
290
+ "A 2-dimensional ndarray is expected "
291
+ "for an AtomArray"
292
+ )
293
+ elif isinstance(self, AtomArrayStack):
294
+ if value.ndim != 3:
295
+ raise ValueError(
296
+ "A 3-dimensional ndarray is expected "
297
+ "for an AtomArrayStack"
298
+ )
299
+ if value.shape[-2] != self._array_length:
300
+ raise ValueError(
301
+ f"Expected array length {self._array_length}, "
302
+ f"but got {len(value)}"
303
+ )
304
+ if value.shape[-1] != 3:
305
+ raise TypeError("Expected 3 coordinates for each atom")
306
+ super().__setattr__("_coord", value.astype(np.float32, copy=False))
307
+
308
+ elif attr == "bonds":
309
+ if isinstance(value, BondList):
310
+ if value.get_atom_count() != self._array_length:
311
+ raise ValueError(
312
+ f"Array length is {self._array_length}, "
313
+ f"but bond list has {value.get_atom_count()} atoms"
314
+ )
315
+ super().__setattr__("_bonds", value)
316
+ elif value is None:
317
+ # Remove bond list
318
+ super().__setattr__("_bonds", None)
319
+ else:
320
+ raise TypeError("Value must be 'BondList'")
321
+
322
+ elif attr == "box":
323
+ if isinstance(value, np.ndarray):
324
+ if isinstance(self, AtomArray):
325
+ if value.ndim != 2:
326
+ raise ValueError(
327
+ "A 2-dimensional ndarray is expected "
328
+ "for an AtomArray"
329
+ )
330
+ else: # AtomArrayStack
331
+ if value.ndim != 3:
332
+ raise ValueError(
333
+ "A 3-dimensional ndarray is expected "
334
+ "for an AtomArrayStack"
335
+ )
336
+ if value.shape[-2:] != (3,3):
337
+ raise TypeError("Box must be a 3x3 matrix (three vectors)")
338
+ box = value.astype(np.float32, copy=False)
339
+ super().__setattr__("_box", box)
340
+ elif value is None:
341
+ # Remove box
342
+ super().__setattr__("_box", None)
343
+ else:
344
+ raise TypeError("Box must be ndarray of floats or None")
345
+
346
+ elif attr == "_annot":
347
+ super().__setattr__(attr, value)
348
+ elif attr in self._annot:
349
+ self.set_annotation(attr, value)
350
+ else:
351
+ super().__setattr__(attr, value)
352
+
353
+ def __dir__(self):
354
+ attr = super().__dir__()
355
+ attr.append("coord")
356
+ attr.append("bonds")
357
+ attr.append("box")
358
+ for name in self._annot.keys():
359
+ attr.append(name)
360
+ return attr
361
+
362
+ def __eq__(self, item):
363
+ """
364
+ See Also
365
+ --------
366
+ equal_annotations
367
+ """
368
+ if not self.equal_annotations(item):
369
+ return False
370
+ if self._bonds != item._bonds:
371
+ return False
372
+ if self._box is None:
373
+ if item._box is not None:
374
+ return False
375
+ else:
376
+ if not np.array_equal(self._box, item._box):
377
+ return False
378
+ return np.array_equal(self._coord, item._coord)
379
+
380
+ def __len__(self):
381
+ """
382
+ The length of the annotation arrays.
383
+
384
+ Returns
385
+ -------
386
+ length : int
387
+ Length of the annotation arrays.
388
+ """
389
+ return self._array_length
390
+
391
+ def __add__(self, array):
392
+ if type(self) != type(array):
393
+ raise TypeError("Can only concatenate two arrays or two stacks")
394
+ # Create either new array or stack, depending of the own type
395
+ if isinstance(self, AtomArray):
396
+ concat = AtomArray(length = self._array_length+array._array_length)
397
+ if isinstance(self, AtomArrayStack):
398
+ concat = AtomArrayStack(self.stack_depth(),
399
+ self._array_length + array._array_length)
400
+
401
+ concat._coord = np.concatenate((self._coord, array.coord), axis=-2)
402
+
403
+ # Transfer only annotations,
404
+ # which are existent in both operands
405
+ arr_categories = list(array._annot.keys())
406
+ for category in self._annot.keys():
407
+ if category in arr_categories:
408
+ annot = self._annot[category]
409
+ arr_annot = array._annot[category]
410
+ concat._annot[category] = np.concatenate((annot,arr_annot))
411
+
412
+ # Concatenate bonds lists,
413
+ # if at least one of them contains bond information
414
+ if self._bonds is not None or array._bonds is not None:
415
+ bonds1 = self._bonds
416
+ bonds2 = array._bonds
417
+ if bonds1 is None:
418
+ bonds1 = BondList(self._array_length)
419
+ if bonds2 is None:
420
+ bonds2 = BondList(array._array_length)
421
+ concat._bonds = bonds1 + bonds2
422
+
423
+ # Copy box
424
+ if self._box is not None:
425
+ concat._box = np.copy(self._box)
426
+ return concat
427
+
428
+ def __copy_fill__(self, clone):
429
+ super().__copy_fill__(clone)
430
+ self._copy_annotations(clone)
431
+ clone._coord = np.copy(self._coord)
432
+
433
+ def _copy_annotations(self, clone):
434
+ for name in self._annot:
435
+ clone._annot[name] = np.copy(self._annot[name])
436
+ if self._box is not None:
437
+ clone._box = np.copy(self._box)
438
+ if self._bonds is not None:
439
+ clone._bonds = self._bonds.copy()
440
+
441
+
442
+ class Atom(Copyable):
443
+ """
444
+ A representation of a single atom.
445
+
446
+ The coordinates an annotations can be accessed directly.
447
+ A detailed description of each annotation category can be viewed
448
+ :doc:`here </apidoc/biotite.structure>`.
449
+
450
+ Parameters
451
+ ----------
452
+ coord: list or ndarray
453
+ The x, y and z coordinates.
454
+ kwargs
455
+ Atom annotations as key value pair.
456
+
457
+ Attributes
458
+ ----------
459
+ {annot} : scalar
460
+ Annotations for this atom.
461
+ coord : ndarray, dtype=float
462
+ ndarray containing the x, y and z coordinate of the atom.
463
+ shape : tuple of int
464
+ Shape of the object.
465
+ In case of an :class:`Atom`, the tuple is empty.
466
+
467
+ Examples
468
+ --------
469
+
470
+ >>> atom = Atom([1,2,3], chain_id="A")
471
+ >>> atom.atom_name = "CA"
472
+ >>> print(atom.atom_name)
473
+ CA
474
+ >>> print(atom.coord)
475
+ [1. 2. 3.]
476
+
477
+ """
478
+
479
+ def __init__(self, coord, **kwargs):
480
+ self._annot = {}
481
+ self._annot["chain_id"] = ""
482
+ self._annot["res_id"] = 0
483
+ self._annot["ins_code"] = ""
484
+ self._annot["res_name"] = ""
485
+ self._annot["hetero"] = False
486
+ self._annot["atom_name"] = ""
487
+ self._annot["element"] = ""
488
+ if "kwargs" in kwargs:
489
+ # kwargs are given directly as dictionary
490
+ kwargs = kwargs["kwargs"]
491
+ for name, annotation in kwargs.items():
492
+ self._annot[name] = annotation
493
+ coord = np.array(coord, dtype=np.float32)
494
+ # Check if coord contains x,y and z coordinates
495
+ if coord.shape != (3,):
496
+ raise ValueError("Position must be ndarray with shape (3,)")
497
+ self.coord = coord
498
+
499
+ def __repr__(self):
500
+ """Represent Atom as a string for debugging."""
501
+ annot = 'chain_id="' + self._annot["chain_id"] + '"'
502
+ annot = annot + ', res_id=' + str(self._annot["res_id"])
503
+ annot = annot + ', ins_code="' + self._annot["ins_code"] + '"'
504
+ annot = annot + ', res_name="' + self._annot["res_name"] + '"'
505
+ annot = annot + ', hetero=' + str(self._annot["hetero"])
506
+ annot = annot + ', atom_name="' + self._annot["atom_name"] + '"'
507
+ annot = annot + ', element="' + self._annot["element"] + '"'
508
+ return f'Atom(np.{np.array_repr(self.coord)}, {annot})'
509
+
510
+ @property
511
+ def shape(self):
512
+ return ()
513
+
514
+ def __getattr__(self, attr):
515
+ if attr in super().__getattribute__("_annot"):
516
+ return self._annot[attr]
517
+ else:
518
+ raise AttributeError(
519
+ f"'{type(self).__name__}' object has no attribute '{attr}'"
520
+ )
521
+
522
+ def __setattr__(self, attr, value):
523
+ if attr == "_annot":
524
+ super().__setattr__(attr, value)
525
+ elif attr == "coord":
526
+ super().__setattr__(attr, value)
527
+ else:
528
+ self._annot[attr] = value
529
+
530
+ def __str__(self):
531
+ hetero = "HET" if self.hetero else ""
532
+ return f"{hetero:3} {self.chain_id:3} " \
533
+ f"{self.res_id:5d}{self.ins_code:1} {self.res_name:3} " \
534
+ f"{self.atom_name:6} {self.element:2} " \
535
+ f"{self.coord[0]:8.3f} " \
536
+ f"{self.coord[1]:8.3f} " \
537
+ f"{self.coord[2]:8.3f}"
538
+
539
+ def __eq__(self, item):
540
+ if not isinstance(item, Atom):
541
+ return False
542
+ if not np.array_equal(self.coord, item.coord):
543
+ return False
544
+ if self._annot.keys() != item._annot.keys():
545
+ return False
546
+ for name in self._annot:
547
+ if self._annot[name] != item._annot[name]:
548
+ return False
549
+ return True
550
+
551
+ def __ne__(self, item):
552
+ return not self == item
553
+
554
+ def __copy_create__(self):
555
+ return Atom(self.coord, **self._annot)
556
+
557
+
558
+ class AtomArray(_AtomArrayBase):
559
+ """
560
+ An array representation of a model consisting of multiple atoms.
561
+
562
+ An :class:`AtomArray` can be seen as a list of :class:`Atom`
563
+ instances.
564
+ Instead of using directly a list, this class uses an *NumPy*
565
+ :class:`ndarray` for each annotation category and the coordinates.
566
+ These
567
+ coordinates can be accessed directly via the :attr:`coord`
568
+ attribute.
569
+ The annotations are accessed either via the category as attribute
570
+ name or the :func:`get_annotation()`, :func:`set_annotation()`
571
+ method.
572
+ Usage of custom annotations is achieved via :func:`add_annotation()`
573
+ or :func:`set_annotation()`.
574
+ A detailed description of each annotation category can be viewed
575
+ :doc:`here </apidoc/biotite.structure>`.
576
+
577
+ In order to get an an subarray of an :class:`AtomArray`,
578
+ *NumPy* style indexing is used.
579
+ This includes slices, boolean arrays, index arrays and even
580
+ *Ellipsis* notation.
581
+ Using a single integer as index returns a single :class:`Atom`
582
+ instance.
583
+
584
+ Inserting or appending an :class:`AtomArray` to another
585
+ :class:`AtomArray` is done with the '+' operator.
586
+ Only the annotation categories, which are existing in both arrays,
587
+ are transferred to the new array.
588
+
589
+ Optionally, an :class:`AtomArray` can store chemical bond
590
+ information via a :class:`BondList` object.
591
+ It can be accessed using the :attr:`bonds` attribute.
592
+ If no bond information is available, :attr:`bonds` is ``None``.
593
+ Consequently the bond information can be removed from the
594
+ :class:`AtomArray`, by setting :attr:`bonds` to ``None``.
595
+ When indexing the :class:`AtomArray` the atom indices in the
596
+ associated :class:`BondList` are updated as well, hence the indices
597
+ in the :class:`BondList` will always point to the same atoms.
598
+ If two :class:`AtomArray` instances are concatenated, the resulting
599
+ :class:`AtomArray` will contain the merged :class:`BondList` if at
600
+ least one of the operands contains bond information.
601
+
602
+ The :attr:`box` attribute contains the box vectors of the unit cell
603
+ or the MD simulation box, respectively.
604
+ Hence, it is a *3 x 3* *ndarray* with the vectors in the last
605
+ dimension.
606
+ If no box is provided, the attribute is ``None``.
607
+ Setting the :attr:`box` attribute to ``None`` means removing the
608
+ box from the atom array.
609
+
610
+ Parameters
611
+ ----------
612
+ length : int
613
+ The fixed amount of atoms in the array.
614
+
615
+ Attributes
616
+ ----------
617
+ {annot} : ndarray
618
+ Multiple n-length annotation arrays.
619
+ coord : ndarray, dtype=float, shape=(n,3)
620
+ ndarray containing the x, y and z coordinate of the
621
+ atoms.
622
+ bonds : BondList or None
623
+ A :class:`BondList`, specifying the indices of atoms
624
+ that form a chemical bond.
625
+ box : ndarray, dtype=float, shape=(3,3) or None
626
+ The surrounding box. May represent a MD simulation box
627
+ or a crystallographic unit cell.
628
+ shape : tuple of int
629
+ Shape of the atom array.
630
+ The single value in the tuple is
631
+ the length of the atom array.
632
+
633
+ Examples
634
+ --------
635
+ Creating an atom array from atoms:
636
+
637
+ >>> atom1 = Atom([1,2,3], chain_id="A")
638
+ >>> atom2 = Atom([2,3,4], chain_id="A")
639
+ >>> atom3 = Atom([3,4,5], chain_id="B")
640
+ >>> atom_array = array([atom1, atom2, atom3])
641
+ >>> print(atom_array.array_length())
642
+ 3
643
+
644
+ Accessing an annotation array:
645
+
646
+ >>> print(atom_array.chain_id)
647
+ ['A' 'A' 'B']
648
+
649
+ Accessing the coordinates:
650
+
651
+ >>> print(atom_array.coord)
652
+ [[1. 2. 3.]
653
+ [2. 3. 4.]
654
+ [3. 4. 5.]]
655
+
656
+ *NumPy* style filtering:
657
+
658
+ >>> atom_array = atom_array[atom_array.chain_id == "A"]
659
+ >>> print(atom_array.array_length())
660
+ 2
661
+
662
+ Inserting an atom array:
663
+
664
+ >>> insert = array([Atom([7,8,9], chain_id="C")])
665
+ >>> atom_array = atom_array[0:1] + insert + atom_array[1:2]
666
+ >>> print(atom_array.chain_id)
667
+ ['A' 'C' 'A']
668
+ """
669
+
670
+ def __init__(self, length):
671
+ super().__init__(length)
672
+ if length is None:
673
+ self._coord = None
674
+ else:
675
+ self._coord = np.full((length, 3), np.nan, dtype=np.float32)
676
+
677
+ def __repr__(self):
678
+ """Represent AtomArray as a string for debugging."""
679
+ atoms = ''
680
+ for i in range(0, self.array_length()):
681
+ if len(atoms) == 0:
682
+ atoms = '\n\t' + self.get_atom(i).__repr__()
683
+ else:
684
+ atoms = atoms + ',\n\t' + self.get_atom(i).__repr__()
685
+ return f'array([{atoms}\n])'
686
+
687
+ @property
688
+ def shape(self):
689
+ """
690
+ Tuple of array dimensions.
691
+
692
+ This property contains the current shape of the
693
+ :class:`AtomArray`.
694
+
695
+ Returns
696
+ -------
697
+ shape : tuple of int
698
+ Shape of the array.
699
+ The single value in the tuple is
700
+ the :func:`array_length()`.
701
+
702
+ See Also
703
+ --------
704
+ array_length
705
+ """
706
+ return self.array_length(),
707
+
708
+ def get_atom(self, index):
709
+ """
710
+ Obtain the atom instance of the array at the specified index.
711
+
712
+ The same as ``array[index]``, if `index` is an integer.
713
+
714
+ Parameters
715
+ ----------
716
+ index : int
717
+ Index of the atom.
718
+
719
+ Returns
720
+ -------
721
+ atom : Atom
722
+ Atom at position `index`.
723
+ """
724
+ kwargs = {}
725
+ for name, annotation in self._annot.items():
726
+ kwargs[name] = annotation[index]
727
+ return Atom(coord = self._coord[index], kwargs=kwargs)
728
+
729
+ def __iter__(self):
730
+ """
731
+ Iterate through the array.
732
+
733
+ Yields
734
+ ------
735
+ atom : Atom
736
+ """
737
+ i = 0
738
+ while i < len(self):
739
+ yield self.get_atom(i)
740
+ i += 1
741
+
742
+ def __getitem__(self, index):
743
+ """
744
+ Obtain a subarray or the atom instance at the specified index.
745
+
746
+ Parameters
747
+ ----------
748
+ index : object
749
+ All index types *NumPy* accepts, are valid.
750
+
751
+ Returns
752
+ -------
753
+ sub_array : Atom or AtomArray
754
+ If `index` is an integer an :class:`Atom` instance is
755
+ returned.
756
+ Otherwise an :class:`AtomArray` with reduced length is
757
+ returned.
758
+ """
759
+ if isinstance(index, numbers.Integral):
760
+ return self.get_atom(index)
761
+ elif isinstance(index, tuple):
762
+ if len(index) == 2 and index[0] is Ellipsis:
763
+ # If first index is "...", just ignore the first index
764
+ return self.__getitem__(index[1])
765
+ else:
766
+ raise IndexError(
767
+ "'AtomArray' does not accept multidimensional indices"
768
+ )
769
+ else:
770
+ return self._subarray(index)
771
+
772
+ def __setitem__(self, index, atom):
773
+ """
774
+ Set the atom at the specified array position.
775
+
776
+ Parameters
777
+ ----------
778
+ index : int
779
+ The position, where the atom is set.
780
+ atom : Atom
781
+ The atom to be set.
782
+ """
783
+ self._set_element(index, atom)
784
+
785
+ def __delitem__(self, index):
786
+ """
787
+ Deletes the atom at the specified array position.
788
+
789
+ Parameters
790
+ ----------
791
+ index : int
792
+ The position where the atom should be deleted.
793
+ """
794
+ self._del_element(index)
795
+
796
+ def __len__(self):
797
+ """
798
+ The length of the array.
799
+
800
+ Returns
801
+ -------
802
+ length : int
803
+ Length of the array.
804
+ """
805
+ return self.array_length()
806
+
807
+ def __eq__(self, item):
808
+ """
809
+ Check if the array equals another :class:`AtomArray`.
810
+
811
+ Parameters
812
+ ----------
813
+ item : object
814
+ Object to campare the array with.
815
+
816
+ Returns
817
+ -------
818
+ equal : bool
819
+ True, if `item` is an :class:`AtomArray`
820
+ and all its attribute arrays equals the ones of this object.
821
+ """
822
+ if not super().__eq__(item):
823
+ return False
824
+ if not isinstance(item, AtomArray):
825
+ return False
826
+ return True
827
+
828
+ def __str__(self):
829
+ """
830
+ Get a string representation of the array.
831
+
832
+ Each line contains the attributes of one atom.
833
+ """
834
+ return "\n".join([str(atom) for atom in self])
835
+
836
+ def __copy_create__(self):
837
+ return AtomArray(self.array_length())
838
+
839
+
840
+ class AtomArrayStack(_AtomArrayBase):
841
+ """
842
+ A collection of multiple :class:`AtomArray` instances, where each
843
+ atom array has equal annotation arrays.
844
+
845
+ Effectively, this means that each atom is occuring in every array in
846
+ the stack at differing coordinates. This situation arises e.g. in
847
+ NMR-elucidated or simulated structures. Since the annotations are
848
+ equal for each array, the annotation arrays are 1-D, while the
849
+ coordinate array is 3-D (m x n x 3).
850
+ A detailed description of each annotation category can be viewed
851
+ :doc:`here </apidoc/biotite.structure>`.
852
+
853
+ Indexing works similar to :class:`AtomArray`, with the difference,
854
+ that two index dimensions are possible:
855
+ The first index dimension specifies the array(s), the second index
856
+ dimension specifies the atoms in each array (same as the index
857
+ in :class:`AtomArray`).
858
+ Using a single integer as first dimension index returns a single
859
+ :class:`AtomArray` instance.
860
+
861
+ Concatenation of atoms for each array in the stack is done using the
862
+ '+' operator. For addition of atom arrays onto the stack use the
863
+ :func:`stack()` method.
864
+
865
+ The :attr:`box` attribute has the shape *m x 3 x 3*, as the cell
866
+ might be different for each frame in the atom array stack.
867
+
868
+ Parameters
869
+ ----------
870
+ depth : int
871
+ The fixed amount of arrays in the stack. When indexing, this is
872
+ the length of the first dimension.
873
+
874
+ length : int
875
+ The fixed amount of atoms in each array in the stack. When
876
+ indexing, this is the length of the second dimension.
877
+
878
+ Attributes
879
+ ----------
880
+ {annot} : ndarray, shape=(n,)
881
+ Mutliple n-length annotation arrays.
882
+ coord : ndarray, dtype=float, shape=(m,n,3)
883
+ ndarray containing the x, y and z coordinate of the
884
+ atoms.
885
+ bonds: BondList or None
886
+ A :class:`BondList`, specifying the indices of atoms
887
+ that form a chemical bond.
888
+ box: ndarray, dtype=float, shape=(m,3,3) or None
889
+ The surrounding box. May represent a MD simulation box
890
+ or a crystallographic unit cell.
891
+ shape : tuple of int
892
+ Shape of the stack.
893
+ The numbers correspond to the stack depth
894
+ and array length, respectively.
895
+
896
+ See also
897
+ --------
898
+ AtomArray
899
+
900
+ Examples
901
+ --------
902
+ Creating an atom array stack from two arrays:
903
+
904
+ >>> atom1 = Atom([1,2,3], chain_id="A")
905
+ >>> atom2 = Atom([2,3,4], chain_id="A")
906
+ >>> atom3 = Atom([3,4,5], chain_id="B")
907
+ >>> atom_array1 = array([atom1, atom2, atom3])
908
+ >>> print(atom_array1.coord)
909
+ [[1. 2. 3.]
910
+ [2. 3. 4.]
911
+ [3. 4. 5.]]
912
+ >>> atom_array2 = atom_array1.copy()
913
+ >>> atom_array2.coord += 3
914
+ >>> print(atom_array2.coord)
915
+ [[4. 5. 6.]
916
+ [5. 6. 7.]
917
+ [6. 7. 8.]]
918
+ >>> array_stack = stack([atom_array1, atom_array2])
919
+ >>> print(array_stack.coord)
920
+ [[[1. 2. 3.]
921
+ [2. 3. 4.]
922
+ [3. 4. 5.]]
923
+ <BLANKLINE>
924
+ [[4. 5. 6.]
925
+ [5. 6. 7.]
926
+ [6. 7. 8.]]]
927
+ """
928
+
929
+ def __init__(self, depth, length):
930
+ super().__init__(length)
931
+ if depth == None or length == None:
932
+ self._coord = None
933
+ else:
934
+ self._coord = np.full((depth, length, 3), np.nan, dtype=np.float32)
935
+
936
+ def __repr__(self):
937
+ """Represent AtomArrayStack as a string for debugging."""
938
+ arrays = ''
939
+ for i in range(0, self.stack_depth()):
940
+ if len(arrays) == 0:
941
+ arrays = '\n\t' + self.get_array(i).__repr__()
942
+ else:
943
+ arrays = arrays + ',\n\t' + self.get_array(i).__repr__()
944
+ return f'stack([{arrays}\n])'
945
+
946
+ def get_array(self, index):
947
+ """
948
+ Obtain the atom array instance of the stack at the specified
949
+ index.
950
+
951
+ The same as ``stack[index]``, if `index` is an integer.
952
+
953
+ Parameters
954
+ ----------
955
+ index : int
956
+ Index of the atom array.
957
+
958
+ Returns
959
+ -------
960
+ array : AtomArray
961
+ AtomArray at position `index`.
962
+ """
963
+ array = AtomArray(self.array_length())
964
+ for name in self._annot:
965
+ array._annot[name] = self._annot[name]
966
+ array._coord = self._coord[index]
967
+ if self._bonds is not None:
968
+ array._bonds = self._bonds.copy()
969
+ if self._box is not None:
970
+ array._box = self._box[index]
971
+
972
+ return array
973
+
974
+ def stack_depth(self):
975
+ """
976
+ Get the depth of the stack.
977
+
978
+ This value represents the amount of atom arrays in the stack.
979
+ It is the same as ``len(array)``.
980
+
981
+ Returns
982
+ -------
983
+ length : int
984
+ Length of the array(s).
985
+ """
986
+ return len(self)
987
+
988
+ @property
989
+ def shape(self):
990
+ """
991
+ Tuple of array dimensions.
992
+
993
+ This property contains the current shape of the
994
+ :class:`AtomArrayStack`.
995
+
996
+ Returns
997
+ -------
998
+ shape : tuple of int
999
+ Shape of the stack.
1000
+ The numbers correspond to the :func:`stack_depth()`
1001
+ and :func:`array_length()`, respectively.
1002
+ """
1003
+ return self.stack_depth(), self.array_length()
1004
+
1005
+ def __iter__(self):
1006
+ """
1007
+ Iterate through the array.
1008
+
1009
+ Yields
1010
+ ------
1011
+ array : AtomArray
1012
+ """
1013
+ i = 0
1014
+ while i < len(self):
1015
+ yield self.get_array(i)
1016
+ i += 1
1017
+
1018
+ def __getitem__(self, index):
1019
+ """
1020
+ Obtain the atom array instance or an substack at the specified
1021
+ index.
1022
+
1023
+ Parameters
1024
+ ----------
1025
+ index : object
1026
+ All index types *NumPy* accepts are valid.
1027
+
1028
+ Returns
1029
+ -------
1030
+ sub_array : AtomArray or AtomArrayStack
1031
+ If `index` is an integer an :class:`AtomArray` instance is
1032
+ returned.
1033
+ Otherwise an :class:`AtomArrayStack` with reduced depth and
1034
+ length is returned.
1035
+ In case the index is a tuple(int, int) an :class:`Atom`
1036
+ instance is returned.
1037
+ """
1038
+ if isinstance(index, numbers.Integral):
1039
+ return self.get_array(index)
1040
+ elif isinstance(index, tuple):
1041
+ if len(index) != 2:
1042
+ raise IndexError(
1043
+ "'AtomArrayStack' does not accept an index "
1044
+ "with more than two dimensions"
1045
+ )
1046
+ if isinstance(index[0], numbers.Integral):
1047
+ array = self.get_array(index[0])
1048
+ return array.__getitem__(index[1])
1049
+ else:
1050
+ if isinstance(index[1], numbers.Integral):
1051
+ # Prevent reduction in dimensionality
1052
+ # in second dimension
1053
+ new_stack = self._subarray(slice(index[1], index[1]+1))
1054
+ else:
1055
+ new_stack = self._subarray(index[1])
1056
+ if index[0] is not Ellipsis:
1057
+ new_stack._coord = new_stack._coord[index[0]]
1058
+ if new_stack._box is not None:
1059
+ new_stack._box = new_stack._box[index[0]]
1060
+ return new_stack
1061
+ else:
1062
+ new_stack = AtomArrayStack(depth=0, length=self.array_length())
1063
+ self._copy_annotations(new_stack)
1064
+ new_stack._coord = self._coord[index]
1065
+ if self._box is not None:
1066
+ new_stack._box = self._box[index]
1067
+ return new_stack
1068
+
1069
+
1070
+ def __setitem__(self, index, array):
1071
+ """
1072
+ Set the atom array at the specified stack position.
1073
+
1074
+ The array and the stack must have equal annotation arrays.
1075
+
1076
+ Parameters
1077
+ ----------
1078
+ index : int
1079
+ The position, where the array atom is set.
1080
+ array : AtomArray
1081
+ The atom array to be set.
1082
+ """
1083
+ if not self.equal_annotations(array):
1084
+ raise ValueError(
1085
+ "The stack and the array have unequal annotations"
1086
+ )
1087
+ if self.bonds != array.bonds:
1088
+ raise ValueError(
1089
+ "The stack and the array have unequal bonds"
1090
+ )
1091
+ if isinstance(index, numbers.Integral):
1092
+ self.coord[index] = array.coord
1093
+ if self.box is not None:
1094
+ self.box[index] = array.box
1095
+ else:
1096
+ raise TypeError(
1097
+ f"Index must be integer, not '{type(index).__name__}'"
1098
+ )
1099
+
1100
+ def __delitem__(self, index):
1101
+ """
1102
+ Deletes the atom array at the specified stack position.
1103
+
1104
+ Parameters
1105
+ ----------
1106
+ index : int
1107
+ The position where the atom array should be deleted.
1108
+ """
1109
+ if isinstance(index, numbers.Integral):
1110
+ self._coord = np.delete(self._coord, index, axis=0)
1111
+ else:
1112
+ raise TypeError(
1113
+ f"Index must be integer, not '{type(index).__name__}'"
1114
+ )
1115
+
1116
+ def __len__(self):
1117
+ """
1118
+ The depth of the stack, i.e. the amount of models.
1119
+
1120
+ Returns
1121
+ -------
1122
+ depth : int
1123
+ depth of the array.
1124
+ """
1125
+ # length is determined by length of coord attribute
1126
+ return self._coord.shape[0]
1127
+
1128
+ def __eq__(self, item):
1129
+ """
1130
+ Check if the array equals another :class:`AtomArray`
1131
+
1132
+ Parameters
1133
+ ----------
1134
+ item : object
1135
+ Object to campare the array with.
1136
+
1137
+ Returns
1138
+ -------
1139
+ equal : bool
1140
+ True, if `item` is an :class:`AtomArray`
1141
+ and all its attribute arrays equals the ones of this object.
1142
+ """
1143
+ if not super().__eq__(item):
1144
+ return False
1145
+ if not isinstance(item, AtomArrayStack):
1146
+ return False
1147
+ return True
1148
+
1149
+ def __str__(self):
1150
+ """
1151
+ Get a string representation of the stack.
1152
+
1153
+ :class:`AtomArray` strings eparated by blank lines
1154
+ and a line indicating the index.
1155
+ """
1156
+ string = ""
1157
+ for i, array in enumerate(self):
1158
+ string += "Model " + str(i+1) + "\n"
1159
+ string += str(array) + "\n" + "\n"
1160
+ return string
1161
+
1162
+ def __copy_create__(self):
1163
+ return AtomArrayStack(self.stack_depth(), self.array_length())
1164
+
1165
+
1166
+ def array(atoms):
1167
+ """
1168
+ Create an :class:`AtomArray` from a list of :class:`Atom`.
1169
+
1170
+ Parameters
1171
+ ----------
1172
+ atoms : iterable object of Atom
1173
+ The atoms to be combined in an array.
1174
+ All atoms must share the same annotation categories.
1175
+
1176
+ Returns
1177
+ -------
1178
+ array : AtomArray
1179
+ The listed atoms as array.
1180
+
1181
+ Examples
1182
+ --------
1183
+
1184
+ Creating an atom array from atoms:
1185
+
1186
+ >>> atom1 = Atom([1,2,3], chain_id="A")
1187
+ >>> atom2 = Atom([2,3,4], chain_id="A")
1188
+ >>> atom3 = Atom([3,4,5], chain_id="B")
1189
+ >>> atom_array = array([atom1, atom2, atom3])
1190
+ >>> print(atom_array)
1191
+ A 0 1.000 2.000 3.000
1192
+ A 0 2.000 3.000 4.000
1193
+ B 0 3.000 4.000 5.000
1194
+ """
1195
+ # Check if all atoms have the same annotation names
1196
+ # Equality check requires sorting
1197
+ names = sorted(atoms[0]._annot.keys())
1198
+ for i, atom in enumerate(atoms):
1199
+ if sorted(atom._annot.keys()) != names:
1200
+ raise ValueError(
1201
+ f"The atom at index {i} does not share the same "
1202
+ f"annotation categories as the atom at index 0"
1203
+ )
1204
+ array = AtomArray(len(atoms))
1205
+ # Add all (also optional) annotation categories
1206
+ for name in names:
1207
+ array.add_annotation(name, dtype=type(atoms[0]._annot[name]))
1208
+ # Add all atoms to AtomArray
1209
+ for i in range(len(atoms)):
1210
+ for name in names:
1211
+ array._annot[name][i] = atoms[i]._annot[name]
1212
+ array._coord[i] = atoms[i].coord
1213
+ return array
1214
+
1215
+
1216
+ def stack(arrays):
1217
+ """
1218
+ Create an :class:`AtomArrayStack` from a list of :class:`AtomArray`.
1219
+
1220
+ Parameters
1221
+ ----------
1222
+ arrays : iterable object of AtomArray
1223
+ The atom arrays to be combined in a stack.
1224
+ All atom arrays must have an equal number of atoms and equal
1225
+ annotation arrays.
1226
+
1227
+ Returns
1228
+ -------
1229
+ stack : AtomArrayStack
1230
+ The stacked atom arrays.
1231
+
1232
+ Examples
1233
+ --------
1234
+ Creating an atom array stack from two arrays:
1235
+
1236
+ >>> atom1 = Atom([1,2,3], chain_id="A")
1237
+ >>> atom2 = Atom([2,3,4], chain_id="A")
1238
+ >>> atom3 = Atom([3,4,5], chain_id="B")
1239
+ >>> atom_array1 = array([atom1, atom2, atom3])
1240
+ >>> print(atom_array1.coord)
1241
+ [[1. 2. 3.]
1242
+ [2. 3. 4.]
1243
+ [3. 4. 5.]]
1244
+ >>> atom_array2 = atom_array1.copy()
1245
+ >>> atom_array2.coord += 3
1246
+ >>> print(atom_array2.coord)
1247
+ [[4. 5. 6.]
1248
+ [5. 6. 7.]
1249
+ [6. 7. 8.]]
1250
+ >>> array_stack = stack([atom_array1, atom_array2])
1251
+ >>> print(array_stack.coord)
1252
+ [[[1. 2. 3.]
1253
+ [2. 3. 4.]
1254
+ [3. 4. 5.]]
1255
+ <BLANKLINE>
1256
+ [[4. 5. 6.]
1257
+ [5. 6. 7.]
1258
+ [6. 7. 8.]]]
1259
+ """
1260
+ array_count = 0
1261
+ ref_array = None
1262
+ for i, array in enumerate(arrays):
1263
+ if ref_array is None:
1264
+ ref_array = array
1265
+ array_count += 1
1266
+ # Check if all arrays share equal annotations
1267
+ if not array.equal_annotations(ref_array):
1268
+ raise ValueError(
1269
+ f"The annotations of the atom array at index {i} are not "
1270
+ f"equal to the annotations of the atom array at index 0"
1271
+ )
1272
+ array_stack = AtomArrayStack(array_count, ref_array.array_length())
1273
+ for name, annotation in ref_array._annot.items():
1274
+ array_stack._annot[name] = annotation
1275
+ coord_list = [array._coord for array in arrays]
1276
+ array_stack._coord = np.stack(coord_list, axis=0)
1277
+ # Take bond list from first array
1278
+ array_stack._bonds = ref_array._bonds
1279
+ # When all atom arrays provide a box, copy the boxes
1280
+ if all([array.box is not None for array in arrays]):
1281
+ array_stack.box = np.array([array.box for array in arrays])
1282
+ return array_stack
1283
+
1284
+
1285
+ def repeat(atoms, coord):
1286
+ """
1287
+ Repeat atoms (:class:`AtomArray` or :class:`AtomArrayStack`)
1288
+ multiple times in the same model with different coordinates.
1289
+
1290
+ Parameters
1291
+ ----------
1292
+ atoms : AtomArray, shape=(n,) or AtomArrayStack, shape=(m,n)
1293
+ The atoms to be repeated.
1294
+ coord : ndarray, dtype=float, shape=(k,n,3) or shape=(k,m,n,3)
1295
+ The coordinates to be used for the repeated atoms.
1296
+ The length of first dimension determines the number of repeats.
1297
+ If `atoms` is an :class:`AtomArray` 3 dimensions, otherwise
1298
+ 4 dimensions are required.
1299
+
1300
+ Returns
1301
+ -------
1302
+ repeated: AtomArray, shape=(n*k,) or AtomArrayStack, shape=(m,n*k)
1303
+ The repeated atoms.
1304
+ Whether an :class:`AtomArray` or an :class:`AtomArrayStack` is
1305
+ returned depends on the input `atoms`.
1306
+
1307
+ Examples
1308
+ --------
1309
+
1310
+ >>> atoms = array([
1311
+ ... Atom([1,2,3], res_id=1, atom_name="N"),
1312
+ ... Atom([4,5,6], res_id=1, atom_name="CA"),
1313
+ ... Atom([7,8,9], res_id=1, atom_name="C")
1314
+ ... ])
1315
+ >>> print(atoms)
1316
+ 1 N 1.000 2.000 3.000
1317
+ 1 CA 4.000 5.000 6.000
1318
+ 1 C 7.000 8.000 9.000
1319
+ >>> repeat_coord = np.array([
1320
+ ... [[0,0,0], [1,1,1], [2,2,2]],
1321
+ ... [[3,3,3], [4,4,4], [5,5,5]]
1322
+ ... ])
1323
+ >>> print(repeat(atoms, repeat_coord))
1324
+ 1 N 0.000 0.000 0.000
1325
+ 1 CA 1.000 1.000 1.000
1326
+ 1 C 2.000 2.000 2.000
1327
+ 1 N 3.000 3.000 3.000
1328
+ 1 CA 4.000 4.000 4.000
1329
+ 1 C 5.000 5.000 5.000
1330
+ """
1331
+ if isinstance(atoms, AtomArray) and coord.ndim != 3:
1332
+ raise ValueError(
1333
+ f"Expected 3 dimensions for the coordinate array, got {coord.ndim}"
1334
+ )
1335
+ elif isinstance(atoms, AtomArrayStack) and coord.ndim != 4:
1336
+ raise ValueError(
1337
+ f"Expected 4 dimensions for the coordinate array, got {coord.ndim}"
1338
+ )
1339
+
1340
+ repetitions = len(coord)
1341
+ orig_length = atoms.array_length()
1342
+ new_length = orig_length * repetitions
1343
+
1344
+ if isinstance(atoms, AtomArray):
1345
+ if coord.ndim != 3:
1346
+ raise ValueError(
1347
+ f"Expected 3 dimensions for the coordinate array, "
1348
+ f"but got {coord.ndim}"
1349
+ )
1350
+ repeated = AtomArray(new_length)
1351
+ repeated.coord = coord.reshape((new_length, 3))
1352
+
1353
+ elif isinstance(atoms, AtomArrayStack):
1354
+ if coord.ndim != 4:
1355
+ raise ValueError(
1356
+ f"Expected 4 dimensions for the coordinate array, "
1357
+ f"but got {coord.ndim}"
1358
+ )
1359
+ repeated = AtomArrayStack(atoms.stack_depth(), new_length)
1360
+ repeated.coord = coord.reshape((atoms.stack_depth(), new_length, 3))
1361
+
1362
+ else:
1363
+ raise TypeError(
1364
+ f"Expected 'AtomArray' or 'AtomArrayStack', "
1365
+ f"but got {type(atoms).__name__}"
1366
+ )
1367
+
1368
+ for category in atoms.get_annotation_categories():
1369
+ annot = np.tile(atoms.get_annotation(category), repetitions)
1370
+ repeated.set_annotation(category, annot)
1371
+ if atoms.bonds is not None:
1372
+ repeated_bonds = atoms.bonds.copy()
1373
+ for _ in range(repetitions-1):
1374
+ repeated_bonds += atoms.bonds
1375
+ repeated.bonds = repeated_bonds
1376
+ if atoms.box is not None:
1377
+ repeated.box = atoms.box.copy()
1378
+
1379
+ return repeated
1380
+
1381
+
1382
+ def from_template(template, coord, box=None):
1383
+ """
1384
+ Create an :class:`AtomArrayStack` using template atoms and given
1385
+ coordinates.
1386
+
1387
+ Parameters
1388
+ ----------
1389
+ template : AtomArray, shape=(n,) or AtomArrayStack, shape=(m,n)
1390
+ The annotation arrays and bonds of the returned stack are taken
1391
+ from this template.
1392
+ coord : ndarray, dtype=float, shape=(l,n,3)
1393
+ The coordinates for each model of the returned stack.
1394
+ box : ndarray, optional, dtype=float, shape=(l,3,3)
1395
+ The box for each model of the returned stack.
1396
+
1397
+ Returns
1398
+ -------
1399
+ array_stack : AtomArrayStack
1400
+ A stack containing the annotation arrays and bonds from
1401
+ `template` but the coordinates from `coord` and the boxes from
1402
+ `boxes`.
1403
+ """
1404
+ if template.array_length() != coord.shape[-2]:
1405
+ raise ValueError(
1406
+ f"Template has {template.array_length()} atoms, but "
1407
+ f"{coord.shape[-2]} coordinates are given"
1408
+ )
1409
+
1410
+ # Create empty stack with no models
1411
+ new_stack = AtomArrayStack(0, template.array_length())
1412
+
1413
+ for category in template.get_annotation_categories():
1414
+ annot = template.get_annotation(category)
1415
+ new_stack.set_annotation(category, annot)
1416
+ if template.bonds is not None:
1417
+ new_stack.bonds = template.bonds.copy()
1418
+ if box is not None:
1419
+ new_stack.box = box.copy()
1420
+
1421
+ # After setting the coordinates the number of models is the number
1422
+ # of models in the new coordinates
1423
+ new_stack.coord = coord
1424
+
1425
+ return new_stack
1426
+
1427
+
1428
+ def coord(item):
1429
+ """
1430
+ Get the atom coordinates of the given array.
1431
+
1432
+ This may be directly and :class:`Atom`, :class:`AtomArray` or
1433
+ :class:`AtomArrayStack` or
1434
+ alternatively an (n x 3) or (m x n x 3) :class:`ndarray`
1435
+ containing the coordinates.
1436
+
1437
+ Parameters
1438
+ ----------
1439
+ item : Atom or AtomArray or AtomArrayStack or ndarray
1440
+ Returns the :attr:`coord` attribute, if `item` is an
1441
+ :class:`Atom`, :class:`AtomArray` or :class:`AtomArrayStack`.
1442
+ Directly returns the input, if `item` is a :class:`ndarray`.
1443
+
1444
+ Returns
1445
+ -------
1446
+ coord : ndarray
1447
+ Atom coordinates.
1448
+ """
1449
+
1450
+ if type(item) in (Atom, AtomArray, AtomArrayStack):
1451
+ return item.coord
1452
+ elif isinstance(item, np.ndarray):
1453
+ return item.astype(np.float32, copy=False)
1454
+ else:
1455
+ return np.array(item, dtype=np.float32)