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