biotite 1.1.0__cp311-cp311-macosx_11_0_arm64.whl → 1.3.0__cp311-cp311-macosx_11_0_arm64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


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

Files changed (160) hide show
  1. biotite/application/application.py +3 -3
  2. biotite/application/autodock/app.py +1 -1
  3. biotite/application/blast/webapp.py +1 -1
  4. biotite/application/clustalo/app.py +1 -1
  5. biotite/application/localapp.py +2 -2
  6. biotite/application/msaapp.py +10 -10
  7. biotite/application/muscle/app3.py +3 -3
  8. biotite/application/muscle/app5.py +3 -3
  9. biotite/application/sra/app.py +0 -5
  10. biotite/application/util.py +21 -1
  11. biotite/application/viennarna/rnaalifold.py +8 -8
  12. biotite/application/viennarna/rnaplot.py +10 -8
  13. biotite/application/viennarna/util.py +1 -1
  14. biotite/application/webapp.py +1 -1
  15. biotite/database/afdb/__init__.py +12 -0
  16. biotite/database/afdb/download.py +191 -0
  17. biotite/database/entrez/dbnames.py +10 -0
  18. biotite/database/entrez/download.py +9 -10
  19. biotite/database/entrez/key.py +1 -1
  20. biotite/database/entrez/query.py +5 -4
  21. biotite/database/pubchem/download.py +6 -6
  22. biotite/database/pubchem/error.py +10 -0
  23. biotite/database/pubchem/query.py +12 -23
  24. biotite/database/rcsb/download.py +3 -2
  25. biotite/database/rcsb/query.py +2 -3
  26. biotite/database/uniprot/check.py +2 -2
  27. biotite/database/uniprot/download.py +2 -5
  28. biotite/database/uniprot/query.py +3 -4
  29. biotite/file.py +14 -2
  30. biotite/interface/__init__.py +19 -0
  31. biotite/interface/openmm/__init__.py +20 -0
  32. biotite/interface/openmm/state.py +93 -0
  33. biotite/interface/openmm/system.py +227 -0
  34. biotite/interface/pymol/__init__.py +201 -0
  35. biotite/interface/pymol/cgo.py +346 -0
  36. biotite/interface/pymol/convert.py +185 -0
  37. biotite/interface/pymol/display.py +267 -0
  38. biotite/interface/pymol/object.py +1226 -0
  39. biotite/interface/pymol/shapes.py +178 -0
  40. biotite/interface/pymol/startup.py +169 -0
  41. biotite/interface/rdkit/__init__.py +19 -0
  42. biotite/interface/rdkit/mol.py +490 -0
  43. biotite/interface/version.py +94 -0
  44. biotite/interface/warning.py +19 -0
  45. biotite/sequence/align/__init__.py +0 -4
  46. biotite/sequence/align/alignment.py +33 -11
  47. biotite/sequence/align/banded.cpython-311-darwin.so +0 -0
  48. biotite/sequence/align/banded.pyx +22 -22
  49. biotite/sequence/align/cigar.py +2 -2
  50. biotite/sequence/align/kmeralphabet.cpython-311-darwin.so +0 -0
  51. biotite/sequence/align/kmeralphabet.pyx +2 -2
  52. biotite/sequence/align/kmersimilarity.cpython-311-darwin.so +0 -0
  53. biotite/sequence/align/kmertable.cpython-311-darwin.so +0 -0
  54. biotite/sequence/align/kmertable.pyx +6 -6
  55. biotite/sequence/align/localgapped.cpython-311-darwin.so +0 -0
  56. biotite/sequence/align/localgapped.pyx +47 -47
  57. biotite/sequence/align/localungapped.cpython-311-darwin.so +0 -0
  58. biotite/sequence/align/localungapped.pyx +10 -10
  59. biotite/sequence/align/matrix.py +12 -3
  60. biotite/sequence/align/multiple.cpython-311-darwin.so +0 -0
  61. biotite/sequence/align/multiple.pyx +1 -2
  62. biotite/sequence/align/pairwise.cpython-311-darwin.so +0 -0
  63. biotite/sequence/align/pairwise.pyx +37 -39
  64. biotite/sequence/align/permutation.cpython-311-darwin.so +0 -0
  65. biotite/sequence/align/selector.cpython-311-darwin.so +0 -0
  66. biotite/sequence/align/selector.pyx +2 -2
  67. biotite/sequence/align/statistics.py +1 -1
  68. biotite/sequence/align/tracetable.cpython-311-darwin.so +0 -0
  69. biotite/sequence/alphabet.py +2 -2
  70. biotite/sequence/annotation.py +19 -13
  71. biotite/sequence/codec.cpython-311-darwin.so +0 -0
  72. biotite/sequence/codon.py +1 -2
  73. biotite/sequence/graphics/alignment.py +25 -39
  74. biotite/sequence/graphics/dendrogram.py +4 -2
  75. biotite/sequence/graphics/features.py +2 -2
  76. biotite/sequence/graphics/logo.py +10 -12
  77. biotite/sequence/io/fasta/convert.py +1 -2
  78. biotite/sequence/io/fasta/file.py +1 -1
  79. biotite/sequence/io/fastq/file.py +3 -3
  80. biotite/sequence/io/genbank/file.py +3 -3
  81. biotite/sequence/io/genbank/sequence.py +2 -0
  82. biotite/sequence/io/gff/convert.py +1 -1
  83. biotite/sequence/io/gff/file.py +1 -2
  84. biotite/sequence/phylo/nj.cpython-311-darwin.so +0 -0
  85. biotite/sequence/phylo/tree.cpython-311-darwin.so +0 -0
  86. biotite/sequence/phylo/upgma.cpython-311-darwin.so +0 -0
  87. biotite/sequence/profile.py +19 -25
  88. biotite/sequence/search.py +0 -1
  89. biotite/sequence/seqtypes.py +12 -5
  90. biotite/sequence/sequence.py +1 -2
  91. biotite/structure/__init__.py +2 -0
  92. biotite/structure/alphabet/i3d.py +1 -2
  93. biotite/structure/alphabet/pb.py +1 -2
  94. biotite/structure/alphabet/unkerasify.py +8 -2
  95. biotite/structure/atoms.py +35 -27
  96. biotite/structure/basepairs.py +39 -40
  97. biotite/structure/bonds.cpython-311-darwin.so +0 -0
  98. biotite/structure/bonds.pyx +8 -5
  99. biotite/structure/box.py +159 -23
  100. biotite/structure/celllist.cpython-311-darwin.so +0 -0
  101. biotite/structure/celllist.pyx +83 -68
  102. biotite/structure/chains.py +17 -55
  103. biotite/structure/charges.cpython-311-darwin.so +0 -0
  104. biotite/structure/compare.py +420 -13
  105. biotite/structure/density.py +1 -1
  106. biotite/structure/dotbracket.py +31 -32
  107. biotite/structure/filter.py +8 -8
  108. biotite/structure/geometry.py +15 -15
  109. biotite/structure/graphics/rna.py +19 -16
  110. biotite/structure/hbond.py +18 -21
  111. biotite/structure/info/atoms.py +11 -2
  112. biotite/structure/info/ccd.py +0 -2
  113. biotite/structure/info/components.bcif +0 -0
  114. biotite/structure/info/groups.py +0 -3
  115. biotite/structure/info/misc.py +0 -1
  116. biotite/structure/info/radii.py +92 -22
  117. biotite/structure/info/standardize.py +1 -2
  118. biotite/structure/integrity.py +4 -6
  119. biotite/structure/io/general.py +2 -2
  120. biotite/structure/io/gro/file.py +8 -9
  121. biotite/structure/io/mol/convert.py +1 -1
  122. biotite/structure/io/mol/ctab.py +33 -28
  123. biotite/structure/io/mol/mol.py +1 -1
  124. biotite/structure/io/mol/sdf.py +39 -13
  125. biotite/structure/io/pdb/convert.py +86 -5
  126. biotite/structure/io/pdb/file.py +90 -24
  127. biotite/structure/io/pdb/hybrid36.cpython-311-darwin.so +0 -0
  128. biotite/structure/io/pdbqt/file.py +4 -4
  129. biotite/structure/io/pdbx/bcif.py +22 -7
  130. biotite/structure/io/pdbx/cif.py +20 -7
  131. biotite/structure/io/pdbx/component.py +6 -0
  132. biotite/structure/io/pdbx/compress.py +71 -34
  133. biotite/structure/io/pdbx/convert.py +429 -77
  134. biotite/structure/io/pdbx/encoding.cpython-311-darwin.so +0 -0
  135. biotite/structure/io/pdbx/encoding.pyx +39 -23
  136. biotite/structure/io/trajfile.py +9 -6
  137. biotite/structure/io/util.py +38 -0
  138. biotite/structure/mechanics.py +0 -1
  139. biotite/structure/molecules.py +0 -15
  140. biotite/structure/pseudoknots.py +13 -19
  141. biotite/structure/repair.py +2 -4
  142. biotite/structure/residues.py +20 -48
  143. biotite/structure/rings.py +335 -0
  144. biotite/structure/sasa.cpython-311-darwin.so +0 -0
  145. biotite/structure/sasa.pyx +30 -30
  146. biotite/structure/segments.py +123 -9
  147. biotite/structure/sequence.py +0 -1
  148. biotite/structure/spacegroups.json +1567 -0
  149. biotite/structure/spacegroups.license +26 -0
  150. biotite/structure/sse.py +0 -2
  151. biotite/structure/superimpose.py +75 -253
  152. biotite/structure/tm.py +581 -0
  153. biotite/structure/transform.py +232 -26
  154. biotite/structure/util.py +3 -3
  155. biotite/version.py +9 -4
  156. biotite/visualize.py +111 -1
  157. {biotite-1.1.0.dist-info → biotite-1.3.0.dist-info}/METADATA +8 -36
  158. {biotite-1.1.0.dist-info → biotite-1.3.0.dist-info}/RECORD +160 -138
  159. {biotite-1.1.0.dist-info → biotite-1.3.0.dist-info}/WHEEL +3 -1
  160. {biotite-1.1.0.dist-info → biotite-1.3.0.dist-info}/licenses/LICENSE.rst +0 -0
