biotite 1.1.0__cp313-cp313-macosx_11_0_arm64.whl → 1.3.0__cp313-cp313-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-313-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-313-darwin.so +0 -0
  51. biotite/sequence/align/kmeralphabet.pyx +2 -2
  52. biotite/sequence/align/kmersimilarity.cpython-313-darwin.so +0 -0
  53. biotite/sequence/align/kmertable.cpython-313-darwin.so +0 -0
  54. biotite/sequence/align/kmertable.pyx +6 -6
  55. biotite/sequence/align/localgapped.cpython-313-darwin.so +0 -0
  56. biotite/sequence/align/localgapped.pyx +47 -47
  57. biotite/sequence/align/localungapped.cpython-313-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-313-darwin.so +0 -0
  61. biotite/sequence/align/multiple.pyx +1 -2
  62. biotite/sequence/align/pairwise.cpython-313-darwin.so +0 -0
  63. biotite/sequence/align/pairwise.pyx +37 -39
  64. biotite/sequence/align/permutation.cpython-313-darwin.so +0 -0
  65. biotite/sequence/align/selector.cpython-313-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-313-darwin.so +0 -0
  69. biotite/sequence/alphabet.py +2 -2
  70. biotite/sequence/annotation.py +19 -13
  71. biotite/sequence/codec.cpython-313-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-313-darwin.so +0 -0
  85. biotite/sequence/phylo/tree.cpython-313-darwin.so +0 -0
  86. biotite/sequence/phylo/upgma.cpython-313-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-313-darwin.so +0 -0
  98. biotite/structure/bonds.pyx +8 -5
  99. biotite/structure/box.py +159 -23
  100. biotite/structure/celllist.cpython-313-darwin.so +0 -0
  101. biotite/structure/celllist.pyx +83 -68
  102. biotite/structure/chains.py +17 -55
  103. biotite/structure/charges.cpython-313-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-313-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-313-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-313-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
