Skip to content

Commit e7f6a89

Browse files
authored
Merge pull request nf-core#252 from JoseEspinosa/af3
Add alphafold3
2 parents 6ffbe27 + 9bae56d commit e7f6a89

34 files changed

Lines changed: 1506 additions & 192 deletions

CHANGELOG.md

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,34 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
4646

4747
### Parameters
4848

49-
| Old parameter | New parameter |
50-
| ------------- | --------------------- |
51-
| | `--pdb_obsolete_path` |
49+
| Old parameter | New parameter |
50+
| ---------------------------- | ------------------------------ |
51+
| | `--pdb_obsolete_path` |
52+
| `--small_bfd_link` | `--alphafold2_small_bfd_link` |
53+
| `--mgnify_link` | `--alphafold2_mgnify_link` |
54+
| `--pdb_mmcif_link` | `--alphafold2_pdb_mmcif_link` |
55+
| `--uniref30_alphafold2_link` | `--alphafold2_uniref30_link` |
56+
| `--uniref90_link` | `--alphafold2_uniref90_link` |
57+
| `--pdb_seqres_link` | `--alphafold2_pdb_seqres_link` |
58+
| `--small_bfd_path` | `--alphafold2_small_bfd_path` |
59+
| `--mgnify_path_alphafold2` | `--alphafold2_mgnify_path` |
60+
| `--pdb_mmcif_path` | `--alphafold2_pdb_mmcif_path` |
61+
| `--uniref30_alphafold2_path` | `--alphafold2_uniref30_path` |
62+
| `--uniref90_path` | `--alphafold2_uniref90_path` |
63+
| `--pdb_seqres_path` | `--alphafold2_pdb_seqres_path` |
64+
| `--uniprot_path` | `--alphafold2_uniprot_path` |
65+
| | `--alphafold3_small_bfd_link` |
66+
| | `--alphafold3_mgnify_link` |
67+
| | `--alphafold3_uniref90_link` |
68+
| | `--alphafold3_pdb_seqres_link` |
69+
| | `--uniprot_link` |
70+
| | `--alphafold3_small_bfd_path` |
71+
| | `--alphafold3_params_path` |
72+
| | `--alphafold3_mgnify_path` |
73+
| | `--alphafold3_pdb_mmcif_path` |
74+
| | `--alphafold3_uniref90_path` |
75+
| | `--alphafold3_pdb_seqres_path` |
76+
| | `--alphafold3_uniprot_path` |
5277

5378
> **NB:** Parameter has been **updated** if both old and new parameter information is present.
5479
> **NB:** Parameter has been **added** if just the new parameter information is present.

README.md

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,17 +35,19 @@ On release, automated continuous integration tests run the pipeline on a full-si
3535