@@ -0,0 +1,335 @@
1
+ # This source code is part of the Biotite package and is distributed
2
+ # under the 3-Clause BSD License. Please see 'LICENSE.rst' for further
3
+ # information.
4
+
5
+ """
6
+ This module provides functions related to aromatic rings.
7
+ """
8
+
9
+ __name__ = "biotite.structure"
10
+ __author__ = "Patrick Kunzmann"
11
+ __all__ = ["find_aromatic_rings", "find_stacking_interactions", "PiStacking"]
12
+
13
+
14
+ from enum import IntEnum
15
+ import networkx as nx
16
+ import numpy as np
17
+ from biotite.structure.bonds import BondType
18
+ from biotite.structure.error import BadStructureError
19
+ from biotite.structure.geometry import displacement
20
+ from biotite.structure.util import norm_vector, vector_dot
21
+
22
+
23
+ class PiStacking(IntEnum):
24
+ """
25
+ The type of pi-stacking interaction.
26
+
27
+ - ``PARALLEL``: parallel pi-stacking (also called *staggered* or *Sandwich*)
28
+ - ``PERPENDICULAR``: perpendicular pi-stacking (also called *T-shaped*)
29
+ """
30
+
31
+ PARALLEL = 0
32
+ PERPENDICULAR = 1
33
+
34
+
35
+ def find_aromatic_rings(atoms):
36
+ """
37
+ Find (anti-)aromatic rings in a structure.
38
+
39
+ Parameters
40
+ ----------
41
+ atoms : AtomArray or AtomArrayStack
42
+ The atoms to be searched for aromatic rings.
43
+ Requires an associated :class:`BondList`.
44
+
45
+ Returns
46
+ -------
47
+ rings : list of ndarray
48
+ The indices of the atoms that form aromatic rings.
49
+ Each ring is represented by a list of indices.
50
+ Only rings with minimum size are returned, i.e. two connected rings
51
+ (e.g. in tryptophan) are reported as separate rings.
52
+
53
+ Notes
54
+ -----
55
+ This function does not distinguish between aromatic and antiaromatic rings.
56
+ All cycles containing atoms that are completely connected by aromatic bonds
57
+ are considered aromatic rings.
58
+
59
+ The PDB *Chemical Component Dictionary* (CCD) does not identify aromatic rings in
60
+ all compounds as such.
61
+ Prominent examples are the nucleobases, where the 6-membered rings are not
62
+ flagged as aromatic.
63
+
64
+ Examples
65
+ --------
66
+
67
+ >>> nad = residue("NAD")
68
+ >>> rings = find_aromatic_rings(nad)
69
+ >>> print(rings)
70
+ [array([41, 37, 36, 35, 43, 42]), array([19, 18, 16, 15, 21, 20]), array([12, 13, 14, 15, 21])]
71
+ >>> for atom_indices in rings:
72
+ ... print(np.sort(nad.atom_name[atom_indices]))
73
+ ['C2N' 'C3N' 'C4N' 'C5N' 'C6N' 'N1N']
74
+ ['C2A' 'C4A' 'C5A' 'C6A' 'N1A' 'N3A']
75
+ ['C4A' 'C5A' 'C8A' 'N7A' 'N9A']
76
+ """
77
+ if atoms.bonds is None:
78
+ raise BadStructureError("Structure must have an associated BondList")
79
+ bond_array = atoms.bonds.as_array()
80
+ # To detect aromatic rings, only keep bonds that are aromatic
81
+ aromatic_bond_array = bond_array[
82
+ np.isin(
83
+ bond_array[:, 2],
84
+ [
85
+ BondType.AROMATIC,
86
+ BondType.AROMATIC_SINGLE,
87
+ BondType.AROMATIC_DOUBLE,
88
+ BondType.AROMATIC_TRIPLE,
89
+ ],
90
+ ),
91
+ # We can omit the bond type now
92
+ :2,
93
+ ]
94
+ aromatic_bond_graph = nx.from_edgelist(aromatic_bond_array.tolist())
95
+ # Find the cycles with minimum size -> cycle basis
96
+ rings = nx.cycle_basis(aromatic_bond_graph)
97
+ return [np.array(ring, dtype=int) for ring in rings]
98
+
99
+
100
+ def find_stacking_interactions(
101
+ atoms,
102
+ centroid_cutoff=6.5,
103
+ plane_angle_tol=np.deg2rad(30.0),
104
+ shift_angle_tol=np.deg2rad(30.0),
105
+ ):
106
+ """
107
+ Find pi-stacking interactions between aromatic rings.
108
+
109
+ Parameters
110
+ ----------
111
+ atoms : AtomArray
112
+ The atoms to be searched for aromatic rings.
113
+ Requires an associated :class:`BondList`.
114
+ centroid_cutoff : float
115
+ The cutoff distance for ring centroids.
116
+ plane_angle_tol : float
117
+ The tolerance for the angle between ring planes that must be either
118
+ parallel or perpendicular.
119
+ Given in radians.
120
+ shift_angle_tol : float
121
+ The tolerance for the angle between the ring plane normals and the
122
+ centroid difference vector.
123
+ Given in radians.
124
+
125
+ Returns
126
+ -------
127
+ interactions : list of tuple(ndarray, ndarray, PiStacking)
128
+ The stacking interactions between aromatic rings.
129
+ Each element in the list represents one stacking interaction.
130
+ The first two elements of each tuple represent atom indices of the stacked
131
+ rings.
132
+ The third element of each tuple is the type of stacking interaction.
133
+
134
+ See Also
135
+ --------
136
+ find_aromatic_rings : Used for finding the aromatic rings in this function.
137
+
138
+ Notes
139
+ -----
140
+ This function does not distinguish between aromatic and antiaromatic rings.
141
+ Furthermore, it does not distinguish between repulsive and attractive stacking:
142
+ Usually, stacking two rings directly above each other is repulsive, as the pi
143
+ orbitals above the rings repel each other, so a slight horizontal shift is
144
+ usually required to make the interaction attractive.
145
+ However, in details this is strongly dependent on heteroatoms and the exact
146
+ orientation of the rings.
147
+ Hence, this function aggregates all stacking interactions to simplify the
148
+ conditions for pi-stacking.
149
+
150
+ The conditions for pi-stacking are :footcite:`Wojcikowski2015` :
151
+
152
+ - The ring centroids must be within cutoff `centroid_cutoff` distance.
153
+ While :footcite:`Wojcikowski2015` uses a cutoff of 5.0 Å, 6.5 Å was
154
+ adopted from :footcite:`Bouysset2021` to better identify perpendicular
155
+ stacking interactions.
156
+ - The planes must be parallel or perpendicular to each other within a default
157
+ tolerance of 30°.
158
+ - The angle between the plane normals and the centroid difference vector must be
159
+ be either 0° or 90° within a default tolerance of 30°, to check for lateral
160
+ shifts.
161
+
162
+ References
163
+ ----------
164
+
165
+ .. footbibliography::
166
+
167
+ Examples
168
+ --------
169
+
170
+ Detect base stacking interactions in a DNA helix
171
+
172
+ >>> from os.path import join
173
+ >>> dna_helix = load_structure(
174
+ ... join(path_to_structures, "base_pairs", "1qxb.cif"), include_bonds=True
175
+ ... )
176
+ >>> interactions = find_stacking_interactions(dna_helix)
177
+ >>> for ring_atom_indices_1, ring_atom_indices_2, stacking_type in interactions:
178
+ ... print(
179
+ ... dna_helix.res_id[ring_atom_indices_1[0]],
180
+ ... dna_helix.res_id[ring_atom_indices_2[0]],
181
+ ... PiStacking(stacking_type).name
182
+ ... )
183
+ 17 18 PARALLEL
184
+ 17 18 PARALLEL
185
+ 5 6 PARALLEL
186
+ 5 6 PARALLEL
187
+ 5 6 PARALLEL
188
+ """
189
+ rings = find_aromatic_rings(atoms)
190
+ if len(rings) == 0:
191
+ return []
192
+
193
+ ring_centroids = np.array(
194
+ [atoms.coord[atom_indices].mean(axis=0) for atom_indices in rings]
195
+ )
196
+ ring_normals = np.array(
197
+ [_get_ring_normal(atoms.coord[atom_indices]) for atom_indices in rings]
198
+ )
199
+
200
+ # Create an index array that contains the Cartesian product of all rings
201
+ indices = np.stack(
202
+ [
203
+ np.repeat(np.arange(len(rings)), len(rings)),
204
+ np.tile(np.arange(len(rings)), len(rings)),
205
+ ],
206
+ axis=-1,
207
+ )
208
+ # Do not include duplicate pairs
209
+ indices = indices[indices[:, 0] > indices[:, 1]]
210
+
211
+ ## Condition 1: Ring centroids are close enough to each other
212
+ diff = displacement(ring_centroids[indices[:, 0]], ring_centroids[indices[:, 1]])
213
+ # Use squared distance to avoid time consuming sqrt computation
214
+ sq_distance = vector_dot(diff, diff)
215
+ is_interacting = sq_distance < centroid_cutoff**2
216
+ indices = indices[is_interacting]
217
+
218
+ ## Condition 2: Ring planes are parallel or perpendicular
219
+ plane_angles = _minimum_angle(
220
+ ring_normals[indices[:, 0]], ring_normals[indices[:, 1]]
221
+ )
222
+ is_parallel = _is_within_tolerance(plane_angles, 0, plane_angle_tol)
223
+ is_perpendicular = _is_within_tolerance(plane_angles, np.pi / 2, plane_angle_tol)
224
+ is_interacting = is_parallel | is_perpendicular
225
+ indices = indices[is_interacting]
226
+ # Keep in sync with the shape of the filtered indices,
227
+ # i.e. after filtering, `is_parallel==False` means a perpendicular interaction
228
+ is_parallel = is_parallel[is_interacting]
229
+
230
+ ## Condition 3: The ring centroids are not shifted too much
231
+ ## (in terms of normal-centroid angle)
232
+ diff = displacement(ring_centroids[indices[:, 0]], ring_centroids[indices[:, 1]])
233
+ norm_vector(diff)
234
+ angles = np.stack(
235
+ [_minimum_angle(ring_normals[indices[:, i]], diff) for i in range(2)]
236
+ )
237
+ is_interacting = (
238
+ # For parallel stacking, the lateral shift may not exceed the tolerance
239
+ (is_parallel & np.any(_is_within_tolerance(angles, 0, shift_angle_tol), axis=0))
240
+ # For perpendicular stacking, one ring must be above the other,
241
+ # but from the perspective of the other ring, the first ring is approximately
242
+ # in the same plane
243
+ | (
244
+ ~is_parallel
245
+ & (
246
+ (
247
+ _is_within_tolerance(angles[0], 0, shift_angle_tol)
248
+ & _is_within_tolerance(angles[1], np.pi / 2, shift_angle_tol)
249
+ )
250
+ | (
251
+ _is_within_tolerance(angles[0], np.pi / 2, shift_angle_tol)
252
+ & _is_within_tolerance(angles[1], 0, shift_angle_tol)
253
+ )
254
+ )
255
+ )
256
+ )
257
+ indices = indices[is_interacting]
258
+ is_parallel = is_parallel[is_interacting]
259
+
260
+ # Only return pairs of rings where all conditions were fulfilled
261
+ return [
262
+ (
263
+ rings[ring_i],
264
+ rings[ring_j],
265
+ PiStacking.PARALLEL if is_parallel[i] else PiStacking.PERPENDICULAR,
266
+ )
267
+ for i, (ring_i, ring_j) in enumerate(indices)
268
+ ]
269
+
270
+
271
+ def _get_ring_normal(ring_coord):
272
+ """
273
+ Get the normal vector perpendicular to the ring plane.
274
+
275
+ Parameters
276
+ ----------
277
+ ring_coord : ndarray
278
+ The coordinates of the atoms in the ring.
279
+
280
+ Returns
281
+ -------
282
+ normal : ndarray
283
+ The normal vector of the ring plane.
284
+ """
285
+ # Simply use any three atoms in the ring to calculate the normal vector
286
+ # We can also safely assume that there are at least three atoms in the ring,
287
+ # as otherwise it would not be a ring
288
+ normal = np.cross(ring_coord[1] - ring_coord[0], ring_coord[2] - ring_coord[0])
289
+ norm_vector(normal)
290
+ return normal
291
+
292
+
293
+ def _minimum_angle(v1, v2):
294
+ """
295
+ Get the minimum angle between two vectors, i.e. the possible angle range is
296
+ ``[0, pi/2]``.
297
+
298
+ Parameters
299
+ ----------
300
+ v1, v2 : ndarray, shape=(n,3), dtype=float
301
+ The vectors to measure the angle between.
302
+
303
+ Returns
304
+ -------
305
+ angle : ndarray, shape=(n,), dtype=float
306
+ The minimum angle between the two vectors.
307
+
308
+ Notes
309
+ -----
310
+ This restriction is added here as the normal vectors of the ring planes
311
+ have no 'preferred side'.
312
+ """
313
+ # Do not distinguish between the 'sides' of the rings -> take absolute of cosine
314
+ return np.arccos(np.abs(vector_dot(v1, v2)))
315
+
316
+
317
+ def _is_within_tolerance(angles, expected_angle, tolerance):
318
+ """
319
+ Check if the angles are within a certain tolerance.
320
+
321
+ Parameters
322
+ ----------
323
+ angles : ndarray, shape=x, dtype=float
324
+ The angles to check.
325
+ expected_angle : float
326
+ The expected angle.
327
+ tolerance : float
328
+ The tolerance.
329
+
330
+ Returns
331
+ -------
332
+ is_within_tolerance : ndarray, shape=x, dtype=bool
333
+ True if the angles are within the tolerance, False otherwise.
334
+ """
335
+ return np.abs(angles - expected_angle) < tolerance
@@ -35,39 +35,38 @@ def sasa(array, float probe_radius=1.4, np.ndarray atom_filter=None,
35
35
  point_number=1000, point_distr="Fibonacci", vdw_radii="ProtOr")
