biotite 1.3.0__cp311-cp311-win_amd64.whl → 1.4.0__cp311-cp311-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.

Potentially problematic release.


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

Files changed (38) hide show
  1. biotite/interface/pymol/object.py +3 -1
  2. biotite/interface/rdkit/mol.py +5 -5
  3. biotite/sequence/align/banded.cp311-win_amd64.pyd +0 -0
  4. biotite/sequence/align/kmeralphabet.cp311-win_amd64.pyd +0 -0
  5. biotite/sequence/align/kmersimilarity.cp311-win_amd64.pyd +0 -0
  6. biotite/sequence/align/kmertable.cp311-win_amd64.pyd +0 -0
  7. biotite/sequence/align/localgapped.cp311-win_amd64.pyd +0 -0
  8. biotite/sequence/align/localungapped.cp311-win_amd64.pyd +0 -0
  9. biotite/sequence/align/multiple.cp311-win_amd64.pyd +0 -0
  10. biotite/sequence/align/pairwise.cp311-win_amd64.pyd +0 -0
  11. biotite/sequence/align/permutation.cp311-win_amd64.pyd +0 -0
  12. biotite/sequence/align/selector.cp311-win_amd64.pyd +0 -0
  13. biotite/sequence/align/tracetable.cp311-win_amd64.pyd +0 -0
  14. biotite/sequence/codec.cp311-win_amd64.pyd +0 -0
  15. biotite/sequence/phylo/nj.cp311-win_amd64.pyd +0 -0
  16. biotite/sequence/phylo/tree.cp311-win_amd64.pyd +0 -0
  17. biotite/sequence/phylo/upgma.cp311-win_amd64.pyd +0 -0
  18. biotite/structure/bonds.cp311-win_amd64.pyd +0 -0
  19. biotite/structure/bonds.pyx +67 -6
  20. biotite/structure/box.py +1 -1
  21. biotite/structure/celllist.cp311-win_amd64.pyd +0 -0
  22. biotite/structure/charges.cp311-win_amd64.pyd +0 -0
  23. biotite/structure/compare.py +2 -0
  24. biotite/structure/info/components.bcif +0 -0
  25. biotite/structure/io/pdb/file.py +15 -5
  26. biotite/structure/io/pdb/hybrid36.cp311-win_amd64.pyd +0 -0
  27. biotite/structure/io/pdbx/bcif.py +6 -3
  28. biotite/structure/io/pdbx/cif.py +5 -2
  29. biotite/structure/io/pdbx/compress.py +2 -2
  30. biotite/structure/io/pdbx/convert.py +25 -20
  31. biotite/structure/io/pdbx/encoding.cp311-win_amd64.pyd +0 -0
  32. biotite/structure/rings.py +117 -1
  33. biotite/structure/sasa.cp311-win_amd64.pyd +0 -0
  34. biotite/version.py +2 -2
  35. {biotite-1.3.0.dist-info → biotite-1.4.0.dist-info}/METADATA +1 -1
  36. {biotite-1.3.0.dist-info → biotite-1.4.0.dist-info}/RECORD +38 -38
  37. {biotite-1.3.0.dist-info → biotite-1.4.0.dist-info}/WHEEL +0 -0
  38. {biotite-1.3.0.dist-info → biotite-1.4.0.dist-info}/licenses/LICENSE.rst +0 -0
@@ -388,7 +388,9 @@ class PyMOLObject:
388
388
  elif isinstance(selection, str):
389
389
  return f"%{self._name} and ({selection})"
390
390
  else:
391
- sel = self.where(np.asarray(selection))
391
+ if not isinstance(selection, slice):
392
+ selection = np.asarray(selection)
393
+ sel = self.where(selection)
392
394
  if sel == "none" and not_none:
393
395
  raise ValueError("Selection contains no atoms")
394
396
  return sel
@@ -59,7 +59,7 @@ _STANDARD_ANNOTATIONS = frozenset(
59
59
  "charge",
60
60
  "b_factor",
61
61
  "occupancy",
62
- "label_alt_id",
62
+ "altloc_id",
63
63
  }
64
64
  )
65
65
 
