RNApolis 0.3.17__tar.gz → 0.4.0__tar.gz
Sign up to get free protection for your applications and to get access to all the features.
- {rnapolis-0.3.17/src/RNApolis.egg-info → rnapolis-0.4.0}/PKG-INFO +2 -1
- {rnapolis-0.3.17 → rnapolis-0.4.0}/setup.py +2 -1
- {rnapolis-0.3.17 → rnapolis-0.4.0/src/RNApolis.egg-info}/PKG-INFO +2 -1
- {rnapolis-0.3.17 → rnapolis-0.4.0}/src/RNApolis.egg-info/requires.txt +1 -0
- {rnapolis-0.3.17 → rnapolis-0.4.0}/src/rnapolis/annotator.py +3 -4
- {rnapolis-0.3.17 → rnapolis-0.4.0}/src/rnapolis/clashfinder.py +1 -2
- {rnapolis-0.3.17 → rnapolis-0.4.0}/src/rnapolis/common.py +0 -1
- {rnapolis-0.3.17 → rnapolis-0.4.0}/src/rnapolis/metareader.py +0 -1
- {rnapolis-0.3.17 → rnapolis-0.4.0}/src/rnapolis/molecule_filter.py +16 -4
- {rnapolis-0.3.17 → rnapolis-0.4.0}/src/rnapolis/parser.py +82 -33
- {rnapolis-0.3.17 → rnapolis-0.4.0}/src/rnapolis/rfam_folder.py +0 -1
- {rnapolis-0.3.17 → rnapolis-0.4.0}/src/rnapolis/tertiary.py +28 -5
- {rnapolis-0.3.17 → rnapolis-0.4.0}/tests/test_annotator.py +1 -3
- {rnapolis-0.3.17 → rnapolis-0.4.0}/tests/test_bugfixes.py +13 -2
- {rnapolis-0.3.17 → rnapolis-0.4.0}/tests/test_common.py +1 -2
- {rnapolis-0.3.17 → rnapolis-0.4.0}/tests/test_rfam_folder.py +0 -1
- {rnapolis-0.3.17 → rnapolis-0.4.0}/tests/test_tertiary.py +4 -4
- {rnapolis-0.3.17 → rnapolis-0.4.0}/LICENSE +0 -0
- {rnapolis-0.3.17 → rnapolis-0.4.0}/README.md +0 -0
- {rnapolis-0.3.17 → rnapolis-0.4.0}/pyproject.toml +0 -0
- {rnapolis-0.3.17 → rnapolis-0.4.0}/setup.cfg +0 -0
- {rnapolis-0.3.17 → rnapolis-0.4.0}/src/RNApolis.egg-info/SOURCES.txt +0 -0
- {rnapolis-0.3.17 → rnapolis-0.4.0}/src/RNApolis.egg-info/dependency_links.txt +0 -0
- {rnapolis-0.3.17 → rnapolis-0.4.0}/src/RNApolis.egg-info/entry_points.txt +0 -0
- {rnapolis-0.3.17 → rnapolis-0.4.0}/src/RNApolis.egg-info/top_level.txt +0 -0
- {rnapolis-0.3.17 → rnapolis-0.4.0}/src/rnapolis/motif_extractor.py +0 -0
- {rnapolis-0.3.17 → rnapolis-0.4.0}/src/rnapolis/transformer.py +0 -0
- {rnapolis-0.3.17 → rnapolis-0.4.0}/src/rnapolis/util.py +0 -0
- {rnapolis-0.3.17 → rnapolis-0.4.0}/tests/test_metareader.py +0 -0
- {rnapolis-0.3.17 → rnapolis-0.4.0}/tests/test_parser.py +0 -0
- {rnapolis-0.3.17 → rnapolis-0.4.0}/tests/test_quadruplexes.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: RNApolis
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.4.0
|
4
4
|
Summary: A Python library containing RNA-related bioinformatics functions and classes
|
5
5
|
Home-page: https://github.com/tzok/rnapolis-py
|
6
6
|
Author: Tomasz Zok
|
@@ -21,6 +21,7 @@ Requires-Dist: mmcif
|
|
21
21
|
Requires-Dist: numpy
|
22
22
|
Requires-Dist: ordered-set
|
23
23
|
Requires-Dist: orjson
|
24
|
+
Requires-Dist: pandas
|
24
25
|
Requires-Dist: pulp
|
25
26
|
Requires-Dist: requests
|
26
27
|
Requires-Dist: scipy
|
@@ -5,7 +5,7 @@ with open("README.md") as f:
|
|
5
5
|
|
6
6
|
setup(
|
7
7
|
name="RNApolis",
|
8
|
-
version="0.
|
8
|
+
version="0.4.0",
|
9
9
|
packages=["rnapolis"],
|
10
10
|
package_dir={"": "src"},
|
11
11
|
author="Tomasz Zok",
|
@@ -42,6 +42,7 @@ setup(
|
|
42
42
|
"numpy",
|
43
43
|
"ordered-set",
|
44
44
|
"orjson",
|
45
|
+
"pandas",
|
45
46
|
"pulp",
|
46
47
|
"requests",
|
47
48
|
"scipy",
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: RNApolis
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.4.0
|
4
4
|
Summary: A Python library containing RNA-related bioinformatics functions and classes
|
5
5
|
Home-page: https://github.com/tzok/rnapolis-py
|
6
6
|
Author: Tomasz Zok
|
@@ -21,6 +21,7 @@ Requires-Dist: mmcif
|
|
21
21
|
Requires-Dist: numpy
|
22
22
|
Requires-Dist: ordered-set
|
23
23
|
Requires-Dist: orjson
|
24
|
+
Requires-Dist: pandas
|
24
25
|
Requires-Dist: pulp
|
25
26
|
Requires-Dist: requests
|
26
27
|
Requires-Dist: scipy
|
@@ -10,8 +10,6 @@ from typing import Dict, List, Optional, Set, Tuple
|
|
10
10
|
import numpy
|
11
11
|
import numpy.typing
|
12
12
|
from ordered_set import OrderedSet
|
13
|
-
from scipy.spatial import KDTree
|
14
|
-
|
15
13
|
from rnapolis.common import (
|
16
14
|
BR,
|
17
15
|
BaseInteractions,
|
@@ -43,6 +41,7 @@ from rnapolis.tertiary import (
|
|
43
41
|
torsion_angle,
|
44
42
|
)
|
45
43
|
from rnapolis.util import handle_input_file
|
44
|
+
from scipy.spatial import KDTree
|
46
45
|
|
47
46
|
HYDROGEN_BOND_MAX_DISTANCE = 4.0
|
48
47
|
HYDROGEN_BOND_ANGLE_RANGE = (50.0, 130.0) # 90 degrees is ideal, so allow +- 40 degrees
|
@@ -149,7 +148,7 @@ def detect_bph_br_classification(
|
|
149
148
|
|
150
149
|
|
151
150
|
def merge_and_clean_bph_br(
|
152
|
-
pairs: List[Tuple[Residue3D, Residue3D, int]]
|
151
|
+
pairs: List[Tuple[Residue3D, Residue3D, int]],
|
153
152
|
) -> Dict[Tuple[Residue3D, Residue3D], OrderedSet[int]]:
|
154
153
|
bph_br_map: Dict[Tuple[Residue3D, Residue3D], OrderedSet[int]] = defaultdict(
|
155
154
|
OrderedSet
|
@@ -588,7 +587,7 @@ def main():
|
|
588
587
|
"-a",
|
589
588
|
"--all-dot-brackets",
|
590
589
|
action="store_true",
|
591
|
-
help="
|
590
|
+
help="(optional) print all dot-brackets, not only optimal one (exclusive with -e/--extended)",
|
592
591
|
)
|
593
592
|
parser.add_argument("-b", "--bpseq", help="(optional) path to output BPSEQ file")
|
594
593
|
parser.add_argument("-c", "--csv", help="(optional) path to output CSV file")
|
@@ -8,11 +8,10 @@ from functools import cached_property
|
|
8
8
|
from typing import List, Optional
|
9
9
|
|
10
10
|
import numpy as np
|
11
|
-
from scipy.spatial import KDTree
|
12
|
-
|
13
11
|
from rnapolis.metareader import read_metadata
|
14
12
|
from rnapolis.parser import read_3d_structure
|
15
13
|
from rnapolis.tertiary import Atom, Residue3D
|
14
|
+
from scipy.spatial import KDTree
|
16
15
|
|
17
16
|
CARBON_RADIUS = 0.6
|
18
17
|
NITROGEN_RADIUS = 0.54
|
@@ -8,7 +8,6 @@ from collections.abc import Sequence
|
|
8
8
|
from dataclasses import dataclass
|
9
9
|
from enum import Enum
|
10
10
|
from functools import cache, cached_property, total_ordering
|
11
|
-
from itertools import permutations
|
12
11
|
from typing import Dict, List, Optional, Tuple
|
13
12
|
|
14
13
|
import graphviz
|
@@ -5,7 +5,6 @@ from typing import List, Set, Tuple
|
|
5
5
|
|
6
6
|
from mmcif.io.IoAdapterPy import IoAdapterPy
|
7
7
|
from mmcif.io.PdbxReader import DataCategory, DataContainer
|
8
|
-
|
9
8
|
from rnapolis.util import handle_input_file
|
10
9
|
|
11
10
|
# Source: https://mmcif.wwpdb.org/dictionaries/mmcif_pdbx_v50.dic/Items/_entity_poly.type.html
|
@@ -158,11 +157,17 @@ def main():
|
|
158
157
|
parser = argparse.ArgumentParser()
|
159
158
|
parser.add_argument(
|
160
159
|
"--type",
|
161
|
-
help="a type of molecule to select (default: polyribonucleotide)",
|
160
|
+
help="a type of molecule to select, you can provide this argument multiple times (default: polyribonucleotide)",
|
162
161
|
action="append",
|
163
162
|
default=["polyribonucleotide"],
|
164
163
|
choices=ENTITY_POLY_TYPES,
|
165
164
|
)
|
165
|
+
parser.add_argument(
|
166
|
+
"--chain",
|
167
|
+
help="a chain ID (label_asym_id) to select, you can provide this argument multiple times (if provided, it overrides the --type argument)",
|
168
|
+
action="append",
|
169
|
+
default=[],
|
170
|
+
)
|
166
171
|
parser.add_argument("path", help="path to a PDBx/mmCIF file")
|
167
172
|
args = parser.parse_args()
|
168
173
|
|
@@ -171,8 +176,15 @@ def main():
|
|
171
176
|
data = adapter.readFile(file.name)
|
172
177
|
output = DataContainer("rnapolis")
|
173
178
|
|
174
|
-
|
175
|
-
|
179
|
+
if args.chain:
|
180
|
+
entity_ids = select_ids(data, "struct_asym", "id", "entity_id", set(args.chain))
|
181
|
+
asym_ids = set(args.chain)
|
182
|
+
else:
|
183
|
+
entity_ids = select_ids(
|
184
|
+
data, "entity_poly", "type", "entity_id", set(args.type)
|
185
|
+
)
|
186
|
+
asym_ids = select_ids(data, "struct_asym", "entity_id", "id", entity_ids)
|
187
|
+
|
176
188
|
auth_asym_ids = select_ids(
|
177
189
|
data, "atom_site", "label_asym_id", "auth_asym_id", asym_ids
|
178
190
|
)
|
@@ -2,7 +2,6 @@ import logging
|
|
2
2
|
from typing import IO, Dict, List, Optional, Tuple, Union
|
3
3
|
|
4
4
|
from mmcif.io.IoAdapterPy import IoAdapterPy
|
5
|
-
|
6
5
|
from rnapolis.common import ResidueAuth, ResidueLabel
|
7
6
|
from rnapolis.tertiary import BASE_ATOMS, Atom, Residue3D, Structure3D
|
8
7
|
|
@@ -13,7 +12,7 @@ logger = logging.getLogger(__name__)
|
|
13
12
|
def read_3d_structure(
|
14
13
|
cif_or_pdb: IO[str], model: Optional[int] = None, nucleic_acid_only: bool = False
|
15
14
|
) -> Structure3D:
|
16
|
-
atoms, modified,
|
15
|
+
atoms, modified, sequence_by_entity, is_nucleic_acid_by_entity = (
|
17
16
|
parse_cif(cif_or_pdb) if is_cif(cif_or_pdb) else parse_pdb(cif_or_pdb)
|
18
17
|
)
|
19
18
|
available_models = {atom.model: None for atom in atoms}
|
@@ -25,7 +24,13 @@ def read_3d_structure(
|
|
25
24
|
atoms = atoms_by_model[model]
|
26
25
|
else:
|
27
26
|
atoms = atoms_by_model[list(available_models.keys())[0]]
|
28
|
-
return group_atoms(
|
27
|
+
return group_atoms(
|
28
|
+
atoms,
|
29
|
+
modified,
|
30
|
+
sequence_by_entity,
|
31
|
+
is_nucleic_acid_by_entity,
|
32
|
+
nucleic_acid_only,
|
33
|
+
)
|
29
34
|
|
30
35
|
|
31
36
|
def is_cif(cif_or_pdb: IO[str]) -> bool:
|
@@ -41,7 +46,8 @@ def parse_cif(
|
|
41
46
|
) -> Tuple[
|
42
47
|
List[Atom],
|
43
48
|
Dict[Union[ResidueLabel, ResidueAuth], str],
|
44
|
-
Dict[
|
49
|
+
Dict[str, str],
|
50
|
+
Dict[str, bool],
|
45
51
|
]:
|
46
52
|
cif.seek(0)
|
47
53
|
|
@@ -49,7 +55,8 @@ def parse_cif(
|
|
49
55
|
data = io_adapter.readFile(cif.name)
|
50
56
|
atoms: List[Atom] = []
|
51
57
|
modified: Dict[Union[ResidueLabel, ResidueAuth], str] = {}
|
52
|
-
|
58
|
+
sequence_by_entity = {}
|
59
|
+
is_nucleic_acid_by_entity = {}
|
53
60
|
|
54
61
|
if data:
|
55
62
|
atom_site = data[0].getObj("atom_site")
|
@@ -60,6 +67,7 @@ def parse_cif(
|
|
60
67
|
for row in atom_site.getRowList():
|
61
68
|
row_dict = dict(zip(atom_site.getAttributeList(), row))
|
62
69
|
|
70
|
+
label_entity_id = row_dict.get("label_entity_id", None)
|
63
71
|
label_chain_name = row_dict.get("label_asym_id", None)
|
64
72
|
label_residue_number = try_parse_int(row_dict.get("label_seq_id", None))
|
65
73
|
label_residue_name = row_dict.get("label_comp_id", None)
|
@@ -128,7 +136,19 @@ def parse_cif(
|
|
128
136
|
else None
|
129
137
|
)
|
130
138
|
|
131
|
-
atoms.append(
|
139
|
+
atoms.append(
|
140
|
+
Atom(
|
141
|
+
label_entity_id,
|
142
|
+
label,
|
143
|
+
auth,
|
144
|
+
model,
|
145
|
+
atom_name,
|
146
|
+
x,
|
147
|
+
y,
|
148
|
+
z,
|
149
|
+
occupancy,
|
150
|
+
)
|
151
|
+
)
|
132
152
|
|
133
153
|
if mod_residue:
|
134
154
|
for row in mod_residue.getRowList():
|
@@ -179,17 +199,24 @@ def parse_cif(
|
|
179
199
|
for row in entity_poly.getRowList():
|
180
200
|
row_dict = dict(zip(entity_poly.getAttributeList(), row))
|
181
201
|
|
182
|
-
|
202
|
+
entity_id = row_dict.get("entity_id", None)
|
203
|
+
type_ = row_dict.get("type", None)
|
183
204
|
pdbx_seq_one_letter_code_can = row_dict.get(
|
184
205
|
"pdbx_seq_one_letter_code_can", None
|
185
206
|
)
|
186
207
|
|
187
|
-
if
|
188
|
-
|
189
|
-
|
190
|
-
|
208
|
+
if entity_id and type_:
|
209
|
+
is_nucleic_acid_by_entity[entity_id] = type_ in (
|
210
|
+
"peptide nucleic acid",
|
211
|
+
"polydeoxyribonucleotide",
|
212
|
+
"polydeoxyribonucleotide/polyribonucleotide hybrid",
|
213
|
+
"polyribonucleotide",
|
214
|
+
)
|
215
|
+
|
216
|
+
if entity_id and pdbx_seq_one_letter_code_can:
|
217
|
+
sequence_by_entity[entity_id] = pdbx_seq_one_letter_code_can
|
191
218
|
|
192
|
-
return atoms, modified,
|
219
|
+
return atoms, modified, sequence_by_entity, is_nucleic_acid_by_entity
|
193
220
|
|
194
221
|
|
195
222
|
def parse_pdb(
|
@@ -197,7 +224,8 @@ def parse_pdb(
|
|
197
224
|
) -> Tuple[
|
198
225
|
List[Atom],
|
199
226
|
Dict[Union[ResidueLabel, ResidueAuth], str],
|
200
|
-
Dict[
|
227
|
+
Dict[str, str],
|
228
|
+
Dict[str, bool],
|
201
229
|
]:
|
202
230
|
pdb.seek(0)
|
203
231
|
atoms: List[Atom] = []
|
@@ -223,7 +251,7 @@ def parse_pdb(
|
|
223
251
|
auth = ResidueAuth(
|
224
252
|
chain_identifier, residue_number, insertion_code, residue_name
|
225
253
|
)
|
226
|
-
atoms.append(Atom(None, auth, model, atom_name, x, y, z, occupancy))
|
254
|
+
atoms.append(Atom(None, None, auth, model, atom_name, x, y, z, occupancy))
|
227
255
|
elif line.startswith("MODRES"):
|
228
256
|
original_name = line[12:15]
|
229
257
|
chain_identifier = line[16]
|
@@ -235,13 +263,14 @@ def parse_pdb(
|
|
235
263
|
)
|
236
264
|
modified[auth] = standard_residue_name
|
237
265
|
|
238
|
-
return atoms, modified, {}
|
266
|
+
return atoms, modified, {}, {}
|
239
267
|
|
240
268
|
|
241
269
|
def group_atoms(
|
242
270
|
atoms: List[Atom],
|
243
271
|
modified: Dict[Union[ResidueLabel, ResidueAuth], str],
|
244
|
-
|
272
|
+
sequence_by_entity: Dict[str, str],
|
273
|
+
is_nucleic_acid_by_entity: Dict[str, bool],
|
245
274
|
nucleic_acid_only: bool,
|
246
275
|
) -> Structure3D:
|
247
276
|
if not atoms:
|
@@ -259,28 +288,45 @@ def group_atoms(
|
|
259
288
|
label = key_previous[0]
|
260
289
|
auth = key_previous[1]
|
261
290
|
model = key_previous[2]
|
291
|
+
entity_id = residue_atoms[-1].entity_id
|
262
292
|
name = get_residue_name(auth, label, modified)
|
263
|
-
one_letter_name = get_one_letter_name(
|
264
|
-
|
293
|
+
one_letter_name = get_one_letter_name(
|
294
|
+
entity_id, label, sequence_by_entity, name
|
295
|
+
)
|
296
|
+
|
297
|
+
if one_letter_name not in "ACGUTN":
|
265
298
|
one_letter_name = detect_one_letter_name(residue_atoms)
|
266
|
-
|
267
|
-
|
299
|
+
|
300
|
+
residues.append(
|
301
|
+
Residue3D(label, auth, model, one_letter_name, tuple(residue_atoms))
|
268
302
|
)
|
269
|
-
|
270
|
-
residues.append(residue)
|
303
|
+
|
271
304
|
key_previous = key
|
272
305
|
residue_atoms = [atom]
|
273
306
|
|
274
307
|
label = key_previous[0]
|
275
308
|
auth = key_previous[1]
|
276
309
|
model = key_previous[2]
|
310
|
+
entity_id = residue_atoms[-1].entity_id
|
277
311
|
name = get_residue_name(auth, label, modified)
|
278
|
-
one_letter_name = get_one_letter_name(label,
|
279
|
-
|
312
|
+
one_letter_name = get_one_letter_name(entity_id, label, sequence_by_entity, name)
|
313
|
+
|
314
|
+
if one_letter_name not in "ACGUTN":
|
280
315
|
one_letter_name = detect_one_letter_name(residue_atoms)
|
281
|
-
|
282
|
-
|
283
|
-
|
316
|
+
|
317
|
+
residues.append(
|
318
|
+
Residue3D(label, auth, model, one_letter_name, tuple(residue_atoms))
|
319
|
+
)
|
320
|
+
|
321
|
+
if nucleic_acid_only:
|
322
|
+
if is_nucleic_acid_by_entity:
|
323
|
+
residues = [
|
324
|
+
residue
|
325
|
+
for residue in residues
|
326
|
+
if is_nucleic_acid_by_entity[residue.atoms[0].entity_id]
|
327
|
+
]
|
328
|
+
else:
|
329
|
+
residues = [residue for residue in residues if residue.is_nucleotide]
|
284
330
|
|
285
331
|
return Structure3D(residues)
|
286
332
|
|
@@ -305,13 +351,14 @@ def get_residue_name(
|
|
305
351
|
|
306
352
|
|
307
353
|
def get_one_letter_name(
|
308
|
-
|
354
|
+
entity_id: Optional[str],
|
355
|
+
label: Optional[ResidueLabel],
|
356
|
+
sequence_by_entity: Dict[str, str],
|
357
|
+
name: str,
|
309
358
|
) -> str:
|
310
359
|
# try getting the value from _entity_poly first
|
311
|
-
if label is not None:
|
312
|
-
|
313
|
-
if key in sequence:
|
314
|
-
return sequence[key]
|
360
|
+
if entity_id is not None and label is not None and entity_id in sequence_by_entity:
|
361
|
+
return sequence_by_entity[entity_id][label.number - 1]
|
315
362
|
# RNA
|
316
363
|
if len(name) == 1:
|
317
364
|
return name
|
@@ -335,11 +382,13 @@ def detect_one_letter_name(atoms: List[Atom]) -> str:
|
|
335
382
|
) / len(atom_names_expected)
|
336
383
|
score[candidate] = count
|
337
384
|
items = sorted(score.items(), key=lambda kv: kv[1], reverse=True)
|
385
|
+
if items[0][1] == 0:
|
386
|
+
return "?"
|
338
387
|
return items[0][0]
|
339
388
|
|
340
389
|
|
341
390
|
def try_parse_int(s: str) -> Optional[int]:
|
342
391
|
try:
|
343
392
|
return int(s)
|
344
|
-
except:
|
393
|
+
except ValueError:
|
345
394
|
return None
|
@@ -7,7 +7,6 @@ from typing import Dict, List, Optional, Set, Tuple, Union
|
|
7
7
|
|
8
8
|
import numpy
|
9
9
|
import numpy.typing
|
10
|
-
|
11
10
|
from rnapolis.common import (
|
12
11
|
BasePair,
|
13
12
|
BpSeq,
|
@@ -97,6 +96,7 @@ AVERAGE_OXYGEN_PHOSPHORUS_DISTANCE_COVALENT = 1.6
|
|
97
96
|
|
98
97
|
@dataclass(frozen=True, order=True)
|
99
98
|
class Atom:
|
99
|
+
entity_id: Optional[str]
|
100
100
|
label: Optional[ResidueLabel]
|
101
101
|
auth: Optional[ResidueAuth]
|
102
102
|
model: int
|
@@ -129,6 +129,29 @@ class Residue3D(Residue):
|
|
129
129
|
"C": set(["N1", "C2", "O2", "N3", "C4", "N4", "C5", "C6"]),
|
130
130
|
"U": set(["N1", "C2", "O2", "N3", "C4", "O4", "C5", "C6"]),
|
131
131
|
}
|
132
|
+
# Heavy atoms in nucleotide
|
133
|
+
nucleotide_heavy_atoms = (
|
134
|
+
set(
|
135
|
+
[
|
136
|
+
"P",
|
137
|
+
"OP1",
|
138
|
+
"OP2",
|
139
|
+
"O5'",
|
140
|
+
"C5'",
|
141
|
+
"C4'",
|
142
|
+
"O4'",
|
143
|
+
"C3'",
|
144
|
+
"O3'",
|
145
|
+
"C2'",
|
146
|
+
"O2'",
|
147
|
+
"C1'",
|
148
|
+
]
|
149
|
+
)
|
150
|
+
.union(nucleobase_heavy_atoms["A"])
|
151
|
+
.union(nucleobase_heavy_atoms["G"])
|
152
|
+
.union(nucleobase_heavy_atoms["C"])
|
153
|
+
.union(nucleobase_heavy_atoms["U"])
|
154
|
+
)
|
132
155
|
|
133
156
|
def __lt__(self, other):
|
134
157
|
return (self.model, self.chain, self.number, self.icode or " ") < (
|
@@ -177,8 +200,8 @@ class Residue3D(Residue):
|
|
177
200
|
|
178
201
|
@cached_property
|
179
202
|
def is_nucleotide(self) -> bool:
|
180
|
-
return
|
181
|
-
[atom for atom in self.atoms
|
203
|
+
return self.nucleotide_heavy_atoms.intersection(
|
204
|
+
set([atom.name for atom in self.atoms])
|
182
205
|
)
|
183
206
|
|
184
207
|
@cached_property
|
@@ -269,7 +292,7 @@ class Residue3D(Residue):
|
|
269
292
|
logging.error(
|
270
293
|
f"Failed to determine the outermost atom for nucleotide {self}, so an arbitrary atom will be used"
|
271
294
|
)
|
272
|
-
yield Atom(self.label, self.auth, self.model, "UNK", 0.0, 0.0, 0.0, None)
|
295
|
+
yield Atom(None, self.label, self.auth, self.model, "UNK", 0.0, 0.0, 0.0, None)
|
273
296
|
|
274
297
|
def __inner_generator(self):
|
275
298
|
# try to find expected atom name
|
@@ -297,7 +320,7 @@ class Residue3D(Residue):
|
|
297
320
|
logging.error(
|
298
321
|
f"Failed to determine the innermost atom for nucleotide {self}, so an arbitrary atom will be used"
|
299
322
|
)
|
300
|
-
yield Atom(self.label, self.auth, self.model, "UNK", 0.0, 0.0, 0.0, None)
|
323
|
+
yield Atom(None, self.label, self.auth, self.model, "UNK", 0.0, 0.0, 0.0, None)
|
301
324
|
|
302
325
|
|
303
326
|
@dataclass(frozen=True, order=True)
|
@@ -36,9 +36,7 @@ def test_1ehz():
|
|
36
36
|
for bp in interactions[i]
|
37
37
|
if (bp.nt1.full_name, bp.nt2.full_name) == element
|
38
38
|
]
|
39
|
-
assert
|
40
|
-
False
|
41
|
-
), f"Interaction {element} occurs {count} times among {labels[i]} type: {duplicates}"
|
39
|
+
assert False, f"Interaction {element} occurs {count} times among {labels[i]} type: {duplicates}"
|
42
40
|
|
43
41
|
|
44
42
|
def test_8btk():
|
@@ -34,7 +34,7 @@ def test_1DFU():
|
|
34
34
|
assert b1u not in mapping.base_pair_graph[b2g]
|
35
35
|
|
36
36
|
|
37
|
-
# in 4WTI the first residue has only O3' atom
|
37
|
+
# in 4WTI the first residue has only O3' atom, but is stil considered a nucleotide
|
38
38
|
def test_4WTI():
|
39
39
|
with open("tests/4WTI_1_T-P.cif") as f:
|
40
40
|
structure3d = read_3d_structure(f, 1)
|
@@ -42,7 +42,7 @@ def test_4WTI():
|
|
42
42
|
mapping = Mapping2D3D(
|
43
43
|
structure3d, base_interactions.basePairs, base_interactions.stackings, True
|
44
44
|
)
|
45
|
-
assert mapping.dot_bracket == ">strand_T\
|
45
|
+
assert mapping.dot_bracket == ">strand_T\nACGG\n..((\n>strand_P\nCC\n))"
|
46
46
|
|
47
47
|
|
48
48
|
# in 1HMH the bases are oriented in 45 degrees and it caused the program to identify invalid base pair
|
@@ -64,3 +64,14 @@ def test_6INQ():
|
|
64
64
|
assert structure3d.find_residue(None, ResidueAuth("T", 0, None, "DC")) is not None
|
65
65
|
assert structure3d.find_residue(ResidueLabel("O", 126, "DG"), None) is not None
|
66
66
|
assert structure3d.find_residue(None, ResidueAuth("N", 0, None, "DG")) is not None
|
67
|
+
|
68
|
+
|
69
|
+
# in 6g90 from rna3db, the sequence contains Ns which were ignored incorrectly
|
70
|
+
def test_6g90():
|
71
|
+
with open("tests/6g90_1.cif") as f:
|
72
|
+
structure3d = read_3d_structure(f, nucleic_acid_only=True)
|
73
|
+
sequence = "".join([residue.one_letter_name for residue in structure3d.residues])
|
74
|
+
assert (
|
75
|
+
sequence
|
76
|
+
== "AUACUUACCUUAAGAUAUCAGAGGAGAUCAAGAAGUCCUACUGAUCAAACAUGCGCUUCCAAGAAGGACGUUAAGCAUUUAUCAUUGAACGUUCAUUGAACAUUGAUGCAAACUCCUUGGUCACACACACGCGGAAGGCGUGUUUGCUGACGUCCCUUGUUUCAAUCAUUGGUUAACUGAUUUUUGGGGCCCUUUGUUCUUCUGAGAAGUGACACCAAUUGGUGUUAGGGGAGCUGGGGCCUUUCAAAANNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNUUUUGGAAGGUCUUGGUCGGGUGGAUCUUAUAAUUUUUGAUUUA"
|
77
|
+
)
|
@@ -2,7 +2,6 @@ from collections import Counter
|
|
2
2
|
|
3
3
|
from hypothesis import given, settings
|
4
4
|
from hypothesis import strategies as st
|
5
|
-
|
6
5
|
from rnapolis.common import (
|
7
6
|
BaseInteractions,
|
8
7
|
BasePair,
|
@@ -93,7 +92,7 @@ def test_rnapdbee_adapters_api_compliance_structure2d(obj):
|
|
93
92
|
|
94
93
|
def test_bpseq_from_dotbracket():
|
95
94
|
expected = BpSeq.from_file("tests/1ET4-A.bpseq")
|
96
|
-
actual = BpSeq.from_dotbracket(DotBracket.from_file(
|
95
|
+
actual = BpSeq.from_dotbracket(DotBracket.from_file("tests/1ET4-A.dbn"))
|
97
96
|
assert expected == actual
|
98
97
|
|
99
98
|
|
@@ -5,10 +5,10 @@ from rnapolis.tertiary import Atom, torsion_angle
|
|
5
5
|
|
6
6
|
|
7
7
|
def test_torsion_angle():
|
8
|
-
a1 = Atom(None, None, 1, "P", 50.63, 49.73, 50.57, None)
|
9
|
-
a2 = Atom(None, None, 1, "O5'", 50.16, 49.14, 52.02, None)
|
10
|
-
a3 = Atom(None, None, 1, "C5'", 50.22, 49.95, 53.21, None)
|
11
|
-
a4 = Atom(None, None, 1, "C4'", 50.97, 49.23, 54.31, None)
|
8
|
+
a1 = Atom(None, None, None, 1, "P", 50.63, 49.73, 50.57, None)
|
9
|
+
a2 = Atom(None, None, None, 1, "O5'", 50.16, 49.14, 52.02, None)
|
10
|
+
a3 = Atom(None, None, None, 1, "C5'", 50.22, 49.95, 53.21, None)
|
11
|
+
a4 = Atom(None, None, None, 1, "C4'", 50.97, 49.23, 54.31, None)
|
12
12
|
assert math.isclose(
|
13
13
|
math.degrees(torsion_angle(a1, a2, a3, a4)), -127.83976634524326
|
14
14
|
)
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|