36
36
 
37
37
  Calculate the Solvent Accessible Surface Area (SASA) of a protein.
38
-
38
+
39
39
  This function uses the Shrake-Rupley ("rolling probe")
40
40
  algorithm :footcite:`Shrake1973`:
41
41
  Every atom is occupied by a evenly distributed point mesh. The
42
42
  points that can be reached by the "rolling probe", are surface
43
43
  accessible.
44
-
44
+
45
45
  Parameters
46
46
  ----------
47
47
  array : AtomArray
48
48
  The protein model to calculate the SASA for.
49
49
  probe_radius : float, optional
50
- The VdW-radius of the solvent molecules (default: 1.4).
50
+ The VdW-radius of the solvent molecules.
51
51
  atom_filter : ndarray, dtype=bool, optional
52
52
  If this parameter is given, SASA is only calculated for the
53
53
  filtered atoms.
54
54
  ignore_ions : bool, optional
55
- If true, all monoatomic ions are removed before SASA calculation
56
- (default: True).
55
+ If true, all monoatomic ions are removed before SASA calculation.
57
56
  point_number : int, optional
58
57
  The number of points in the mesh occupying each atom for SASA
59
- calculation (default: 100). The SASA calculation time is
60
- proportional to the amount of sphere points.
58
+ calculation.
59
+ The SASA calculation time is proportional to the amount of sphere points.
61
60
  point_distr : str or function, optional