3636
ii. [AlphaFold2 split](https://github.com/luisas/alphafold_split) - AlphaFold2 MSA computation and model inference in separate processes
3737

38-
iii. [ColabFold](https://github.com/sokrypton/ColabFold) - MMseqs2 API server followed by ColabFold
38+
iii. [AlphaFold3](https://github.com/deepmind/alphafold) - Regular AlphaFold3 (MSA computation and model inference in the same process)
3939

40-
iv. [ColabFold](https://github.com/sokrypton/ColabFold) - MMseqs2 local search followed by ColabFold
40+
iv. [ColabFold](https://github.com/sokrypton/ColabFold) - MMseqs2 API server followed by ColabFold
4141

42-
v. [ESMFold](https://github.com/facebookresearch/esm) - Regular ESM
42+
v. [ColabFold](https://github.com/sokrypton/ColabFold) - MMseqs2 local search followed by ColabFold
4343

44-
vi. [RoseTTAFold-All-Atom](https://github.com/baker-laboratory/RoseTTAFold-All-Atom/) - Regular RFAA
44+
vi. [ESMFold](https://github.com/facebookresearch/esm) - Regular ESM
4545

46-
vii. [HelixFold3](https://github.com/PaddlePaddle/PaddleHelix/tree/dev/apps/protein_folding/helixfold3) - Regular HF3
46+
vii. [RoseTTAFold-All-Atom](https://github.com/baker-laboratory/RoseTTAFold-All-Atom/) - Regular RFAA
4747

48-
viii. [Boltz](https://github.com/jwohlwend/boltz/) - Regular Boltz-1
48+
viii. [HelixFold3](https://github.com/PaddlePaddle/PaddleHelix/tree/dev/apps/protein_folding/helixfold3) - Regular HF3
49+
50+
ix. [Boltz](https://github.com/jwohlwend/boltz/) - Regular Boltz-1
4951

5052
## Usage
5153

@@ -92,6 +94,21 @@ The pipeline takes care of downloading the databases and parameters required by
9294
-profile <docker/singularity/podman/shifter/charliecloud/conda/institute>
9395
```
9496

97+
- The AlphaFold3 mode can be run using the command below:
98+
99+
```console
100+
nextflow run nf-core/proteinfold \
101+
--input samplesheet.csv \
102+
--outdir <OUTDIR> \
103+
--mode alphafold3 \
104+
--alphafold3_db <null (default) | DB_PATH> \
105+
--use_gpu <true/false> \
106+
-profile <docker/singularity/podman/shifter/charliecloud/conda/institute>
107+
```
108+
109+
> [!WARNING]
110+
> The AlphaFold3 weights are not provided by this pipeline. Users must obtain the weights directly from DeepMind according to their [terms of use](https://github.com/deepmind/alphafold/blob/main/WEIGHTS_TERMS_OF_USE.md) and [prohibited use policy](https://github.com/deepmind/alphafold/blob/main/WEIGHTS_PROHIBITED_USE_POLICY.md). Please ensure you comply with all terms and conditions before using AlphaFold3. For more information about AlphaFold3 usage and requirements, please refer to the [official AlphaFold3 repository](https://github.com/deepmind/alphafold).
111+
95112
- Below, the command to run colabfold_local mode:
96113

97114
```console

bin/extract_metrics.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ def read_json(id, json_files):
115115
if json_file.endswith("_data.json"): #AF3 output with MSA info
116116
# Can't just used format_msa_rows since there's FASTA headers in the json content
117117
unpaired_MSAs = data['sequences'][0]['protein']['unpairedMsa']
118-
msa_lines = [line for line in unpaired_MSAs.split("\n") if not line.startswith(">") and line.strip()]
118+
msa_lines = [''.join(c for c in line if not c.islower()) for line in unpaired_MSAs.split("\n") if line.strip() and not line.startswith(">")]
119119
msa_rows = [[str(AA_to_int.get(residue, 20)) for residue in line] for line in msa_lines]
120120
write_tsv(f"{id}_msa.tsv", msa_rows)
121121
#AF3 output with PAE info, or HF3 PAE data. TODO: Need to make sure the workflow points to [protein]/[protein]_rank1/all_results.json

bin/fasta_to_alphafold3_json.py

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
#!/usr/bin/env python3
2+
3+
import sys
4+
import argparse
5+
import json
6+
import string
7+
8+
def parse_args(args=None):
9+
"""
10+
Parse command line arguments for the script.
11+
12+
Required arguments:
13+
FILE_IN: Input fasta file path
14+
ID: Identifier for the protein sequence (will be used in output filename and JSON)
15+
16+
Optional arguments:
17+
-ms/--model_seed: AlphaFold3 model seed(s) to use (default: [11])
18+
"""
19+
Description = "Convert fasta files to Alphafold3 json format."
20+
Epilog = "Example usage: python fasta_to_alphafold3_json.py <FILE_IN> <ID>"
21+
22+
parser = argparse.ArgumentParser(description=Description, epilog=Epilog)
23+
24+
## REQUIRED PARAMETERS
25+
parser.add_argument(
26+
"FILE_IN",
27+
help="Input fasta file."
28+
)
29+
parser.add_argument(
30+
"ID",
31+
help="ID for file name and for json id tag."
32+
)
33+
34+
## OPTIONAL PARAMETERS
35+
parser.add_argument(
36+
"-ms",
37+
"--model_seed",
38+
type=int,
39+
nargs='+',
40+
dest="MODEL_SEED",
41+
default=[11],
42+
help="Alphafold 3 model seed."
43+
)
44+
45+
return parser.parse_args(args)
46+
47+
def sanitised_name(id):
48+
"""
49+
Sanitize the input ID to create a valid filename.
50+
51+
This function is copied from AlphaFold3 source code to ensure consistent naming:
52+
https://github.com/google-deepmind/alphafold3/blob/7fdf96161d61a6e18048e5c62bf7e1d711992943/src/alphafold3/common/folding_input.py#L1166-L1170
53+
It converts the ID to lowercase, replaces spaces with underscores, and removes
54+
any characters that aren't allowed in filenames.
55+
56+
Args:
57+
id (str): Input identifier
58+
59+
Returns:
60+
str: Sanitized version of the ID suitable for use as a filename
61+
"""
62+
lower_spaceless_name = id.lower().replace(' ', '_')
63+
allowed_chars = set(string.ascii_lowercase + string.digits + '_-.')
64+
return ''.join(l for l in lower_spaceless_name if l in allowed_chars)
65+
66+
def fasta_to_alphafold3_json(file_in, id):
67+
"""
68+
Convert a single-sequence FASTA file to AlphaFold3 JSON format.
69+
70+
This function reads a FASTA file and converts it to the format required by AlphaFold3.
71+
It only processes single-sequence FASTA files and raises an error for multi-sequence files.
72+
73+
The function expects a samplesheet.csv with the following format:
74+
id,fasta
75+
T1024,path/to/T1024.fasta
76+
T1026,path/to/T1026.fasta
77+
78+
Args:
79+
file_in (str): Path to input FASTA file
80+
id (str): Identifier for the sequence
81+
82+
Returns:
83+
dict: Dictionary containing the sequence information in AlphaFold3 format
84+
85+
Raises:
86+
RuntimeError: If the input file contains multiple sequences
87+
"""
88+
sequence_list = []
89+
sequence = None
90+
fasta_mapping_dict = {}
91+
92+
with open(file_in, "r", encoding="utf-8-sig") as fin:
93+
n_seq = 0
94+
for l in fin:
95+
l = l.strip()
96+
if l.startswith(">"):
97+
if n_seq > 1:
98+
raise RuntimeError("Multifasta files are not allowed")
99+
n_seq += 1
100+
if sequence:
101+
sequence_list.append(sequence)
102+
sequence = {"id": id, "sequence": ""}
103+
else:
104+
sequence["sequence"] += l
105+
106+
return sequence
107+
108+
def create_json_dict(sequence, model_seed):
109+
"""
110+
Create the final JSON dictionary in AlphaFold3 format.
111+
112+
The function creates a JSON structure that follows AlphaFold3's requirements:
113+
{
114+
"name": "sequence_id",
115+
"sequences": [
116+
{
117+
"protein": {
118+
"id": "A",
119+
"sequence": "protein_sequence"
120+
}
121+
}
122+
],
123+
"modelSeeds": [seed_values],
124+
"dialect": "alphafold3",
125+
"version": 1
126+
}
127+
128+
Args:
129+
sequence (dict): Dictionary containing sequence information
130+
model_seed (list): List of model seeds to use
131+
132+
Returns:
133+
dict: JSON-compatible dictionary in AlphaFold3 format
134+
"""
135+
json_sequence_dict = {}
136+
137+
item = {
138+
"name": f"{sequence['id']}",
139+
"sequences": [
140+
{
141+
"protein": {
142+
"id": "A",
143+
"sequence": sequence["sequence"]
144+
}
145+
},
146+
],
147+
"modelSeeds": model_seed,
148+
"dialect": "alphafold3",
149+
"version": 1
150+
}
151+
152+
json_sequence_dict[sequence["id"]] = item
153+
154+
return json_sequence_dict
155+
156+
def main(args=None):
157+
"""
158+
Main function to process FASTA files and create AlphaFold3 JSON files.
159+
160+
The script:
161+
1. Parses command line arguments
162+
2. Sanitizes the input ID for filename use
163+
3. Reads and processes the FASTA file
164+
4. Creates the JSON structure
165+
5. Writes the output to a JSON file
166+
167+
The output filename will be the sanitized ID with .json extension.
168+
"""
169+
args = parse_args(args)
170+
id = args.ID
171+
172+
if id.endswith(".json"):
173+
id = id[:-5]
174+
reformatted_id = sanitised_name(id)
175+
else:
176+
reformatted_id = sanitised_name(id)
177+
178+
out_json = f"{reformatted_id}.json"
179+
180+
sequence = fasta_to_alphafold3_json(args.FILE_IN, reformatted_id)
181+
json_dict = create_json_dict(sequence, args.MODEL_SEED)
182+
183+
print ("json file " + out_json)
184+
with open(out_json, "w") as fout:
185+
json.dump(json_dict[reformatted_id], fout, indent=4)
186+
187+
with open(out_json, 'r') as f:
188+
json_str = f.read()
189+
190+
if __name__ == "__main__":
191+
sys.exit(main())

bin/generate_report.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,7 @@ def pdb_to_lddt(struct_files, generate_tsv):
318318
model_name = {
319319
"esmfold": "ESMFold",
320320
"alphafold2": "AlphaFold2",
321+
"alphafold3": "Alphafold3",
321322
"colabfold": "ColabFold",
322323
"rosettafold_all_atom": "Rosettafold_All_Atom",
323324
"helixfold3": "HelixFold3",

bin/mmcif_to_pdb.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
#!/usr/bin/env python
2+
3+
###############################################################################
4+
###############################################################################
5+
## Created on December 16th 2024 convert cif files to pdb
6+
###############################################################################
7+
###############################################################################
8+
9+
import argparse
10+
import sys
11+
from Bio import PDB
12+
13+
def parse_args(args=None):
14+
Description = "Convert mmcif files to pdb format."
15+
Epilog = """Example usage: python mmcif_to_pdb.py <MMCIF_IN>"""
16+
17+
parser = argparse.ArgumentParser(description=Description, epilog=Epilog)
18+
parser.add_argument(
19+
"MMCIF_IN",
20+
help="Input mmcif file."
21+
)
22+
parser.add_argument(
23+
"-po",
24+
"--pdb_out",
25+
type=str,
26+
dest="PDB_OUT",
27+
default="",
28+
help="Output pdb file."
29+
)
30+
return parser.parse_args(args)
31+
32+
33+
def mmcif_to_pdb(mmcif_file, pdb_file):
34+
"""
35+
Convert an mmCIF file to PDB format.
36+
"""
37+
# Parse the mmCIF file
38+
parser = PDB.MMCIFParser(QUIET=True)
39+
structure = parser.get_structure("structure", mmcif_file)
40+
41+
# Write to PDB format
42+
io = PDB.PDBIO()
43+
io.set_structure(structure)
44+
io.save(pdb_file)
45+
46+
return pdb_file
47+
48+
49+
############################################
50+
############################################
51+
## MAIN FUNCTION
52+
############################################
53+
############################################
54+
55+
def main(args=None):
56+
args = parse_args(args)
57+
58+
# Name output PDB file name
59+
pdb_file = args.PDB_OUT
60+
if not pdb_file:
61+
pdb_file = args.MMCIF_IN.rsplit(".", 1)[0] + ".pdb"
62+
63+
pdb_file = mmcif_to_pdb(args.MMCIF_IN, pdb_file)
64+
print(f"Converted {args.MMCIF_IN} to {pdb_file}")
65+
66+
67+
if __name__ == "__main__":
68+
main()

0 commit comments

Comments
 (0)