@@ -202,8 +202,8 @@ def to_mol(
202
202
  rdkit_atom_res_info.SetOccupancy(atoms.occupancy[i].item())
203
203
  if "b_factor" in has_annot:
204
204
  rdkit_atom_res_info.SetTempFactor(atoms.b_factor[i].item())
205
- if "label_alt_id" in has_annot:
206
- rdkit_atom_res_info.SetAltLoc(atoms.label_alt_id[i].item())
205
+ if "altloc_id" in has_annot:
206
+ rdkit_atom_res_info.SetAltLoc(atoms.altloc_id[i].item())
207
207
  rdkit_atom.SetPDBResidueInfo(rdkit_atom_res_info)
208
208
 
209
209
  # add extra annotations
@@ -361,7 +361,7 @@ def from_mol(mol, conformer_id=None, add_hydrogen=None):
361
361
  atoms.add_annotation("charge", int)
362
362
  atoms.add_annotation("b_factor", float)
363
363
  atoms.add_annotation("occupancy", float)
364
- atoms.add_annotation("label_alt_id", str)
364
+ atoms.add_annotation("altloc_id", str)
365
365
 
366
366
  for rdkit_atom in rdkit_atoms:
367
367
  _atom_idx = rdkit_atom.GetIdx()
@@ -406,7 +406,7 @@ def from_mol(mol, conformer_id=None, add_hydrogen=None):
406
406
  atoms.res_id[_atom_idx] = residue_info.GetResidueNumber()
407
407
  atoms.ins_code[_atom_idx] = residue_info.GetInsertionCode()
408
408
  atoms.res_name[_atom_idx] = residue_info.GetResidueName()
409
- atoms.label_alt_id[_atom_idx] = residue_info.GetAltLoc()
409
+ atoms.altloc_id[_atom_idx] = residue_info.GetAltLoc()
410
410
  atoms.hetero[_atom_idx] = residue_info.GetIsHeteroAtom()
411
411
  atoms.b_factor[_atom_idx] = residue_info.GetTempFactor()
412
412
  atoms.occupancy[_atom_idx] = residue_info.GetOccupancy()
Binary file
Binary file
@@ -517,14 +517,41 @@ class BondList(Copyable):
517
517
  0 1 SINGLE
518
518
  1 2 DOUBLE
519
519
  """
520
- bond_types = self._bonds[:,2]
521
520
  for aromatic_type, non_aromatic_type in [
522
521
  (BondType.AROMATIC_SINGLE, BondType.SINGLE),
523
522
  (BondType.AROMATIC_DOUBLE, BondType.DOUBLE),
524
523
  (BondType.AROMATIC_TRIPLE, BondType.TRIPLE),
525
524
  (BondType.AROMATIC, BondType.ANY),
526
525
  ]:
527
- bond_types[bond_types == aromatic_type] = non_aromatic_type
526
+ mask = self._bonds[:, 2] == aromatic_type
527
+ self._bonds[mask, 2] = non_aromatic_type
528
+
529
+ def remove_kekulization(self):
530
+ """
531
+ Remove the bond order information from aromatic bonds, i.e. convert all
532
+ aromatic bonds to :attr:`BondType.ANY`.
533
+
534
+ Examples
535
+ --------
536
+
537
+ >>> bond_list = BondList(3)
538
+ >>> bond_list.add_bond(0, 1, BondType.AROMATIC_SINGLE)
539
+ >>> bond_list.add_bond(1, 2, BondType.AROMATIC_DOUBLE)
540
+ >>> bond_list.remove_kekulization()
541
+ >>> for i, j, bond_type in bond_list.as_array():
542
+ ... print(i, j, BondType(bond_type).name)
543
+ 0 1 AROMATIC
544
+ 1 2 AROMATIC
545
+ """
546
+ kekulized_mask = np.isin(
547
+ self._bonds[:, 2],
548
+ (
549
+ BondType.AROMATIC_SINGLE,
550
+ BondType.AROMATIC_DOUBLE,
551
+ BondType.AROMATIC_TRIPLE,
552
+ ),
553
+ )
554
+ self._bonds[kekulized_mask, 2] = BondType.AROMATIC
528
555
 
529
556
  def remove_bond_order(self):
530
557
  """
@@ -532,6 +559,41 @@ class BondList(Copyable):
532
559
  """
533
560
  self._bonds[:,2] = BondType.ANY
534
561
 
562
+ def convert_bond_type(self, original_bond_type, new_bond_type):
563
+ """
564
+ convert_bond_type(original_bond_type, new_bond_type)
565
+
566
+ Convert all occurences of a given bond type into another bond type.
567
+
568
+ Parameters
569
+ ----------
570
+ original_bond_type : BondType or int
571
+ The bond type to convert.
572
+ new_bond_type : BondType or int
573
+ The new bond type.
574
+
575
+ Examples
576
+ --------
577
+
578
+ >>> bond_list = BondList(4)
579
+ >>> bond_list.add_bond(0, 1, BondType.DOUBLE)
580
+ >>> bond_list.add_bond(1, 2, BondType.COORDINATION)
581
+ >>> bond_list.add_bond(2, 3, BondType.COORDINATION)
582
+ >>> for i, j, bond_type in bond_list.as_array():
583
+ ... print(i, j, BondType(bond_type).name)
584
+ 0 1 DOUBLE
585
+ 1 2 COORDINATION
586
+ 2 3 COORDINATION
587
+ >>> bond_list.convert_bond_type(BondType.COORDINATION, BondType.SINGLE)
588
+ >>> for i, j, bond_type in bond_list.as_array():
589
+ ... print(i, j, BondType(bond_type).name)
590
+ 0 1 DOUBLE
591
+ 1 2 SINGLE
592
+ 2 3 SINGLE
593
+ """
594
+ mask = self._bonds[:, 2] == original_bond_type
595
+ self._bonds[mask, 2] = new_bond_type
596
+
535
597
  def get_atom_count(self):
536
598
  """
537
599
  get_atom_count()
@@ -1437,9 +1499,8 @@ _DEFAULT_DISTANCE_RANGE = {
1437
1499
  def connect_via_distances(atoms, dict distance_range=None, bint inter_residue=True,
1438
1500
  default_bond_type=BondType.ANY, bint periodic=False):
1439
1501
  """
1440
- connect_via_distances(atoms, distance_range=None, atom_mask=None,
1441
- inter_residue=True, default_bond_type=BondType.ANY,
1442
- periodic=False)
1502
+ connect_via_distances(atoms, distance_range=None, inter_residue=True,
1503
+ default_bond_type=BondType.ANY, periodic=False)
1443
1504
 
1444
1505
  Create a :class:`BondList` for a given atom array, based on
1445
1506
  pairwise atom distances.
@@ -1589,7 +1650,7 @@ def connect_via_distances(atoms, dict distance_range=None, bint inter_residue=Tr
1589
1650
  def connect_via_residue_names(atoms, bint inter_residue=True,
1590
1651
  dict custom_bond_dict=None):
1591
1652
  """
1592
- connect_via_residue_names(atoms, atom_mask=None, inter_residue=True)
1653
+ connect_via_residue_names(atoms, inter_residue=True, custom_bond_dict=None)
1593
1654
 
1594
1655
  Create a :class:`BondList` for a given atom array (stack), based on
1595
1656
  the deposited bonds for each residue in the RCSB ``components.cif``
biotite/structure/box.py CHANGED
@@ -361,7 +361,7 @@ def repeat_box(atoms, amount=1):
361
361
  if atoms.box is None:
362
362
  raise BadStructureError("Structure has no box")
363
363
 
364
- repeat_coord, indices = repeat_box_coord(atoms.coord, atoms.box)
364
+ repeat_coord, indices = repeat_box_coord(atoms.coord, atoms.box, amount)
365
365
  # Unroll repeated coordinates for input to 'repeat()'
366
366
  if repeat_coord.ndim == 2:
367
367
  repeat_coord = repeat_coord.reshape(-1, atoms.array_length(), 3)
@@ -449,6 +449,8 @@ def lddt(
449
449
  # Aggregate the fractions over the desired level
450
450
  if isinstance(aggregation, str) and aggregation == "all":
451
451
  # Average over all contacts
452
+ if len(fraction_preserved_bins) == 0:
453
+ return np.float32(np.nan)
452
454
  return np.mean(fraction_preserved_bins, axis=-1)
453
455
  else:
454
456
  # A string is also a 'Sequence'
Binary file
@@ -936,7 +936,11 @@ class PDBFile(TextFile):
936
936
  if transform_start is None:
937
937
  raise InvalidFileError("No 'BIOMT' records found for chosen assembly")
938
938
  rotations, translations = _parse_transformations(
939
- assembly_lines[transform_start:stop]
939
+ [
940
+ line
941
+ for line in assembly_lines[transform_start:stop]
942
+ if len(line.strip()) > 0
943
+ ]
940
944
  )
941
945
  # Filter affected chains
942
946
  sub_structure = structure[
@@ -1193,7 +1197,7 @@ class PDBFile(TextFile):
1193
1197
  conect_lines = [line for line in self.lines if line.startswith("CONECT")]
1194
1198
 
1195
1199
  # Mapping from atom ids to indices in an AtomArray
1196
- atom_id_to_index = np.zeros(atom_ids[-1] + 1, dtype=int)
1200
+ atom_id_to_index = np.full(atom_ids[-1] + 1, -1, dtype=int)
1197
1201
  try:
1198
1202
  for i, id in enumerate(atom_ids):
1199
1203
  atom_id_to_index[id] = i
@@ -1202,15 +1206,21 @@ class PDBFile(TextFile):
1202
1206
 
1203
1207
  bonds = []
1204
1208
  for line in conect_lines:
1205
- center_id = atom_id_to_index[decode_hybrid36(line[6:11])]
1209
+ center_index = atom_id_to_index[decode_hybrid36(line[6:11])]
1210
+ if center_index == -1:
1211
+ # Atom ID is not in the AtomArray (probably removed altloc)
1212
+ continue
1206
1213
  for i in range(11, 31, 5):
1207
1214
  id_string = line[i : i + 5]
1208
1215
  try:
1209
- id = atom_id_to_index[decode_hybrid36(id_string)]
1216
+ contact_index = atom_id_to_index[decode_hybrid36(id_string)]
1217
+ if contact_index == -1:
1218
+ # Atom ID is not in the AtomArray (probably removed altloc)
1219
+ continue
1210
1220
  except ValueError:
1211
1221
  # String is empty -> no further IDs
1212
1222
  break
1213
- bonds.append((center_id, id))
1223
+ bonds.append((center_index, contact_index))
1214
1224
 
1215
1225
  # The length of the 'atom_ids' array
1216
1226
  # is equal to the length of the AtomArray
@@ -511,7 +511,7 @@ class BinaryCIFBlock(_HierarchicalContainer):
511
511
 
512
512
  def __delitem__(self, key):
513
513
  try:
514
- return super().__setitem__("_" + key)
514
+ return super().__delitem__("_" + key)
515
515
  except KeyError:
516
516
  raise KeyError(key)
517
517
 
@@ -581,9 +581,12 @@ class BinaryCIFFile(File, _HierarchicalContainer):
581
581
 
582
582
  @property
583
583
  def block(self):
584
- if len(self) != 1:
584
+ if len(self) == 0:
585
+ raise ValueError("There are no blocks in the file")
586
+ elif len(self) > 1:
585
587
  raise ValueError("There are multiple blocks in the file")
586
- return self[next(iter(self))]
588
+ else:
589
+ return self[next(iter(self))]
587
590
 
588
591
  @staticmethod
589
592
  def subcomponent_class():
@@ -799,9 +799,12 @@ class CIFFile(_Component, File, MutableMapping):
799
799
 
800
800
  @property
801
801
  def block(self):
802
- if len(self) != 1:
802
+ if len(self) == 0:
803
+ raise ValueError("There are no blocks in the file")
804
+ elif len(self) > 1:
803
805
  raise ValueError("There are multiple blocks in the file")
804
- return self[next(iter(self))]
806
+ else:
807
+ return self[next(iter(self))]
805
808
 
806
809
  @staticmethod
807
810
  def subcomponent_class():
@@ -56,14 +56,14 @@ def compress(data, float_tolerance=None, rtol=1e-6, atol=1e-4):
56
56
  >>> pdbx_file.write(uncompressed_file)
57
57
  >>> _ = uncompressed_file.seek(0)
58
58
  >>> print(f"{len(uncompressed_file.read()) // 1000} KB")
59
- 927 KB
59
+ 937 KB
60
60
  >>> # Write compressed file
61
61
  >>> pdbx_file = compress(pdbx_file)
62
62
  >>> compressed_file = BytesIO()
63
63
  >>> pdbx_file.write(compressed_file)
64
64
  >>> _ = compressed_file.seek(0)
65
65
  >>> print(f"{len(compressed_file.read()) // 1000} KB")
66
- 111 KB
66
+ 114 KB
67
67
  """
68
68
  if float_tolerance is not None:
69
69
  warnings.warn(
@@ -775,7 +775,10 @@ def _filter_altloc(array, atom_site, altloc):
775
775
  if altloc == "all":
776
776
  array.set_annotation("altloc_id", altloc_ids.as_array(str))
777
777
  return array, atom_site
778
- elif altloc_ids is None or (altloc_ids.mask.array != MaskValue.PRESENT).all():
778
+ elif altloc_ids is None or (
779
+ altloc_ids.mask is not None
780
+ and (altloc_ids.mask.array != MaskValue.PRESENT).all()
781
+ ):
779
782
  # No altlocs in atom_site category
780
783
  return array, atom_site
781
784
  elif altloc == "occupancy" and occupancy is not None:
@@ -873,11 +876,7 @@ def set_structure(
873
876
  this parameter is ignored.
874
877
  If the file is empty, a new data block will be created.
875
878
  include_bonds : bool, optional
876
- If set to true and `array` has associated ``bonds`` , the
877
- intra-residue bonds will be written into the ``chem_comp_bond``
878
- category.
879
- Inter-residue bonds will be written into the ``struct_conn``
880
- independent of this parameter.
879
+ DEPRECATED: Has no effect anymore.
881
880
  extra_fields : list of str, optional
882
881
  List of additional fields from the ``atom_site`` category
883
882
  that should be written into the file.
@@ -898,6 +897,13 @@ def set_structure(
898
897
  >>> set_structure(file, atom_array)
899
898
  >>> file.write(os.path.join(path_to_directory, "structure.cif"))
900
899
  """
900
+ if include_bonds:
901
+ warnings.warn(
902
+ "`include_bonds` parameter is deprecated, "
903
+ "intra-residue are always written, if available",
904
+ DeprecationWarning,
905
+ )
906
+
901
907
  _check_non_empty(array)
902
908
 
903
909
  block = _get_or_create_block(pdbx_file, data_block)
@@ -975,10 +981,9 @@ def set_structure(
975
981
  struct_conn = _set_inter_residue_bonds(array, atom_site)
976
982
  if struct_conn is not None:
977
983
  block["struct_conn"] = struct_conn
978
- if include_bonds:
979
- chem_comp_bond = _set_intra_residue_bonds(array, atom_site)
980
- if chem_comp_bond is not None:
981
- block["chem_comp_bond"] = chem_comp_bond
984
+ chem_comp_bond = _set_intra_residue_bonds(array, atom_site)
985
+ if chem_comp_bond is not None:
986
+ block["chem_comp_bond"] = chem_comp_bond
982
987
 
983
988
  # In case of a single model handle each coordinate
984
989
  # simply like a flattened array
@@ -1652,11 +1657,11 @@ def get_assembly(
1652
1657
  If set to true, a :class:`BondList` will be created for the
1653
1658
  resulting :class:`AtomArray` containing the bond information
1654
1659
  from the file.
1655
- Bonds, whose order could not be determined from the
1656
- *Chemical Component Dictionary*
1657
- (e.g. especially inter-residue bonds),
1658
- have :attr:`BondType.ANY`, since the PDB format itself does
1659
- not support bond orders.
1660
+ Inter-residue bonds, will be read from the ``struct_conn``
1661
+ category.
1662
+ Intra-residue bonds will be read from the ``chem_comp_bond``, if
1663
+ available, otherwise they will be derived from the Chemical
1664
+ Component Dictionary.
1660
1665
 
1661
1666
  Returns
1662
1667
  -------
@@ -1926,11 +1931,11 @@ def get_unit_cell(
1926
1931
  If set to true, a :class:`BondList` will be created for the
1927
1932
  resulting :class:`AtomArray` containing the bond information
1928
1933
  from the file.
1929
- Bonds, whose order could not be determined from the
1930
- *Chemical Component Dictionary*
1931
- (e.g. especially inter-residue bonds),
1932
- have :attr:`BondType.ANY`, since the PDB format itself does
1933
- not support bond orders.
1934
+ Inter-residue bonds, will be read from the ``struct_conn``
1935
+ category.
1936
+ Intra-residue bonds will be read from the ``chem_comp_bond``, if
1937
+ available, otherwise they will be derived from the Chemical
1938
+ Component Dictionary.
1934
1939
 
1935
1940
  Returns
1936
1941
  -------
@@ -8,7 +8,12 @@ This module provides functions related to aromatic rings.
8
8
 
9
9
  __name__ = "biotite.structure"
10
10
  __author__ = "Patrick Kunzmann"
11
- __all__ = ["find_aromatic_rings", "find_stacking_interactions", "PiStacking"]
11
+ __all__ = [
12
+ "find_aromatic_rings",
13
+ "find_stacking_interactions",
14
+ "find_pi_cation_interactions",
15
+ "PiStacking",
16
+ ]
12
17
 
13
18
 
14
19
  from enum import IntEnum
@@ -268,6 +273,117 @@ def find_stacking_interactions(
268
273
  ]
269
274
 
270
275
 
276
+ def find_pi_cation_interactions(
277
+ atoms,
278
+ distance_cutoff=5.0,
279
+ angle_tol=np.deg2rad(30.0),
280
+ ):
281
+ """
282
+ Find pi-cation interactions between aromatic rings and cations.
283
+
284
+ Parameters
285
+ ----------
286
+ atoms : AtomArray
287
+ The atoms to be searched for pi-cation interactions.
288
+ Requires an associated :class:`BondList` and ``charge`` annotation.
289
+ distance_cutoff : float, optional
290
+ The cutoff distance between ring centroid and cation.
291
+ angle_tol : float, optional
292
+ The tolerance for the angle between the ring plane normal
293
+ and the centroid-cation vector. Perfect pi-cation interaction
294
+ has 0° angle (perpendicular to ring plane).
295
+ Given in radians.
296
+
297
+ Returns
298
+ -------
299
+ interactions : list of tuple(ndarray, int)
300
+ The pi-cation interactions between aromatic rings and cations.
301
+ Each element in the list represents one pi-cation interaction.
302
+ The first element of each tuple represents atom indices of the
303
+ aromatic ring, the second element is the atom index of the cation.
304
+
305
+ See Also
306
+ --------
307
+ find_aromatic_rings : Used for finding the aromatic rings in this function.
308
+ find_stacking_interactions : Find pi-stacking interactions between rings.
309
+
310
+ Notes
311
+ -----
312
+ The conditions for pi-cation interactions are:
313
+ - The distance between ring centroid and cation must be within
314
+ `distance_cutoff`. :footcite:`Wojcikowski2015` uses 5.0 Å,
315
+ whereas :footcite:`Bouysset2021` uses 4.5 Å.
316
+ - The angle between the ring plane normal and the centroid-cation
317
+ vector must be within `angle_tol` of 0° (perpendicular to plane).
318
+
319
+ Examples
320
+ --------
321
+ >>> from os.path import join
322
+ >>> structure = load_structure(join(path_to_structures, "3wip.cif"), include_bonds=True, extra_fields=["charge"])
323
+ >>> interactions = find_pi_cation_interactions(structure)
324
+ >>> for ring_indices, cation_index in interactions:
325
+ ... print(
326
+ ... structure.res_name[ring_indices[0]],
327
+ ... structure.res_name[cation_index]
328
+ ... )
329
+ TYR ACH
330
+ TRP ACH
331
+ """
332
+ if atoms.bonds is None:
333
+ raise BadStructureError("Structure must have an associated BondList")
334
+
335
+ if atoms.charge is None:
336
+ raise BadStructureError(
337
+ "Structure must have a 'charge' annotation to identify cations."
338
+ )
339
+
340
+ rings = find_aromatic_rings(atoms)
341
+ if len(rings) == 0:
342
+ return []
343
+
344
+ cation_mask = atoms.charge > 0
345
+ cation_indices = np.where(cation_mask)[0]
346
+
347
+ if len(cation_indices) == 0:
348
+ return []
349
+
350
+ # Calculate ring centroids and normals
351
+ ring_centroids = np.array(
352
+ [atoms.coord[atom_indices].mean(axis=0) for atom_indices in rings]
353
+ )
354
+ ring_normals = np.array(
355
+ [_get_ring_normal(atoms.coord[atom_indices]) for atom_indices in rings]
356
+ )
357
+
358
+ cation_coords = atoms.coord[cation_indices]
359
+
360
+ # Create an index array that contains the Cartesian product of all rings and cations
361
+ indices = np.stack(
362
+ [
363
+ np.repeat(np.arange(len(rings)), len(cation_indices)),
364
+ np.tile(np.arange(len(cation_indices)), len(rings)),
365
+ ],
366
+ axis=-1,
367
+ )
368
+
369
+ ## Condition 1: Ring centroids and cations are close enough to each other
370
+ diff = displacement(ring_centroids[indices[:, 0]], cation_coords[indices[:, 1]])
371
+ # Use squared distance to avoid time consuming sqrt computation
372
+ sq_distance = vector_dot(diff, diff)
373
+ is_interacting = sq_distance < distance_cutoff**2
374
+ indices = indices[is_interacting]
375
+
376
+ ## Condition 2: Angle between ring normal and centroid-cation vector
377
+ diff = displacement(ring_centroids[indices[:, 0]], cation_coords[indices[:, 1]])
378
+ norm_vector(diff)
379
+ angles = _minimum_angle(ring_normals[indices[:, 0]], diff)
380
+ is_interacting = _is_within_tolerance(angles, 0, angle_tol)
381
+ indices = indices[is_interacting]
382
+
383
+ # Only return pairs where all conditions were fulfilled
384
+ return [(rings[ring_i], cation_indices[cation_j]) for ring_i, cation_j in indices]
385
+
386
+
271
387
  def _get_ring_normal(ring_coord):
272
388
  """
273
389
  Get the normal vector perpendicular to the ring plane.
Binary file
biotite/version.py CHANGED
@@ -17,5 +17,5 @@ __version__: str
17
17
  __version_tuple__: VERSION_TUPLE
18
18
  version_tuple: VERSION_TUPLE
19
19
 
20
- __version__ = version = '1.3.0'
21
- __version_tuple__ = version_tuple = (1, 3, 0)
20
+ __version__ = version = '1.4.0'
21
+ __version_tuple__ = version_tuple = (1, 4, 0)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: biotite
3
- Version: 1.3.0
3
+ Version: 1.4.0
4
4
  Summary: A comprehensive library for computational molecular biology
5
5
  Project-URL: homepage, https://www.biotite-python.org
6
6
  Project-URL: repository, https://github.com/biotite-dev/biotite
@@ -2,7 +2,7 @@ biotite/__init__.py,sha256=gzqOy5P7Zf667uYuUwqjLLW0GiBdqc23NLcZaXMuHKw,563
2
2
  biotite/copyable.py,sha256=Qnja44k58dAc3WkIUM0svqrbvqO9pSV-Atr9MUJSoCI,1968
3
3
  biotite/file.py,sha256=ktpRUk0OH5_wKIJHtCX3zDjXeJ3eQ4ycDjpp7HttUYM,6977
4
4
  biotite/setup_ccd.py,sha256=9StRz4qPVdTJNrPUoA_liwvkKnTb8O0U8PDxt1tDVwc,6248
5
- biotite/version.py,sha256=gjq5-5A1fJztrO0JABjZYm7eEvg0FysPGOYPEkyBwM8,532
5
+ biotite/version.py,sha256=mQpvfIn8roT-O8LS07P1pK-7O17jWuq_vL-TTh8VVvs,532
6
6
  biotite/visualize.py,sha256=z4lBfJAt5YxGqtgxgcLHKMwVcE4u6fqExEyrOHcSiaY,14330
7
7
  biotite/application/__init__.py,sha256=Rei3EwlpCnC_cJNQoTJYY_Gw_qLdsqgnRxV5b1slHJU,3148
8
8
  biotite/application/application.py,sha256=J9ydVvN_5C3nYuLl12EPBhM67i0GQm6MPy0F1K-jdgc,8372
@@ -65,11 +65,11 @@ biotite/interface/pymol/__init__.py,sha256=fL56vVJ0Ro-3aftoE3lBVRmwPRl2iHWhsnSEB
65
65
  biotite/interface/pymol/cgo.py,sha256=naAQ-5R2wpsbXy3UkjkFvGkzRx8xVr53qlGpltyRO8I,10205
66
66
  biotite/interface/pymol/convert.py,sha256=mukBkBjGO0kv5Ec_ALG9i3BPYUCap1Zf1LiIJvBLrZw,6229
67
67
  biotite/interface/pymol/display.py,sha256=R8vgVRoByPr_mkhm81fJdtzyZCMzRj8jyfviNPAfTLU,8725
68
- biotite/interface/pymol/object.py,sha256=t_sT7lu1Vh_jC0uzzv7ydSf02jzeClT4tfiPN9fIMyY,45419
68
+ biotite/interface/pymol/object.py,sha256=-eegu5qwye8_WE_89baCsk7nfqeHpQo50VoXPa4dCjA,45508
69
69
  biotite/interface/pymol/shapes.py,sha256=w0_fBczmPtI3tQrmrwOp8owg6WUfhX3qXhKXibtfwBA,6244
70
70
  biotite/interface/pymol/startup.py,sha256=vHdpd3RusQXJr9PSPbJxWbo8CuJSN4YpecNCM0az1Ak,4788
71
71
  biotite/interface/rdkit/__init__.py,sha256=0hPcE6j-HKorkcFq3thhx6k2BEjeo0bbwuCGTEqB5YI,551
72
- biotite/interface/rdkit/mol.py,sha256=GH0iSrs54qNWSZbVjPw3T0hjJbTWkCFjTwpq8ef7XTI,20134
72
+ biotite/interface/rdkit/mol.py,sha256=EbNDkS0EqjJ6IxDIJHrAjMParbIDkfZpjjRY9R9L5M8,20119
73
73
  biotite/sequence/__init__.py,sha256=fUoOYLTavBTicHu-JCQkvrCAyRfKRspfCItufOBDGGE,3507
74
74
  biotite/sequence/alphabet.py,sha256=rI3KC_U05_gdLdXypGDicmJWvU2ruISMlCxr-NGPi0E,17782
75
75
  biotite/sequence/annotation.py,sha256=XeVjiagSR5aVIRywAIl927Iwdgni4VKj7SyQJjP9Wkw,30541
@@ -241,12 +241,12 @@ biotite/sequence/phylo/upgma.pyx,sha256=llgc-9fEOU-D1a-ESMSIAoPjHvsd_L0-GnwkL-uz
241
241
  biotite/structure/__init__.py,sha256=nTlutppQQCPYYPH0MXB104--L70rOPUj6P8CcRDyUKA,5623
242
242
  biotite/structure/atoms.py,sha256=x6SnfqCTZIWWReEh62YDUTF3sPjjJKkldDYqu2XigDk,52406
243
243
  biotite/structure/basepairs.py,sha256=4IfbB0yE7-a1cGnMmYwpCu53zVBiJBOyGJ-TxzzigmY,51311
244
- biotite/structure/bonds.pyx,sha256=mFJxrbNtGDHQWdSD5KwEJgGsIsfZBo8B18C4DvknYvo,71734
245
- biotite/structure/box.py,sha256=ryQDQc0PkuPHfk7dRdMMVUreKWGhbZGPOZZUwhtCaW4,24112
244
+ biotite/structure/bonds.pyx,sha256=b3MKKo_ttNUNfoDCCaxrYKNhpNgwOcPdGe2pd-ZrreA,73827
245
+ biotite/structure/box.py,sha256=8JjPHYdc0_SQsMSb6i05GmghW8O0KQZ20ZrSgtKYuXo,24120
246
246
  biotite/structure/celllist.pyx,sha256=tZXwbuN1QACGG2lHiA71ZMtfcdcvypoxCyRdodKFvyU,35427
247
247
  biotite/structure/chains.py,sha256=Xv9VwUQbko6Yg7zCqwNAUCm7x6qTciQ0NqUUSRYmswo,8600
248
248
  biotite/structure/charges.pyx,sha256=ZqQeMaSr0kjgBWRN_mYWBXV6wYtglADRrvY4ZQ0KKSU,19341
249
- biotite/structure/compare.py,sha256=h6v6pAVxLiDKzeEbFltI9OuLtVBNVCAmFU7m8whFcKE,28034
249
+ biotite/structure/compare.py,sha256=Sy9xVcDY0sELBHcVfvXV9fn6OawgUjGGyKOTnWwavqM,28120
250
250
  biotite/structure/density.py,sha256=bCRVos_1LF2Uqq8fmVnZNhRUdDupdTXgPem-bkca24s,4306
251
251
  biotite/structure/dotbracket.py,sha256=1aOy386N6H666YK_vVaE1Ed28Xx9-OlW9jWLI51v0U4,7411
252
252
  biotite/structure/error.py,sha256=NaQBi7CGUYpa7-Y9t0We8iVEJwqpsNWLXkr1PTBKlxQ,822
@@ -260,7 +260,7 @@ biotite/structure/pseudoknots.py,sha256=8kknr_m4eUsVdJUe2gPRofWkWP9NYgOU-NAmJw2o
260
260
  biotite/structure/rdf.py,sha256=XAkbnbfk0wx_cIlrDYXBMUzzShgICUCpyPPfTNviWBQ,8349
261
261
  biotite/structure/repair.py,sha256=MqWQTIOH67giGQ-_yRFARa0uqNFRHHOb-PwSBkdzUL4,7298
262
262
  biotite/structure/residues.py,sha256=3g3b1SBh-euil6wKg-WSNJWwhhsUp2BUAI68-Yhgu14,22378
263
- biotite/structure/rings.py,sha256=2lkqhvlR1ENvmbXpzXg5stzh1mVfLwSXGBSLrtXUzYY,11781
263
+ biotite/structure/rings.py,sha256=XF4wH_nCIswVgtC22gF81nxDvwyWO2gRKtNLoJgxiIs,16035
264
264
  biotite/structure/sasa.pyx,sha256=Vp-WbGMa2HduTf9KDOoR4IQnarUGemVqjVyn_PbFGhU,13239
265
265
  biotite/structure/segments.py,sha256=ZfU1WtimhcvXy0tWv7AYbW1wis04_DtxC4Eal2u2JrM,10033
266
266
  biotite/structure/sequence.py,sha256=RFayTEC85_ETGq0mQVX0gy3tZU6-dTQStyReVzauTqU,4355
@@ -287,7 +287,7 @@ biotite/structure/info/atom_masses.json,sha256=DJ2YMCN4Fvn0zzT91N6TotoLMPm_EkeOF
287
287
  biotite/structure/info/atoms.py,sha256=rBmzMqUffYmKuQVVHFEz-SaaFSrsRSLFoMwa8dzYYZg,3100
288
288
  biotite/structure/info/bonds.py,sha256=7qp4EYo61x6j7OK9Eyolh7Pmg4PU8Rodr5_KfmnyzP8,4793
289
289
  biotite/structure/info/ccd.py,sha256=o0Jb7YYJbYpyljfNGiqXF6bXP7IUlCY3cRMQj1EyWvg,6051
290
- biotite/structure/info/components.bcif,sha256=ouff7fpRp47Fq6CYsKBZlLpHkaN6x6X6q-dVnRs7uro,59990720
290
+ biotite/structure/info/components.bcif,sha256=y9ysqudHo8BQmtqI5JEMnEFXQtG1yt1X9WBtyw8yD2Y,60736935
291
291
  biotite/structure/info/groups.py,sha256=0NTOaFgvqf-yWTMNiZguVx6i0sVWXB-AhnPyhuRcK5o,3312
292
292
  biotite/structure/info/masses.py,sha256=s8heq0bG33-6NTeRqA2WpoOqUy-mwQTfM5a7CWA0YE0,4676
293
293
  biotite/structure/info/misc.py,sha256=_-TSbBsZ70fv3Kcwa-28LTOOZpLJZkHE23XjvysLdF4,3565
@@ -311,44 +311,44 @@ biotite/structure/io/netcdf/__init__.py,sha256=BVUimn1EklpgdoCFE5sC-H4E47exDe_xI
311
311
  biotite/structure/io/netcdf/file.py,sha256=8jht6qCgw0Q7q1-WaQ1iCBjc1QLzjrwpppe3uBZhZ1Y,2256
312
312
  biotite/structure/io/pdb/__init__.py,sha256=UqePAxP48oww8SEexxr6JlugRua8wgOryAvxmHd58f4,753
313
313
  biotite/structure/io/pdb/convert.py,sha256=EN9-10tp_ZZ199lhJNHMUA7jp1IO0FfRaxRHD20wSNw,14188
314
- biotite/structure/io/pdb/file.py,sha256=ncGCHbdrk4jlC0TkOt6HenEgOMcYUugPP5UYwu0xs6I,54991
314
+ biotite/structure/io/pdb/file.py,sha256=TsOj8f3L6PgoRpYer3QAlWUCxiExtSLmU3VDd5B8iaE,55451
315
315
  biotite/structure/io/pdb/hybrid36.pyx,sha256=-feGX_k9H7uAfbqmy37PvlWbuij-mknpbCR2ot1l4kk,8277
316
316
  biotite/structure/io/pdbqt/__init__.py,sha256=JiBnbhNHevtmVIwEeYBubOzK5xu1d9dSny7Zf3Aas74,456
317
317
  biotite/structure/io/pdbqt/convert.py,sha256=M2SytCYO2ihvpcUQCqz4PD4DAKwCPxcjGbhFhJjbDZY,4121
318
318
  biotite/structure/io/pdbqt/file.py,sha256=Jl4DiewnIGd9UeVruhTxkZaclIW94aYpMDBHB37PVtc,27326
319
319
  biotite/structure/io/pdbx/__init__.py,sha256=0G68Qdc29KdXt0R9di2uBv4r173kuXXqGCbMtDdth0E,740
320
- biotite/structure/io/pdbx/bcif.py,sha256=cpl55pbDDlQKWZDkTZsgMDZKWv303AvedV3-n5Y05OA,21808
321
- biotite/structure/io/pdbx/cif.py,sha256=7jaC3DueZ0m738nFGYfkMkseXDiMhiBKWF0Gx_fk6AI,35956
320
+ biotite/structure/io/pdbx/bcif.py,sha256=RP9WdnD2nmqiDarNyjOm9FgnEbnLY1Yvm-DM8DyCykk,21921
321
+ biotite/structure/io/pdbx/cif.py,sha256=wnFthOoyIugjAUoYVddaP_1lSEbnWNf5NM564fMFER4,36069
322
322
  biotite/structure/io/pdbx/component.py,sha256=MiVzeqTI9FHl8hbbpQkOGmlHyhtmwW4V18nQVcL-Quk,8649
323
- biotite/structure/io/pdbx/compress.py,sha256=r8Odnftx_k5CQgkpJ0tvfQkbQwTjaWVjWtyef4DtI7M,13668
324
- biotite/structure/io/pdbx/convert.py,sha256=QSv6O0sEE1XKrzqVQ_bjIKrziSGxOJzJCuBZKEQ1Ho0,81994
323
+ biotite/structure/io/pdbx/compress.py,sha256=GDUUtvaSSSQnL1ujLLIopP83JrGudDGpD1xQdesB6EI,13668
324
+ biotite/structure/io/pdbx/convert.py,sha256=mQTtz-cE5JSkDMArs9G2MFAzQuoZMtUNoef_PJyyvT8,82018
325
325
  biotite/structure/io/pdbx/encoding.pyx,sha256=mZHuUc9ScdwXlwSnCwD9UiUzArW8Dqf-9Yyu5xYms5w,33174
326
326
  biotite/structure/io/trr/__init__.py,sha256=xp4FpuAy_ESqzVWyugRXUHZ0iF8j5ox6EfCxSWhkcUY,372
327
327
  biotite/structure/io/trr/file.py,sha256=XcdYOf9M2tjRXMM87p9U7jMdQb0PlTFvDHXHgSdjbl8,1278
328
328
  biotite/structure/io/xtc/__init__.py,sha256=P17UBQnG-KmOlScHp46FXAb5i7d-ZCMlyljcOkFolMw,370
329
329
  biotite/structure/io/xtc/file.py,sha256=X4iIDjAWn1ZySg3NjbPeSMNByoyH1y8asmtqcBYycpE,1278
330
- biotite/sequence/codec.cp311-win_amd64.pyd,sha256=kkxrOof80yPlDl4JX6Yx74l_w0ZXFUiGar9FH59hdro,247296
331
- biotite/structure/bonds.cp311-win_amd64.pyd,sha256=svuaarGY80VRu_zrR47lMZwuZ81zJgaruRbeNFx8fyw,490496
332
- biotite/structure/celllist.cp311-win_amd64.pyd,sha256=e8f_otkWA1vEvtBJNLvplYNrbV5jZ2A8xwHZZW4Ouzk,260096
333
- biotite/structure/charges.cp311-win_amd64.pyd,sha256=xEp1hkRKHFV8TMKAHWp-h3vvLUTbXkGztKVuPfgwUCg,217088
334
- biotite/structure/sasa.cp311-win_amd64.pyd,sha256=z5XvXU5j-C4p9DgTLmltSFEyag_rGcvinv7ZBeBCL4U,194048
335
- biotite/sequence/align/banded.cp311-win_amd64.pyd,sha256=RnEGLttmPc2T4SLcuHF5Dt1pfqkicDvr1f3psMefZHg,497152
336
- biotite/sequence/align/kmeralphabet.cp311-win_amd64.pyd,sha256=u8fj0jv3E2rwV6RaepVhUnQX6B-O7DWzXLi_qJdz6_Q,335872
337
- biotite/sequence/align/kmersimilarity.cp311-win_amd64.pyd,sha256=8epdgmNBDU1YDSmE-KZfzc2MrH2CL5VwkAM2NVs7lMc,175616
338
- biotite/sequence/align/kmertable.cp311-win_amd64.pyd,sha256=CKb4j7sjsGQ1MzPOFI1ShUM0gP5lGwkF9a6X8HuSWE8,603648
339
- biotite/sequence/align/localgapped.cp311-win_amd64.pyd,sha256=7wAcebzNXtYh6X7KUjqhLvuh453oYZeXcnh5ZyyyraI,992768
340
- biotite/sequence/align/localungapped.cp311-win_amd64.pyd,sha256=84obiAq4iUL-M8I_AeC_5-vEu2VwwMealGhdXQFaoDU,261632
341
- biotite/sequence/align/multiple.cp311-win_amd64.pyd,sha256=5Mo3vmVXMhpXC_5lUys91wBwaD5I4LzXOuTSz88_CGc,415232
342
- biotite/sequence/align/pairwise.cp311-win_amd64.pyd,sha256=PU9oAdvXz4DmWgeXqErCBaDsKcj5868JnMbSFHF2bUY,537088
343
- biotite/sequence/align/permutation.cp311-win_amd64.pyd,sha256=LX0ZJOqmJiHZmDKNm5KX0YV05IFP-nX66Vr5chTYDB8,186368
344
- biotite/sequence/align/selector.cp311-win_amd64.pyd,sha256=VqfYJS1N3LO-_o2Ux_to7lZ9xMxVlxXANO6rlJ7DDh4,258048
345
- biotite/sequence/align/tracetable.cp311-win_amd64.pyd,sha256=i1cSSu1fsy1IgmNNaVFkV7_6KpLBE4sHWh662Bk9fcw,152064
346
- biotite/sequence/phylo/nj.cp311-win_amd64.pyd,sha256=gEjPaqC9BtSRBi8yEgpcrrap2tLb4PwjQIxYzA35S3o,172544
347
- biotite/sequence/phylo/tree.cp311-win_amd64.pyd,sha256=9NVUquqHYIzFMniGgtAIONBaBxfuw_uOTFfnG7OHDGM,211456
348
- biotite/sequence/phylo/upgma.cp311-win_amd64.pyd,sha256=WQPEqEHLo8Uc-gmJI1it_Zx5OkChdO1OzD8VCwxe12Q,165376
349
- biotite/structure/io/pdb/hybrid36.cp311-win_amd64.pyd,sha256=p6sURtgr-kChAA1lCw5w8Buh-p7hY5KOI_fkGYsCJlg,153088
350
- biotite/structure/io/pdbx/encoding.cp311-win_amd64.pyd,sha256=_xuqRjpI_TbwxdC2UHQvyfV4DM5qBvDB0R1jTdYRhPo,1013248
351
- biotite-1.3.0.dist-info/METADATA,sha256=gcnHDjnEHlvw3vQKLx2ZpW64zoh-snCRfNU0n0erVEw,5525
352
- biotite-1.3.0.dist-info/WHEEL,sha256=hBfdzPpFqdGjAw_HiLXeq0mZQ5PGB410nwD8_9kDykc,97
353
- biotite-1.3.0.dist-info/licenses/LICENSE.rst,sha256=wDDtR8LuBedgqNIor2N5YGv4nj9flZltCDSu2fhjGbo,1564
354
- biotite-1.3.0.dist-info/RECORD,,
330
+ biotite/sequence/codec.cp311-win_amd64.pyd,sha256=_qHa1kyvBM4n7wZhPNr_raZyTtWyh_hJ1luiJMAP7HM,227840
331
+ biotite/structure/bonds.cp311-win_amd64.pyd,sha256=ovVj3b9Xu_V4GM08hyEKKT2AYjyVHysSR2fsGQ6yDY4,451072
332
+ biotite/structure/celllist.cp311-win_amd64.pyd,sha256=vnp20quHYBW-YN54HHc599jqquoBOvdfO16wvDN6M1I,241152
333
+ biotite/structure/charges.cp311-win_amd64.pyd,sha256=p5BgnMn9JIuOr6x-B1seMHAWENAeJPaWQktvEHuofmY,198144
334
+ biotite/structure/sasa.cp311-win_amd64.pyd,sha256=Sb1yqVYb4IPwEjeumWxYYP6MYNoElEgGxds3T-DuFMA,178176
335
+ biotite/sequence/align/banded.cp311-win_amd64.pyd,sha256=vFWN8D6u_5xPisrn7An22BlzqUZ-Tb48xmBgXoyWMC8,437760
336
+ biotite/sequence/align/kmeralphabet.cp311-win_amd64.pyd,sha256=hU6Qi8_DlVAn6YkQ_RWtxjHb4SI7LCQ-GZuPYMZLdiE,304128
337
+ biotite/sequence/align/kmersimilarity.cp311-win_amd64.pyd,sha256=5WcnNfb3Yi5vuunMnIiXKl_m_VXD9NVP0n-RSoFNYYc,159744
338
+ biotite/sequence/align/kmertable.cp311-win_amd64.pyd,sha256=9lns_qHGxyvNU-qG6jmdH8cNrXPENZJIXovEY12dDeg,565248
339
+ biotite/sequence/align/localgapped.cp311-win_amd64.pyd,sha256=0bvzgjYQEFh3p-lUp-g03wat5ASw_QTgQKOx-eR0dp4,886784
340
+ biotite/sequence/align/localungapped.cp311-win_amd64.pyd,sha256=ztQtkaNEZxPjRolmSyA-oX48zVC2fdDW-Kf36mUqT1A,236032
341
+ biotite/sequence/align/multiple.cp311-win_amd64.pyd,sha256=7MUCdmVSptOVdTAR1jkLxVcXoJaLIwjjEN1LrUMANVM,372224
342
+ biotite/sequence/align/pairwise.cp311-win_amd64.pyd,sha256=P4zh3eW-DMROp5FlhFUX_BSoDaHpL-fCGw-NzDv7HOM,482304
343
+ biotite/sequence/align/permutation.cp311-win_amd64.pyd,sha256=aQJ8eIv51gwiKRYHqvlrOLcSjC-04VBVw1cbQrGMCow,169984
344
+ biotite/sequence/align/selector.cp311-win_amd64.pyd,sha256=2u_agFHdFGRt54PtN98TBVktRVPs0AWcUvQ82YLj_N4,232448
345
+ biotite/sequence/align/tracetable.cp311-win_amd64.pyd,sha256=PX_x3P4ISWGkMFo09uiNx31Euf94a_yrr7kS_4fBBmM,140288
346
+ biotite/sequence/phylo/nj.cp311-win_amd64.pyd,sha256=hqh03Z-12Ca-A37iEbVGOrkVOTFoFl1Sa3W-VmuO0js,159744
347
+ biotite/sequence/phylo/tree.cp311-win_amd64.pyd,sha256=qynNIltZr8PSI78APPkU0qjCR3ANvF5OSpUG2DvLuM0,193024
348
+ biotite/sequence/phylo/upgma.cp311-win_amd64.pyd,sha256=TjWAmInwmPTJyRbdZg6udDtDgi7tlgGptRbEFJ8r_Zg,152576
349
+ biotite/structure/io/pdb/hybrid36.cp311-win_amd64.pyd,sha256=nqf3og2d649rwiwjNZ9Exi7Ss9oJGnnmnpTgwgdSVWE,141824
350
+ biotite/structure/io/pdbx/encoding.cp311-win_amd64.pyd,sha256=nUw8kcZmdfKkiIFUA9VTXVy1dmsS1ybc7CnmLMOmMQ0,928768
351
+ biotite-1.4.0.dist-info/METADATA,sha256=NZDvKedU4WJkuZIOpWAI3GXJIdbfP5jDh_CbaLSLfgU,5525
352
+ biotite-1.4.0.dist-info/WHEEL,sha256=hBfdzPpFqdGjAw_HiLXeq0mZQ5PGB410nwD8_9kDykc,97
353
+ biotite-1.4.0.dist-info/licenses/LICENSE.rst,sha256=wDDtR8LuBedgqNIor2N5YGv4nj9flZltCDSu2fhjGbo,1564
354
+ biotite-1.4.0.dist-info/RECORD,,