@@ -41,22 +41,22 @@ def fetch(
41
41
  to be downloaded.
42
42
  format : {'sdf', 'asnt' 'asnb', 'xml', 'json', 'jsonp', 'png'}
43
43
  The format of the files to be downloaded.
44
+ target_path : str, optional
45
+ The target directory of the downloaded files.
46
+ By default, the file content is stored in a file-like object
47
+ (:class:`StringIO` or :class:`BytesIO`, respectively).
44
48
  as_structural_formula : bool, optional
45
49
  If set to true, the structural formula is download instead of
46
50
  an 3D conformer.
47
51
  This means that coordinates lie in th xy-plane and represent
48
52
  the positions atoms would have an a structural formula
49
53
  representation.
50
- target_path : str, optional
51
- The target directory of the downloaded files.
52
- By default, the file content is stored in a file-like object
53
- (:class:`StringIO` or :class:`BytesIO`, respectively).
54
54
  overwrite : bool, optional
55
55
  If true, existing files will be overwritten.
56
56
  Otherwise the respective file will only be downloaded, if the
57
57
  file does not exist yet in the specified target directory or if
58
58
  the file is empty.
59
- verbose: bool, optional
59
+ verbose : bool, optional
60
60
  If set to true, the function will output the download progress.
61
61
  throttle_threshold : float or None, optional
62
62
  A value between 0 and 1.
@@ -114,7 +114,7 @@ def fetch(
114
114
  raise TypeError("CIDs must be given as integers, not as string")
115
115
  # Verbose output
116
116
  if verbose:
117
- print(f"Fetching file {i+1:d} / {len(cids):d} ({cid})...", end="\r")
117
+ print(f"Fetching file {i + 1:d} / {len(cids):d} ({cid})...", end="\r")
118
118
 
119
119
  # Fetch file from database
120
120
  if target_path is not None:
@@ -11,6 +11,16 @@ def parse_error_details(response_text):
11
11
  """
12
12
  Parse the ``Detail: ...`` or alternatively ``Message: ...`` part of
13
13
  an error response.
14
+
15
+ Parameters
16
+ ----------
17
+ response_text : str
18
+ The text of the response.
19
+
20
+ Returns
21
+ -------
22
+ error_details : str
23
+ The error details.
14
24
  """
15
25
  for message_line_indicator in ["Detail: ", "Message: "]:
16
26
  for line in response_text.splitlines():
@@ -240,6 +240,11 @@ class FormulaQuery(Query):
240
240
  The maximum number of matches that this query may return.
241
241
  By default, the *PubChem* default value is used, which can
242
242
  be considered unlimited.
243
+
244
+ Returns
245
+ -------
246
+ query : FormulaQuery
247
+ The query.
243
248
  """
244
249
  element_counter = collections.Counter(atoms.element)
245
250
  formula = ""
@@ -327,7 +332,7 @@ class StructureQuery(Query, metaclass=abc.ABCMeta):
327
332
  )
328
333
  if not query_key_found:
329
334
  raise TypeError(
330
- "Expected exactly one of 'smiles', 'smarts', 'inchi', 'sdf' " "or 'cid'"
335
+ "Expected exactly one of 'smiles', 'smarts', 'inchi', 'sdf' or 'cid'"
331
336
  )
332
337
  if "number" in kwargs:
333
338
  self._number = kwargs["number"]
@@ -348,8 +353,13 @@ class StructureQuery(Query, metaclass=abc.ABCMeta):
348
353
  ----------
349
354
  atoms : AtomArray or AtomArrayStack
350
355
  The query structure.
351
- **kwargs : dict, optional
356
+ *args, **kwargs
352
357
  See the constructor for additional options.
358
+
359
+ Returns
360
+ -------
361
+ query : StructureQuery
362
+ The query object.
353
363
  """
354
364
  mol_file = MOLFile()
355
365
  mol_file.set_structure(atoms)
@@ -448,26 +458,19 @@ class SuperOrSubstructureQuery(StructureQuery, metaclass=abc.ABCMeta):
448
458
  be considered unlimited.
449
459
  match_charges : bool, optional
450
460
  If set to true, atoms must match the specified charge.
451
- (Default: False)
452
461
  match_tautomers : bool, optional
453
462
  If set to true, allow match to tautomers of the given structure.
454
- (Default: False)
455
463
  rings_not_embedded : bool, optional
456
464
  If set to true, rings may not be embedded in a larger system.
457
- (Default: False)
458
465
  single_double_bonds_match : bool, optional
459
466
  If set to true, single or double bonds match aromatic bonds.
460
- (Default: True)
461
467
  chains_match_rings : bool, optional
462
468
  If set to true, chain bonds in the query may match rings in
463
469
  hits.
464
- (Default: True)
465
470
  strip_hydrogen : bool, optional
466
471
  If set to true, remove any explicit hydrogens before searching.
467
- (Default: False)
468
472
  stereo : {'ignore', 'exact', 'relative', 'nonconflicting'}, optional
469
473
  How to handle stereo.
470
- (Default: 'ignore')
471
474
 
472
475
  Notes
473
476
  -----
@@ -528,26 +531,19 @@ class SuperstructureQuery(SuperOrSubstructureQuery):
528
531
  be considered unlimited.
529
532
  match_charges : bool, optional
530
533
  If set to true, atoms must match the specified charge.
531
- (Default: False)
532
534
  match_tautomers : bool, optional
533
535
  If set to true, allow match to tautomers of the given structure.
534
- (Default: False)
535
536
  rings_not_embedded : bool, optional
536
537
  If set to true, rings may not be embedded in a larger system.
537
- (Default: False)
538
538
  single_double_bonds_match : bool, optional
539
539
  If set to true, single or double bonds match aromatic bonds.
540
- (Default: True)
541
540
  chains_match_rings : bool, optional
542
541
  If set to true, chain bonds in the query may match rings in
543
542
  hits.
544
- (Default: True)
545
543
  strip_hydrogen : bool, optional
546
544
  If set to true, remove any explicit hydrogens before searching.
547
- (Default: False)
548
545
  stereo : {'ignore', 'exact', 'relative', 'nonconflicting'}, optional
549
546
  How to handle stereo.
550
- (Default: 'ignore')
551
547
 
552
548
  Notes
553
549
  -----
@@ -601,26 +597,19 @@ class SubstructureQuery(SuperOrSubstructureQuery):
601
597
  be considered unlimited.
602
598
  match_charges : bool, optional
603
599
  If set to true, atoms must match the specified charge.
604
- (Default: False)
605
600
  match_tautomers : bool, optional
606
601
  If set to true, allow match to tautomers of the given structure.
607
- (Default: False)
608
602
  rings_not_embedded : bool, optional
609
603
  If set to true, rings may not be embedded in a larger system.
610
- (Default: False)
611
604
  single_double_bonds_match : bool, optional
612
605
  If set to true, single or double bonds match aromatic bonds.
613
- (Default: True)
614
606
  chains_match_rings : bool, optional
615
607
  If set to true, chain bonds in the query may match rings in
616
608
  hits.
617
- (Default: True)
618
609
  strip_hydrogen : bool, optional
619
610
  If set to true, remove any explicit hydrogens before searching.
620
- (Default: False)
621
611
  stereo : {'ignore', 'exact', 'relative', 'nonconflicting'}, optional
622
612
  How to handle stereo.
623
- (Default: 'ignore')
624
613
 
625
614
  Notes
626
615
  -----
@@ -44,7 +44,7 @@ def fetch(pdb_ids, format, target_path=None, overwrite=False, verbose=False):
44
44
  Otherwise the respective file will only be downloaded, if the
45
45
  file does not exist yet in the specified target directory or if
46
46
  the file is empty.
47
- verbose: bool, optional
47
+ verbose : bool, optional
48
48
  If set to true, the function will output the download progress.
49
49
 
50
50
  Returns
@@ -91,7 +91,7 @@ def fetch(pdb_ids, format, target_path=None, overwrite=False, verbose=False):
91
91
  for i, id in enumerate(pdb_ids):
92
92
  # Verbose output
93
93
  if verbose:
94
- print(f"Fetching file {i+1:d} / {len(pdb_ids):d} ({id})...", end="\r")
94
+ print(f"Fetching file {i + 1:d} / {len(pdb_ids):d} ({id})...", end="\r")
95
95
 
96
96
  # Fetch file from database
97
97
  if target_path is not None:
@@ -152,6 +152,7 @@ def _assert_valid_file(response_text, pdb_id):
152
152
  for err_msg in [
153
153
  "404 Not Found",
154
154
  "<title>RCSB Protein Data Bank Error Page</title>",
155
+ "<title>PDB Archive over AWS</title>",
155
156
  "No fasta files were found.",
156
157
  "No valid PDB IDs were submitted.",
157
158
  ]
@@ -257,7 +257,7 @@ class FieldQuery(SingleQuery):
257
257
  "exists",
258
258
  ]:
259
259
  raise TypeError(
260
- f"Constructor got an unexpected keyword argument " f"'{self._operator}'"
260
+ f"Constructor got an unexpected keyword argument '{self._operator}'"
261
261
  )
262
262
 
263
263
  # Convert dates into ISO 8601
@@ -944,8 +944,7 @@ def _initialize_query_dict(query, return_type, group_by, content_types):
944
944
  if group_by is not None:
945
945
  if not group_by.is_compatible_return_type(return_type):
946
946
  raise ValueError(
947
- f"Return type '{return_type}' is not compatible "
948
- f"with the given Grouping"
947
+ f"Return type '{return_type}' is not compatible with the given Grouping"
949
948
  )
950
949
  request_options["group_by"] = group_by.get_content()
951
950
 
@@ -16,8 +16,8 @@ def assert_valid_response(response):
16
16
 
17
17
  Parameters
18
18
  ----------
19
- response_status_code: int
20
- Status code of request.get.
19
+ response : Response
20
+ Status code of :func:`requests.get()`.
21
21
  """
22
22
  if len(response.content) == 0:
23
23
  raise RequestError("No content returned")
@@ -41,7 +41,6 @@ def fetch(ids, format, target_path=None, overwrite=False, verbose=False):
41
41
  Download files from the UniProt in various formats.
42
42
 
43
43
  Available databases are UniProtKB, UniRef and UniParc.
44
-
45
44
  This function requires an internet connection.
46
45
 
47
46
  Parameters
@@ -58,11 +57,9 @@ def fetch(ids, format, target_path=None, overwrite=False, verbose=False):
58
57
  overwrite : bool, optional
59
58
  If true, existing files will be overwritten. Otherwise the
60
59
  respective file will only be downloaded if the file does not
61
- exist yet in the specified target directory or if the file is
62
- empty. (Default: False)
63
- verbose: bool, optional
60
+ exist yet in the specified target directory.
61
+ verbose : bool, optional
64
62
  If true, the function will output the download progress.
65
- (Default: False)
66
63
 
67
64
  Returns
68
65
  -------
@@ -50,9 +50,9 @@ class CompositeQuery(Query):
50
50
 
51
51
  Parameters
52
52
  ----------
53
- operator: str, {"AND", "OR", "NOT"}
53
+ operator : str, {"AND", "OR", "NOT"}
54
54
  The combination operator.
55
- queries : iterable object of SimpleQuery
55
+ query1, query2 : SimpleQuery
56
56
  The queries to be combined.
57
57
  """
58
58
 
@@ -114,7 +114,7 @@ class SimpleQuery(Query):
114
114
  The list of possible fields and the required search term
115
115
  formatting can be found
116
116
  `here <https://www.uniprot.org/help/query-fields>`_.
117
- term: str
117
+ term : str
118
118
  The search term.
119
119
  """
120
120
 
@@ -264,7 +264,6 @@ def search(query, number=500):
264
264
  The search query.
265
265
  number : int
266
266
  The maximum number of IDs that are obtained.
267
- (Default: 500)
268
267
 
269
268
  Returns
270
269
  -------
biotite/file.py CHANGED
@@ -44,7 +44,7 @@ class File(Copyable, metaclass=abc.ABCMeta):
44
44
 
45
45
  Returns
46
46
  -------
47
- file_object : File
47
+ file : File
48
48
  An instance from the respective :class:`File` subclass
49
49
  representing the parsed file.
50
50
  """
@@ -57,7 +57,7 @@ class File(Copyable, metaclass=abc.ABCMeta):
57
57
 
58
58
  Parameters
59
59
  ----------
60
- file_name : file-like object or str
60
+ file : file-like object or str
61
61
  The file to be written to.
62
62
  Alternatively a file path can be supplied.
63
63
  """
@@ -207,6 +207,18 @@ def wrap_string(text, width):
207
207
 
208
208
  This function simply wraps the given `text` after `width`
209
209
  characters, ignoring sentences, whitespaces, etc.
210
+
211
+ Parameters
212
+ ----------
213
+ text : str
214
+ The text to be wrapped.
215
+ width : int
216
+ The maximum number of characters per line.
217
+
218
+ Returns
219
+ -------
220
+ lines : list of str
221
+ The wrapped lines.
210
222
  """
211
223
  lines = []
212
224
  for i in range(0, len(text), width):
@@ -0,0 +1,19 @@
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 subpackage provides interfaces to other Python packages in the bioinformatics
7
+ ecosystem.
8
+ Its purpose is to convert between native Biotite objects, such as :class:`.AtomArray`
9
+ and :class:`.Sequence`, and the corresponding objects in the respective interfaced
10
+ package.
11
+ In contrast to :mod:`biotite.application`, where an entire application run is handled
12
+ under the hood, :mod:`biotite.interface` only covers the object conversion, allowing
13
+ for more flexibility.
14
+ """
15
+
16
+ __name__ = "biotite.interface"
17
+ __author__ = "Patrick Kunzmann"
18
+
19
+ from .warning import *
@@ -0,0 +1,20 @@
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 subpackage provides an interface to the `OpenMM <https://openmm.org/>`_
7
+ molecular simulation toolkit.
8
+ It allows conversion between :class:`.AtomArray`/:class:`.AtomArrayStack` and
9
+ structure-related objects from *OpenMM*.
10
+ """
11
+
12
+ __name__ = "biotite.interface.openmm"
13
+ __author__ = "Patrick Kunzmann"
14
+
15
+ from biotite.interface.version import require_package
16
+
17
+ require_package("openmm")
18
+
19
+ from .state import *
20
+ from .system import *
@@ -0,0 +1,93 @@
1
+ __name__ = "biotite.interface.openmm"
2
+ __author__ = "Patrick Kunzmann"
3
+ __all__ = ["from_context", "from_state", "from_states"]
4
+
5
+ import numpy as np
6
+ import openmm
7
+ import biotite.structure as struc
8
+
9
+
10
+ def from_context(template, context):
11
+ """
12
+ Parse the coordinates and box of the current state of an
13
+ :class:`openmm.openmm.Context` into an :class:`AtomArray`.
14
+
15
+ Parameters
16
+ ----------
17
+ template : AtomArray
18
+ This structure is used as template.
19
+ The output :class:`AtomArray` is equal to this template with the
20
+ exception of the coordinates and the box vectors.
21
+ context : Context
22
+ The coordinates are parsed from the current state of this
23
+ :class:`openmm.openmm.Context`.
24
+
25
+ Returns
26
+ -------
27
+ atoms : AtomArray
28
+ The created :class:`AtomArray`.
29
+ """
30
+ state = context.getState(getPositions=True)
31
+ return from_state(template, state)
32
+
33
+
34
+ def from_state(template, state):
35
+ """
36
+ Parse the coordinates and box of the given :class:`openmm.openmm.State`
37
+ into an :class:`AtomArray`.
38
+
39
+ Parameters
40
+ ----------
41
+ template : AtomArray
42
+ This structure is used as template.
43
+ The output :class:`AtomArray` is equal to this template with the
44
+ exception of the coordinates and the box vectors.
45
+ state : State
46
+ The coordinates are parsed from this state.
47
+ Must be created with ``getPositions=True``.
48
+
49
+ Returns
50
+ -------
51
+ atoms : AtomArray
52
+ The created :class:`AtomArray`.
53
+ """
54
+ coord, box = _parse_state(state)
55
+ atoms = template.copy()
56
+ atoms.coord = coord
57
+ atoms.box = box
58
+ return atoms
59
+
60
+
61
+ def from_states(template, states):
62
+ """
63
+ Parse the coordinates and box vectors of multiple :class:`openmm.openmm.State`
64
+ objects into an :class:`AtomArrayStack`.
65
+
66
+ Parameters
67
+ ----------
68
+ template : AtomArray
69
+ This structure is used as template.
70
+ The output :class:`AtomArray` is equal to this template with the
71
+ exception of the coordinates and the box vectors.
72
+ states : iterable of State
73
+ The coordinates are parsed from these states.
74
+ Must be created with ``getPositions=True``.
75
+
76
+ Returns
77
+ -------
78
+ atoms : AtomArrayStack
79
+ The created :class:`AtomArrayStack`.
80
+ """
81
+ coords = []
82
+ boxes = []
83
+ for state in states:
84
+ coord, box = _parse_state(state)
85
+ coords.append(coord)
86
+ boxes.append(box)
87
+ return struc.from_template(template, np.stack(coords), np.stack(boxes))
88
+
89
+
90
+ def _parse_state(state):
91
+ coord = state.getPositions(asNumpy=True).value_in_unit(openmm.unit.angstrom)
92
+ box = state.getPeriodicBoxVectors(asNumpy=True).value_in_unit(openmm.unit.angstrom)
93
+ return coord, box
@@ -0,0 +1,227 @@
1
+ __name__ = "biotite.interface.openmm"
2
+ __author__ = "Patrick Kunzmann"
3
+ __all__ = ["to_system", "to_topology", "from_topology"]
4
+
5
+ import numpy as np
6
+ import openmm
7
+ import openmm.app as app
8
+ import openmm.unit as unit
9
+ import biotite.structure as struc
10
+ import biotite.structure.info as info
11
+
12
+ _BOND_TYPE_TO_ORDER = {
13
+ struc.BondType.SINGLE: 1,
14
+ struc.BondType.DOUBLE: 2,
15
+ struc.BondType.TRIPLE: 3,
16
+ struc.BondType.QUADRUPLE: 4,
17
+ struc.BondType.AROMATIC_SINGLE: 1,
18
+ struc.BondType.AROMATIC_DOUBLE: 2,
19
+ struc.BondType.AROMATIC_TRIPLE: 3,
20
+ }
21
+
22
+
23
+ def to_system(atoms):
24
+ """
25
+ Create a :class:`openmm.openmm.System` from an :class:`AtomArray` or
26
+ :class:`AtomArrayStack`.
27
+
28
+ Parameters
29
+ ----------
30
+ atoms : AtomArray or AtomArrayStack
31
+ The structure to be converted.
32
+ The box vectors are set from the ``box`` attribute.
33
+ If multiple models are given the box of the first model is selected.
34
+
35
+ Returns
36
+ -------
37
+ system : System
38
+ The created :class:`openmm.openmm.System`.
39
+ """
40
+ system = openmm.System()
41
+
42
+ for element in atoms.element:
43
+ system.addParticle(info.mass(element))
44
+
45
+ if atoms.box is not None:
46
+ if atoms.box.ndim == 3:
47
+ # If an `AtomArrayStack`, the first box is chosen
48
+ box = atoms.box[0]
49
+ else:
50
+ box = atoms.box
51
+ if not _check_box_requirements(box):
52
+ raise struc.BadStructureError(
53
+ "Box does not fulfill OpenMM's requirements for periodic boxes"
54
+ )
55
+ system.setDefaultPeriodicBoxVectors(*(box * unit.angstrom))
56
+
57
+ return system
58
+
59
+
60
+ def to_topology(atoms):
61
+ """
62
+ Create a :class:`openmm.app.topology.Topology` from an :class:`AtomArray` or
63
+ :class:`AtomArrayStack`.
64
+
65
+ Parameters
66
+ ----------
67
+ atoms : AtomArray or AtomArrayStack
68
+ The structure to be converted.
69
+ An associated :class:`BondList` is required.
70
+
71
+ Returns
72
+ -------
73
+ topology : Topology
74
+ The created :class:`openmm.app.topology.Topology`.
75
+ """
76
+ if "atom_id" in atoms.get_annotation_categories():
77
+ atom_id = atoms.atom_id
78
+ else:
79
+ atom_id = np.arange(atoms.array_length()) + 1
80
+
81
+ chain_starts = struc.get_chain_starts(atoms)
82
+ res_starts = struc.get_residue_starts(atoms)
83
+
84
+ # Lists of chain, residue and atom objects that will be filled later
85
+ chain_list = []
86
+ residue_list = []
87
+ atom_list = []
88
+ # Each atom's index in the chain and residue list
89
+ chain_idx = _generate_idx(chain_starts, atoms.array_length())
90
+ res_idx = _generate_idx(res_starts, atoms.array_length())
91
+
92
+ topology = app.Topology()
93
+
94
+ ## Add atoms
95
+ for start_i in chain_starts:
96
+ chain_list.append(topology.addChain(id=atoms.chain_id[start_i]))
97
+ for start_i in res_starts:
98
+ residue_list.append(
99
+ topology.addResidue(
100
+ name=atoms.res_name[start_i],
101
+ chain=chain_list[chain_idx[start_i]],
102
+ insertionCode=atoms.ins_code[start_i],
103
+ id=str(atoms.res_id[start_i]),
104
+ )
105
+ )
106
+ for i in np.arange(atoms.array_length()):
107
+ atom_list.append(
108
+ topology.addAtom(
109
+ name=atoms.atom_name[i],
110
+ element=app.Element.getBySymbol(atoms.element[i]),
111
+ residue=residue_list[res_idx[i]],
112
+ id=str(atom_id[start_i]),
113
+ )
114
+ )
115
+
116
+ ## Add bonds
117
+ if atoms.bonds is None:
118
+ raise struc.BadStructureError("Input structure misses an associated BondList")
119
+ for atom_i, atom_j, bond_type in atoms.bonds.as_array():
120
+ topology.addBond(
121
+ atom_list[atom_i],
122
+ atom_list[atom_j],
123
+ order=_BOND_TYPE_TO_ORDER.get(bond_type),
124
+ )
125
+
126
+ ## Add box
127
+ if atoms.box is not None:
128
+ if atoms.box.ndim == 3:
129
+ # If an `AtomArrayStack`, the first box is chosen
130
+ box = atoms.box[0]
131
+ else:
132
+ box = atoms.box
133
+ if not _check_box_requirements(box):
134
+ raise struc.BadStructureError(
135
+ "Box does not fulfill OpenMM's requirements for periodic boxes"
136
+ )
137
+ topology.setPeriodicBoxVectors(box * unit.angstrom)
138
+
139
+ return topology
140
+
141
+
142
+ def from_topology(topology):
143
+ """
144
+ Create a :class:`AtomArray` from a :class:`openmm.app.topology.Topology`.
145
+
146
+ Parameters
147
+ ----------
148
+ topology : Topology
149
+ The topology to be converted.
150
+
151
+ Returns
152
+ -------
153
+ atoms : AtomArray
154
+ The created :class:`AtomArray`.
155
+ As the :class:`openmm.app.topology.Topology` does not contain atom
156
+ coordinates, the values of the :class:`AtomArray` ``coord``
157
+ are set to *NaN*.
158
+
159
+ Notes
160
+ -----
161
+ This function is especially useful for obtaining an updated
162
+ template, if the original topology was modified
163
+ (e.g. via :class:`openmm.app.modeller.Modeller`).
164
+ """
165
+ atoms = struc.AtomArray(topology.getNumAtoms())
166
+
167
+ chain_ids = []
168
+ res_ids = []
169
+ ins_codes = []
170
+ res_names = []
171
+ atom_names = []
172
+ elements = []
173
+ for chain in topology.chains():
174
+ chain_id = chain.id
175
+ for residue in chain.residues():
176
+ res_name = residue.name
177
+ res_id = int(residue.id)
178
+ ins_code = residue.insertionCode
179
+ for atom in residue.atoms():
180
+ chain_ids.append(chain_id)
181
+ res_ids.append(res_id)
182
+ ins_codes.append(ins_code)
183
+ res_names.append(res_name)
184
+ atom_names.append(atom.name.upper())
185
+ elements.append(atom.element.symbol.upper())
186
+ atoms.chain_id = chain_ids
187
+ atoms.res_id = res_ids
188
+ atoms.ins_code = ins_codes
189
+ atoms.res_name = res_names
190
+ atoms.atom_name = atom_names
191
+ atoms.element = elements
192
+ atoms.hetero = ~(struc.filter_amino_acids(atoms) | struc.filter_nucleotides(atoms))
193
+
194
+ bonds = []
195
+ atom_to_index = {atom: i for i, atom in enumerate(topology.atoms())}
196
+ for bond in topology.bonds():
197
+ order = bond.order if bond.order is not None else struc.BondType.ANY
198
+ bonds.append([atom_to_index[bond.atom1], atom_to_index[bond.atom2], order])
199
+ atoms.bonds = struc.BondList(atoms.array_length(), np.array(bonds))
200
+
201
+ box = topology.getPeriodicBoxVectors()
202
+ if box is None:
203
+ atoms.box = None
204
+ else:
205
+ atoms.box = np.asarray(box.value_in_unit(openmm.unit.angstrom))
206
+
207
+ return atoms
208
+
209
+
210
+ def _generate_idx(starts, length):
211
+ # An array that is 1, at start positions and 0 everywhere else
212
+ start_counter = np.zeros(length, dtype=int)
213
+ start_counter[starts] = 1
214
+ # The first index should be zero -> the first start is not counted
215
+ start_counter[0] = 0
216
+ return np.cumsum(start_counter)
217
+
218
+
219
+ def _check_box_requirements(box):
220
+ """
221
+ Return True, if the given box fulfills *OpenMM*'s requirements for
222
+ boxes, else otherwise.
223
+
224
+ The first vector must be on the x-axis
225
+ and the second vector must be on the xy-plane.
226
+ """
227
+ return np.all(np.triu(box, k=1) == 0)