uniaf3 0.2.0__py3-none-any.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.
- uniaf3/__init__.py +5 -0
- uniaf3/adapters/__init__.py +119 -0
- uniaf3/adapters/_helpers.py +23 -0
- uniaf3/adapters/alphafold3.py +356 -0
- uniaf3/adapters/alphafold3_server.py +341 -0
- uniaf3/adapters/boltz.py +675 -0
- uniaf3/adapters/chai.py +754 -0
- uniaf3/adapters/protenix.py +707 -0
- uniaf3/cli.py +363 -0
- uniaf3/constant.py +344 -0
- uniaf3/msa.py +420 -0
- uniaf3/schema/__init__.py +31 -0
- uniaf3/schema/alphafold3.py +281 -0
- uniaf3/schema/alphafold3_server.py +265 -0
- uniaf3/schema/base.py +611 -0
- uniaf3/schema/boltz.py +407 -0
- uniaf3/schema/chai.py +359 -0
- uniaf3/schema/protenix.py +391 -0
- uniaf3/utils.py +141 -0
- uniaf3/vendor/Components-smiles-stereo-oe.smi.parquet +0 -0
- uniaf3/vendor/__init__.py +1 -0
- uniaf3/vendor/ccd.py +15 -0
- uniaf3/vendor/chai1_fasta.py +75 -0
- uniaf3/vendor/chai1_glycans.py +99 -0
- uniaf3/vendor/chai1_msa.py +294 -0
- uniaf3/vendor/colabfold_msa.py +289 -0
- uniaf3/vendor/protenix_template.py +212 -0
- uniaf3-0.2.0.dist-info/METADATA +478 -0
- uniaf3-0.2.0.dist-info/RECORD +31 -0
- uniaf3-0.2.0.dist-info/WHEEL +4 -0
- uniaf3-0.2.0.dist-info/entry_points.txt +3 -0
uniaf3/__init__.py
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"""Adapters to convert between UniAF3Config and model-specific configs.
|
|
2
|
+
|
|
3
|
+
Each model has a ``to_*`` and ``from_*`` function pair in its own module under
|
|
4
|
+
``uniaf3.adapters``.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
from uniaf3.adapters.alphafold3 import from_alphafold3, to_alphafold3
|
|
12
|
+
from uniaf3.adapters.alphafold3_server import (
|
|
13
|
+
from_alphafold3_server,
|
|
14
|
+
to_alphafold3_server,
|
|
15
|
+
)
|
|
16
|
+
from uniaf3.adapters.boltz import from_boltz, to_boltz
|
|
17
|
+
from uniaf3.adapters.chai import from_chai, to_chai
|
|
18
|
+
from uniaf3.adapters.protenix import from_protenix, to_protenix
|
|
19
|
+
from uniaf3.schema import (
|
|
20
|
+
AF3Config,
|
|
21
|
+
AF3ServerConfig,
|
|
22
|
+
AnyConfig,
|
|
23
|
+
AnyConfigList,
|
|
24
|
+
BoltzConfig,
|
|
25
|
+
ChaiConfig,
|
|
26
|
+
ProtenixConfig,
|
|
27
|
+
UniAF3Config,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
__all__ = [
|
|
31
|
+
"from_alphafold3",
|
|
32
|
+
"from_alphafold3_server",
|
|
33
|
+
"from_boltz",
|
|
34
|
+
"from_chai",
|
|
35
|
+
"from_protenix",
|
|
36
|
+
"from_uniaf3",
|
|
37
|
+
"to_alphafold3",
|
|
38
|
+
"to_alphafold3_server",
|
|
39
|
+
"to_boltz",
|
|
40
|
+
"to_chai",
|
|
41
|
+
"to_protenix",
|
|
42
|
+
"to_uniaf3",
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def to_uniaf3(
|
|
47
|
+
conf: AnyConfig, *, msa_dir: str | Path = "."
|
|
48
|
+
) -> UniAF3Config | list[UniAF3Config]:
|
|
49
|
+
"""Convert any supported model config to UniAF3Config.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
conf: A config object from any supported model format.
|
|
53
|
+
msa_dir: Directory to save MSA files (used by Boltz and Chai).
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
The equivalent UniAF3Config.
|
|
57
|
+
|
|
58
|
+
Raises:
|
|
59
|
+
TypeError: If the config type is not recognized.
|
|
60
|
+
|
|
61
|
+
"""
|
|
62
|
+
if isinstance(conf, UniAF3Config):
|
|
63
|
+
return conf
|
|
64
|
+
if isinstance(conf, AF3ServerConfig):
|
|
65
|
+
return from_alphafold3_server(conf)
|
|
66
|
+
if isinstance(conf, AF3Config):
|
|
67
|
+
return from_alphafold3(conf)
|
|
68
|
+
if isinstance(conf, BoltzConfig):
|
|
69
|
+
return from_boltz(conf, msa_dir=msa_dir)
|
|
70
|
+
if isinstance(conf, ChaiConfig):
|
|
71
|
+
return from_chai(conf, msa_dir=msa_dir)
|
|
72
|
+
if isinstance(conf, ProtenixConfig):
|
|
73
|
+
return from_protenix(conf)
|
|
74
|
+
raise TypeError(f"Unsupported config type: {type(conf)}")
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def from_uniaf3(
|
|
78
|
+
conf: UniAF3Config | list[UniAF3Config],
|
|
79
|
+
target: type[AnyConfig],
|
|
80
|
+
*,
|
|
81
|
+
name: str = "uniaf3_job",
|
|
82
|
+
msa_dir: str | Path = ".",
|
|
83
|
+
strict: bool = False,
|
|
84
|
+
) -> AnyConfig | AnyConfigList:
|
|
85
|
+
"""Convert a UniAF3Config to a specific model config.
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
conf: The UniAF3Config to convert.
|
|
89
|
+
target: The target config class.
|
|
90
|
+
name: Job name for models that require one (AF3, Protenix).
|
|
91
|
+
msa_dir: Directory to save MSA CSV files (used by Boltz and Chai).
|
|
92
|
+
strict: If True, raise errors for unsupported features.
|
|
93
|
+
|
|
94
|
+
Returns:
|
|
95
|
+
The target model config.
|
|
96
|
+
|
|
97
|
+
Raises:
|
|
98
|
+
TypeError: If the target type is not recognized.
|
|
99
|
+
|
|
100
|
+
"""
|
|
101
|
+
if target is UniAF3Config:
|
|
102
|
+
return conf
|
|
103
|
+
if target is AF3Config:
|
|
104
|
+
if isinstance(conf, list):
|
|
105
|
+
return [to_alphafold3(c, name=name, strict=strict) for c in conf]
|
|
106
|
+
return to_alphafold3(conf, name=name, strict=strict)
|
|
107
|
+
if target is AF3ServerConfig:
|
|
108
|
+
return to_alphafold3_server(conf, name=name, strict=strict)
|
|
109
|
+
if target is BoltzConfig:
|
|
110
|
+
if isinstance(conf, list):
|
|
111
|
+
return [to_boltz(c, msa_dir=msa_dir, strict=strict) for c in conf]
|
|
112
|
+
return to_boltz(conf, msa_dir=msa_dir, strict=strict)
|
|
113
|
+
if target is ChaiConfig:
|
|
114
|
+
if isinstance(conf, list):
|
|
115
|
+
return [to_chai(c, msa_dir=msa_dir, strict=strict) for c in conf]
|
|
116
|
+
return to_chai(conf, msa_dir=msa_dir, strict=strict)
|
|
117
|
+
if target is ProtenixConfig:
|
|
118
|
+
return to_protenix(conf, name=name, strict=strict)
|
|
119
|
+
raise TypeError(f"Unsupported target type: {target}")
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""Shared helpers for adapter modules."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import warnings
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def ensure_list(val: str | list[str]) -> list[str]:
|
|
9
|
+
"""Normalize id field to a list."""
|
|
10
|
+
return val if isinstance(val, list) else [val]
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def warn_lossy_conversion(msg: str):
|
|
14
|
+
"""Emit a warning for lossy conversion behavior."""
|
|
15
|
+
warnings.warn(f"Lossy conversion: {msg}", UserWarning, stacklevel=3)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def err_unsupported_feature(strict: bool, msg: str):
|
|
19
|
+
"""Help handle unsupported features based on the strict flag."""
|
|
20
|
+
if strict:
|
|
21
|
+
raise ValueError(msg)
|
|
22
|
+
else:
|
|
23
|
+
warnings.warn(f"Skipping unsupported feature: {msg}", UserWarning, stacklevel=3)
|
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
"""Adapter for converting between UniAF3Config and AlphaFold3 config."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from uniaf3.adapters._helpers import (
|
|
6
|
+
err_unsupported_feature,
|
|
7
|
+
warn_lossy_conversion,
|
|
8
|
+
)
|
|
9
|
+
from uniaf3.schema.alphafold3 import (
|
|
10
|
+
AF3DNA,
|
|
11
|
+
AF3RNA,
|
|
12
|
+
AF3BondedAtom,
|
|
13
|
+
AF3Config,
|
|
14
|
+
AF3Ligand,
|
|
15
|
+
AF3NucleotideModification,
|
|
16
|
+
AF3Protein,
|
|
17
|
+
AF3ProteinModification,
|
|
18
|
+
AF3SequenceEntry,
|
|
19
|
+
AF3Template,
|
|
20
|
+
)
|
|
21
|
+
from uniaf3.schema.base import (
|
|
22
|
+
Atom,
|
|
23
|
+
AuxiliaryParams,
|
|
24
|
+
CovalentBond,
|
|
25
|
+
Glycan,
|
|
26
|
+
Ligand,
|
|
27
|
+
Polymer,
|
|
28
|
+
PolymerType,
|
|
29
|
+
ProteinSeq,
|
|
30
|
+
SequenceModification,
|
|
31
|
+
StructuralTemplate,
|
|
32
|
+
UniAF3Config,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def to_alphafold3(
|
|
37
|
+
config: UniAF3Config,
|
|
38
|
+
name: str = "uniaf3_job",
|
|
39
|
+
strict: bool = False,
|
|
40
|
+
) -> AF3Config:
|
|
41
|
+
"""Convert a UniAF3Config to an AlphaFold3 config.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
config: UniAF3Config pydantic object.
|
|
45
|
+
name: Job name for the AF3 config.
|
|
46
|
+
strict: If True, raise errors when encountering unsupported features.
|
|
47
|
+
If False, skip unsupported features with warnings.
|
|
48
|
+
|
|
49
|
+
"""
|
|
50
|
+
sequences: list[AF3SequenceEntry] = []
|
|
51
|
+
for seq in config.sequences:
|
|
52
|
+
if isinstance(seq, Glycan):
|
|
53
|
+
# TODO: AF3 does not have a native glycan type. Glycans must be
|
|
54
|
+
# represented as multi-CCD ligands. This requires knowing the
|
|
55
|
+
# component CCD codes, which the chai_str notation may not directly
|
|
56
|
+
# map to.
|
|
57
|
+
err_unsupported_feature(
|
|
58
|
+
strict,
|
|
59
|
+
f"Glycans are not directly supported in AF3: {seq}",
|
|
60
|
+
)
|
|
61
|
+
continue
|
|
62
|
+
|
|
63
|
+
if isinstance(seq, ProteinSeq):
|
|
64
|
+
mods = (
|
|
65
|
+
[
|
|
66
|
+
AF3ProteinModification(ptmType=m.ccd, ptmPosition=m.position)
|
|
67
|
+
for m in seq.modifications
|
|
68
|
+
]
|
|
69
|
+
if seq.modifications
|
|
70
|
+
else None
|
|
71
|
+
)
|
|
72
|
+
protein = AF3Protein(
|
|
73
|
+
id=seq.id,
|
|
74
|
+
sequence=seq.sequence,
|
|
75
|
+
modifications=mods,
|
|
76
|
+
description=seq.description,
|
|
77
|
+
unpairedMsaPath=seq.unpaired_msa,
|
|
78
|
+
pairedMsaPath=seq.paired_msa,
|
|
79
|
+
)
|
|
80
|
+
# Templates
|
|
81
|
+
if seq.templates:
|
|
82
|
+
af3_templates = []
|
|
83
|
+
for tmpl in seq.templates:
|
|
84
|
+
if tmpl.query_chains or tmpl.template_chains:
|
|
85
|
+
warn_lossy_conversion(
|
|
86
|
+
"UniAF3Config.sequences[*].templates.{query_chains,template_chains} are not represented by AF3Config.sequences[*].protein.templates."
|
|
87
|
+
)
|
|
88
|
+
if (
|
|
89
|
+
tmpl.boltz_enable_force
|
|
90
|
+
or tmpl.boltz_template_threshold is not None
|
|
91
|
+
):
|
|
92
|
+
warn_lossy_conversion(
|
|
93
|
+
"UniAF3Config.sequences[*].templates.{boltz_enable_force,boltz_template_threshold} are not represented by AF3Config.sequences[*].protein.templates."
|
|
94
|
+
)
|
|
95
|
+
# TODO: extract a single chain from the template structure
|
|
96
|
+
af3_templates.append(
|
|
97
|
+
AF3Template(
|
|
98
|
+
mmcifPath=tmpl.path,
|
|
99
|
+
queryIndices=tmpl.query_idx or [],
|
|
100
|
+
templateIndices=tmpl.template_idx or [],
|
|
101
|
+
)
|
|
102
|
+
)
|
|
103
|
+
protein.templates = af3_templates
|
|
104
|
+
sequences.append(AF3SequenceEntry(protein=protein))
|
|
105
|
+
|
|
106
|
+
elif isinstance(seq, Polymer):
|
|
107
|
+
if seq.polymer_type == PolymerType.Protein:
|
|
108
|
+
mods = (
|
|
109
|
+
[
|
|
110
|
+
AF3ProteinModification(ptmType=m.ccd, ptmPosition=m.position)
|
|
111
|
+
for m in seq.modifications
|
|
112
|
+
]
|
|
113
|
+
if seq.modifications
|
|
114
|
+
else None
|
|
115
|
+
)
|
|
116
|
+
protein = AF3Protein(
|
|
117
|
+
id=seq.id,
|
|
118
|
+
sequence=seq.sequence,
|
|
119
|
+
modifications=mods,
|
|
120
|
+
description=seq.description,
|
|
121
|
+
)
|
|
122
|
+
sequences.append(AF3SequenceEntry(protein=protein))
|
|
123
|
+
elif seq.polymer_type == PolymerType.DNA:
|
|
124
|
+
mods = (
|
|
125
|
+
[
|
|
126
|
+
AF3NucleotideModification(
|
|
127
|
+
modificationType=m.ccd, basePosition=m.position
|
|
128
|
+
)
|
|
129
|
+
for m in seq.modifications
|
|
130
|
+
]
|
|
131
|
+
if seq.modifications
|
|
132
|
+
else None
|
|
133
|
+
)
|
|
134
|
+
dna = AF3DNA(
|
|
135
|
+
id=seq.id,
|
|
136
|
+
sequence=seq.sequence,
|
|
137
|
+
modifications=mods,
|
|
138
|
+
description=seq.description,
|
|
139
|
+
)
|
|
140
|
+
sequences.append(AF3SequenceEntry(dna=dna))
|
|
141
|
+
elif seq.polymer_type == PolymerType.RNA:
|
|
142
|
+
mods = (
|
|
143
|
+
[
|
|
144
|
+
AF3NucleotideModification(
|
|
145
|
+
modificationType=m.ccd, basePosition=m.position
|
|
146
|
+
)
|
|
147
|
+
for m in seq.modifications
|
|
148
|
+
]
|
|
149
|
+
if seq.modifications
|
|
150
|
+
else None
|
|
151
|
+
)
|
|
152
|
+
rna = AF3RNA(
|
|
153
|
+
id=seq.id,
|
|
154
|
+
sequence=seq.sequence,
|
|
155
|
+
modifications=mods,
|
|
156
|
+
description=seq.description,
|
|
157
|
+
)
|
|
158
|
+
sequences.append(AF3SequenceEntry(rna=rna))
|
|
159
|
+
|
|
160
|
+
elif isinstance(seq, Ligand):
|
|
161
|
+
if seq.ccd:
|
|
162
|
+
lig = AF3Ligand(
|
|
163
|
+
id=seq.id,
|
|
164
|
+
ccdCodes=seq.ccd,
|
|
165
|
+
description=seq.description,
|
|
166
|
+
)
|
|
167
|
+
elif seq.smiles:
|
|
168
|
+
lig = AF3Ligand(
|
|
169
|
+
id=seq.id,
|
|
170
|
+
smiles=seq.smiles,
|
|
171
|
+
description=seq.description,
|
|
172
|
+
)
|
|
173
|
+
else:
|
|
174
|
+
continue
|
|
175
|
+
sequences.append(AF3SequenceEntry(ligand=lig))
|
|
176
|
+
|
|
177
|
+
# Bonded atom pairs (only covalent bonds)
|
|
178
|
+
bonded_atom_pairs: list[tuple[AF3BondedAtom, AF3BondedAtom]] = []
|
|
179
|
+
if config.covalent_bonds:
|
|
180
|
+
for r in config.covalent_bonds:
|
|
181
|
+
# AF3 requires atom names to be given
|
|
182
|
+
if r.atom1.atom_name is None or r.atom2.atom_name is None:
|
|
183
|
+
err_unsupported_feature(
|
|
184
|
+
strict,
|
|
185
|
+
f"AF3 bondedAtomPairs require atom names, but got: {r}",
|
|
186
|
+
)
|
|
187
|
+
continue
|
|
188
|
+
a1: AF3BondedAtom = (
|
|
189
|
+
r.atom1.chain_id,
|
|
190
|
+
r.atom1.residue_idx,
|
|
191
|
+
r.atom1.atom_name,
|
|
192
|
+
)
|
|
193
|
+
a2: AF3BondedAtom = (
|
|
194
|
+
r.atom2.chain_id,
|
|
195
|
+
r.atom2.residue_idx,
|
|
196
|
+
r.atom2.atom_name,
|
|
197
|
+
)
|
|
198
|
+
bonded_atom_pairs.append((a1, a2))
|
|
199
|
+
|
|
200
|
+
if config.contact_restraints or config.pocket_restraints:
|
|
201
|
+
err_unsupported_feature(
|
|
202
|
+
strict,
|
|
203
|
+
"AF3 does not support contact or pocket restraints.",
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
return AF3Config(
|
|
207
|
+
name=name,
|
|
208
|
+
modelSeeds=config.aux.seeds,
|
|
209
|
+
sequences=sequences,
|
|
210
|
+
bondedAtomPairs=bonded_atom_pairs or None,
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def from_alphafold3(config: AF3Config) -> UniAF3Config:
|
|
215
|
+
"""Convert an AlphaFold3 config to a UniAF3Config.
|
|
216
|
+
|
|
217
|
+
Args:
|
|
218
|
+
config: AF3Config pydantic object.
|
|
219
|
+
|
|
220
|
+
Returns:
|
|
221
|
+
A UniAF3Config.
|
|
222
|
+
|
|
223
|
+
"""
|
|
224
|
+
sequences: list[Polymer | ProteinSeq | Ligand | Glycan] = []
|
|
225
|
+
if config.name:
|
|
226
|
+
warn_lossy_conversion(
|
|
227
|
+
f"AF3Config.name ('{config.name}') is not represented in UniAF3Config."
|
|
228
|
+
)
|
|
229
|
+
if config.userCCD is not None or config.userCCDPath is not None:
|
|
230
|
+
warn_lossy_conversion(
|
|
231
|
+
"AF3Config.{userCCD,userCCDPath} are not represented in UniAF3Config."
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
for entry in config.sequences:
|
|
235
|
+
if entry.protein is not None:
|
|
236
|
+
p = entry.protein
|
|
237
|
+
mods = (
|
|
238
|
+
[
|
|
239
|
+
SequenceModification(ccd=m.ptmType, position=m.ptmPosition)
|
|
240
|
+
for m in p.modifications
|
|
241
|
+
]
|
|
242
|
+
if p.modifications
|
|
243
|
+
else None
|
|
244
|
+
)
|
|
245
|
+
if p.unpairedMsa is not None or p.pairedMsa is not None:
|
|
246
|
+
warn_lossy_conversion(
|
|
247
|
+
"AF3Config.sequences[*].protein.{unpairedMsa,pairedMsa} are not imported; UniAF3 maps only file-based MSA paths."
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
templates = None
|
|
251
|
+
if p.templates:
|
|
252
|
+
if any(
|
|
253
|
+
t.mmcif is not None and t.mmcifPath is None for t in p.templates
|
|
254
|
+
):
|
|
255
|
+
warn_lossy_conversion(
|
|
256
|
+
"AF3Config.sequences[*].protein.templates[*].mmcif is not preserved; only mmcifPath maps to UniAF3 templates.path."
|
|
257
|
+
)
|
|
258
|
+
templates = [
|
|
259
|
+
StructuralTemplate(
|
|
260
|
+
path=t.mmcifPath or "",
|
|
261
|
+
query_idx=t.queryIndices,
|
|
262
|
+
template_idx=t.templateIndices,
|
|
263
|
+
)
|
|
264
|
+
for t in p.templates
|
|
265
|
+
]
|
|
266
|
+
|
|
267
|
+
seq = ProteinSeq(
|
|
268
|
+
polymer_type=PolymerType.Protein,
|
|
269
|
+
id=p.id,
|
|
270
|
+
sequence=p.sequence,
|
|
271
|
+
modifications=mods,
|
|
272
|
+
description=p.description,
|
|
273
|
+
unpaired_msa=p.unpairedMsaPath,
|
|
274
|
+
paired_msa=p.pairedMsaPath,
|
|
275
|
+
templates=templates,
|
|
276
|
+
)
|
|
277
|
+
sequences.append(seq)
|
|
278
|
+
|
|
279
|
+
elif entry.dna is not None:
|
|
280
|
+
d = entry.dna
|
|
281
|
+
mods = (
|
|
282
|
+
[
|
|
283
|
+
SequenceModification(
|
|
284
|
+
ccd=m.modificationType, position=m.basePosition
|
|
285
|
+
)
|
|
286
|
+
for m in d.modifications
|
|
287
|
+
]
|
|
288
|
+
if d.modifications
|
|
289
|
+
else None
|
|
290
|
+
)
|
|
291
|
+
seq = Polymer(
|
|
292
|
+
polymer_type=PolymerType.DNA,
|
|
293
|
+
id=d.id,
|
|
294
|
+
sequence=d.sequence,
|
|
295
|
+
modifications=mods,
|
|
296
|
+
description=d.description,
|
|
297
|
+
)
|
|
298
|
+
sequences.append(seq)
|
|
299
|
+
|
|
300
|
+
elif entry.rna is not None:
|
|
301
|
+
r = entry.rna
|
|
302
|
+
mods = (
|
|
303
|
+
[
|
|
304
|
+
SequenceModification(
|
|
305
|
+
ccd=m.modificationType, position=m.basePosition
|
|
306
|
+
)
|
|
307
|
+
for m in r.modifications
|
|
308
|
+
]
|
|
309
|
+
if r.modifications
|
|
310
|
+
else None
|
|
311
|
+
)
|
|
312
|
+
seq = Polymer(
|
|
313
|
+
polymer_type=PolymerType.RNA,
|
|
314
|
+
id=r.id,
|
|
315
|
+
sequence=r.sequence,
|
|
316
|
+
modifications=mods,
|
|
317
|
+
description=r.description,
|
|
318
|
+
)
|
|
319
|
+
sequences.append(seq)
|
|
320
|
+
|
|
321
|
+
elif entry.ligand is not None:
|
|
322
|
+
lg = entry.ligand
|
|
323
|
+
lig = Ligand(
|
|
324
|
+
id=lg.id,
|
|
325
|
+
ccd=lg.ccdCodes,
|
|
326
|
+
smiles=lg.smiles,
|
|
327
|
+
description=lg.description,
|
|
328
|
+
)
|
|
329
|
+
sequences.append(lig)
|
|
330
|
+
|
|
331
|
+
# Bonded atom pairs → covalent bonds
|
|
332
|
+
covalent_bonds: list[CovalentBond] = []
|
|
333
|
+
if config.bondedAtomPairs:
|
|
334
|
+
for a1, a2 in config.bondedAtomPairs:
|
|
335
|
+
covalent_bonds.append(
|
|
336
|
+
CovalentBond(
|
|
337
|
+
atom1=Atom(
|
|
338
|
+
chain_id=a1[0],
|
|
339
|
+
residue_idx=a1[1],
|
|
340
|
+
atom_name=a1[2],
|
|
341
|
+
residue_name=None,
|
|
342
|
+
),
|
|
343
|
+
atom2=Atom(
|
|
344
|
+
chain_id=a2[0],
|
|
345
|
+
residue_idx=a2[1],
|
|
346
|
+
atom_name=a2[2],
|
|
347
|
+
residue_name=None,
|
|
348
|
+
),
|
|
349
|
+
)
|
|
350
|
+
)
|
|
351
|
+
|
|
352
|
+
return UniAF3Config(
|
|
353
|
+
sequences=sequences,
|
|
354
|
+
covalent_bonds=covalent_bonds or None,
|
|
355
|
+
aux=AuxiliaryParams(seeds=config.modelSeeds),
|
|
356
|
+
)
|