62
61
  If a function is given, the function is used to calculate the
63
62
  point distribution for the mesh (the function must take `float`
64
63
  *n* as parameter and return a *(n x 3)* :class:`ndarray`).
65
64
  Alternatively a string can be given to choose a built-in
66
65
  distribution:
67
-
66
+
68
67
  - **Fibonacci** - Distribute points using a golden section
69
68
  spiral.
70
-
69
+
71
70
  By default *Fibonacci* is used.
72
71
  vdw_radii : str or ndarray, dtype=float, optional
73
72
  Indicates the set of VdW radii to be used. If an `array`-length
@@ -76,33 +75,34 @@ def sasa(array, float probe_radius=1.4, np.ndarray atom_filter=None,
76
75
  SASA calculation (e.g. solvent atoms) can have arbitrary values
77
76
  (e.g. `NaN`). If instead a string is given, one of the
78
77
  built-in sets is used:
79
-
78
+
80
79
  - **ProtOr** - A set, which does not require hydrogen atoms
81
80
  in the model. Suitable for crystal structures.
82
81
  :footcite:`Tsai1999`
83
82
  - **Single** - A set, which uses a defined VdW radius for
84
83
  every single atom, therefore hydrogen atoms are required
85
84
  in the model (e.g. NMR elucidated structures).
86
- :footcite:`Bondi1964`
87
-
85
+ Values for main group elements are taken from :footcite:`Mantina2009`,
86
+ and for relevant transition metals from the :footcite:`RDKit`.
87
+
88
88
  By default *ProtOr* is used.
89
-
90
-
89
+
90
+
91
91
  Returns
92
92
  -------
93
93
  sasa : ndarray, dtype=bool, shape=(n,)
94
- Atom-wise SASA. `NaN` for atoms where SASA has not been
94
+ Atom-wise SASA. `NaN` for atoms where SASA has not been
95
95
  calculated
96
96
  (solvent atoms, hydrogen atoms (ProtOr), atoms not in `filter`).
97
-
97
+
98
98
  References
99
99
  ----------
100
-
100
+
101
101
  .. footbibliography::
102
-
102
+
103
103
  """
104
104
  cdef int i=0, j=0, k=0, adj_atom_i=0, rel_atom_i=0
105
-
105
+
106
106
  cdef np.ndarray sasa_filter
107
107
  cdef np.ndarray occl_filter
108
108
  if atom_filter is not None:
@@ -121,7 +121,7 @@ def sasa(array, float probe_radius=1.4, np.ndarray atom_filter=None,
121
121
  filter = ~filter_monoatomic_ions(array)
122
122
  sasa_filter = sasa_filter & filter
123
123
  occl_filter = occl_filter & filter
124
-
124
+
125
125
  cdef np.ndarray sphere_points
126
126
  if callable(point_distr):
127
127
  sphere_points = point_distr(point_number)
@@ -130,7 +130,7 @@ def sasa(array, float probe_radius=1.4, np.ndarray atom_filter=None,
130
130
  else:
131
131
  raise ValueError(f"'{point_distr}' is not a valid point distribution")
132
132
  sphere_points = sphere_points.astype(np.float32)
133
-
133
+
134
134
  cdef np.ndarray radii
135
135
  if isinstance(vdw_radii, np.ndarray):
136
136
  radii = vdw_radii.astype(np.float32)
@@ -158,17 +158,17 @@ def sasa(array, float probe_radius=1.4, np.ndarray atom_filter=None,
158
158
  raise KeyError(f"'{vdw_radii}' is not a valid radii set")
159
159
  # Increase atom radii by probe size ("rolling probe")
160
160
  radii += probe_radius
161
-
161
+
162
162
  # Memoryview for filter
163
163
  # Problem with creating boolean memoryviews
164
164
  # -> Type uint8 is used
165
165
  cdef np_bool[:] sasa_filter_view = np.frombuffer(sasa_filter,
166
166
  dtype=np.uint8)
167
-
167
+
168
168
  cdef np.ndarray occl_r = radii[occl_filter]
169
169
  # Atom array containing occluding atoms
170
170
  occl_array = array[occl_filter]
171
-
171
+
172
172
  # Memoryviews for coordinates of entire (main) array
173
173
  # and for coordinates of occluding atom array
174
174
  cdef float32[:,:] main_coord = array.coord.astype(np.float32,
@@ -190,10 +190,10 @@ def sasa(array, float probe_radius=1.4, np.ndarray atom_filter=None,
190
190
  cdef float32[:] occl_radii_sq = occl_r * occl_r
191
191
  # Memoryview for atomwise SASA
192
192
  cdef float32[:] sasa = np.full(len(array), np.nan, dtype=np.float32)
193
-
193
+
194
194
  # Area of a sphere point on a unit sphere
195
195
  cdef float32 area_per_point = 4.0 * np.pi / point_number
196
-
196
+
197
197
  # Define further statically typed variables
198
198
  # that are needed for SASA calculation
199
199
  cdef int n_accesible = 0
@@ -212,8 +212,8 @@ def sasa(array, float probe_radius=1.4, np.ndarray atom_filter=None,
212
212
  cdef float32 occl_y = 0
213
213
  cdef float32 occl_z = 0
214
214
  cdef float32[:,:] relevant_occl_coord = None
215
-
216
- # Cell size is as large as the maximum distance,
215
+
216
+ # Cell size is as large as the maximum distance,
217
217
  # where two atom can intersect.
218
218
  # Therefore intersecting atoms are always in the same or adjacent cell.
219
219
  cell_list = CellList(occl_array, np.max(radii[occl_filter])*2)
@@ -226,7 +226,7 @@ def sasa(array, float probe_radius=1.4, np.ndarray atom_filter=None,
226
226
  cell_indices = cell_list.get_atoms_in_cells(array.coord)
227
227
  cell_indices_view = cell_indices
228
228
  max_adj_list_length = cell_indices.shape[0]
229
-
229
+
230
230
  # Later on, this array stores coordinates for actual
231
231
  # occluding atoms for a certain atom to calculate the
232
232
  # SASA for
@@ -236,7 +236,7 @@ def sasa(array, float probe_radius=1.4, np.ndarray atom_filter=None,
236
236
  # adjacent atoms
237
237
  relevant_occl_coord = np.zeros((max_adj_list_length, 4),
238
238
  dtype=np.float32)
239
-
239
+
240
240
  # Actual SASA calculation
241
241
  for i in range(array_length):
242
242
  # First level: The atoms to calculate SASA for