From b66f9a341f071abdfd474acb9a6d9d9e5afcfffc Mon Sep 17 00:00:00 2001 From: ziadbkh Date: Fri, 19 Apr 2024 13:53:13 +1000 Subject: [PATCH 001/227] add gadi config --- conf/gadi.config | 42 ++++++++++++++++++++++++++++++++++++++++++ nextflow.config | 4 ++++ 2 files changed, 46 insertions(+) create mode 100755 conf/gadi.config diff --git a/conf/gadi.config b/conf/gadi.config new file mode 100755 index 00000000..ab0aae34 --- /dev/null +++ b/conf/gadi.config @@ -0,0 +1,42 @@ +/* +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Nextflow config file for running full-size tests +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Defines input files and everything required to run a full size pipeline test. + + Use as follows: + nextflow run nf-core/proteinfold -profile test_full_alphafold2_split, --outdir + +---------------------------------------------------------------------------------------- +*/ + + +if (params.use_gpu) { singularity.runOptions = '--nv' } +singularity.cacheDir = "/g/data/if89/singularity_cache/" +params { + mode = 'alphafold2' + alphafold2_mode = 'split_msa_prediction' + input = 'https://raw.githubusercontent.com/nf-core/test-datasets/proteinfold/testdata/samplesheet/v1.0/samplesheet.csv' + alphafold2_db = '/g/data/if89/alphafold2/standard/' + use_dgxa100 = false +} + +process { + storage = "gdata/if89+scratch/${params.project}" + + if (params.use_gpu) { + withName: 'RUN_ALPHAFOLD2_PRED|RUN_ALPHAFOLD2' { + if (params.use_dgxa100){ + queue = "dgxa100" + cpus = 16 + }else{ + queue = "gpuvolta" + cpus = 12 + } + + gpus = 1 + + } + } + +} \ No newline at end of file diff --git a/nextflow.config b/nextflow.config index 2bd14507..47aa6677 100644 --- a/nextflow.config +++ b/nextflow.config @@ -253,6 +253,10 @@ profiles { test_full_colabfold_multimer { includeConfig 'conf/test_full_colabfold_webserver_multimer.config' } test_full_esmfold { includeConfig 'conf/test_full_esmfold.config' } test_full_esmfold_multimer { includeConfig 'conf/test_full_esmfold_multimer.config' } + gadi { + includeConfig 'https://raw.githubusercontent.com/nf-core/configs/master/conf/nci_gadi.config' + includeConfig 'conf/gadi.config' + } } // Set default registry for Apptainer, Docker, Podman and Singularity independent of -profile From 442c6ce70e027272c165761c10ef7c9a7947aca1 Mon Sep 17 00:00:00 2001 From: ziadbkh Date: Sun, 12 May 2024 15:57:44 +1000 Subject: [PATCH 002/227] add visualisation --- assets/alphafold_template.html | 688 +++++++++++++++++++++++++++ assets/generat_plots_2.py | 132 +++++ conf/gadi.config | 5 +- modules/local/extract_outputs.nf | 42 ++ modules/local/generat_report.nf | 28 ++ modules/local/run_alphafold2_pred.nf | 3 +- nextflow.config | 2 +- tower.yml | 2 + workflows/alphafold2.nf | 40 +- 9 files changed, 935 insertions(+), 7 deletions(-) create mode 100755 assets/alphafold_template.html create mode 100644 assets/generat_plots_2.py create mode 100644 modules/local/extract_outputs.nf create mode 100644 modules/local/generat_report.nf diff --git a/assets/alphafold_template.html b/assets/alphafold_template.html new file mode 100755 index 00000000..32be8829 --- /dev/null +++ b/assets/alphafold_template.html @@ -0,0 +1,688 @@ + + + + + + + + + Alphafold structure prediction + + + + + + + + + + + + + + +

Alphafold structure prediction

+
+
+
+
+
+
+ +
+ +
+ Select a representation to display +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+

+ Scroll up/down + to zoom in and out +

+

+ Click + drag + to rotate the structure +

+

+ CTRL + click + drag + to move the structure +

+

+ Click + an atom to bring it into focus +

+
+ +
+
+
+
+
<50
+
70
+
90+
+
+
+ +
+

+ + Alphafold produces a + + per-residue confidence score (pLDDT) + + between 0 and 100. Some regions below 50 pLDDT may be + unstructured in isolation. + +

+
+
+
+
+ +
+
+

Select model

+

The top-ranked structures predicted by Alphafold

+
+ + + + + + + + + +
+
+ +
+

Toggle representations

+
+ + + + + + + +
+
+ +
+

Actions

+
+ + + +
+
+ +
+

Download

+
+ + + +
+
+
+
+
+
+ + + + + + +
+
+ + + + + + diff --git a/assets/generat_plots_2.py b/assets/generat_plots_2.py new file mode 100644 index 00000000..c0948dd6 --- /dev/null +++ b/assets/generat_plots_2.py @@ -0,0 +1,132 @@ +#!/usr/bin/env python + +import os +from matplotlib import pyplot as plt +import argparse +from collections import OrderedDict + +def generate_output_images(msa_path, plddt_paths, name, out_dir): + msa = [] + with open(msa_path, 'r') as in_file: + for line in in_file: + msa.append([int(x) for x in line.strip().split()]) + + seqid = [] + for sequence in msa: + matches = [1.0 if first == other else 0.0 for first, other in zip(msa[0], sequence)] + seqid.append(sum(matches) / len(matches)) + + seqid_sort = sorted(range(len(seqid)), key=seqid.__getitem__) + + non_gaps = [] + for sequence in msa: + non_gaps.append([float(num != 21) if num != 21 else float('nan') for num in sequence]) + + sorted_non_gaps = [non_gaps[i] for i in seqid_sort] + final = [] + for sorted_seq, identity in zip(sorted_non_gaps, [seqid[i] for i in seqid_sort]): + final.append([value * identity if not isinstance(value, str) else value for value in sorted_seq]) + + + + ################################################################## + plt.figure(figsize=(14, 14), dpi=100) + ################################################################## + plt.title("Sequence coverage") + plt.imshow(final, + interpolation='nearest', aspect='auto', + cmap="rainbow_r", vmin=0, vmax=1, origin='lower') + + column_counts = [0] * len(msa[0]) + for col in range(len(msa[0])): + for row in msa: + if row[col] != 21: + column_counts[col] += 1 + + plt.plot(column_counts, color='black') + plt.xlim(-0.5, len(msa[0]) - 0.5) + plt.ylim(-0.5, len(msa) - 0.5) + + plt.colorbar(label="Sequence identity to query", ) + plt.xlabel("Positions") + plt.ylabel("Sequences") + plt.savefig(f"{out_dir}/{name+('_' if name else '')}seq_coverage.png") + + ################################################################## + plddt_per_model = OrderedDict() + plddt_paths_srt = plddt_paths + plddt_paths_srt.sort() + for plddt_path in plddt_paths_srt: + with open(plddt_path, 'r') as in_file: + plddt_per_model[os.path.basename(plddt_path)[:-4]] = [float(x) for x in in_file.read().strip().split()] + + plt.figure(figsize=(14, 14), dpi=100) + plt.title("Predicted LDDT per position") + for model_name, value_plddt in plddt_per_model.items(): + plt.plot(value_plddt, label=model_name) + plt.ylim(0, 100) + plt.ylabel("Predicted LDDT") + plt.xlabel("Positions") + plt.savefig(f"{out_dir}/{name+('_' if name else '')}coverage_LDDT.png") + + # split into figures + i = 0 + for model_name, value_plddt in plddt_per_model.items(): + plt.figure(figsize=(14, 14), dpi=100) + plt.title("Predicted LDDT per position") + plt.plot(value_plddt, label=model_name) + plt.ylim(0, 100) + plt.ylabel("Predicted LDDT") + plt.xlabel("Positions") + plt.savefig(f"{out_dir}/{name+('_' if name else '')}coverage_LDDT_{i}.png") + i += 1 + + + ################################################################## + + + ################################################################## + """ + num_models = 5 # columns + num_runs_per_model = math.ceil(len(model_names)/num_models) + fig = plt.figure(figsize=(3 * num_models, 2 * num_runs_per_model), dpi=100) + for n, (model_name, value) in enumerate(pae_plddt_per_model.items()): + plt.subplot(num_runs_per_model, num_models, n + 1) + plt.title(model_name) + plt.imshow(value["pae"], label=model_name, cmap="bwr", vmin=0, vmax=30) + plt.colorbar() + fig.tight_layout() + plt.savefig(f"{out_dir}/{name+('_' if name else '')}PAE.png") + """ + ################################################################## + + + +print("Starting..") +parser = argparse.ArgumentParser() +parser.add_argument('--msa',dest='msa',required=True) +parser.add_argument('--plddt',dest='plddt',required=True, nargs="+") +parser.add_argument('--pdb',dest='pdb',required=True, nargs="+") +parser.add_argument('--name',dest='name') +parser.add_argument('--output_dir',dest='output_dir') +parser.add_argument('--html_template',dest='html_template') +parser.set_defaults(output_dir='') +parser.set_defaults(name='') +args = parser.parse_args() + + +generate_output_images(args.msa, args.plddt, args.name, args.output_dir) + +print("generating html report...") +structures = args.pdb +structures.sort() + +alphfold_template = open(args.html_template, "r").read() +alphfold_template = alphfold_template.replace(f"*sample_name_here*", args.name) +i = 0 +for structure in structures: + alphfold_template = alphfold_template.replace(f"*_data_ranked_{i}.pdb*", open(structure, "r").read().replace("\n", "\\n")) + i += 1 + +with open(f"{args.output_dir}/{args.name}_alphafold.html", "w") as out_file: + out_file.write(alphfold_template) diff --git a/conf/gadi.config b/conf/gadi.config index ab0aae34..cf910ec1 100755 --- a/conf/gadi.config +++ b/conf/gadi.config @@ -13,6 +13,7 @@ if (params.use_gpu) { singularity.runOptions = '--nv' } singularity.cacheDir = "/g/data/if89/singularity_cache/" +singularity.autoMounts = true params { mode = 'alphafold2' alphafold2_mode = 'split_msa_prediction' @@ -28,15 +29,13 @@ process { withName: 'RUN_ALPHAFOLD2_PRED|RUN_ALPHAFOLD2' { if (params.use_dgxa100){ queue = "dgxa100" - cpus = 16 + cpus = 16 }else{ queue = "gpuvolta" cpus = 12 } - gpus = 1 } } - } \ No newline at end of file diff --git a/modules/local/extract_outputs.nf b/modules/local/extract_outputs.nf new file mode 100644 index 00000000..cd71636e --- /dev/null +++ b/modules/local/extract_outputs.nf @@ -0,0 +1,42 @@ +process EXTRACT_OUTPUTS { + tag "$id" + label 'process_single' + + // Exit if running this module with -profile conda / -profile mamba + if (workflow.profile.tokenize(',').intersect(['conda', 'mamba']).size() >= 1) { + error("Local RUN_ALPHAFOLD2 module does not support Conda. Please use Docker / Singularity / Podman instead.") + } + + container "nf-core/proteinfold_alphafold2_standard:dev" + + input: + tuple val(id), path(pkl_files) + + output: + tuple val(id), path ("*_msa.tsv"), optional: true, emit: msa_info + tuple val(id), path ("*_lddt_*.tsv"), optional: true, emit: lddt_info + path "versions.yml", emit: versions + + when: + task.ext.when == null || task.ext.when + script: + def args = task.ext.args ?: '' + //#for pkl_file in [\"${"\", \"".join(pkl_files)}\"]: + """ + #!/usr/bin/env python + import pickle + import os, sys + for pkl_file in [\"${pkl_files.join("\", \"")}\"]: + dict_data = pickle.load(open(pkl_file,'rb')) + if pkl_file.endswith("features.pkl"): + with open ("${id}_msa.tsv", "w") as out_f: + for val in dict_data['msa']: + out_f.write("\\t".join([str(x) for x in val]) + "\\n") + else: + model_id = os.path.basename(pkl_file).replace("result_model_", "").replace("_pred_0.pkl", "") + with open (f"${id}_lddt_{model_id}.tsv", "w") as out_f: + out_f.write("\\t".join([str(x) for x in dict_data['plddt']]) + "\\n") + with open ("versions.yml", "w") as version_file: + version_file.write("\\"${task.process}\\":\\n python: {}\\n".format(sys.version.split()[0].strip())) + """ +} diff --git a/modules/local/generat_report.nf b/modules/local/generat_report.nf new file mode 100644 index 00000000..29865125 --- /dev/null +++ b/modules/local/generat_report.nf @@ -0,0 +1,28 @@ +process GENERATE_REPORT { + tag "$id" + label 'process_single' + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/multiqc:1.21--pyhdfd78af_0' : + 'biocontainers/multiqc:1.21--pyhdfd78af_0' }" + + input: + tuple val(id), path(msa) + tuple val(id), path(lddt) + tuple val(id), path(pdb) + path(template) + path(script) + output: + tuple val(id), path ("*.html"), emit: report + tuple val(id), path ("*.png"), emit: images + //path "versions.yml", emit: versions + + when: + task.ext.when == null || task.ext.when + script: + def args = task.ext.args ?: '' + + """ + #export MPLCONFIGDIR=\$PBS_JOBFS + python ./generat_plots_2.py --msa ${msa} --plddt ${lddt.join(' ')} --pdb ${pdb.join(' ')} --html_template ${template} --output_dir ./ --name ${id} || true + """ +} diff --git a/modules/local/run_alphafold2_pred.nf b/modules/local/run_alphafold2_pred.nf index ee9983c5..66c9302f 100644 --- a/modules/local/run_alphafold2_pred.nf +++ b/modules/local/run_alphafold2_pred.nf @@ -29,7 +29,8 @@ process RUN_ALPHAFOLD2_PRED { path msa output: - path ("${fasta.baseName}*") + tuple val(meta), path ("${fasta.baseName}*") + tuple val(meta), path ("${fasta.baseName}*/*"), emit: af_out path "*_mqc.tsv", emit: multiqc path "versions.yml", emit: versions diff --git a/nextflow.config b/nextflow.config index 47aa6677..aca4fe7a 100644 --- a/nextflow.config +++ b/nextflow.config @@ -253,7 +253,7 @@ profiles { test_full_colabfold_multimer { includeConfig 'conf/test_full_colabfold_webserver_multimer.config' } test_full_esmfold { includeConfig 'conf/test_full_esmfold.config' } test_full_esmfold_multimer { includeConfig 'conf/test_full_esmfold_multimer.config' } - gadi { + gadi { includeConfig 'https://raw.githubusercontent.com/nf-core/configs/master/conf/nci_gadi.config' includeConfig 'conf/gadi.config' } diff --git a/tower.yml b/tower.yml index 787aedfe..3c8be6f5 100644 --- a/tower.yml +++ b/tower.yml @@ -3,3 +3,5 @@ reports: display: "MultiQC HTML report" samplesheet.csv: display: "Auto-created samplesheet with collated metadata and FASTQ paths" + *_alphafold.html: + display: "Predected structures" \ No newline at end of file diff --git a/workflows/alphafold2.nf b/workflows/alphafold2.nf index c85e672b..3d2abe15 100644 --- a/workflows/alphafold2.nf +++ b/workflows/alphafold2.nf @@ -10,7 +10,8 @@ include { RUN_ALPHAFOLD2 } from '../modules/local/run_alphafold2' include { RUN_ALPHAFOLD2_MSA } from '../modules/local/run_alphafold2_msa' include { RUN_ALPHAFOLD2_PRED } from '../modules/local/run_alphafold2_pred' - +include { EXTRACT_OUTPUTS } from '../modules/local/extract_outputs' +include { GENERATE_REPORT } from '../modules/local/generat_report' /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ IMPORT NF-CORE MODULES/SUBWORKFLOWS @@ -74,6 +75,7 @@ workflow ALPHAFOLD2 { .transpose() .set { ch_fasta } } + ch_alphafold_outputs = Channel.empty() if (alphafold2_mode == 'standard') { // @@ -96,6 +98,7 @@ workflow ALPHAFOLD2 { ) ch_multiqc_rep = RUN_ALPHAFOLD2.out.multiqc.collect() ch_versions = ch_versions.mix(RUN_ALPHAFOLD2.out.versions) + } else if (alphafold2_mode == 'split_msa_prediction') { // @@ -117,7 +120,11 @@ workflow ALPHAFOLD2 { ch_uniprot ) ch_versions = ch_versions.mix(RUN_ALPHAFOLD2_MSA.out.versions) - + + RUN_ALPHAFOLD2_MSA.out.features + .map{[it.getName().split("\\.")[0], [it]]} + .set{ch_features} + RUN_ALPHAFOLD2_PRED ( ch_fasta, full_dbs, @@ -136,8 +143,37 @@ workflow ALPHAFOLD2 { ) ch_multiqc_rep = RUN_ALPHAFOLD2_PRED.out.multiqc.collect() ch_versions = ch_versions.mix(RUN_ALPHAFOLD2_PRED.out.versions) + RUN_ALPHAFOLD2_PRED.out.af_out.set{ch_alphafold_outputs} + } + ch_alphafold_outputs + .map{[it[0].id, it[1].findAll { it.getName().endsWith('.pkl') && it.getName().startsWith('result_model_') } ]} + .set{ch_pred} + + EXTRACT_OUTPUTS(ch_features.mix(ch_pred)) + + + ch_alphafold_outputs + .map{[it[0].id, it[1].findAll { it.getName().endsWith('.pdb') && it.getName().startsWith('ranked_') } ]} + .set{ch_pdb} + + + EXTRACT_OUTPUTS.out.msa_info.join( + EXTRACT_OUTPUTS.out.lddt_info.join( + ch_pdb + ) + ).set{ch_all} + + GENERATE_REPORT( + ch_all.map{[it[0], it[1]]}, + ch_all.map{[it[0], it[2]]}, + ch_all.map{[it[0], it[3]]}, + Channel.fromPath("$projectDir/assets/alphafold_template.html").first(), + Channel.fromPath("$projectDir/assets/generat_plots_2.py").first(), + + ) + // // Collate and save software versions // From b7bd69f80b40a8035a8a94f36c884912f4146288 Mon Sep 17 00:00:00 2001 From: ziadbkh Date: Sun, 12 May 2024 16:02:24 +1000 Subject: [PATCH 003/227] fix tower report --- tower.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) mode change 100644 => 100755 tower.yml diff --git a/tower.yml b/tower.yml old mode 100644 new mode 100755 index 3c8be6f5..315e0b97 --- a/tower.yml +++ b/tower.yml @@ -3,5 +3,5 @@ reports: display: "MultiQC HTML report" samplesheet.csv: display: "Auto-created samplesheet with collated metadata and FASTQ paths" - *_alphafold.html: + "*_alphafold.html": display: "Predected structures" \ No newline at end of file From a7a870fef162e434425f01ac5c8507c1511c0482 Mon Sep 17 00:00:00 2001 From: ziadbkh Date: Sun, 12 May 2024 16:55:45 +1000 Subject: [PATCH 004/227] add plots --- tower.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tower.yml b/tower.yml index 315e0b97..16a92fd3 100755 --- a/tower.yml +++ b/tower.yml @@ -4,4 +4,6 @@ reports: samplesheet.csv: display: "Auto-created samplesheet with collated metadata and FASTQ paths" "*_alphafold.html": - display: "Predected structures" \ No newline at end of file + display: "Predected structures" + "*.png": + display: "Plots" \ No newline at end of file From 2d349a925d67d2218db0c473b796d78d212d333b Mon Sep 17 00:00:00 2001 From: ziadbkh Date: Sun, 12 May 2024 21:21:58 +1000 Subject: [PATCH 005/227] add figs to report --- assets/alphafold_template.html | 16 ++++++++-------- assets/generat_plots_2.py | 11 +++++++++++ tower.yml | 2 -- 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/assets/alphafold_template.html b/assets/alphafold_template.html index 32be8829..8477b6f5 100755 --- a/assets/alphafold_template.html +++ b/assets/alphafold_template.html @@ -414,9 +414,9 @@

Download

- - - + + +
@@ -475,11 +475,11 @@

Download

] const LDDT_IMGS = [ - "*sample_name_here*_coverage_LDDT_0.png", - "*sample_name_here*_coverage_LDDT_1.png", - "*sample_name_here*_coverage_LDDT_2.png", - "*sample_name_here*_coverage_LDDT_3.png", - "*sample_name_here*_coverage_LDDT_4.png" + "coverage_LDDT_0.png", + "coverage_LDDT_1.png", + "coverage_LDDT_2.png", + "coverage_LDDT_3.png", + "coverage_LDDT_4.png" ] const REPRESENTATIONS = [ diff --git a/assets/generat_plots_2.py b/assets/generat_plots_2.py index c0948dd6..3b2ac1d8 100644 --- a/assets/generat_plots_2.py +++ b/assets/generat_plots_2.py @@ -4,6 +4,7 @@ from matplotlib import pyplot as plt import argparse from collections import OrderedDict +import base64 def generate_output_images(msa_path, plddt_paths, name, out_dir): msa = [] @@ -123,10 +124,20 @@ def generate_output_images(msa_path, plddt_paths, name, out_dir): alphfold_template = open(args.html_template, "r").read() alphfold_template = alphfold_template.replace(f"*sample_name_here*", args.name) + i = 0 for structure in structures: alphfold_template = alphfold_template.replace(f"*_data_ranked_{i}.pdb*", open(structure, "r").read().replace("\n", "\\n")) i += 1 +with open(f"{args.output_dir}/{args.name + ('_' if args.name else '')}seq_coverage.png", "rb") as in_file: + alphfold_template = alphfold_template.replace(f"seq_coverage.png", f"data:image/png;base64,{base64.b64encode(in_file.read()).decode('utf-8')}") + +for i in range(0, 5): + with open(f"{args.output_dir}/{args.name + ('_' if args.name else '')}coverage_LDDT_{i}.png", "rb") as in_file: + alphfold_template = alphfold_template.replace(f"coverage_LDDT_{i}.png", f"data:image/png;base64,{base64.b64encode(in_file.read()).decode('utf-8')}") + + + with open(f"{args.output_dir}/{args.name}_alphafold.html", "w") as out_file: out_file.write(alphfold_template) diff --git a/tower.yml b/tower.yml index 16a92fd3..c0b95ad4 100755 --- a/tower.yml +++ b/tower.yml @@ -5,5 +5,3 @@ reports: display: "Auto-created samplesheet with collated metadata and FASTQ paths" "*_alphafold.html": display: "Predected structures" - "*.png": - display: "Plots" \ No newline at end of file From 7a183c88ce358b7c9720dc16c28c2252564dbd7b Mon Sep 17 00:00:00 2001 From: ziadbkh Date: Mon, 13 May 2024 13:04:26 +1000 Subject: [PATCH 006/227] update --- assets/alphafold_template.html | 14 +++--- assets/generat_plots_2.py | 92 +++++++++++++++++++++++++++++++--- 2 files changed, 93 insertions(+), 13 deletions(-) diff --git a/assets/alphafold_template.html b/assets/alphafold_template.html index 8477b6f5..533e2fee 100755 --- a/assets/alphafold_template.html +++ b/assets/alphafold_template.html @@ -412,13 +412,13 @@

Download

- - - - - - -
+
+
+
+
+ +
+
diff --git a/assets/generat_plots_2.py b/assets/generat_plots_2.py index 3b2ac1d8..5b7bc5ec 100644 --- a/assets/generat_plots_2.py +++ b/assets/generat_plots_2.py @@ -5,6 +5,10 @@ import argparse from collections import OrderedDict import base64 +import os +from collections import OrderedDict +import plotly.graph_objects as go +from plotly.subplots import make_subplots def generate_output_images(msa_path, plddt_paths, name, out_dir): msa = [] @@ -102,6 +106,71 @@ def generate_output_images(msa_path, plddt_paths, name, out_dir): ################################################################## +def generate_plots(msa_path, plddt_paths, name, out_dir): + msa = [] + with open(msa_path, 'r') as in_file: + for line in in_file: + msa.append([int(x) for x in line.strip().split()]) + + seqid = [] + for sequence in msa: + matches = [1.0 if first == other else 0.0 for first, other in zip(msa[0], sequence)] + seqid.append(sum(matches) / len(matches)) + + seqid_sort = sorted(range(len(seqid)), key=seqid.__getitem__) + + non_gaps = [] + for sequence in msa: + non_gaps.append([float(num != 21) if num != 21 else float('nan') for num in sequence]) + + sorted_non_gaps = [non_gaps[i] for i in seqid_sort] + final = [] + for sorted_seq, identity in zip(sorted_non_gaps, [seqid[i] for i in seqid_sort]): + final.append([value * identity if not isinstance(value, str) else value for value in sorted_seq]) + + # Plotting Sequence Coverage using Plotly + fig = go.Figure() + fig.add_trace(go.Heatmap( + z=final, + colorscale="Rainbow", + zmin=0, + zmax=1, + )) + fig.update_layout( + title="Sequence coverage", + xaxis_title="Positions", + yaxis_title="Sequences" + ) + # Save as interactive HTML instead of an image + fig.savefig(f"{out_dir}/{name+('_' if name else '')}seq_coverage.png") + """ + #fig.to_html(full_html=False).write_html(f"{out_dir}/{name+('_' if name else '')}seq_coverage.html") + with open (f"{out_dir}/{name+('_' if name else '')}seq_coverage.html", "w") as out_plt: + out_plt.write(fig.to_html(full_html=False)) + """ + # Plotting Predicted LDDT per position using Plotly + plddt_per_model = OrderedDict() + plddt_paths.sort() + for plddt_path in plddt_paths: + with open(plddt_path, 'r') as in_file: + plddt_per_model[os.path.basename(plddt_path)[:-4]] = [float(x) for x in in_file.read().strip().split()] + + i = 0 + for model_name, value_plddt in plddt_per_model.items(): + fig = go.Figure() + fig.add_trace(go.Scatter( + x=list(range(len(value_plddt))), + y=value_plddt, + mode='lines', + name=model_name + )) + fig.update_layout(title="Predicted LDDT per Position") + fig.savefig(f"{out_dir}/{name+('_' if name else '')}coverage_LDDT_{i}.png") + """ + with open (f"{out_dir}/{name+('_' if name else '')}coverage_LDDT_{i}.html", "w") as out_plt: + out_plt.write(fig.to_html(full_html=False).replace("\"", "\\\"")) + """ + i += 1 print("Starting..") parser = argparse.ArgumentParser() @@ -118,6 +187,8 @@ def generate_output_images(msa_path, plddt_paths, name, out_dir): generate_output_images(args.msa, args.plddt, args.name, args.output_dir) +#generate_plots(args.msa, args.plddt, args.name, args.output_dir) + print("generating html report...") structures = args.pdb structures.sort() @@ -130,14 +201,23 @@ def generate_output_images(msa_path, plddt_paths, name, out_dir): alphfold_template = alphfold_template.replace(f"*_data_ranked_{i}.pdb*", open(structure, "r").read().replace("\n", "\\n")) i += 1 -with open(f"{args.output_dir}/{args.name + ('_' if args.name else '')}seq_coverage.png", "rb") as in_file: - alphfold_template = alphfold_template.replace(f"seq_coverage.png", f"data:image/png;base64,{base64.b64encode(in_file.read()).decode('utf-8')}") - -for i in range(0, 5): - with open(f"{args.output_dir}/{args.name + ('_' if args.name else '')}coverage_LDDT_{i}.png", "rb") as in_file: - alphfold_template = alphfold_template.replace(f"coverage_LDDT_{i}.png", f"data:image/png;base64,{base64.b64encode(in_file.read()).decode('utf-8')}") +if True: + with open(f"{args.output_dir}/{args.name + ('_' if args.name else '')}seq_coverage.png", "rb") as in_file: + alphfold_template = alphfold_template.replace(f"seq_coverage.png", f"data:image/png;base64,{base64.b64encode(in_file.read()).decode('utf-8')}") + + for i in range(0, 5): + with open(f"{args.output_dir}/{args.name + ('_' if args.name else '')}coverage_LDDT_{i}.png", "rb") as in_file: + alphfold_template = alphfold_template.replace(f"coverage_LDDT_{i}.png", f"data:image/png;base64,{base64.b64encode(in_file.read()).decode('utf-8')}") + +""" +with open(f"{args.output_dir}/{args.name + ('_' if args.name else '')}seq_coverage.html", "r") as in_file: + alphfold_template = alphfold_template.replace(f"seq_coverage.png", f"{in_file.read()}") +for i in range(0, 5): + with open(f"{args.output_dir}/{args.name + ('_' if args.name else '')}coverage_LDDT_{i}.html", "r") as in_file: + alphfold_template = alphfold_template.replace(f"coverage_LDDT_{i}.png", f"{in_file.read()}") +""" with open(f"{args.output_dir}/{args.name}_alphafold.html", "w") as out_file: out_file.write(alphfold_template) From 17eef1c396d694df8baa074c1ef5969f2c563d3a Mon Sep 17 00:00:00 2001 From: ziadbkh Date: Mon, 20 May 2024 13:29:26 +1000 Subject: [PATCH 007/227] add visulaisation report --- assets/NO_FILE | 0 bin/extract_output.py | 28 +++++++ {assets => bin}/generat_plots_2.py | 118 +++++++++++++++------------ conf/gadi.config | 26 +++--- conf/test_full_esmfold.config | 2 +- main.nf | 86 ++++++++++--------- modules/local/generat_report.nf | 18 ++-- modules/local/run_alphafold2.nf | 13 ++- modules/local/run_alphafold2_pred.nf | 12 ++- modules/local/run_esmfold.nf | 7 +- workflows/alphafold2.nf | 57 +++++++------ workflows/esmfold.nf | 21 ++++- 12 files changed, 239 insertions(+), 149 deletions(-) create mode 100644 assets/NO_FILE create mode 100755 bin/extract_output.py rename {assets => bin}/generat_plots_2.py (65%) mode change 100644 => 100755 diff --git a/assets/NO_FILE b/assets/NO_FILE new file mode 100644 index 00000000..e69de29b diff --git a/bin/extract_output.py b/bin/extract_output.py new file mode 100755 index 00000000..a43a8a3c --- /dev/null +++ b/bin/extract_output.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python +import pickle +import os, sys +import argparse + +def read_pkl(id, pkl_files): + for pkl_file in pkl_files: + dict_data = pickle.load(open(pkl_file,'rb')) + #print(dict_data.keys()) + if pkl_file.endswith("features.pkl"): + with open (f"{id}_msa.tsv", "w") as out_f: + for val in dict_data['msa']: + out_f.write("\t".join([str(x) for x in val]) + "\n") + else: + model_id = os.path.basename(pkl_file).replace("result_model_", "").replace("_pred_0.pkl", "") + with open (f"{id}_lddt_{model_id}.tsv", "w") as out_f: + out_f.write("\t".join([str(x) for x in dict_data['plddt']]) + "\n") + + +parser = argparse.ArgumentParser() +parser.add_argument('--pkls',dest='pkls',required=True, nargs="+") +parser.add_argument('--name',dest='name') +parser.add_argument('--output_dir',dest='output_dir') +parser.set_defaults(output_dir='') +parser.set_defaults(name='') +args = parser.parse_args() + +read_pkl(args.name, args.pkls) diff --git a/assets/generat_plots_2.py b/bin/generat_plots_2.py old mode 100644 new mode 100755 similarity index 65% rename from assets/generat_plots_2.py rename to bin/generat_plots_2.py index 5b7bc5ec..87982a25 --- a/assets/generat_plots_2.py +++ b/bin/generat_plots_2.py @@ -10,60 +10,69 @@ import plotly.graph_objects as go from plotly.subplots import make_subplots -def generate_output_images(msa_path, plddt_paths, name, out_dir): +def generate_output_images(msa_path, plddt_paths, name, out_dir, in_type): msa = [] - with open(msa_path, 'r') as in_file: - for line in in_file: - msa.append([int(x) for x in line.strip().split()]) + if not msa_path.endswith("NO_FILE"): + with open(msa_path, 'r') as in_file: + for line in in_file: + msa.append([int(x) for x in line.strip().split()]) - seqid = [] - for sequence in msa: - matches = [1.0 if first == other else 0.0 for first, other in zip(msa[0], sequence)] - seqid.append(sum(matches) / len(matches)) - - seqid_sort = sorted(range(len(seqid)), key=seqid.__getitem__) + seqid = [] + for sequence in msa: + matches = [1.0 if first == other else 0.0 for first, other in zip(msa[0], sequence)] + seqid.append(sum(matches) / len(matches)) - non_gaps = [] - for sequence in msa: - non_gaps.append([float(num != 21) if num != 21 else float('nan') for num in sequence]) - - sorted_non_gaps = [non_gaps[i] for i in seqid_sort] - final = [] - for sorted_seq, identity in zip(sorted_non_gaps, [seqid[i] for i in seqid_sort]): - final.append([value * identity if not isinstance(value, str) else value for value in sorted_seq]) + seqid_sort = sorted(range(len(seqid)), key=seqid.__getitem__) + non_gaps = [] + for sequence in msa: + non_gaps.append([float(num != 21) if num != 21 else float('nan') for num in sequence]) + sorted_non_gaps = [non_gaps[i] for i in seqid_sort] + final = [] + for sorted_seq, identity in zip(sorted_non_gaps, [seqid[i] for i in seqid_sort]): + final.append([value * identity if not isinstance(value, str) else value for value in sorted_seq]) - ################################################################## - plt.figure(figsize=(14, 14), dpi=100) - ################################################################## - plt.title("Sequence coverage") - plt.imshow(final, - interpolation='nearest', aspect='auto', - cmap="rainbow_r", vmin=0, vmax=1, origin='lower') - - column_counts = [0] * len(msa[0]) - for col in range(len(msa[0])): - for row in msa: - if row[col] != 21: - column_counts[col] += 1 - - plt.plot(column_counts, color='black') - plt.xlim(-0.5, len(msa[0]) - 0.5) - plt.ylim(-0.5, len(msa) - 0.5) - - plt.colorbar(label="Sequence identity to query", ) - plt.xlabel("Positions") - plt.ylabel("Sequences") - plt.savefig(f"{out_dir}/{name+('_' if name else '')}seq_coverage.png") + ################################################################## + plt.figure(figsize=(14, 14), dpi=100) + ################################################################## + plt.title("Sequence coverage") + plt.imshow(final, + interpolation='nearest', aspect='auto', + cmap="rainbow_r", vmin=0, vmax=1, origin='lower') + + column_counts = [0] * len(msa[0]) + for col in range(len(msa[0])): + for row in msa: + if row[col] != 21: + column_counts[col] += 1 + + plt.plot(column_counts, color='black') + plt.xlim(-0.5, len(msa[0]) - 0.5) + plt.ylim(-0.5, len(msa) - 0.5) + + plt.colorbar(label="Sequence identity to query", ) + plt.xlabel("Positions") + plt.ylabel("Sequences") + plt.savefig(f"{out_dir}/{name+('_' if name else '')}seq_coverage.png") + + ################################################################## - ################################################################## plddt_per_model = OrderedDict() plddt_paths_srt = plddt_paths plddt_paths_srt.sort() for plddt_path in plddt_paths_srt: with open(plddt_path, 'r') as in_file: - plddt_per_model[os.path.basename(plddt_path)[:-4]] = [float(x) for x in in_file.read().strip().split()] + if in_type == "ESM-FOLD": + plddt_per_model[os.path.basename(plddt_path)[:-4]] = [] + in_file.readline() + for line in in_file: + vals = line.strip().split() + #print(vals) + if len(vals) == 5: + plddt_per_model[os.path.basename(plddt_path)[:-4]].append(float(vals[-1].strip())) + else: + plddt_per_model[os.path.basename(plddt_path)[:-4]] = [float(x) for x in in_file.read().strip().split()] plt.figure(figsize=(14, 14), dpi=100) plt.title("Predicted LDDT per position") @@ -104,7 +113,7 @@ def generate_output_images(msa_path, plddt_paths, name, out_dir): plt.savefig(f"{out_dir}/{name+('_' if name else '')}PAE.png") """ ################################################################## - + def generate_plots(msa_path, plddt_paths, name, out_dir): msa = [] @@ -174,18 +183,20 @@ def generate_plots(msa_path, plddt_paths, name, out_dir): print("Starting..") parser = argparse.ArgumentParser() -parser.add_argument('--msa',dest='msa',required=True) -parser.add_argument('--plddt',dest='plddt',required=True, nargs="+") -parser.add_argument('--pdb',dest='pdb',required=True, nargs="+") -parser.add_argument('--name',dest='name') +parser.add_argument('--type', dest='in_type') +parser.add_argument('--msa', dest='msa',required=True) +parser.add_argument('--plddt', dest='plddt',required=True, nargs="+") +parser.add_argument('--pdb', dest='pdb',required=True, nargs="+") +parser.add_argument('--name', dest='name') parser.add_argument('--output_dir',dest='output_dir') parser.add_argument('--html_template',dest='html_template') parser.set_defaults(output_dir='') +parser.set_defaults(in_type='ESM-FOLD') parser.set_defaults(name='') args = parser.parse_args() -generate_output_images(args.msa, args.plddt, args.name, args.output_dir) +generate_output_images(args.msa, args.plddt, args.name, args.output_dir, args.in_type) #generate_plots(args.msa, args.plddt, args.name, args.output_dir) @@ -202,10 +213,13 @@ def generate_plots(msa_path, plddt_paths, name, out_dir): i += 1 if True: - with open(f"{args.output_dir}/{args.name + ('_' if args.name else '')}seq_coverage.png", "rb") as in_file: - alphfold_template = alphfold_template.replace(f"seq_coverage.png", f"data:image/png;base64,{base64.b64encode(in_file.read()).decode('utf-8')}") - - for i in range(0, 5): + if not args.msa.endswith("NO_FILE"): + with open(f"{args.output_dir}/{args.name + ('_' if args.name else '')}seq_coverage.png", "rb") as in_file: + alphfold_template = alphfold_template.replace("seq_coverage.png", f"data:image/png;base64,{base64.b64encode(in_file.read()).decode('utf-8')}") + else: + alphfold_template = alphfold_template.replace("seq_coverage.png","") + + for i in range(0, len(args.plddt)): with open(f"{args.output_dir}/{args.name + ('_' if args.name else '')}coverage_LDDT_{i}.png", "rb") as in_file: alphfold_template = alphfold_template.replace(f"coverage_LDDT_{i}.png", f"data:image/png;base64,{base64.b64encode(in_file.read()).decode('utf-8')}") diff --git a/conf/gadi.config b/conf/gadi.config index cf910ec1..d35ce0b0 100755 --- a/conf/gadi.config +++ b/conf/gadi.config @@ -20,22 +20,28 @@ params { input = 'https://raw.githubusercontent.com/nf-core/test-datasets/proteinfold/testdata/samplesheet/v1.0/samplesheet.csv' alphafold2_db = '/g/data/if89/alphafold2/standard/' use_dgxa100 = false + esmfold_params_path = '/g/data/if89/esm-fold/checkpoints' } process { - storage = "gdata/if89+scratch/${params.project}" + storage = "gdata/if89+scratch/${params.project}+gdata/${params.project}" if (params.use_gpu) { withName: 'RUN_ALPHAFOLD2_PRED|RUN_ALPHAFOLD2' { - if (params.use_dgxa100){ - queue = "dgxa100" - cpus = 16 - }else{ - queue = "gpuvolta" - cpus = 12 - } - gpus = 1 - + if (params.use_dgxa100){ + queue = "dgxa100" + cpus = 16 + }else{ + queue = "gpuvolta" + cpus = 12 + } + gpus = 1 } } + + withName: 'RUN_ESMFOLD' { + queue = "copyq" + cpus = 1 + time = 10.h + } } \ No newline at end of file diff --git a/conf/test_full_esmfold.config b/conf/test_full_esmfold.config index a3919070..64bb7628 100644 --- a/conf/test_full_esmfold.config +++ b/conf/test_full_esmfold.config @@ -18,5 +18,5 @@ params { mode = 'esmfold' esmfold_model_preset = 'monomer' input = 'https://raw.githubusercontent.com/nf-core/test-datasets/proteinfold/testdata/samplesheet/v1.0/samplesheet.csv' - esmfold_db = 's3://proteinfold-dataset/test-data/db/esmfold' + //esmfold_db = 's3://proteinfold-dataset/test-data/db/esmfold' } diff --git a/main.nf b/main.nf index 5b46e229..76ebb911 100644 --- a/main.nf +++ b/main.nf @@ -63,53 +63,50 @@ workflow NFCORE_PROTEINFOLD { if(params.mode == "alphafold2") { // // SUBWORKFLOW: Prepare Alphafold2 DBs - // - PREPARE_ALPHAFOLD2_DBS ( - params.alphafold2_db, - params.full_dbs, - params.bfd_path, - params.small_bfd_path, - params.alphafold2_params_path, - params.mgnify_path, - params.pdb70_path, - params.pdb_mmcif_path, - params.uniref30_alphafold2_path, - params.uniref90_path, - params.pdb_seqres_path, - params.uniprot_path, - params.bfd_link, - params.small_bfd_link, - params.alphafold2_params_link, - params.mgnify_link, - params.pdb70_link, - params.pdb_mmcif_link, - params.pdb_obsolete_link, - params.uniref30_alphafold2_link, - params.uniref90_link, - params.pdb_seqres_link, - params.uniprot_sprot_link, - params.uniprot_trembl_link - ) - ch_versions = ch_versions.mix(PREPARE_ALPHAFOLD2_DBS.out.versions) - // // WORKFLOW: Run nf-core/alphafold2 workflow // + + ch_params = Channel.fromPath( params.alphafold2_params_path ) + ch_mgnify = Channel.fromPath( params.mgnify_path ) + ch_pdb70 = Channel.fromPath( params.pdb70_path, type: 'dir' ) + ch_mmcif_files = Channel.fromPath( params.pdb_mmcif_path, type: 'dir' ) + ch_mmcif_obsolete = Channel.fromPath( params.pdb_mmcif_path, type: 'file' ) + ch_mmcif = ch_mmcif_files.mix(ch_mmcif_obsolete) + ch_uniref30 = Channel.fromPath( params.uniref30_alphafold2_path, type: 'any' ) + ch_uniref90 = Channel.fromPath( params.uniref90_path ) + ch_pdb_seqres = Channel.fromPath( params.pdb_seqres_path ) + ch_uniprot = Channel.fromPath( params.uniprot_path ) + ch_small_bfd = Channel.fromPath( params.small_bfd_path) + ch_bfd = Channel.fromPath( params.bfd_path) + + /*ch_params.view() + ch_params.first().view() + ch_bfd.ifEmpty([]).first().view() + ch_small_bfd.ifEmpty([]).first().view() + + ch_uniref90.first().view() + ch_pdb_seqres.first().view() + ch_uniprot.first().view() + + + */ + + ALPHAFOLD2 ( - ch_versions, params.full_dbs, params.alphafold2_mode, params.alphafold2_model_preset, - PREPARE_ALPHAFOLD2_DBS.out.params.first(), - PREPARE_ALPHAFOLD2_DBS.out.bfd.ifEmpty([]).first(), - PREPARE_ALPHAFOLD2_DBS.out.small_bfd.ifEmpty([]).first(), - PREPARE_ALPHAFOLD2_DBS.out.mgnify.first(), - PREPARE_ALPHAFOLD2_DBS.out.pdb70.first(), - PREPARE_ALPHAFOLD2_DBS.out.pdb_mmcif.first(), - PREPARE_ALPHAFOLD2_DBS.out.uniref30.first(), - PREPARE_ALPHAFOLD2_DBS.out.uniref90.first(), - PREPARE_ALPHAFOLD2_DBS.out.pdb_seqres.first(), - PREPARE_ALPHAFOLD2_DBS.out.uniprot.first() + ch_params.toList(), + ch_bfd.ifEmpty([]).first(), + ch_small_bfd.ifEmpty([]).first(), + ch_mgnify.first(), + ch_pdb70.first(), + ch_mmcif.toList(), + ch_uniref30.toList(), + ch_uniref90.first(), + ch_pdb_seqres.first(), + ch_uniprot.first() ) ch_multiqc = ALPHAFOLD2.out.multiqc_report ch_versions = ch_versions.mix(ALPHAFOLD2.out.versions) @@ -157,21 +154,22 @@ workflow NFCORE_PROTEINFOLD { // // SUBWORKFLOW: Prepare esmfold DBs // - PREPARE_ESMFOLD_DBS ( + /*PREPARE_ESMFOLD_DBS ( params.esmfold_db, params.esmfold_params_path, params.esmfold_3B_v1, params.esm2_t36_3B_UR50D, params.esm2_t36_3B_UR50D_contact_regression - ) - ch_versions = ch_versions.mix(PREPARE_ESMFOLD_DBS.out.versions) + )*/ + + //ch_versions = ch_versions.mix(PREPARE_ESMFOLD_DBS.out.versions) // // WORKFLOW: Run nf-core/esmfold workflow // ESMFOLD ( ch_versions, - PREPARE_ESMFOLD_DBS.out.params, + params.esmfold_params_path, params.num_recycle ) ch_multiqc = ESMFOLD.out.multiqc_report diff --git a/modules/local/generat_report.nf b/modules/local/generat_report.nf index 29865125..69875541 100644 --- a/modules/local/generat_report.nf +++ b/modules/local/generat_report.nf @@ -1,19 +1,20 @@ process GENERATE_REPORT { - tag "$id" + tag "${meta.id}" label 'process_single' container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? 'https://depot.galaxyproject.org/singularity/multiqc:1.21--pyhdfd78af_0' : 'biocontainers/multiqc:1.21--pyhdfd78af_0' }" input: - tuple val(id), path(msa) - tuple val(id), path(lddt) - tuple val(id), path(pdb) + tuple val(meta_msa), path(msa) + tuple val(meta), path(lddt) + tuple val(meta), path(pdb) path(template) - path(script) + val(output_type) + output: - tuple val(id), path ("*.html"), emit: report - tuple val(id), path ("*.png"), emit: images + tuple val(meta), path ("*.html"), emit: report + tuple val(meta), path ("*.png"), emit: images //path "versions.yml", emit: versions when: @@ -22,7 +23,6 @@ process GENERATE_REPORT { def args = task.ext.args ?: '' """ - #export MPLCONFIGDIR=\$PBS_JOBFS - python ./generat_plots_2.py --msa ${msa} --plddt ${lddt.join(' ')} --pdb ${pdb.join(' ')} --html_template ${template} --output_dir ./ --name ${id} || true + generat_plots_2.py --type ${output_type} --msa ${msa} --plddt ${lddt.join(' ')} --pdb ${pdb.join(' ')} --html_template ${template} --output_dir ./ --name ${meta.id} """ } diff --git a/modules/local/run_alphafold2.nf b/modules/local/run_alphafold2.nf index 5607712d..53c49e0b 100644 --- a/modules/local/run_alphafold2.nf +++ b/modules/local/run_alphafold2.nf @@ -28,7 +28,9 @@ process RUN_ALPHAFOLD2 { path ('uniprot/*') output: - path ("${fasta.baseName}*") + tuple val(meta), path ("${fasta.baseName}*"), emit: af_out + tuple val(meta), path ("${fasta.baseName}/${fasta.baseName}*tsv"), emit: af_out_tsv + tuple val(meta), path ("${fasta.baseName}/ranked*pdb"), emit: af_out_pdb path "*_mqc.tsv", emit: multiqc path "versions.yml", emit: versions @@ -72,6 +74,15 @@ process RUN_ALPHAFOLD2 { paste ranked_0_plddt.tsv ranked_1_plddt.tsv ranked_2_plddt.tsv ranked_3_plddt.tsv ranked_4_plddt.tsv > plddt.tsv echo -e Positions"\\t"rank_0"\\t"rank_1"\\t"rank_2"\\t"rank_3"\\t"rank_4 > header.tsv cat header.tsv plddt.tsv > ../"${fasta.baseName}"_plddt_mqc.tsv + + extract_output.py --name ${fasta.baseName} \\ + --pkls result_model_1_pred_0.pkl \\ + result_model_2_pred_0.pkl \\ + result_model_3_pred_0.pkl \\ + result_model_4_pred_0.pkl \\ + result_model_5_pred_0.pkl \\ + features.pkl + cd .. cat <<-END_VERSIONS > versions.yml diff --git a/modules/local/run_alphafold2_pred.nf b/modules/local/run_alphafold2_pred.nf index 66c9302f..88b4f878 100644 --- a/modules/local/run_alphafold2_pred.nf +++ b/modules/local/run_alphafold2_pred.nf @@ -30,7 +30,8 @@ process RUN_ALPHAFOLD2_PRED { output: tuple val(meta), path ("${fasta.baseName}*") - tuple val(meta), path ("${fasta.baseName}*/*"), emit: af_out + tuple val(meta), path ("${fasta.baseName}/${fasta.baseName}*tsv"), emit: af_out_tsv + tuple val(meta), path ("${fasta.baseName}/ranked*pdb"), emit: af_out_pdb path "*_mqc.tsv", emit: multiqc path "versions.yml", emit: versions @@ -59,6 +60,15 @@ process RUN_ALPHAFOLD2_PRED { paste ranked_0_plddt.tsv ranked_1_plddt.tsv ranked_2_plddt.tsv ranked_3_plddt.tsv ranked_4_plddt.tsv > plddt.tsv echo -e Positions"\\t"rank_0"\\t"rank_1"\\t"rank_2"\\t"rank_3"\\t"rank_4 > header.tsv cat header.tsv plddt.tsv > ../"${fasta.baseName}"_plddt_mqc.tsv + + extract_output.py --name ${fasta.baseName} \\ + --pkls result_model_1_pred_0.pkl \\ + result_model_2_pred_0.pkl \\ + result_model_3_pred_0.pkl \\ + result_model_4_pred_0.pkl \\ + result_model_5_pred_0.pkl \\ + ../${fasta.baseName}.features.pkl + cd .. cat <<-END_VERSIONS > versions.yml diff --git a/modules/local/run_esmfold.nf b/modules/local/run_esmfold.nf index 5f7a25ce..f4567239 100644 --- a/modules/local/run_esmfold.nf +++ b/modules/local/run_esmfold.nf @@ -10,12 +10,12 @@ process RUN_ESMFOLD { input: tuple val(meta), path(fasta) - path ('./checkpoints/') + path (esm_fold_parms) val numRec output: - path ("${fasta.baseName}*.pdb"), emit: pdb - path ("${fasta.baseName}_plddt_mqc.tsv"), emit: multiqc + tuple val(meta), path ("${fasta.baseName}*.pdb"), emit: pdb + tuple val(meta), path ("${fasta.baseName}_plddt_mqc.tsv"), emit: multiqc path "versions.yml", emit: versions when: @@ -33,6 +33,7 @@ process RUN_ESMFOLD { --num-recycles ${numRec} \ $args + mv *.pdb "${fasta.baseName}".pdb awk '{print \$2"\\t"\$3"\\t"\$4"\\t"\$6"\\t"\$11}' "${fasta.baseName}"*.pdb | grep -v 'N/A' | uniq > plddt.tsv echo -e Atom_serial_number"\\t"Atom_name"\\t"Residue_name"\\t"Residue_sequence_number"\\t"pLDDT > header.tsv cat header.tsv plddt.tsv > "${fasta.baseName}"_plddt_mqc.tsv diff --git a/workflows/alphafold2.nf b/workflows/alphafold2.nf index 3d2abe15..2374f34c 100644 --- a/workflows/alphafold2.nf +++ b/workflows/alphafold2.nf @@ -41,7 +41,6 @@ include { methodsDescriptionText } from '../subworkflows/local/utils_nfcore_prot workflow ALPHAFOLD2 { take: - ch_versions // channel: [ path(versions.yml) ] full_dbs // boolean: Use full databases (otherwise reduced version) alphafold2_mode // string: Mode to run Alphafold2 in alphafold2_model_preset // string: Specifies the model preset to use for Alphafold2 @@ -58,10 +57,12 @@ workflow ALPHAFOLD2 { main: ch_multiqc_files = Channel.empty() + ch_versions = Channel.empty() // // Create input channel from input file provided through params.input // + Channel .fromSamplesheet("input") .set { ch_fasta } @@ -81,6 +82,9 @@ workflow ALPHAFOLD2 { // // SUBWORKFLOW: Run Alphafold2 standard mode // + //full_dbs.view() + //ch_alphafold2_params.view() + RUN_ALPHAFOLD2 ( ch_fasta, full_dbs, @@ -96,6 +100,16 @@ workflow ALPHAFOLD2 { ch_pdb_seqres, ch_uniprot ) + RUN_ALPHAFOLD2.out.af_out_tsv + .map{[it[0], it[1].findAll{ it.getName().contains("_lddt_")}]} + .set{ch_af_out_lddt} + + RUN_ALPHAFOLD2.out.af_out_tsv + .map{[it[0], it[1].findAll{ it.getName().contains("_msa.tsv")}]} + .set{ch_af_out_msa} + + RUN_ALPHAFOLD2.out.af_out_pdb.set{ch_af_out_pdb} + ch_multiqc_rep = RUN_ALPHAFOLD2.out.multiqc.collect() ch_versions = ch_versions.mix(RUN_ALPHAFOLD2.out.versions) @@ -143,37 +157,28 @@ workflow ALPHAFOLD2 { ) ch_multiqc_rep = RUN_ALPHAFOLD2_PRED.out.multiqc.collect() ch_versions = ch_versions.mix(RUN_ALPHAFOLD2_PRED.out.versions) - RUN_ALPHAFOLD2_PRED.out.af_out.set{ch_alphafold_outputs} + RUN_ALPHAFOLD2_PRED.out.af_out_tsv + .map{[it[0], it[1].findAll{ it.getName().contains("_lddt_")}]} + .set{ch_af_out_lddt} + + RUN_ALPHAFOLD2_PRED.out.af_out_tsv + .map{[it[0], it[1].findAll{ it.getName().contains("_msa.tsv")}]} + .set{ch_af_out_msa} + + RUN_ALPHAFOLD2_PRED.out.af_out_pdb.set{ch_af_out_pdb} } - ch_alphafold_outputs - .map{[it[0].id, it[1].findAll { it.getName().endsWith('.pkl') && it.getName().startsWith('result_model_') } ]} - .set{ch_pred} - - EXTRACT_OUTPUTS(ch_features.mix(ch_pred)) - - - ch_alphafold_outputs - .map{[it[0].id, it[1].findAll { it.getName().endsWith('.pdb') && it.getName().startsWith('ranked_') } ]} - .set{ch_pdb} - - - EXTRACT_OUTPUTS.out.msa_info.join( - EXTRACT_OUTPUTS.out.lddt_info.join( - ch_pdb - ) - ).set{ch_all} - GENERATE_REPORT( - ch_all.map{[it[0], it[1]]}, - ch_all.map{[it[0], it[2]]}, - ch_all.map{[it[0], it[3]]}, + ch_af_out_msa, + ch_af_out_lddt, + ch_af_out_pdb, Channel.fromPath("$projectDir/assets/alphafold_template.html").first(), - Channel.fromPath("$projectDir/assets/generat_plots_2.py").first(), - + Channel.value("ALPHAFOLD2") ) - + + + // // Collate and save software versions // diff --git a/workflows/esmfold.nf b/workflows/esmfold.nf index 4bf7e2a4..84307370 100644 --- a/workflows/esmfold.nf +++ b/workflows/esmfold.nf @@ -9,7 +9,7 @@ // include { RUN_ESMFOLD } from '../modules/local/run_esmfold' include { MULTIFASTA_TO_SINGLEFASTA } from '../modules/local/multifasta_to_singlefasta' - +include { GENERATE_REPORT } from '../modules/local/generat_report' /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ IMPORT NF-CORE MODULES/SUBWORKFLOWS @@ -21,6 +21,7 @@ include { MULTIFASTA_TO_SINGLEFASTA } from '../modules/local/multifasta_to_singl // include { MULTIQC } from '../modules/nf-core/multiqc/main' + // // SUBWORKFLOW: Consisting entirely of nf-core/modules // @@ -76,6 +77,21 @@ workflow ESMFOLD { ch_versions = ch_versions.mix(RUN_ESMFOLD.out.versions) } + RUN_ESMFOLD.out.multiqc + .map{[it[0].id, it[0], it[1]]} + .join( + RUN_ESMFOLD.out.pdb + .map{[it[0].id, it[0], it[1]]} + ).set{ch_all} + + GENERATE_REPORT( + Channel.of([["id":"TEMP"], file("$projectDir/assets/NO_FILE")]), + ch_all.map{[it[1], [it[2]]]}, + ch_all.map{[it[3], [it[4]]]}, + Channel.fromPath("$projectDir/assets/alphafold_template.html").first(), + Channel.value("ESM-FOLD") + ) + // // Collate and save software versions // @@ -99,7 +115,7 @@ workflow ESMFOLD { ch_multiqc_files = ch_multiqc_files.mix(ch_workflow_summary.collectFile(name: 'workflow_summary_mqc.yaml')) ch_multiqc_files = ch_multiqc_files.mix(ch_methods_description.collectFile(name: 'methods_description_mqc.yaml')) ch_multiqc_files = ch_multiqc_files.mix(ch_collated_versions) - ch_multiqc_files = ch_multiqc_files.mix(RUN_ESMFOLD.out.multiqc.collect()) + ch_multiqc_files = ch_multiqc_files.mix(RUN_ESMFOLD.out.multiqc.map{it[1]}.collect()) MULTIQC ( ch_multiqc_files.collect(), @@ -108,6 +124,7 @@ workflow ESMFOLD { ch_multiqc_logo.toList() ) ch_multiqc_report = MULTIQC.out.report.toList() + emit: multiqc_report = ch_multiqc_report // channel: /path/to/multiqc_report.html versions = ch_versions // channel: [ path(versions.yml) ] From 491b3552cec92e069583d70445cd3a074d74783f Mon Sep 17 00:00:00 2001 From: ziadbkh Date: Mon, 20 May 2024 15:23:03 +1000 Subject: [PATCH 008/227] fix split msa --- modules/local/run_alphafold2_msa.nf | 4 ++-- workflows/alphafold2.nf | 14 +++++++++----- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/modules/local/run_alphafold2_msa.nf b/modules/local/run_alphafold2_msa.nf index 6c9bfcf8..28f8ceb9 100644 --- a/modules/local/run_alphafold2_msa.nf +++ b/modules/local/run_alphafold2_msa.nf @@ -28,8 +28,8 @@ process RUN_ALPHAFOLD2_MSA { path ('uniprot/*') output: - path ("${fasta.baseName}*") - path ("${fasta.baseName}.features.pkl"), emit: features + tuple val(meta), path ("${fasta.baseName}*") + tuple val(meta), path ("${fasta.baseName}.features.pkl"), emit: features path "versions.yml" , emit: versions when: diff --git a/workflows/alphafold2.nf b/workflows/alphafold2.nf index 2374f34c..0ae9ac8e 100644 --- a/workflows/alphafold2.nf +++ b/workflows/alphafold2.nf @@ -136,11 +136,15 @@ workflow ALPHAFOLD2 { ch_versions = ch_versions.mix(RUN_ALPHAFOLD2_MSA.out.versions) RUN_ALPHAFOLD2_MSA.out.features - .map{[it.getName().split("\\.")[0], [it]]} - .set{ch_features} - + .map{[it[0].id, it[0], it[1]]} + .join( + ch_fasta + .map{[it[0].id, it[0], it[1]]} + ) + .set{ch_af_all} + RUN_ALPHAFOLD2_PRED ( - ch_fasta, + ch_af_all.map{[it[3], it[4]]}, full_dbs, alphafold2_model_preset, ch_alphafold2_params, @@ -153,7 +157,7 @@ workflow ALPHAFOLD2 { ch_uniref90, ch_pdb_seqres, ch_uniprot, - RUN_ALPHAFOLD2_MSA.out.features + ch_af_all.map{it[2]} ) ch_multiqc_rep = RUN_ALPHAFOLD2_PRED.out.multiqc.collect() ch_versions = ch_versions.mix(RUN_ALPHAFOLD2_PRED.out.versions) From d6b0fff4b501f625e10c8578663b36b801bcdd6c Mon Sep 17 00:00:00 2001 From: ziadbkh Date: Mon, 20 May 2024 16:14:03 +1000 Subject: [PATCH 009/227] fix split esm-fold nofile channel --- workflows/esmfold.nf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workflows/esmfold.nf b/workflows/esmfold.nf index 84307370..841ef048 100644 --- a/workflows/esmfold.nf +++ b/workflows/esmfold.nf @@ -85,7 +85,7 @@ workflow ESMFOLD { ).set{ch_all} GENERATE_REPORT( - Channel.of([["id":"TEMP"], file("$projectDir/assets/NO_FILE")]), + Channel.value([["id":"TEMP"], file("$projectDir/assets/NO_FILE")]), ch_all.map{[it[1], [it[2]]]}, ch_all.map{[it[3], [it[4]]]}, Channel.fromPath("$projectDir/assets/alphafold_template.html").first(), From d200e82bfda8200d7f268a5d88653524a6857981 Mon Sep 17 00:00:00 2001 From: ziadbkh Date: Mon, 1 Jul 2024 14:33:22 +1000 Subject: [PATCH 010/227] reports-update --- assets/alphafold_template.html | 17 +++++++++++++++++ bin/generat_plots_2.py | 11 +++++++++++ conf/dbs.config | 2 +- conf/gadi.config | 20 +++++++++++++------- nextflow.config | 1 - nextflow_schema.json | 23 ++++++++++++++++++++++- tower.yml | 2 ++ workflows/colabfold.nf | 4 ++-- workflows/esmfold.nf | 1 + 9 files changed, 69 insertions(+), 12 deletions(-) diff --git a/assets/alphafold_template.html b/assets/alphafold_template.html index 533e2fee..1655e078 100755 --- a/assets/alphafold_template.html +++ b/assets/alphafold_template.html @@ -570,6 +570,23 @@

Download

}) } + const loadModel_n = () => { + reps = Object.keys(state.representations); + if (reps.length) { + state.representations = {}; + } else { + reps = [DEFAULT_REPRESENTATION]; + } + + // Load PDB entry + return stage.loadFile( uri(state.model) ).then( (o) => { + state.modelObject = o; + reps.forEach( (r) => addModelRepresentation(r) ); + stage.setSpin(state.spin); + o.autoView(); + setLoading(0); + }) + } // Representations --------------------------------------------------------- const toggleModelRepresentation = (rep) => { diff --git a/bin/generat_plots_2.py b/bin/generat_plots_2.py index 87982a25..bf6bfbba 100755 --- a/bin/generat_plots_2.py +++ b/bin/generat_plots_2.py @@ -207,21 +207,29 @@ def generate_plots(msa_path, plddt_paths, name, out_dir): alphfold_template = open(args.html_template, "r").read() alphfold_template = alphfold_template.replace(f"*sample_name_here*", args.name) +alphfold_template2 = open(args.html_template, "r").read() +alphfold_template2 = alphfold_template2.replace(f"*sample_name_here*", args.name) + + i = 0 for structure in structures: alphfold_template = alphfold_template.replace(f"*_data_ranked_{i}.pdb*", open(structure, "r").read().replace("\n", "\\n")) + alphfold_template2 = alphfold_template2.replace(f"*_data_ranked_{i}.pdb*", structure) i += 1 if True: if not args.msa.endswith("NO_FILE"): with open(f"{args.output_dir}/{args.name + ('_' if args.name else '')}seq_coverage.png", "rb") as in_file: alphfold_template = alphfold_template.replace("seq_coverage.png", f"data:image/png;base64,{base64.b64encode(in_file.read()).decode('utf-8')}") + alphfold_template2 = alphfold_template2.replace("seq_coverage.png", f"{args.name + ('_' if args.name else '')}seq_coverage.png") else: alphfold_template = alphfold_template.replace("seq_coverage.png","") + alphfold_template2 = alphfold_template2.replace("seq_coverage.png","") for i in range(0, len(args.plddt)): with open(f"{args.output_dir}/{args.name + ('_' if args.name else '')}coverage_LDDT_{i}.png", "rb") as in_file: alphfold_template = alphfold_template.replace(f"coverage_LDDT_{i}.png", f"data:image/png;base64,{base64.b64encode(in_file.read()).decode('utf-8')}") + alphfold_template2 = alphfold_template2.replace(f"coverage_LDDT_{i}.png", f"{args.name + ('_' if args.name else '')}coverage_LDDT_{i}.png") """ @@ -235,3 +243,6 @@ def generate_plots(msa_path, plddt_paths, name, out_dir): """ with open(f"{args.output_dir}/{args.name}_alphafold.html", "w") as out_file: out_file.write(alphfold_template) + +with open(f"{args.output_dir}/{args.name}_alphafold2.html", "w") as out_file: + out_file.write(alphfold_template2) \ No newline at end of file diff --git a/conf/dbs.config b/conf/dbs.config index d8a9c126..f29e6c9a 100644 --- a/conf/dbs.config +++ b/conf/dbs.config @@ -54,5 +54,5 @@ params { esm2_t36_3B_UR50D_contact_regression = 'https://dl.fbaipublicfiles.com/fair-esm/regression/esm2_t36_3B_UR50D-contact-regression.pt' // Esmfold paths - esmfold_params_path = "${params.esmfold_db}/*" + //esmfold_params_path = "${params.esmfold_db}/*" } diff --git a/conf/gadi.config b/conf/gadi.config index d35ce0b0..23127eeb 100755 --- a/conf/gadi.config +++ b/conf/gadi.config @@ -12,22 +12,28 @@ if (params.use_gpu) { singularity.runOptions = '--nv' } + singularity.cacheDir = "/g/data/if89/singularity_cache/" singularity.autoMounts = true + params { mode = 'alphafold2' alphafold2_mode = 'split_msa_prediction' input = 'https://raw.githubusercontent.com/nf-core/test-datasets/proteinfold/testdata/samplesheet/v1.0/samplesheet.csv' - alphafold2_db = '/g/data/if89/alphafold2/standard/' + alphafold2_db = '/g/data/if89/protienfold_dbs/alphafold2_dbs/' use_dgxa100 = false - esmfold_params_path = '/g/data/if89/esm-fold/checkpoints' + esmfold_params_path = '/g/data/if89/protienfold_dbs/esmfold_dbs/checkpoints' + colabfold_db = "/g/data/if89/protienfold_dbs/colabfold_dbs" + colabfold_server = 'local' + colabfold_model_preset = 'alphafold2_ptm' + } process { storage = "gdata/if89+scratch/${params.project}+gdata/${params.project}" if (params.use_gpu) { - withName: 'RUN_ALPHAFOLD2_PRED|RUN_ALPHAFOLD2' { + withName: 'RUN_ALPHAFOLD2_PRED|RUN_ALPHAFOLD2|RUN_ESMFOLD' { if (params.use_dgxa100){ queue = "dgxa100" cpus = 16 @@ -39,9 +45,9 @@ process { } } - withName: 'RUN_ESMFOLD' { - queue = "copyq" - cpus = 1 - time = 10.h + withName: 'MMSEQS_COLABFOLDSEARCH' { + memory = 300.GB + time = 16.h } + } \ No newline at end of file diff --git a/nextflow.config b/nextflow.config index aca4fe7a..cac3227b 100644 --- a/nextflow.config +++ b/nextflow.config @@ -13,7 +13,6 @@ params { input = null mode = 'alphafold2' // {alphafold2, colabfold, esmfold} use_gpu = false - // Alphafold2 parameters alphafold2_mode = "standard" max_template_date = "2020-05-14" diff --git a/nextflow_schema.json b/nextflow_schema.json index d2ef75da..bcd9c169 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -29,7 +29,7 @@ "description": "The output directory where the results will be saved. You have to use absolute paths to storage on Cloud infrastructure.", "fa_icon": "fas fa-folder-open" }, - "mode": { + "mode": { "type": "string", "default": "alphafold2", "description": "Specifies the mode in which the pipeline will be run", @@ -194,6 +194,27 @@ } } }, + "gadi_config_options": { + "title": "Institutional config options", + "type": "object", + "fa_icon": "fas fa-university", + "description": "Configurations for running on GADI at NCI.", + "help_text": ".", + "properties": { + "use_dgxa100": { + "type": "boolean", + "default": false, + "description": "If true uses the DGXA 100 GPU nodes", + "fa_icon": "fas fa-battery-full" + }, + "project": { + "type": "string", + "description": "Thge project code on GADI.", + "default": "", + "fa_icon": "fas fa-users-cog" + } + } + }, "institutional_config_options": { "title": "Institutional config options", "type": "object", diff --git a/tower.yml b/tower.yml index c0b95ad4..5e889484 100755 --- a/tower.yml +++ b/tower.yml @@ -5,3 +5,5 @@ reports: display: "Auto-created samplesheet with collated metadata and FASTQ paths" "*_alphafold.html": display: "Predected structures" + "*_alphafold2.html": + display: "Predected structures 2" diff --git a/workflows/colabfold.nf b/workflows/colabfold.nf index dd38fd0f..ecc37af4 100644 --- a/workflows/colabfold.nf +++ b/workflows/colabfold.nf @@ -56,7 +56,7 @@ workflow COLABFOLD { Channel .fromSamplesheet("input") .set { ch_fasta } - + ch_fasta.view() if (params.colabfold_server == 'webserver') { // // MODULE: Run colabfold @@ -91,7 +91,7 @@ workflow COLABFOLD { // // MODULE: Run mmseqs // - if (params.colabfold_model_preset != 'AlphaFold2-ptm') { + if (params.colabfold_model_preset != 'alphafold2_ptm') { MULTIFASTA_TO_CSV( ch_fasta ) diff --git a/workflows/esmfold.nf b/workflows/esmfold.nf index 841ef048..47ec985e 100644 --- a/workflows/esmfold.nf +++ b/workflows/esmfold.nf @@ -50,6 +50,7 @@ workflow ESMFOLD { // // Create input channel from input file provided through params.input // + Channel .fromSamplesheet("input") .set { ch_fasta } From 80ed298453f2eea024d033acc15e2498a5ce1ff4 Mon Sep 17 00:00:00 2001 From: ziadbkh Date: Tue, 2 Jul 2024 14:53:17 +1000 Subject: [PATCH 011/227] add plots --- tower.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tower.yml b/tower.yml index 5e889484..b6d2d138 100755 --- a/tower.yml +++ b/tower.yml @@ -7,3 +7,5 @@ reports: display: "Predected structures" "*_alphafold2.html": display: "Predected structures 2" + "*.png": + display: "plots" \ No newline at end of file From f419edd8e85993b3e588869c959cddcd3f30a30f Mon Sep 17 00:00:00 2001 From: ziadbkh Date: Fri, 19 Jul 2024 17:01:05 +1000 Subject: [PATCH 012/227] new report --- assets/alphafold_template.html | 1253 ++++++++++------- assets/alphafold_template_v0.html | 705 ++++++++++ bin/generat_plots.py | 341 +++++ ...enerat_plots_2.py => generat_plots_old.py} | 0 conf/test.config | 2 +- main.nf | 10 - modules/local/generat_report.nf | 3 +- modules/local/old_run_alphafold2.nf | 57 + workflows/alphafold2.nf | 2 +- 9 files changed, 1846 insertions(+), 527 deletions(-) create mode 100755 assets/alphafold_template_v0.html create mode 100755 bin/generat_plots.py rename bin/{generat_plots_2.py => generat_plots_old.py} (100%) create mode 100644 modules/local/old_run_alphafold2.nf diff --git a/assets/alphafold_template.html b/assets/alphafold_template.html index 1655e078..1074c1c4 100755 --- a/assets/alphafold_template.html +++ b/assets/alphafold_template.html @@ -1,496 +1,650 @@ - - + - - - - - Alphafold structure prediction - - - - - - + + + + Alphafold structure prediction + + + + + - - - -

Alphafold structure prediction

-
-
-
-
-
-
- -
- -
- Select a representation to display -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- -
-
-

- Scroll up/down - to zoom in and out -

-

- Click + drag - to rotate the structure -

-

- CTRL + click + drag - to move the structure -

-

- Click - an atom to bring it into focus -

-
- -
-
-
-
-
<50
-
70
-
90+
-
-
- -
-

- - Alphafold produces a - - per-residue confidence score (pLDDT) - - between 0 and 100. Some regions below 50 pLDDT may be - unstructured in isolation. - -

-
-
+ + + + + + + + +
+
+

+ Alphafold structure prediction (*sample_name_here*) +

+
+
+ + +
+ +
+ +
+ + + + + +
+ +
+ + + + + +
+ +
+
+ Navigation +
+
+
+ Scroll up/down + to zoom in and out +
+
+ Click + drag + to rotate the structure +
+
+ CTRL + click + drag + to move the structure +
+
+ Click + an atom to bring it into focus +
+
+
+ + +
+
+
+ Toggle representations +
+
+ + + + +
+
+ +
+
+ Actions +
+
+ + + +
+
+ +
+
+ Download +
+ +
+ +
-
-
-
-
- +
+ +
+
+
+
+
+
+
+ + +
+
+
+ Sequence Coverage +
+
+
+ +
+
+ +
+
+ pLDDT +
+
+ +
+
+
+
+
+ +
- + +
+
+
+ + +
+
+

+ The Australian BioCommons + is supported by + Bioplatforms Australia +

+

+ Bioplatforms Australia + is enabled by + NCRIS +

+
+
+
+ - diff --git a/assets/alphafold_template_v0.html b/assets/alphafold_template_v0.html new file mode 100755 index 00000000..1655e078 --- /dev/null +++ b/assets/alphafold_template_v0.html @@ -0,0 +1,705 @@ + + + + + + + + + Alphafold structure prediction + + + + + + + + + + + + + + +

Alphafold structure prediction

+
+
+
+
+
+
+ +
+ +
+ Select a representation to display +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+

+ Scroll up/down + to zoom in and out +

+

+ Click + drag + to rotate the structure +

+

+ CTRL + click + drag + to move the structure +

+

+ Click + an atom to bring it into focus +

+
+ +
+
+
+
+
<50
+
70
+
90+
+
+
+ +
+

+ + Alphafold produces a + + per-residue confidence score (pLDDT) + + between 0 and 100. Some regions below 50 pLDDT may be + unstructured in isolation. + +

+
+
+
+
+ +
+
+

Select model

+

The top-ranked structures predicted by Alphafold

+
+ + + + + + + + + +
+
+ +
+

Toggle representations

+
+ + + + + + + +
+
+ +
+

Actions

+
+ + + +
+
+ +
+

Download

+
+ + + +
+
+
+
+
+
+
+
+
+
+ +
+
+
+ + + + + + diff --git a/bin/generat_plots.py b/bin/generat_plots.py new file mode 100755 index 00000000..cc666ae6 --- /dev/null +++ b/bin/generat_plots.py @@ -0,0 +1,341 @@ +#!/usr/bin/env python + +import os +from matplotlib import pyplot as plt +import argparse +from collections import OrderedDict +import base64 +import os +from collections import OrderedDict +import plotly.graph_objects as go +from plotly.subplots import make_subplots + + +#from Bio import PDB + +def generate_output_images(msa_path, plddt_paths, name, out_dir, in_type): + msa = [] + if not msa_path.endswith("NO_FILE"): + with open(msa_path, 'r') as in_file: + for line in in_file: + msa.append([int(x) for x in line.strip().split()]) + + seqid = [] + for sequence in msa: + matches = [1.0 if first == other else 0.0 for first, other in zip(msa[0], sequence)] + seqid.append(sum(matches) / len(matches)) + + seqid_sort = sorted(range(len(seqid)), key=seqid.__getitem__) + + non_gaps = [] + for sequence in msa: + non_gaps.append([float(num != 21) if num != 21 else float('nan') for num in sequence]) + + sorted_non_gaps = [non_gaps[i] for i in seqid_sort] + final = [] + for sorted_seq, identity in zip(sorted_non_gaps, [seqid[i] for i in seqid_sort]): + final.append([value * identity if not isinstance(value, str) else value for value in sorted_seq]) + + # ################################################################## + plt.figure(figsize=(14, 14), dpi=100) + # ################################################################## + plt.title("Sequence coverage", fontsize=30, pad=36) + plt.imshow(final, + interpolation='nearest', aspect='auto', + cmap="rainbow_r", vmin=0, vmax=1, origin='lower') + + column_counts = [0] * len(msa[0]) + for col in range(len(msa[0])): + for row in msa: + if row[col] != 21: + column_counts[col] += 1 + + plt.plot(column_counts, color='black') + plt.xlim(-0.5, len(msa[0]) - 0.5) + plt.ylim(-0.5, len(msa) - 0.5) + + plt.tick_params(axis='both', which='both', labelsize=18) + + cbar = plt.colorbar() + cbar.set_label("Sequence identity to query", fontsize=24, labelpad=24) + cbar.ax.tick_params(labelsize=18) + plt.xlabel("Positions", fontsize=24, labelpad=24) + plt.ylabel("Sequences", fontsize=24, labelpad=36) + plt.savefig(f"{out_dir}/{name+('_' if name else '')}seq_coverage.png") + + # ################################################################## + + plddt_per_model = OrderedDict() + plddt_paths_srt = plddt_paths + plddt_paths_srt.sort() + for plddt_path in plddt_paths_srt: + with open(plddt_path, 'r') as in_file: + if in_type == "ESM-FOLD": + plddt_per_model[os.path.basename(plddt_path)[:-4]] = [] + in_file.readline() + for line in in_file: + vals = line.strip().split() + #print(vals) + if len(vals) == 5: + plddt_per_model[os.path.basename(plddt_path)[:-4]].append(float(vals[-1].strip())) + else: + plddt_per_model[os.path.basename(plddt_path)[:-4]] = [float(x) for x in in_file.read().strip().split()] + + # plt.figure(figsize=(14, 14), dpi=100) + # plt.title("Predicted LDDT per position") + # for model_name, value_plddt in plddt_per_model.items(): + # plt.plot(value_plddt, label=model_name) + # plt.ylim(0, 100) + # plt.ylabel("Predicted LDDT") + # plt.xlabel("Positions") + # plt.savefig(f"{out_dir}/{name+('_' if name else '')}coverage_LDDT.png") + + # # split into figures + # i = 0 + # for model_name, value_plddt in plddt_per_model.items(): + # plt.figure(figsize=(14, 14), dpi=100) + # plt.title("Predicted LDDT per position") + # plt.plot(value_plddt, label=model_name) + # plt.ylim(0, 100) + # plt.ylabel("Predicted LDDT") + # plt.xlabel("Positions") + # plt.savefig(f"{out_dir}/{name+('_' if name else '')}coverage_LDDT_{i}.png") + # i += 1 + + fig = go.Figure() + for idx, (model_name, value_plddt) in enumerate(plddt_per_model.items()): + rank_label = f"Ranked {idx}" + fig.add_trace(go.Scatter( + x=list(range(len(value_plddt))), + y=value_plddt, + mode='lines', + name=rank_label, + text=[f"Position {i}: {value:.2f}" for i, value in enumerate(value_plddt)], + hoverinfo='text' + )) + fig.update_layout( + title=dict( + text='Predicted LDDT per position', + x=0.5, + xanchor='center' + ), + xaxis=dict( + title='Positions', + showline=True, + linecolor='black', + gridcolor='WhiteSmoke' + ), + yaxis=dict( + title='Predicted LDDT', + range=[0, 100], + minallowed=0, + maxallowed=100, + showline=True, + linecolor='black', + gridcolor='WhiteSmoke' + ), + legend=dict( + yanchor="bottom", + y=0, + xanchor="right", + x=1.3 + ), + plot_bgcolor='white', + width=600, + height=600, + modebar_remove=['toImage', 'zoomIn', 'zoomOut'] + ) + html_content = fig.to_html(full_html=False, config={'displayModeBar': True, 'displaylogo': False, 'scrollZoom': True}) + + with open(f"{out_dir}/{name+('_' if name else '')}coverage_LDDT.html", "w") as out_file: + out_file.write(html_content) + + + ################################################################## + + + ################################################################## + """ + num_models = 5 # columns + num_runs_per_model = math.ceil(len(model_names)/num_models) + fig = plt.figure(figsize=(3 * num_models, 2 * num_runs_per_model), dpi=100) + for n, (model_name, value) in enumerate(pae_plddt_per_model.items()): + plt.subplot(num_runs_per_model, num_models, n + 1) + plt.title(model_name) + plt.imshow(value["pae"], label=model_name, cmap="bwr", vmin=0, vmax=30) + plt.colorbar() + fig.tight_layout() + plt.savefig(f"{out_dir}/{name+('_' if name else '')}PAE.png") + """ + ################################################################## + + +def generate_plots(msa_path, plddt_paths, name, out_dir): + msa = [] + with open(msa_path, 'r') as in_file: + for line in in_file: + msa.append([int(x) for x in line.strip().split()]) + + seqid = [] + for sequence in msa: + matches = [1.0 if first == other else 0.0 for first, other in zip(msa[0], sequence)] + seqid.append(sum(matches) / len(matches)) + + seqid_sort = sorted(range(len(seqid)), key=seqid.__getitem__) + + non_gaps = [] + for sequence in msa: + non_gaps.append([float(num != 21) if num != 21 else float('nan') for num in sequence]) + + sorted_non_gaps = [non_gaps[i] for i in seqid_sort] + final = [] + for sorted_seq, identity in zip(sorted_non_gaps, [seqid[i] for i in seqid_sort]): + final.append([value * identity if not isinstance(value, str) else value for value in sorted_seq]) + + # Plotting Sequence Coverage using Plotly + fig = go.Figure() + fig.add_trace(go.Heatmap( + z=final, + colorscale="Rainbow", + zmin=0, + zmax=1, + )) + fig.update_layout( + title="Sequence coverage", + xaxis_title="Positions", + yaxis_title="Sequences" + ) + # Save as interactive HTML instead of an image + fig.savefig(f"{out_dir}/{name+('_' if name else '')}seq_coverage.png") + """ + #fig.to_html(full_html=False).write_html(f"{out_dir}/{name+('_' if name else '')}seq_coverage.html") + with open (f"{out_dir}/{name+('_' if name else '')}seq_coverage.html", "w") as out_plt: + out_plt.write(fig.to_html(full_html=False)) + """ + # Plotting Predicted LDDT per position using Plotly + plddt_per_model = OrderedDict() + plddt_paths.sort() + for plddt_path in plddt_paths: + with open(plddt_path, 'r') as in_file: + plddt_per_model[os.path.basename(plddt_path)[:-4]] = [float(x) for x in in_file.read().strip().split()] + + i = 0 + for model_name, value_plddt in plddt_per_model.items(): + fig = go.Figure() + fig.add_trace(go.Scatter( + x=list(range(len(value_plddt))), + y=value_plddt, + mode='lines', + name=model_name + )) + fig.update_layout(title="Predicted LDDT per Position") + fig.savefig(f"{out_dir}/{name+('_' if name else '')}coverage_LDDT_{i}.png") + """ + with open (f"{out_dir}/{name+('_' if name else '')}coverage_LDDT_{i}.html", "w") as out_plt: + out_plt.write(fig.to_html(full_html=False).replace("\"", "\\\"")) + """ + i += 1 + +def align_structures(structures): + return structures + """ + parser = PDB.PDBParser(QUIET=True) + structures = [parser.get_structure(f'Structure_{i}', pdb) for i, pdb in enumerate(structures)] + + ref_structure = structures[0] + ref_atoms = [atom for atom in ref_structure.get_atoms()] + + super_imposer = PDB.Superimposer() + aligned_structures = [structures[0]] # Include the reference structure in the list + + for i, structure in enumerate(structures[1:], start=1): + target_atoms = [atom for atom in structure.get_atoms()] + + super_imposer.set_atoms(ref_atoms, target_atoms) + super_imposer.apply(structure.get_atoms()) + + aligned_structure = f'aligned_structure_{i}.pdb' + io = PDB.PDBIO() + io.set_structure(structure) + io.save(aligned_structure) + aligned_structures.append(aligned_structure) + + return aligned_structures + """ + +print("Starting..") +parser = argparse.ArgumentParser() +parser.add_argument('--type', dest='in_type') +parser.add_argument('--msa', dest='msa',required=True) +parser.add_argument('--plddt', dest='plddt',required=True, nargs="+") +parser.add_argument('--pdb', dest='pdb',required=True, nargs="+") +parser.add_argument('--name', dest='name') +parser.add_argument('--output_dir',dest='output_dir') +parser.add_argument('--html_template',dest='html_template') +parser.set_defaults(output_dir='') +parser.set_defaults(in_type='ESM-FOLD') +parser.set_defaults(name='') +args = parser.parse_args() + + +generate_output_images(args.msa, args.plddt, args.name, args.output_dir, args.in_type) + +#generate_plots(args.msa, args.plddt, args.name, args.output_dir) + +print("generating html report...") +structures = args.pdb +structures.sort() +aligned_structures = align_structures(structures) + +## Ziad uncomment this +""" +io = PDB.PDBIO() +ref_structure_path = 'aligned_structure_0.pdb' +io.set_structure(aligned_structures[0]) +io.save(ref_structure_path) +aligned_structures[0] = ref_structure_path +""" +alphfold_template = open(args.html_template, "r").read() +alphfold_template = alphfold_template.replace(f"*sample_name_here*", args.name) + +i = 0 +for structure in aligned_structures: + alphfold_template = alphfold_template.replace(f"*_data_ranked_{i}.pdb*", open(structure, "r").read().replace("\n", "\\n")) + i += 1 + +if True: + if not args.msa.endswith("NO_FILE"): + with open(f"{args.output_dir}/{args.name + ('_' if args.name else '')}seq_coverage.png", "rb") as in_file: + alphfold_template = alphfold_template.replace("seq_coverage.png", f"data:image/png;base64,{base64.b64encode(in_file.read()).decode('utf-8')}") + + # with open(f"{args.output_dir}/{args.name + ('_' if args.name else '')}seq_coverage.html", "r") as in_file: + # seq_cov_html = in_file.read() + # alphfold_template = alphfold_template.replace("
", seq_cov_html) + + else: + alphfold_template = alphfold_template.replace("seq_coverage.png","") + + # for i in range(0, len(args.plddt)): + # with open(f"{args.output_dir}/{args.name + ('_' if args.name else '')}coverage_LDDT_{i}.png", "rb") as in_file: + # alphfold_template = alphfold_template.replace(f"coverage_LDDT_{i}.png", f"data:image/png;base64,{base64.b64encode(in_file.read()).decode('utf-8')}") + + # for i in range(0, len(args.plddt)): + # with open(f"{args.output_dir}/{args.name + ('_' if args.name else '')}coverage_LDDT_{i}.html", "r") as in_file: + # lddt_html = in_file.read() + # alphfold_template = alphfold_template.replace("
", lddt_html) + + with open(f"{args.output_dir}/{args.name + ('_' if args.name else '')}coverage_LDDT.html", "r") as in_file: + lddt_html = in_file.read() + alphfold_template = alphfold_template.replace("
", lddt_html) + +""" +with open(f"{args.output_dir}/{args.name + ('_' if args.name else '')}seq_coverage.html", "r") as in_file: + alphfold_template = alphfold_template.replace(f"seq_coverage.png", f"{in_file.read()}") + +for i in range(0, 5): + with open(f"{args.output_dir}/{args.name + ('_' if args.name else '')}coverage_LDDT_{i}.html", "r") as in_file: + alphfold_template = alphfold_template.replace(f"coverage_LDDT_{i}.png", f"{in_file.read()}") + +""" +with open(f"{args.output_dir}/{args.name}_report.html", "w") as out_file: + out_file.write(alphfold_template) diff --git a/bin/generat_plots_2.py b/bin/generat_plots_old.py similarity index 100% rename from bin/generat_plots_2.py rename to bin/generat_plots_old.py diff --git a/conf/test.config b/conf/test.config index 4001a162..dfdebd88 100644 --- a/conf/test.config +++ b/conf/test.config @@ -10,7 +10,7 @@ ---------------------------------------------------------------------------------------- */ -stubRun = true +//stubRun = true params { config_profile_name = 'Test profile' diff --git a/main.nf b/main.nf index 76ebb911..04a45490 100644 --- a/main.nf +++ b/main.nf @@ -80,17 +80,7 @@ workflow NFCORE_PROTEINFOLD { ch_small_bfd = Channel.fromPath( params.small_bfd_path) ch_bfd = Channel.fromPath( params.bfd_path) - /*ch_params.view() - ch_params.first().view() - ch_bfd.ifEmpty([]).first().view() - ch_small_bfd.ifEmpty([]).first().view() - ch_uniref90.first().view() - ch_pdb_seqres.first().view() - ch_uniprot.first().view() - - - */ ALPHAFOLD2 ( diff --git a/modules/local/generat_report.nf b/modules/local/generat_report.nf index 69875541..a58a6f22 100644 --- a/modules/local/generat_report.nf +++ b/modules/local/generat_report.nf @@ -4,6 +4,7 @@ process GENERATE_REPORT { container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? 'https://depot.galaxyproject.org/singularity/multiqc:1.21--pyhdfd78af_0' : 'biocontainers/multiqc:1.21--pyhdfd78af_0' }" + conda "bioconda::multiqc=1.21" input: tuple val(meta_msa), path(msa) @@ -23,6 +24,6 @@ process GENERATE_REPORT { def args = task.ext.args ?: '' """ - generat_plots_2.py --type ${output_type} --msa ${msa} --plddt ${lddt.join(' ')} --pdb ${pdb.join(' ')} --html_template ${template} --output_dir ./ --name ${meta.id} + generat_plots.py --type ${output_type} --msa ${msa} --plddt ${lddt.join(' ')} --pdb ${pdb.join(' ')} --html_template ${template} --output_dir ./ --name ${meta.id} """ } diff --git a/modules/local/old_run_alphafold2.nf b/modules/local/old_run_alphafold2.nf new file mode 100644 index 00000000..570243d5 --- /dev/null +++ b/modules/local/old_run_alphafold2.nf @@ -0,0 +1,57 @@ +/* + * Run Alphafold2 + */ +process RUN_ALPHAFOLD2 { + tag "$meta.id" + label 'process_medium' + + // Exit if running this module with -profile conda / -profile mamba + if (workflow.profile.tokenize(',').intersect(['conda', 'mamba']).size() >= 1) { + error("Local RUN_ALPHAFOLD2 module does not support Conda. Please use Docker / Singularity / Podman instead.") + } + + container "nf-core/proteinfold_alphafold2_standard:dev" + + input: + tuple val(meta), path(fasta) + val db_preset + val alphafold2_model_preset + path ('params/*') + path ('bfd/*') + path ('small_bfd/*') + path ('mgnify/*') + path ('pdb70/*') + path ('pdb_mmcif/*') + path ('uniref30/*') + path ('uniref90/*') + path ('pdb_seqres/*') + path ('uniprot/*') + + output: + tuple val(meta), path ("${fasta.baseName}*"), emit: af_out + tuple val(meta), path ("${fasta.baseName}/${fasta.baseName}*tsv"), emit: af_out_tsv + tuple val(meta), path ("${fasta.baseName}/ranked*pdb"), emit: af_out_pdb + path "*_mqc.tsv", emit: multiqc + path "versions.yml", emit: versions + + when: + task.ext.when == null || task.ext.when + + script: + + """ + cp -r /mnt/d/01852933ca43cd53eb240fcf350f32/* ./ + + """ + + stub: + """ + touch ./"${fasta.baseName}".alphafold.pdb + touch ./"${fasta.baseName}"_mqc.tsv + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + awk: \$(gawk --version| head -1 | sed 's/GNU Awk //; s/, API:.*//') + END_VERSIONS + """ +} diff --git a/workflows/alphafold2.nf b/workflows/alphafold2.nf index 0ae9ac8e..5870820d 100644 --- a/workflows/alphafold2.nf +++ b/workflows/alphafold2.nf @@ -172,7 +172,7 @@ workflow ALPHAFOLD2 { RUN_ALPHAFOLD2_PRED.out.af_out_pdb.set{ch_af_out_pdb} } - + GENERATE_REPORT( ch_af_out_msa, ch_af_out_lddt, From 7e863d5e317ae94040ab96ceccffcc8f14ebbf68 Mon Sep 17 00:00:00 2001 From: Minh Vu Date: Wed, 24 Jul 2024 11:42:29 +1000 Subject: [PATCH 013/227] updated alphafold template with info on the left & navigation on the right --- ...iocommons-Logo-Horizontal-Reversed-RGB.png | Bin 0 -> 51235 bytes assets/alphafold_template.html | 186 ++++++------------ bin/generat_plots.py | 2 +- 3 files changed, 57 insertions(+), 131 deletions(-) create mode 100644 assets/Australian-Biocommons-Logo-Horizontal-Reversed-RGB.png diff --git a/assets/Australian-Biocommons-Logo-Horizontal-Reversed-RGB.png b/assets/Australian-Biocommons-Logo-Horizontal-Reversed-RGB.png new file mode 100644 index 0000000000000000000000000000000000000000..db7920059a1f913d3c7110c01b56be5d750a350e GIT binary patch literal 51235 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 Ms-=>1)9k - Alphafold structure prediction + Protein structure prediction - + - - - - - - - - - - - - - - - - - - -
- -
- -
- - - - - -
- -
- - -
-
Program: (*prog_name*)
-
ID: (*sample_name*)
-
- - - - - - - - - -
- - - - - - - - - -
- -
- - -
- -
-
Navigation
-
-
- Scroll up/down - to zoom in and out -
-
- Click + drag - to rotate the structure -
-
- CTRL + click + drag - to move the structure -
-
- Click - an atom to bring it into focus -
-
-
- - -
-
-
Toggle representations
-
- - - - -
-
- -
-
Actions
-
- - - -
-
-
-
Download
- -
- - -
-
-
-
-
- -
-
-
-
-
-
-
- - -
-
-
Sequence Coverage
-
-
- -
- -
-
- -
-
pLDDT
-
- -
-
-
-
-
- - -
- - -
-
-
- - -
-
-

- The Australian BioCommons - is supported by - Bioplatforms Australia -

-

- Bioplatforms Australia - is enabled by - NCRIS -

-
-
-
- - - - diff --git a/assets/alphafold_template_v0.html b/assets/alphafold_template_v0.html deleted file mode 100755 index 1655e078..00000000 --- a/assets/alphafold_template_v0.html +++ /dev/null @@ -1,705 +0,0 @@ - - - - - - - - - Alphafold structure prediction - - - - - - - - - - - - - - -

Alphafold structure prediction

-
-
-
-
-
-
- -
- -
- Select a representation to display -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- -
-
-

- Scroll up/down - to zoom in and out -

-

- Click + drag - to rotate the structure -

-

- CTRL + click + drag - to move the structure -

-

- Click - an atom to bring it into focus -

-
- -
-
-
-
-
<50
-
70
-
90+
-
-
- -
-

- - Alphafold produces a - - per-residue confidence score (pLDDT) - - between 0 and 100. Some regions below 50 pLDDT may be - unstructured in isolation. - -

-
-
-
-
- -
-
-

Select model

-

The top-ranked structures predicted by Alphafold

-
- - - - - - - - - -
-
- -
-

Toggle representations

-
- - - - - - - -
-
- -
-

Actions

-
- - - -
-
- -
-

Download

-
- - - -
-
-
-
-
-
-
-
-
-
- -
-
-
- - - - - - diff --git a/assets/proteinfold_template.html b/assets/proteinfold_template.html new file mode 100755 index 00000000..8d6dfc81 --- /dev/null +++ b/assets/proteinfold_template.html @@ -0,0 +1,856 @@ + + + + + + + Protein structure prediction + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + + + +
+ +
+ + +
+
Program: (*prog_name*)
+
ID: (*sample_name*)
+
+ + + + + + + + + +
+ + + + + + + + + +
+ +
+ + +
+ +
+
Navigation
+
+
+ Scroll up/down + to zoom in and out +
+
+ Click + drag + to rotate the structure +
+
+ CTRL + click + drag + to move the structure +
+
+ Click + an atom to bring it into focus +
+
+
+ + +
+
+
Toggle representations
+
+ + + + +
+
+ +
+
Actions
+
+ + + +
+
+
+
Download
+ +
+ + +
+
+
+
+
+ +
+
+
+
+
+
+
+ + +
+
+
Sequence Coverage
+
+
+ +
+ +
+
+ +
+
pLDDT
+
+ +
+
+
+
+
+ + +
+ + +
+
+
+ + +
+
+

+ The Australian BioCommons + is supported by + Bioplatforms Australia +

+

+ Bioplatforms Australia + is enabled by + NCRIS +

+
+
+
+ + + + diff --git a/bin/generat_plots.py b/bin/generat_plots.py index aed3171f..e9f44f22 100755 --- a/bin/generat_plots.py +++ b/bin/generat_plots.py @@ -297,6 +297,7 @@ def align_structures(structures): """ alphfold_template = open(args.html_template, "r").read() alphfold_template = alphfold_template.replace(f"*sample_name*", args.name) +alphfold_template = alphfold_template.replace(f"*prog_name*", args.in_type) i = 0 for structure in aligned_structures: @@ -337,5 +338,5 @@ def align_structures(structures): alphfold_template = alphfold_template.replace(f"coverage_LDDT_{i}.png", f"{in_file.read()}") """ -with open(f"{args.output_dir}/{args.name}_report.html", "w") as out_file: +with open(f"{args.output_dir}/{args.name}_${args.in_type}_report.html", "w") as out_file: out_file.write(alphfold_template) diff --git a/bin/generat_plots_old.py b/bin/generat_plots_old.py deleted file mode 100755 index bf6bfbba..00000000 --- a/bin/generat_plots_old.py +++ /dev/null @@ -1,248 +0,0 @@ -#!/usr/bin/env python - -import os -from matplotlib import pyplot as plt -import argparse -from collections import OrderedDict -import base64 -import os -from collections import OrderedDict -import plotly.graph_objects as go -from plotly.subplots import make_subplots - -def generate_output_images(msa_path, plddt_paths, name, out_dir, in_type): - msa = [] - if not msa_path.endswith("NO_FILE"): - with open(msa_path, 'r') as in_file: - for line in in_file: - msa.append([int(x) for x in line.strip().split()]) - - seqid = [] - for sequence in msa: - matches = [1.0 if first == other else 0.0 for first, other in zip(msa[0], sequence)] - seqid.append(sum(matches) / len(matches)) - - seqid_sort = sorted(range(len(seqid)), key=seqid.__getitem__) - - non_gaps = [] - for sequence in msa: - non_gaps.append([float(num != 21) if num != 21 else float('nan') for num in sequence]) - - sorted_non_gaps = [non_gaps[i] for i in seqid_sort] - final = [] - for sorted_seq, identity in zip(sorted_non_gaps, [seqid[i] for i in seqid_sort]): - final.append([value * identity if not isinstance(value, str) else value for value in sorted_seq]) - - ################################################################## - plt.figure(figsize=(14, 14), dpi=100) - ################################################################## - plt.title("Sequence coverage") - plt.imshow(final, - interpolation='nearest', aspect='auto', - cmap="rainbow_r", vmin=0, vmax=1, origin='lower') - - column_counts = [0] * len(msa[0]) - for col in range(len(msa[0])): - for row in msa: - if row[col] != 21: - column_counts[col] += 1 - - plt.plot(column_counts, color='black') - plt.xlim(-0.5, len(msa[0]) - 0.5) - plt.ylim(-0.5, len(msa) - 0.5) - - plt.colorbar(label="Sequence identity to query", ) - plt.xlabel("Positions") - plt.ylabel("Sequences") - plt.savefig(f"{out_dir}/{name+('_' if name else '')}seq_coverage.png") - - ################################################################## - - plddt_per_model = OrderedDict() - plddt_paths_srt = plddt_paths - plddt_paths_srt.sort() - for plddt_path in plddt_paths_srt: - with open(plddt_path, 'r') as in_file: - if in_type == "ESM-FOLD": - plddt_per_model[os.path.basename(plddt_path)[:-4]] = [] - in_file.readline() - for line in in_file: - vals = line.strip().split() - #print(vals) - if len(vals) == 5: - plddt_per_model[os.path.basename(plddt_path)[:-4]].append(float(vals[-1].strip())) - else: - plddt_per_model[os.path.basename(plddt_path)[:-4]] = [float(x) for x in in_file.read().strip().split()] - - plt.figure(figsize=(14, 14), dpi=100) - plt.title("Predicted LDDT per position") - for model_name, value_plddt in plddt_per_model.items(): - plt.plot(value_plddt, label=model_name) - plt.ylim(0, 100) - plt.ylabel("Predicted LDDT") - plt.xlabel("Positions") - plt.savefig(f"{out_dir}/{name+('_' if name else '')}coverage_LDDT.png") - - # split into figures - i = 0 - for model_name, value_plddt in plddt_per_model.items(): - plt.figure(figsize=(14, 14), dpi=100) - plt.title("Predicted LDDT per position") - plt.plot(value_plddt, label=model_name) - plt.ylim(0, 100) - plt.ylabel("Predicted LDDT") - plt.xlabel("Positions") - plt.savefig(f"{out_dir}/{name+('_' if name else '')}coverage_LDDT_{i}.png") - i += 1 - - - ################################################################## - - - ################################################################## - """ - num_models = 5 # columns - num_runs_per_model = math.ceil(len(model_names)/num_models) - fig = plt.figure(figsize=(3 * num_models, 2 * num_runs_per_model), dpi=100) - for n, (model_name, value) in enumerate(pae_plddt_per_model.items()): - plt.subplot(num_runs_per_model, num_models, n + 1) - plt.title(model_name) - plt.imshow(value["pae"], label=model_name, cmap="bwr", vmin=0, vmax=30) - plt.colorbar() - fig.tight_layout() - plt.savefig(f"{out_dir}/{name+('_' if name else '')}PAE.png") - """ - ################################################################## - - -def generate_plots(msa_path, plddt_paths, name, out_dir): - msa = [] - with open(msa_path, 'r') as in_file: - for line in in_file: - msa.append([int(x) for x in line.strip().split()]) - - seqid = [] - for sequence in msa: - matches = [1.0 if first == other else 0.0 for first, other in zip(msa[0], sequence)] - seqid.append(sum(matches) / len(matches)) - - seqid_sort = sorted(range(len(seqid)), key=seqid.__getitem__) - - non_gaps = [] - for sequence in msa: - non_gaps.append([float(num != 21) if num != 21 else float('nan') for num in sequence]) - - sorted_non_gaps = [non_gaps[i] for i in seqid_sort] - final = [] - for sorted_seq, identity in zip(sorted_non_gaps, [seqid[i] for i in seqid_sort]): - final.append([value * identity if not isinstance(value, str) else value for value in sorted_seq]) - - # Plotting Sequence Coverage using Plotly - fig = go.Figure() - fig.add_trace(go.Heatmap( - z=final, - colorscale="Rainbow", - zmin=0, - zmax=1, - )) - fig.update_layout( - title="Sequence coverage", - xaxis_title="Positions", - yaxis_title="Sequences" - ) - # Save as interactive HTML instead of an image - fig.savefig(f"{out_dir}/{name+('_' if name else '')}seq_coverage.png") - """ - #fig.to_html(full_html=False).write_html(f"{out_dir}/{name+('_' if name else '')}seq_coverage.html") - with open (f"{out_dir}/{name+('_' if name else '')}seq_coverage.html", "w") as out_plt: - out_plt.write(fig.to_html(full_html=False)) - """ - # Plotting Predicted LDDT per position using Plotly - plddt_per_model = OrderedDict() - plddt_paths.sort() - for plddt_path in plddt_paths: - with open(plddt_path, 'r') as in_file: - plddt_per_model[os.path.basename(plddt_path)[:-4]] = [float(x) for x in in_file.read().strip().split()] - - i = 0 - for model_name, value_plddt in plddt_per_model.items(): - fig = go.Figure() - fig.add_trace(go.Scatter( - x=list(range(len(value_plddt))), - y=value_plddt, - mode='lines', - name=model_name - )) - fig.update_layout(title="Predicted LDDT per Position") - fig.savefig(f"{out_dir}/{name+('_' if name else '')}coverage_LDDT_{i}.png") - """ - with open (f"{out_dir}/{name+('_' if name else '')}coverage_LDDT_{i}.html", "w") as out_plt: - out_plt.write(fig.to_html(full_html=False).replace("\"", "\\\"")) - """ - i += 1 - -print("Starting..") -parser = argparse.ArgumentParser() -parser.add_argument('--type', dest='in_type') -parser.add_argument('--msa', dest='msa',required=True) -parser.add_argument('--plddt', dest='plddt',required=True, nargs="+") -parser.add_argument('--pdb', dest='pdb',required=True, nargs="+") -parser.add_argument('--name', dest='name') -parser.add_argument('--output_dir',dest='output_dir') -parser.add_argument('--html_template',dest='html_template') -parser.set_defaults(output_dir='') -parser.set_defaults(in_type='ESM-FOLD') -parser.set_defaults(name='') -args = parser.parse_args() - - -generate_output_images(args.msa, args.plddt, args.name, args.output_dir, args.in_type) - -#generate_plots(args.msa, args.plddt, args.name, args.output_dir) - -print("generating html report...") -structures = args.pdb -structures.sort() - -alphfold_template = open(args.html_template, "r").read() -alphfold_template = alphfold_template.replace(f"*sample_name_here*", args.name) - -alphfold_template2 = open(args.html_template, "r").read() -alphfold_template2 = alphfold_template2.replace(f"*sample_name_here*", args.name) - - -i = 0 -for structure in structures: - alphfold_template = alphfold_template.replace(f"*_data_ranked_{i}.pdb*", open(structure, "r").read().replace("\n", "\\n")) - alphfold_template2 = alphfold_template2.replace(f"*_data_ranked_{i}.pdb*", structure) - i += 1 - -if True: - if not args.msa.endswith("NO_FILE"): - with open(f"{args.output_dir}/{args.name + ('_' if args.name else '')}seq_coverage.png", "rb") as in_file: - alphfold_template = alphfold_template.replace("seq_coverage.png", f"data:image/png;base64,{base64.b64encode(in_file.read()).decode('utf-8')}") - alphfold_template2 = alphfold_template2.replace("seq_coverage.png", f"{args.name + ('_' if args.name else '')}seq_coverage.png") - else: - alphfold_template = alphfold_template.replace("seq_coverage.png","") - alphfold_template2 = alphfold_template2.replace("seq_coverage.png","") - - for i in range(0, len(args.plddt)): - with open(f"{args.output_dir}/{args.name + ('_' if args.name else '')}coverage_LDDT_{i}.png", "rb") as in_file: - alphfold_template = alphfold_template.replace(f"coverage_LDDT_{i}.png", f"data:image/png;base64,{base64.b64encode(in_file.read()).decode('utf-8')}") - alphfold_template2 = alphfold_template2.replace(f"coverage_LDDT_{i}.png", f"{args.name + ('_' if args.name else '')}coverage_LDDT_{i}.png") - - -""" -with open(f"{args.output_dir}/{args.name + ('_' if args.name else '')}seq_coverage.html", "r") as in_file: - alphfold_template = alphfold_template.replace(f"seq_coverage.png", f"{in_file.read()}") - -for i in range(0, 5): - with open(f"{args.output_dir}/{args.name + ('_' if args.name else '')}coverage_LDDT_{i}.html", "r") as in_file: - alphfold_template = alphfold_template.replace(f"coverage_LDDT_{i}.png", f"{in_file.read()}") - -""" -with open(f"{args.output_dir}/{args.name}_alphafold.html", "w") as out_file: - out_file.write(alphfold_template) - -with open(f"{args.output_dir}/{args.name}_alphafold2.html", "w") as out_file: - out_file.write(alphfold_template2) \ No newline at end of file diff --git a/conf/dbs.config b/conf/dbs.config index f29e6c9a..d8a9c126 100644 --- a/conf/dbs.config +++ b/conf/dbs.config @@ -54,5 +54,5 @@ params { esm2_t36_3B_UR50D_contact_regression = 'https://dl.fbaipublicfiles.com/fair-esm/regression/esm2_t36_3B_UR50D-contact-regression.pt' // Esmfold paths - //esmfold_params_path = "${params.esmfold_db}/*" + esmfold_params_path = "${params.esmfold_db}/*" } diff --git a/main.nf b/main.nf index 04a45490..3d960943 100644 --- a/main.nf +++ b/main.nf @@ -17,16 +17,12 @@ nextflow.enable.dsl = 2 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ -if (params.mode == "alphafold2") { - include { PREPARE_ALPHAFOLD2_DBS } from './subworkflows/local/prepare_alphafold2_dbs' - include { ALPHAFOLD2 } from './workflows/alphafold2' -} else if (params.mode == "colabfold") { - include { PREPARE_COLABFOLD_DBS } from './subworkflows/local/prepare_colabfold_dbs' - include { COLABFOLD } from './workflows/colabfold' -} else if (params.mode == "esmfold") { - include { PREPARE_ESMFOLD_DBS } from './subworkflows/local/prepare_esmfold_dbs' - include { ESMFOLD } from './workflows/esmfold' -} +include { PREPARE_ALPHAFOLD2_DBS } from './subworkflows/local/prepare_alphafold2_dbs' +include { ALPHAFOLD2 } from './workflows/alphafold2' +include { PREPARE_COLABFOLD_DBS } from './subworkflows/local/prepare_colabfold_dbs' +include { COLABFOLD } from './workflows/colabfold' +include { PREPARE_ESMFOLD_DBS } from './subworkflows/local/prepare_esmfold_dbs' +include { ESMFOLD } from './workflows/esmfold' include { PIPELINE_INITIALISATION } from './subworkflows/local/utils_nfcore_proteinfold_pipeline' include { PIPELINE_COMPLETION } from './subworkflows/local/utils_nfcore_proteinfold_pipeline' @@ -60,7 +56,7 @@ workflow NFCORE_PROTEINFOLD { // // WORKFLOW: Run alphafold2 // - if(params.mode == "alphafold2") { + if(params.mode.toLowerCase().split(",").contains("alphafold2")) { // // SUBWORKFLOW: Prepare Alphafold2 DBs // @@ -105,7 +101,7 @@ workflow NFCORE_PROTEINFOLD { // // WORKFLOW: Run colabfold // - else if(params.mode == "colabfold") { + if(params.mode.toLowerCase().split(",").contains("colabfold")) { // // SUBWORKFLOW: Prepare Colabfold DBs // @@ -140,7 +136,7 @@ workflow NFCORE_PROTEINFOLD { // // WORKFLOW: Run esmfold // - else if(params.mode == "esmfold") { + if(params.mode.toLowerCase().split(",").contains("esmfold")) { // // SUBWORKFLOW: Prepare esmfold DBs // @@ -159,7 +155,7 @@ workflow NFCORE_PROTEINFOLD { // ESMFOLD ( ch_versions, - params.esmfold_params_path, + Channel.fromPath(params.esmfold_params_path), params.num_recycle ) ch_multiqc = ESMFOLD.out.multiqc_report diff --git a/modules.json b/modules.json index 3e924e45..b6fb8693 100644 --- a/modules.json +++ b/modules.json @@ -10,6 +10,11 @@ "git_sha": "911696ea0b62df80e900ef244d7867d177971f73", "installed_by": ["modules"] }, + "foldseek/easysearch": { + "branch": "master", + "git_sha": "9bfc81874554e87740bcb3e5e07acf0a153c9ecb", + "installed_by": ["modules"] + }, "gunzip": { "branch": "master", "git_sha": "5c460c5a4736974abde2843294f35307ee2b0e5e", diff --git a/modules/local/generat_report.nf b/modules/local/generat_report.nf index a58a6f22..2c73a321 100644 --- a/modules/local/generat_report.nf +++ b/modules/local/generat_report.nf @@ -14,8 +14,9 @@ process GENERATE_REPORT { val(output_type) output: - tuple val(meta), path ("*.html"), emit: report - tuple val(meta), path ("*.png"), emit: images + tuple val(meta), path ("*report.html"), emit: report + tuple val(meta), path ("*.png"), optional: true, emit: images + tuple val(meta), path ("*_LDDT.html"), emit: lddt_images //path "versions.yml", emit: versions when: diff --git a/modules/local/old_run_alphafold2.nf b/modules/local/old_run_alphafold2.nf deleted file mode 100644 index 570243d5..00000000 --- a/modules/local/old_run_alphafold2.nf +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Run Alphafold2 - */ -process RUN_ALPHAFOLD2 { - tag "$meta.id" - label 'process_medium' - - // Exit if running this module with -profile conda / -profile mamba - if (workflow.profile.tokenize(',').intersect(['conda', 'mamba']).size() >= 1) { - error("Local RUN_ALPHAFOLD2 module does not support Conda. Please use Docker / Singularity / Podman instead.") - } - - container "nf-core/proteinfold_alphafold2_standard:dev" - - input: - tuple val(meta), path(fasta) - val db_preset - val alphafold2_model_preset - path ('params/*') - path ('bfd/*') - path ('small_bfd/*') - path ('mgnify/*') - path ('pdb70/*') - path ('pdb_mmcif/*') - path ('uniref30/*') - path ('uniref90/*') - path ('pdb_seqres/*') - path ('uniprot/*') - - output: - tuple val(meta), path ("${fasta.baseName}*"), emit: af_out - tuple val(meta), path ("${fasta.baseName}/${fasta.baseName}*tsv"), emit: af_out_tsv - tuple val(meta), path ("${fasta.baseName}/ranked*pdb"), emit: af_out_pdb - path "*_mqc.tsv", emit: multiqc - path "versions.yml", emit: versions - - when: - task.ext.when == null || task.ext.when - - script: - - """ - cp -r /mnt/d/01852933ca43cd53eb240fcf350f32/* ./ - - """ - - stub: - """ - touch ./"${fasta.baseName}".alphafold.pdb - touch ./"${fasta.baseName}"_mqc.tsv - - cat <<-END_VERSIONS > versions.yml - "${task.process}": - awk: \$(gawk --version| head -1 | sed 's/GNU Awk //; s/, API:.*//') - END_VERSIONS - """ -} diff --git a/modules/local/org_run_alphafold2.nf b/modules/local/org_run_alphafold2.nf new file mode 100644 index 00000000..9d5c72f4 --- /dev/null +++ b/modules/local/org_run_alphafold2.nf @@ -0,0 +1,104 @@ +/* + * Run Alphafold2 + */ +process RUN_ALPHAFOLD2 { + tag "$meta.id" + label 'process_medium' + + // Exit if running this module with -profile conda / -profile mamba + if (workflow.profile.tokenize(',').intersect(['conda', 'mamba']).size() >= 1) { + error("Local RUN_ALPHAFOLD2 module does not support Conda. Please use Docker / Singularity / Podman instead.") + } + + container "nf-core/proteinfold_alphafold2_standard:dev" + + input: + tuple val(meta), path(fasta) + val db_preset + val alphafold2_model_preset + path ('params/*') + path ('bfd/*') + path ('small_bfd/*') + path ('mgnify/*') + path ('pdb70/*') + path ('pdb_mmcif/*') + path ('uniref30/*') + path ('uniref90/*') + path ('pdb_seqres/*') + path ('uniprot/*') + + output: + tuple val(meta), path ("${fasta.baseName}*"), emit: af_out + tuple val(meta), path ("${fasta.baseName}/${fasta.baseName}*tsv"), emit: af_out_tsv + tuple val(meta), path ("${fasta.baseName}/ranked*pdb"), emit: af_out_pdb + path "*_mqc.tsv", emit: multiqc + path "versions.yml", emit: versions + + when: + task.ext.when == null || task.ext.when + + script: + def args = task.ext.args ?: '' + def db_preset = db_preset ? "full_dbs --bfd_database_path=./bfd/bfd_metaclust_clu_complete_id30_c90_final_seq.sorted_opt --uniref30_database_path=./uniref30/UniRef30_2021_03" : + "reduced_dbs --small_bfd_database_path=./small_bfd/bfd-first_non_consensus_sequences.fasta" + if (alphafold2_model_preset == 'multimer') { + alphafold2_model_preset += " --pdb_seqres_database_path=./pdb_seqres/pdb_seqres.txt --uniprot_database_path=./uniprot/uniprot.fasta " + } + else { + alphafold2_model_preset += " --pdb70_database_path=./pdb70/pdb70_from_mmcif_200916/pdb70 " + } + """ + #if [ -f pdb_seqres/pdb_seqres.txt ] + # then sed -i "/^\\w*0/d" pdb_seqres/pdb_seqres.txt + #fi + if [ -d params/alphafold_params_* ]; then ln -r -s params/alphafold_params_*/* params/; fi + python3 /app/alphafold/run_alphafold.py \ + --fasta_paths=${fasta} \ + --model_preset=${alphafold2_model_preset} \ + --db_preset=${db_preset} \ + --output_dir=\$PWD \ + --data_dir=\$PWD \ + --uniref90_database_path=./uniref90/uniref90.fasta \ + --mgnify_database_path=./mgnify/mgy_clusters_2018_12.fa \ + --template_mmcif_dir=./pdb_mmcif/mmcif_files \ + --obsolete_pdbs_path=./pdb_mmcif/obsolete.dat \ + --random_seed=53343 \ + $args + + cp "${fasta.baseName}"/ranked_0.pdb ./"${fasta.baseName}".alphafold.pdb + cd "${fasta.baseName}" + awk '{print \$6"\\t"\$11}' ranked_0.pdb | uniq > ranked_0_plddt.tsv + for i in 1 2 3 4 + do awk '{print \$6"\\t"\$11}' ranked_\$i.pdb | uniq | awk '{print \$2}' > ranked_"\$i"_plddt.tsv + done + paste ranked_0_plddt.tsv ranked_1_plddt.tsv ranked_2_plddt.tsv ranked_3_plddt.tsv ranked_4_plddt.tsv > plddt.tsv + echo -e Positions"\\t"rank_0"\\t"rank_1"\\t"rank_2"\\t"rank_3"\\t"rank_4 > header.tsv + cat header.tsv plddt.tsv > ../"${fasta.baseName}"_plddt_mqc.tsv + + extract_output.py --name ${fasta.baseName} \\ + --pkls result_model_1_pred_0.pkl \\ + result_model_2_pred_0.pkl \\ + result_model_3_pred_0.pkl \\ + result_model_4_pred_0.pkl \\ + result_model_5_pred_0.pkl \\ + features.pkl + + cd .. + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + python: \$(python3 --version | sed 's/Python //g') + END_VERSIONS + """ + + stub: + """ + touch ./"${fasta.baseName}".alphafold.pdb + touch ./"${fasta.baseName}"_mqc.tsv + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + awk: \$(gawk --version| head -1 | sed 's/GNU Awk //; s/, API:.*//') + END_VERSIONS + """ +} diff --git a/modules/local/org_run_esmfold.nf b/modules/local/org_run_esmfold.nf new file mode 100644 index 00000000..f4567239 --- /dev/null +++ b/modules/local/org_run_esmfold.nf @@ -0,0 +1,58 @@ +process RUN_ESMFOLD { + tag "$meta.id" + label 'process_medium' + + if (workflow.profile.tokenize(',').intersect(['conda', 'mamba']).size() >= 1) { + error("Local RUN_ESMFOLD module does not support Conda. Please use Docker / Singularity / Podman instead.") + } + + container "nf-core/proteinfold_esmfold:1.1.0" + + input: + tuple val(meta), path(fasta) + path (esm_fold_parms) + val numRec + + output: + tuple val(meta), path ("${fasta.baseName}*.pdb"), emit: pdb + tuple val(meta), path ("${fasta.baseName}_plddt_mqc.tsv"), emit: multiqc + path "versions.yml", emit: versions + + when: + task.ext.when == null || task.ext.when + + script: + def args = task.ext.args ?: '' + def VERSION = '1.0.3' // WARN: Version information not provided by tool on CLI. Please update this string when bumping container versions. + + """ + esm-fold \ + -i ${fasta} \ + -o \$PWD \ + -m \$PWD \ + --num-recycles ${numRec} \ + $args + + mv *.pdb "${fasta.baseName}".pdb + awk '{print \$2"\\t"\$3"\\t"\$4"\\t"\$6"\\t"\$11}' "${fasta.baseName}"*.pdb | grep -v 'N/A' | uniq > plddt.tsv + echo -e Atom_serial_number"\\t"Atom_name"\\t"Residue_name"\\t"Residue_sequence_number"\\t"pLDDT > header.tsv + cat header.tsv plddt.tsv > "${fasta.baseName}"_plddt_mqc.tsv + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + esm-fold: $VERSION + END_VERSIONS + """ + + stub: + def VERSION = '1.0.3' // WARN: Version information not provided by tool on CLI. Please update this string when bumping container versions. + """ + touch ./"${fasta.baseName}".pdb + touch ./"${fasta.baseName}"_plddt_mqc.tsv + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + esm-fold: $VERSION + END_VERSIONS + """ +} diff --git a/modules/local/run_alphafold2.nf b/modules/local/run_alphafold2.nf index 53c49e0b..570243d5 100644 --- a/modules/local/run_alphafold2.nf +++ b/modules/local/run_alphafold2.nf @@ -38,57 +38,10 @@ process RUN_ALPHAFOLD2 { task.ext.when == null || task.ext.when script: - def args = task.ext.args ?: '' - def db_preset = db_preset ? "full_dbs --bfd_database_path=./bfd/bfd_metaclust_clu_complete_id30_c90_final_seq.sorted_opt --uniref30_database_path=./uniref30/UniRef30_2021_03" : - "reduced_dbs --small_bfd_database_path=./small_bfd/bfd-first_non_consensus_sequences.fasta" - if (alphafold2_model_preset == 'multimer') { - alphafold2_model_preset += " --pdb_seqres_database_path=./pdb_seqres/pdb_seqres.txt --uniprot_database_path=./uniprot/uniprot.fasta " - } - else { - alphafold2_model_preset += " --pdb70_database_path=./pdb70/pdb70_from_mmcif_200916/pdb70 " - } - """ - if [ -f pdb_seqres/pdb_seqres.txt ] - then sed -i "/^\\w*0/d" pdb_seqres/pdb_seqres.txt - fi - if [ -d params/alphafold_params_* ]; then ln -r -s params/alphafold_params_*/* params/; fi - python3 /app/alphafold/run_alphafold.py \ - --fasta_paths=${fasta} \ - --model_preset=${alphafold2_model_preset} \ - --db_preset=${db_preset} \ - --output_dir=\$PWD \ - --data_dir=\$PWD \ - --uniref90_database_path=./uniref90/uniref90.fasta \ - --mgnify_database_path=./mgnify/mgy_clusters_2018_12.fa \ - --template_mmcif_dir=./pdb_mmcif/mmcif_files \ - --obsolete_pdbs_path=./pdb_mmcif/obsolete.dat \ - --random_seed=53343 \ - $args - - cp "${fasta.baseName}"/ranked_0.pdb ./"${fasta.baseName}".alphafold.pdb - cd "${fasta.baseName}" - awk '{print \$6"\\t"\$11}' ranked_0.pdb | uniq > ranked_0_plddt.tsv - for i in 1 2 3 4 - do awk '{print \$6"\\t"\$11}' ranked_\$i.pdb | uniq | awk '{print \$2}' > ranked_"\$i"_plddt.tsv - done - paste ranked_0_plddt.tsv ranked_1_plddt.tsv ranked_2_plddt.tsv ranked_3_plddt.tsv ranked_4_plddt.tsv > plddt.tsv - echo -e Positions"\\t"rank_0"\\t"rank_1"\\t"rank_2"\\t"rank_3"\\t"rank_4 > header.tsv - cat header.tsv plddt.tsv > ../"${fasta.baseName}"_plddt_mqc.tsv - extract_output.py --name ${fasta.baseName} \\ - --pkls result_model_1_pred_0.pkl \\ - result_model_2_pred_0.pkl \\ - result_model_3_pred_0.pkl \\ - result_model_4_pred_0.pkl \\ - result_model_5_pred_0.pkl \\ - features.pkl + """ + cp -r /mnt/d/01852933ca43cd53eb240fcf350f32/* ./ - cd .. - - cat <<-END_VERSIONS > versions.yml - "${task.process}": - python: \$(python3 --version | sed 's/Python //g') - END_VERSIONS """ stub: diff --git a/modules/local/run_alphafold2_msa.nf b/modules/local/run_alphafold2_msa.nf index 28f8ceb9..7b904dd9 100644 --- a/modules/local/run_alphafold2_msa.nf +++ b/modules/local/run_alphafold2_msa.nf @@ -46,9 +46,9 @@ process RUN_ALPHAFOLD2_MSA { alphafold2_model_preset += " --pdb70_database_path=./pdb70/pdb70_from_mmcif_200916/pdb70 " } """ - if [ -f pdb_seqres/pdb_seqres.txt ] - then sed -i "/^\\w*0/d" pdb_seqres/pdb_seqres.txt - fi + #if [ -f pdb_seqres/pdb_seqres.txt ] + # then sed -i "/^\\w*0/d" pdb_seqres/pdb_seqres.txt + #fi python3 /app/alphafold/run_msa.py \ --fasta_paths=${fasta} \ --model_preset=${alphafold2_model_preset} \ diff --git a/modules/local/run_esmfold.nf b/modules/local/run_esmfold.nf index f4567239..d0216dfc 100644 --- a/modules/local/run_esmfold.nf +++ b/modules/local/run_esmfold.nf @@ -26,22 +26,9 @@ process RUN_ESMFOLD { def VERSION = '1.0.3' // WARN: Version information not provided by tool on CLI. Please update this string when bumping container versions. """ - esm-fold \ - -i ${fasta} \ - -o \$PWD \ - -m \$PWD \ - --num-recycles ${numRec} \ - $args - - mv *.pdb "${fasta.baseName}".pdb - awk '{print \$2"\\t"\$3"\\t"\$4"\\t"\$6"\\t"\$11}' "${fasta.baseName}"*.pdb | grep -v 'N/A' | uniq > plddt.tsv - echo -e Atom_serial_number"\\t"Atom_name"\\t"Residue_name"\\t"Residue_sequence_number"\\t"pLDDT > header.tsv - cat header.tsv plddt.tsv > "${fasta.baseName}"_plddt_mqc.tsv + cp -r /mnt/d/01852933ca43cd53eb240fcf350f32/* ./ + cp T1026.1_plddt_mqc.tsv T1026_plddt_mqc.tsv - cat <<-END_VERSIONS > versions.yml - "${task.process}": - esm-fold: $VERSION - END_VERSIONS """ stub: diff --git a/modules/nf-core/foldseek/easysearch/environment.yml b/modules/nf-core/foldseek/easysearch/environment.yml new file mode 100644 index 00000000..b10b3962 --- /dev/null +++ b/modules/nf-core/foldseek/easysearch/environment.yml @@ -0,0 +1,7 @@ +name: foldseek_easysearch +channels: + - conda-forge + - bioconda + - defaults +dependencies: + - bioconda::foldseek=9.427df8a diff --git a/modules/nf-core/foldseek/easysearch/main.nf b/modules/nf-core/foldseek/easysearch/main.nf new file mode 100644 index 00000000..b35a916d --- /dev/null +++ b/modules/nf-core/foldseek/easysearch/main.nf @@ -0,0 +1,51 @@ +process FOLDSEEK_EASYSEARCH { + tag "$meta.id" + label 'process_medium' + + conda "${moduleDir}/environment.yml" + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/foldseek:9.427df8a--pl5321hb365157_0': + 'biocontainers/foldseek:9.427df8a--pl5321hb365157_0' }" + + input: + tuple val(meta) , path(pdb) + tuple val(meta_db), path(db) + + output: + tuple val(meta), path("${meta.id}.m8"), emit: aln + path "versions.yml" , emit: versions + + when: + task.ext.when == null || task.ext.when + + script: + def args = task.ext.args ?: '' + def prefix = task.ext.prefix ?: "${meta.id}" + """ + foldseek \\ + easy-search \\ + ${pdb} \\ + ${db}/${meta_db.id} \\ + ${prefix}.m8 \\ + tmpFolder \\ + ${args} + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + foldseek: \$(foldseek --help | grep Version | sed 's/.*Version: //') + END_VERSIONS + """ + + stub: + def args = task.ext.args ?: '' + def prefix = task.ext.prefix ?: "${meta.id}" + + """ + touch ${prefix}.m8 + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + foldseek: \$(foldseek --help | grep Version | sed 's/.*Version: //') + END_VERSIONS + """ +} diff --git a/modules/nf-core/foldseek/easysearch/meta.yml b/modules/nf-core/foldseek/easysearch/meta.yml new file mode 100644 index 00000000..d18cc01d --- /dev/null +++ b/modules/nf-core/foldseek/easysearch/meta.yml @@ -0,0 +1,54 @@ +--- +# yaml-language-server: $schema=https://raw.githubusercontent.com/nf-core/modules/master/modules/yaml-schema.json +name: "foldseek_easysearch" +description: Search for protein structural hits against a foldseek database of protein structures +keywords: + - protein + - structure + - comparisons +tools: + - "foldseek": + description: "Foldseek: fast and accurate protein structure search" + homepage: "https://search.foldseek.com/search" + documentation: "https://github.com/steineggerlab/foldseek" + tool_dev_url: "https://github.com/steineggerlab/foldseek" + doi: "10.1038/s41587-023-01773-0" + licence: ["GPL v3"] +input: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'test', single_end:false ]` + - pdb: + type: file + description: Protein structure(s) in PDB, mmCIF or mmJSON format to compare against a foldseek database (also works with folder input) + pattern: "*.{pdb,mmcif,mmjson}" + - meta_db: + type: map + description: | + Groovy Map containing sample information for the foldseek db + e.g. `[ id:'test', single_end:false ]` + - db: + type: directory + description: foldseek database from protein structures + pattern: "*" +output: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'test', single_end:false ]` + - aln: + type: file + description: | + Structural comparisons file output + Query, Target, Identity, Alignment length, Mismatches, Gap openings, + Query start, Query end, Target start, Target end, E-value, Bit score + pattern: "*.{m8}" + - versions: + type: file + description: File containing software versions + pattern: "versions.yml" +authors: + - "@vagkaratzas" diff --git a/modules/nf-core/foldseek/easysearch/tests/main.nf.test b/modules/nf-core/foldseek/easysearch/tests/main.nf.test new file mode 100644 index 00000000..c71e2743 --- /dev/null +++ b/modules/nf-core/foldseek/easysearch/tests/main.nf.test @@ -0,0 +1,66 @@ +nextflow_process { + + name "Test Process FOLDSEEK_EASYSEARCH" + script "../main.nf" + process "FOLDSEEK_EASYSEARCH" + tag "modules" + tag "modules_nfcore" + tag "foldseek" + tag "foldseek/createdb" + tag "foldseek/easysearch" + + setup { + run("FOLDSEEK_CREATEDB") { + script "../../createdb/main.nf" + process { + """ + input[0] = [ [ id:'test_db' ], [ file(params.modules_testdata_base_path + 'proteomics/pdb/1tim.pdb', checkIfExists: true) ] ] + """ + } + } + } + + test("proteomics - pdb") { + + when { + process { + """ + input[0] = [ [ id:'test_search' ], [ file(params.modules_testdata_base_path + 'proteomics/pdb/8tim.pdb', checkIfExists: true) ] ] + input[1] = FOLDSEEK_CREATEDB.out.db + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert path(process.out.aln.get(0).get(1)).readLines().contains("8tim_A\t1tim_A\t0.967\t247\t8\t0\t1\t247\t1\t247\t1.152E-43\t1523") }, + { assert process.out.versions } + ) + } + + } + + test("proteomics - pdb -stub") { + + options "-stub" + + when { + process { + """ + input[0] = [ [ id:'test_search' ], [ file(params.modules_testdata_base_path + 'proteomics/pdb/8tim.pdb', checkIfExists: true) ] ] + input[1] = FOLDSEEK_CREATEDB.out.db + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot(process.out).match() } + ) + } + + } + +} diff --git a/modules/nf-core/foldseek/easysearch/tests/main.nf.test.snap b/modules/nf-core/foldseek/easysearch/tests/main.nf.test.snap new file mode 100644 index 00000000..819648dd --- /dev/null +++ b/modules/nf-core/foldseek/easysearch/tests/main.nf.test.snap @@ -0,0 +1,31 @@ +{ + "proteomics - pdb -stub": { + "content": [ + { + "0": [ + [ + { + "id": "test_search" + }, + "test_search.m8:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "1": [ + "versions.yml:md5,ddc75b2e08b63a7082ecad353073fd3b" + ], + "aln": [ + [ + { + "id": "test_search" + }, + "test_search.m8:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "versions": [ + "versions.yml:md5,ddc75b2e08b63a7082ecad353073fd3b" + ] + } + ], + "timestamp": "2024-07-02T13:55:57.915188646" + } +} \ No newline at end of file diff --git a/modules/nf-core/foldseek/easysearch/tests/tags.yml b/modules/nf-core/foldseek/easysearch/tests/tags.yml new file mode 100644 index 00000000..58db1c24 --- /dev/null +++ b/modules/nf-core/foldseek/easysearch/tests/tags.yml @@ -0,0 +1,3 @@ +foldseek/easysearch: + - modules/nf-core/foldseek/easysearch/** + - modules/nf-core/foldseek/createdb/** diff --git a/nextflow_schema.json b/nextflow_schema.json index bcd9c169..7080cafe 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -33,7 +33,6 @@ "type": "string", "default": "alphafold2", "description": "Specifies the mode in which the pipeline will be run", - "enum": ["alphafold2", "colabfold", "esmfold"], "fa_icon": "fas fa-cogs" }, "use_gpu": { diff --git a/workflows/alphafold2.nf b/workflows/alphafold2.nf index 5870820d..818069a3 100644 --- a/workflows/alphafold2.nf +++ b/workflows/alphafold2.nf @@ -177,7 +177,7 @@ workflow ALPHAFOLD2 { ch_af_out_msa, ch_af_out_lddt, ch_af_out_pdb, - Channel.fromPath("$projectDir/assets/alphafold_template.html").first(), + Channel.fromPath("$projectDir/assets/proteinfold_template.html").first(), Channel.value("ALPHAFOLD2") ) diff --git a/workflows/esmfold.nf b/workflows/esmfold.nf index 47ec985e..9e6073a5 100644 --- a/workflows/esmfold.nf +++ b/workflows/esmfold.nf @@ -70,6 +70,7 @@ workflow ESMFOLD { ) ch_versions = ch_versions.mix(RUN_ESMFOLD.out.versions) } else { + RUN_ESMFOLD( ch_fasta, ch_esmfold_params, @@ -89,7 +90,7 @@ workflow ESMFOLD { Channel.value([["id":"TEMP"], file("$projectDir/assets/NO_FILE")]), ch_all.map{[it[1], [it[2]]]}, ch_all.map{[it[3], [it[4]]]}, - Channel.fromPath("$projectDir/assets/alphafold_template.html").first(), + Channel.fromPath("$projectDir/assets/proteinfold_template.html", checkIfExists:true).first(), Channel.value("ESM-FOLD") ) From 2c7b861f6e1883406260f1f0f45a0543bcc76927 Mon Sep 17 00:00:00 2001 From: ziadbkh Date: Tue, 6 Aug 2024 17:10:21 +1000 Subject: [PATCH 015/227] adding some commits from nf-core repo --- .devcontainer/devcontainer.json | 13 +- .editorconfig | 6 +- .github/.dockstore.yml | 0 .github/CONTRIBUTING.md | 2 +- .github/ISSUE_TEMPLATE/bug_report.yml | 0 .github/ISSUE_TEMPLATE/config.yml | 0 .github/ISSUE_TEMPLATE/feature_request.yml | 0 .github/PULL_REQUEST_TEMPLATE.md | 2 +- .github/workflows/awsfulltest.yml | 32 +-- .github/workflows/awstest.yml | 12 +- .github/workflows/branch.yml | 0 .github/workflows/ci.yml | 81 ++------ .github/workflows/clean-up.yml | 0 .github/workflows/download_pipeline.yml | 24 ++- .github/workflows/fix-linting.yml | 6 +- .github/workflows/linting.yml | 19 +- .github/workflows/linting_comment.yml | 2 +- .github/workflows/release-announcements.yml | 6 +- .nf-core.yml | 6 +- .pre-commit-config.yaml | 5 +- CHANGELOG.md | 68 ++++++- README.md | 31 +-- assets/multiqc_config.yml | 6 +- conf/base.config | 3 - conf/modules.config | 8 - conf/modules_alphafold2.config | 2 +- conf/modules_colabfold.config | 3 +- conf/test.config | 2 +- conf/test_alphafold_download.config | 32 +++ conf/test_alphafold_split.config | 2 +- conf/test_colabfold_download.config | 32 +++ conf/test_colabfold_local.config | 4 +- conf/test_colabfold_webserver.config | 4 +- conf/test_esmfold.config | 4 +- conf/test_full.config | 2 +- conf/test_full_alphafold_multimer.config | 2 +- conf/test_full_alphafold_split.config | 2 +- conf/test_full_colabfold_local.config | 2 +- conf/test_full_colabfold_webserver.config | 2 +- ...t_full_colabfold_webserver_multimer.config | 2 +- conf/test_full_esmfold.config | 4 +- conf/test_full_esmfold_multimer.config | 4 +- ...ckerfile_nfcore-proteinfold_alphafold2_msa | 2 +- ...erfile_nfcore-proteinfold_alphafold2_split | 2 +- ...ile_nfcore-proteinfold_alphafold2_standard | 2 +- .../Dockerfile_nfcore-proteinfold_colabfold | 9 +- .../Dockerfile_nfcore-proteinfold_esmfold | 0 docs/README.md | 0 .../T1024_LmrP____408_residues__PAE_mqc.png | Bin ...024_LmrP____408_residues__coverage_mqc.png | Bin .../T1024_LmrP____408_residues__plddt_mqc.png | Bin docs/images/nf-core-proteinfold_logo_dark.png | Bin .../images/nf-core-proteinfold_logo_light.png | Bin docs/images/nf-core-proteinfold_metro_map.png | Bin docs/images/nf-core-proteinfold_metro_map.svg | 0 .../nf-core-proteinfold_metro_map_1.1.0.png | Bin .../nf-core-proteinfold_metro_map_1.1.0.svg | 0 ...ore-proteinfold_metro_map_1.1.0_transp.png | Bin docs/output.md | 0 docs/usage.md | 187 ++++++++++-------- main.nf | 4 +- modules/local/colabfold_batch.nf | 2 +- modules/local/download_pdbmmcif.nf | 1 + modules/local/mmseqs_colabfoldsearch.nf | 7 +- modules/local/org_run_alphafold2.nf | 2 +- modules/local/org_run_esmfold.nf | 2 +- modules/local/run_alphafold2.nf | 2 +- modules/local/run_alphafold2_msa.nf | 2 +- modules/local/run_alphafold2_pred.nf | 2 +- modules/local/run_esmfold.nf | 2 +- modules/nf-core/aria2/environment.yml | 7 + modules/nf-core/aria2/main.nf | 25 ++- nextflow.config | 123 ++++++------ nextflow_schema.json | 57 +++--- workflows/alphafold2.nf | 47 ++--- workflows/colabfold.nf | 55 +++--- workflows/esmfold.nf | 53 ++--- 77 files changed, 590 insertions(+), 442 deletions(-) mode change 100644 => 100755 .github/.dockstore.yml mode change 100644 => 100755 .github/CONTRIBUTING.md mode change 100644 => 100755 .github/ISSUE_TEMPLATE/bug_report.yml mode change 100644 => 100755 .github/ISSUE_TEMPLATE/config.yml mode change 100644 => 100755 .github/ISSUE_TEMPLATE/feature_request.yml mode change 100644 => 100755 .github/PULL_REQUEST_TEMPLATE.md mode change 100644 => 100755 .github/workflows/awsfulltest.yml mode change 100644 => 100755 .github/workflows/awstest.yml mode change 100644 => 100755 .github/workflows/branch.yml mode change 100644 => 100755 .github/workflows/ci.yml mode change 100644 => 100755 .github/workflows/clean-up.yml mode change 100644 => 100755 .github/workflows/download_pipeline.yml mode change 100644 => 100755 .github/workflows/fix-linting.yml mode change 100644 => 100755 .github/workflows/linting.yml mode change 100644 => 100755 .github/workflows/linting_comment.yml mode change 100644 => 100755 .github/workflows/release-announcements.yml create mode 100755 conf/test_alphafold_download.config create mode 100755 conf/test_colabfold_download.config mode change 100644 => 100755 dockerfiles/Dockerfile_nfcore-proteinfold_alphafold2_msa mode change 100644 => 100755 dockerfiles/Dockerfile_nfcore-proteinfold_alphafold2_split mode change 100644 => 100755 dockerfiles/Dockerfile_nfcore-proteinfold_alphafold2_standard mode change 100644 => 100755 dockerfiles/Dockerfile_nfcore-proteinfold_colabfold mode change 100644 => 100755 dockerfiles/Dockerfile_nfcore-proteinfold_esmfold mode change 100644 => 100755 docs/README.md mode change 100644 => 100755 docs/images/T1024_LmrP____408_residues__PAE_mqc.png mode change 100644 => 100755 docs/images/T1024_LmrP____408_residues__coverage_mqc.png mode change 100644 => 100755 docs/images/T1024_LmrP____408_residues__plddt_mqc.png mode change 100644 => 100755 docs/images/nf-core-proteinfold_logo_dark.png mode change 100644 => 100755 docs/images/nf-core-proteinfold_logo_light.png mode change 100644 => 100755 docs/images/nf-core-proteinfold_metro_map.png mode change 100644 => 100755 docs/images/nf-core-proteinfold_metro_map.svg mode change 100644 => 100755 docs/images/nf-core-proteinfold_metro_map_1.1.0.png mode change 100644 => 100755 docs/images/nf-core-proteinfold_metro_map_1.1.0.svg mode change 100644 => 100755 docs/images/nf-core-proteinfold_metro_map_1.1.0_transp.png mode change 100644 => 100755 docs/output.md mode change 100644 => 100755 docs/usage.md create mode 100755 modules/nf-core/aria2/environment.yml diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index b290e090..81ac8f34 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -2,7 +2,6 @@ "name": "nfcore", "image": "nfcore/gitpod:latest", "remoteUser": "gitpod", - "runArgs": ["--privileged"], // Configure tool-specific properties. "customizations": { @@ -10,11 +9,19 @@ "vscode": { // Set *default* container specific settings.json values on container create. "settings": { - "python.defaultInterpreterPath": "/opt/conda/bin/python" + "python.defaultInterpreterPath": "/opt/conda/bin/python", + "python.linting.enabled": true, + "python.linting.pylintEnabled": true, + "python.formatting.autopep8Path": "/opt/conda/bin/autopep8", + "python.formatting.yapfPath": "/opt/conda/bin/yapf", + "python.linting.flake8Path": "/opt/conda/bin/flake8", + "python.linting.pycodestylePath": "/opt/conda/bin/pycodestyle", + "python.linting.pydocstylePath": "/opt/conda/bin/pydocstyle", + "python.linting.pylintPath": "/opt/conda/bin/pylint" }, // Add the IDs of extensions you want installed when the container is created. "extensions": ["ms-python.python", "ms-python.vscode-pylance", "nf-core.nf-core-extensionpack"] } } -} +} \ No newline at end of file diff --git a/.editorconfig b/.editorconfig index dd9ffa53..72dda289 100644 --- a/.editorconfig +++ b/.editorconfig @@ -28,10 +28,6 @@ indent_style = unset [/assets/email*] indent_size = unset -# ignore Readme -[README.md] -indent_style = unset - -# ignore python +# ignore python and markdown [*.{py,md}] indent_style = unset diff --git a/.github/.dockstore.yml b/.github/.dockstore.yml old mode 100644 new mode 100755 diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md old mode 100644 new mode 100755 index f34ebfd5..ad8a7f87 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -29,7 +29,7 @@ If you're not used to this workflow with git, you can start with some [docs from You have the option to test your changes locally by running the pipeline. For receiving warnings about process selectors and other `debug` information, it is recommended to use the debug profile. Execute all the tests with the following command: ```bash -nf-test test --profile debug,test,docker --verbose +nextflow run . --profile debug,test,docker --outdir ``` When you create a pull request with changes, [GitHub Actions](https://github.com/features/actions) will run automatic tests. diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml old mode 100644 new mode 100755 diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml old mode 100644 new mode 100755 diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml old mode 100644 new mode 100755 diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md old mode 100644 new mode 100755 index 3dde701c..8dc3e6a4 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -18,7 +18,7 @@ Learn more about contributing: [CONTRIBUTING.md](https://github.com/nf-core/prot - [ ] If you've added a new tool - have you followed the pipeline conventions in the [contribution docs](https://github.com/nf-core/proteinfold/tree/master/.github/CONTRIBUTING.md) - [ ] If necessary, also make a PR on the nf-core/proteinfold _branch_ on the [nf-core/test-datasets](https://github.com/nf-core/test-datasets) repository. - [ ] Make sure your code lints (`nf-core lint`). -- [ ] Ensure the test suite passes (`nf-test test main.nf.test -profile test,docker`). +- [ ] Ensure the test suite passes (`nextflow run . -profile test,docker --outdir `). - [ ] Check for unexpected warnings in debug mode (`nextflow run . -profile debug,test,docker --outdir `). - [ ] Usage Documentation in `docs/usage.md` is updated. - [ ] Output Documentation in `docs/output.md` is updated. diff --git a/.github/workflows/awsfulltest.yml b/.github/workflows/awsfulltest.yml old mode 100644 new mode 100755 index eef3ae69..3774758d --- a/.github/workflows/awsfulltest.yml +++ b/.github/workflows/awsfulltest.yml @@ -8,8 +8,8 @@ on: types: [published] workflow_dispatch: jobs: - run-tower: - name: Run AWS AlphaFold2 full monomer tests + run-platform: + name: Run AWS full tests if: github.repository == 'nf-core/proteinfold' runs-on: ubuntu-latest # Do a full-scale run on each of the mode @@ -17,17 +17,17 @@ jobs: matrix: mode: [ - "_alphafold2_standard", - "_alphafold2_split", - "_alphafold2_multimer", - "_colabfold_local", - "_colabfold_webserver", - "_colabfold_multimer", - "_esmfold", - "_esmfold_multimer", + "alphafold2_standard", + "alphafold2_split", + "alphafold2_multimer", + "colabfold_local", + "colabfold_webserver", + "colabfold_multimer", + "esmfold", + "esmfold_multimer", ] steps: - - name: Launch workflow via tower + - name: Launch workflow via Seqera Platform uses: seqeralabs/action-tower-launch@v2 with: workspace_id: ${{ secrets.TOWER_WORKSPACE_ID }} @@ -40,10 +40,12 @@ jobs: "hook_url": "${{ secrets.MEGATESTS_ALERTS_SLACK_HOOK_URL }}", "outdir": "s3://${{ secrets.AWS_S3_BUCKET }}/proteinfold/results-${{ github.sha }}/mode_${{ matrix.mode }}" } - profiles: test_full_${{ matrix.mode }},aws_tower + profiles: test_full_${{ matrix.mode }} + - uses: actions/upload-artifact@v4 + if: success() || failure() with: - name: Tower debug log file + name: Seqera Platform debug log file path: | - tower_action_*.log - tower_action_*.json + seqera_platform_action_*.log + seqera_platform_action_*.json diff --git a/.github/workflows/awstest.yml b/.github/workflows/awstest.yml old mode 100644 new mode 100755 index 70e47cdf..ee725793 --- a/.github/workflows/awstest.yml +++ b/.github/workflows/awstest.yml @@ -5,13 +5,13 @@ name: nf-core AWS test on: workflow_dispatch: jobs: - run-tower: + run-platform: name: Run AWS tests if: github.repository == 'nf-core/proteinfold' runs-on: ubuntu-latest steps: - # Launch workflow using Tower CLI tool action - - name: Launch workflow via tower + # Launch workflow using Seqera Platform CLI tool action + - name: Launch workflow via Seqera Platform uses: seqeralabs/action-tower-launch@v2 with: workspace_id: ${{ secrets.TOWER_WORKSPACE_ID }} @@ -27,7 +27,7 @@ jobs: - uses: actions/upload-artifact@v4 with: - name: Tower debug log file + name: Seqera Platform debug log file path: | - tower_action_*.log - tower_action_*.json + seqera_platform_action_*.log + seqera_platform_action_*.json diff --git a/.github/workflows/branch.yml b/.github/workflows/branch.yml old mode 100644 new mode 100755 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml old mode 100644 new mode 100755 index 17260c5d..47ad6707 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,82 +26,27 @@ jobs: NXF_VER: - "23.04.0" - "latest-everything" + parameters: + - "test" + - "test_alphafold2_split" + - "test_alphafold2_download" + - "test_colabfold_local" + - "test_colabfold_webserver" + - "test_colabfold_download" + - "test_esmfold" + steps: - name: Check out pipeline code - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 + uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4 - name: Install Nextflow - uses: nf-core/setup-nextflow@v1 + uses: nf-core/setup-nextflow@v2 with: version: "${{ matrix.NXF_VER }}" - name: Disk space cleanup uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be # v1.3.1 - - name: Run pipeline with test data - run: | - nextflow run ${GITHUB_WORKSPACE} -profile test,docker --outdir ./results - - test_alphafold2_split: - name: Test alphafold2 split workflow - if: ${{ github.event_name != 'push' || (github.event_name == 'push' && github.repository == 'nf-core/proteinfold') }} - runs-on: ubuntu-latest - steps: - - name: Check out pipeline code - uses: actions/checkout@v2 - - - name: Install Nextflow - run: | - wget -qO- get.nextflow.io | bash - sudo mv nextflow /usr/local/bin/ - - name: Run pipeline with stub-run in alphafold2 split mode - run: | - nextflow run ${GITHUB_WORKSPACE} -profile test_alphafold2_split,docker --outdir ./results - - test_colabfold_local: - name: Test Colabfold local workflow - if: ${{ github.event_name != 'push' || (github.event_name == 'push' && github.repository == 'nf-core/proteinfold') }} - runs-on: ubuntu-latest - steps: - - name: Check out pipeline code - uses: actions/checkout@v2 - - - name: Install Nextflow - run: | - wget -qO- get.nextflow.io | bash - sudo mv nextflow /usr/local/bin/ - - name: Run pipeline with stub-run in colabfold_local mode - run: | - nextflow run ${GITHUB_WORKSPACE} -profile test_colabfold_local,docker --outdir ./results - - test_colabfold_webserver: - name: Test Colabfold webserver workflow - if: ${{ github.event_name != 'push' || (github.event_name == 'push' && github.repository == 'nf-core/proteinfold') }} - runs-on: ubuntu-latest - steps: - - name: Check out pipeline code - uses: actions/checkout@v2 - - - name: Install Nextflow - run: | - wget -qO- get.nextflow.io | bash - sudo mv nextflow /usr/local/bin/ - - name: Run pipeline with stub-run in colabfold_webserver mode - run: | - nextflow run ${GITHUB_WORKSPACE} -profile test_colabfold_webserver,docker --outdir ./results - - test_esmfold: - name: Test ESMFold workflow - if: ${{ github.event_name != 'push' || (github.event_name == 'push' && github.repository == 'nf-core/proteinfold') }} - runs-on: ubuntu-latest - steps: - - name: Check out pipeline code - uses: actions/checkout@v2 - - - name: Install Nextflow - run: | - wget -qO- get.nextflow.io | bash - sudo mv nextflow /usr/local/bin/ - - name: Run pipeline with stub-run in esmfold mode + - name: Run pipeline with test data ${{ matrix.parameters }} profile run: | - nextflow run ${GITHUB_WORKSPACE} -profile test_esmfold,docker --outdir ./results + nextflow run ${GITHUB_WORKSPACE} -profile ${{ matrix.parameters }},docker --outdir ./results_${{ matrix.parameters }} diff --git a/.github/workflows/clean-up.yml b/.github/workflows/clean-up.yml old mode 100644 new mode 100755 diff --git a/.github/workflows/download_pipeline.yml b/.github/workflows/download_pipeline.yml old mode 100644 new mode 100755 index 08622fd5..640ac03c --- a/.github/workflows/download_pipeline.yml +++ b/.github/workflows/download_pipeline.yml @@ -14,6 +14,8 @@ on: pull_request: types: - opened + - edited + - synchronize branches: - master pull_request_target: @@ -28,11 +30,14 @@ jobs: runs-on: ubuntu-latest steps: - name: Install Nextflow - uses: nf-core/setup-nextflow@v1 + uses: nf-core/setup-nextflow@v2 - - uses: actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c # v5 + - name: Disk space cleanup + uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be # v1.3.1 + + - uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # v5 with: - python-version: "3.11" + python-version: "3.12" architecture: "x64" - uses: eWaterCycle/setup-singularity@931d4e31109e875b13309ae1d07c70ca8fbc8537 # v7 with: @@ -41,7 +46,7 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install git+https://github.com/nf-core/tools.git@dev + pip install git+https://github.com/nf-core/tools.git - name: Get the repository name and current branch set as environment variable run: | @@ -65,8 +70,17 @@ jobs: - name: Inspect download run: tree ./${{ env.REPOTITLE_LOWERCASE }} - - name: Run the downloaded pipeline + - name: Run the downloaded pipeline (stub) + id: stub_run_pipeline + continue-on-error: true env: NXF_SINGULARITY_CACHEDIR: ./ NXF_SINGULARITY_HOME_MOUNT: true run: nextflow run ./${{ env.REPOTITLE_LOWERCASE }}/$( sed 's/\W/_/g' <<< ${{ env.REPO_BRANCH }}) -stub -profile test,singularity --outdir ./results + - name: Run the downloaded pipeline (stub run not supported) + id: run_pipeline + if: ${{ job.steps.stub_run_pipeline.status == failure() }} + env: + NXF_SINGULARITY_CACHEDIR: ./ + NXF_SINGULARITY_HOME_MOUNT: true + run: nextflow run ./${{ env.REPOTITLE_LOWERCASE }}/$( sed 's/\W/_/g' <<< ${{ env.REPO_BRANCH }}) -profile test,singularity --outdir ./results diff --git a/.github/workflows/fix-linting.yml b/.github/workflows/fix-linting.yml old mode 100644 new mode 100755 index f459fc21..ddaa085a --- a/.github/workflows/fix-linting.yml +++ b/.github/workflows/fix-linting.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest steps: # Use the @nf-core-bot token to check out so we can push later - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4 with: token: ${{ secrets.nf_core_bot_auth_token }} @@ -32,9 +32,9 @@ jobs: GITHUB_TOKEN: ${{ secrets.nf_core_bot_auth_token }} # Install and run pre-commit - - uses: actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c # v5 + - uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # v5 with: - python-version: 3.11 + python-version: "3.12" - name: Install pre-commit run: pip install pre-commit diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml old mode 100644 new mode 100755 index 073e1876..1fcafe88 --- a/.github/workflows/linting.yml +++ b/.github/workflows/linting.yml @@ -14,13 +14,12 @@ jobs: pre-commit: runs-on: ubuntu-latest steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4 - - name: Set up Python 3.11 - uses: actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c # v5 + - name: Set up Python 3.12 + uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # v5 with: - python-version: 3.11 - cache: "pip" + python-version: "3.12" - name: Install pre-commit run: pip install pre-commit @@ -32,14 +31,14 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out pipeline code - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 + uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4 - name: Install Nextflow - uses: nf-core/setup-nextflow@v1 + uses: nf-core/setup-nextflow@v2 - - uses: actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c # v5 + - uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # v5 with: - python-version: "3.11" + python-version: "3.12" architecture: "x64" - name: Install dependencies @@ -60,7 +59,7 @@ jobs: - name: Upload linting log file artifact if: ${{ always() }} - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4 + uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4 with: name: linting-logs path: | diff --git a/.github/workflows/linting_comment.yml b/.github/workflows/linting_comment.yml old mode 100644 new mode 100755 index b706875f..40acc23f --- a/.github/workflows/linting_comment.yml +++ b/.github/workflows/linting_comment.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Download lint results - uses: dawidd6/action-download-artifact@f6b0bace624032e30a85a8fd9c1a7f8f611f5737 # v3 + uses: dawidd6/action-download-artifact@09f2f74827fd3a8607589e5ad7f9398816f540fe # v3 with: workflow: linting.yml workflow_conclusion: completed diff --git a/.github/workflows/release-announcements.yml b/.github/workflows/release-announcements.yml old mode 100644 new mode 100755 index d468aeaa..03ecfcf7 --- a/.github/workflows/release-announcements.yml +++ b/.github/workflows/release-announcements.yml @@ -12,7 +12,7 @@ jobs: - name: get topics and convert to hashtags id: get_topics run: | - curl -s https://nf-co.re/pipelines.json | jq -r '.remote_workflows[] | select(.full_name == "${{ github.repository }}") | .topics[]' | awk '{print "#"$0}' | tr '\n' ' ' >> $GITHUB_OUTPUT + echo "topics=$(curl -s https://nf-co.re/pipelines.json | jq -r '.remote_workflows[] | select(.full_name == "${{ github.repository }}") | .topics[]' | awk '{print "#"$0}' | tr '\n' ' ')" >> $GITHUB_OUTPUT - uses: rzr/fediverse-action@master with: @@ -25,13 +25,13 @@ jobs: Please see the changelog: ${{ github.event.release.html_url }} - ${{ steps.get_topics.outputs.GITHUB_OUTPUT }} #nfcore #openscience #nextflow #bioinformatics + ${{ steps.get_topics.outputs.topics }} #nfcore #openscience #nextflow #bioinformatics send-tweet: runs-on: ubuntu-latest steps: - - uses: actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c # v5 + - uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # v5 with: python-version: "3.10" - name: Install dependencies diff --git a/.nf-core.yml b/.nf-core.yml index cfe39173..39feb094 100644 --- a/.nf-core.yml +++ b/.nf-core.yml @@ -1,6 +1,6 @@ repository_type: pipeline +nf_core_version: "2.14.1" lint: files_unchanged: - - .github/ISSUE_TEMPLATE/bug_report.yml - - pyproject.toml - multiqc_config: false + - .github/CONTRIBUTING.md + multiqc_config: false \ No newline at end of file diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index af57081f..10207fa6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -3,8 +3,11 @@ repos: rev: "v3.1.0" hooks: - id: prettier + additional_dependencies: + - prettier@3.2.5 + - repo: https://github.com/editorconfig-checker/editorconfig-checker.python rev: "2.7.3" hooks: - id: editorconfig-checker - alias: ec + alias: ec \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 7dfac881..2fbddadb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,30 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## 1.1.0dev - [date] +## [[1.1.1](https://github.com/nf-core/proteinfold/releases/tag/1.1.1)] - 2025-07-30 + +- Minor patch release to fix multiqc report. + +### Enhancements & fixes + +## [[1.1.0](https://github.com/nf-core/proteinfold/releases/tag/1.1.0)] - 2025-06-25 + +### Credits + +Special thanks to the following for their contributions to the release: + +- [Adam Talbot](https://github.com/adamrtalbot) +- [Athanasios Baltzis](https://github.com/athbaltzis) +- [Björn Langer](https://github.com/bjlang) +- [Igor Trujnara](https://github.com/itrujnara) +- [Matthias Hörtenhuber](https://github.com/mashehu) +- [Maxime Garcia](https://github.com/maxulysse) +- [Júlia Mir Pedrol](https://github.com/mirpedrol) +- [Ziad Al-Bkhetan](https://github.com/ziadbkh) + +Thank you to everyone else that has contributed by reporting bugs, enhancements or in any other way, shape or form. + +## [[1.1.0](https://github.com/nf-core/proteinfold/releases/tag/1.1.0)] - 2025-06-21 ### Enhancements & fixes @@ -30,6 +53,49 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [[#115](https://github.com/nf-core/proteinfold/issues/115)] - Throw message error when profile conda is used. - [[#131](https://github.com/nf-core/proteinfold/issues/131)] - Add esmfold small tests. - [[#144](https://github.com/nf-core/proteinfold/issues/144)] - Force value channels when providing dbs (downloaded) in `main.nf` to enable the processing of multiple samples. +- [[#147](https://github.com/nf-core/proteinfold/issues/147)] - Update modules to last version. +- [[#145](https://github.com/nf-core/proteinfold/issues/145)] - Implement test to check the processes/subworkflows triggered when downloading the databases. +- [[#130](https://github.com/nf-core/proteinfold/issues/130)] - Add `--skip_multiqc` parameter. +- [[PR #154](https://github.com/nf-core/proteinfold/pull/154)] - Update pipeline template to [nf-core/tools 2.14.1](https://github.com/nf-core/tools/releases/tag/2.14.1). +- [[#148](https://github.com/nf-core/proteinfold/issues/148)] - Update Colabfold DBs. +- [[PR #159](https://github.com/nf-core/proteinfold/pull/159)] - Update `mgnify` paths to new available version. +- [[PR ##163](https://github.com/nf-core/proteinfold/pull/163)] - Fix full test CI. +- [[#150]](https://github.com/nf-core/proteinfold/issues/150)] - Add thanks to the AWS Open Data Sponsorship program in `README.md`. +- [[PR ##166](https://github.com/nf-core/proteinfold/pull/166)] - Create 2 different parameters for Colabfold and ESMfold number of recycles. + +### Parameters + +| Old parameter | New parameter | +| --------------------- | ---------------------------------------- | +| `--uniclust30` | | +| `--bfd` | `--bfd_link` | +| `--small_bfd` | `--small_bfd_link` | +| `--alphafold2_params` | `--alphafold2_params_link` | +| `--mgnify` | `--mgnify_link` | +| `--pdb70` | `--pdb70_link` | +| `--pdb_mmcif` | `--pdb_mmcif_link` | +| `--pdb_obsolete` | `--pdb_obsolete_link` | +| `--uniref90` | `--uniref90_link` | +| `--pdb_seqres` | `--pdb_seqres_link` | +| `--uniprot_sprot` | `--uniprot_sprot_link` | +| `--uniprot_trembl` | `--uniprot_trembl_link` | +| `--uniclust30_path` | `--uniref30_alphafold2_path` | +| `--uniref30` | `--uniref30_colabfold_link` | +| `--uniref30_path` | `--uniref30_colabfold_path` | +| `--num_recycle` | `--num_recycles_colabfold` | +| | `--num_recycles_esmfold` | +| | `--uniref30_alphafold2_link` | +| | `--esmfold_db` | +| | `--esmfold_model_preset` | +| | `--esmfold_3B_v1` | +| | `--esm2_t36_3B_UR50D` | +| | `--esm2_t36_3B_UR50D_contact_regression` | +| | `--esmfold_params_path` | +| | `--skip_multiqc` | + +> **NB:** Parameter has been **updated** if both old and new parameter information is present. +> **NB:** Parameter has been **added** if just the new parameter information is present. +> **NB:** Parameter has been **removed** if parameter information isn't present. ## 1.0.0 - White Silver Reebok diff --git a/README.md b/README.md index d1eed3a2..63a92f26 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ [![run with conda](http://img.shields.io/badge/run%20with-conda-3EB049?labelColor=000000&logo=anaconda)](https://docs.conda.io/en/latest/) [![run with docker](https://img.shields.io/badge/run%20with-docker-0db7ed?labelColor=000000&logo=docker)](https://www.docker.com/) [![run with singularity](https://img.shields.io/badge/run%20with-singularity-1d355c.svg?labelColor=000000)](https://sylabs.io/docs/) -[![Launch on Seqera Platform](https://img.shields.io/badge/Launch%20%F0%9F%9A%80-Seqera%20Platform-%234256e7)](https://tower.nf/launch?pipeline=https://github.com/nf-core/proteinfold) +[![Launch on Seqera Platform](https://img.shields.io/badge/Launch%20%F0%9F%9A%80-Seqera%20Platform-%234256e7)](https://cloud.seqera.io/launch?pipeline=https://github.com/nf-core/proteinfold) [![Get help on Slack](http://img.shields.io/badge/slack-nf--core%20%23proteinfold-4A154B?labelColor=000000&logo=slack)](https://nfcore.slack.com/channels/proteinfold)[![Follow on Twitter](http://img.shields.io/badge/twitter-%40nf__core-1DA1F2?labelColor=000000&logo=twitter)](https://twitter.com/nf_core)[![Follow on Mastodon](https://img.shields.io/badge/mastodon-nf__core-6364ff?labelColor=FFFFFF&logo=mastodon)](https://mstdn.science/@nf_core)[![Watch on YouTube](http://img.shields.io/badge/youtube-nf--core-FF0000?labelColor=000000&logo=youtube)](https://www.youtube.com/c/nf-core) @@ -31,7 +31,7 @@ On release, automated continuous integration tests run the pipeline on a full-si 1. Choice of protein structure prediction method: - i. [AlphaFold2](https://github.com/deepmind/alphafold) + i. [AlphaFold2](https://github.com/deepmind/alphafold) - Regular AlphaFold2 (MSA computation and model inference in the same process) ii. [AlphaFold2 split](https://github.com/luisas/alphafold_split) - AlphaFold2 MSA computation and model inference in separate processes @@ -39,7 +39,7 @@ On release, automated continuous integration tests run the pipeline on a full-si iv. [ColabFold](https://github.com/sokrypton/ColabFold) - MMseqs2 local search followed by ColabFold - v. [ESMFold](https://github.com/facebookresearch/esm) + v. [ESMFold](https://github.com/facebookresearch/esm) - Regular ESM ## Usage @@ -55,9 +55,9 @@ nextflow run nf-core/proteinfold \ --outdir ``` -The pipeline takes care of downloading the required databases and parameters required by AlphaFold2, Colabfold or ESMFold. In case you have already downloaded the required files, you can skip this step by providing the path using the corresponding parameter [`--alphafold2_db`], [`--colabfold_db`] or [`--esmfold_db`] +The pipeline takes care of downloading the databases and parameters required by AlphaFold2, Colabfold or ESMFold. In case you have already downloaded the required files, you can skip this step by providing the path to the databases using the corresponding parameter [`--alphafold2_db`], [`--colabfold_db`] or [`--esmfold_db`]. Please refer to the [usage documentation](https://nf-co.re/proteinfold/usage) to check the directory structure you need to provide for each of the databases. -- Typical command to run AlphaFold2 mode: +- The typical command to run AlphaFold2 mode is shown below: ```console nextflow run nf-core/proteinfold \ @@ -71,7 +71,7 @@ The pipeline takes care of downloading the required databases and parameters req -profile ``` -- Typical command to run AlphaFold2 splitting the MSA from the prediction execution: +- Here is the command to run AlphaFold2 splitting the MSA from the prediction execution: ```console nextflow run nf-core/proteinfold \ @@ -86,7 +86,7 @@ The pipeline takes care of downloading the required databases and parameters req -profile ``` -- Typical command to run colabfold_local mode: +- Below, the command to run colabfold_local mode: ```console nextflow run nf-core/proteinfold \ @@ -95,7 +95,7 @@ The pipeline takes care of downloading the required databases and parameters req --mode colabfold \ --colabfold_server local \ --colabfold_db \ - --num_recycle 3 \ + --num_recycles_colabfold 3 \ --use_amber \ --colabfold_model_preset "AlphaFold2-ptm" \ --use_gpu \ @@ -103,7 +103,7 @@ The pipeline takes care of downloading the required databases and parameters req -profile ``` -- Typical command to run colabfold_webserver mode: +- The typical command to run colabfold_webserver mode would be: ```console nextflow run nf-core/proteinfold \ @@ -113,17 +113,18 @@ The pipeline takes care of downloading the required databases and parameters req --colabfold_server webserver \ --host_url \ --colabfold_db \ - --num_recycle 3 \ + --num_recycles_colabfold 3 \ --use_amber \ --colabfold_model_preset "AlphaFold2-ptm" \ --use_gpu \ -profile ``` - > **Warning** + [!WARNING] + > If you aim to carry out a large amount of predictions using the colabfold_webserver mode, please setup and use your own custom MMSeqs2 API Server. You can find instructions [here](https://github.com/sokrypton/ColabFold/tree/main/MsaServer). -- Typical command to run esmfold mode: +- The esmfold mode can be run using the command below: ```console nextflow run nf-core/proteinfold \ @@ -132,7 +133,7 @@ The pipeline takes care of downloading the required databases and parameters req --mode esmfold \ --esmfold_model_preset \ --esmfold_db \ - --num_recycles 4 \ + --num_recycles_esmfold 4 \ --use_gpu \ -profile ``` @@ -153,10 +154,10 @@ For more details about the output files and reports, please refer to the nf-core/proteinfold was originally written by Athanasios Baltzis ([@athbaltzis](https://github.com/athbaltzis)), Jose Espinosa-Carrasco ([@JoseEspinosa](https://github.com/JoseEspinosa)), Luisa Santus ([@luisas](https://github.com/luisas)) and Leila Mansouri ([@l-mansouri](https://github.com/l-mansouri)) from [The Comparative Bioinformatics Group](https://www.crg.eu/en/cedric_notredame) at [The Centre for Genomic Regulation, Spain](https://www.crg.eu/) under the umbrella of the [BovReg project](https://www.bovreg.eu/) and Harshil Patel ([@drpatelh](https://github.com/drpatelh)) from [Seqera Labs, Spain](https://seqera.io/). -We thank the following people for their extensive assistance in the development of this pipeline: - Many thanks to others who have helped out and contributed along the way too, including (but not limited to): Norman Goodacre and Waleed Osman from Interline Therapeutics ([@interlinetx](https://github.com/interlinetx)), Martin Steinegger ([@martin-steinegger](https://github.com/martin-steinegger)) and Raoul J.P. Bonnal ([@rjpbonnal](https://github.com/rjpbonnal)) +We would also like to thanks to the AWS Open Data Sponsorship Program for generously providing the resources necessary to host the data utilized in the testing, development, and deployment of nf-core proteinfold. + ## Contributions and Support If you would like to contribute to this pipeline, please see the [contributing guidelines](.github/CONTRIBUTING.md). diff --git a/assets/multiqc_config.yml b/assets/multiqc_config.yml index f6acb16a..0238c366 100644 --- a/assets/multiqc_config.yml +++ b/assets/multiqc_config.yml @@ -1,7 +1,7 @@ report_comment: > - This report has been generated by the nf-core/proteinfold + This report has been generated by the nf-core/proteinfold analysis pipeline. For information about how to interpret these results, please see the - documentation. + documentation. report_section_order: "nf-core-proteinfold-methods-description": order: -1000 @@ -19,4 +19,4 @@ run_modules: - run_alphafold2_pred - colabfold_batch -disable_version_detection: true +disable_version_detection: true \ No newline at end of file diff --git a/conf/base.config b/conf/base.config index f13f56b2..69ad41e9 100644 --- a/conf/base.config +++ b/conf/base.config @@ -59,7 +59,4 @@ process { errorStrategy = 'retry' maxRetries = 2 } - withName:CUSTOM_DUMPSOFTWAREVERSIONS { - cache = false - } } diff --git a/conf/modules.config b/conf/modules.config index 955501f3..c12b372d 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -20,14 +20,6 @@ process { mode: params.publish_dir_mode, saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] - - withName: 'CUSTOM_DUMPSOFTWAREVERSIONS' { - publishDir = [ - path: { "${params.outdir}/pipeline_info" }, - mode: params.publish_dir_mode, - pattern: '*_versions.yml' - ] - } } // diff --git a/conf/modules_alphafold2.config b/conf/modules_alphafold2.config index 9a266160..4aae2d30 100644 --- a/conf/modules_alphafold2.config +++ b/conf/modules_alphafold2.config @@ -15,7 +15,7 @@ // process { - withName: 'GUNZIP|COMBINE_UNIPROT|DOWNLOAD_PDBMMCIF' { + withName: 'GUNZIP|COMBINE_UNIPROT|DOWNLOAD_PDBMMCIF|ARIA2_PDB_SEQRES' { publishDir = [ path: {"${params.outdir}/DBs/${params.mode}/${params.alphafold2_mode}"}, mode: 'symlink', diff --git a/conf/modules_colabfold.config b/conf/modules_colabfold.config index 3a6d2e23..a7a719b0 100644 --- a/conf/modules_colabfold.config +++ b/conf/modules_colabfold.config @@ -37,7 +37,8 @@ if (params.colabfold_server == 'local') { ] } withName: 'MMSEQS_CREATEINDEX' { - ext.args = '--remove-tmp-files 1' + ext.args = '--remove-tmp-files 1' + ext.args2 = '*_seq.tsv' publishDir = [ enabled: false ] diff --git a/conf/test.config b/conf/test.config index dfdebd88..b8996f1a 100644 --- a/conf/test.config +++ b/conf/test.config @@ -24,7 +24,7 @@ params { // Input data to test alphafold2 analysis mode = 'alphafold2' alphafold2_mode = 'standard' - input = 'https://raw.githubusercontent.com/nf-core/test-datasets/proteinfold/testdata/samplesheet/v1.0/samplesheet.csv' + input = params.pipelines_testdata_base_path + '/proteinfold/testdata/samplesheet/v1.0/samplesheet.csv' alphafold2_db = "${projectDir}/assets/dummy_db_dir" } diff --git a/conf/test_alphafold_download.config b/conf/test_alphafold_download.config new file mode 100755 index 00000000..24e7e5c8 --- /dev/null +++ b/conf/test_alphafold_download.config @@ -0,0 +1,32 @@ +/* +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Nextflow config file for running minimal tests +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Defines input files and everything required to run a fast and simple pipeline test. + Use as follows: + nextflow run nf-core/proteinfold -profile test_alphafold2_download, --outdir +---------------------------------------------------------------------------------------- +*/ + +stubRun = true + +params { + config_profile_name = 'Test profile' + config_profile_description = 'Minimal test dataset to check pipeline function' + + // Limit resources so that this can run on GitHub Actions + max_cpus = 2 + max_memory = '6.GB' + max_time = '6.h' + + // Input data to test alphafold2 analysis + mode = 'alphafold2' + alphafold2_mode = 'standard' + input = params.pipelines_testdata_base_path + 'proteinfold/testdata/samplesheet/v1.0/samplesheet.csv' +} + +process { + withName: 'ARIA2|UNTAR|RUN_ALPHAFOLD2' { + container = 'biocontainers/gawk:5.1.0' + } +} \ No newline at end of file diff --git a/conf/test_alphafold_split.config b/conf/test_alphafold_split.config index 1bc651f6..295ffd67 100644 --- a/conf/test_alphafold_split.config +++ b/conf/test_alphafold_split.config @@ -24,7 +24,7 @@ params { // Input data to test alphafold2 splitting MSA from prediction analysis mode = 'alphafold2' alphafold2_mode = 'split_msa_prediction' - input = 'https://raw.githubusercontent.com/nf-core/test-datasets/proteinfold/testdata/samplesheet/v1.0/samplesheet.csv' + input = params.pipelines_testdata_base_path + 'proteinfold/testdata/samplesheet/v1.0/samplesheet.csv' alphafold2_db = "${projectDir}/assets/dummy_db_dir" } diff --git a/conf/test_colabfold_download.config b/conf/test_colabfold_download.config new file mode 100755 index 00000000..7a641118 --- /dev/null +++ b/conf/test_colabfold_download.config @@ -0,0 +1,32 @@ +/* +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Nextflow config file for running minimal tests +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Defines input files and everything required to run a fast and simple pipeline test. + Use as follows: + nextflow run nf-core/proteinfold -profile test_colabfold_download, --outdir +---------------------------------------------------------------------------------------- +*/ + +stubRun = true + +params { + config_profile_name = 'Test profile' + config_profile_description = 'Minimal test dataset to check pipeline function' + + // Limit resources so that this can run on GitHub Actions + max_cpus = 2 + max_memory = '6.GB' + max_time = '6.h' + + // Input data to test colabfold analysis + mode = 'colabfold' + colabfold_server = 'webserver' + input = params.pipelines_testdata_base_path + 'proteinfold/testdata/samplesheet/v1.0/samplesheet.csv' +} + +process { + withName: 'ARIA2|UNTAR|COLABFOLD_BATCH' { + container = 'biocontainers/gawk:5.1.0' + } +} \ No newline at end of file diff --git a/conf/test_colabfold_local.config b/conf/test_colabfold_local.config index 43c04b4c..b401c0aa 100644 --- a/conf/test_colabfold_local.config +++ b/conf/test_colabfold_local.config @@ -4,7 +4,7 @@ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Defines input files and everything required to run a fast and simple pipeline test. Use as follows: - nextflow run nf-core/proteinfold -profile test, --outdir + nextflow run nf-core/proteinfold -profile test_colabfold_local, --outdir ---------------------------------------------------------------------------------------- */ @@ -23,7 +23,7 @@ params { mode = 'colabfold' colabfold_server = 'local' colabfold_db = "${projectDir}/assets/dummy_db_dir" - input = 'https://raw.githubusercontent.com/nf-core/test-datasets/proteinfold/testdata/samplesheet/v1.0/samplesheet.csv' + input = params.pipelines_testdata_base_path + 'proteinfold/testdata/samplesheet/v1.0/samplesheet.csv' } process { diff --git a/conf/test_colabfold_webserver.config b/conf/test_colabfold_webserver.config index 99ebf182..3cd74de7 100644 --- a/conf/test_colabfold_webserver.config +++ b/conf/test_colabfold_webserver.config @@ -4,7 +4,7 @@ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Defines input files and everything required to run a fast and simple pipeline test. Use as follows: - nextflow run nf-core/proteinfold -profile test, --outdir + nextflow run nf-core/proteinfold -profile test_colabfold_webserver, --outdir ---------------------------------------------------------------------------------------- */ @@ -23,7 +23,7 @@ params { mode = 'colabfold' colabfold_server = 'webserver' colabfold_db = "${projectDir}/assets/dummy_db_dir" - input = 'https://raw.githubusercontent.com/nf-core/test-datasets/proteinfold/testdata/samplesheet/v1.0/samplesheet.csv' + input = params.pipelines_testdata_base_path + 'proteinfold/testdata/samplesheet/v1.0/samplesheet.csv' } process { diff --git a/conf/test_esmfold.config b/conf/test_esmfold.config index 38202ac8..ad984742 100644 --- a/conf/test_esmfold.config +++ b/conf/test_esmfold.config @@ -4,7 +4,7 @@ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Defines input files and everything required to run a fast and simple pipeline test. Use as follows: - nextflow run nf-core/proteinfold -profile test, --outdir + nextflow run nf-core/proteinfold -profile test_esmfold, --outdir ---------------------------------------------------------------------------------------- */ @@ -22,7 +22,7 @@ params { // Input data to test esmfold mode = 'esmfold' esmfold_db = "${projectDir}/assets/dummy_db_dir" - input = 'https://raw.githubusercontent.com/nf-core/test-datasets/proteinfold/testdata/samplesheet/v1.0/samplesheet.csv' + input = params.pipelines_testdata_base_path + 'proteinfold/testdata/samplesheet/v1.0/samplesheet.csv' } process { diff --git a/conf/test_full.config b/conf/test_full.config index 2c8a4fae..18233938 100644 --- a/conf/test_full.config +++ b/conf/test_full.config @@ -17,6 +17,6 @@ params { // Input data for full test of alphafold standard mode mode = 'alphafold2' alphafold2_mode = 'standard' - input = 'https://raw.githubusercontent.com/nf-core/test-datasets/proteinfold/testdata/samplesheet/v1.0/samplesheet.csv' + input = params.pipelines_testdata_base_path + 'proteinfold/testdata/samplesheet/v1.0/samplesheet.csv' alphafold2_db = 's3://proteinfold-dataset/test-data/db/alphafold_mini' } diff --git a/conf/test_full_alphafold_multimer.config b/conf/test_full_alphafold_multimer.config index 2f8b0627..62e81966 100644 --- a/conf/test_full_alphafold_multimer.config +++ b/conf/test_full_alphafold_multimer.config @@ -18,6 +18,6 @@ params { mode = 'alphafold2' alphafold2_mode = 'standard' alphafold2_model_preset = 'multimer' - input = 'https://raw.githubusercontent.com/nf-core/test-datasets/proteinfold/testdata/samplesheet/v1.0/samplesheet_multimer.csv' + input = params.pipelines_testdata_base_path + 'proteinfold/testdata/samplesheet/v1.0/samplesheet_multimer.csv' alphafold2_db = 's3://proteinfold-dataset/test-data/db/alphafold_mini' } diff --git a/conf/test_full_alphafold_split.config b/conf/test_full_alphafold_split.config index 9cb378c2..90df73f2 100644 --- a/conf/test_full_alphafold_split.config +++ b/conf/test_full_alphafold_split.config @@ -17,6 +17,6 @@ params { // Input data to test colabfold with a local server analysis mode = 'alphafold2' alphafold2_mode = 'split_msa_prediction' - input = 'https://raw.githubusercontent.com/nf-core/test-datasets/proteinfold/testdata/samplesheet/v1.0/samplesheet.csv' + input = params.pipelines_testdata_base_path + 'proteinfold/testdata/samplesheet/v1.0/samplesheet.csv' alphafold2_db = 's3://proteinfold-dataset/test-data/db/alphafold_mini' } diff --git a/conf/test_full_colabfold_local.config b/conf/test_full_colabfold_local.config index 90f1c811..ad91f5e0 100644 --- a/conf/test_full_colabfold_local.config +++ b/conf/test_full_colabfold_local.config @@ -19,7 +19,7 @@ params { mode = 'colabfold' colabfold_server = 'local' colabfold_model_preset = 'alphafold2_ptm' - input = 'https://raw.githubusercontent.com/nf-core/test-datasets/proteinfold/testdata/samplesheet/v1.0/samplesheet.csv' + input = params.pipelines_testdata_base_path + 'proteinfold/testdata/samplesheet/v1.0/samplesheet.csv' colabfold_db = 's3://proteinfold-dataset/test-data/db/colabfold_mini' } process { diff --git a/conf/test_full_colabfold_webserver.config b/conf/test_full_colabfold_webserver.config index a9db381a..7e296189 100644 --- a/conf/test_full_colabfold_webserver.config +++ b/conf/test_full_colabfold_webserver.config @@ -18,6 +18,6 @@ params { mode = 'colabfold' colabfold_server = 'webserver' colabfold_model_preset = 'alphafold2_ptm' - input = 'https://raw.githubusercontent.com/nf-core/test-datasets/proteinfold/testdata/samplesheet/v1.0/samplesheet.csv' + input = params.pipelines_testdata_base_path + 'proteinfold/testdata/samplesheet/v1.0/samplesheet.csv' colabfold_db = 's3://proteinfold-dataset/test-data/db/colabfold_mini' } diff --git a/conf/test_full_colabfold_webserver_multimer.config b/conf/test_full_colabfold_webserver_multimer.config index 612a1221..c8adca61 100644 --- a/conf/test_full_colabfold_webserver_multimer.config +++ b/conf/test_full_colabfold_webserver_multimer.config @@ -18,6 +18,6 @@ params { mode = 'colabfold' colabfold_server = 'webserver' colabfold_model_preset = 'alphafold2_multimer_v3' - input = 'https://raw.githubusercontent.com/nf-core/test-datasets/proteinfold/testdata/samplesheet/v1.0/samplesheet_multimer.csv' + input = params.pipelines_testdata_base_path + 'proteinfold/testdata/samplesheet/v1.0/samplesheet_multimer.csv' colabfold_db = 's3://proteinfold-dataset/test-data/db/colabfold_mini' } diff --git a/conf/test_full_esmfold.config b/conf/test_full_esmfold.config index 64bb7628..136e7414 100644 --- a/conf/test_full_esmfold.config +++ b/conf/test_full_esmfold.config @@ -5,7 +5,7 @@ Defines input files and everything required to run a full size pipeline test. Use as follows: - nextflow run nf-core/proteinfold -profile test_full_colabfold_webserver, --outdir + nextflow run nf-core/proteinfold -profile test_full_esmfold, --outdir ---------------------------------------------------------------------------------------- */ @@ -17,6 +17,6 @@ params { // Input data for full test of esmfold monomer mode = 'esmfold' esmfold_model_preset = 'monomer' - input = 'https://raw.githubusercontent.com/nf-core/test-datasets/proteinfold/testdata/samplesheet/v1.0/samplesheet.csv' + input = params.pipelines_testdata_base_path + 'proteinfold/testdata/samplesheet/v1.0/samplesheet.csv' //esmfold_db = 's3://proteinfold-dataset/test-data/db/esmfold' } diff --git a/conf/test_full_esmfold_multimer.config b/conf/test_full_esmfold_multimer.config index 36445443..498ae002 100644 --- a/conf/test_full_esmfold_multimer.config +++ b/conf/test_full_esmfold_multimer.config @@ -5,7 +5,7 @@ Defines input files and everything required to run a full size pipeline test. Use as follows: - nextflow run nf-core/proteinfold -profile test_full_colabfold_webserver, --outdir + nextflow run nf-core/proteinfold -profile test_full_esmfold_multimer, --outdir ---------------------------------------------------------------------------------------- */ @@ -17,6 +17,6 @@ params { // Input data for full test of esmfold multimer mode = 'esmfold' esmfold_model_preset = 'multimer' - input = 'https://raw.githubusercontent.com/nf-core/test-datasets/proteinfold/testdata/samplesheet/v1.0/samplesheet_multimer.csv' + input = params.pipelines_testdata_base_path + 'proteinfold/testdata/samplesheet/v1.0/samplesheet_multimer.csv' esmfold_db = 's3://proteinfold-dataset/test-data/db/esmfold' } diff --git a/dockerfiles/Dockerfile_nfcore-proteinfold_alphafold2_msa b/dockerfiles/Dockerfile_nfcore-proteinfold_alphafold2_msa old mode 100644 new mode 100755 index af184d3a..64baaa38 --- a/dockerfiles/Dockerfile_nfcore-proteinfold_alphafold2_msa +++ b/dockerfiles/Dockerfile_nfcore-proteinfold_alphafold2_msa @@ -9,7 +9,7 @@ LABEL authors="Luisa Santus, Athanasios Baltzis, Jose Espinosa-Carrasco, Leila M SHELL ["/bin/bash", "-o", "pipefail", "-c"] # Add env variables -ENV LD_LIBRARY_PATH="/conda/lib:/usr/local/cuda-11.1/lib64:$LD_LIBRARY_PATH" +ENV LD_LIBRARY_PATH="/conda/lib:/usr/local/cuda-11.4/lib64:$LD_LIBRARY_PATH" ENV PATH="/conda/bin:$PATH" RUN apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A4B469963BF863CC diff --git a/dockerfiles/Dockerfile_nfcore-proteinfold_alphafold2_split b/dockerfiles/Dockerfile_nfcore-proteinfold_alphafold2_split old mode 100644 new mode 100755 index 2c7fd102..4f4c89b4 --- a/dockerfiles/Dockerfile_nfcore-proteinfold_alphafold2_split +++ b/dockerfiles/Dockerfile_nfcore-proteinfold_alphafold2_split @@ -9,7 +9,7 @@ LABEL authors="Athanasios Baltzis, Jose Espinosa-Carrasco, Leila Mansouri" \ SHELL ["/bin/bash", "-o", "pipefail", "-c"] # Add env variables -ENV LD_LIBRARY_PATH="/conda/lib:/usr/local/cuda-11.1/lib64:$LD_LIBRARY_PATH" +ENV LD_LIBRARY_PATH="/conda/lib:/usr/local/cuda-11.4/lib64:$LD_LIBRARY_PATH" ENV PATH="/conda/bin:$PATH" RUN apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A4B469963BF863CC diff --git a/dockerfiles/Dockerfile_nfcore-proteinfold_alphafold2_standard b/dockerfiles/Dockerfile_nfcore-proteinfold_alphafold2_standard old mode 100644 new mode 100755 index e7b49f5a..774d89f6 --- a/dockerfiles/Dockerfile_nfcore-proteinfold_alphafold2_standard +++ b/dockerfiles/Dockerfile_nfcore-proteinfold_alphafold2_standard @@ -9,7 +9,7 @@ LABEL authors="Athanasios Baltzis, Jose Espinosa-Carrasco, Leila Mansouri" \ SHELL ["/bin/bash", "-o", "pipefail", "-c"] # Add env variables -ENV LD_LIBRARY_PATH="/conda/lib:/usr/local/cuda-11.1/lib64:$LD_LIBRARY_PATH" +ENV LD_LIBRARY_PATH="/conda/lib:/usr/local/cuda-11.4/lib64:$LD_LIBRARY_PATH" ENV PATH="/conda/bin:$PATH" RUN apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A4B469963BF863CC diff --git a/dockerfiles/Dockerfile_nfcore-proteinfold_colabfold b/dockerfiles/Dockerfile_nfcore-proteinfold_colabfold old mode 100644 new mode 100755 index 6e639b50..2ac1f851 --- a/dockerfiles/Dockerfile_nfcore-proteinfold_colabfold +++ b/dockerfiles/Dockerfile_nfcore-proteinfold_colabfold @@ -1,5 +1,6 @@ -FROM nvidia/cuda:11.4.2-cudnn8-runtime-ubuntu18.04 -LABEL authors="Athanasios Baltzis, Leila Mansouri" \ +FROM nvidia/cuda:11.4.3-cudnn8-runtime-ubuntu18.04 + +LABEL authors="Athanasios Baltzis, Jose Espinosa-Carrasco, Leila Mansouri" \ title="nfcore/proteinfold_colabfold" \ Version="1.1.0" \ description="Docker image containing all software requirements to run the COLABFOLD_BATCH module using the nf-core/proteinfold pipeline" @@ -24,7 +25,7 @@ RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y \ && rm -rf /var/lib/apt/lists/* RUN cd / \ - && wget https://raw.githubusercontent.com/YoshitakaMo/localcolabfold/fb0598ebbd8227096a052d3684a549ddf33d06bb/install_colabbatch_linux.sh \ + && wget https://raw.githubusercontent.com/YoshitakaMo/localcolabfold/82a3635/install_colabbatch_linux.sh \ && sed -i "/colabfold.download/d" install_colabbatch_linux.sh \ && sed -i "s|cudatoolkit==.*\sopenmm|cudatoolkit==11.1.1 openmm|g" install_colabbatch_linux.sh \ && bash install_colabbatch_linux.sh @@ -33,4 +34,4 @@ RUN /localcolabfold/colabfold-conda/bin/python3.10 -m pip install tensorflow-cpu #Silence download of the AlphaFold2 params RUN sed -i "s|download_alphafold_params(|#download_alphafold_params(|g" /localcolabfold/colabfold-conda/lib/python3.10/site-packages/colabfold/batch.py - +RUN sed -i "s|if args\.num_models|#if args\.num_models|g" /localcolabfold/colabfold-conda/lib/python3.10/site-packages/colabfold/batch.py diff --git a/dockerfiles/Dockerfile_nfcore-proteinfold_esmfold b/dockerfiles/Dockerfile_nfcore-proteinfold_esmfold old mode 100644 new mode 100755 diff --git a/docs/README.md b/docs/README.md old mode 100644 new mode 100755 diff --git a/docs/images/T1024_LmrP____408_residues__PAE_mqc.png b/docs/images/T1024_LmrP____408_residues__PAE_mqc.png old mode 100644 new mode 100755 diff --git a/docs/images/T1024_LmrP____408_residues__coverage_mqc.png b/docs/images/T1024_LmrP____408_residues__coverage_mqc.png old mode 100644 new mode 100755 diff --git a/docs/images/T1024_LmrP____408_residues__plddt_mqc.png b/docs/images/T1024_LmrP____408_residues__plddt_mqc.png old mode 100644 new mode 100755 diff --git a/docs/images/nf-core-proteinfold_logo_dark.png b/docs/images/nf-core-proteinfold_logo_dark.png old mode 100644 new mode 100755 diff --git a/docs/images/nf-core-proteinfold_logo_light.png b/docs/images/nf-core-proteinfold_logo_light.png old mode 100644 new mode 100755 diff --git a/docs/images/nf-core-proteinfold_metro_map.png b/docs/images/nf-core-proteinfold_metro_map.png old mode 100644 new mode 100755 diff --git a/docs/images/nf-core-proteinfold_metro_map.svg b/docs/images/nf-core-proteinfold_metro_map.svg old mode 100644 new mode 100755 diff --git a/docs/images/nf-core-proteinfold_metro_map_1.1.0.png b/docs/images/nf-core-proteinfold_metro_map_1.1.0.png old mode 100644 new mode 100755 diff --git a/docs/images/nf-core-proteinfold_metro_map_1.1.0.svg b/docs/images/nf-core-proteinfold_metro_map_1.1.0.svg old mode 100644 new mode 100755 diff --git a/docs/images/nf-core-proteinfold_metro_map_1.1.0_transp.png b/docs/images/nf-core-proteinfold_metro_map_1.1.0_transp.png old mode 100644 new mode 100755 diff --git a/docs/output.md b/docs/output.md old mode 100644 new mode 100755 diff --git a/docs/usage.md b/docs/usage.md old mode 100644 new mode 100755 index 9544d587..12e47552 --- a/docs/usage.md +++ b/docs/usage.md @@ -18,9 +18,7 @@ You will need to create a samplesheet with information about the sequences you w ### Full samplesheet -The samplesheet can have as many columns as you desire, however, there is a strict requirement for the first 2 columns to match those defined in the table below. - -A final samplesheet file may look something like the one below. This is for 2 sequences. +A sample of the final samplesheet file for two sequences is shown below: ```csv title="samplesheet.csv" sequence,fasta @@ -28,6 +26,8 @@ T1024,https://raw.githubusercontent.com/nf-core/test-datasets/proteinfold/testda T1026,https://raw.githubusercontent.com/nf-core/test-datasets/proteinfold/testdata/sequences/T1026.fasta ``` +The samplesheet can have as many columns as you desire, however, there is a strict requirement for the first 2 columns to match those defined in the table below: + | Column | Description | | ---------- | --------------------------------------------------------------------------------------------------- | | `sequence` | Custom sequence name. Spaces in sequence names are automatically converted to underscores (`_`). | @@ -37,9 +37,11 @@ An [example samplesheet](../assets/samplesheet.csv) has been provided with the p ## Running the pipeline -The typical commands for running the pipeline on AlphaFold2, Colabfold and ESMFold modes are as follows: +The typical commands for running the pipeline on AlphaFold2, Colabfold and ESMFold modes are shown below. -```csv title="samplesheet.csv" +AlphaFold2 regular can be run using this command: + +```bash nextflow run nf-core/proteinfold \ --input samplesheet.csv \ --outdir \ @@ -48,10 +50,12 @@ nextflow run nf-core/proteinfold \ --full_dbs \ --alphafold2_model_preset monomer \ --use_gpu \ - -profile + -profile ``` -```console +To run the AlphaFold2 that splits the MSA calculation from the model inference, you can use the `--alphafold2_mode split_msa_prediction` parameter, as shown below: + +```bash nextflow run nf-core/proteinfold \ --input samplesheet.csv \ --outdir \ @@ -61,36 +65,43 @@ nextflow run nf-core/proteinfold \ --full_dbs \ --alphafold2_model_preset monomer \ --use_gpu \ - -profile + -profile ``` -If you specify the `--alphafold2_db ` parameter, the directory structure of your path should be like this: +To provide the predownloaded AlphaFold2 databases and parameters you can specify the `--alphafold2_db ` parameter and the directory structure of your path should be like this: -``` -├── mgnify -│   └── mgy_clusters_2018_12.fa -├── alphafold_params_2022-03-02 +
+Directory structure +```console +├── alphafold_params_2022-12-06 │   ├── LICENSE │   ├── params_model_1_multimer.npz │   ├── params_model_1_multimer_v2.npz +│   ├── params_model_1_multimer_v3.npz │   ├── params_model_1.npz │   ├── params_model_1_ptm.npz │   ├── params_model_2_multimer.npz │   ├── params_model_2_multimer_v2.npz +│   ├── params_model_2_multimer_v3.npz │   ├── params_model_2.npz │   ├── params_model_2_ptm.npz │   ├── params_model_3_multimer.npz │   ├── params_model_3_multimer_v2.npz +│   ├── params_model_3_multimer_v3.npz │   ├── params_model_3.npz │   ├── params_model_3_ptm.npz │   ├── params_model_4_multimer.npz │   ├── params_model_4_multimer_v2.npz +│   ├── params_model_4_multimer_v3.npz │   ├── params_model_4.npz │   ├── params_model_4_ptm.npz │   ├── params_model_5_multimer.npz │   ├── params_model_5_multimer_v2.npz +│   ├── params_model_5_multimer_v3.npz │   ├── params_model_5.npz │   └── params_model_5_ptm.npz +├── mgnify +│   └── mgy_clusters_2022_05.fa ├── pdb70 │   └── pdb70_from_mmcif_200916 │   ├── md5sum @@ -200,43 +211,40 @@ If you specify the `--alphafold2_db ` parameter, the directory structure of your │   └── pdb_seqres.txt ├── small_bfd │   └── bfd-first_non_consensus_sequences.fasta -├── uniclust30 -│   └── uniclust30_2018_08 -│   ├── uniclust30_2018_08_a3m_db -> uniclust30_2018_08_a3m.ffdata -│   ├── uniclust30_2018_08_a3m_db.index -│   ├── uniclust30_2018_08_a3m.ffdata -│   ├── uniclust30_2018_08_a3m.ffindex -│   ├── uniclust30_2018_08.cs219 -│   ├── uniclust30_2018_08_cs219.ffdata -│   ├── uniclust30_2018_08_cs219.ffindex -│   ├── uniclust30_2018_08.cs219.sizes -│   ├── uniclust30_2018_08_hhm_db -> uniclust30_2018_08_hhm.ffdata -│   ├── uniclust30_2018_08_hhm_db.index -│   ├── uniclust30_2018_08_hhm.ffdata -│   ├── uniclust30_2018_08_hhm.ffindex -│   └── uniclust30_2018_08_md5sum ├── uniprot │   └── uniprot.fasta +├── uniref30 +│   ├── UniRef30_2021_03_a3m.ffdata +│   ├── UniRef30_2021_03_a3m.ffindex +│   ├── UniRef30_2021_03_cs219.ffdata +│   ├── UniRef30_2021_03_cs219.ffindex +| ├── UniRef30_2021_03_hhm.ffdata +│   └── UniRef30_2021_03_hhm.ffindex └── uniref90 └── uniref90.fasta ``` +
-```console +Colabfold mode using use your own custom MMSeqs2 API server (`--colabfold_server local`) can be run using the following command: + +```bash nextflow run nf-core/proteinfold \ --input samplesheet.csv \ --outdir \ --mode colabfold \ --colabfold_server local \ --colabfold_db \ - --num_recycle 3 \ + --num_recycles_colabfold 3 \ --use_amber \ --colabfold_model_preset "AlphaFold2-ptm" \ --use_gpu \ - --db_load_mode 0 - -profile + --db_load_mode 0 \ + -profile ``` -```console +The command to run run Colabfold, using the Colabfold webserver is shown below: + +```bash nextflow run nf-core/proteinfold \ --input samplesheet.csv \ --outdir \ @@ -244,16 +252,18 @@ nextflow run nf-core/proteinfold \ --colabfold_server webserver \ --host_url \ --colabfold_db \ - --num_recycle 3 \ + --num_recycles_colabfold 3 \ --use_amber \ --colabfold_model_preset "AlphaFold2-ptm" \ --use_gpu \ - -profile + -profile ``` -If you specify the `--colabfold_db ` parameter, the directory structure of your path should be like this: +If you specify the `--colabfold_db ` parameter, the directory structure of your path should be like this: -``` +
+Directory structure +```console ├── colabfold_envdb_202108 │   ├── colabfold_envdb_202108_db.0 │   ├── colabfold_envdb_202108_db.1 @@ -331,60 +341,65 @@ If you specify the `--colabfold_db ` parameter, the directory structure of your │   │   ├── params_model_4_ptm.npz │   │   ├── params_model_5.npz │   │   └── params_model_5_ptm.npz -│   └── alphafold_params_colab_2022-03-02 +│   └── alphafold_params_colab_2022-12-06 │   ├── LICENSE -│   ├── params_model_1_multimer_v2.npz +│   ├── params_model_1_multimer_v3.npz │   ├── params_model_1.npz -│   ├── params_model_2_multimer_v2.npz +│   ├── params_model_2_multimer_v3.npz │   ├── params_model_2.npz │   ├── params_model_2_ptm.npz -│   ├── params_model_3_multimer_v2.npz +│   ├── params_model_3_multimer_v3.npz │   ├── params_model_3.npz -│   ├── params_model_4_multimer_v2.npz +│   ├── params_model_4_multimer_v3.npz │   ├── params_model_4.npz -│   ├── params_model_5_multimer_v2.npz +│   ├── params_model_5_multimer_v3.npz │   └── params_model_5.npz -└── uniref30_2202 - ├── uniref30_2202_db.0 - ├── uniref30_2202_db.1 - ├── uniref30_2202_db.2 - ├── uniref30_2202_db.3 - ├── uniref30_2202_db.4 - ├── uniref30_2202_db.5 - ├── uniref30_2202_db.6 - ├── uniref30_2202_db.7 - ├── uniref30_2202_db_aln.0 - ├── uniref30_2202_db_aln.1 - ├── uniref30_2202_db_aln.2 - ├── uniref30_2202_db_aln.3 - ├── uniref30_2202_db_aln.4 - ├── uniref30_2202_db_aln.5 - ├── uniref30_2202_db_aln.6 - ├── uniref30_2202_db_aln.7 - ├── uniref30_2202_db_aln.dbtype - ├── uniref30_2202_db_aln.index - ├── uniref30_2202_db.dbtype - ├── uniref30_2202_db_h - ├── uniref30_2202_db_h.dbtype - ├── uniref30_2202_db_h.index - ├── uniref30_2202_db.idx - ├── uniref30_2202_db.idx.dbtype - ├── uniref30_2202_db.idx.index - ├── uniref30_2202_db.index - ├── uniref30_2202_db_seq.0 - ├── uniref30_2202_db_seq.1 - ├── uniref30_2202_db_seq.2 - ├── uniref30_2202_db_seq.3 - ├── uniref30_2202_db_seq.4 - ├── uniref30_2202_db_seq.5 - ├── uniref30_2202_db_seq.6 - ├── uniref30_2202_db_seq.7 - ├── uniref30_2202_db_seq.dbtype - ├── uniref30_2202_db_seq_h -> uniref30_2202_db_h - ├── uniref30_2202_db_seq_h.dbtype -> uniref30_2202_db_h.dbtype - ├── uniref30_2202_db_seq_h.index -> uniref30_2202_db_h.index - └── uniref30_2202_db_seq.index +└── uniref30_2302 + ├── uniref30_2302_aln.tsv + ├── uniref30_2302_db.0 + ├── uniref30_2302_db.1 + ├── uniref30_2302_db.2 + ├── uniref30_2302_db.3 + ├── uniref30_2302_db.4 + ├── uniref30_2302_db.5 + ├── uniref30_2302_db.6 + ├── uniref30_2302_db.7 + ├── uniref30_2302_db_aln.0 + ├── uniref30_2302_db_aln.1 + ├── uniref30_2302_db_aln.2 + ├── uniref30_2302_db_aln.3 + ... + ├── uniref30_2302_db_aln.97 + ├── uniref30_2302_db_aln.98 + ├── uniref30_2302_db_aln.99 + ├── uniref30_2302_db_aln.dbtype + ├── uniref30_2302_db_aln.index + ├── uniref30_2302_db.dbtype + ├── uniref30_2302_db_h + ├── uniref30_2302_db_h.dbtype + ├── uniref30_2302_db_h.index + ├── uniref30_2302_db.idx + ├── uniref30_2302_db.idx.dbtype + ├── uniref30_2302_db.idx.index + ├── uniref30_2302_db.idx_mapping + ├── uniref30_2302_db.idx_taxonomy + ├── uniref30_2302_db.index + ├── uniref30_2302_db_mapping + ├── uniref30_2302_db_seq.0 + ├── uniref30_2302_db_seq.1 + ├── uniref30_2302_db_seq.2 + ├── uniref30_2302_db_seq.3 + ... + ├── uniref30_2302_db_seq.97 + ├── uniref30_2302_db_seq.98 + ├── uniref30_2302_db_seq.99 + ├── uniref30_2302_db_seq.dbtype + ├── uniref30_2302_db_seq_h -> uniref30_2302_db_h + ├── uniref30_2302_db_seq_h.dbtype -> uniref30_2302_db_h.dbtype + ├── uniref30_2302_db_seq_h.index -> uniref30_2302_db_h.index + └── uniref30_2302_db_seq.index ``` +
```console nextflow run nf-core/proteinfold \ @@ -392,13 +407,13 @@ nextflow run nf-core/proteinfold \ --outdir \ --mode esmfold --esmfold_db \ - --num_recycles 4 \ + --num_recycles_esmfold 4 \ --esmfold_model_preset \ --use_gpu \ -profile ``` -If you specify the `--esmfold_db ` parameter, the directory structure of your path should be like this: +If you specify the `--esmfold_db ` parameter, the directory structure of your path should be like this: ```console └── checkpoints @@ -503,6 +518,8 @@ If `-profile` is not specified, the pipeline will run locally and expect all sof - A generic configuration profile to be used with [Charliecloud](https://hpc.github.io/charliecloud/) - `apptainer` - A generic configuration profile to be used with [Apptainer](https://apptainer.org/) +- `wave` + - A generic configuration profile to enable [Wave](https://seqera.io/wave/) containers. Use together with one of the above (requires Nextflow ` 24.03.0-edge` or later). - `conda` - A generic configuration profile to be used with [Conda](https://conda.io/docs/). Please only use Conda as a last resort i.e. when it's not possible to run the pipeline with Docker, Singularity, Podman, Shifter, Charliecloud, or Apptainer. diff --git a/main.nf b/main.nf index 3d960943..07d06410 100644 --- a/main.nf +++ b/main.nf @@ -127,7 +127,7 @@ workflow NFCORE_PROTEINFOLD { PREPARE_COLABFOLD_DBS.out.params.first(), PREPARE_COLABFOLD_DBS.out.colabfold_db.first(), PREPARE_COLABFOLD_DBS.out.uniref30.first(), - params.num_recycle + params.num_recycles_colabfold ) ch_multiqc = COLABFOLD.out.multiqc_report ch_versions = ch_versions.mix(COLABFOLD.out.versions) @@ -156,7 +156,7 @@ workflow NFCORE_PROTEINFOLD { ESMFOLD ( ch_versions, Channel.fromPath(params.esmfold_params_path), - params.num_recycle + params.num_recycles_esmfold ) ch_multiqc = ESMFOLD.out.multiqc_report ch_versions = ch_versions.mix(ESMFOLD.out.versions) diff --git a/modules/local/colabfold_batch.nf b/modules/local/colabfold_batch.nf index 28f26274..5dab51fb 100644 --- a/modules/local/colabfold_batch.nf +++ b/modules/local/colabfold_batch.nf @@ -7,7 +7,7 @@ process COLABFOLD_BATCH { error("Local COLABFOLD_BATCH module does not support Conda. Please use Docker / Singularity / Podman instead.") } - container "nf-core/proteinfold_colabfold:1.1.0" + container "nf-core/proteinfold_colabfold:1.1.1" input: tuple val(meta), path(fasta) diff --git a/modules/local/download_pdbmmcif.nf b/modules/local/download_pdbmmcif.nf index fef63755..98ef831e 100644 --- a/modules/local/download_pdbmmcif.nf +++ b/modules/local/download_pdbmmcif.nf @@ -2,6 +2,7 @@ * Download PDB MMCIF database */ process DOWNLOAD_PDBMMCIF { + tag "${source_url_pdb_mmcif}--${source_url_pdb_obsolete}" label 'process_low' label 'error_retry' diff --git a/modules/local/mmseqs_colabfoldsearch.nf b/modules/local/mmseqs_colabfoldsearch.nf index 978a627e..b879c8cd 100644 --- a/modules/local/mmseqs_colabfoldsearch.nf +++ b/modules/local/mmseqs_colabfoldsearch.nf @@ -7,7 +7,7 @@ process MMSEQS_COLABFOLDSEARCH { error("Local MMSEQS_COLABFOLDSEARCH module does not support Conda. Please use Docker / Singularity / Podman instead.") } - container "nf-core/proteinfold_colabfold:1.1.0" + container "nf-core/proteinfold_colabfold:1.1.1" input: tuple val(meta), path(fasta) @@ -16,7 +16,7 @@ process MMSEQS_COLABFOLDSEARCH { path uniref30 output: - tuple val(meta), path("${meta.id}.a3m"), emit: a3m + tuple val(meta), path("**.a3m"), emit: a3m path "versions.yml", emit: versions when: @@ -47,7 +47,8 @@ process MMSEQS_COLABFOLDSEARCH { stub: def VERSION = '1.5.2' // WARN: Version information not provided by tool on CLI. Please update this string when bumping container versions. """ - touch ${meta.id}.a3m + mkdir results + touch results/${meta.id}.a3m cat <<-END_VERSIONS > versions.yml "${task.process}": diff --git a/modules/local/org_run_alphafold2.nf b/modules/local/org_run_alphafold2.nf index 9d5c72f4..c46c2246 100644 --- a/modules/local/org_run_alphafold2.nf +++ b/modules/local/org_run_alphafold2.nf @@ -10,7 +10,7 @@ process RUN_ALPHAFOLD2 { error("Local RUN_ALPHAFOLD2 module does not support Conda. Please use Docker / Singularity / Podman instead.") } - container "nf-core/proteinfold_alphafold2_standard:dev" + container "nf-core/proteinfold_alphafold2_standard:1.1.1" input: tuple val(meta), path(fasta) diff --git a/modules/local/org_run_esmfold.nf b/modules/local/org_run_esmfold.nf index f4567239..c20a22c0 100644 --- a/modules/local/org_run_esmfold.nf +++ b/modules/local/org_run_esmfold.nf @@ -6,7 +6,7 @@ process RUN_ESMFOLD { error("Local RUN_ESMFOLD module does not support Conda. Please use Docker / Singularity / Podman instead.") } - container "nf-core/proteinfold_esmfold:1.1.0" + container "nf-core/proteinfold_esmfold:1.1.1" input: tuple val(meta), path(fasta) diff --git a/modules/local/run_alphafold2.nf b/modules/local/run_alphafold2.nf index 570243d5..31bdac8c 100644 --- a/modules/local/run_alphafold2.nf +++ b/modules/local/run_alphafold2.nf @@ -10,7 +10,7 @@ process RUN_ALPHAFOLD2 { error("Local RUN_ALPHAFOLD2 module does not support Conda. Please use Docker / Singularity / Podman instead.") } - container "nf-core/proteinfold_alphafold2_standard:dev" + container "nf-core/proteinfold_alphafold2_standard:1.1.1" input: tuple val(meta), path(fasta) diff --git a/modules/local/run_alphafold2_msa.nf b/modules/local/run_alphafold2_msa.nf index 7b904dd9..991387b4 100644 --- a/modules/local/run_alphafold2_msa.nf +++ b/modules/local/run_alphafold2_msa.nf @@ -10,7 +10,7 @@ process RUN_ALPHAFOLD2_MSA { error("Local RUN_ALPHAFOLD2_MSA module does not support Conda. Please use Docker / Singularity / Podman instead.") } - container "nf-core/proteinfold_alphafold2_msa:dev" + container "nf-core/proteinfold_alphafold2_msa:1.1.1" input: tuple val(meta), path(fasta) diff --git a/modules/local/run_alphafold2_pred.nf b/modules/local/run_alphafold2_pred.nf index 88b4f878..66ba22de 100644 --- a/modules/local/run_alphafold2_pred.nf +++ b/modules/local/run_alphafold2_pred.nf @@ -10,7 +10,7 @@ process RUN_ALPHAFOLD2_PRED { error("Local RUN_ALPHAFOLD2_PRED module does not support Conda. Please use Docker / Singularity / Podman instead.") } - container "nf-core/proteinfold_alphafold2_split:dev" + container "nf-core/proteinfold_alphafold2_split:1.1.1" input: tuple val(meta), path(fasta) diff --git a/modules/local/run_esmfold.nf b/modules/local/run_esmfold.nf index d0216dfc..490f16f7 100644 --- a/modules/local/run_esmfold.nf +++ b/modules/local/run_esmfold.nf @@ -6,7 +6,7 @@ process RUN_ESMFOLD { error("Local RUN_ESMFOLD module does not support Conda. Please use Docker / Singularity / Podman instead.") } - container "nf-core/proteinfold_esmfold:1.1.0" + container "nf-core/proteinfold_esmfold:1.1.1" input: tuple val(meta), path(fasta) diff --git a/modules/nf-core/aria2/environment.yml b/modules/nf-core/aria2/environment.yml new file mode 100755 index 00000000..1095bd44 --- /dev/null +++ b/modules/nf-core/aria2/environment.yml @@ -0,0 +1,7 @@ +name: aria2 +channels: + - conda-forge + - bioconda + - defaults +dependencies: + - conda-forge::aria2=1.36.0 \ No newline at end of file diff --git a/modules/nf-core/aria2/main.nf b/modules/nf-core/aria2/main.nf index af595a9f..972ee036 100644 --- a/modules/nf-core/aria2/main.nf +++ b/modules/nf-core/aria2/main.nf @@ -1,19 +1,18 @@ - process ARIA2 { tag "$source_url" label 'process_single' - conda "conda-forge::aria2=1.36.0" + conda "${moduleDir}/environment.yml" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? 'https://depot.galaxyproject.org/singularity/aria2:1.36.0' : 'biocontainers/aria2:1.36.0' }" input: - val source_url + tuple val(meta), val(source_url) output: - path ("$downloaded_file"), emit: downloaded_file - path "versions.yml" , emit: versions + tuple val(meta), path("$downloaded_file"), emit: downloaded_file + path "versions.yml" , emit: versions when: task.ext.when == null || task.ext.when @@ -23,8 +22,6 @@ process ARIA2 { downloaded_file = source_url.split("/")[-1] """ - set -e - aria2c \\ --check-certificate=false \\ $args \\ @@ -35,4 +32,16 @@ process ARIA2 { aria2: \$(echo \$(aria2c --version 2>&1) | grep 'aria2 version' | cut -f3 -d ' ') END_VERSIONS """ -} + + stub: + downloaded_file = source_url.split("/")[-1] + + """ + touch ${downloaded_file} + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + aria2: \$(echo \$(aria2c --version 2>&1) | grep 'aria2 version' | cut -f3 -d ' ') + END_VERSIONS + """ +} \ No newline at end of file diff --git a/nextflow.config b/nextflow.config index cac3227b..a773b20f 100644 --- a/nextflow.config +++ b/nextflow.config @@ -49,7 +49,7 @@ params { // Colabfold parameters colabfold_server = "webserver" colabfold_model_preset = "alphafold2_ptm" // {'auto', 'alphafold2', 'alphafold2_ptm', 'alphafold2_multimer_v1', 'alphafold2_multimer_v2', 'alphafold2_multimer_v3'} - num_recycle = 3 + num_recycles_colabfold = 3 use_amber = true colabfold_db = null db_load_mode = 0 @@ -68,7 +68,7 @@ params { // Esmfold parameters esmfold_db = null esmfold_model_preset = "monomer" - num_recycles = 4 + num_recycles_esmfold = 4 // Esmfold links esmfold_3B_v1 = null @@ -77,6 +77,9 @@ params { // Esmfold paths esmfold_params_path = null + + // Process skipping options + skip_multiqc = false // MultiQC options multiqc_config = null @@ -95,7 +98,8 @@ params { hook_url = null help = false version = false - + pipelines_testdata_base_path = 'https://raw.githubusercontent.com/nf-core/test-datasets/' + // Config options config_profile_name = null config_profile_description = null @@ -138,30 +142,30 @@ try { profiles { debug { - dumpHashes = true - process.beforeScript = 'echo $HOSTNAME' - cleanup = false + dumpHashes = true + process.beforeScript = 'echo $HOSTNAME' + cleanup = false nextflow.enable.configProcessNamesValidation = true } conda { - conda.enabled = true - docker.enabled = false - singularity.enabled = false - podman.enabled = false - shifter.enabled = false - charliecloud.enabled = false - channels = ['conda-forge', 'bioconda', 'defaults'] - apptainer.enabled = false + conda.enabled = true + docker.enabled = false + singularity.enabled = false + podman.enabled = false + shifter.enabled = false + charliecloud.enabled = false + conda.channels = ['conda-forge', 'bioconda', 'defaults'] + apptainer.enabled = false } mamba { - conda.enabled = true - conda.useMamba = true - docker.enabled = false - singularity.enabled = false - podman.enabled = false - shifter.enabled = false - charliecloud.enabled = false - apptainer.enabled = false + conda.enabled = true + conda.useMamba = true + docker.enabled = false + singularity.enabled = false + podman.enabled = false + shifter.enabled = false + charliecloud.enabled = false + apptainer.enabled = false } docker { docker.enabled = true @@ -197,51 +201,60 @@ profiles { apptainer.enabled = false } podman { - podman.enabled = true - conda.enabled = false - docker.enabled = false - singularity.enabled = false - shifter.enabled = false - charliecloud.enabled = false - apptainer.enabled = false + podman.enabled = true + conda.enabled = false + docker.enabled = false + singularity.enabled = false + shifter.enabled = false + charliecloud.enabled = false + apptainer.enabled = false } shifter { - shifter.enabled = true - conda.enabled = false - docker.enabled = false - singularity.enabled = false - podman.enabled = false - charliecloud.enabled = false - apptainer.enabled = false + shifter.enabled = true + conda.enabled = false + docker.enabled = false + singularity.enabled = false + podman.enabled = false + charliecloud.enabled = false + apptainer.enabled = false } charliecloud { - charliecloud.enabled = true - conda.enabled = false - docker.enabled = false - singularity.enabled = false - podman.enabled = false - shifter.enabled = false - apptainer.enabled = false + charliecloud.enabled = true + conda.enabled = false + docker.enabled = false + singularity.enabled = false + podman.enabled = false + shifter.enabled = false + apptainer.enabled = false } apptainer { - apptainer.enabled = true - apptainer.autoMounts = true - conda.enabled = false - docker.enabled = false - singularity.enabled = false - podman.enabled = false - shifter.enabled = false - charliecloud.enabled = false + apptainer.enabled = true + apptainer.autoMounts = true + conda.enabled = false + docker.enabled = false + singularity.enabled = false + podman.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + wave { + apptainer.ociAutoPull = true + singularity.ociAutoPull = true + wave.enabled = true + wave.freeze = true + wave.strategy = 'conda,container' } gitpod { - executor.name = 'local' - executor.cpus = 4 - executor.memory = 8.GB + executor.name = 'local' + executor.cpus = 4 + executor.memory = 8.GB } test { includeConfig 'conf/test.config' } test_alphafold2_split { includeConfig 'conf/test_alphafold_split.config' } + test_alphafold2_download { includeConfig 'conf/test_alphafold_download.config' } test_colabfold_local { includeConfig 'conf/test_colabfold_local.config' } test_colabfold_webserver { includeConfig 'conf/test_colabfold_webserver.config' } + test_colabfold_download { includeConfig 'conf/test_colabfold_download.config' } test_esmfold { includeConfig 'conf/test_esmfold.config' } test_full { includeConfig 'conf/test_full.config' } test_full_alphafold2_standard { includeConfig 'conf/test_full.config' } @@ -313,7 +326,7 @@ manifest { description = """Protein 3D structure prediction pipeline""" mainScript = 'main.nf' nextflowVersion = '!>=23.04.0' - version = '1.1.0dev' + version = '1.1.1' doi = '10.5281/zenodo.7629996' } diff --git a/nextflow_schema.json b/nextflow_schema.json index 7080cafe..df0bbfe3 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -29,10 +29,11 @@ "description": "The output directory where the results will be saved. You have to use absolute paths to storage on Cloud infrastructure.", "fa_icon": "fas fa-folder-open" }, - "mode": { + "mode": { "type": "string", "default": "alphafold2", "description": "Specifies the mode in which the pipeline will be run", + "enum": ["alphafold2", "colabfold", "esmfold"], "fa_icon": "fas fa-cogs" }, "use_gpu": { @@ -129,10 +130,10 @@ ], "fa_icon": "fas fa-stream" }, - "num_recycle": { + "num_recycles_colabfold": { "type": "integer", "default": 3, - "description": "Number of recycles", + "description": "Number of recycles for Colabfold", "fa_icon": "fas fa-recycle" }, "use_amber": { @@ -179,7 +180,7 @@ "description": "Specifies the PARAMS path used by 'esmfold' mode", "fa_icon": "fas fa-folder-open" }, - "num_recycles": { + "num_recycles_esmfold": { "type": "integer", "default": 4, "description": "Specifies the number of recycles used by Esmfold", @@ -193,24 +194,16 @@ } } }, - "gadi_config_options": { - "title": "Institutional config options", + "process_skipping_options": { + "title": "Process skipping options", "type": "object", - "fa_icon": "fas fa-university", - "description": "Configurations for running on GADI at NCI.", - "help_text": ".", + "fa_icon": "fas fa-fast-forward", + "description": "Options to skip various steps within the workflow.", "properties": { - "use_dgxa100": { + "skip_multiqc": { "type": "boolean", - "default": false, - "description": "If true uses the DGXA 100 GPU nodes", - "fa_icon": "fas fa-battery-full" - }, - "project": { - "type": "string", - "description": "Thge project code on GADI.", - "default": "", - "fa_icon": "fas fa-users-cog" + "description": "Skip MultiQC.", + "fa_icon": "fas fa-fast-forward" } } }, @@ -323,7 +316,7 @@ }, "mgnify_link": { "type": "string", - "default": "https://storage.googleapis.com/alphafold-databases/casp14_versions/mgy_clusters_2018_12.fa.gz", + "default": "https://storage.googleapis.com/alphafold-databases/v2.3/mgy_clusters_2022_05.fa.gz", "description": "Link to the MGnify database", "fa_icon": "fas fa-link" }, @@ -341,8 +334,8 @@ }, "pdb_obsolete_link": { "type": "string", - "default": "ftp://ftp.wwpdb.org/pub/pdb/data/status/obsolete.dat", - "description": "Link to the PDV obsolete database", + "default": "https://files.wwpdb.org/pub/pdb/data/status/obsolete.dat", + "description": "Link to the PDB obsolete database", "fa_icon": "fas fa-link" }, "uniref30_alphafold2_link": { @@ -353,25 +346,25 @@ }, "uniref90_link": { "type": "string", - "default": "ftp://ftp.uniprot.org/pub/databases/uniprot/uniref/uniref90/uniref90.fasta.gz", + "default": "https://ftp.ebi.ac.uk/pub/databases/uniprot/uniref/uniref90/uniref90.fasta.gz", "description": "Link to the UniRef90 database", "fa_icon": "fas fa-link" }, "pdb_seqres_link": { "type": "string", - "default": "ftp://ftp.wwpdb.org/pub/pdb/derived_data/pdb_seqres.txt", + "default": "https://files.wwpdb.org/pub/pdb/derived_data/pdb_seqres.txt", "description": "Link to the PDB SEQRES database", "fa_icon": "fas fa-link" }, "uniprot_sprot_link": { "type": "string", - "default": "ftp://ftp.ebi.ac.uk/pub/databases/uniprot/current_release/knowledgebase/complete/uniprot_sprot.fasta.gz", + "default": "https://ftp.ebi.ac.uk/pub/databases/uniprot/current_release/knowledgebase/complete/uniprot_sprot.fasta.gz", "description": "Link to the SwissProt UniProt database", "fa_icon": "fas fa-link" }, "uniprot_trembl_link": { "type": "string", - "default": "ftp://ftp.ebi.ac.uk/pub/databases/uniprot/current_release/knowledgebase/complete/uniprot_trembl.fasta.gz", + "default": "https://ftp.ebi.ac.uk/pub/databases/uniprot/current_release/knowledgebase/complete/uniprot_trembl.fasta.gz", "description": "Link to the TrEMBL UniProt database", "fa_icon": "fas fa-link" } @@ -449,7 +442,7 @@ }, "uniref30_colabfold_link": { "type": "string", - "default": "https://wwwuser.gwdg.de/~compbiol/colabfold/uniref30_2202.tar.gz", + "default": "https://wwwuser.gwdg.de/~compbiol/colabfold/uniref30_2302.tar.gz", "description": "Link to the UniRef30 database", "fa_icon": "fas fa-link" }, @@ -643,6 +636,13 @@ "description": "Validation of parameters in lenient more.", "hidden": true, "help_text": "Allows string values that are parseable as numbers or booleans. For further information see [JSONSchema docs](https://github.com/everit-org/json-schema#lenient-mode)." + }, + "pipelines_testdata_base_path": { + "type": "string", + "fa_icon": "far fa-check-circle", + "description": "Base URL or local path to location of pipeline test dataset files", + "default": "https://raw.githubusercontent.com/nf-core/test-datasets/", + "hidden": true } } } @@ -660,6 +660,9 @@ { "$ref": "#/definitions/esmfold_options" }, + { + "$ref": "#/definitions/process_skipping_options" + }, { "$ref": "#/definitions/institutional_config_options" }, diff --git a/workflows/alphafold2.nf b/workflows/alphafold2.nf index 818069a3..3c84dce8 100644 --- a/workflows/alphafold2.nf +++ b/workflows/alphafold2.nf @@ -193,28 +193,31 @@ workflow ALPHAFOLD2 { // // MODULE: MultiQC // - ch_multiqc_report = Channel.empty() - ch_multiqc_config = Channel.fromPath("$projectDir/assets/multiqc_config.yml", checkIfExists: true) - ch_multiqc_custom_config = params.multiqc_config ? Channel.fromPath( params.multiqc_config ) : Channel.empty() - ch_multiqc_logo = params.multiqc_logo ? Channel.fromPath( params.multiqc_logo ) : Channel.empty() - summary_params = paramsSummaryMap(workflow, parameters_schema: "nextflow_schema.json") - ch_workflow_summary = Channel.value(paramsSummaryMultiqc(summary_params)) - ch_multiqc_custom_methods_description = params.multiqc_methods_description ? file(params.multiqc_methods_description, checkIfExists: true) : file("$projectDir/assets/methods_description_template.yml", checkIfExists: true) - ch_methods_description = Channel.value(methodsDescriptionText(ch_multiqc_custom_methods_description)) - - ch_multiqc_files = Channel.empty() - ch_multiqc_files = ch_multiqc_files.mix(ch_workflow_summary.collectFile(name: 'workflow_summary_mqc.yaml')) - ch_multiqc_files = ch_multiqc_files.mix(ch_methods_description.collectFile(name: 'methods_description_mqc.yaml')) - ch_multiqc_files = ch_multiqc_files.mix(ch_collated_versions) - ch_multiqc_files = ch_multiqc_files.mix(ch_multiqc_rep) - - MULTIQC ( - ch_multiqc_files.collect(), - ch_multiqc_config.toList(), - ch_multiqc_custom_config.toList(), - ch_multiqc_logo.toList() - ) - ch_multiqc_report = MULTIQC.out.report.toList() + ch_multiqc_report = Channel.empty() + if (!params.skip_multiqc) { + ch_multiqc_report = Channel.empty() + ch_multiqc_config = Channel.fromPath("$projectDir/assets/multiqc_config.yml", checkIfExists: true) + ch_multiqc_custom_config = params.multiqc_config ? Channel.fromPath( params.multiqc_config ) : Channel.empty() + ch_multiqc_logo = params.multiqc_logo ? Channel.fromPath( params.multiqc_logo ) : Channel.empty() + summary_params = paramsSummaryMap(workflow, parameters_schema: "nextflow_schema.json") + ch_workflow_summary = Channel.value(paramsSummaryMultiqc(summary_params)) + ch_multiqc_custom_methods_description = params.multiqc_methods_description ? file(params.multiqc_methods_description, checkIfExists: true) : file("$projectDir/assets/methods_description_template.yml", checkIfExists: true) + ch_methods_description = Channel.value(methodsDescriptionText(ch_multiqc_custom_methods_description)) + + ch_multiqc_files = Channel.empty() + ch_multiqc_files = ch_multiqc_files.mix(ch_workflow_summary.collectFile(name: 'workflow_summary_mqc.yaml')) + ch_multiqc_files = ch_multiqc_files.mix(ch_methods_description.collectFile(name: 'methods_description_mqc.yaml')) + ch_multiqc_files = ch_multiqc_files.mix(ch_collated_versions) + ch_multiqc_files = ch_multiqc_files.mix(ch_multiqc_rep) + + MULTIQC ( + ch_multiqc_files.collect(), + ch_multiqc_config.toList(), + ch_multiqc_custom_config.toList(), + ch_multiqc_logo.toList() + ) + ch_multiqc_report = MULTIQC.out.report.toList() + } emit: multiqc_report = ch_multiqc_report // channel: /path/to/multiqc_report.html diff --git a/workflows/colabfold.nf b/workflows/colabfold.nf index ecc37af4..be86d681 100644 --- a/workflows/colabfold.nf +++ b/workflows/colabfold.nf @@ -45,7 +45,7 @@ workflow COLABFOLD { ch_colabfold_params // channel: path(colabfold_params) ch_colabfold_db // channel: path(colabfold_db) ch_uniref30 // channel: path(uniref30) - num_recycle // int: Number of recycles for esmfold + num_recycles // int: Number of recycles for esmfold main: ch_multiqc_files = Channel.empty() @@ -72,7 +72,7 @@ workflow COLABFOLD { ch_colabfold_params, [], [], - num_recycle + num_recycles ) ch_versions = ch_versions.mix(COLABFOLD_BATCH.out.versions) } else { @@ -82,7 +82,7 @@ workflow COLABFOLD { ch_colabfold_params, [], [], - num_recycle + num_recycles ) ch_versions = ch_versions.mix(COLABFOLD_BATCH.out.versions) } @@ -122,7 +122,7 @@ workflow COLABFOLD { ch_colabfold_params, ch_colabfold_db, ch_uniref30, - num_recycle + num_recycles ) ch_versions = ch_versions.mix(COLABFOLD_BATCH.out.versions) } @@ -137,28 +137,31 @@ workflow COLABFOLD { // // MODULE: MultiQC // - ch_multiqc_report = Channel.empty() - ch_multiqc_config = Channel.fromPath("$projectDir/assets/multiqc_config.yml", checkIfExists: true) - ch_multiqc_custom_config = params.multiqc_config ? Channel.fromPath( params.multiqc_config ) : Channel.empty() - ch_multiqc_logo = params.multiqc_logo ? Channel.fromPath( params.multiqc_logo ) : Channel.empty() - summary_params = paramsSummaryMap(workflow, parameters_schema: "nextflow_schema.json") - ch_workflow_summary = Channel.value(paramsSummaryMultiqc(summary_params)) - ch_multiqc_custom_methods_description = params.multiqc_methods_description ? file(params.multiqc_methods_description, checkIfExists: true) : file("$projectDir/assets/methods_description_template.yml", checkIfExists: true) - ch_methods_description = Channel.value(methodsDescriptionText(ch_multiqc_custom_methods_description)) - - ch_multiqc_files = Channel.empty() - ch_multiqc_files = ch_multiqc_files.mix(ch_workflow_summary.collectFile(name: 'workflow_summary_mqc.yaml')) - ch_multiqc_files = ch_multiqc_files.mix(ch_methods_description.collectFile(name: 'methods_description_mqc.yaml')) - ch_multiqc_files = ch_multiqc_files.mix(ch_collated_versions) - ch_multiqc_files = ch_multiqc_files.mix(COLABFOLD_BATCH.out.multiqc.collect()) - - MULTIQC ( - ch_multiqc_files.collect(), - ch_multiqc_config.toList(), - ch_multiqc_custom_config.toList(), - ch_multiqc_logo.toList() - ) - ch_multiqc_report = MULTIQC.out.report.toList() + ch_multiqc_report = Channel.empty() + if (!params.skip_multiqc) { + ch_multiqc_report = Channel.empty() + ch_multiqc_config = Channel.fromPath("$projectDir/assets/multiqc_config.yml", checkIfExists: true) + ch_multiqc_custom_config = params.multiqc_config ? Channel.fromPath( params.multiqc_config ) : Channel.empty() + ch_multiqc_logo = params.multiqc_logo ? Channel.fromPath( params.multiqc_logo ) : Channel.empty() + summary_params = paramsSummaryMap(workflow, parameters_schema: "nextflow_schema.json") + ch_workflow_summary = Channel.value(paramsSummaryMultiqc(summary_params)) + ch_multiqc_custom_methods_description = params.multiqc_methods_description ? file(params.multiqc_methods_description, checkIfExists: true) : file("$projectDir/assets/methods_description_template.yml", checkIfExists: true) + ch_methods_description = Channel.value(methodsDescriptionText(ch_multiqc_custom_methods_description)) + + ch_multiqc_files = Channel.empty() + ch_multiqc_files = ch_multiqc_files.mix(ch_workflow_summary.collectFile(name: 'workflow_summary_mqc.yaml')) + ch_multiqc_files = ch_multiqc_files.mix(ch_methods_description.collectFile(name: 'methods_description_mqc.yaml')) + ch_multiqc_files = ch_multiqc_files.mix(ch_collated_versions) + ch_multiqc_files = ch_multiqc_files.mix(COLABFOLD_BATCH.out.multiqc.collect()) + + MULTIQC ( + ch_multiqc_files.collect(), + ch_multiqc_config.toList(), + ch_multiqc_custom_config.toList(), + ch_multiqc_logo.toList() + ) + ch_multiqc_report = MULTIQC.out.report.toList() + } emit: multiqc_report = ch_multiqc_report // channel: /path/to/multiqc_report.html diff --git a/workflows/esmfold.nf b/workflows/esmfold.nf index 9e6073a5..68e5db77 100644 --- a/workflows/esmfold.nf +++ b/workflows/esmfold.nf @@ -42,7 +42,7 @@ workflow ESMFOLD { take: ch_versions // channel: [ path(versions.yml) ] ch_esmfold_params // directory: /path/to/esmfold/params/ - ch_num_recycle // int: Number of recycles for esmfold + ch_num_recycles // int: Number of recycles for esmfold main: ch_multiqc_files = Channel.empty() @@ -66,7 +66,7 @@ workflow ESMFOLD { RUN_ESMFOLD( MULTIFASTA_TO_SINGLEFASTA.out.input_fasta, ch_esmfold_params, - ch_num_recycle + ch_num_recycles ) ch_versions = ch_versions.mix(RUN_ESMFOLD.out.versions) } else { @@ -74,7 +74,7 @@ workflow ESMFOLD { RUN_ESMFOLD( ch_fasta, ch_esmfold_params, - ch_num_recycle + ch_num_recycles ) ch_versions = ch_versions.mix(RUN_ESMFOLD.out.versions) } @@ -104,28 +104,31 @@ workflow ESMFOLD { // // MODULE: MultiQC // - ch_multiqc_report = Channel.empty() - ch_multiqc_config = Channel.fromPath("$projectDir/assets/multiqc_config.yml", checkIfExists: true) - ch_multiqc_custom_config = params.multiqc_config ? Channel.fromPath( params.multiqc_config ) : Channel.empty() - ch_multiqc_logo = params.multiqc_logo ? Channel.fromPath( params.multiqc_logo ) : Channel.empty() - summary_params = paramsSummaryMap(workflow, parameters_schema: "nextflow_schema.json") - ch_workflow_summary = Channel.value(paramsSummaryMultiqc(summary_params)) - ch_multiqc_methods_description = params.multiqc_methods_description ? file(params.multiqc_methods_description, checkIfExists: true) : file("$projectDir/assets/methods_description_template.yml", checkIfExists: true) - ch_methods_description = Channel.value(methodsDescriptionText(ch_multiqc_methods_description)) - - ch_multiqc_files = Channel.empty() - ch_multiqc_files = ch_multiqc_files.mix(ch_workflow_summary.collectFile(name: 'workflow_summary_mqc.yaml')) - ch_multiqc_files = ch_multiqc_files.mix(ch_methods_description.collectFile(name: 'methods_description_mqc.yaml')) - ch_multiqc_files = ch_multiqc_files.mix(ch_collated_versions) - ch_multiqc_files = ch_multiqc_files.mix(RUN_ESMFOLD.out.multiqc.map{it[1]}.collect()) - - MULTIQC ( - ch_multiqc_files.collect(), - ch_multiqc_config.toList(), - ch_multiqc_custom_config.toList(), - ch_multiqc_logo.toList() - ) - ch_multiqc_report = MULTIQC.out.report.toList() + ch_multiqc_report = Channel.empty() + if (!params.skip_multiqc) { + ch_multiqc_report = Channel.empty() + ch_multiqc_config = Channel.fromPath("$projectDir/assets/multiqc_config.yml", checkIfExists: true) + ch_multiqc_custom_config = params.multiqc_config ? Channel.fromPath( params.multiqc_config ) : Channel.empty() + ch_multiqc_logo = params.multiqc_logo ? Channel.fromPath( params.multiqc_logo ) : Channel.empty() + summary_params = paramsSummaryMap(workflow, parameters_schema: "nextflow_schema.json") + ch_workflow_summary = Channel.value(paramsSummaryMultiqc(summary_params)) + ch_multiqc_methods_description = params.multiqc_methods_description ? file(params.multiqc_methods_description, checkIfExists: true) : file("$projectDir/assets/methods_description_template.yml", checkIfExists: true) + ch_methods_description = Channel.value(methodsDescriptionText(ch_multiqc_methods_description)) + + ch_multiqc_files = Channel.empty() + ch_multiqc_files = ch_multiqc_files.mix(ch_workflow_summary.collectFile(name: 'workflow_summary_mqc.yaml')) + ch_multiqc_files = ch_multiqc_files.mix(ch_methods_description.collectFile(name: 'methods_description_mqc.yaml')) + ch_multiqc_files = ch_multiqc_files.mix(ch_collated_versions) + ch_multiqc_files = ch_multiqc_files.mix(RUN_ESMFOLD.out.multiqc.collect()) + + MULTIQC ( + ch_multiqc_files.collect(), + ch_multiqc_config.toList(), + ch_multiqc_custom_config.toList(), + ch_multiqc_logo.toList() + ) + ch_multiqc_report = MULTIQC.out.report.toList() + } emit: multiqc_report = ch_multiqc_report // channel: /path/to/multiqc_report.html From 34d6d78c4ae63a88ae13eaf1609e968180e00f7c Mon Sep 17 00:00:00 2001 From: Athanasios Baltzis Date: Wed, 15 Feb 2023 11:20:01 +0100 Subject: [PATCH 016/227] Bump to version 1.1.0dev --- nextflow.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nextflow.config b/nextflow.config index ec71afb5..56b3c01e 100644 --- a/nextflow.config +++ b/nextflow.config @@ -244,7 +244,7 @@ manifest { description = """Protein 3D structure prediction pipeline""" mainScript = 'main.nf' nextflowVersion = '!>=22.10.1' - version = '1.0.0' + version = '1.1.0dev' doi = '10.5281/zenodo.7629996' } From 9d7a81d008d5afb571a1e1f52f02bb3e8fa738ea Mon Sep 17 00:00:00 2001 From: Athanasios Baltzis Date: Wed, 15 Feb 2023 11:29:41 +0100 Subject: [PATCH 017/227] Update CHANGELOG --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b5c164e..49907a78 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,3 +10,7 @@ Initial release of nf-core/proteinfold, created with the [nf-core](https://nf-co ### Enhancements & fixes - Updated pipeline template to [nf-core/tools 2.7.2](https://github.com/nf-core/tools/releases/tag/2.7.2) + +## 1.1.0 + +### Enhancements & fixes From 7792fa4faa35c272738f34569ee64d400395c10f Mon Sep 17 00:00:00 2001 From: Rob Syme Date: Mon, 27 Feb 2023 22:16:58 -0500 Subject: [PATCH 018/227] Add accelerator directive to GPU processes --- conf/modules_alphafold2.config | 2 ++ 1 file changed, 2 insertions(+) diff --git a/conf/modules_alphafold2.config b/conf/modules_alphafold2.config index 25190dbf..3bee4018 100644 --- a/conf/modules_alphafold2.config +++ b/conf/modules_alphafold2.config @@ -27,6 +27,7 @@ process { if (params.alphafold2_mode == 'standard') { process { withName: 'RUN_ALPHAFOLD2' { + accelerator = params.use_gpu ? 1 : 0 ext.args = [ params.use_gpu ? '--use_gpu_relax=true' : '--use_gpu_relax=false', params.max_template_date ? "--max_template_date ${params.max_template_date}" : '' @@ -53,6 +54,7 @@ if (params.alphafold2_mode == 'split_msa_prediction') { } withName: 'RUN_ALPHAFOLD2_PRED' { + accelerator = params.use_gpu ? 1 : 0 ext.args = params.use_gpu ? '--use_gpu_relax=true' : '--use_gpu_relax=false' publishDir = [ path: { "${params.outdir}/${params.mode}/${params.alphafold2_mode}" }, From 86b7252ceea120a5ca728da005450d18407821e5 Mon Sep 17 00:00:00 2001 From: Rob Syme Date: Mon, 27 Feb 2023 22:19:21 -0500 Subject: [PATCH 019/227] Add gpu to COLABFOLD_BATCH --- conf/modules_colabfold.config | 1 + 1 file changed, 1 insertion(+) diff --git a/conf/modules_colabfold.config b/conf/modules_colabfold.config index 83015351..a2137847 100644 --- a/conf/modules_colabfold.config +++ b/conf/modules_colabfold.config @@ -49,6 +49,7 @@ if (params.colabfold_server == 'local') { ] } withName: 'COLABFOLD_BATCH' { + accelerator = params.use_gpu ? 1 : 0 ext.args = [ params.use_gpu ? '' : '--cpu', params.use_amber ? '--amber' : '', From a1b8827fe3e7f0aa105efbd9eaa87ccbbbc4a2f7 Mon Sep 17 00:00:00 2001 From: Rob Syme Date: Wed, 1 Mar 2023 09:20:11 -0500 Subject: [PATCH 020/227] Accelerator null instead of zero --- conf/modules_colabfold.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/modules_colabfold.config b/conf/modules_colabfold.config index a2137847..faaa229d 100644 --- a/conf/modules_colabfold.config +++ b/conf/modules_colabfold.config @@ -49,7 +49,7 @@ if (params.colabfold_server == 'local') { ] } withName: 'COLABFOLD_BATCH' { - accelerator = params.use_gpu ? 1 : 0 + accelerator = params.use_gpu ? 1 : null ext.args = [ params.use_gpu ? '' : '--cpu', params.use_amber ? '--amber' : '', From e5c629b49d35dab5d513290628658d2322851757 Mon Sep 17 00:00:00 2001 From: Rob Syme Date: Wed, 1 Mar 2023 09:34:21 -0500 Subject: [PATCH 021/227] If statement for accelerator --- conf/modules_alphafold2.config | 4 ++-- conf/modules_colabfold.config | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/conf/modules_alphafold2.config b/conf/modules_alphafold2.config index 3bee4018..9a266160 100644 --- a/conf/modules_alphafold2.config +++ b/conf/modules_alphafold2.config @@ -27,7 +27,7 @@ process { if (params.alphafold2_mode == 'standard') { process { withName: 'RUN_ALPHAFOLD2' { - accelerator = params.use_gpu ? 1 : 0 + if(params.use_gpu) { accelerator = 1 } ext.args = [ params.use_gpu ? '--use_gpu_relax=true' : '--use_gpu_relax=false', params.max_template_date ? "--max_template_date ${params.max_template_date}" : '' @@ -54,7 +54,7 @@ if (params.alphafold2_mode == 'split_msa_prediction') { } withName: 'RUN_ALPHAFOLD2_PRED' { - accelerator = params.use_gpu ? 1 : 0 + if(params.use_gpu) { accelerator = 1 } ext.args = params.use_gpu ? '--use_gpu_relax=true' : '--use_gpu_relax=false' publishDir = [ path: { "${params.outdir}/${params.mode}/${params.alphafold2_mode}" }, diff --git a/conf/modules_colabfold.config b/conf/modules_colabfold.config index faaa229d..2e2773eb 100644 --- a/conf/modules_colabfold.config +++ b/conf/modules_colabfold.config @@ -49,7 +49,7 @@ if (params.colabfold_server == 'local') { ] } withName: 'COLABFOLD_BATCH' { - accelerator = params.use_gpu ? 1 : null + if(params.use_gpu) { accelerator = 1 } ext.args = [ params.use_gpu ? '' : '--cpu', params.use_amber ? '--amber' : '', From 479d97e6723408233ba89fd3e1638ca1d11cd21c Mon Sep 17 00:00:00 2001 From: Rob Syme Date: Thu, 2 Mar 2023 09:22:27 -0500 Subject: [PATCH 022/227] Changelog fix --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 49907a78..4afe30e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,3 +14,5 @@ Initial release of nf-core/proteinfold, created with the [nf-core](https://nf-co ## 1.1.0 ### Enhancements & fixes + +Add `accelerator` directive to GPU processes when `params.use_gpu` is true. From 415d32f31798ef7275799b4bef76b8f6e7874f1e Mon Sep 17 00:00:00 2001 From: Athanasios Baltzis Date: Tue, 14 Mar 2023 20:34:38 +0100 Subject: [PATCH 023/227] Deal with multiline fasta for colabfold multimers --- modules/local/multifasta_to_csv.nf | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/local/multifasta_to_csv.nf b/modules/local/multifasta_to_csv.nf index dfd67457..ee54f872 100644 --- a/modules/local/multifasta_to_csv.nf +++ b/modules/local/multifasta_to_csv.nf @@ -18,7 +18,8 @@ process MULTIFASTA_TO_CSV { script: """ - echo -e id,sequence'\\n'${meta.id},`awk '!/^>/ {print \$0}' ${fasta} | tr '\\n' ':' | sed 's/:\$//'` > input.csv + awk '/^>/ {printf("\\n%s\\n",\$0);next; } { printf("%s",\$0);} END {printf("\\n");}' ${fasta} > single_line.fasta + echo -e id,sequence'\\n'${meta.id},`awk '!/^>/ {print \$0}' single_line.fasta | tr '\\n' ':' | sed 's/:\$//' | sed 's/^://'` > input.csv cat <<-END_VERSIONS > versions.yml "${task.process}": From 5c93f6b28c9198e846de336671a6d07623c5bec7 Mon Sep 17 00:00:00 2001 From: Athanasios Baltzis Date: Tue, 14 Mar 2023 20:38:06 +0100 Subject: [PATCH 024/227] Update CHANGELOG --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4afe30e5..7cef8643 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,4 +15,6 @@ Initial release of nf-core/proteinfold, created with the [nf-core](https://nf-co ### Enhancements & fixes -Add `accelerator` directive to GPU processes when `params.use_gpu` is true. +- Add `accelerator` directive to GPU processes when `params.use_gpu` is true. + +- Support multiline fasta for colabfold multimer predictions From 5ac8eee4f16a9fad1b213bde195dd23e08ba2783 Mon Sep 17 00:00:00 2001 From: Athanasios Baltzis Date: Tue, 2 May 2023 08:57:38 +0200 Subject: [PATCH 025/227] Reduce pdb_mmcif excessive symlinking --- conf/dbs.config | 2 +- subworkflows/local/prepare_alphafold2_dbs.nf | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/conf/dbs.config b/conf/dbs.config index e186f9c0..3f116951 100644 --- a/conf/dbs.config +++ b/conf/dbs.config @@ -28,7 +28,7 @@ params { alphafold2_params_path = "${params.alphafold2_db}/alphafold_params_*/*" mgnify_path = "${params.alphafold2_db}/mgnify/*" pdb70_path = "${params.alphafold2_db}/pdb70/**" - pdb_mmcif_path = "${params.alphafold2_db}/pdb_mmcif/**" + pdb_mmcif_path = "${params.alphafold2_db}/pdb_mmcif/*" uniclust30_path = "${params.alphafold2_db}/uniclust30/**" uniref90_path = "${params.alphafold2_db}/uniref90/*" pdb_seqres_path = "${params.alphafold2_db}/pdb_seqres/*" diff --git a/subworkflows/local/prepare_alphafold2_dbs.nf b/subworkflows/local/prepare_alphafold2_dbs.nf index 20adc42e..f4653019 100644 --- a/subworkflows/local/prepare_alphafold2_dbs.nf +++ b/subworkflows/local/prepare_alphafold2_dbs.nf @@ -36,9 +36,11 @@ workflow PREPARE_ALPHAFOLD2_DBS { ch_params = file( params.alphafold2_params_path ) ch_mgnify = file( params.mgnify_path ) - ch_pdb70 = file( params.pdb70_path, type: 'any' ) - ch_mmcif = file( params.pdb_mmcif_path, type: 'any' ) - ch_uniclust30 = file( params.uniclust30_path, type: 'any' ) + ch_pdb70 = file( params.pdb70_path, type: 'dir' ) + ch_mmcif_files = file( params.pdb_mmcif_path, type: 'dir' ) + ch_mmcif_obsolete = file( params.pdb_mmcif_path, type: 'file' ) + ch_mmcif = ch_mmcif_files + ch_mmcif_obsolete + ch_uniclust30 = file( params.uniclust30_path, type: 'dir' ) ch_uniref90 = file( params.uniref90_path ) ch_pdb_seqres = file( params.pdb_seqres_path ) ch_uniprot = file( params.uniprot_path ) From afbb5e1da681aa75278d6b3065ab6e14ed713938 Mon Sep 17 00:00:00 2001 From: Athanasios Baltzis Date: Tue, 2 May 2023 09:11:11 +0200 Subject: [PATCH 026/227] Update documentation --- docs/usage.md | 291 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 291 insertions(+) diff --git a/docs/usage.md b/docs/usage.md index c04ff2c0..26c2ccbd 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -63,6 +63,161 @@ nextflow run nf-core/proteinfold \ --use_gpu \ -profile ``` +If you specify the `--alphafold2_db ` parameter, the directory structure of your path should be like this: +``` +├── mgnify +│   └── mgy_clusters_2018_12.fa +├── alphafold_params_2022-03-02 +│   ├── LICENSE +│   ├── params_model_1_multimer.npz +│   ├── params_model_1_multimer_v2.npz +│   ├── params_model_1.npz +│   ├── params_model_1_ptm.npz +│   ├── params_model_2_multimer.npz +│   ├── params_model_2_multimer_v2.npz +│   ├── params_model_2.npz +│   ├── params_model_2_ptm.npz +│   ├── params_model_3_multimer.npz +│   ├── params_model_3_multimer_v2.npz +│   ├── params_model_3.npz +│   ├── params_model_3_ptm.npz +│   ├── params_model_4_multimer.npz +│   ├── params_model_4_multimer_v2.npz +│   ├── params_model_4.npz +│   ├── params_model_4_ptm.npz +│   ├── params_model_5_multimer.npz +│   ├── params_model_5_multimer_v2.npz +│   ├── params_model_5.npz +│   └── params_model_5_ptm.npz +├── pdb70 +│   └── pdb70_from_mmcif_200916 +│   ├── md5sum +│   ├── pdb70_a3m.ffdata +│   ├── pdb70_a3m.ffindex +│   ├── pdb70_clu.tsv +│   ├── pdb70_cs219.ffdata +│   ├── pdb70_cs219.ffindex +│   ├── pdb70_hhm.ffdata +│   ├── pdb70_hhm.ffindex +│   └── pdb_filter.dat +├── pdb_mmcif +│   ├── mmcif_files +│   │   ├── 1g6g.cif +│   │   ├── 1go4.cif +│   │   ├── 1isn.cif +│   │   ├── 1kuu.cif +│   │   ├── 1m7s.cif +│   │   ├── 1mwq.cif +│   │   ├── 1ni5.cif +│   │   ├── 1qgd.cif +│   │   ├── 1tp9.cif +│   │   ├── 1wa9.cif +│   │   ├── 1ye5.cif +│   │   ├── 1yhl.cif +│   │   ├── 2bjd.cif +│   │   ├── 2bo9.cif +│   │   ├── 2e7t.cif +│   │   ├── 2fyg.cif +│   │   ├── 2j0q.cif +│   │   ├── 2jcq.cif +│   │   ├── 2m4k.cif +│   │   ├── 2n9o.cif +│   │   ├── 2nsx.cif +│   │   ├── 2w4u.cif +│   │   ├── 2wd6.cif +│   │   ├── 2wh5.cif +│   │   ├── 2wji.cif +│   │   ├── 2yu3.cif +│   │   ├── 3cw2.cif +│   │   ├── 3d45.cif +│   │   ├── 3gnz.cif +│   │   ├── 3j0a.cif +│   │   ├── 3jaj.cif +│   │   ├── 3mzo.cif +│   │   ├── 3nrn.cif +│   │   ├── 3piv.cif +│   │   ├── 3pof.cif +│   │   ├── 3pvd.cif +│   │   ├── 3q45.cif +│   │   ├── 3qh6.cif +│   │   ├── 3rg2.cif +│   │   ├── 3sxe.cif +│   │   ├── 3uai.cif +│   │   ├── 3uid.cif +│   │   ├── 3wae.cif +│   │   ├── 3wt1.cif +│   │   ├── 3wtr.cif +│   │   ├── 3wy2.cif +│   │   ├── 3zud.cif +│   │   ├── 4bix.cif +│   │   ├── 4bzx.cif +│   │   ├── 4c1n.cif +│   │   ├── 4cej.cif +│   │   ├── 4chm.cif +│   │   ├── 4fzo.cif +│   │   ├── 4i1f.cif +│   │   ├── 4ioa.cif +│   │   ├── 4j6o.cif +│   │   ├── 4m9q.cif +│   │   ├── 4mal.cif +│   │   ├── 4nhe.cif +│   │   ├── 4o2w.cif +│   │   ├── 4pzo.cif +│   │   ├── 4qlx.cif +│   │   ├── 4uex.cif +│   │   ├── 4zm4.cif +│   │   ├── 4zv1.cif +│   │   ├── 5aj4.cif +│   │   ├── 5frs.cif +│   │   ├── 5hwo.cif +│   │   ├── 5kbk.cif +│   │   ├── 5odq.cif +│   │   ├── 5u5t.cif +│   │   ├── 5wzq.cif +│   │   ├── 5x9z.cif +│   │   ├── 5xe5.cif +│   │   ├── 5ynv.cif +│   │   ├── 5yud.cif +│   │   ├── 5z5c.cif +│   │   ├── 5zb3.cif +│   │   ├── 5zlg.cif +│   │   ├── 6a6i.cif +│   │   ├── 6az3.cif +│   │   ├── 6ban.cif +│   │   ├── 6g1f.cif +│   │   ├── 6ix4.cif +│   │   ├── 6jwp.cif +│   │   ├── 6ng9.cif +│   │   ├── 6ojj.cif +│   │   ├── 6s0x.cif +│   │   ├── 6sg9.cif +│   │   ├── 6vi4.cif +│   │   └── 7sp5.cif +│   └── obsolete.dat +├── pdb_seqres +│   └── pdb_seqres.txt +├── small_bfd +│   └── bfd-first_non_consensus_sequences.fasta +├── uniclust30 +│   └── uniclust30_2018_08 +│   ├── uniclust30_2018_08_a3m_db -> uniclust30_2018_08_a3m.ffdata +│   ├── uniclust30_2018_08_a3m_db.index +│   ├── uniclust30_2018_08_a3m.ffdata +│   ├── uniclust30_2018_08_a3m.ffindex +│   ├── uniclust30_2018_08.cs219 +│   ├── uniclust30_2018_08_cs219.ffdata +│   ├── uniclust30_2018_08_cs219.ffindex +│   ├── uniclust30_2018_08.cs219.sizes +│   ├── uniclust30_2018_08_hhm_db -> uniclust30_2018_08_hhm.ffdata +│   ├── uniclust30_2018_08_hhm_db.index +│   ├── uniclust30_2018_08_hhm.ffdata +│   ├── uniclust30_2018_08_hhm.ffindex +│   └── uniclust30_2018_08_md5sum +├── uniprot +│   └── uniprot.fasta +└── uniref90 + └── uniref90.fasta +``` ```console nextflow run nf-core/proteinfold \ @@ -94,8 +249,144 @@ nextflow run nf-core/proteinfold \ -profile ``` +If you specify the `--colabfold_db ` parameter, the directory structure of your path should be like this: +``` +├── colabfold_envdb_202108 +│   ├── colabfold_envdb_202108_db.0 +│   ├── colabfold_envdb_202108_db.1 +│   ├── colabfold_envdb_202108_db.10 +│   ├── colabfold_envdb_202108_db.11 +│   ├── colabfold_envdb_202108_db.12 +│   ├── colabfold_envdb_202108_db.13 +│   ├── colabfold_envdb_202108_db.14 +│   ├── colabfold_envdb_202108_db.15 +│   ├── colabfold_envdb_202108_db.2 +│   ├── colabfold_envdb_202108_db.3 +│   ├── colabfold_envdb_202108_db.4 +│   ├── colabfold_envdb_202108_db.5 +│   ├── colabfold_envdb_202108_db.6 +│   ├── colabfold_envdb_202108_db.7 +│   ├── colabfold_envdb_202108_db.8 +│   ├── colabfold_envdb_202108_db.9 +│   ├── colabfold_envdb_202108_db_aln.0 +│   ├── colabfold_envdb_202108_db_aln.1 +│   ├── colabfold_envdb_202108_db_aln.10 +│   ├── colabfold_envdb_202108_db_aln.11 +│   ├── colabfold_envdb_202108_db_aln.12 +│   ├── colabfold_envdb_202108_db_aln.13 +│   ├── colabfold_envdb_202108_db_aln.14 +│   ├── colabfold_envdb_202108_db_aln.15 +│   ├── colabfold_envdb_202108_db_aln.2 +│   ├── colabfold_envdb_202108_db_aln.3 +│   ├── colabfold_envdb_202108_db_aln.4 +│   ├── colabfold_envdb_202108_db_aln.5 +│   ├── colabfold_envdb_202108_db_aln.6 +│   ├── colabfold_envdb_202108_db_aln.7 +│   ├── colabfold_envdb_202108_db_aln.8 +│   ├── colabfold_envdb_202108_db_aln.9 +│   ├── colabfold_envdb_202108_db_aln.dbtype +│   ├── colabfold_envdb_202108_db_aln.index +│   ├── colabfold_envdb_202108_db.dbtype +│   ├── colabfold_envdb_202108_db_h +│   ├── colabfold_envdb_202108_db_h.dbtype +│   ├── colabfold_envdb_202108_db_h.index +│   ├── colabfold_envdb_202108_db.idx +│   ├── colabfold_envdb_202108_db.idx.dbtype +│   ├── colabfold_envdb_202108_db.idx.index +│   ├── colabfold_envdb_202108_db.index +│   ├── colabfold_envdb_202108_db_seq.0 +│   ├── colabfold_envdb_202108_db_seq.1 +│   ├── colabfold_envdb_202108_db_seq.10 +│   ├── colabfold_envdb_202108_db_seq.11 +│   ├── colabfold_envdb_202108_db_seq.12 +│   ├── colabfold_envdb_202108_db_seq.13 +│   ├── colabfold_envdb_202108_db_seq.14 +│   ├── colabfold_envdb_202108_db_seq.15 +│   ├── colabfold_envdb_202108_db_seq.2 +│   ├── colabfold_envdb_202108_db_seq.3 +│   ├── colabfold_envdb_202108_db_seq.4 +│   ├── colabfold_envdb_202108_db_seq.5 +│   ├── colabfold_envdb_202108_db_seq.6 +│   ├── colabfold_envdb_202108_db_seq.7 +│   ├── colabfold_envdb_202108_db_seq.8 +│   ├── colabfold_envdb_202108_db_seq.9 +│   ├── colabfold_envdb_202108_db_seq.dbtype +│   ├── colabfold_envdb_202108_db_seq_h -> colabfold_envdb_202108_db_h +│   ├── colabfold_envdb_202108_db_seq_h.dbtype -> colabfold_envdb_202108_db_h.dbtype +│   ├── colabfold_envdb_202108_db_seq_h.index -> colabfold_envdb_202108_db_h.index +│   ├── colabfold_envdb_202108_db_seq.index +├── params +│   ├── alphafold_params_2021-07-14 +│   │   ├── LICENSE +│   │   ├── params_model_1.npz +│   │   ├── params_model_1_ptm.npz +│   │   ├── params_model_2.npz +│   │   ├── params_model_2_ptm.npz +│   │   ├── params_model_3.npz +│   │   ├── params_model_3_ptm.npz +│   │   ├── params_model_4.npz +│   │   ├── params_model_4_ptm.npz +│   │   ├── params_model_5.npz +│   │   └── params_model_5_ptm.npz +│   └── alphafold_params_colab_2022-03-02 +│   ├── LICENSE +│   ├── params_model_1_multimer_v2.npz +│   ├── params_model_1.npz +│   ├── params_model_2_multimer_v2.npz +│   ├── params_model_2.npz +│   ├── params_model_2_ptm.npz +│   ├── params_model_3_multimer_v2.npz +│   ├── params_model_3.npz +│   ├── params_model_4_multimer_v2.npz +│   ├── params_model_4.npz +│   ├── params_model_5_multimer_v2.npz +│   └── params_model_5.npz +└── uniref30_2202 + ├── uniref30_2202_db.0 + ├── uniref30_2202_db.1 + ├── uniref30_2202_db.2 + ├── uniref30_2202_db.3 + ├── uniref30_2202_db.4 + ├── uniref30_2202_db.5 + ├── uniref30_2202_db.6 + ├── uniref30_2202_db.7 + ├── uniref30_2202_db_aln.0 + ├── uniref30_2202_db_aln.1 + ├── uniref30_2202_db_aln.2 + ├── uniref30_2202_db_aln.3 + ├── uniref30_2202_db_aln.4 + ├── uniref30_2202_db_aln.5 + ├── uniref30_2202_db_aln.6 + ├── uniref30_2202_db_aln.7 + ├── uniref30_2202_db_aln.dbtype + ├── uniref30_2202_db_aln.index + ├── uniref30_2202_db.dbtype + ├── uniref30_2202_db_h + ├── uniref30_2202_db_h.dbtype + ├── uniref30_2202_db_h.index + ├── uniref30_2202_db.idx + ├── uniref30_2202_db.idx.dbtype + ├── uniref30_2202_db.idx.index + ├── uniref30_2202_db.index + ├── uniref30_2202_db_seq.0 + ├── uniref30_2202_db_seq.1 + ├── uniref30_2202_db_seq.2 + ├── uniref30_2202_db_seq.3 + ├── uniref30_2202_db_seq.4 + ├── uniref30_2202_db_seq.5 + ├── uniref30_2202_db_seq.6 + ├── uniref30_2202_db_seq.7 + ├── uniref30_2202_db_seq.dbtype + ├── uniref30_2202_db_seq_h -> uniref30_2202_db_h + ├── uniref30_2202_db_seq_h.dbtype -> uniref30_2202_db_h.dbtype + ├── uniref30_2202_db_seq_h.index -> uniref30_2202_db_h.index + └── uniref30_2202_db_seq.index +``` + This will launch the pipeline with the `docker` configuration profile. See below for more information about profiles. + + Note that the pipeline will create the following files in your working directory: ```bash From 0883f7206f3b801b1c5bf1997ea23c2db171626d Mon Sep 17 00:00:00 2001 From: Rike Date: Wed, 15 Mar 2023 16:25:19 +0100 Subject: [PATCH 027/227] update paths --- conf/test_full.config | 2 +- conf/test_full_alphafold_multimer.config | 2 +- conf/test_full_alphafold_split.config | 2 +- conf/test_full_colabfold_local.config | 2 +- conf/test_full_colabfold_webserver.config | 2 +- conf/test_full_colabfold_webserver_multimer.config | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/conf/test_full.config b/conf/test_full.config index 7b301f90..dc8ff985 100644 --- a/conf/test_full.config +++ b/conf/test_full.config @@ -18,5 +18,5 @@ params { mode = 'alphafold2' alphafold2_mode = 'standard' input = 'https://raw.githubusercontent.com/nf-core/test-datasets/proteinfold/testdata/samplesheet/v1.0/samplesheet.csv' - alphafold2_db = 's3://nf-core-awsmegatests/proteinfold/input_data/db/alphafold_mini' + alphafold2_db = 's3://ngi-igenomes/test-data/proteinfold/db/alphafold_mini' } diff --git a/conf/test_full_alphafold_multimer.config b/conf/test_full_alphafold_multimer.config index 7eec75e5..eca98ba4 100644 --- a/conf/test_full_alphafold_multimer.config +++ b/conf/test_full_alphafold_multimer.config @@ -19,5 +19,5 @@ params { alphafold2_mode = 'standard' alphafold2_model_preset = 'multimer' input = 'https://raw.githubusercontent.com/nf-core/test-datasets/proteinfold/testdata/samplesheet/v1.0/samplesheet_multimer.csv' - alphafold2_db = 's3://nf-core-awsmegatests/proteinfold/input_data/db/alphafold_mini' + alphafold2_db = 's3://ngi-igenomes/test-data/proteinfold/db/alphafold_mini' } diff --git a/conf/test_full_alphafold_split.config b/conf/test_full_alphafold_split.config index 9b5b8b4f..b7262d1f 100644 --- a/conf/test_full_alphafold_split.config +++ b/conf/test_full_alphafold_split.config @@ -18,5 +18,5 @@ params { mode = 'alphafold2' alphafold2_mode = 'split_msa_prediction' input = 'https://raw.githubusercontent.com/nf-core/test-datasets/proteinfold/testdata/samplesheet/v1.0/samplesheet.csv' - alphafold2_db = 's3://nf-core-awsmegatests/proteinfold/input_data/db/alphafold_mini' + alphafold2_db = 's3://ngi-igenomes/test-data/proteinfold/db/alphafold_mini' } diff --git a/conf/test_full_colabfold_local.config b/conf/test_full_colabfold_local.config index ffa1ba04..e34ff152 100644 --- a/conf/test_full_colabfold_local.config +++ b/conf/test_full_colabfold_local.config @@ -20,7 +20,7 @@ params { colabfold_server = 'local' colabfold_model_preset = 'AlphaFold2-ptm' input = 'https://raw.githubusercontent.com/nf-core/test-datasets/proteinfold/testdata/samplesheet/v1.0/samplesheet.csv' - colabfold_db = 's3://nf-core-awsmegatests/proteinfold/input_data/db/colabfold_mini' + colabfold_db = 's3://ngi-igenomes/test-data/proteinfold/db/colabfold_mini' } process { withName:MMSEQS_COLABFOLDSEARCH { diff --git a/conf/test_full_colabfold_webserver.config b/conf/test_full_colabfold_webserver.config index 0cdc08ca..b54347de 100644 --- a/conf/test_full_colabfold_webserver.config +++ b/conf/test_full_colabfold_webserver.config @@ -19,5 +19,5 @@ params { colabfold_server = 'webserver' colabfold_model_preset = 'AlphaFold2-ptm' input = 'https://raw.githubusercontent.com/nf-core/test-datasets/proteinfold/testdata/samplesheet/v1.0/samplesheet.csv' - colabfold_db = 's3://nf-core-awsmegatests/proteinfold/input_data/db/colabfold_mini' + colabfold_db = 's3://ngi-igenomes/test-data/proteinfold/db/colabfold_mini' } diff --git a/conf/test_full_colabfold_webserver_multimer.config b/conf/test_full_colabfold_webserver_multimer.config index f8a1f323..8b50a4a1 100644 --- a/conf/test_full_colabfold_webserver_multimer.config +++ b/conf/test_full_colabfold_webserver_multimer.config @@ -19,5 +19,5 @@ params { colabfold_server = 'webserver' colabfold_model_preset = 'AlphaFold2-multimer-v2' input = 'https://raw.githubusercontent.com/nf-core/test-datasets/proteinfold/testdata/samplesheet/v1.0/samplesheet_multimer.csv' - colabfold_db = 's3://nf-core-awsmegatests/proteinfold/input_data/db/colabfold_mini' + colabfold_db = 's3://ngi-igenomes/test-data/proteinfold/db/colabfold_mini' } From 3291b16c23c768563e5f983c56fdd81cee047080 Mon Sep 17 00:00:00 2001 From: Athanasios Baltzis Date: Tue, 2 May 2023 09:22:30 +0200 Subject: [PATCH 028/227] Update modules.json --- modules.json | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/modules.json b/modules.json index d4f7f84b..499933af 100644 --- a/modules.json +++ b/modules.json @@ -8,41 +8,55 @@ "aria2": { "branch": "master", "git_sha": "c8e35eb2055c099720a75538d1b8adb3fb5a464c", - "installed_by": ["modules"] + "installed_by": [ + "modules" + ] }, "custom/dumpsoftwareversions": { "branch": "master", "git_sha": "c8e35eb2055c099720a75538d1b8adb3fb5a464c", - "installed_by": ["modules"] + "installed_by": [ + "modules" + ] }, "gunzip": { "branch": "master", "git_sha": "c8e35eb2055c099720a75538d1b8adb3fb5a464c", - "installed_by": ["modules"] + "installed_by": [ + "modules" + ] }, "mmseqs/createindex": { "branch": "master", "git_sha": "c8e35eb2055c099720a75538d1b8adb3fb5a464c", - "installed_by": ["modules"] + "installed_by": [ + "modules" + ] }, "mmseqs/tsv2exprofiledb": { "branch": "master", "git_sha": "c8e35eb2055c099720a75538d1b8adb3fb5a464c", - "installed_by": ["modules"] + "installed_by": [ + "modules" + ] }, "multiqc": { "branch": "master", "git_sha": "c8e35eb2055c099720a75538d1b8adb3fb5a464c", - "installed_by": ["modules"] + "installed_by": [ + "modules" + ] }, "untar": { "branch": "master", "git_sha": "c8e35eb2055c099720a75538d1b8adb3fb5a464c", - "installed_by": ["modules"], + "installed_by": [ + "modules" + ], "patch": "modules/nf-core/untar/untar.diff" } } } } } -} +} \ No newline at end of file From eb242079bbee04d335ed5b4c94f45f4a42ee0291 Mon Sep 17 00:00:00 2001 From: Athanasios Baltzis Date: Tue, 2 May 2023 09:22:41 +0200 Subject: [PATCH 029/227] Update modules.json --- modules.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules.json b/modules.json index 499933af..9a42796b 100644 --- a/modules.json +++ b/modules.json @@ -59,4 +59,4 @@ } } } -} \ No newline at end of file +} From 584cb3b8427e75bf5d9d6319abd3942cd6d6f77b Mon Sep 17 00:00:00 2001 From: Athanasios Baltzis Date: Tue, 2 May 2023 09:27:34 +0200 Subject: [PATCH 030/227] Make prettier happy --- docs/usage.md | 5 +++-- modules.json | 28 +++++++--------------------- 2 files changed, 10 insertions(+), 23 deletions(-) diff --git a/docs/usage.md b/docs/usage.md index 26c2ccbd..c33aaa36 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -63,7 +63,9 @@ nextflow run nf-core/proteinfold \ --use_gpu \ -profile ``` + If you specify the `--alphafold2_db ` parameter, the directory structure of your path should be like this: + ``` ├── mgnify │   └── mgy_clusters_2018_12.fa @@ -250,6 +252,7 @@ nextflow run nf-core/proteinfold \ ``` If you specify the `--colabfold_db ` parameter, the directory structure of your path should be like this: + ``` ├── colabfold_envdb_202108 │   ├── colabfold_envdb_202108_db.0 @@ -385,8 +388,6 @@ If you specify the `--colabfold_db ` parameter, the directory structure of your This will launch the pipeline with the `docker` configuration profile. See below for more information about profiles. - - Note that the pipeline will create the following files in your working directory: ```bash diff --git a/modules.json b/modules.json index 9a42796b..d4f7f84b 100644 --- a/modules.json +++ b/modules.json @@ -8,51 +8,37 @@ "aria2": { "branch": "master", "git_sha": "c8e35eb2055c099720a75538d1b8adb3fb5a464c", - "installed_by": [ - "modules" - ] + "installed_by": ["modules"] }, "custom/dumpsoftwareversions": { "branch": "master", "git_sha": "c8e35eb2055c099720a75538d1b8adb3fb5a464c", - "installed_by": [ - "modules" - ] + "installed_by": ["modules"] }, "gunzip": { "branch": "master", "git_sha": "c8e35eb2055c099720a75538d1b8adb3fb5a464c", - "installed_by": [ - "modules" - ] + "installed_by": ["modules"] }, "mmseqs/createindex": { "branch": "master", "git_sha": "c8e35eb2055c099720a75538d1b8adb3fb5a464c", - "installed_by": [ - "modules" - ] + "installed_by": ["modules"] }, "mmseqs/tsv2exprofiledb": { "branch": "master", "git_sha": "c8e35eb2055c099720a75538d1b8adb3fb5a464c", - "installed_by": [ - "modules" - ] + "installed_by": ["modules"] }, "multiqc": { "branch": "master", "git_sha": "c8e35eb2055c099720a75538d1b8adb3fb5a464c", - "installed_by": [ - "modules" - ] + "installed_by": ["modules"] }, "untar": { "branch": "master", "git_sha": "c8e35eb2055c099720a75538d1b8adb3fb5a464c", - "installed_by": [ - "modules" - ], + "installed_by": ["modules"], "patch": "modules/nf-core/untar/untar.diff" } } From 11f691c8eb31b43882622503215545d5900ad066 Mon Sep 17 00:00:00 2001 From: Athanasios Baltzis Date: Tue, 2 May 2023 09:37:23 +0200 Subject: [PATCH 031/227] Update CHANGELOG.md --- CHANGELOG.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7cef8643..509dbbfe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,8 @@ Initial release of nf-core/proteinfold, created with the [nf-core](https://nf-co ### Enhancements & fixes -- Add `accelerator` directive to GPU processes when `params.use_gpu` is true. +- [#80] Add `accelerator` directive to GPU processes when `params.use_gpu` is true. -- Support multiline fasta for colabfold multimer predictions +- [#81] Support multiline fasta for colabfold multimer predictions + +- [#89] Fix issue with excessive symlinking in the pdb_mmcif database From 4314a197e080b88bd1ed9f2aab4566da02fb0741 Mon Sep 17 00:00:00 2001 From: Athanasios Baltzis Date: Tue, 2 May 2023 10:06:41 +0200 Subject: [PATCH 032/227] Update dbs.config --- conf/dbs.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/dbs.config b/conf/dbs.config index 3f116951..9a8e0317 100644 --- a/conf/dbs.config +++ b/conf/dbs.config @@ -29,7 +29,7 @@ params { mgnify_path = "${params.alphafold2_db}/mgnify/*" pdb70_path = "${params.alphafold2_db}/pdb70/**" pdb_mmcif_path = "${params.alphafold2_db}/pdb_mmcif/*" - uniclust30_path = "${params.alphafold2_db}/uniclust30/**" + uniclust30_path = "${params.alphafold2_db}/uniclust30/*" uniref90_path = "${params.alphafold2_db}/uniref90/*" pdb_seqres_path = "${params.alphafold2_db}/pdb_seqres/*" uniprot_path = "${params.alphafold2_db}/uniprot/*" From 5ffc6dce9a21e96c5e42f9b88e93ac22631f1b23 Mon Sep 17 00:00:00 2001 From: nf-core-bot Date: Fri, 28 Apr 2023 14:25:23 +0000 Subject: [PATCH 033/227] Template update for nf-core/tools version 2.8 --- .editorconfig | 2 +- .github/ISSUE_TEMPLATE/bug_report.yml | 2 +- .github/PULL_REQUEST_TEMPLATE.md | 3 +- .github/workflows/awsfulltest.yml | 7 + .github/workflows/awstest.yml | 2 +- .github/workflows/branch.yml | 2 +- .github/workflows/clean-up.yml | 24 + .github/workflows/linting.yml | 2 +- .pre-commit-config.yaml | 5 + README.md | 63 +-- bin/check_samplesheet.py | 1 - conf/base.config | 2 +- conf/igenomes.config | 440 ++++++++++++++++++ conf/test_full.config | 2 + docs/usage.md | 130 ++---- lib/NfcoreSchema.groovy | 4 +- lib/WorkflowAlphafold2.groovy | 27 ++ lib/WorkflowMain.groovy | 13 +- main.nf | 1 - modules.json | 4 +- modules/local/samplesheet_check.nf | 2 +- .../custom/dumpsoftwareversions/main.nf | 6 +- .../custom/dumpsoftwareversions/meta.yml | 2 + modules/nf-core/multiqc/main.nf | 6 +- modules/nf-core/multiqc/meta.yml | 3 +- nextflow.config | 27 +- tower.yml | 5 + 27 files changed, 642 insertions(+), 145 deletions(-) create mode 100644 .github/workflows/clean-up.yml create mode 100644 .pre-commit-config.yaml create mode 100644 conf/igenomes.config create mode 100644 tower.yml diff --git a/.editorconfig b/.editorconfig index b78de6e6..b6b31907 100644 --- a/.editorconfig +++ b/.editorconfig @@ -8,7 +8,7 @@ trim_trailing_whitespace = true indent_size = 4 indent_style = space -[*.{md,yml,yaml,html,css,scss,js,cff}] +[*.{md,yml,yaml,html,css,scss,js}] indent_size = 2 # These files are edited and tested upstream in nf-core/modules diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 396fa3a8..93ef6491 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -45,6 +45,6 @@ body: * Nextflow version _(eg. 22.10.1)_ * Hardware _(eg. HPC, Desktop, Cloud)_ * Executor _(eg. slurm, local, awsbatch)_ - * Container engine: _(e.g. Docker, Singularity, Conda, Podman, Shifter or Charliecloud)_ + * Container engine: _(e.g. Docker, Singularity, Conda, Podman, Shifter, Charliecloud, or Apptainer)_ * OS _(eg. CentOS Linux, macOS, Linux Mint)_ * Version of nf-core/proteinfold _(eg. 1.1, 1.5, 1.8.2)_ diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 64d45b17..652d1bfe 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -15,7 +15,8 @@ Learn more about contributing: [CONTRIBUTING.md](https://github.com/nf-core/prot - [ ] This comment contains a description of changes (with reason). - [ ] If you've fixed a bug or added code that should be tested, add tests! -- [ ] If you've added a new tool - have you followed the pipeline conventions in the [contribution docs](https://github.com/nf-core/proteinfold/tree/master/.github/CONTRIBUTING.md)- [ ] If necessary, also make a PR on the nf-core/proteinfold _branch_ on the [nf-core/test-datasets](https://github.com/nf-core/test-datasets) repository. +- [ ] If you've added a new tool - have you followed the pipeline conventions in the [contribution docs](https://github.com/nf-core/proteinfold/tree/master/.github/CONTRIBUTING.md) +- [ ] If necessary, also make a PR on the nf-core/proteinfold _branch_ on the [nf-core/test-datasets](https://github.com/nf-core/test-datasets) repository. - [ ] Make sure your code lints (`nf-core lint`). - [ ] Ensure the test suite passes (`nextflow run . -profile test,docker --outdir `). - [ ] Usage Documentation in `docs/usage.md` is updated. diff --git a/.github/workflows/awsfulltest.yml b/.github/workflows/awsfulltest.yml index 0ea067af..0e3d52e8 100644 --- a/.github/workflows/awsfulltest.yml +++ b/.github/workflows/awsfulltest.yml @@ -26,7 +26,14 @@ jobs: ] steps: - name: Launch workflow via tower +<<<<<<< HEAD uses: nf-core/tower-action@v3 +======= + uses: seqeralabs/action-tower-launch@v1 + # TODO nf-core: You can customise AWS full pipeline tests as required + # Add full size test data (but still relatively small datasets for few samples) + # on the `test_full.config` test runs with only one set of parameters +>>>>>>> Template update for nf-core/tools version 2.8 with: workspace_id: ${{ secrets.TOWER_WORKSPACE_ID }} access_token: ${{ secrets.TOWER_ACCESS_TOKEN }} diff --git a/.github/workflows/awstest.yml b/.github/workflows/awstest.yml index f4025204..9bd51e81 100644 --- a/.github/workflows/awstest.yml +++ b/.github/workflows/awstest.yml @@ -12,7 +12,7 @@ jobs: steps: # Launch workflow using Tower CLI tool action - name: Launch workflow via tower - uses: nf-core/tower-action@v3 + uses: seqeralabs/action-tower-launch@v1 with: workspace_id: ${{ secrets.TOWER_WORKSPACE_ID }} access_token: ${{ secrets.TOWER_ACCESS_TOKEN }} diff --git a/.github/workflows/branch.yml b/.github/workflows/branch.yml index 649c9b99..3784bfd9 100644 --- a/.github/workflows/branch.yml +++ b/.github/workflows/branch.yml @@ -13,7 +13,7 @@ jobs: - name: Check PRs if: github.repository == 'nf-core/proteinfold' run: | - { [[ ${{github.event.pull_request.head.repo.full_name }} == nf-core/proteinfold ]] && [[ $GITHUB_HEAD_REF = "dev" ]]; } || [[ $GITHUB_HEAD_REF == "patch" ]] + { [[ ${{github.event.pull_request.head.repo.full_name }} == nf-core/proteinfold ]] && [[ $GITHUB_HEAD_REF == "dev" ]]; } || [[ $GITHUB_HEAD_REF == "patch" ]] # If the above check failed, post a comment on the PR explaining the failure # NOTE - this doesn't currently work if the PR is coming from a fork, due to limitations in GitHub actions secrets diff --git a/.github/workflows/clean-up.yml b/.github/workflows/clean-up.yml new file mode 100644 index 00000000..694e90ec --- /dev/null +++ b/.github/workflows/clean-up.yml @@ -0,0 +1,24 @@ +name: "Close user-tagged issues and PRs" +on: + schedule: + - cron: "0 0 * * 0" # Once a week + +jobs: + clean-up: + runs-on: ubuntu-latest + permissions: + issues: write + pull-requests: write + steps: + - uses: actions/stale@v7 + with: + stale-issue-message: "This issue has been tagged as awaiting-changes or awaiting-feedback by an nf-core contributor. Remove stale label or add a comment otherwise this issue will be closed in 20 days." + stale-pr-message: "This PR has been tagged as awaiting-changes or awaiting-feedback by an nf-core contributor. Remove stale label or add a comment if it is still useful." + close-issue-message: "This issue was closed because it has been tagged as awaiting-changes or awaiting-feedback by an nf-core contributor and then staled for 20 days with no activity." + days-before-stale: 30 + days-before-close: 20 + days-before-pr-close: -1 + any-of-labels: "awaiting-changes,awaiting-feedback" + exempt-issue-labels: "WIP" + exempt-pr-labels: "WIP" + repo-token: "${{ secrets.GITHUB_TOKEN }}" diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml index 858d622e..888cb4bc 100644 --- a/.github/workflows/linting.yml +++ b/.github/workflows/linting.yml @@ -78,7 +78,7 @@ jobs: - uses: actions/setup-python@v4 with: - python-version: "3.7" + python-version: "3.8" architecture: "x64" - name: Install dependencies diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..0c31cdb9 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,5 @@ +repos: + - repo: https://github.com/pre-commit/mirrors-prettier + rev: "v2.7.1" + hooks: + - id: prettier diff --git a/README.md b/README.md index 5407007f..880713c4 100644 --- a/README.md +++ b/README.md @@ -8,21 +8,17 @@ [![run with singularity](https://img.shields.io/badge/run%20with-singularity-1d355c.svg?labelColor=000000)](https://sylabs.io/docs/) [![Launch on Nextflow Tower](https://img.shields.io/badge/Launch%20%F0%9F%9A%80-Nextflow%20Tower-%234256e7)](https://tower.nf/launch?pipeline=https://github.com/nf-core/proteinfold) -[![Get help on Slack](http://img.shields.io/badge/slack-nf--core%20%23proteinfold-4A154B?labelColor=000000&logo=slack)](https://nfcore.slack.com/channels/proteinfold)[![Follow on Twitter](http://img.shields.io/badge/twitter-%40nf__core-1DA1F2?labelColor=000000&logo=twitter)](https://twitter.com/nf_core)[![Watch on YouTube](http://img.shields.io/badge/youtube-nf--core-FF0000?labelColor=000000&logo=youtube)](https://www.youtube.com/c/nf-core) +[![Get help on Slack](http://img.shields.io/badge/slack-nf--core%20%23proteinfold-4A154B?labelColor=000000&logo=slack)](https://nfcore.slack.com/channels/proteinfold)[![Follow on Twitter](http://img.shields.io/badge/twitter-%40nf__core-1DA1F2?labelColor=000000&logo=twitter)](https://twitter.com/nf_core)[![Follow on Mastodon](https://img.shields.io/badge/mastodon-nf__core-6364ff?labelColor=FFFFFF&logo=mastodon)](https://mstdn.science/@nf_core)[![Watch on YouTube](http://img.shields.io/badge/youtube-nf--core-FF0000?labelColor=000000&logo=youtube)](https://www.youtube.com/c/nf-core) ## Introduction - +**nf-core/proteinfold** is a bioinformatics pipeline that ... -**nf-core/proteinfold** is a bioinformatics best-practice analysis pipeline for Protein 3D structure prediction pipeline. - -The pipeline is built using [Nextflow](https://www.nextflow.io), a workflow tool to run tasks across multiple compute infrastructures in a very portable manner. It uses Docker/Singularity containers making installation trivial and results highly reproducible. The [Nextflow DSL2](https://www.nextflow.io/docs/latest/dsl2.html) implementation of this pipeline uses one container per process which makes it much easier to maintain and update software dependencies. Where possible, these processes have been submitted to and installed from [nf-core/modules](https://github.com/nf-core/modules) in order to make them available to all nf-core pipelines, and to everyone within the Nextflow community! - - - -On release, automated continuous integration tests run the pipeline on a full-sized dataset on the AWS cloud infrastructure. This ensures that the pipeline runs on AWS, has sensible resource allocation defaults set to run on real-world datasets, and permits the persistent storage of results to benchmark between pipeline releases and other analysis sources.The results obtained from the full-sized test can be viewed on the [nf-core website](https://nf-co.re/proteinfold/results). - -## Pipeline summary + ![Alt text](docs/images/nf-core-proteinfold_metro_map.png?raw=true "nf-core-proteinfold metro map") @@ -36,26 +32,39 @@ On release, automated continuous integration tests run the pipeline on a full-si iv. [ColabFold](https://github.com/sokrypton/ColabFold) - MMseqs2 local search followed by ColabFold -## Quick Start +## Usage + +> **Note** +> If you are new to Nextflow and nf-core, please refer to [this page](https://nf-co.re/docs/usage/installation) on how +> to set-up Nextflow. Make sure to [test your setup](https://nf-co.re/docs/usage/introduction#how-to-run-a-pipeline) +> with `-profile test` before running the workflow on actual data. + + - Note that some form of configuration will be needed so that Nextflow knows how to fetch the required software. This is usually done in the form of a config profile (`YOURPROFILE` in the example command above). You can chain multiple config profiles in a comma-separated string. +Now, you can run the pipeline using: - > - The pipeline comes with config profiles called `docker`, `singularity`, `podman`, `shifter`, `charliecloud` and `conda` which instruct the pipeline to use the named tool for software management. For example, `-profile test,docker`. - > - Please check [nf-core/configs](https://github.com/nf-core/configs#documentation) to see if a custom config file to run nf-core pipelines already exists for your Institute. If so, you can simply use `-profile ` in your command. This will enable either `docker` or `singularity` and set the appropriate execution settings for your local compute environment. - > - If you are using `singularity`, please use the [`nf-core download`](https://nf-co.re/tools/#downloading-pipelines-for-offline-use) command to download images first, before running the pipeline. Setting the [`NXF_SINGULARITY_CACHEDIR` or `singularity.cacheDir`](https://www.nextflow.io/docs/latest/singularity.html?#singularity-docker-hub) Nextflow options enables you to store and re-use the images from a central location for future pipeline runs. - > - If you are using `conda`, it is highly recommended to use the [`NXF_CONDA_CACHEDIR` or `conda.cacheDir`](https://www.nextflow.io/docs/latest/conda.html) settings to store the environments in a central location for future pipeline runs. + -4. Start running your own analysis! +```bash +nextflow run nf-core/proteinfold \ + -profile \ + --input samplesheet.csv \ + --outdir +``` The pipeline takes care of downloading the required databases and parameters required by AlphaFold2 and/or Colabfold. In case you have already downloaded the required files, you can skip this step by providing the path using the corresponding parameter [`--alphafold2_db`] or [`--colabfold_db`] @@ -122,9 +131,11 @@ On release, automated continuous integration tests run the pipeline on a full-si -profile ``` -## Documentation +## Pipeline output -The nf-core/proteinfold pipeline comes with documentation about the pipeline [usage](https://nf-co.re/proteinfold/usage), [parameters](https://nf-co.re/proteinfold/parameters) and [output](https://nf-co.re/proteinfold/output). +To see the the results of a test run with a full size dataset refer to the [results](https://nf-co.re/proteinfold/results) tab on the nf-core website pipeline page. +For more details about the output files and reports, please refer to the +[output documentation](https://nf-co.re/proteinfold/output). ## Credits diff --git a/bin/check_samplesheet.py b/bin/check_samplesheet.py index 5a1bf052..b031dbe1 100755 --- a/bin/check_samplesheet.py +++ b/bin/check_samplesheet.py @@ -37,7 +37,6 @@ def print_error(error, context="Line", context_str=""): print(error_str) sys.exit(1) - # TODO nf-core: Update the check_samplesheet function def check_samplesheet(file_in, file_out): """ diff --git a/conf/base.config b/conf/base.config index 37479a30..f13f56b2 100644 --- a/conf/base.config +++ b/conf/base.config @@ -15,7 +15,7 @@ process { memory = { check_max( 6.GB * task.attempt, 'memory' ) } time = { check_max( 4.h * task.attempt, 'time' ) } - errorStrategy = { task.exitStatus in [143,137,104,134,139] ? 'retry' : 'finish' } + errorStrategy = { task.exitStatus in ((130..145) + 104) ? 'retry' : 'finish' } maxRetries = 1 maxErrors = '-1' diff --git a/conf/igenomes.config b/conf/igenomes.config new file mode 100644 index 00000000..3f114377 --- /dev/null +++ b/conf/igenomes.config @@ -0,0 +1,440 @@ +/* +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Nextflow config file for iGenomes paths +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Defines reference genomes using iGenome paths. + Can be used by any config that customises the base path using: + $params.igenomes_base / --igenomes_base +---------------------------------------------------------------------------------------- +*/ + +params { + // illumina iGenomes reference file paths + genomes { + 'GRCh37' { + fasta = "${params.igenomes_base}/Homo_sapiens/Ensembl/GRCh37/Sequence/WholeGenomeFasta/genome.fa" + bwa = "${params.igenomes_base}/Homo_sapiens/Ensembl/GRCh37/Sequence/BWAIndex/version0.6.0/" + bowtie2 = "${params.igenomes_base}/Homo_sapiens/Ensembl/GRCh37/Sequence/Bowtie2Index/" + star = "${params.igenomes_base}/Homo_sapiens/Ensembl/GRCh37/Sequence/STARIndex/" + bismark = "${params.igenomes_base}/Homo_sapiens/Ensembl/GRCh37/Sequence/BismarkIndex/" + gtf = "${params.igenomes_base}/Homo_sapiens/Ensembl/GRCh37/Annotation/Genes/genes.gtf" + bed12 = "${params.igenomes_base}/Homo_sapiens/Ensembl/GRCh37/Annotation/Genes/genes.bed" + readme = "${params.igenomes_base}/Homo_sapiens/Ensembl/GRCh37/Annotation/README.txt" + mito_name = "MT" + macs_gsize = "2.7e9" + blacklist = "${projectDir}/assets/blacklists/GRCh37-blacklist.bed" + } + 'GRCh38' { + fasta = "${params.igenomes_base}/Homo_sapiens/NCBI/GRCh38/Sequence/WholeGenomeFasta/genome.fa" + bwa = "${params.igenomes_base}/Homo_sapiens/NCBI/GRCh38/Sequence/BWAIndex/version0.6.0/" + bowtie2 = "${params.igenomes_base}/Homo_sapiens/NCBI/GRCh38/Sequence/Bowtie2Index/" + star = "${params.igenomes_base}/Homo_sapiens/NCBI/GRCh38/Sequence/STARIndex/" + bismark = "${params.igenomes_base}/Homo_sapiens/NCBI/GRCh38/Sequence/BismarkIndex/" + gtf = "${params.igenomes_base}/Homo_sapiens/NCBI/GRCh38/Annotation/Genes/genes.gtf" + bed12 = "${params.igenomes_base}/Homo_sapiens/NCBI/GRCh38/Annotation/Genes/genes.bed" + mito_name = "chrM" + macs_gsize = "2.7e9" + blacklist = "${projectDir}/assets/blacklists/hg38-blacklist.bed" + } + 'CHM13' { + fasta = "${params.igenomes_base}/Homo_sapiens/UCSC/CHM13/Sequence/WholeGenomeFasta/genome.fa" + bwa = "${params.igenomes_base}/Homo_sapiens/UCSC/CHM13/Sequence/BWAIndex/" + bwamem2 = "${params.igenomes_base}/Homo_sapiens/UCSC/CHM13/Sequence/BWAmem2Index/" + gtf = "${params.igenomes_base}/Homo_sapiens/NCBI/CHM13/Annotation/Genes/genes.gtf" + gff = "ftp://ftp.ncbi.nlm.nih.gov/genomes/all/GCF/009/914/755/GCF_009914755.1_T2T-CHM13v2.0/GCF_009914755.1_T2T-CHM13v2.0_genomic.gff.gz" + mito_name = "chrM" + } + 'GRCm38' { + fasta = "${params.igenomes_base}/Mus_musculus/Ensembl/GRCm38/Sequence/WholeGenomeFasta/genome.fa" + bwa = "${params.igenomes_base}/Mus_musculus/Ensembl/GRCm38/Sequence/BWAIndex/version0.6.0/" + bowtie2 = "${params.igenomes_base}/Mus_musculus/Ensembl/GRCm38/Sequence/Bowtie2Index/" + star = "${params.igenomes_base}/Mus_musculus/Ensembl/GRCm38/Sequence/STARIndex/" + bismark = "${params.igenomes_base}/Mus_musculus/Ensembl/GRCm38/Sequence/BismarkIndex/" + gtf = "${params.igenomes_base}/Mus_musculus/Ensembl/GRCm38/Annotation/Genes/genes.gtf" + bed12 = "${params.igenomes_base}/Mus_musculus/Ensembl/GRCm38/Annotation/Genes/genes.bed" + readme = "${params.igenomes_base}/Mus_musculus/Ensembl/GRCm38/Annotation/README.txt" + mito_name = "MT" + macs_gsize = "1.87e9" + blacklist = "${projectDir}/assets/blacklists/GRCm38-blacklist.bed" + } + 'TAIR10' { + fasta = "${params.igenomes_base}/Arabidopsis_thaliana/Ensembl/TAIR10/Sequence/WholeGenomeFasta/genome.fa" + bwa = "${params.igenomes_base}/Arabidopsis_thaliana/Ensembl/TAIR10/Sequence/BWAIndex/version0.6.0/" + bowtie2 = "${params.igenomes_base}/Arabidopsis_thaliana/Ensembl/TAIR10/Sequence/Bowtie2Index/" + star = "${params.igenomes_base}/Arabidopsis_thaliana/Ensembl/TAIR10/Sequence/STARIndex/" + bismark = "${params.igenomes_base}/Arabidopsis_thaliana/Ensembl/TAIR10/Sequence/BismarkIndex/" + gtf = "${params.igenomes_base}/Arabidopsis_thaliana/Ensembl/TAIR10/Annotation/Genes/genes.gtf" + bed12 = "${params.igenomes_base}/Arabidopsis_thaliana/Ensembl/TAIR10/Annotation/Genes/genes.bed" + readme = "${params.igenomes_base}/Arabidopsis_thaliana/Ensembl/TAIR10/Annotation/README.txt" + mito_name = "Mt" + } + 'EB2' { + fasta = "${params.igenomes_base}/Bacillus_subtilis_168/Ensembl/EB2/Sequence/WholeGenomeFasta/genome.fa" + bwa = "${params.igenomes_base}/Bacillus_subtilis_168/Ensembl/EB2/Sequence/BWAIndex/version0.6.0/" + bowtie2 = "${params.igenomes_base}/Bacillus_subtilis_168/Ensembl/EB2/Sequence/Bowtie2Index/" + star = "${params.igenomes_base}/Bacillus_subtilis_168/Ensembl/EB2/Sequence/STARIndex/" + bismark = "${params.igenomes_base}/Bacillus_subtilis_168/Ensembl/EB2/Sequence/BismarkIndex/" + gtf = "${params.igenomes_base}/Bacillus_subtilis_168/Ensembl/EB2/Annotation/Genes/genes.gtf" + bed12 = "${params.igenomes_base}/Bacillus_subtilis_168/Ensembl/EB2/Annotation/Genes/genes.bed" + readme = "${params.igenomes_base}/Bacillus_subtilis_168/Ensembl/EB2/Annotation/README.txt" + } + 'UMD3.1' { + fasta = "${params.igenomes_base}/Bos_taurus/Ensembl/UMD3.1/Sequence/WholeGenomeFasta/genome.fa" + bwa = "${params.igenomes_base}/Bos_taurus/Ensembl/UMD3.1/Sequence/BWAIndex/version0.6.0/" + bowtie2 = "${params.igenomes_base}/Bos_taurus/Ensembl/UMD3.1/Sequence/Bowtie2Index/" + star = "${params.igenomes_base}/Bos_taurus/Ensembl/UMD3.1/Sequence/STARIndex/" + bismark = "${params.igenomes_base}/Bos_taurus/Ensembl/UMD3.1/Sequence/BismarkIndex/" + gtf = "${params.igenomes_base}/Bos_taurus/Ensembl/UMD3.1/Annotation/Genes/genes.gtf" + bed12 = "${params.igenomes_base}/Bos_taurus/Ensembl/UMD3.1/Annotation/Genes/genes.bed" + readme = "${params.igenomes_base}/Bos_taurus/Ensembl/UMD3.1/Annotation/README.txt" + mito_name = "MT" + } + 'WBcel235' { + fasta = "${params.igenomes_base}/Caenorhabditis_elegans/Ensembl/WBcel235/Sequence/WholeGenomeFasta/genome.fa" + bwa = "${params.igenomes_base}/Caenorhabditis_elegans/Ensembl/WBcel235/Sequence/BWAIndex/version0.6.0/" + bowtie2 = "${params.igenomes_base}/Caenorhabditis_elegans/Ensembl/WBcel235/Sequence/Bowtie2Index/" + star = "${params.igenomes_base}/Caenorhabditis_elegans/Ensembl/WBcel235/Sequence/STARIndex/" + bismark = "${params.igenomes_base}/Caenorhabditis_elegans/Ensembl/WBcel235/Sequence/BismarkIndex/" + gtf = "${params.igenomes_base}/Caenorhabditis_elegans/Ensembl/WBcel235/Annotation/Genes/genes.gtf" + bed12 = "${params.igenomes_base}/Caenorhabditis_elegans/Ensembl/WBcel235/Annotation/Genes/genes.bed" + mito_name = "MtDNA" + macs_gsize = "9e7" + } + 'CanFam3.1' { + fasta = "${params.igenomes_base}/Canis_familiaris/Ensembl/CanFam3.1/Sequence/WholeGenomeFasta/genome.fa" + bwa = "${params.igenomes_base}/Canis_familiaris/Ensembl/CanFam3.1/Sequence/BWAIndex/version0.6.0/" + bowtie2 = "${params.igenomes_base}/Canis_familiaris/Ensembl/CanFam3.1/Sequence/Bowtie2Index/" + star = "${params.igenomes_base}/Canis_familiaris/Ensembl/CanFam3.1/Sequence/STARIndex/" + bismark = "${params.igenomes_base}/Canis_familiaris/Ensembl/CanFam3.1/Sequence/BismarkIndex/" + gtf = "${params.igenomes_base}/Canis_familiaris/Ensembl/CanFam3.1/Annotation/Genes/genes.gtf" + bed12 = "${params.igenomes_base}/Canis_familiaris/Ensembl/CanFam3.1/Annotation/Genes/genes.bed" + readme = "${params.igenomes_base}/Canis_familiaris/Ensembl/CanFam3.1/Annotation/README.txt" + mito_name = "MT" + } + 'GRCz10' { + fasta = "${params.igenomes_base}/Danio_rerio/Ensembl/GRCz10/Sequence/WholeGenomeFasta/genome.fa" + bwa = "${params.igenomes_base}/Danio_rerio/Ensembl/GRCz10/Sequence/BWAIndex/version0.6.0/" + bowtie2 = "${params.igenomes_base}/Danio_rerio/Ensembl/GRCz10/Sequence/Bowtie2Index/" + star = "${params.igenomes_base}/Danio_rerio/Ensembl/GRCz10/Sequence/STARIndex/" + bismark = "${params.igenomes_base}/Danio_rerio/Ensembl/GRCz10/Sequence/BismarkIndex/" + gtf = "${params.igenomes_base}/Danio_rerio/Ensembl/GRCz10/Annotation/Genes/genes.gtf" + bed12 = "${params.igenomes_base}/Danio_rerio/Ensembl/GRCz10/Annotation/Genes/genes.bed" + mito_name = "MT" + } + 'BDGP6' { + fasta = "${params.igenomes_base}/Drosophila_melanogaster/Ensembl/BDGP6/Sequence/WholeGenomeFasta/genome.fa" + bwa = "${params.igenomes_base}/Drosophila_melanogaster/Ensembl/BDGP6/Sequence/BWAIndex/version0.6.0/" + bowtie2 = "${params.igenomes_base}/Drosophila_melanogaster/Ensembl/BDGP6/Sequence/Bowtie2Index/" + star = "${params.igenomes_base}/Drosophila_melanogaster/Ensembl/BDGP6/Sequence/STARIndex/" + bismark = "${params.igenomes_base}/Drosophila_melanogaster/Ensembl/BDGP6/Sequence/BismarkIndex/" + gtf = "${params.igenomes_base}/Drosophila_melanogaster/Ensembl/BDGP6/Annotation/Genes/genes.gtf" + bed12 = "${params.igenomes_base}/Drosophila_melanogaster/Ensembl/BDGP6/Annotation/Genes/genes.bed" + mito_name = "M" + macs_gsize = "1.2e8" + } + 'EquCab2' { + fasta = "${params.igenomes_base}/Equus_caballus/Ensembl/EquCab2/Sequence/WholeGenomeFasta/genome.fa" + bwa = "${params.igenomes_base}/Equus_caballus/Ensembl/EquCab2/Sequence/BWAIndex/version0.6.0/" + bowtie2 = "${params.igenomes_base}/Equus_caballus/Ensembl/EquCab2/Sequence/Bowtie2Index/" + star = "${params.igenomes_base}/Equus_caballus/Ensembl/EquCab2/Sequence/STARIndex/" + bismark = "${params.igenomes_base}/Equus_caballus/Ensembl/EquCab2/Sequence/BismarkIndex/" + gtf = "${params.igenomes_base}/Equus_caballus/Ensembl/EquCab2/Annotation/Genes/genes.gtf" + bed12 = "${params.igenomes_base}/Equus_caballus/Ensembl/EquCab2/Annotation/Genes/genes.bed" + readme = "${params.igenomes_base}/Equus_caballus/Ensembl/EquCab2/Annotation/README.txt" + mito_name = "MT" + } + 'EB1' { + fasta = "${params.igenomes_base}/Escherichia_coli_K_12_DH10B/Ensembl/EB1/Sequence/WholeGenomeFasta/genome.fa" + bwa = "${params.igenomes_base}/Escherichia_coli_K_12_DH10B/Ensembl/EB1/Sequence/BWAIndex/version0.6.0/" + bowtie2 = "${params.igenomes_base}/Escherichia_coli_K_12_DH10B/Ensembl/EB1/Sequence/Bowtie2Index/" + star = "${params.igenomes_base}/Escherichia_coli_K_12_DH10B/Ensembl/EB1/Sequence/STARIndex/" + bismark = "${params.igenomes_base}/Escherichia_coli_K_12_DH10B/Ensembl/EB1/Sequence/BismarkIndex/" + gtf = "${params.igenomes_base}/Escherichia_coli_K_12_DH10B/Ensembl/EB1/Annotation/Genes/genes.gtf" + bed12 = "${params.igenomes_base}/Escherichia_coli_K_12_DH10B/Ensembl/EB1/Annotation/Genes/genes.bed" + readme = "${params.igenomes_base}/Escherichia_coli_K_12_DH10B/Ensembl/EB1/Annotation/README.txt" + } + 'Galgal4' { + fasta = "${params.igenomes_base}/Gallus_gallus/Ensembl/Galgal4/Sequence/WholeGenomeFasta/genome.fa" + bwa = "${params.igenomes_base}/Gallus_gallus/Ensembl/Galgal4/Sequence/BWAIndex/version0.6.0/" + bowtie2 = "${params.igenomes_base}/Gallus_gallus/Ensembl/Galgal4/Sequence/Bowtie2Index/" + star = "${params.igenomes_base}/Gallus_gallus/Ensembl/Galgal4/Sequence/STARIndex/" + bismark = "${params.igenomes_base}/Gallus_gallus/Ensembl/Galgal4/Sequence/BismarkIndex/" + gtf = "${params.igenomes_base}/Gallus_gallus/Ensembl/Galgal4/Annotation/Genes/genes.gtf" + bed12 = "${params.igenomes_base}/Gallus_gallus/Ensembl/Galgal4/Annotation/Genes/genes.bed" + mito_name = "MT" + } + 'Gm01' { + fasta = "${params.igenomes_base}/Glycine_max/Ensembl/Gm01/Sequence/WholeGenomeFasta/genome.fa" + bwa = "${params.igenomes_base}/Glycine_max/Ensembl/Gm01/Sequence/BWAIndex/version0.6.0/" + bowtie2 = "${params.igenomes_base}/Glycine_max/Ensembl/Gm01/Sequence/Bowtie2Index/" + star = "${params.igenomes_base}/Glycine_max/Ensembl/Gm01/Sequence/STARIndex/" + bismark = "${params.igenomes_base}/Glycine_max/Ensembl/Gm01/Sequence/BismarkIndex/" + gtf = "${params.igenomes_base}/Glycine_max/Ensembl/Gm01/Annotation/Genes/genes.gtf" + bed12 = "${params.igenomes_base}/Glycine_max/Ensembl/Gm01/Annotation/Genes/genes.bed" + readme = "${params.igenomes_base}/Glycine_max/Ensembl/Gm01/Annotation/README.txt" + } + 'Mmul_1' { + fasta = "${params.igenomes_base}/Macaca_mulatta/Ensembl/Mmul_1/Sequence/WholeGenomeFasta/genome.fa" + bwa = "${params.igenomes_base}/Macaca_mulatta/Ensembl/Mmul_1/Sequence/BWAIndex/version0.6.0/" + bowtie2 = "${params.igenomes_base}/Macaca_mulatta/Ensembl/Mmul_1/Sequence/Bowtie2Index/" + star = "${params.igenomes_base}/Macaca_mulatta/Ensembl/Mmul_1/Sequence/STARIndex/" + bismark = "${params.igenomes_base}/Macaca_mulatta/Ensembl/Mmul_1/Sequence/BismarkIndex/" + gtf = "${params.igenomes_base}/Macaca_mulatta/Ensembl/Mmul_1/Annotation/Genes/genes.gtf" + bed12 = "${params.igenomes_base}/Macaca_mulatta/Ensembl/Mmul_1/Annotation/Genes/genes.bed" + readme = "${params.igenomes_base}/Macaca_mulatta/Ensembl/Mmul_1/Annotation/README.txt" + mito_name = "MT" + } + 'IRGSP-1.0' { + fasta = "${params.igenomes_base}/Oryza_sativa_japonica/Ensembl/IRGSP-1.0/Sequence/WholeGenomeFasta/genome.fa" + bwa = "${params.igenomes_base}/Oryza_sativa_japonica/Ensembl/IRGSP-1.0/Sequence/BWAIndex/version0.6.0/" + bowtie2 = "${params.igenomes_base}/Oryza_sativa_japonica/Ensembl/IRGSP-1.0/Sequence/Bowtie2Index/" + star = "${params.igenomes_base}/Oryza_sativa_japonica/Ensembl/IRGSP-1.0/Sequence/STARIndex/" + bismark = "${params.igenomes_base}/Oryza_sativa_japonica/Ensembl/IRGSP-1.0/Sequence/BismarkIndex/" + gtf = "${params.igenomes_base}/Oryza_sativa_japonica/Ensembl/IRGSP-1.0/Annotation/Genes/genes.gtf" + bed12 = "${params.igenomes_base}/Oryza_sativa_japonica/Ensembl/IRGSP-1.0/Annotation/Genes/genes.bed" + mito_name = "Mt" + } + 'CHIMP2.1.4' { + fasta = "${params.igenomes_base}/Pan_troglodytes/Ensembl/CHIMP2.1.4/Sequence/WholeGenomeFasta/genome.fa" + bwa = "${params.igenomes_base}/Pan_troglodytes/Ensembl/CHIMP2.1.4/Sequence/BWAIndex/version0.6.0/" + bowtie2 = "${params.igenomes_base}/Pan_troglodytes/Ensembl/CHIMP2.1.4/Sequence/Bowtie2Index/" + star = "${params.igenomes_base}/Pan_troglodytes/Ensembl/CHIMP2.1.4/Sequence/STARIndex/" + bismark = "${params.igenomes_base}/Pan_troglodytes/Ensembl/CHIMP2.1.4/Sequence/BismarkIndex/" + gtf = "${params.igenomes_base}/Pan_troglodytes/Ensembl/CHIMP2.1.4/Annotation/Genes/genes.gtf" + bed12 = "${params.igenomes_base}/Pan_troglodytes/Ensembl/CHIMP2.1.4/Annotation/Genes/genes.bed" + readme = "${params.igenomes_base}/Pan_troglodytes/Ensembl/CHIMP2.1.4/Annotation/README.txt" + mito_name = "MT" + } + 'Rnor_5.0' { + fasta = "${params.igenomes_base}/Rattus_norvegicus/Ensembl/Rnor_5.0/Sequence/WholeGenomeFasta/genome.fa" + bwa = "${params.igenomes_base}/Rattus_norvegicus/Ensembl/Rnor_5.0/Sequence/BWAIndex/version0.6.0/" + bowtie2 = "${params.igenomes_base}/Rattus_norvegicus/Ensembl/Rnor_5.0/Sequence/Bowtie2Index/" + star = "${params.igenomes_base}/Rattus_norvegicus/Ensembl/Rnor_5.0/Sequence/STARIndex/" + bismark = "${params.igenomes_base}/Rattus_norvegicus/Ensembl/Rnor_5.0/Sequence/BismarkIndex/" + gtf = "${params.igenomes_base}/Rattus_norvegicus/Ensembl/Rnor_5.0/Annotation/Genes/genes.gtf" + bed12 = "${params.igenomes_base}/Rattus_norvegicus/Ensembl/Rnor_5.0/Annotation/Genes/genes.bed" + mito_name = "MT" + } + 'Rnor_6.0' { + fasta = "${params.igenomes_base}/Rattus_norvegicus/Ensembl/Rnor_6.0/Sequence/WholeGenomeFasta/genome.fa" + bwa = "${params.igenomes_base}/Rattus_norvegicus/Ensembl/Rnor_6.0/Sequence/BWAIndex/version0.6.0/" + bowtie2 = "${params.igenomes_base}/Rattus_norvegicus/Ensembl/Rnor_6.0/Sequence/Bowtie2Index/" + star = "${params.igenomes_base}/Rattus_norvegicus/Ensembl/Rnor_6.0/Sequence/STARIndex/" + bismark = "${params.igenomes_base}/Rattus_norvegicus/Ensembl/Rnor_6.0/Sequence/BismarkIndex/" + gtf = "${params.igenomes_base}/Rattus_norvegicus/Ensembl/Rnor_6.0/Annotation/Genes/genes.gtf" + bed12 = "${params.igenomes_base}/Rattus_norvegicus/Ensembl/Rnor_6.0/Annotation/Genes/genes.bed" + mito_name = "MT" + } + 'R64-1-1' { + fasta = "${params.igenomes_base}/Saccharomyces_cerevisiae/Ensembl/R64-1-1/Sequence/WholeGenomeFasta/genome.fa" + bwa = "${params.igenomes_base}/Saccharomyces_cerevisiae/Ensembl/R64-1-1/Sequence/BWAIndex/version0.6.0/" + bowtie2 = "${params.igenomes_base}/Saccharomyces_cerevisiae/Ensembl/R64-1-1/Sequence/Bowtie2Index/" + star = "${params.igenomes_base}/Saccharomyces_cerevisiae/Ensembl/R64-1-1/Sequence/STARIndex/" + bismark = "${params.igenomes_base}/Saccharomyces_cerevisiae/Ensembl/R64-1-1/Sequence/BismarkIndex/" + gtf = "${params.igenomes_base}/Saccharomyces_cerevisiae/Ensembl/R64-1-1/Annotation/Genes/genes.gtf" + bed12 = "${params.igenomes_base}/Saccharomyces_cerevisiae/Ensembl/R64-1-1/Annotation/Genes/genes.bed" + mito_name = "MT" + macs_gsize = "1.2e7" + } + 'EF2' { + fasta = "${params.igenomes_base}/Schizosaccharomyces_pombe/Ensembl/EF2/Sequence/WholeGenomeFasta/genome.fa" + bwa = "${params.igenomes_base}/Schizosaccharomyces_pombe/Ensembl/EF2/Sequence/BWAIndex/version0.6.0/" + bowtie2 = "${params.igenomes_base}/Schizosaccharomyces_pombe/Ensembl/EF2/Sequence/Bowtie2Index/" + star = "${params.igenomes_base}/Schizosaccharomyces_pombe/Ensembl/EF2/Sequence/STARIndex/" + bismark = "${params.igenomes_base}/Schizosaccharomyces_pombe/Ensembl/EF2/Sequence/BismarkIndex/" + gtf = "${params.igenomes_base}/Schizosaccharomyces_pombe/Ensembl/EF2/Annotation/Genes/genes.gtf" + bed12 = "${params.igenomes_base}/Schizosaccharomyces_pombe/Ensembl/EF2/Annotation/Genes/genes.bed" + readme = "${params.igenomes_base}/Schizosaccharomyces_pombe/Ensembl/EF2/Annotation/README.txt" + mito_name = "MT" + macs_gsize = "1.21e7" + } + 'Sbi1' { + fasta = "${params.igenomes_base}/Sorghum_bicolor/Ensembl/Sbi1/Sequence/WholeGenomeFasta/genome.fa" + bwa = "${params.igenomes_base}/Sorghum_bicolor/Ensembl/Sbi1/Sequence/BWAIndex/version0.6.0/" + bowtie2 = "${params.igenomes_base}/Sorghum_bicolor/Ensembl/Sbi1/Sequence/Bowtie2Index/" + star = "${params.igenomes_base}/Sorghum_bicolor/Ensembl/Sbi1/Sequence/STARIndex/" + bismark = "${params.igenomes_base}/Sorghum_bicolor/Ensembl/Sbi1/Sequence/BismarkIndex/" + gtf = "${params.igenomes_base}/Sorghum_bicolor/Ensembl/Sbi1/Annotation/Genes/genes.gtf" + bed12 = "${params.igenomes_base}/Sorghum_bicolor/Ensembl/Sbi1/Annotation/Genes/genes.bed" + readme = "${params.igenomes_base}/Sorghum_bicolor/Ensembl/Sbi1/Annotation/README.txt" + } + 'Sscrofa10.2' { + fasta = "${params.igenomes_base}/Sus_scrofa/Ensembl/Sscrofa10.2/Sequence/WholeGenomeFasta/genome.fa" + bwa = "${params.igenomes_base}/Sus_scrofa/Ensembl/Sscrofa10.2/Sequence/BWAIndex/version0.6.0/" + bowtie2 = "${params.igenomes_base}/Sus_scrofa/Ensembl/Sscrofa10.2/Sequence/Bowtie2Index/" + star = "${params.igenomes_base}/Sus_scrofa/Ensembl/Sscrofa10.2/Sequence/STARIndex/" + bismark = "${params.igenomes_base}/Sus_scrofa/Ensembl/Sscrofa10.2/Sequence/BismarkIndex/" + gtf = "${params.igenomes_base}/Sus_scrofa/Ensembl/Sscrofa10.2/Annotation/Genes/genes.gtf" + bed12 = "${params.igenomes_base}/Sus_scrofa/Ensembl/Sscrofa10.2/Annotation/Genes/genes.bed" + readme = "${params.igenomes_base}/Sus_scrofa/Ensembl/Sscrofa10.2/Annotation/README.txt" + mito_name = "MT" + } + 'AGPv3' { + fasta = "${params.igenomes_base}/Zea_mays/Ensembl/AGPv3/Sequence/WholeGenomeFasta/genome.fa" + bwa = "${params.igenomes_base}/Zea_mays/Ensembl/AGPv3/Sequence/BWAIndex/version0.6.0/" + bowtie2 = "${params.igenomes_base}/Zea_mays/Ensembl/AGPv3/Sequence/Bowtie2Index/" + star = "${params.igenomes_base}/Zea_mays/Ensembl/AGPv3/Sequence/STARIndex/" + bismark = "${params.igenomes_base}/Zea_mays/Ensembl/AGPv3/Sequence/BismarkIndex/" + gtf = "${params.igenomes_base}/Zea_mays/Ensembl/AGPv3/Annotation/Genes/genes.gtf" + bed12 = "${params.igenomes_base}/Zea_mays/Ensembl/AGPv3/Annotation/Genes/genes.bed" + mito_name = "Mt" + } + 'hg38' { + fasta = "${params.igenomes_base}/Homo_sapiens/UCSC/hg38/Sequence/WholeGenomeFasta/genome.fa" + bwa = "${params.igenomes_base}/Homo_sapiens/UCSC/hg38/Sequence/BWAIndex/version0.6.0/" + bowtie2 = "${params.igenomes_base}/Homo_sapiens/UCSC/hg38/Sequence/Bowtie2Index/" + star = "${params.igenomes_base}/Homo_sapiens/UCSC/hg38/Sequence/STARIndex/" + bismark = "${params.igenomes_base}/Homo_sapiens/UCSC/hg38/Sequence/BismarkIndex/" + gtf = "${params.igenomes_base}/Homo_sapiens/UCSC/hg38/Annotation/Genes/genes.gtf" + bed12 = "${params.igenomes_base}/Homo_sapiens/UCSC/hg38/Annotation/Genes/genes.bed" + mito_name = "chrM" + macs_gsize = "2.7e9" + blacklist = "${projectDir}/assets/blacklists/hg38-blacklist.bed" + } + 'hg19' { + fasta = "${params.igenomes_base}/Homo_sapiens/UCSC/hg19/Sequence/WholeGenomeFasta/genome.fa" + bwa = "${params.igenomes_base}/Homo_sapiens/UCSC/hg19/Sequence/BWAIndex/version0.6.0/" + bowtie2 = "${params.igenomes_base}/Homo_sapiens/UCSC/hg19/Sequence/Bowtie2Index/" + star = "${params.igenomes_base}/Homo_sapiens/UCSC/hg19/Sequence/STARIndex/" + bismark = "${params.igenomes_base}/Homo_sapiens/UCSC/hg19/Sequence/BismarkIndex/" + gtf = "${params.igenomes_base}/Homo_sapiens/UCSC/hg19/Annotation/Genes/genes.gtf" + bed12 = "${params.igenomes_base}/Homo_sapiens/UCSC/hg19/Annotation/Genes/genes.bed" + readme = "${params.igenomes_base}/Homo_sapiens/UCSC/hg19/Annotation/README.txt" + mito_name = "chrM" + macs_gsize = "2.7e9" + blacklist = "${projectDir}/assets/blacklists/hg19-blacklist.bed" + } + 'mm10' { + fasta = "${params.igenomes_base}/Mus_musculus/UCSC/mm10/Sequence/WholeGenomeFasta/genome.fa" + bwa = "${params.igenomes_base}/Mus_musculus/UCSC/mm10/Sequence/BWAIndex/version0.6.0/" + bowtie2 = "${params.igenomes_base}/Mus_musculus/UCSC/mm10/Sequence/Bowtie2Index/" + star = "${params.igenomes_base}/Mus_musculus/UCSC/mm10/Sequence/STARIndex/" + bismark = "${params.igenomes_base}/Mus_musculus/UCSC/mm10/Sequence/BismarkIndex/" + gtf = "${params.igenomes_base}/Mus_musculus/UCSC/mm10/Annotation/Genes/genes.gtf" + bed12 = "${params.igenomes_base}/Mus_musculus/UCSC/mm10/Annotation/Genes/genes.bed" + readme = "${params.igenomes_base}/Mus_musculus/UCSC/mm10/Annotation/README.txt" + mito_name = "chrM" + macs_gsize = "1.87e9" + blacklist = "${projectDir}/assets/blacklists/mm10-blacklist.bed" + } + 'bosTau8' { + fasta = "${params.igenomes_base}/Bos_taurus/UCSC/bosTau8/Sequence/WholeGenomeFasta/genome.fa" + bwa = "${params.igenomes_base}/Bos_taurus/UCSC/bosTau8/Sequence/BWAIndex/version0.6.0/" + bowtie2 = "${params.igenomes_base}/Bos_taurus/UCSC/bosTau8/Sequence/Bowtie2Index/" + star = "${params.igenomes_base}/Bos_taurus/UCSC/bosTau8/Sequence/STARIndex/" + bismark = "${params.igenomes_base}/Bos_taurus/UCSC/bosTau8/Sequence/BismarkIndex/" + gtf = "${params.igenomes_base}/Bos_taurus/UCSC/bosTau8/Annotation/Genes/genes.gtf" + bed12 = "${params.igenomes_base}/Bos_taurus/UCSC/bosTau8/Annotation/Genes/genes.bed" + mito_name = "chrM" + } + 'ce10' { + fasta = "${params.igenomes_base}/Caenorhabditis_elegans/UCSC/ce10/Sequence/WholeGenomeFasta/genome.fa" + bwa = "${params.igenomes_base}/Caenorhabditis_elegans/UCSC/ce10/Sequence/BWAIndex/version0.6.0/" + bowtie2 = "${params.igenomes_base}/Caenorhabditis_elegans/UCSC/ce10/Sequence/Bowtie2Index/" + star = "${params.igenomes_base}/Caenorhabditis_elegans/UCSC/ce10/Sequence/STARIndex/" + bismark = "${params.igenomes_base}/Caenorhabditis_elegans/UCSC/ce10/Sequence/BismarkIndex/" + gtf = "${params.igenomes_base}/Caenorhabditis_elegans/UCSC/ce10/Annotation/Genes/genes.gtf" + bed12 = "${params.igenomes_base}/Caenorhabditis_elegans/UCSC/ce10/Annotation/Genes/genes.bed" + readme = "${params.igenomes_base}/Caenorhabditis_elegans/UCSC/ce10/Annotation/README.txt" + mito_name = "chrM" + macs_gsize = "9e7" + } + 'canFam3' { + fasta = "${params.igenomes_base}/Canis_familiaris/UCSC/canFam3/Sequence/WholeGenomeFasta/genome.fa" + bwa = "${params.igenomes_base}/Canis_familiaris/UCSC/canFam3/Sequence/BWAIndex/version0.6.0/" + bowtie2 = "${params.igenomes_base}/Canis_familiaris/UCSC/canFam3/Sequence/Bowtie2Index/" + star = "${params.igenomes_base}/Canis_familiaris/UCSC/canFam3/Sequence/STARIndex/" + bismark = "${params.igenomes_base}/Canis_familiaris/UCSC/canFam3/Sequence/BismarkIndex/" + gtf = "${params.igenomes_base}/Canis_familiaris/UCSC/canFam3/Annotation/Genes/genes.gtf" + bed12 = "${params.igenomes_base}/Canis_familiaris/UCSC/canFam3/Annotation/Genes/genes.bed" + readme = "${params.igenomes_base}/Canis_familiaris/UCSC/canFam3/Annotation/README.txt" + mito_name = "chrM" + } + 'danRer10' { + fasta = "${params.igenomes_base}/Danio_rerio/UCSC/danRer10/Sequence/WholeGenomeFasta/genome.fa" + bwa = "${params.igenomes_base}/Danio_rerio/UCSC/danRer10/Sequence/BWAIndex/version0.6.0/" + bowtie2 = "${params.igenomes_base}/Danio_rerio/UCSC/danRer10/Sequence/Bowtie2Index/" + star = "${params.igenomes_base}/Danio_rerio/UCSC/danRer10/Sequence/STARIndex/" + bismark = "${params.igenomes_base}/Danio_rerio/UCSC/danRer10/Sequence/BismarkIndex/" + gtf = "${params.igenomes_base}/Danio_rerio/UCSC/danRer10/Annotation/Genes/genes.gtf" + bed12 = "${params.igenomes_base}/Danio_rerio/UCSC/danRer10/Annotation/Genes/genes.bed" + mito_name = "chrM" + macs_gsize = "1.37e9" + } + 'dm6' { + fasta = "${params.igenomes_base}/Drosophila_melanogaster/UCSC/dm6/Sequence/WholeGenomeFasta/genome.fa" + bwa = "${params.igenomes_base}/Drosophila_melanogaster/UCSC/dm6/Sequence/BWAIndex/version0.6.0/" + bowtie2 = "${params.igenomes_base}/Drosophila_melanogaster/UCSC/dm6/Sequence/Bowtie2Index/" + star = "${params.igenomes_base}/Drosophila_melanogaster/UCSC/dm6/Sequence/STARIndex/" + bismark = "${params.igenomes_base}/Drosophila_melanogaster/UCSC/dm6/Sequence/BismarkIndex/" + gtf = "${params.igenomes_base}/Drosophila_melanogaster/UCSC/dm6/Annotation/Genes/genes.gtf" + bed12 = "${params.igenomes_base}/Drosophila_melanogaster/UCSC/dm6/Annotation/Genes/genes.bed" + mito_name = "chrM" + macs_gsize = "1.2e8" + } + 'equCab2' { + fasta = "${params.igenomes_base}/Equus_caballus/UCSC/equCab2/Sequence/WholeGenomeFasta/genome.fa" + bwa = "${params.igenomes_base}/Equus_caballus/UCSC/equCab2/Sequence/BWAIndex/version0.6.0/" + bowtie2 = "${params.igenomes_base}/Equus_caballus/UCSC/equCab2/Sequence/Bowtie2Index/" + star = "${params.igenomes_base}/Equus_caballus/UCSC/equCab2/Sequence/STARIndex/" + bismark = "${params.igenomes_base}/Equus_caballus/UCSC/equCab2/Sequence/BismarkIndex/" + gtf = "${params.igenomes_base}/Equus_caballus/UCSC/equCab2/Annotation/Genes/genes.gtf" + bed12 = "${params.igenomes_base}/Equus_caballus/UCSC/equCab2/Annotation/Genes/genes.bed" + readme = "${params.igenomes_base}/Equus_caballus/UCSC/equCab2/Annotation/README.txt" + mito_name = "chrM" + } + 'galGal4' { + fasta = "${params.igenomes_base}/Gallus_gallus/UCSC/galGal4/Sequence/WholeGenomeFasta/genome.fa" + bwa = "${params.igenomes_base}/Gallus_gallus/UCSC/galGal4/Sequence/BWAIndex/version0.6.0/" + bowtie2 = "${params.igenomes_base}/Gallus_gallus/UCSC/galGal4/Sequence/Bowtie2Index/" + star = "${params.igenomes_base}/Gallus_gallus/UCSC/galGal4/Sequence/STARIndex/" + bismark = "${params.igenomes_base}/Gallus_gallus/UCSC/galGal4/Sequence/BismarkIndex/" + gtf = "${params.igenomes_base}/Gallus_gallus/UCSC/galGal4/Annotation/Genes/genes.gtf" + bed12 = "${params.igenomes_base}/Gallus_gallus/UCSC/galGal4/Annotation/Genes/genes.bed" + readme = "${params.igenomes_base}/Gallus_gallus/UCSC/galGal4/Annotation/README.txt" + mito_name = "chrM" + } + 'panTro4' { + fasta = "${params.igenomes_base}/Pan_troglodytes/UCSC/panTro4/Sequence/WholeGenomeFasta/genome.fa" + bwa = "${params.igenomes_base}/Pan_troglodytes/UCSC/panTro4/Sequence/BWAIndex/version0.6.0/" + bowtie2 = "${params.igenomes_base}/Pan_troglodytes/UCSC/panTro4/Sequence/Bowtie2Index/" + star = "${params.igenomes_base}/Pan_troglodytes/UCSC/panTro4/Sequence/STARIndex/" + bismark = "${params.igenomes_base}/Pan_troglodytes/UCSC/panTro4/Sequence/BismarkIndex/" + gtf = "${params.igenomes_base}/Pan_troglodytes/UCSC/panTro4/Annotation/Genes/genes.gtf" + bed12 = "${params.igenomes_base}/Pan_troglodytes/UCSC/panTro4/Annotation/Genes/genes.bed" + readme = "${params.igenomes_base}/Pan_troglodytes/UCSC/panTro4/Annotation/README.txt" + mito_name = "chrM" + } + 'rn6' { + fasta = "${params.igenomes_base}/Rattus_norvegicus/UCSC/rn6/Sequence/WholeGenomeFasta/genome.fa" + bwa = "${params.igenomes_base}/Rattus_norvegicus/UCSC/rn6/Sequence/BWAIndex/version0.6.0/" + bowtie2 = "${params.igenomes_base}/Rattus_norvegicus/UCSC/rn6/Sequence/Bowtie2Index/" + star = "${params.igenomes_base}/Rattus_norvegicus/UCSC/rn6/Sequence/STARIndex/" + bismark = "${params.igenomes_base}/Rattus_norvegicus/UCSC/rn6/Sequence/BismarkIndex/" + gtf = "${params.igenomes_base}/Rattus_norvegicus/UCSC/rn6/Annotation/Genes/genes.gtf" + bed12 = "${params.igenomes_base}/Rattus_norvegicus/UCSC/rn6/Annotation/Genes/genes.bed" + mito_name = "chrM" + } + 'sacCer3' { + fasta = "${params.igenomes_base}/Saccharomyces_cerevisiae/UCSC/sacCer3/Sequence/WholeGenomeFasta/genome.fa" + bwa = "${params.igenomes_base}/Saccharomyces_cerevisiae/UCSC/sacCer3/Sequence/BWAIndex/version0.6.0/" + bowtie2 = "${params.igenomes_base}/Saccharomyces_cerevisiae/UCSC/sacCer3/Sequence/Bowtie2Index/" + star = "${params.igenomes_base}/Saccharomyces_cerevisiae/UCSC/sacCer3/Sequence/STARIndex/" + bismark = "${params.igenomes_base}/Saccharomyces_cerevisiae/UCSC/sacCer3/Sequence/BismarkIndex/" + readme = "${params.igenomes_base}/Saccharomyces_cerevisiae/UCSC/sacCer3/Annotation/README.txt" + mito_name = "chrM" + macs_gsize = "1.2e7" + } + 'susScr3' { + fasta = "${params.igenomes_base}/Sus_scrofa/UCSC/susScr3/Sequence/WholeGenomeFasta/genome.fa" + bwa = "${params.igenomes_base}/Sus_scrofa/UCSC/susScr3/Sequence/BWAIndex/version0.6.0/" + bowtie2 = "${params.igenomes_base}/Sus_scrofa/UCSC/susScr3/Sequence/Bowtie2Index/" + star = "${params.igenomes_base}/Sus_scrofa/UCSC/susScr3/Sequence/STARIndex/" + bismark = "${params.igenomes_base}/Sus_scrofa/UCSC/susScr3/Sequence/BismarkIndex/" + gtf = "${params.igenomes_base}/Sus_scrofa/UCSC/susScr3/Annotation/Genes/genes.gtf" + bed12 = "${params.igenomes_base}/Sus_scrofa/UCSC/susScr3/Annotation/Genes/genes.bed" + readme = "${params.igenomes_base}/Sus_scrofa/UCSC/susScr3/Annotation/README.txt" + mito_name = "chrM" + } + } +} diff --git a/conf/test_full.config b/conf/test_full.config index dc8ff985..fef0cffa 100644 --- a/conf/test_full.config +++ b/conf/test_full.config @@ -10,6 +10,8 @@ ---------------------------------------------------------------------------------------- */ +cleanup = true + params { config_profile_name = 'Full test profile' config_profile_description = 'Full test dataset to check pipeline function' diff --git a/docs/usage.md b/docs/usage.md index c33aaa36..51dd018a 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -397,6 +397,29 @@ work # Directory containing the nextflow working files # Other nextflow hidden files, eg. history of pipeline runs and old logs. ``` +If you wish to repeatedly use the same parameters for multiple runs, rather than specifying each flag in the command, you can specify these in a params file. + +Pipeline settings can be provided in a `yaml` or `json` file via `-params-file `. + +> ⚠️ Do not use `-c ` to specify parameters as this will result in errors. Custom config files specified with `-c` must only be used for [tuning process resource specifications](https://nf-co.re/docs/usage/configuration#tuning-workflow-resources), other infrastructural tweaks (such as output directories), or module arguments (args). +> The above pipeline run specified with a params file in yaml format: + +```bash +nextflow run nf-core/proteinfold -profile docker -params-file params.yaml +``` + +with `params.yaml` containing: + +```yaml +input: './samplesheet.csv' +outdir: './results/' +genome: 'GRCh37' +input: 'data' +<...> +``` + +You can also generate such `YAML`/`JSON` files via [nf-core/launch](https://nf-co.re/launch). + ### Updating the pipeline When you run the above command, Nextflow automatically pulls the pipeline code from GitHub and stores it as a cached version. When running the pipeline after this, it will always use the cached version if available - even if the pipeline has been updated since. To make sure that you're running the latest version of the pipeline, make sure that you regularly update the cached version of the pipeline: @@ -413,6 +436,10 @@ First, go to the [nf-core/proteinfold releases page](https://github.com/nf-core/ This version number will be logged in reports when you run the pipeline, so that you'll know what you used when you look back in the future. For example, at the bottom of the MultiQC reports. +To further assist in reproducbility, you can use share and re-use [parameter files](#running-the-pipeline) to repeat pipeline runs with the same settings without having to write out a command with every single parameter. + +> 💡 If you wish to share such profile (such as upload as supplementary material for academic publications), make sure to NOT include cluster specific paths to files, nor institutional specific profiles. + ## Core Nextflow arguments > **NB:** These options are part of Nextflow and use a _single_ hyphen (pipeline parameters use a double-hyphen). @@ -421,7 +448,7 @@ This version number will be logged in reports when you run the pipeline, so that Use this parameter to choose a configuration profile. Profiles can give configuration presets for different compute environments. -Several generic profiles are bundled with the pipeline which instruct the pipeline to use software packaged using different methods (Docker, Singularity, Podman, Shifter, Charliecloud, Conda) - see below. +Several generic profiles are bundled with the pipeline which instruct the pipeline to use software packaged using different methods (Docker, Singularity, Podman, Shifter, Charliecloud, Apptainer, Conda) - see below. > We highly recommend the use of Docker or Singularity containers for full pipeline reproducibility, however when this is not possible, Conda is also supported. @@ -445,8 +472,10 @@ If `-profile` is not specified, the pipeline will run locally and expect all sof - A generic configuration profile to be used with [Shifter](https://nersc.gitlab.io/development/shifter/how-to-use/) - `charliecloud` - A generic configuration profile to be used with [Charliecloud](https://hpc.github.io/charliecloud/) +- `apptainer` + - A generic configuration profile to be used with [Apptainer](https://apptainer.org/) - `conda` - - A generic configuration profile to be used with [Conda](https://conda.io/docs/). Please only use Conda as a last resort i.e. when it's not possible to run the pipeline with Docker, Singularity, Podman, Shifter or Charliecloud. + - A generic configuration profile to be used with [Conda](https://conda.io/docs/). Please only use Conda as a last resort i.e. when it's not possible to run the pipeline with Docker, Singularity, Podman, Shifter, Charliecloud, or Apptainer. ### `-resume` @@ -464,102 +493,19 @@ Specify the path to a specific config file (this is a core Nextflow command). Se Whilst the default requirements set within the pipeline will hopefully work for most people and with most input data, you may find that you want to customise the compute resources that the pipeline requests. Each step in the pipeline has a default set of requirements for number of CPUs, memory and time. For most of the steps in the pipeline, if the job exits with any of the error codes specified [here](https://github.com/nf-core/rnaseq/blob/4c27ef5610c87db00c3c5a3eed10b1d161abf575/conf/base.config#L18) it will automatically be resubmitted with higher requests (2 x original, then 3 x original). If it still fails after the third attempt then the pipeline execution is stopped. -For example, if the nf-core/rnaseq pipeline is failing after multiple re-submissions of the `STAR_ALIGN` process due to an exit code of `137` this would indicate that there is an out of memory issue: - -```console -[62/149eb0] NOTE: Process `NFCORE_RNASEQ:RNASEQ:ALIGN_STAR:STAR_ALIGN (WT_REP1)` terminated with an error exit status (137) -- Execution is retried (1) -Error executing process > 'NFCORE_RNASEQ:RNASEQ:ALIGN_STAR:STAR_ALIGN (WT_REP1)' - -Caused by: - Process `NFCORE_RNASEQ:RNASEQ:ALIGN_STAR:STAR_ALIGN (WT_REP1)` terminated with an error exit status (137) - -Command executed: - STAR \ - --genomeDir star \ - --readFilesIn WT_REP1_trimmed.fq.gz \ - --runThreadN 2 \ - --outFileNamePrefix WT_REP1. \ - - -Command exit status: - 137 - -Command output: - (empty) - -Command error: - .command.sh: line 9: 30 Killed STAR --genomeDir star --readFilesIn WT_REP1_trimmed.fq.gz --runThreadN 2 --outFileNamePrefix WT_REP1. -Work dir: - /home/pipelinetest/work/9d/172ca5881234073e8d76f2a19c88fb - -Tip: you can replicate the issue by changing to the process work dir and entering the command `bash .command.run` -``` - -#### For beginners - -A first step to bypass this error, you could try to increase the amount of CPUs, memory, and time for the whole pipeline. Therefor you can try to increase the resource for the parameters `--max_cpus`, `--max_memory`, and `--max_time`. Based on the error above, you have to increase the amount of memory. Therefore you can go to the [parameter documentation of rnaseq](https://nf-co.re/rnaseq/3.9/parameters) and scroll down to the `show hidden parameter` button to get the default value for `--max_memory`. In this case 128GB, you than can try to run your pipeline again with `--max_memory 200GB -resume` to skip all process, that were already calculated. If you can not increase the resource of the complete pipeline, you can try to adapt the resource for a single process as mentioned below. - -#### Advanced option on process level - -To bypass this error you would need to find exactly which resources are set by the `STAR_ALIGN` process. The quickest way is to search for `process STAR_ALIGN` in the [nf-core/rnaseq Github repo](https://github.com/nf-core/rnaseq/search?q=process+STAR_ALIGN). -We have standardised the structure of Nextflow DSL2 pipelines such that all module files will be present in the `modules/` directory and so, based on the search results, the file we want is `modules/nf-core/star/align/main.nf`. -If you click on the link to that file you will notice that there is a `label` directive at the top of the module that is set to [`label process_high`](https://github.com/nf-core/rnaseq/blob/4c27ef5610c87db00c3c5a3eed10b1d161abf575/modules/nf-core/software/star/align/main.nf#L9). -The [Nextflow `label`](https://www.nextflow.io/docs/latest/process.html#label) directive allows us to organise workflow processes in separate groups which can be referenced in a configuration file to select and configure subset of processes having similar computing requirements. -The default values for the `process_high` label are set in the pipeline's [`base.config`](https://github.com/nf-core/rnaseq/blob/4c27ef5610c87db00c3c5a3eed10b1d161abf575/conf/base.config#L33-L37) which in this case is defined as 72GB. -Providing you haven't set any other standard nf-core parameters to **cap** the [maximum resources](https://nf-co.re/usage/configuration#max-resources) used by the pipeline then we can try and bypass the `STAR_ALIGN` process failure by creating a custom config file that sets at least 72GB of memory, in this case increased to 100GB. -The custom config below can then be provided to the pipeline via the [`-c`](#-c) parameter as highlighted in previous sections. - -```nextflow -process { - withName: 'NFCORE_RNASEQ:RNASEQ:ALIGN_STAR:STAR_ALIGN' { - memory = 100.GB - } -} -``` - -> **NB:** We specify the full process name i.e. `NFCORE_RNASEQ:RNASEQ:ALIGN_STAR:STAR_ALIGN` in the config file because this takes priority over the short name (`STAR_ALIGN`) and allows existing configuration using the full process name to be correctly overridden. -> -> If you get a warning suggesting that the process selector isn't recognised check that the process name has been specified correctly. - -### Updating containers (advanced users) - -The [Nextflow DSL2](https://www.nextflow.io/docs/latest/dsl2.html) implementation of this pipeline uses one container per process which makes it much easier to maintain and update software dependencies. If for some reason you need to use a different version of a particular tool with the pipeline then you just need to identify the `process` name and override the Nextflow `container` definition for that process using the `withName` declaration. For example, in the [nf-core/viralrecon](https://nf-co.re/viralrecon) pipeline a tool called [Pangolin](https://github.com/cov-lineages/pangolin) has been used during the COVID-19 pandemic to assign lineages to SARS-CoV-2 genome sequenced samples. Given that the lineage assignments change quite frequently it doesn't make sense to re-release the nf-core/viralrecon everytime a new version of Pangolin has been released. However, you can override the default container used by the pipeline by creating a custom config file and passing it as a command-line argument via `-c custom.config`. - -1. Check the default version used by the pipeline in the module file for [Pangolin](https://github.com/nf-core/viralrecon/blob/a85d5969f9025409e3618d6c280ef15ce417df65/modules/nf-core/software/pangolin/main.nf#L14-L19) -2. Find the latest version of the Biocontainer available on [Quay.io](https://quay.io/repository/biocontainers/pangolin?tag=latest&tab=tags) -3. Create the custom config accordingly: - - - For Docker: +To change the resource requests, please see the [max resources](https://nf-co.re/docs/usage/configuration#max-resources) and [tuning workflow resources](https://nf-co.re/docs/usage/configuration#tuning-workflow-resources) section of the nf-core website. - ```nextflow - process { - withName: PANGOLIN { - container = 'quay.io/biocontainers/pangolin:3.0.5--pyhdfd78af_0' - } - } - ``` +### Custom Containers - - For Singularity: +In some cases you may wish to change which container or conda environment a step of the pipeline uses for a particular tool. By default nf-core pipelines use containers and software from the [biocontainers](https://biocontainers.pro/) or [bioconda](https://bioconda.github.io/) projects. However in some cases the pipeline specified version maybe out of date. - ```nextflow - process { - withName: PANGOLIN { - container = 'https://depot.galaxyproject.org/singularity/pangolin:3.0.5--pyhdfd78af_0' - } - } - ``` +To use a different container from the default container or conda environment specified in a pipeline, please see the [updating tool versions](https://nf-co.re/docs/usage/configuration#updating-tool-versions) section of the nf-core website. - - For Conda: +### Custom Tool Arguments - ```nextflow - process { - withName: PANGOLIN { - conda = 'bioconda::pangolin=3.0.5' - } - } - ``` +A pipeline might not always support every possible argument or option of a particular tool used in pipeline. Fortunately, nf-core pipelines provide some freedom to users to insert additional parameters that the pipeline does not include by default. -> **NB:** If you wish to periodically update individual tool-specific results (e.g. Pangolin) generated by the pipeline then you must ensure to keep the `work/` directory otherwise the `-resume` ability of the pipeline will be compromised and it will restart from scratch. +To learn how to provide additional arguments to a particular tool of the pipeline, please see the [customising tool arguments](https://nf-co.re/docs/usage/configuration#customising-tool-arguments) section of the nf-core website. ### nf-core/configs diff --git a/lib/NfcoreSchema.groovy b/lib/NfcoreSchema.groovy index 33cd4f6e..9b34804d 100755 --- a/lib/NfcoreSchema.groovy +++ b/lib/NfcoreSchema.groovy @@ -2,6 +2,7 @@ // This file holds several functions used to perform JSON parameter validation, help and summary rendering for the nf-core pipeline template. // +import nextflow.Nextflow import org.everit.json.schema.Schema import org.everit.json.schema.loader.SchemaLoader import org.everit.json.schema.ValidationException @@ -83,6 +84,7 @@ class NfcoreSchema { 'stub-run', 'test', 'w', + 'with-apptainer', 'with-charliecloud', 'with-conda', 'with-dag', @@ -177,7 +179,7 @@ class NfcoreSchema { } if (has_error) { - System.exit(1) + Nextflow.error('Exiting!') } } diff --git a/lib/WorkflowAlphafold2.groovy b/lib/WorkflowAlphafold2.groovy index 6f67744c..e31a9412 100755 --- a/lib/WorkflowAlphafold2.groovy +++ b/lib/WorkflowAlphafold2.groovy @@ -2,6 +2,7 @@ // This file holds several functions specific to the workflow/proteinfold.nf in the nf-core/proteinfold pipeline // +import nextflow.Nextflow import groovy.text.SimpleTemplateEngine class WorkflowAlphafold2 { @@ -10,6 +11,15 @@ class WorkflowAlphafold2 { // Check and validate parameters // public static void initialise(params, log) { +<<<<<<< HEAD:lib/WorkflowAlphafold2.groovy +======= + genomeExistsError(params, log) + + + if (!params.fasta) { + Nextflow.error "Genome fasta file not specified with e.g. '--fasta genome.fa' or via a detectable config file." + } +>>>>>>> Template update for nf-core/tools version 2.8:lib/WorkflowProteinfold.groovy } // @@ -54,5 +64,22 @@ class WorkflowAlphafold2 { def description_html = engine.createTemplate(methods_text).make(meta) return description_html +<<<<<<< HEAD:lib/WorkflowAlphafold2.groovy +======= + } + + // + // Exit pipeline if incorrect --genome key provided + // + private static void genomeExistsError(params, log) { + if (params.genomes && params.genome && !params.genomes.containsKey(params.genome)) { + def error_string = "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" + + " Genome '${params.genome}' not found in any config files provided to the pipeline.\n" + + " Currently, the available genome keys are:\n" + + " ${params.genomes.keySet().join(", ")}\n" + + "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" + Nextflow.error(error_string) + } +>>>>>>> Template update for nf-core/tools version 2.8:lib/WorkflowProteinfold.groovy } } diff --git a/lib/WorkflowMain.groovy b/lib/WorkflowMain.groovy index 4925607d..975aaeaa 100755 --- a/lib/WorkflowMain.groovy +++ b/lib/WorkflowMain.groovy @@ -2,6 +2,8 @@ // This file holds several functions specific to the main.nf workflow in the nf-core/proteinfold pipeline // +import nextflow.Nextflow + class WorkflowMain { // @@ -21,7 +23,7 @@ class WorkflowMain { // // Generate help string // - public static String help(workflow, params, log) { + public static String help(workflow, params) { def command = "nextflow run ${workflow.manifest.name} --input samplesheet.csv --genome GRCh37 -profile docker" def help_string = '' help_string += NfcoreTemplate.logo(workflow, params.monochrome_logs) @@ -34,7 +36,7 @@ class WorkflowMain { // // Generate parameter summary log string // - public static String paramsSummaryLog(workflow, params, log) { + public static String paramsSummaryLog(workflow, params) { def summary_log = '' summary_log += NfcoreTemplate.logo(workflow, params.monochrome_logs) summary_log += NfcoreSchema.paramsSummaryLog(workflow, params) @@ -49,7 +51,7 @@ class WorkflowMain { public static void initialise(workflow, params, log) { // Print help to screen if required if (params.help) { - log.info help(workflow, params, log) + log.info help(workflow, params) System.exit(0) } @@ -61,7 +63,7 @@ class WorkflowMain { } // Print parameter summary log to screen - log.info paramsSummaryLog(workflow, params, log) + log.info paramsSummaryLog(workflow, params) // Validate workflow parameters via the JSON schema if (params.validate_params) { @@ -82,8 +84,7 @@ class WorkflowMain { // Check input has been provided if (!params.input) { - log.error "Please provide an input samplesheet to the pipeline e.g. '--input samplesheet.csv'" - System.exit(1) + Nextflow.error("Please provide an input samplesheet to the pipeline e.g. '--input samplesheet.csv'") } } // diff --git a/main.nf b/main.nf index eebc1469..36213f31 100644 --- a/main.nf +++ b/main.nf @@ -4,7 +4,6 @@ nf-core/proteinfold ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Github : https://github.com/nf-core/proteinfold - Website: https://nf-co.re/proteinfold Slack : https://nfcore.slack.com/channels/proteinfold ---------------------------------------------------------------------------------------- diff --git a/modules.json b/modules.json index d4f7f84b..4bc44595 100644 --- a/modules.json +++ b/modules.json @@ -12,7 +12,7 @@ }, "custom/dumpsoftwareversions": { "branch": "master", - "git_sha": "c8e35eb2055c099720a75538d1b8adb3fb5a464c", + "git_sha": "76cc4938c1f6ea5c7d83fed1eeffc146787f9543", "installed_by": ["modules"] }, "gunzip": { @@ -32,7 +32,7 @@ }, "multiqc": { "branch": "master", - "git_sha": "c8e35eb2055c099720a75538d1b8adb3fb5a464c", + "git_sha": "f2d63bd5b68925f98f572eed70993d205cc694b7", "installed_by": ["modules"] }, "untar": { diff --git a/modules/local/samplesheet_check.nf b/modules/local/samplesheet_check.nf index ad6022c0..58027d71 100644 --- a/modules/local/samplesheet_check.nf +++ b/modules/local/samplesheet_check.nf @@ -5,7 +5,7 @@ process SAMPLESHEET_CHECK { conda "conda-forge::python=3.8.3" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? 'https://depot.galaxyproject.org/singularity/python:3.8.3' : - 'quay.io/biocontainers/python:3.8.3' }" + 'biocontainers/python:3.8.3' }" input: path samplesheet diff --git a/modules/nf-core/custom/dumpsoftwareversions/main.nf b/modules/nf-core/custom/dumpsoftwareversions/main.nf index 3df21765..800a6099 100644 --- a/modules/nf-core/custom/dumpsoftwareversions/main.nf +++ b/modules/nf-core/custom/dumpsoftwareversions/main.nf @@ -2,10 +2,10 @@ process CUSTOM_DUMPSOFTWAREVERSIONS { label 'process_single' // Requires `pyyaml` which does not have a dedicated container but is in the MultiQC container - conda "bioconda::multiqc=1.13" + conda "bioconda::multiqc=1.14" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/multiqc:1.13--pyhdfd78af_0' : - 'quay.io/biocontainers/multiqc:1.13--pyhdfd78af_0' }" + 'https://depot.galaxyproject.org/singularity/multiqc:1.14--pyhdfd78af_0' : + 'quay.io/biocontainers/multiqc:1.14--pyhdfd78af_0' }" input: path versions diff --git a/modules/nf-core/custom/dumpsoftwareversions/meta.yml b/modules/nf-core/custom/dumpsoftwareversions/meta.yml index 60b546a0..c32657de 100644 --- a/modules/nf-core/custom/dumpsoftwareversions/meta.yml +++ b/modules/nf-core/custom/dumpsoftwareversions/meta.yml @@ -1,7 +1,9 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/nf-core/modules/master/modules/yaml-schema.json name: custom_dumpsoftwareversions description: Custom module used to dump software versions within the nf-core pipeline template keywords: - custom + - dump - version tools: - custom: diff --git a/modules/nf-core/multiqc/main.nf b/modules/nf-core/multiqc/main.nf index 68f66bea..4b604749 100644 --- a/modules/nf-core/multiqc/main.nf +++ b/modules/nf-core/multiqc/main.nf @@ -1,10 +1,10 @@ process MULTIQC { label 'process_single' - conda "bioconda::multiqc=1.13" + conda "bioconda::multiqc=1.14" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/multiqc:1.13--pyhdfd78af_0' : - 'quay.io/biocontainers/multiqc:1.13--pyhdfd78af_0' }" + 'https://depot.galaxyproject.org/singularity/multiqc:1.14--pyhdfd78af_0' : + 'quay.io/biocontainers/multiqc:1.14--pyhdfd78af_0' }" input: path multiqc_files, stageAs: "?/*" diff --git a/modules/nf-core/multiqc/meta.yml b/modules/nf-core/multiqc/meta.yml index ebc29b27..f93b5ee5 100644 --- a/modules/nf-core/multiqc/meta.yml +++ b/modules/nf-core/multiqc/meta.yml @@ -1,3 +1,4 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/nf-core/modules/master/modules/yaml-schema.json name: MultiQC description: Aggregate results from bioinformatics analyses across many samples into a single report keywords: @@ -37,7 +38,7 @@ output: description: MultiQC report file pattern: "multiqc_report.html" - data: - type: dir + type: directory description: MultiQC data dir pattern: "multiqc_data" - plots: diff --git a/nextflow.config b/nextflow.config index 56b3c01e..96fa0d2b 100644 --- a/nextflow.config +++ b/nextflow.config @@ -123,7 +123,11 @@ try { profiles { - debug { process.beforeScript = 'echo $HOSTNAME' } + debug { + dumpHashes = true + process.beforeScript = 'echo $HOSTNAME' + cleanup = false + } conda { conda.enabled = true docker.enabled = false @@ -131,6 +135,7 @@ profiles { podman.enabled = false shifter.enabled = false charliecloud.enabled = false + apptainer.enabled = false } mamba { conda.enabled = true @@ -140,15 +145,18 @@ profiles { podman.enabled = false shifter.enabled = false charliecloud.enabled = false + apptainer.enabled = false } docker { docker.enabled = true + docker.registry = 'quay.io' docker.userEmulation = true if (params.use_gpu) { docker.runOptions = '--gpus all' } singularity.enabled = false podman.enabled = false shifter.enabled = false charliecloud.enabled = false + apptainer.enabled = false } arm { if (params.use_gpu) { @@ -165,27 +173,44 @@ profiles { podman.enabled = false shifter.enabled = false charliecloud.enabled = false + apptainer.enabled = false } podman { podman.enabled = true + podman.registry = 'quay.io' + conda.enabled = false docker.enabled = false singularity.enabled = false shifter.enabled = false charliecloud.enabled = false + apptainer.enabled = false } shifter { shifter.enabled = true + conda.enabled = false docker.enabled = false singularity.enabled = false podman.enabled = false charliecloud.enabled = false + apptainer.enabled = false } charliecloud { charliecloud.enabled = true + conda.enabled = false docker.enabled = false singularity.enabled = false podman.enabled = false shifter.enabled = false + apptainer.enabled = false + } + apptainer { + apptainer.enabled = true + conda.enabled = false + docker.enabled = false + singularity.enabled = false + podman.enabled = false + shifter.enabled = false + charliecloud.enabled = false } gitpod { executor.name = 'local' diff --git a/tower.yml b/tower.yml new file mode 100644 index 00000000..787aedfe --- /dev/null +++ b/tower.yml @@ -0,0 +1,5 @@ +reports: + multiqc_report.html: + display: "MultiQC HTML report" + samplesheet.csv: + display: "Auto-created samplesheet with collated metadata and FASTQ paths" From a6b913cce776e6a9fb10a001165ddc72ff366c66 Mon Sep 17 00:00:00 2001 From: Athanasios Baltzis Date: Tue, 2 May 2023 14:48:24 +0200 Subject: [PATCH 034/227] Run prettier --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 880713c4..a645203a 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,7 @@ nextflow run nf-core/proteinfold \ --outdir ``` - The pipeline takes care of downloading the required databases and parameters required by AlphaFold2 and/or Colabfold. In case you have already downloaded the required files, you can skip this step by providing the path using the corresponding parameter [`--alphafold2_db`] or [`--colabfold_db`] +The pipeline takes care of downloading the required databases and parameters required by AlphaFold2 and/or Colabfold. In case you have already downloaded the required files, you can skip this step by providing the path using the corresponding parameter [`--alphafold2_db`] or [`--colabfold_db`] - Typical command to run AlphaFold2 mode: From e73842c91791d8af0c3296ac8f66b72b28cbea2e Mon Sep 17 00:00:00 2001 From: Athanasios Baltzis Date: Tue, 2 May 2023 16:27:10 +0200 Subject: [PATCH 035/227] Update .github/workflows/awsfulltest.yml Co-authored-by: Jose Espinosa-Carrasco --- .github/workflows/awsfulltest.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/awsfulltest.yml b/.github/workflows/awsfulltest.yml index 0e3d52e8..98291c89 100644 --- a/.github/workflows/awsfulltest.yml +++ b/.github/workflows/awsfulltest.yml @@ -26,14 +26,10 @@ jobs: ] steps: - name: Launch workflow via tower -<<<<<<< HEAD uses: nf-core/tower-action@v3 -======= - uses: seqeralabs/action-tower-launch@v1 # TODO nf-core: You can customise AWS full pipeline tests as required # Add full size test data (but still relatively small datasets for few samples) # on the `test_full.config` test runs with only one set of parameters ->>>>>>> Template update for nf-core/tools version 2.8 with: workspace_id: ${{ secrets.TOWER_WORKSPACE_ID }} access_token: ${{ secrets.TOWER_ACCESS_TOKEN }} From 37666bf77ed7f4ab7144832c08840ee048b1dbbb Mon Sep 17 00:00:00 2001 From: Leila Mansouri Date: Wed, 3 May 2023 11:06:22 +0200 Subject: [PATCH 036/227] Added changes to run with new versions of colabfold (v.1.5.2) and alphafold (v.2.3.2) --- conf/dbs.config | 39 ++++++++++--------- conf/test_full_colabfold_local.config | 2 +- conf/test_full_colabfold_webserver.config | 2 +- ...t_full_colabfold_webserver_multimer.config | 2 +- modules/local/colabfold_batch.nf | 8 ++-- modules/local/mmseqs_colabfoldsearch.nf | 11 +++--- modules/local/run_alphafold2.nf | 6 +-- modules/local/run_alphafold2_msa.nf | 9 +++-- modules/local/run_alphafold2_pred.nf | 6 +-- nextflow.config | 13 ++++--- nextflow_schema.json | 16 ++++---- subworkflows/local/prepare_alphafold2_dbs.nf | 16 +++++--- workflows/alphafold2.nf | 6 +-- 13 files changed, 74 insertions(+), 62 deletions(-) diff --git a/conf/dbs.config b/conf/dbs.config index 9a8e0317..ea668852 100644 --- a/conf/dbs.config +++ b/conf/dbs.config @@ -9,18 +9,18 @@ params { // AlphaFold2 links - bfd = 'https://storage.googleapis.com/alphafold-databases/casp14_versions/bfd_metaclust_clu_complete_id30_c90_final_seq.sorted_opt.tar.gz' - small_bfd = 'https://storage.googleapis.com/alphafold-databases/reduced_dbs/bfd-first_non_consensus_sequences.fasta.gz' - alphafold2_params = 'https://storage.googleapis.com/alphafold/alphafold_params_2022-03-02.tar' - mgnify = 'https://storage.googleapis.com/alphafold-databases/casp14_versions/mgy_clusters_2018_12.fa.gz' - pdb70 = 'http://wwwuser.gwdg.de/~compbiol/data/hhsuite/databases/hhsuite_dbs/old-releases/pdb70_from_mmcif_200916.tar.gz' - pdb_mmcif = 'rsync.rcsb.org::ftp_data/structures/divided/mmCIF/' //'rsync.rcsb.org::ftp_data/structures/divided/mmCIF/' ftp.pdbj.org::ftp_data/structures/divided/mmCIF/ rsync.ebi.ac.uk::pub/databases/pdb/data/structures/divided/mmCIF/ - pdb_obsolete = 'ftp://ftp.wwpdb.org/pub/pdb/data/status/obsolete.dat' - uniclust30 = 'https://storage.googleapis.com/alphafold-databases/casp14_versions/uniclust30_2018_08_hhsuite.tar.gz' - uniref90 = 'ftp://ftp.uniprot.org/pub/databases/uniprot/uniref/uniref90/uniref90.fasta.gz' - pdb_seqres = 'ftp://ftp.wwpdb.org/pub/pdb/derived_data/pdb_seqres.txt' - uniprot_sprot = 'ftp://ftp.ebi.ac.uk/pub/databases/uniprot/current_release/knowledgebase/complete/uniprot_sprot.fasta.gz' - uniprot_trembl = 'ftp://ftp.ebi.ac.uk/pub/databases/uniprot/current_release/knowledgebase/complete/uniprot_trembl.fasta.gz' + bfd = 'https://storage.googleapis.com/alphafold-databases/casp14_versions/bfd_metaclust_clu_complete_id30_c90_final_seq.sorted_opt.tar.gz' + small_bfd = 'https://storage.googleapis.com/alphafold-databases/reduced_dbs/bfd-first_non_consensus_sequences.fasta.gz' + alphafold2_params = 'https://storage.googleapis.com/alphafold/alphafold_params_2022-12-06.tar' + mgnify = 'https://storage.googleapis.com/alphafold-databases/casp14_versions/mgy_clusters_2018_12.fa.gz' + pdb70 = 'http://wwwuser.gwdg.de/~compbiol/data/hhsuite/databases/hhsuite_dbs/old-releases/pdb70_from_mmcif_200916.tar.gz' + pdb_mmcif = 'rsync.rcsb.org::ftp_data/structures/divided/mmCIF/' //'rsync.rcsb.org::ftp_data/structures/divided/mmCIF/' ftp.pdbj.org::ftp_data/structures/divided/mmCIF/ rsync.ebi.ac.uk::pub/databases/pdb/data/structures/divided/mmCIF/ + pdb_obsolete = 'ftp://ftp.wwpdb.org/pub/pdb/data/status/obsolete.dat' + uniref30_alphafold2 = 'https://storage.googleapis.com/alphafold-databases/v2.3/UniRef30_2021_03.tar.gz' + uniref90 = 'ftp://ftp.uniprot.org/pub/databases/uniprot/uniref/uniref90/uniref90.fasta.gz' + pdb_seqres = 'ftp://ftp.wwpdb.org/pub/pdb/derived_data/pdb_seqres.txt' + uniprot_sprot = 'ftp://ftp.ebi.ac.uk/pub/databases/uniprot/current_release/knowledgebase/complete/uniprot_sprot.fasta.gz' + uniprot_trembl = 'ftp://ftp.ebi.ac.uk/pub/databases/uniprot/current_release/knowledgebase/complete/uniprot_trembl.fasta.gz' // Alphafold paths bfd_path = "${params.alphafold2_db}/bfd/*" @@ -29,21 +29,22 @@ params { mgnify_path = "${params.alphafold2_db}/mgnify/*" pdb70_path = "${params.alphafold2_db}/pdb70/**" pdb_mmcif_path = "${params.alphafold2_db}/pdb_mmcif/*" - uniclust30_path = "${params.alphafold2_db}/uniclust30/*" + uniref30_alphafold2_path = "${params.alphafold2_db}/uniref30/**" uniref90_path = "${params.alphafold2_db}/uniref90/*" pdb_seqres_path = "${params.alphafold2_db}/pdb_seqres/*" uniprot_path = "${params.alphafold2_db}/uniprot/*" // Colabfold links - colabfold_db_link = 'http://wwwuser.gwdg.de/~compbiol/colabfold/colabfold_envdb_202108.tar.gz' - uniref30 = 'https://wwwuser.gwdg.de/~compbiol/colabfold/uniref30_2202.tar.gz' + colabfold_db_link = 'http://wwwuser.gwdg.de/~compbiol/colabfold/colabfold_envdb_202108.tar.gz' + uniref30_colabfold_link = 'https://wwwuser.gwdg.de/~compbiol/colabfold/uniref30_2202.tar.gz' // Colabfold paths colabfold_db_path = "${params.colabfold_db}/colabfold_envdb_202108" - uniref30_path = "${params.colabfold_db}/uniref30_2202" + uniref30_colabfold_path = "${params.colabfold_db}/uniref30_2202" colabfold_alphafold2_params_tags = [ - "AlphaFold2-multimer-v1" : "alphafold_params_colab_2021-10-27", - "AlphaFold2-multimer-v2" : "alphafold_params_colab_2022-03-02", - "AlphaFold2-ptm" : "alphafold_params_2021-07-14" + "alphafold2_multimer_v1" : "alphafold_params_colab_2021-10-27", + "alphafold2_multimer_v2" : "alphafold_params_colab_2022-03-02", + "alphafold2_multimer_v3" : "alphafold_params_colab_2022-12-06", + "alphafold2_ptm" : "alphafold_params_2021-07-14" ] } diff --git a/conf/test_full_colabfold_local.config b/conf/test_full_colabfold_local.config index e34ff152..44dd270f 100644 --- a/conf/test_full_colabfold_local.config +++ b/conf/test_full_colabfold_local.config @@ -18,7 +18,7 @@ params { // Input data to test colabfold with a local server analysis mode = 'colabfold' colabfold_server = 'local' - colabfold_model_preset = 'AlphaFold2-ptm' + colabfold_model_preset = 'alphafold2_ptm' input = 'https://raw.githubusercontent.com/nf-core/test-datasets/proteinfold/testdata/samplesheet/v1.0/samplesheet.csv' colabfold_db = 's3://ngi-igenomes/test-data/proteinfold/db/colabfold_mini' } diff --git a/conf/test_full_colabfold_webserver.config b/conf/test_full_colabfold_webserver.config index b54347de..8c503b09 100644 --- a/conf/test_full_colabfold_webserver.config +++ b/conf/test_full_colabfold_webserver.config @@ -17,7 +17,7 @@ params { // Input data for full test of colabfold with Colabfold server mode = 'colabfold' colabfold_server = 'webserver' - colabfold_model_preset = 'AlphaFold2-ptm' + colabfold_model_preset = 'alphafold2_ptm' input = 'https://raw.githubusercontent.com/nf-core/test-datasets/proteinfold/testdata/samplesheet/v1.0/samplesheet.csv' colabfold_db = 's3://ngi-igenomes/test-data/proteinfold/db/colabfold_mini' } diff --git a/conf/test_full_colabfold_webserver_multimer.config b/conf/test_full_colabfold_webserver_multimer.config index 8b50a4a1..99063c91 100644 --- a/conf/test_full_colabfold_webserver_multimer.config +++ b/conf/test_full_colabfold_webserver_multimer.config @@ -17,7 +17,7 @@ params { // Input data for full test of colabfold with Colabfold server mode = 'colabfold' colabfold_server = 'webserver' - colabfold_model_preset = 'AlphaFold2-multimer-v2' + colabfold_model_preset = 'alphafold2_multimer_v3' input = 'https://raw.githubusercontent.com/nf-core/test-datasets/proteinfold/testdata/samplesheet/v1.0/samplesheet_multimer.csv' colabfold_db = 's3://ngi-igenomes/test-data/proteinfold/db/colabfold_mini' } diff --git a/modules/local/colabfold_batch.nf b/modules/local/colabfold_batch.nf index e6312fed..b693bb65 100644 --- a/modules/local/colabfold_batch.nf +++ b/modules/local/colabfold_batch.nf @@ -3,8 +3,8 @@ process COLABFOLD_BATCH { label 'process_medium' container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'docker://nfcore/proteinfold_colabfold:1.0.0' : - 'nfcore/proteinfold_colabfold:1.0.0' }" + 'docker://lmansouri/colabfold:no_download' : + 'lmansouri/colabfold:no_download' }" input: tuple val(meta), path(fasta) @@ -24,7 +24,7 @@ process COLABFOLD_BATCH { script: def args = task.ext.args ?: '' - def VERSION = '1.2.0' // WARN: Version information not provided by tool on CLI. Please update this string when bumping container versions. + def VERSION = '1.5.2' // WARN: Version information not provided by tool on CLI. Please update this string when bumping container versions. """ ln -r -s params/alphafold_params_*/* params/ @@ -45,7 +45,7 @@ process COLABFOLD_BATCH { """ stub: - def VERSION = '1.2.0' // WARN: Version information not provided by tool on CLI. Please update this string when bumping container versions. + def VERSION = '1.5.2' // WARN: Version information not provided by tool on CLI. Please update this string when bumping container versions. """ touch ./"${fasta.baseName}"_colabfold.pdb touch ./"${fasta.baseName}"_mqc.png diff --git a/modules/local/mmseqs_colabfoldsearch.nf b/modules/local/mmseqs_colabfoldsearch.nf index 55cb17b1..6ecbb59e 100644 --- a/modules/local/mmseqs_colabfoldsearch.nf +++ b/modules/local/mmseqs_colabfoldsearch.nf @@ -3,8 +3,9 @@ process MMSEQS_COLABFOLDSEARCH { label 'process_high_memory' container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'docker://nfcore/proteinfold_colabfold:1.0.0' : - 'nfcore/proteinfold_colabfold:1.0.0' }" + 'docker://lmansouri/colabfold:no_download' : + 'lmansouri/colabfold:no_download' }" + input: tuple val(meta), path(fasta) @@ -21,13 +22,13 @@ process MMSEQS_COLABFOLDSEARCH { script: def args = task.ext.args ?: '' - def VERSION = '1.2.0' // WARN: Version information not provided by tool on CLI. Please update this string when bumping container versions. + def VERSION = '1.5.2' // WARN: Version information not provided by tool on CLI. Please update this string when bumping container versions. """ ln -r -s $uniref30/uniref30_* ./db ln -r -s $colabfold_db/colabfold_envdb* ./db - /colabfold_batch/colabfold-conda/bin/colabfold_search \\ + /localcolabfold/colabfold-conda/bin/colabfold_search \\ $args \\ --threads $task.cpus ${fasta} \\ ./db \\ @@ -42,7 +43,7 @@ process MMSEQS_COLABFOLDSEARCH { """ stub: - def VERSION = '1.2.0' // WARN: Version information not provided by tool on CLI. Please update this string when bumping container versions. + def VERSION = '1.5.2' // WARN: Version information not provided by tool on CLI. Please update this string when bumping container versions. """ touch ${meta.id}.a3m diff --git a/modules/local/run_alphafold2.nf b/modules/local/run_alphafold2.nf index 559ad4e5..8013beb2 100644 --- a/modules/local/run_alphafold2.nf +++ b/modules/local/run_alphafold2.nf @@ -6,8 +6,8 @@ process RUN_ALPHAFOLD2 { label 'process_medium' container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'docker://nfcore/proteinfold_alphafold2_standard:1.0.0' : - 'nfcore/proteinfold_alphafold2_standard:1.0.0' }" + 'docker://lmansouri/alphafold-standard:2.0.0' : + 'lmansouri/alphafold-standard:2.0.0' }" input: tuple val(meta), path(fasta) @@ -19,7 +19,7 @@ process RUN_ALPHAFOLD2 { path ('mgnify/*') path ('pdb70/*') path ('pdb_mmcif/*') - path ('uniclust30/*') + path ('uniref30/*') path ('uniref90/*') path ('pdb_seqres/*') path ('uniprot/*') diff --git a/modules/local/run_alphafold2_msa.nf b/modules/local/run_alphafold2_msa.nf index 78278810..164b35d1 100644 --- a/modules/local/run_alphafold2_msa.nf +++ b/modules/local/run_alphafold2_msa.nf @@ -6,8 +6,9 @@ process RUN_ALPHAFOLD2_MSA { label 'process_medium' container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'docker://nfcore/proteinfold_alphafold2_msa:1.0.0' : - 'nfcore/proteinfold_alphafold2_msa:1.0.0' }" + 'docker://lmansouri/alphafold-msa:2.0.0' : + 'lmansouri/alphafold-msa:2.0.0' }" + input: tuple val(meta), path(fasta) @@ -19,7 +20,7 @@ process RUN_ALPHAFOLD2_MSA { path ('mgnify/*') path ('pdb70/*') path ('pdb_mmcif/*') - path ('uniclust30/*') + path ('uniref30/*') path ('uniref90/*') path ('pdb_seqres/*') path ('uniprot/*') @@ -34,7 +35,7 @@ process RUN_ALPHAFOLD2_MSA { script: def args = task.ext.args ?: '' - def db_preset = db_preset ? "full_dbs --bfd_database_path=./bfd/bfd_metaclust_clu_complete_id30_c90_final_seq.sorted_opt --uniclust30_database_path=./uniclust30/uniclust30_2018_08/uniclust30_2018_08" : + def db_preset = db_preset ? "full_dbs --bfd_database_path=./bfd/bfd_metaclust_clu_complete_id30_c90_final_seq.sorted_opt --uniref30_database_path=./uniref30/" : "reduced_dbs --small_bfd_database_path=./small_bfd/bfd-first_non_consensus_sequences.fasta" if (alphafold2_model_preset == 'multimer') { alphafold2_model_preset += " --pdb_seqres_database_path=./pdb_seqres/pdb_seqres.txt --uniprot_database_path=./uniprot/uniprot.fasta " diff --git a/modules/local/run_alphafold2_pred.nf b/modules/local/run_alphafold2_pred.nf index 43143b9d..7ec1c6e2 100644 --- a/modules/local/run_alphafold2_pred.nf +++ b/modules/local/run_alphafold2_pred.nf @@ -6,8 +6,8 @@ process RUN_ALPHAFOLD2_PRED { label 'process_medium' container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'docker://nfcore/proteinfold_alphafold2_split:1.0.0' : - 'nfcore/proteinfold_alphafold2_split:1.0.0' }" + 'docker://lmansouri/alphafold-split:2.0.0' : + 'lmansouri/alphafold-split:2.0.0' }" input: tuple val(meta), path(fasta) @@ -19,7 +19,7 @@ process RUN_ALPHAFOLD2_PRED { path ('mgnify/*') path ('pdb70/*') path ('pdb_mmcif/*') - path ('uniclust30/*') + path ('uniref30/*') path ('uniref90/*') path ('pdb_seqres/*') path ('uniprot/*') diff --git a/nextflow.config b/nextflow.config index 96fa0d2b..b1aff145 100644 --- a/nextflow.config +++ b/nextflow.config @@ -29,7 +29,7 @@ params { pdb70 = null pdb_mmcif = null pdb_obsolete = null - uniclust30 = null + uniref30_alphafold = null uniref90 = null pdb_seqres = null uniprot_sprot = null @@ -42,14 +42,14 @@ params { mgnify_path = null pdb70_path = null pdb_mmcif_path = null - uniclust30_path = null + uniref30_alphafold_path = null uniref90_path = null pdb_seqres_path = null uniprot_path = null // Colabfold parameters colabfold_server = "webserver" - colabfold_model_preset = "AlphaFold2-ptm" // {AlphaFold2-ptm,AlphaFold2-multimer-v1,AlphaFold2-multimer-v2} + colabfold_model_preset = "alphafold2_ptm" // {'auto', 'alphafold2', 'alphafold2_ptm', 'alphafold2_multimer_v1', 'alphafold2_multimer_v2', 'alphafold2_multimer_v3'} num_recycle = 3 use_amber = true colabfold_db = null @@ -60,11 +60,11 @@ params { // Colabfold links colabfold_db_link = null - uniref30 = null + uniref30_colabfold_path = null // Colabfold paths colabfold_db_path = null - uniref30_path = null + uniref30_0_colabfold_path = null // MultiQC options multiqc_config = null @@ -318,3 +318,6 @@ def check_max(obj, type) { } } } + + + diff --git a/nextflow_schema.json b/nextflow_schema.json index dbe9832c..cf7fc87b 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -113,9 +113,9 @@ }, "colabfold_model_preset": { "type": "string", - "default": "AlphaFold2-ptm", + "default": "alphafold2_ptm", "description": "Model preset for 'colabfold' mode", - "enum": ["AlphaFold2-ptm", "AlphaFold2-multimer-v1", "AlphaFold2-multimer-v2"], + "enum": ["auto", "alphafold2", "alphafold2_ptm", "alphafold2_multimer_v1", "alphafold2_multimer_v2", "alphafold2_multimer_v3"], "fa_icon": "fas fa-stream" }, "num_recycle": { @@ -258,7 +258,7 @@ }, "alphafold2_params": { "type": "string", - "default": "https://storage.googleapis.com/alphafold/alphafold_params_2022-03-02.tar", + "default": "https://storage.googleapis.com/alphafold/alphafold_params_2022-12-06.tar", "description": "Link to the Alphafold2 parameters", "fa_icon": "fas fa-link" }, @@ -286,9 +286,9 @@ "description": "Link to the PDV obsolete database", "fa_icon": "fas fa-link" }, - "uniclust30": { + "uniref30_alphafold2": { "type": "string", - "default": "https://storage.googleapis.com/alphafold-databases/casp14_versions/uniclust30_2018_08_hhsuite.tar.gz", + "default": "https://storage.googleapis.com/alphafold-databases/v2.3/UniRef30_2021_03.tar.gz", "description": "Link to the Uniclust30 database", "fa_icon": "fas fa-link" }, @@ -354,9 +354,9 @@ "description": "Path to the PDB mmCIF database", "fa_icon": "fas fa-folder-open" }, - "uniclust30_path": { + "uniref30_alphafold2_path": { "type": "string", - "description": "Path to the Uniclust30 database", + "description": "Path to the Uniref30 database", "fa_icon": "fas fa-folder-open" }, "uniref90_path": { @@ -412,7 +412,7 @@ "description": "Link to the Colabfold database", "fa_icon": "fas fa-folder-open" }, - "uniref30_path": { + "uniref30_colabfold_path": { "type": "string", "description": "Link to the UniRef30 database", "fa_icon": "fas fa-folder-open" diff --git a/subworkflows/local/prepare_alphafold2_dbs.nf b/subworkflows/local/prepare_alphafold2_dbs.nf index f4653019..ba583e54 100644 --- a/subworkflows/local/prepare_alphafold2_dbs.nf +++ b/subworkflows/local/prepare_alphafold2_dbs.nf @@ -36,11 +36,17 @@ workflow PREPARE_ALPHAFOLD2_DBS { ch_params = file( params.alphafold2_params_path ) ch_mgnify = file( params.mgnify_path ) +<<<<<<< Updated upstream ch_pdb70 = file( params.pdb70_path, type: 'dir' ) ch_mmcif_files = file( params.pdb_mmcif_path, type: 'dir' ) ch_mmcif_obsolete = file( params.pdb_mmcif_path, type: 'file' ) ch_mmcif = ch_mmcif_files + ch_mmcif_obsolete ch_uniclust30 = file( params.uniclust30_path, type: 'dir' ) +======= + ch_pdb70 = file( params.pdb70_path, type: 'any' ) + ch_mmcif = file( params.pdb_mmcif_path, type: 'any' ) + ch_uniref30 = file( params.uniref30_alphafold_path, type: 'any' ) +>>>>>>> Stashed changes ch_uniref90 = file( params.uniref90_path ) ch_pdb_seqres = file( params.pdb_seqres_path ) ch_uniprot = file( params.uniprot_path ) @@ -86,11 +92,11 @@ workflow PREPARE_ALPHAFOLD2_DBS { ch_mmcif = DOWNLOAD_PDBMMCIF.out.ch_db ch_versions = ch_versions.mix(DOWNLOAD_PDBMMCIF.out.versions) - ARIA2_UNICLUST30( - params.uniclust30 + ARIA2_UNIREF30( + params.uniref30 ) - ch_uniclust30 = ARIA2_UNICLUST30.out.db - ch_versions = ch_versions.mix(ARIA2_UNICLUST30.out.versions) + ch_uniref30 = ARIA2_UNICLUST30.out.db + ch_versions = ch_versions.mix(ARIA2_UNIref30.out.versions) ARIA2_UNIREF90( params.uniref90 @@ -127,7 +133,7 @@ workflow PREPARE_ALPHAFOLD2_DBS { mgnify = ch_mgnify pdb70 = ch_pdb70 pdb_mmcif = ch_mmcif - uniclust30 = ch_uniclust30 + uniref30 = ch_uniref30 uniref90 = ch_uniref90 pdb_seqres = ch_pdb_seqres uniprot = ch_uniprot diff --git a/workflows/alphafold2.nf b/workflows/alphafold2.nf index 67a53a60..72809296 100644 --- a/workflows/alphafold2.nf +++ b/workflows/alphafold2.nf @@ -117,7 +117,7 @@ workflow ALPHAFOLD2 { PREPARE_ALPHAFOLD2_DBS.out.mgnify, PREPARE_ALPHAFOLD2_DBS.out.pdb70, PREPARE_ALPHAFOLD2_DBS.out.pdb_mmcif, - PREPARE_ALPHAFOLD2_DBS.out.uniclust30, + PREPARE_ALPHAFOLD2_DBS.out.uniref30, PREPARE_ALPHAFOLD2_DBS.out.uniref90, PREPARE_ALPHAFOLD2_DBS.out.pdb_seqres, PREPARE_ALPHAFOLD2_DBS.out.uniprot, @@ -138,7 +138,7 @@ workflow ALPHAFOLD2 { PREPARE_ALPHAFOLD2_DBS.out.mgnify, PREPARE_ALPHAFOLD2_DBS.out.pdb70, PREPARE_ALPHAFOLD2_DBS.out.pdb_mmcif, - PREPARE_ALPHAFOLD2_DBS.out.uniclust30, + PREPARE_ALPHAFOLD2_DBS.out.uniref30, PREPARE_ALPHAFOLD2_DBS.out.uniref90, PREPARE_ALPHAFOLD2_DBS.out.pdb_seqres, PREPARE_ALPHAFOLD2_DBS.out.uniprot @@ -156,7 +156,7 @@ workflow ALPHAFOLD2 { PREPARE_ALPHAFOLD2_DBS.out.mgnify, PREPARE_ALPHAFOLD2_DBS.out.pdb70, PREPARE_ALPHAFOLD2_DBS.out.pdb_mmcif, - PREPARE_ALPHAFOLD2_DBS.out.uniclust30, + PREPARE_ALPHAFOLD2_DBS.out.uniref30, PREPARE_ALPHAFOLD2_DBS.out.uniref90, PREPARE_ALPHAFOLD2_DBS.out.pdb_seqres, PREPARE_ALPHAFOLD2_DBS.out.uniprot, From fe4b3520d6216e30ec77641ceeb5ec784da2ba5f Mon Sep 17 00:00:00 2001 From: l-mansouri Date: Wed, 3 May 2023 11:38:19 +0200 Subject: [PATCH 037/227] Added changes to run with new versions of colabfold (v.1.5.2) and alphafold (v.2.3.2) --- modules.json | 30 ++++++++++++++------ nextflow.config | 8 +++--- nextflow_schema.json | 2 +- subworkflows/local/prepare_alphafold2_dbs.nf | 6 ---- 4 files changed, 27 insertions(+), 19 deletions(-) diff --git a/modules.json b/modules.json index 4bc44595..d22eb6f5 100644 --- a/modules.json +++ b/modules.json @@ -8,41 +8,55 @@ "aria2": { "branch": "master", "git_sha": "c8e35eb2055c099720a75538d1b8adb3fb5a464c", - "installed_by": ["modules"] + "installed_by": [ + "modules" + ] }, "custom/dumpsoftwareversions": { "branch": "master", "git_sha": "76cc4938c1f6ea5c7d83fed1eeffc146787f9543", - "installed_by": ["modules"] + "installed_by": [ + "modules" + ] }, "gunzip": { "branch": "master", "git_sha": "c8e35eb2055c099720a75538d1b8adb3fb5a464c", - "installed_by": ["modules"] + "installed_by": [ + "modules" + ] }, "mmseqs/createindex": { "branch": "master", "git_sha": "c8e35eb2055c099720a75538d1b8adb3fb5a464c", - "installed_by": ["modules"] + "installed_by": [ + "modules" + ] }, "mmseqs/tsv2exprofiledb": { "branch": "master", "git_sha": "c8e35eb2055c099720a75538d1b8adb3fb5a464c", - "installed_by": ["modules"] + "installed_by": [ + "modules" + ] }, "multiqc": { "branch": "master", "git_sha": "f2d63bd5b68925f98f572eed70993d205cc694b7", - "installed_by": ["modules"] + "installed_by": [ + "modules" + ] }, "untar": { "branch": "master", "git_sha": "c8e35eb2055c099720a75538d1b8adb3fb5a464c", - "installed_by": ["modules"], + "installed_by": [ + "modules" + ], "patch": "modules/nf-core/untar/untar.diff" } } } } } -} +} \ No newline at end of file diff --git a/nextflow.config b/nextflow.config index b1aff145..ef681965 100644 --- a/nextflow.config +++ b/nextflow.config @@ -29,7 +29,7 @@ params { pdb70 = null pdb_mmcif = null pdb_obsolete = null - uniref30_alphafold = null + uniref30_alphafold2 = null uniref90 = null pdb_seqres = null uniprot_sprot = null @@ -42,7 +42,7 @@ params { mgnify_path = null pdb70_path = null pdb_mmcif_path = null - uniref30_alphafold_path = null + uniref30_alphafold2_path = null uniref90_path = null pdb_seqres_path = null uniprot_path = null @@ -60,11 +60,11 @@ params { // Colabfold links colabfold_db_link = null - uniref30_colabfold_path = null + uniref30_colabfold_link = null // Colabfold paths colabfold_db_path = null - uniref30_0_colabfold_path = null + uniref30_colabfold_path = null // MultiQC options multiqc_config = null diff --git a/nextflow_schema.json b/nextflow_schema.json index cf7fc87b..aecc6b20 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -388,7 +388,7 @@ "description": "Link to the Colabfold database", "fa_icon": "fas fa-link" }, - "uniref30": { + "uniref30_colabfold_link": { "type": "string", "default": "http://wwwuser.gwdg.de/~compbiol/colabfold/uniref30_2103.tar.gz", "description": "Link to the UniRef30 database", diff --git a/subworkflows/local/prepare_alphafold2_dbs.nf b/subworkflows/local/prepare_alphafold2_dbs.nf index ba583e54..49e7a844 100644 --- a/subworkflows/local/prepare_alphafold2_dbs.nf +++ b/subworkflows/local/prepare_alphafold2_dbs.nf @@ -36,17 +36,11 @@ workflow PREPARE_ALPHAFOLD2_DBS { ch_params = file( params.alphafold2_params_path ) ch_mgnify = file( params.mgnify_path ) -<<<<<<< Updated upstream ch_pdb70 = file( params.pdb70_path, type: 'dir' ) ch_mmcif_files = file( params.pdb_mmcif_path, type: 'dir' ) ch_mmcif_obsolete = file( params.pdb_mmcif_path, type: 'file' ) ch_mmcif = ch_mmcif_files + ch_mmcif_obsolete - ch_uniclust30 = file( params.uniclust30_path, type: 'dir' ) -======= - ch_pdb70 = file( params.pdb70_path, type: 'any' ) - ch_mmcif = file( params.pdb_mmcif_path, type: 'any' ) ch_uniref30 = file( params.uniref30_alphafold_path, type: 'any' ) ->>>>>>> Stashed changes ch_uniref90 = file( params.uniref90_path ) ch_pdb_seqres = file( params.pdb_seqres_path ) ch_uniprot = file( params.uniprot_path ) From 365aab34de6615519daa3822a53ff753090ca012 Mon Sep 17 00:00:00 2001 From: l-mansouri Date: Wed, 3 May 2023 11:54:09 +0200 Subject: [PATCH 038/227] Added changes to run with new versions of colabfold (v.1.5.2) and alphafold (v.2.3.2) --- modules.json | 30 ++++++-------------- nextflow_schema.json | 9 +++++- subworkflows/local/prepare_alphafold2_dbs.nf | 2 +- 3 files changed, 17 insertions(+), 24 deletions(-) diff --git a/modules.json b/modules.json index d22eb6f5..4bc44595 100644 --- a/modules.json +++ b/modules.json @@ -8,55 +8,41 @@ "aria2": { "branch": "master", "git_sha": "c8e35eb2055c099720a75538d1b8adb3fb5a464c", - "installed_by": [ - "modules" - ] + "installed_by": ["modules"] }, "custom/dumpsoftwareversions": { "branch": "master", "git_sha": "76cc4938c1f6ea5c7d83fed1eeffc146787f9543", - "installed_by": [ - "modules" - ] + "installed_by": ["modules"] }, "gunzip": { "branch": "master", "git_sha": "c8e35eb2055c099720a75538d1b8adb3fb5a464c", - "installed_by": [ - "modules" - ] + "installed_by": ["modules"] }, "mmseqs/createindex": { "branch": "master", "git_sha": "c8e35eb2055c099720a75538d1b8adb3fb5a464c", - "installed_by": [ - "modules" - ] + "installed_by": ["modules"] }, "mmseqs/tsv2exprofiledb": { "branch": "master", "git_sha": "c8e35eb2055c099720a75538d1b8adb3fb5a464c", - "installed_by": [ - "modules" - ] + "installed_by": ["modules"] }, "multiqc": { "branch": "master", "git_sha": "f2d63bd5b68925f98f572eed70993d205cc694b7", - "installed_by": [ - "modules" - ] + "installed_by": ["modules"] }, "untar": { "branch": "master", "git_sha": "c8e35eb2055c099720a75538d1b8adb3fb5a464c", - "installed_by": [ - "modules" - ], + "installed_by": ["modules"], "patch": "modules/nf-core/untar/untar.diff" } } } } } -} \ No newline at end of file +} diff --git a/nextflow_schema.json b/nextflow_schema.json index aecc6b20..26ba21dd 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -115,7 +115,14 @@ "type": "string", "default": "alphafold2_ptm", "description": "Model preset for 'colabfold' mode", - "enum": ["auto", "alphafold2", "alphafold2_ptm", "alphafold2_multimer_v1", "alphafold2_multimer_v2", "alphafold2_multimer_v3"], + "enum": [ + "auto", + "alphafold2", + "alphafold2_ptm", + "alphafold2_multimer_v1", + "alphafold2_multimer_v2", + "alphafold2_multimer_v3" + ], "fa_icon": "fas fa-stream" }, "num_recycle": { diff --git a/subworkflows/local/prepare_alphafold2_dbs.nf b/subworkflows/local/prepare_alphafold2_dbs.nf index 49e7a844..6419d3dc 100644 --- a/subworkflows/local/prepare_alphafold2_dbs.nf +++ b/subworkflows/local/prepare_alphafold2_dbs.nf @@ -40,7 +40,7 @@ workflow PREPARE_ALPHAFOLD2_DBS { ch_mmcif_files = file( params.pdb_mmcif_path, type: 'dir' ) ch_mmcif_obsolete = file( params.pdb_mmcif_path, type: 'file' ) ch_mmcif = ch_mmcif_files + ch_mmcif_obsolete - ch_uniref30 = file( params.uniref30_alphafold_path, type: 'any' ) + ch_uniref30 = file( params.uniref30_alphafold2_path, type: 'any' ) ch_uniref90 = file( params.uniref90_path ) ch_pdb_seqres = file( params.pdb_seqres_path ) ch_uniprot = file( params.uniprot_path ) From 282acb94bf37c1d8313f2b6c7b930dba52e3ca20 Mon Sep 17 00:00:00 2001 From: Leila Mansouri Date: Wed, 3 May 2023 15:02:34 +0200 Subject: [PATCH 039/227] changed params name in the stub run --- subworkflows/local/prepare_colabfold_dbs.nf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/subworkflows/local/prepare_colabfold_dbs.nf b/subworkflows/local/prepare_colabfold_dbs.nf index ef1f45e4..0ff6f70e 100644 --- a/subworkflows/local/prepare_colabfold_dbs.nf +++ b/subworkflows/local/prepare_colabfold_dbs.nf @@ -21,7 +21,7 @@ workflow PREPARE_COLABFOLD_DBS { ch_params = file( params.colabfold_alphafold2_params_path, type: 'any' ) if (params.colabfold_server == 'local') { ch_colabfold_db = file( params.colabfold_db_path, type: 'any' ) - ch_uniref30 = file( params.uniref30_path , type: 'any' ) + ch_uniref30 = file( params.uniref30_colabfold_path , type: 'any' ) } } else { From 3b1c5a9d787983a3e846847faba466a7d2df9a1c Mon Sep 17 00:00:00 2001 From: Leila Mansouri Date: Wed, 3 May 2023 15:24:58 +0200 Subject: [PATCH 040/227] specified where to fetch the ubuntu docker containers --- modules/local/combine_uniprot.nf | 2 +- modules/local/multifasta_to_csv.nf | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/local/combine_uniprot.nf b/modules/local/combine_uniprot.nf index 6bc68e64..0d8e9159 100644 --- a/modules/local/combine_uniprot.nf +++ b/modules/local/combine_uniprot.nf @@ -4,7 +4,7 @@ process COMBINE_UNIPROT { conda "conda-forge::sed=4.7" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? 'https://depot.galaxyproject.org/singularity/ubuntu:20.04' : - 'ubuntu:20.04' }" + 'docker.io/ubuntu:20.04' }" input: path uniprot_sprot diff --git a/modules/local/multifasta_to_csv.nf b/modules/local/multifasta_to_csv.nf index ee54f872..87b77412 100644 --- a/modules/local/multifasta_to_csv.nf +++ b/modules/local/multifasta_to_csv.nf @@ -4,7 +4,7 @@ process MULTIFASTA_TO_CSV { container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? 'https://depot.galaxyproject.org/singularity/ubuntu:20.04' : - 'ubuntu:20.04' }" + 'docker.io/ubuntu:20.04' }" input: tuple val(meta), path(fasta) From bdf53ed5ce9322afa788899285a7ddb7959effd0 Mon Sep 17 00:00:00 2001 From: l-mansouri Date: Thu, 4 May 2023 14:54:16 +0200 Subject: [PATCH 041/227] changed container names to be consisten with pipeline realease version --- modules/local/colabfold_batch.nf | 4 ++-- modules/local/run_alphafold2.nf | 4 ++-- modules/local/run_alphafold2_msa.nf | 4 ++-- modules/local/run_alphafold2_pred.nf | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/modules/local/colabfold_batch.nf b/modules/local/colabfold_batch.nf index b693bb65..101748b6 100644 --- a/modules/local/colabfold_batch.nf +++ b/modules/local/colabfold_batch.nf @@ -3,8 +3,8 @@ process COLABFOLD_BATCH { label 'process_medium' container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'docker://lmansouri/colabfold:no_download' : - 'lmansouri/colabfold:no_download' }" + 'docker://lmansouri/colabfold:1.1.0' : + 'lmansouri/colabfold:1.1.0' }" input: tuple val(meta), path(fasta) diff --git a/modules/local/run_alphafold2.nf b/modules/local/run_alphafold2.nf index 8013beb2..9e5cb896 100644 --- a/modules/local/run_alphafold2.nf +++ b/modules/local/run_alphafold2.nf @@ -6,8 +6,8 @@ process RUN_ALPHAFOLD2 { label 'process_medium' container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'docker://lmansouri/alphafold-standard:2.0.0' : - 'lmansouri/alphafold-standard:2.0.0' }" + 'docker://lmansouri/alphafold-standard:1.1.0' : + 'lmansouri/alphafold-standard:1.1.0' }" input: tuple val(meta), path(fasta) diff --git a/modules/local/run_alphafold2_msa.nf b/modules/local/run_alphafold2_msa.nf index 164b35d1..117c32a0 100644 --- a/modules/local/run_alphafold2_msa.nf +++ b/modules/local/run_alphafold2_msa.nf @@ -6,8 +6,8 @@ process RUN_ALPHAFOLD2_MSA { label 'process_medium' container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'docker://lmansouri/alphafold-msa:2.0.0' : - 'lmansouri/alphafold-msa:2.0.0' }" + 'docker://lmansouri/alphafold-msa:1.1.0' : + 'lmansouri/alphafold-msa:1.1.0' }" input: diff --git a/modules/local/run_alphafold2_pred.nf b/modules/local/run_alphafold2_pred.nf index 7ec1c6e2..d6385250 100644 --- a/modules/local/run_alphafold2_pred.nf +++ b/modules/local/run_alphafold2_pred.nf @@ -6,8 +6,8 @@ process RUN_ALPHAFOLD2_PRED { label 'process_medium' container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'docker://lmansouri/alphafold-split:2.0.0' : - 'lmansouri/alphafold-split:2.0.0' }" + 'docker://lmansouri/alphafold-split:1.1.0' : + 'lmansouri/alphafold-split:1.1.0' }" input: tuple val(meta), path(fasta) From 9180821e4a7743a2a4bfe47361eb6f985b8635bf Mon Sep 17 00:00:00 2001 From: l-mansouri Date: Fri, 5 May 2023 10:29:00 +0200 Subject: [PATCH 042/227] updated the name of the containers to the nf-core ones --- modules/local/colabfold_batch.nf | 4 ++-- modules/local/run_alphafold2.nf | 4 ++-- modules/local/run_alphafold2_msa.nf | 4 ++-- modules/local/run_alphafold2_pred.nf | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/modules/local/colabfold_batch.nf b/modules/local/colabfold_batch.nf index 101748b6..20ce2904 100644 --- a/modules/local/colabfold_batch.nf +++ b/modules/local/colabfold_batch.nf @@ -3,8 +3,8 @@ process COLABFOLD_BATCH { label 'process_medium' container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'docker://lmansouri/colabfold:1.1.0' : - 'lmansouri/colabfold:1.1.0' }" + 'docker://nfcore/proteinfold_colabfold:1.1.0' : + 'nfcore/proteinfold_colabfold:1.1.0' }" input: tuple val(meta), path(fasta) diff --git a/modules/local/run_alphafold2.nf b/modules/local/run_alphafold2.nf index 9e5cb896..5cc0c813 100644 --- a/modules/local/run_alphafold2.nf +++ b/modules/local/run_alphafold2.nf @@ -6,8 +6,8 @@ process RUN_ALPHAFOLD2 { label 'process_medium' container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'docker://lmansouri/alphafold-standard:1.1.0' : - 'lmansouri/alphafold-standard:1.1.0' }" + 'docker://nfcore/proteinfold_alphafold2_standard:1.1.0' : + 'nfcore/proteinfold_alphafold2_standard:1.1.0' }" input: tuple val(meta), path(fasta) diff --git a/modules/local/run_alphafold2_msa.nf b/modules/local/run_alphafold2_msa.nf index 117c32a0..c2197ca5 100644 --- a/modules/local/run_alphafold2_msa.nf +++ b/modules/local/run_alphafold2_msa.nf @@ -6,8 +6,8 @@ process RUN_ALPHAFOLD2_MSA { label 'process_medium' container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'docker://lmansouri/alphafold-msa:1.1.0' : - 'lmansouri/alphafold-msa:1.1.0' }" + 'docker://nfcore/proteinfold_alphafold2_msa:1.1.0' : + 'nfcore/proteinfold_alphafold2_msa:1.1.0' }" input: diff --git a/modules/local/run_alphafold2_pred.nf b/modules/local/run_alphafold2_pred.nf index d6385250..e76432e9 100644 --- a/modules/local/run_alphafold2_pred.nf +++ b/modules/local/run_alphafold2_pred.nf @@ -6,8 +6,8 @@ process RUN_ALPHAFOLD2_PRED { label 'process_medium' container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'docker://lmansouri/alphafold-split:1.1.0' : - 'lmansouri/alphafold-split:1.1.0' }" + 'docker://nfcore/proteinfold_alphafold2_split:1.1.0' : + 'nfcore/proteinfold_alphafold2_split:1.1.0' }" input: tuple val(meta), path(fasta) From 07f15512aa159aba3853b6e3fd86e40fd1e248ca Mon Sep 17 00:00:00 2001 From: l-mansouri Date: Fri, 5 May 2023 10:31:56 +0200 Subject: [PATCH 043/227] updated the name of the containers to the nf-core ones --- modules/local/mmseqs_colabfoldsearch.nf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/local/mmseqs_colabfoldsearch.nf b/modules/local/mmseqs_colabfoldsearch.nf index 6ecbb59e..6fb754e5 100644 --- a/modules/local/mmseqs_colabfoldsearch.nf +++ b/modules/local/mmseqs_colabfoldsearch.nf @@ -3,8 +3,8 @@ process MMSEQS_COLABFOLDSEARCH { label 'process_high_memory' container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'docker://lmansouri/colabfold:no_download' : - 'lmansouri/colabfold:no_download' }" + 'docker://nfcore/proteinfold_colabfold:1.1.0' : + 'nfcore/proteinfold_colabfold:1.1.0' }" input: From 9e43234c1a372f3df159bb1f33e502a0765e97df Mon Sep 17 00:00:00 2001 From: l-mansouri Date: Fri, 5 May 2023 11:28:08 +0200 Subject: [PATCH 044/227] updated redfundant path --- conf/dbs.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/dbs.config b/conf/dbs.config index ea668852..d06329e5 100644 --- a/conf/dbs.config +++ b/conf/dbs.config @@ -29,7 +29,7 @@ params { mgnify_path = "${params.alphafold2_db}/mgnify/*" pdb70_path = "${params.alphafold2_db}/pdb70/**" pdb_mmcif_path = "${params.alphafold2_db}/pdb_mmcif/*" - uniref30_alphafold2_path = "${params.alphafold2_db}/uniref30/**" + uniref30_alphafold2_path = "${params.alphafold2_db}/uniref30/*" uniref90_path = "${params.alphafold2_db}/uniref90/*" pdb_seqres_path = "${params.alphafold2_db}/pdb_seqres/*" uniprot_path = "${params.alphafold2_db}/uniprot/*" From d140de2deb5fa13ea629a92c8bb7dee66e486fbc Mon Sep 17 00:00:00 2001 From: Leila Mansouri <48998340+l-mansouri@users.noreply.github.com> Date: Fri, 5 May 2023 10:55:09 +0200 Subject: [PATCH 045/227] Update modules/local/combine_uniprot.nf Co-authored-by: Jose Espinosa-Carrasco --- modules/local/combine_uniprot.nf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/local/combine_uniprot.nf b/modules/local/combine_uniprot.nf index 0d8e9159..c658c2f1 100644 --- a/modules/local/combine_uniprot.nf +++ b/modules/local/combine_uniprot.nf @@ -4,7 +4,7 @@ process COMBINE_UNIPROT { conda "conda-forge::sed=4.7" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? 'https://depot.galaxyproject.org/singularity/ubuntu:20.04' : - 'docker.io/ubuntu:20.04' }" + 'docker.io/library/ubuntu:20.04' }" input: path uniprot_sprot From 0cb6d86e0bd7171da7dd4ec66da83b18e991bc89 Mon Sep 17 00:00:00 2001 From: Leila Mansouri <48998340+l-mansouri@users.noreply.github.com> Date: Fri, 5 May 2023 10:58:45 +0200 Subject: [PATCH 046/227] Update modules/local/multifasta_to_csv.nf Co-authored-by: Jose Espinosa-Carrasco --- modules/local/multifasta_to_csv.nf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/local/multifasta_to_csv.nf b/modules/local/multifasta_to_csv.nf index 87b77412..09824884 100644 --- a/modules/local/multifasta_to_csv.nf +++ b/modules/local/multifasta_to_csv.nf @@ -4,7 +4,7 @@ process MULTIFASTA_TO_CSV { container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? 'https://depot.galaxyproject.org/singularity/ubuntu:20.04' : - 'docker.io/ubuntu:20.04' }" + 'docker.io/library/ubuntu:20.04' }" input: tuple val(meta), path(fasta) From 8e1c52c7f497d144766bad6bd604ec6774535444 Mon Sep 17 00:00:00 2001 From: Athanasios Baltzis Date: Fri, 28 Apr 2023 19:00:43 +0200 Subject: [PATCH 047/227] Create esmfold module and workflow --- modules/local/run_esmfold.nf | 49 +++++++++++ workflows/esmfold.nf | 166 +++++++++++++++++++++++++++++++++++ 2 files changed, 215 insertions(+) create mode 100644 modules/local/run_esmfold.nf create mode 100644 workflows/esmfold.nf diff --git a/modules/local/run_esmfold.nf b/modules/local/run_esmfold.nf new file mode 100644 index 00000000..f48e2db9 --- /dev/null +++ b/modules/local/run_esmfold.nf @@ -0,0 +1,49 @@ +process RUN_ESMFOLD { + tag "$meta.id" + label 'process_medium' + + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'docker://athbaltzis/esmfold:v0.1' : + 'athbaltzis/esmfold:v0.1' }" + + input: + tuple val(meta), path(fasta) + + output: + path ("${fasta.baseName}*.pdb"), emit: pdb + path ("${fasta.baseName}_plddt_mqc.tsv"), emit: multiqc + path "versions.yml", emit: versions + + when: + task.ext.when == null || task.ext.when + + script: + def args = task.ext.args ?: '' + def VERSION = '1.0.3' // WARN: Version information not provided by tool on CLI. Please update this string when bumping container versions. + + """ + esm-fold \ + -i ${fasta} \ + -o \$PWD \ + $args + + awk '{print \$6"\\t"\$11}' "${fasta.baseName}"*.pdb | uniq > "${fasta.baseName}"_plddt_mqc.tsv + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + esm-fold: $VERSION + END_VERSIONS + """ + + stub: + def VERSION = '1.0.3' // WARN: Version information not provided by tool on CLI. Please update this string when bumping container versions. + """ + touch ./"${fasta.baseName}".pdb + touch ./"${fasta.baseName}"_mqc.tsv + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + esm-fold: $VERSION + END_VERSIONS + """ +} diff --git a/workflows/esmfold.nf b/workflows/esmfold.nf new file mode 100644 index 00000000..6ff82ffa --- /dev/null +++ b/workflows/esmfold.nf @@ -0,0 +1,166 @@ +/* +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + VALIDATE INPUTS +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +*/ + +def summary_params = NfcoreSchema.paramsSummaryMap(workflow, params) + +// Validate input parameters +WorkflowEsmfold.initialise(params, log) + +// Check input path parameters to see if they exist +def checkPathParamList = [ + params.input +] +for (param in checkPathParamList) { if (param) { file(param, checkIfExists: true) } } + +// Check mandatory parameters +if (params.input) { ch_input = file(params.input) } else { exit 1, 'Input file not specified!' } + +/* +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CONFIG FILES +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +*/ + +ch_multiqc_config = Channel.fromPath("$projectDir/assets/multiqc_config.yml", checkIfExists: true) +ch_multiqc_custom_config = params.multiqc_config ? Channel.fromPath( params.multiqc_config, checkIfExists: true ) : Channel.empty() +ch_multiqc_logo = params.multiqc_logo ? Channel.fromPath( params.multiqc_logo, checkIfExists: true ) : Channel.empty() +ch_multiqc_custom_methods_description = params.multiqc_methods_description ? file(params.multiqc_methods_description, checkIfExists: true) : file("$projectDir/assets/methods_description_template.yml", checkIfExists: true) + +/* +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + IMPORT LOCAL MODULES/SUBWORKFLOWS +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +*/ + +// +// SUBWORKFLOW: Consisting of a mix of local and nf-core/modules +// +include { INPUT_CHECK } from '../subworkflows/local/input_check' +//include { PREPARE_COLABFOLD_DBS } from '../subworkflows/local/prepare_colabfold_dbs' + +// +// MODULE: Local to the pipeline +// +include { RUN_ESMFOLD } from '../modules/local/run_esmfold' +include { MULTIFASTA_TO_CSV } from '../modules/local/multifasta_to_csv' + +/* +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + IMPORT NF-CORE MODULES/SUBWORKFLOWS +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +*/ + +// +// MODULE: Installed directly from nf-core/modules +// +include { MULTIQC } from '../modules/nf-core/multiqc/main' +include { CUSTOM_DUMPSOFTWAREVERSIONS } from '../modules/nf-core/custom/dumpsoftwareversions/main' + +/* +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + RUN MAIN WORKFLOW +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +*/ + +// Info required for completion email and summary +def multiqc_report = [] + +workflow ESMFOLD { + + ch_versions = Channel.empty() + + // + // SUBWORKFLOW: Read in samplesheet, validate and stage input files + // + INPUT_CHECK ( + ch_input + ) + ch_versions = ch_versions.mix(INPUT_CHECK.out.versions) + + PREPARE_ESMFOLD( ) + ch_versions = ch_versions.mix(PREPARE_ESMFOLD.out.versions) + + // + // MODULE: Run colabfold + // + if (params.colabfold_model_preset != 'AlphaFold2-ptm') { + MULTIFASTA_TO_CSV( + INPUT_CHECK.out.fastas + ) + ch_versions = ch_versions.mix(MULTIFASTA_TO_CSV.out.versions) + RUN_ESMFOLD( + MULTIFASTA_TO_CSV.out.input_csv, + params.colabfold_model_preset, + PREPARE_COLABFOLD_DBS.out.params, + [], + [], + params.num_recycle + ) + ch_versions = ch_versions.mix(RUN_ESMFOLD.out.versions) + } else { + RUN_ESMFOLD( + INPUT_CHECK.out.fastas, + params.colabfold_model_preset, + PREPARE_COLABFOLD_DBS.out.params, + [], + [], + params.num_recycle + ) + ch_versions = ch_versions.mix(COLABFOLD_BATCH.out.versions) + } + + // + // MODULE: Pipeline reporting + // + CUSTOM_DUMPSOFTWAREVERSIONS ( + ch_versions.unique().collectFile(name: 'collated_versions.yml') + ) + + // + // MODULE: MultiQC + // + workflow_summary = WorkflowColabfold.paramsSummaryMultiqc(workflow, summary_params) + ch_workflow_summary = Channel.value(workflow_summary) + + methods_description = WorkflowColabfold.methodsDescriptionText(workflow, ch_multiqc_custom_methods_description) + ch_methods_description = Channel.value(methods_description) + + ch_multiqc_files = Channel.empty() + ch_multiqc_files = ch_multiqc_files.mix(ch_workflow_summary.collectFile(name: 'workflow_summary_mqc.yaml')) + ch_multiqc_files = ch_multiqc_files.mix(ch_methods_description.collectFile(name: 'methods_description_mqc.yaml')) + ch_multiqc_files = ch_multiqc_files.mix(CUSTOM_DUMPSOFTWAREVERSIONS.out.mqc_yml.collect()) + ch_multiqc_files = ch_multiqc_files.mix(COLABFOLD_BATCH.out.multiqc.collect()) + + MULTIQC ( + ch_multiqc_files.collect(), + ch_multiqc_config.toList(), + ch_multiqc_custom_config.toList(), + ch_multiqc_logo.toList() + ) + multiqc_report = MULTIQC.out.report.toList() +} + +/* +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + COMPLETION EMAIL AND SUMMARY +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +*/ + +workflow.onComplete { + if (params.email || params.email_on_fail) { + NfcoreTemplate.email(workflow, params, summary_params, projectDir, log, multiqc_report) + } + NfcoreTemplate.summary(workflow, params, log) + if (params.hook_url) { + NfcoreTemplate.IM_notification(workflow, params, summary_params, projectDir, log) + } +} + +/* +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + THE END +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +*/ From 30aa31e96a309ade50ce9cd0dc30454b1e00a955 Mon Sep 17 00:00:00 2001 From: Athanasios Baltzis Date: Tue, 2 May 2023 18:32:59 +0200 Subject: [PATCH 048/227] Add WorkflowEsmofld.groovy --- lib/WorkflowEsmfold.groovy | 58 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 lib/WorkflowEsmfold.groovy diff --git a/lib/WorkflowEsmfold.groovy b/lib/WorkflowEsmfold.groovy new file mode 100644 index 00000000..8b0d755b --- /dev/null +++ b/lib/WorkflowEsmfold.groovy @@ -0,0 +1,58 @@ +// +// This file holds several functions specific to the workflow/proteinfold.nf in the nf-core/proteinfold pipeline +// + +import groovy.text.SimpleTemplateEngine + +class WorkflowEsmfold { + + // + // Check and validate parameters + // + public static void initialise(params, log) { + } + + // + // Get workflow summary for MultiQC + // + public static String paramsSummaryMultiqc(workflow, summary) { + String summary_section = '' + for (group in summary.keySet()) { + def group_params = summary.get(group) // This gets the parameters of that particular group + if (group_params) { + summary_section += "

$group

\n" + summary_section += "
\n" + for (param in group_params.keySet()) { + summary_section += "
$param
${group_params.get(param) ?: 'N/A'}
\n" + } + summary_section += "
\n" + } + } + + String yaml_file_text = "id: '${workflow.manifest.name.replace('/','-')}-summary'\n" + yaml_file_text += "description: ' - this information is collected when the pipeline is started.'\n" + yaml_file_text += "section_name: '${workflow.manifest.name} Workflow Summary'\n" + yaml_file_text += "section_href: 'https://github.com/${workflow.manifest.name}'\n" + yaml_file_text += "plot_type: 'html'\n" + yaml_file_text += "data: |\n" + yaml_file_text += "${summary_section}" + return yaml_file_text + } + + public static String methodsDescriptionText(run_workflow, mqc_methods_yaml) { + // Convert to a named map so can be used as with familar NXF ${workflow} variable syntax in the MultiQC YML file + def meta = [:] + meta.workflow = run_workflow.toMap() + meta["manifest_map"] = run_workflow.manifest.toMap() + + meta["doi_text"] = meta.manifest_map.doi ? "(doi: ${meta.manifest_map.doi})" : "" + meta["nodoi_text"] = meta.manifest_map.doi ? "": "
  • If available, make sure to update the text to include the Zenodo DOI of version of the pipeline used.
  • " + + def methods_text = mqc_methods_yaml.text + + def engine = new SimpleTemplateEngine() + def description_html = engine.createTemplate(methods_text).make(meta) + + return description_html + } +} From 1056286c33d36755a3c7cd51984b11ac018de80b Mon Sep 17 00:00:00 2001 From: Athanasios Baltzis Date: Tue, 2 May 2023 18:46:53 +0200 Subject: [PATCH 049/227] Add modules_esmfold config and fix arguments --- conf/modules_esmfold.config | 23 +++++++++++++++++++++++ modules/local/run_esmfold.nf | 2 ++ 2 files changed, 25 insertions(+) create mode 100644 conf/modules_esmfold.config diff --git a/conf/modules_esmfold.config b/conf/modules_esmfold.config new file mode 100644 index 00000000..230dd343 --- /dev/null +++ b/conf/modules_esmfold.config @@ -0,0 +1,23 @@ +/* +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Config file for defining DSL2 per module options and publishing paths +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Available keys to override module options: + ext.args = Additional arguments appended to command in module. + ext.args2 = Second set of arguments appended to command in module (multi-tool modules). + ext.args3 = Third set of arguments appended to command in module (multi-tool modules). + ext.prefix = File name prefix for output files. +---------------------------------------------------------------------------------------- +*/ + +process { + withName: 'RUN_ESMFOLD' { + ext.args = {params.use_gpu ? '' : '--cpu-only'} + publishDir = [ + path: { "${params.outdir}/esmfold" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename }, + pattern: '*.*' + ] + } +} diff --git a/modules/local/run_esmfold.nf b/modules/local/run_esmfold.nf index f48e2db9..6f3607ec 100644 --- a/modules/local/run_esmfold.nf +++ b/modules/local/run_esmfold.nf @@ -8,6 +8,7 @@ process RUN_ESMFOLD { input: tuple val(meta), path(fasta) + path ('./checkpoint') output: path ("${fasta.baseName}*.pdb"), emit: pdb @@ -25,6 +26,7 @@ process RUN_ESMFOLD { esm-fold \ -i ${fasta} \ -o \$PWD \ + -m ./checkpoints $args awk '{print \$6"\\t"\$11}' "${fasta.baseName}"*.pdb | uniq > "${fasta.baseName}"_plddt_mqc.tsv From 410a8c2898f6181db0993edda092b33185266293 Mon Sep 17 00:00:00 2001 From: Athanasios Baltzis Date: Tue, 2 May 2023 19:33:13 +0200 Subject: [PATCH 050/227] Update nextflow_schema and nextflow.config --- modules/local/run_esmfold.nf | 4 +++- nextflow.config | 5 ++++- nextflow_schema.json | 22 +++++++++++++++++++++- 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/modules/local/run_esmfold.nf b/modules/local/run_esmfold.nf index 6f3607ec..39837091 100644 --- a/modules/local/run_esmfold.nf +++ b/modules/local/run_esmfold.nf @@ -9,6 +9,7 @@ process RUN_ESMFOLD { input: tuple val(meta), path(fasta) path ('./checkpoint') + val numRec output: path ("${fasta.baseName}*.pdb"), emit: pdb @@ -26,7 +27,8 @@ process RUN_ESMFOLD { esm-fold \ -i ${fasta} \ -o \$PWD \ - -m ./checkpoints + -m ./checkpoints \ + --num-recycles ${numRec} \ $args awk '{print \$6"\\t"\$11}' "${fasta.baseName}"*.pdb | uniq > "${fasta.baseName}"_plddt_mqc.tsv diff --git a/nextflow.config b/nextflow.config index ef681965..20faf1c9 100644 --- a/nextflow.config +++ b/nextflow.config @@ -11,7 +11,7 @@ params { // Input options input = null - mode = 'alphafold2' // {alphafold2, colabfold} + mode = 'alphafold2' // {alphafold2, colabfold, esmfold} use_gpu = false // Alphafold2 parameters @@ -66,6 +66,9 @@ params { colabfold_db_path = null uniref30_colabfold_path = null + // Esmfold parameters + num_recycles = 4 + // MultiQC options multiqc_config = null multiqc_title = null diff --git a/nextflow_schema.json b/nextflow_schema.json index 26ba21dd..64e44227 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -32,7 +32,7 @@ "type": "string", "default": "alphafold2", "description": "Specifies the mode in which the pipeline will be run", - "enum": ["alphafold2", "colabfold"], + "enum": ["alphafold2", "colabfold", "esmfold"], "fa_icon": "fas fa-cogs" }, "use_gpu": { @@ -162,6 +162,26 @@ } } }, + "esmfold_options": { + "title": "Esmfold options", + "type": "object", + "fa_icon": "fas fa-coins", + "description": "Esmfold options.", + "properties": { + "esmfold_db": { + "type": "string", + "description": "Specifies the PARAMS path used by 'esmfold' mode", + "fa_icon": "fas fa-folder-open" + }, + "num_recycles": { + "type": "integer", + "default": 4, + "description": "Specifies the number of recycles used by Esmfold", + "enum": ["webserver", "local"], + "fa_icon": "fas fa-server" + } + } + }, "institutional_config_options": { "title": "Institutional config options", "type": "object", From b2a53d6f224af0f0bdd6f05a66a54974e2353705 Mon Sep 17 00:00:00 2001 From: Athanasios Baltzis Date: Tue, 2 May 2023 20:12:23 +0200 Subject: [PATCH 051/227] Create ESMFOLD workflow --- conf/dbs.config | 8 +++++ nextflow.config | 9 ++++++ nextflow_schema.json | 6 ++++ subworkflows/local/prepare_esmfold_dbs.nf | 36 +++++++++++++++++++++++ workflows/esmfold.nf | 29 ++++++++---------- 5 files changed, 72 insertions(+), 16 deletions(-) create mode 100644 subworkflows/local/prepare_esmfold_dbs.nf diff --git a/conf/dbs.config b/conf/dbs.config index d06329e5..6a7156dd 100644 --- a/conf/dbs.config +++ b/conf/dbs.config @@ -47,4 +47,12 @@ params { "alphafold2_multimer_v3" : "alphafold_params_colab_2022-12-06", "alphafold2_ptm" : "alphafold_params_2021-07-14" ] + + // Esmfold links + esmfold_3B_v1 = 'https://dl.fbaipublicfiles.com/fair-esm/models/esmfold_3B_v1.pt' + esm2_t36_3B_UR50D = 'https://dl.fbaipublicfiles.com/fair-esm/models/esm2_t36_3B_UR50D.pt' + esm2_t36_3B_UR50D_contact_regression = 'https://dl.fbaipublicfiles.com/fair-esm/regression/esm2_t36_3B_UR50D-contact-regression.pt' + + // Esmfold paths + esmfold_params_path = "${params.esmfold_db}/esmfold_params/*" } diff --git a/nextflow.config b/nextflow.config index 20faf1c9..339368f4 100644 --- a/nextflow.config +++ b/nextflow.config @@ -67,8 +67,17 @@ params { uniref30_colabfold_path = null // Esmfold parameters + esmfold_model_preset = "monomer" num_recycles = 4 + // Esmfold links + esmfold_3B_v1 = null + esm2_t36_3B_UR50D = null + esm2_t36_3B_UR50D_contact_regression = null + + // Esmfold paths + esmfold_params_path = null + // MultiQC options multiqc_config = null multiqc_title = null diff --git a/nextflow_schema.json b/nextflow_schema.json index 64e44227..58f95d40 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -179,6 +179,12 @@ "description": "Specifies the number of recycles used by Esmfold", "enum": ["webserver", "local"], "fa_icon": "fas fa-server" + }, + "esmfold_model_preset": { + "type": "string", + "description": "Specifies whether is a 'monomer' or 'multimer' prediction", + "enum": ["monomer","multimer"], + "fa_icon": "fas fa-stream" } } }, diff --git a/subworkflows/local/prepare_esmfold_dbs.nf b/subworkflows/local/prepare_esmfold_dbs.nf new file mode 100644 index 00000000..db8c39d7 --- /dev/null +++ b/subworkflows/local/prepare_esmfold_dbs.nf @@ -0,0 +1,36 @@ +// +// Download all the required Esmfold parameters +// + +include { ARIA2 as ARIA2_ESMFOLD_3B_V1 } from '../../modules/nf-core/aria2/main' +include { ARIA2 as ARIA2_ESM2_T36_3B_UR50D } from '../../modules/nf-core/aria2/main' +include { ARIA2 as ARIA2_ESM2_T36_3B_UR50D_CONTACT_REGRESSION } from '../../modules/nf-core/aria2/main' + + +workflow PREPARE_ESMFOLD_DBS { + main: + ch_versions = Channel.empty() + + + if (params.esmfold_db) { + ch_params = file( params.esmfold_params_path ) + } + else { + ARIA2_ESMFOLD_3B_V1 ( + params.esmfold_3B_v1 + ) + ARIA2_ESM2_T36_3B_UR50D ( + params.esm2_t36_3B_UR50D + ) + ARIA2_ESM2_T36_3B_UR50D_CONTACT_REGRESSION ( + params.esm2_t36_3B_UR50D_contact_regression + ) + ch_params = ARIA2_ESMFOLD_3B_V1.out.downloaded_file.mix(ARIA2_ESM2_T36_3B_UR50D.out.downloaded_file,ARIA2_ESM2_T36_3B_UR50D_CONTACT_REGRESSION.out.downloaded_file) + ch_versions = ch_versions.mix(ARIA2_ESMFOLD_3B_V1.out.versions) + + } + + emit: + params = ch_params + versions = ch_versions +} diff --git a/workflows/esmfold.nf b/workflows/esmfold.nf index 6ff82ffa..7381ff9b 100644 --- a/workflows/esmfold.nf +++ b/workflows/esmfold.nf @@ -11,7 +11,8 @@ WorkflowEsmfold.initialise(params, log) // Check input path parameters to see if they exist def checkPathParamList = [ - params.input + params.input, + params.esmfold_db ] for (param in checkPathParamList) { if (param) { file(param, checkIfExists: true) } } @@ -39,7 +40,7 @@ ch_multiqc_custom_methods_description = params.multiqc_methods_description ? fil // SUBWORKFLOW: Consisting of a mix of local and nf-core/modules // include { INPUT_CHECK } from '../subworkflows/local/input_check' -//include { PREPARE_COLABFOLD_DBS } from '../subworkflows/local/prepare_colabfold_dbs' +include { PREPARE_ESMFOLD_DBS } from '../subworkflows/local/prepare_esmfold_dbs' // // MODULE: Local to the pipeline @@ -84,32 +85,28 @@ workflow ESMFOLD { ch_versions = ch_versions.mix(PREPARE_ESMFOLD.out.versions) // - // MODULE: Run colabfold + // MODULE: Run esmfold // - if (params.colabfold_model_preset != 'AlphaFold2-ptm') { + if (params.esmfold_model_preset != 'monomer') { MULTIFASTA_TO_CSV( INPUT_CHECK.out.fastas ) ch_versions = ch_versions.mix(MULTIFASTA_TO_CSV.out.versions) RUN_ESMFOLD( MULTIFASTA_TO_CSV.out.input_csv, - params.colabfold_model_preset, - PREPARE_COLABFOLD_DBS.out.params, - [], - [], - params.num_recycle + PREPARE_ESMFOLD_DBS.out.params, + params.num_recycles ) ch_versions = ch_versions.mix(RUN_ESMFOLD.out.versions) } else { RUN_ESMFOLD( INPUT_CHECK.out.fastas, - params.colabfold_model_preset, - PREPARE_COLABFOLD_DBS.out.params, + PREPARE_ESMFOLD_DBS.out.params, [], [], - params.num_recycle + params.num_recycles ) - ch_versions = ch_versions.mix(COLABFOLD_BATCH.out.versions) + ch_versions = ch_versions.mix(RUN_ESMFOLD.out.versions) } // @@ -122,17 +119,17 @@ workflow ESMFOLD { // // MODULE: MultiQC // - workflow_summary = WorkflowColabfold.paramsSummaryMultiqc(workflow, summary_params) + workflow_summary = WorkflowEsmfold.paramsSummaryMultiqc(workflow, summary_params) ch_workflow_summary = Channel.value(workflow_summary) - methods_description = WorkflowColabfold.methodsDescriptionText(workflow, ch_multiqc_custom_methods_description) + methods_description = WorkflowEsmfold.methodsDescriptionText(workflow, ch_multiqc_custom_methods_description) ch_methods_description = Channel.value(methods_description) ch_multiqc_files = Channel.empty() ch_multiqc_files = ch_multiqc_files.mix(ch_workflow_summary.collectFile(name: 'workflow_summary_mqc.yaml')) ch_multiqc_files = ch_multiqc_files.mix(ch_methods_description.collectFile(name: 'methods_description_mqc.yaml')) ch_multiqc_files = ch_multiqc_files.mix(CUSTOM_DUMPSOFTWAREVERSIONS.out.mqc_yml.collect()) - ch_multiqc_files = ch_multiqc_files.mix(COLABFOLD_BATCH.out.multiqc.collect()) + ch_multiqc_files = ch_multiqc_files.mix(RUN_ESMFOLD.out.multiqc.collect()) MULTIQC ( ch_multiqc_files.collect(), From 31da64b1605689a102450e477c19cf29b5356192 Mon Sep 17 00:00:00 2001 From: Athanasios Baltzis Date: Tue, 2 May 2023 20:23:40 +0200 Subject: [PATCH 052/227] Add test profiles for esmfold --- .github/workflows/awsfulltest.yml | 2 ++ conf/test_esmfold.config | 33 ++++++++++++++++++++++++++ conf/test_full_esmfold.config | 22 +++++++++++++++++ conf/test_full_esmfold_multimer.config | 22 +++++++++++++++++ nextflow.config | 2 ++ 5 files changed, 81 insertions(+) create mode 100644 conf/test_esmfold.config create mode 100644 conf/test_full_esmfold.config create mode 100644 conf/test_full_esmfold_multimer.config diff --git a/.github/workflows/awsfulltest.yml b/.github/workflows/awsfulltest.yml index 98291c89..13405ee2 100644 --- a/.github/workflows/awsfulltest.yml +++ b/.github/workflows/awsfulltest.yml @@ -23,6 +23,8 @@ jobs: "_colabfold_local", "_colabfold_webserver", "_colabfold_multimer", + "_esmfold", + "_esmfold_multimer" ] steps: - name: Launch workflow via tower diff --git a/conf/test_esmfold.config b/conf/test_esmfold.config new file mode 100644 index 00000000..38202ac8 --- /dev/null +++ b/conf/test_esmfold.config @@ -0,0 +1,33 @@ +/* +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Nextflow config file for running minimal tests +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Defines input files and everything required to run a fast and simple pipeline test. + Use as follows: + nextflow run nf-core/proteinfold -profile test, --outdir +---------------------------------------------------------------------------------------- +*/ + +stubRun = true + +params { + config_profile_name = 'Test profile' + config_profile_description = 'Minimal test dataset to check pipeline function' + + // Limit resources so that this can run on GitHub Actions + max_cpus = 2 + max_memory = '6.GB' + max_time = '6.h' + + // Input data to test esmfold + mode = 'esmfold' + esmfold_db = "${projectDir}/assets/dummy_db_dir" + input = 'https://raw.githubusercontent.com/nf-core/test-datasets/proteinfold/testdata/samplesheet/v1.0/samplesheet.csv' +} + +process { + withName: 'RUN_ESMFOLD' { + container = 'quay.io/biocontainers/gawk:5.1.0' + } +} + diff --git a/conf/test_full_esmfold.config b/conf/test_full_esmfold.config new file mode 100644 index 00000000..b7edbe46 --- /dev/null +++ b/conf/test_full_esmfold.config @@ -0,0 +1,22 @@ +/* +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Nextflow config file for running full-size tests +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Defines input files and everything required to run a full size pipeline test. + + Use as follows: + nextflow run nf-core/proteinfold -profile test_full_colabfold_webserver, --outdir + +---------------------------------------------------------------------------------------- +*/ + +params { + config_profile_name = 'Full test profile for esmfold monomer' + config_profile_description = 'Minimal test dataset to check pipeline function' + + // Input data for full test of esmfold monomer + mode = 'esmfold' + esmfold_model_preset = 'monomer' + input = 'https://raw.githubusercontent.com/nf-core/test-datasets/proteinfold/testdata/samplesheet/v1.0/samplesheet.csv' + esmfold_db = 's3://nf-core-awsmegatests/proteinfold/input_data/db/esmfold' +} diff --git a/conf/test_full_esmfold_multimer.config b/conf/test_full_esmfold_multimer.config new file mode 100644 index 00000000..ee42df0a --- /dev/null +++ b/conf/test_full_esmfold_multimer.config @@ -0,0 +1,22 @@ +/* +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Nextflow config file for running full-size tests +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Defines input files and everything required to run a full size pipeline test. + + Use as follows: + nextflow run nf-core/proteinfold -profile test_full_colabfold_webserver, --outdir + +---------------------------------------------------------------------------------------- +*/ + +params { + config_profile_name = 'Full test profile for esmfold multimer' + config_profile_description = 'Minimal test dataset to check pipeline function' + + // Input data for full test of esmfold multimer + mode = 'esmfold' + esmfold_model_preset = 'multimer' + input = 'https://raw.githubusercontent.com/nf-core/test-datasets/proteinfold/testdata/samplesheet/v1.0/samplesheet.csv' + esmfold_db = 's3://nf-core-awsmegatests/proteinfold/input_data/db/esmfold' +} diff --git a/nextflow.config b/nextflow.config index 339368f4..face9c67 100644 --- a/nextflow.config +++ b/nextflow.config @@ -240,6 +240,8 @@ profiles { test_full_colabfold_local { includeConfig 'conf/test_full_colabfold_local.config' } test_full_colabfold_webserver { includeConfig 'conf/test_full_colabfold_webserver.config' } test_full_colabfold_multimer { includeConfig 'conf/test_full_colabfold_webserver_multimer.config' } + test_full_esmfold { includeConfig 'conf/test_full_esmfold.config' } + test_full_esmfold_multimer { includeConfig 'conf/test_full_esmfold_multimer.config' } } // Export these variables to prevent local Python/R libraries from conflicting with those in the container From d0681b7b6aa36b0eb7a66726b3afbc69d1305376 Mon Sep 17 00:00:00 2001 From: Athanasios Baltzis Date: Thu, 4 May 2023 12:42:35 +0200 Subject: [PATCH 053/227] Add ESMFOLD workflow to the main and fix bugs --- conf/dbs.config | 2 +- conf/modules_esmfold.config | 2 +- conf/test_full_esmfold_multimer.config | 2 +- main.nf | 10 +++++- modules/local/multifasta_to_singlefasta.nf | 39 +++++++++++++++++++++ modules/local/params_to_dir.nf | 38 ++++++++++++++++++++ modules/local/run_esmfold.nf | 8 ++--- nextflow.config | 3 ++ nextflow_schema.json | 40 +++++++++++++++++++++- subworkflows/local/prepare_esmfold_dbs.nf | 13 ++++--- workflows/esmfold.nf | 14 ++++---- 11 files changed, 149 insertions(+), 22 deletions(-) create mode 100644 modules/local/multifasta_to_singlefasta.nf create mode 100644 modules/local/params_to_dir.nf diff --git a/conf/dbs.config b/conf/dbs.config index 6a7156dd..ea0d9e9b 100644 --- a/conf/dbs.config +++ b/conf/dbs.config @@ -54,5 +54,5 @@ params { esm2_t36_3B_UR50D_contact_regression = 'https://dl.fbaipublicfiles.com/fair-esm/regression/esm2_t36_3B_UR50D-contact-regression.pt' // Esmfold paths - esmfold_params_path = "${params.esmfold_db}/esmfold_params/*" + esmfold_params_path = "${params.esmfold_db}/*" } diff --git a/conf/modules_esmfold.config b/conf/modules_esmfold.config index 230dd343..81b3048f 100644 --- a/conf/modules_esmfold.config +++ b/conf/modules_esmfold.config @@ -14,7 +14,7 @@ process { withName: 'RUN_ESMFOLD' { ext.args = {params.use_gpu ? '' : '--cpu-only'} publishDir = [ - path: { "${params.outdir}/esmfold" }, + path: { "${params.outdir}/${params.mode}" }, mode: 'copy', saveAs: { filename -> filename.equals('versions.yml') ? null : filename }, pattern: '*.*' diff --git a/conf/test_full_esmfold_multimer.config b/conf/test_full_esmfold_multimer.config index ee42df0a..b6e594fc 100644 --- a/conf/test_full_esmfold_multimer.config +++ b/conf/test_full_esmfold_multimer.config @@ -17,6 +17,6 @@ params { // Input data for full test of esmfold multimer mode = 'esmfold' esmfold_model_preset = 'multimer' - input = 'https://raw.githubusercontent.com/nf-core/test-datasets/proteinfold/testdata/samplesheet/v1.0/samplesheet.csv' + input = 'https://raw.githubusercontent.com/nf-core/test-datasets/proteinfold/testdata/samplesheet/v1.0/samplesheet_multimer.csv' esmfold_db = 's3://nf-core-awsmegatests/proteinfold/input_data/db/esmfold' } diff --git a/main.nf b/main.nf index 36213f31..0c7531a4 100644 --- a/main.nf +++ b/main.nf @@ -38,6 +38,8 @@ if (params.mode == "alphafold2") { include { ALPHAFOLD2 } from './workflows/alphafold2' } else if (params.mode == "colabfold") { include { COLABFOLD } from './workflows/colabfold' +} else if (params.mode == "esmfold") { + include { ESMFOLD } from './workflows/esmfold' } workflow NFCORE_PROTEINFOLD { @@ -51,10 +53,16 @@ workflow NFCORE_PROTEINFOLD { // // WORKFLOW: Run colabfold // - // else if(params.mode == "colabfold_webserver" || params.mode == "colabfold_local") { else if(params.mode == "colabfold") { COLABFOLD () } + + // + // WORKFLOW: Run esmfold + // + else if(params.mode == "esmfold") { + ESMFOLD () + } } /* diff --git a/modules/local/multifasta_to_singlefasta.nf b/modules/local/multifasta_to_singlefasta.nf new file mode 100644 index 00000000..f4b58cf8 --- /dev/null +++ b/modules/local/multifasta_to_singlefasta.nf @@ -0,0 +1,39 @@ +process MULTIFASTA_TO_SINGLEFASTA { + tag "$meta.id" + label 'process_single' + + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/ubuntu:20.04' : + 'ubuntu:20.04' }" + + input: + tuple val(meta), path(fasta) + + output: + tuple val(meta), path("${meta.id}.fasta"), emit: input_fasta + path "versions.yml", emit: versions + + when: + task.ext.when == null || task.ext.when + + script: + """ + awk '/^>/ {printf("\\n%s\\n",\$0);next; } { printf("%s",\$0);} END {printf("\\n");}' ${fasta} > single_line.fasta + echo -e '>'${meta.id}'\\n'`awk '!/^>/ {print \$0}' single_line.fasta | tr '\\n' ':' | sed 's/:\$//' | sed 's/^://'` > ${meta.id}.fasta + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + sed: \$(echo \$(sed --version 2>&1) | sed 's/^.*GNU sed) //; s/ .*\$//') + END_VERSIONS + """ + + stub: + """ + touch input.csv + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + sed: \$(echo \$(sed --version 2>&1) | sed 's/^.*GNU sed) //; s/ .*\$//') + END_VERSIONS + """ +} diff --git a/modules/local/params_to_dir.nf b/modules/local/params_to_dir.nf new file mode 100644 index 00000000..6bc0de89 --- /dev/null +++ b/modules/local/params_to_dir.nf @@ -0,0 +1,38 @@ +process PARAMS_TO_DIR { + label 'process_single' + + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/ubuntu:20.04' : + 'docker.io/ubuntu:20.04' }" + + input: + path(models) + + output: + path("checkpoints"), emit: input_models + path "versions.yml", emit: versions + + when: + task.ext.when == null || task.ext.when + + script: + """ + mkdir checkpoints + cp *.pt checkpoints + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + sed: \$(echo \$(sed --version 2>&1) | sed 's/^.*GNU sed) //; s/ .*\$//') + END_VERSIONS + """ + + stub: + """ + mkdir checkpoints + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + sed: \$(echo \$(sed --version 2>&1) | sed 's/^.*GNU sed) //; s/ .*\$//') + END_VERSIONS + """ +} diff --git a/modules/local/run_esmfold.nf b/modules/local/run_esmfold.nf index 39837091..5c102e46 100644 --- a/modules/local/run_esmfold.nf +++ b/modules/local/run_esmfold.nf @@ -3,12 +3,12 @@ process RUN_ESMFOLD { label 'process_medium' container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'docker://athbaltzis/esmfold:v0.1' : - 'athbaltzis/esmfold:v0.1' }" + 'docker://athbaltzis/esmfold:1.1.0' : + 'athbaltzis/esmfold:1.1.0' }" input: tuple val(meta), path(fasta) - path ('./checkpoint') + path ('./checkpoints') val numRec output: @@ -27,7 +27,7 @@ process RUN_ESMFOLD { esm-fold \ -i ${fasta} \ -o \$PWD \ - -m ./checkpoints \ + -m \$PWD \ --num-recycles ${numRec} \ $args diff --git a/nextflow.config b/nextflow.config index face9c67..5004887b 100644 --- a/nextflow.config +++ b/nextflow.config @@ -67,6 +67,7 @@ params { uniref30_colabfold_path = null // Esmfold parameters + esmfold_db = null esmfold_model_preset = "monomer" num_recycles = 4 @@ -295,6 +296,8 @@ if (params.mode == 'alphafold2') { includeConfig 'conf/modules_alphafold2.config' } else if (params.mode == 'colabfold') { includeConfig 'conf/modules_colabfold.config' +} else if (params.mode == 'esmfold') { + includeConfig 'conf/modules_esmfold.config' } // Load links to DBs and parameters diff --git a/nextflow_schema.json b/nextflow_schema.json index 58f95d40..b756f519 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -177,7 +177,6 @@ "type": "integer", "default": 4, "description": "Specifies the number of recycles used by Esmfold", - "enum": ["webserver", "local"], "fa_icon": "fas fa-server" }, "esmfold_model_preset": { @@ -462,6 +461,45 @@ } } }, + "esmfold_parameters_link_options": { + "title": "Esmfold parameters links options", + "type": "object", + "description": "Parameters used to provide the links to the parameters public resources to Esmfold.", + "fa_icon": "fas fa-database", + "properties": { + "esmfold_3B_v1": { + "type": "string", + "default": "https://dl.fbaipublicfiles.com/fair-esm/models/esmfold_3B_v1.pt", + "description": "Link to the Esmfold 3B-v1 model", + "fa_icon": "fas fa-link" + }, + "esm2_t36_3B_UR50D": { + "type": "string", + "default": "https://dl.fbaipublicfiles.com/fair-esm/models/esm2_t36_3B_UR50D.pt", + "description": "Link to the Esmfold t36-3B-UR50D model", + "fa_icon": "fas fa-link" + }, + "esm2_t36_3B_UR50D_contact_regression": { + "type": "string", + "default": "https://dl.fbaipublicfiles.com/fair-esm/regression/esm2_t36_3B_UR50D-contact-regression.pt", + "description": "Link to the Esmfold t36-3B-UR50D-contact-regression model", + "fa_icon": "fas fa-link" + } + } + }, + "esmfold_parameters_path_options": { + "title": "Esmfold parameters links options", + "type": "object", + "description": "Parameters used to provide the links to the parameters public resources to Esmfold.", + "fa_icon": "fas fa-database", + "properties": { + "esmfold_params_path": { + "type": "string", + "description": "Link to the Esmfold parameters", + "fa_icon": "fas fa-folder-open" + } + } + }, "generic_options": { "title": "Generic options", "type": "object", diff --git a/subworkflows/local/prepare_esmfold_dbs.nf b/subworkflows/local/prepare_esmfold_dbs.nf index db8c39d7..35b705ba 100644 --- a/subworkflows/local/prepare_esmfold_dbs.nf +++ b/subworkflows/local/prepare_esmfold_dbs.nf @@ -5,15 +5,14 @@ include { ARIA2 as ARIA2_ESMFOLD_3B_V1 } from '../../modules/nf-core/aria2/main' include { ARIA2 as ARIA2_ESM2_T36_3B_UR50D } from '../../modules/nf-core/aria2/main' include { ARIA2 as ARIA2_ESM2_T36_3B_UR50D_CONTACT_REGRESSION } from '../../modules/nf-core/aria2/main' - +include { PARAMS_TO_DIR } from '../../modules/local/params_to_dir' workflow PREPARE_ESMFOLD_DBS { main: ch_versions = Channel.empty() - if (params.esmfold_db) { - ch_params = file( params.esmfold_params_path ) + ch_params = file( params.esmfold_params_path, type: 'dir' ) } else { ARIA2_ESMFOLD_3B_V1 ( @@ -25,9 +24,13 @@ workflow PREPARE_ESMFOLD_DBS { ARIA2_ESM2_T36_3B_UR50D_CONTACT_REGRESSION ( params.esm2_t36_3B_UR50D_contact_regression ) - ch_params = ARIA2_ESMFOLD_3B_V1.out.downloaded_file.mix(ARIA2_ESM2_T36_3B_UR50D.out.downloaded_file,ARIA2_ESM2_T36_3B_UR50D_CONTACT_REGRESSION.out.downloaded_file) + collect_params = ARIA2_ESMFOLD_3B_V1.out.downloaded_file.mix(ARIA2_ESM2_T36_3B_UR50D.out.downloaded_file,ARIA2_ESM2_T36_3B_UR50D_CONTACT_REGRESSION.out.downloaded_file).collect() ch_versions = ch_versions.mix(ARIA2_ESMFOLD_3B_V1.out.versions) - + PARAMS_TO_DIR ( + collect_params + ) + ch_params = PARAMS_TO_DIR.out.input_models + ch_versions = ch_versions.mix(PARAMS_TO_DIR.out.versions) } emit: diff --git a/workflows/esmfold.nf b/workflows/esmfold.nf index 7381ff9b..df9d14c0 100644 --- a/workflows/esmfold.nf +++ b/workflows/esmfold.nf @@ -46,7 +46,7 @@ include { PREPARE_ESMFOLD_DBS } from '../subworkflows/local/prepare_esmfold_dbs' // MODULE: Local to the pipeline // include { RUN_ESMFOLD } from '../modules/local/run_esmfold' -include { MULTIFASTA_TO_CSV } from '../modules/local/multifasta_to_csv' +include { MULTIFASTA_TO_SINGLEFASTA } from '../modules/local/multifasta_to_singlefasta' /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -81,19 +81,19 @@ workflow ESMFOLD { ) ch_versions = ch_versions.mix(INPUT_CHECK.out.versions) - PREPARE_ESMFOLD( ) - ch_versions = ch_versions.mix(PREPARE_ESMFOLD.out.versions) + PREPARE_ESMFOLD_DBS( ) + ch_versions = ch_versions.mix(PREPARE_ESMFOLD_DBS.out.versions) // // MODULE: Run esmfold // if (params.esmfold_model_preset != 'monomer') { - MULTIFASTA_TO_CSV( + MULTIFASTA_TO_SINGLEFASTA( INPUT_CHECK.out.fastas ) - ch_versions = ch_versions.mix(MULTIFASTA_TO_CSV.out.versions) + ch_versions = ch_versions.mix(MULTIFASTA_TO_SINGLEFASTA.out.versions) RUN_ESMFOLD( - MULTIFASTA_TO_CSV.out.input_csv, + MULTIFASTA_TO_SINGLEFASTA.out.input_fasta, PREPARE_ESMFOLD_DBS.out.params, params.num_recycles ) @@ -102,8 +102,6 @@ workflow ESMFOLD { RUN_ESMFOLD( INPUT_CHECK.out.fastas, PREPARE_ESMFOLD_DBS.out.params, - [], - [], params.num_recycles ) ch_versions = ch_versions.mix(RUN_ESMFOLD.out.versions) From ae17797ec850b2b373fbf5bf8ce97042d4f45f70 Mon Sep 17 00:00:00 2001 From: Athanasios Baltzis Date: Fri, 5 May 2023 12:36:38 +0200 Subject: [PATCH 054/227] Update nextflow_schema --- nextflow_schema.json | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/nextflow_schema.json b/nextflow_schema.json index b756f519..5ecb7c1f 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -182,7 +182,7 @@ "esmfold_model_preset": { "type": "string", "description": "Specifies whether is a 'monomer' or 'multimer' prediction", - "enum": ["monomer","multimer"], + "enum": ["monomer", "multimer"], "fa_icon": "fas fa-stream" } } @@ -614,6 +614,9 @@ { "$ref": "#/definitions/colabfold_options" }, + { + "$ref": "#/definitions/esmfold_options" + }, { "$ref": "#/definitions/institutional_config_options" }, @@ -632,6 +635,12 @@ { "$ref": "#/definitions/colabfold_dbs_and_parameters_path_options" }, + { + "$ref": "#/definitions/esmfold_parameters_link_options" + }, + { + "$ref": "#/definitions/esmfold_parameters_path_options" + }, { "$ref": "#/definitions/generic_options" } From 7f00318ccd9b5316ff00d9e9ce89dc52f7f0266a Mon Sep 17 00:00:00 2001 From: Athanasios Baltzis Date: Fri, 5 May 2023 16:00:15 +0200 Subject: [PATCH 055/227] Update metromap --- .../nf-core-proteinfold_metro_map_1.1.0.png | Bin 0 -> 202826 bytes .../nf-core-proteinfold_metro_map_1.1.0.svg | 1640 +++++++++++++++++ 2 files changed, 1640 insertions(+) create mode 100644 docs/images/nf-core-proteinfold_metro_map_1.1.0.png create mode 100644 docs/images/nf-core-proteinfold_metro_map_1.1.0.svg diff --git a/docs/images/nf-core-proteinfold_metro_map_1.1.0.png b/docs/images/nf-core-proteinfold_metro_map_1.1.0.png new file mode 100644 index 0000000000000000000000000000000000000000..8db0e9b001b3e96de61eb4afb7f6a9d3e374aca7 GIT binary patch literal 202826 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­Su*LqoTu)hqEbvV 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

    *7n{jf>j*8C*2;Y8j?T`W(ckr!lcR<|OVnH_M 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

    - - - + + +
    @@ -475,11 +475,11 @@

    Download

    ] const LDDT_IMGS = [ - "*sample_name_here*_coverage_LDDT_0.png", - "*sample_name_here*_coverage_LDDT_1.png", - "*sample_name_here*_coverage_LDDT_2.png", - "*sample_name_here*_coverage_LDDT_3.png", - "*sample_name_here*_coverage_LDDT_4.png" + "coverage_LDDT_0.png", + "coverage_LDDT_1.png", + "coverage_LDDT_2.png", + "coverage_LDDT_3.png", + "coverage_LDDT_4.png" ] const REPRESENTATIONS = [ diff --git a/assets/generat_plots_2.py b/assets/generat_plots_2.py index c0948dd6..3b2ac1d8 100644 --- a/assets/generat_plots_2.py +++ b/assets/generat_plots_2.py @@ -4,6 +4,7 @@ from matplotlib import pyplot as plt import argparse from collections import OrderedDict +import base64 def generate_output_images(msa_path, plddt_paths, name, out_dir): msa = [] @@ -123,10 +124,20 @@ def generate_output_images(msa_path, plddt_paths, name, out_dir): alphfold_template = open(args.html_template, "r").read() alphfold_template = alphfold_template.replace(f"*sample_name_here*", args.name) + i = 0 for structure in structures: alphfold_template = alphfold_template.replace(f"*_data_ranked_{i}.pdb*", open(structure, "r").read().replace("\n", "\\n")) i += 1 +with open(f"{args.output_dir}/{args.name + ('_' if args.name else '')}seq_coverage.png", "rb") as in_file: + alphfold_template = alphfold_template.replace(f"seq_coverage.png", f"data:image/png;base64,{base64.b64encode(in_file.read()).decode('utf-8')}") + +for i in range(0, 5): + with open(f"{args.output_dir}/{args.name + ('_' if args.name else '')}coverage_LDDT_{i}.png", "rb") as in_file: + alphfold_template = alphfold_template.replace(f"coverage_LDDT_{i}.png", f"data:image/png;base64,{base64.b64encode(in_file.read()).decode('utf-8')}") + + + with open(f"{args.output_dir}/{args.name}_alphafold.html", "w") as out_file: out_file.write(alphfold_template) diff --git a/tower.yml b/tower.yml index 16a92fd3..c0b95ad4 100755 --- a/tower.yml +++ b/tower.yml @@ -5,5 +5,3 @@ reports: display: "Auto-created samplesheet with collated metadata and FASTQ paths" "*_alphafold.html": display: "Predected structures" - "*.png": - display: "Plots" \ No newline at end of file From 0261a0aee7d88420d217c15ed6c1bfa8a2592742 Mon Sep 17 00:00:00 2001 From: ziadbkh Date: Mon, 13 May 2024 13:04:26 +1000 Subject: [PATCH 207/227] update --- assets/alphafold_template.html | 14 +++--- assets/generat_plots_2.py | 92 +++++++++++++++++++++++++++++++--- 2 files changed, 93 insertions(+), 13 deletions(-) diff --git a/assets/alphafold_template.html b/assets/alphafold_template.html index 8477b6f5..533e2fee 100755 --- a/assets/alphafold_template.html +++ b/assets/alphafold_template.html @@ -412,13 +412,13 @@

    Download

    - - - - - - -
    +
    +
    +
    +
    + +
    +
    diff --git a/assets/generat_plots_2.py b/assets/generat_plots_2.py index 3b2ac1d8..5b7bc5ec 100644 --- a/assets/generat_plots_2.py +++ b/assets/generat_plots_2.py @@ -5,6 +5,10 @@ import argparse from collections import OrderedDict import base64 +import os +from collections import OrderedDict +import plotly.graph_objects as go +from plotly.subplots import make_subplots def generate_output_images(msa_path, plddt_paths, name, out_dir): msa = [] @@ -102,6 +106,71 @@ def generate_output_images(msa_path, plddt_paths, name, out_dir): ################################################################## +def generate_plots(msa_path, plddt_paths, name, out_dir): + msa = [] + with open(msa_path, 'r') as in_file: + for line in in_file: + msa.append([int(x) for x in line.strip().split()]) + + seqid = [] + for sequence in msa: + matches = [1.0 if first == other else 0.0 for first, other in zip(msa[0], sequence)] + seqid.append(sum(matches) / len(matches)) + + seqid_sort = sorted(range(len(seqid)), key=seqid.__getitem__) + + non_gaps = [] + for sequence in msa: + non_gaps.append([float(num != 21) if num != 21 else float('nan') for num in sequence]) + + sorted_non_gaps = [non_gaps[i] for i in seqid_sort] + final = [] + for sorted_seq, identity in zip(sorted_non_gaps, [seqid[i] for i in seqid_sort]): + final.append([value * identity if not isinstance(value, str) else value for value in sorted_seq]) + + # Plotting Sequence Coverage using Plotly + fig = go.Figure() + fig.add_trace(go.Heatmap( + z=final, + colorscale="Rainbow", + zmin=0, + zmax=1, + )) + fig.update_layout( + title="Sequence coverage", + xaxis_title="Positions", + yaxis_title="Sequences" + ) + # Save as interactive HTML instead of an image + fig.savefig(f"{out_dir}/{name+('_' if name else '')}seq_coverage.png") + """ + #fig.to_html(full_html=False).write_html(f"{out_dir}/{name+('_' if name else '')}seq_coverage.html") + with open (f"{out_dir}/{name+('_' if name else '')}seq_coverage.html", "w") as out_plt: + out_plt.write(fig.to_html(full_html=False)) + """ + # Plotting Predicted LDDT per position using Plotly + plddt_per_model = OrderedDict() + plddt_paths.sort() + for plddt_path in plddt_paths: + with open(plddt_path, 'r') as in_file: + plddt_per_model[os.path.basename(plddt_path)[:-4]] = [float(x) for x in in_file.read().strip().split()] + + i = 0 + for model_name, value_plddt in plddt_per_model.items(): + fig = go.Figure() + fig.add_trace(go.Scatter( + x=list(range(len(value_plddt))), + y=value_plddt, + mode='lines', + name=model_name + )) + fig.update_layout(title="Predicted LDDT per Position") + fig.savefig(f"{out_dir}/{name+('_' if name else '')}coverage_LDDT_{i}.png") + """ + with open (f"{out_dir}/{name+('_' if name else '')}coverage_LDDT_{i}.html", "w") as out_plt: + out_plt.write(fig.to_html(full_html=False).replace("\"", "\\\"")) + """ + i += 1 print("Starting..") parser = argparse.ArgumentParser() @@ -118,6 +187,8 @@ def generate_output_images(msa_path, plddt_paths, name, out_dir): generate_output_images(args.msa, args.plddt, args.name, args.output_dir) +#generate_plots(args.msa, args.plddt, args.name, args.output_dir) + print("generating html report...") structures = args.pdb structures.sort() @@ -130,14 +201,23 @@ def generate_output_images(msa_path, plddt_paths, name, out_dir): alphfold_template = alphfold_template.replace(f"*_data_ranked_{i}.pdb*", open(structure, "r").read().replace("\n", "\\n")) i += 1 -with open(f"{args.output_dir}/{args.name + ('_' if args.name else '')}seq_coverage.png", "rb") as in_file: - alphfold_template = alphfold_template.replace(f"seq_coverage.png", f"data:image/png;base64,{base64.b64encode(in_file.read()).decode('utf-8')}") - -for i in range(0, 5): - with open(f"{args.output_dir}/{args.name + ('_' if args.name else '')}coverage_LDDT_{i}.png", "rb") as in_file: - alphfold_template = alphfold_template.replace(f"coverage_LDDT_{i}.png", f"data:image/png;base64,{base64.b64encode(in_file.read()).decode('utf-8')}") +if True: + with open(f"{args.output_dir}/{args.name + ('_' if args.name else '')}seq_coverage.png", "rb") as in_file: + alphfold_template = alphfold_template.replace(f"seq_coverage.png", f"data:image/png;base64,{base64.b64encode(in_file.read()).decode('utf-8')}") + + for i in range(0, 5): + with open(f"{args.output_dir}/{args.name + ('_' if args.name else '')}coverage_LDDT_{i}.png", "rb") as in_file: + alphfold_template = alphfold_template.replace(f"coverage_LDDT_{i}.png", f"data:image/png;base64,{base64.b64encode(in_file.read()).decode('utf-8')}") + +""" +with open(f"{args.output_dir}/{args.name + ('_' if args.name else '')}seq_coverage.html", "r") as in_file: + alphfold_template = alphfold_template.replace(f"seq_coverage.png", f"{in_file.read()}") +for i in range(0, 5): + with open(f"{args.output_dir}/{args.name + ('_' if args.name else '')}coverage_LDDT_{i}.html", "r") as in_file: + alphfold_template = alphfold_template.replace(f"coverage_LDDT_{i}.png", f"{in_file.read()}") +""" with open(f"{args.output_dir}/{args.name}_alphafold.html", "w") as out_file: out_file.write(alphfold_template) From bf50d6425b2d267dfc8dd93d48fc26383197d816 Mon Sep 17 00:00:00 2001 From: ziadbkh Date: Mon, 20 May 2024 13:29:26 +1000 Subject: [PATCH 208/227] add visulaisation report --- assets/NO_FILE | 0 bin/extract_output.py | 28 +++++++ {assets => bin}/generat_plots_2.py | 118 +++++++++++++++------------ conf/gadi.config | 26 +++--- conf/test_full_esmfold.config | 2 +- main.nf | 86 ++++++++++--------- modules/local/generat_report.nf | 18 ++-- modules/local/run_alphafold2.nf | 13 ++- modules/local/run_alphafold2_pred.nf | 12 ++- modules/local/run_esmfold.nf | 7 +- workflows/alphafold2.nf | 57 +++++++------ workflows/esmfold.nf | 21 ++++- 12 files changed, 239 insertions(+), 149 deletions(-) create mode 100644 assets/NO_FILE create mode 100755 bin/extract_output.py rename {assets => bin}/generat_plots_2.py (65%) mode change 100644 => 100755 diff --git a/assets/NO_FILE b/assets/NO_FILE new file mode 100644 index 00000000..e69de29b diff --git a/bin/extract_output.py b/bin/extract_output.py new file mode 100755 index 00000000..a43a8a3c --- /dev/null +++ b/bin/extract_output.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python +import pickle +import os, sys +import argparse + +def read_pkl(id, pkl_files): + for pkl_file in pkl_files: + dict_data = pickle.load(open(pkl_file,'rb')) + #print(dict_data.keys()) + if pkl_file.endswith("features.pkl"): + with open (f"{id}_msa.tsv", "w") as out_f: + for val in dict_data['msa']: + out_f.write("\t".join([str(x) for x in val]) + "\n") + else: + model_id = os.path.basename(pkl_file).replace("result_model_", "").replace("_pred_0.pkl", "") + with open (f"{id}_lddt_{model_id}.tsv", "w") as out_f: + out_f.write("\t".join([str(x) for x in dict_data['plddt']]) + "\n") + + +parser = argparse.ArgumentParser() +parser.add_argument('--pkls',dest='pkls',required=True, nargs="+") +parser.add_argument('--name',dest='name') +parser.add_argument('--output_dir',dest='output_dir') +parser.set_defaults(output_dir='') +parser.set_defaults(name='') +args = parser.parse_args() + +read_pkl(args.name, args.pkls) diff --git a/assets/generat_plots_2.py b/bin/generat_plots_2.py old mode 100644 new mode 100755 similarity index 65% rename from assets/generat_plots_2.py rename to bin/generat_plots_2.py index 5b7bc5ec..87982a25 --- a/assets/generat_plots_2.py +++ b/bin/generat_plots_2.py @@ -10,60 +10,69 @@ import plotly.graph_objects as go from plotly.subplots import make_subplots -def generate_output_images(msa_path, plddt_paths, name, out_dir): +def generate_output_images(msa_path, plddt_paths, name, out_dir, in_type): msa = [] - with open(msa_path, 'r') as in_file: - for line in in_file: - msa.append([int(x) for x in line.strip().split()]) + if not msa_path.endswith("NO_FILE"): + with open(msa_path, 'r') as in_file: + for line in in_file: + msa.append([int(x) for x in line.strip().split()]) - seqid = [] - for sequence in msa: - matches = [1.0 if first == other else 0.0 for first, other in zip(msa[0], sequence)] - seqid.append(sum(matches) / len(matches)) - - seqid_sort = sorted(range(len(seqid)), key=seqid.__getitem__) + seqid = [] + for sequence in msa: + matches = [1.0 if first == other else 0.0 for first, other in zip(msa[0], sequence)] + seqid.append(sum(matches) / len(matches)) - non_gaps = [] - for sequence in msa: - non_gaps.append([float(num != 21) if num != 21 else float('nan') for num in sequence]) - - sorted_non_gaps = [non_gaps[i] for i in seqid_sort] - final = [] - for sorted_seq, identity in zip(sorted_non_gaps, [seqid[i] for i in seqid_sort]): - final.append([value * identity if not isinstance(value, str) else value for value in sorted_seq]) + seqid_sort = sorted(range(len(seqid)), key=seqid.__getitem__) + non_gaps = [] + for sequence in msa: + non_gaps.append([float(num != 21) if num != 21 else float('nan') for num in sequence]) + sorted_non_gaps = [non_gaps[i] for i in seqid_sort] + final = [] + for sorted_seq, identity in zip(sorted_non_gaps, [seqid[i] for i in seqid_sort]): + final.append([value * identity if not isinstance(value, str) else value for value in sorted_seq]) - ################################################################## - plt.figure(figsize=(14, 14), dpi=100) - ################################################################## - plt.title("Sequence coverage") - plt.imshow(final, - interpolation='nearest', aspect='auto', - cmap="rainbow_r", vmin=0, vmax=1, origin='lower') - - column_counts = [0] * len(msa[0]) - for col in range(len(msa[0])): - for row in msa: - if row[col] != 21: - column_counts[col] += 1 - - plt.plot(column_counts, color='black') - plt.xlim(-0.5, len(msa[0]) - 0.5) - plt.ylim(-0.5, len(msa) - 0.5) - - plt.colorbar(label="Sequence identity to query", ) - plt.xlabel("Positions") - plt.ylabel("Sequences") - plt.savefig(f"{out_dir}/{name+('_' if name else '')}seq_coverage.png") + ################################################################## + plt.figure(figsize=(14, 14), dpi=100) + ################################################################## + plt.title("Sequence coverage") + plt.imshow(final, + interpolation='nearest', aspect='auto', + cmap="rainbow_r", vmin=0, vmax=1, origin='lower') + + column_counts = [0] * len(msa[0]) + for col in range(len(msa[0])): + for row in msa: + if row[col] != 21: + column_counts[col] += 1 + + plt.plot(column_counts, color='black') + plt.xlim(-0.5, len(msa[0]) - 0.5) + plt.ylim(-0.5, len(msa) - 0.5) + + plt.colorbar(label="Sequence identity to query", ) + plt.xlabel("Positions") + plt.ylabel("Sequences") + plt.savefig(f"{out_dir}/{name+('_' if name else '')}seq_coverage.png") + + ################################################################## - ################################################################## plddt_per_model = OrderedDict() plddt_paths_srt = plddt_paths plddt_paths_srt.sort() for plddt_path in plddt_paths_srt: with open(plddt_path, 'r') as in_file: - plddt_per_model[os.path.basename(plddt_path)[:-4]] = [float(x) for x in in_file.read().strip().split()] + if in_type == "ESM-FOLD": + plddt_per_model[os.path.basename(plddt_path)[:-4]] = [] + in_file.readline() + for line in in_file: + vals = line.strip().split() + #print(vals) + if len(vals) == 5: + plddt_per_model[os.path.basename(plddt_path)[:-4]].append(float(vals[-1].strip())) + else: + plddt_per_model[os.path.basename(plddt_path)[:-4]] = [float(x) for x in in_file.read().strip().split()] plt.figure(figsize=(14, 14), dpi=100) plt.title("Predicted LDDT per position") @@ -104,7 +113,7 @@ def generate_output_images(msa_path, plddt_paths, name, out_dir): plt.savefig(f"{out_dir}/{name+('_' if name else '')}PAE.png") """ ################################################################## - + def generate_plots(msa_path, plddt_paths, name, out_dir): msa = [] @@ -174,18 +183,20 @@ def generate_plots(msa_path, plddt_paths, name, out_dir): print("Starting..") parser = argparse.ArgumentParser() -parser.add_argument('--msa',dest='msa',required=True) -parser.add_argument('--plddt',dest='plddt',required=True, nargs="+") -parser.add_argument('--pdb',dest='pdb',required=True, nargs="+") -parser.add_argument('--name',dest='name') +parser.add_argument('--type', dest='in_type') +parser.add_argument('--msa', dest='msa',required=True) +parser.add_argument('--plddt', dest='plddt',required=True, nargs="+") +parser.add_argument('--pdb', dest='pdb',required=True, nargs="+") +parser.add_argument('--name', dest='name') parser.add_argument('--output_dir',dest='output_dir') parser.add_argument('--html_template',dest='html_template') parser.set_defaults(output_dir='') +parser.set_defaults(in_type='ESM-FOLD') parser.set_defaults(name='') args = parser.parse_args() -generate_output_images(args.msa, args.plddt, args.name, args.output_dir) +generate_output_images(args.msa, args.plddt, args.name, args.output_dir, args.in_type) #generate_plots(args.msa, args.plddt, args.name, args.output_dir) @@ -202,10 +213,13 @@ def generate_plots(msa_path, plddt_paths, name, out_dir): i += 1 if True: - with open(f"{args.output_dir}/{args.name + ('_' if args.name else '')}seq_coverage.png", "rb") as in_file: - alphfold_template = alphfold_template.replace(f"seq_coverage.png", f"data:image/png;base64,{base64.b64encode(in_file.read()).decode('utf-8')}") - - for i in range(0, 5): + if not args.msa.endswith("NO_FILE"): + with open(f"{args.output_dir}/{args.name + ('_' if args.name else '')}seq_coverage.png", "rb") as in_file: + alphfold_template = alphfold_template.replace("seq_coverage.png", f"data:image/png;base64,{base64.b64encode(in_file.read()).decode('utf-8')}") + else: + alphfold_template = alphfold_template.replace("seq_coverage.png","") + + for i in range(0, len(args.plddt)): with open(f"{args.output_dir}/{args.name + ('_' if args.name else '')}coverage_LDDT_{i}.png", "rb") as in_file: alphfold_template = alphfold_template.replace(f"coverage_LDDT_{i}.png", f"data:image/png;base64,{base64.b64encode(in_file.read()).decode('utf-8')}") diff --git a/conf/gadi.config b/conf/gadi.config index cf910ec1..d35ce0b0 100755 --- a/conf/gadi.config +++ b/conf/gadi.config @@ -20,22 +20,28 @@ params { input = 'https://raw.githubusercontent.com/nf-core/test-datasets/proteinfold/testdata/samplesheet/v1.0/samplesheet.csv' alphafold2_db = '/g/data/if89/alphafold2/standard/' use_dgxa100 = false + esmfold_params_path = '/g/data/if89/esm-fold/checkpoints' } process { - storage = "gdata/if89+scratch/${params.project}" + storage = "gdata/if89+scratch/${params.project}+gdata/${params.project}" if (params.use_gpu) { withName: 'RUN_ALPHAFOLD2_PRED|RUN_ALPHAFOLD2' { - if (params.use_dgxa100){ - queue = "dgxa100" - cpus = 16 - }else{ - queue = "gpuvolta" - cpus = 12 - } - gpus = 1 - + if (params.use_dgxa100){ + queue = "dgxa100" + cpus = 16 + }else{ + queue = "gpuvolta" + cpus = 12 + } + gpus = 1 } } + + withName: 'RUN_ESMFOLD' { + queue = "copyq" + cpus = 1 + time = 10.h + } } \ No newline at end of file diff --git a/conf/test_full_esmfold.config b/conf/test_full_esmfold.config index a3919070..64bb7628 100644 --- a/conf/test_full_esmfold.config +++ b/conf/test_full_esmfold.config @@ -18,5 +18,5 @@ params { mode = 'esmfold' esmfold_model_preset = 'monomer' input = 'https://raw.githubusercontent.com/nf-core/test-datasets/proteinfold/testdata/samplesheet/v1.0/samplesheet.csv' - esmfold_db = 's3://proteinfold-dataset/test-data/db/esmfold' + //esmfold_db = 's3://proteinfold-dataset/test-data/db/esmfold' } diff --git a/main.nf b/main.nf index 6b49836d..20d59aff 100644 --- a/main.nf +++ b/main.nf @@ -64,53 +64,50 @@ workflow NFCORE_PROTEINFOLD { if(params.mode == "alphafold2") { // // SUBWORKFLOW: Prepare Alphafold2 DBs - // - PREPARE_ALPHAFOLD2_DBS ( - params.alphafold2_db, - params.full_dbs, - params.bfd_path, - params.small_bfd_path, - params.alphafold2_params_path, - params.mgnify_path, - params.pdb70_path, - params.pdb_mmcif_path, - params.uniref30_alphafold2_path, - params.uniref90_path, - params.pdb_seqres_path, - params.uniprot_path, - params.bfd_link, - params.small_bfd_link, - params.alphafold2_params_link, - params.mgnify_link, - params.pdb70_link, - params.pdb_mmcif_link, - params.pdb_obsolete_link, - params.uniref30_alphafold2_link, - params.uniref90_link, - params.pdb_seqres_link, - params.uniprot_sprot_link, - params.uniprot_trembl_link - ) - ch_versions = ch_versions.mix(PREPARE_ALPHAFOLD2_DBS.out.versions) - // // WORKFLOW: Run nf-core/alphafold2 workflow // + + ch_params = Channel.fromPath( params.alphafold2_params_path ) + ch_mgnify = Channel.fromPath( params.mgnify_path ) + ch_pdb70 = Channel.fromPath( params.pdb70_path, type: 'dir' ) + ch_mmcif_files = Channel.fromPath( params.pdb_mmcif_path, type: 'dir' ) + ch_mmcif_obsolete = Channel.fromPath( params.pdb_mmcif_path, type: 'file' ) + ch_mmcif = ch_mmcif_files.mix(ch_mmcif_obsolete) + ch_uniref30 = Channel.fromPath( params.uniref30_alphafold2_path, type: 'any' ) + ch_uniref90 = Channel.fromPath( params.uniref90_path ) + ch_pdb_seqres = Channel.fromPath( params.pdb_seqres_path ) + ch_uniprot = Channel.fromPath( params.uniprot_path ) + ch_small_bfd = Channel.fromPath( params.small_bfd_path) + ch_bfd = Channel.fromPath( params.bfd_path) + + /*ch_params.view() + ch_params.first().view() + ch_bfd.ifEmpty([]).first().view() + ch_small_bfd.ifEmpty([]).first().view() + + ch_uniref90.first().view() + ch_pdb_seqres.first().view() + ch_uniprot.first().view() + + + */ + + ALPHAFOLD2 ( - ch_versions, params.full_dbs, params.alphafold2_mode, params.alphafold2_model_preset, - PREPARE_ALPHAFOLD2_DBS.out.params.first(), - PREPARE_ALPHAFOLD2_DBS.out.bfd.ifEmpty([]).first(), - PREPARE_ALPHAFOLD2_DBS.out.small_bfd.ifEmpty([]).first(), - PREPARE_ALPHAFOLD2_DBS.out.mgnify.first(), - PREPARE_ALPHAFOLD2_DBS.out.pdb70.first(), - PREPARE_ALPHAFOLD2_DBS.out.pdb_mmcif.first(), - PREPARE_ALPHAFOLD2_DBS.out.uniref30.first(), - PREPARE_ALPHAFOLD2_DBS.out.uniref90.first(), - PREPARE_ALPHAFOLD2_DBS.out.pdb_seqres.first(), - PREPARE_ALPHAFOLD2_DBS.out.uniprot.first() + ch_params.toList(), + ch_bfd.ifEmpty([]).first(), + ch_small_bfd.ifEmpty([]).first(), + ch_mgnify.first(), + ch_pdb70.first(), + ch_mmcif.toList(), + ch_uniref30.toList(), + ch_uniref90.first(), + ch_pdb_seqres.first(), + ch_uniprot.first() ) ch_multiqc = ALPHAFOLD2.out.multiqc_report ch_versions = ch_versions.mix(ALPHAFOLD2.out.versions) @@ -158,21 +155,22 @@ workflow NFCORE_PROTEINFOLD { // // SUBWORKFLOW: Prepare esmfold DBs // - PREPARE_ESMFOLD_DBS ( + /*PREPARE_ESMFOLD_DBS ( params.esmfold_db, params.esmfold_params_path, params.esmfold_3B_v1, params.esm2_t36_3B_UR50D, params.esm2_t36_3B_UR50D_contact_regression - ) - ch_versions = ch_versions.mix(PREPARE_ESMFOLD_DBS.out.versions) + )*/ + + //ch_versions = ch_versions.mix(PREPARE_ESMFOLD_DBS.out.versions) // // WORKFLOW: Run nf-core/esmfold workflow // ESMFOLD ( ch_versions, - PREPARE_ESMFOLD_DBS.out.params, + params.esmfold_params_path, params.num_recycle ) ch_multiqc = ESMFOLD.out.multiqc_report diff --git a/modules/local/generat_report.nf b/modules/local/generat_report.nf index 29865125..69875541 100644 --- a/modules/local/generat_report.nf +++ b/modules/local/generat_report.nf @@ -1,19 +1,20 @@ process GENERATE_REPORT { - tag "$id" + tag "${meta.id}" label 'process_single' container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? 'https://depot.galaxyproject.org/singularity/multiqc:1.21--pyhdfd78af_0' : 'biocontainers/multiqc:1.21--pyhdfd78af_0' }" input: - tuple val(id), path(msa) - tuple val(id), path(lddt) - tuple val(id), path(pdb) + tuple val(meta_msa), path(msa) + tuple val(meta), path(lddt) + tuple val(meta), path(pdb) path(template) - path(script) + val(output_type) + output: - tuple val(id), path ("*.html"), emit: report - tuple val(id), path ("*.png"), emit: images + tuple val(meta), path ("*.html"), emit: report + tuple val(meta), path ("*.png"), emit: images //path "versions.yml", emit: versions when: @@ -22,7 +23,6 @@ process GENERATE_REPORT { def args = task.ext.args ?: '' """ - #export MPLCONFIGDIR=\$PBS_JOBFS - python ./generat_plots_2.py --msa ${msa} --plddt ${lddt.join(' ')} --pdb ${pdb.join(' ')} --html_template ${template} --output_dir ./ --name ${id} || true + generat_plots_2.py --type ${output_type} --msa ${msa} --plddt ${lddt.join(' ')} --pdb ${pdb.join(' ')} --html_template ${template} --output_dir ./ --name ${meta.id} """ } diff --git a/modules/local/run_alphafold2.nf b/modules/local/run_alphafold2.nf index 5607712d..53c49e0b 100644 --- a/modules/local/run_alphafold2.nf +++ b/modules/local/run_alphafold2.nf @@ -28,7 +28,9 @@ process RUN_ALPHAFOLD2 { path ('uniprot/*') output: - path ("${fasta.baseName}*") + tuple val(meta), path ("${fasta.baseName}*"), emit: af_out + tuple val(meta), path ("${fasta.baseName}/${fasta.baseName}*tsv"), emit: af_out_tsv + tuple val(meta), path ("${fasta.baseName}/ranked*pdb"), emit: af_out_pdb path "*_mqc.tsv", emit: multiqc path "versions.yml", emit: versions @@ -72,6 +74,15 @@ process RUN_ALPHAFOLD2 { paste ranked_0_plddt.tsv ranked_1_plddt.tsv ranked_2_plddt.tsv ranked_3_plddt.tsv ranked_4_plddt.tsv > plddt.tsv echo -e Positions"\\t"rank_0"\\t"rank_1"\\t"rank_2"\\t"rank_3"\\t"rank_4 > header.tsv cat header.tsv plddt.tsv > ../"${fasta.baseName}"_plddt_mqc.tsv + + extract_output.py --name ${fasta.baseName} \\ + --pkls result_model_1_pred_0.pkl \\ + result_model_2_pred_0.pkl \\ + result_model_3_pred_0.pkl \\ + result_model_4_pred_0.pkl \\ + result_model_5_pred_0.pkl \\ + features.pkl + cd .. cat <<-END_VERSIONS > versions.yml diff --git a/modules/local/run_alphafold2_pred.nf b/modules/local/run_alphafold2_pred.nf index 66c9302f..88b4f878 100644 --- a/modules/local/run_alphafold2_pred.nf +++ b/modules/local/run_alphafold2_pred.nf @@ -30,7 +30,8 @@ process RUN_ALPHAFOLD2_PRED { output: tuple val(meta), path ("${fasta.baseName}*") - tuple val(meta), path ("${fasta.baseName}*/*"), emit: af_out + tuple val(meta), path ("${fasta.baseName}/${fasta.baseName}*tsv"), emit: af_out_tsv + tuple val(meta), path ("${fasta.baseName}/ranked*pdb"), emit: af_out_pdb path "*_mqc.tsv", emit: multiqc path "versions.yml", emit: versions @@ -59,6 +60,15 @@ process RUN_ALPHAFOLD2_PRED { paste ranked_0_plddt.tsv ranked_1_plddt.tsv ranked_2_plddt.tsv ranked_3_plddt.tsv ranked_4_plddt.tsv > plddt.tsv echo -e Positions"\\t"rank_0"\\t"rank_1"\\t"rank_2"\\t"rank_3"\\t"rank_4 > header.tsv cat header.tsv plddt.tsv > ../"${fasta.baseName}"_plddt_mqc.tsv + + extract_output.py --name ${fasta.baseName} \\ + --pkls result_model_1_pred_0.pkl \\ + result_model_2_pred_0.pkl \\ + result_model_3_pred_0.pkl \\ + result_model_4_pred_0.pkl \\ + result_model_5_pred_0.pkl \\ + ../${fasta.baseName}.features.pkl + cd .. cat <<-END_VERSIONS > versions.yml diff --git a/modules/local/run_esmfold.nf b/modules/local/run_esmfold.nf index 5f7a25ce..f4567239 100644 --- a/modules/local/run_esmfold.nf +++ b/modules/local/run_esmfold.nf @@ -10,12 +10,12 @@ process RUN_ESMFOLD { input: tuple val(meta), path(fasta) - path ('./checkpoints/') + path (esm_fold_parms) val numRec output: - path ("${fasta.baseName}*.pdb"), emit: pdb - path ("${fasta.baseName}_plddt_mqc.tsv"), emit: multiqc + tuple val(meta), path ("${fasta.baseName}*.pdb"), emit: pdb + tuple val(meta), path ("${fasta.baseName}_plddt_mqc.tsv"), emit: multiqc path "versions.yml", emit: versions when: @@ -33,6 +33,7 @@ process RUN_ESMFOLD { --num-recycles ${numRec} \ $args + mv *.pdb "${fasta.baseName}".pdb awk '{print \$2"\\t"\$3"\\t"\$4"\\t"\$6"\\t"\$11}' "${fasta.baseName}"*.pdb | grep -v 'N/A' | uniq > plddt.tsv echo -e Atom_serial_number"\\t"Atom_name"\\t"Residue_name"\\t"Residue_sequence_number"\\t"pLDDT > header.tsv cat header.tsv plddt.tsv > "${fasta.baseName}"_plddt_mqc.tsv diff --git a/workflows/alphafold2.nf b/workflows/alphafold2.nf index 3d2abe15..2374f34c 100644 --- a/workflows/alphafold2.nf +++ b/workflows/alphafold2.nf @@ -41,7 +41,6 @@ include { methodsDescriptionText } from '../subworkflows/local/utils_nfcore_prot workflow ALPHAFOLD2 { take: - ch_versions // channel: [ path(versions.yml) ] full_dbs // boolean: Use full databases (otherwise reduced version) alphafold2_mode // string: Mode to run Alphafold2 in alphafold2_model_preset // string: Specifies the model preset to use for Alphafold2 @@ -58,10 +57,12 @@ workflow ALPHAFOLD2 { main: ch_multiqc_files = Channel.empty() + ch_versions = Channel.empty() // // Create input channel from input file provided through params.input // + Channel .fromSamplesheet("input") .set { ch_fasta } @@ -81,6 +82,9 @@ workflow ALPHAFOLD2 { // // SUBWORKFLOW: Run Alphafold2 standard mode // + //full_dbs.view() + //ch_alphafold2_params.view() + RUN_ALPHAFOLD2 ( ch_fasta, full_dbs, @@ -96,6 +100,16 @@ workflow ALPHAFOLD2 { ch_pdb_seqres, ch_uniprot ) + RUN_ALPHAFOLD2.out.af_out_tsv + .map{[it[0], it[1].findAll{ it.getName().contains("_lddt_")}]} + .set{ch_af_out_lddt} + + RUN_ALPHAFOLD2.out.af_out_tsv + .map{[it[0], it[1].findAll{ it.getName().contains("_msa.tsv")}]} + .set{ch_af_out_msa} + + RUN_ALPHAFOLD2.out.af_out_pdb.set{ch_af_out_pdb} + ch_multiqc_rep = RUN_ALPHAFOLD2.out.multiqc.collect() ch_versions = ch_versions.mix(RUN_ALPHAFOLD2.out.versions) @@ -143,37 +157,28 @@ workflow ALPHAFOLD2 { ) ch_multiqc_rep = RUN_ALPHAFOLD2_PRED.out.multiqc.collect() ch_versions = ch_versions.mix(RUN_ALPHAFOLD2_PRED.out.versions) - RUN_ALPHAFOLD2_PRED.out.af_out.set{ch_alphafold_outputs} + RUN_ALPHAFOLD2_PRED.out.af_out_tsv + .map{[it[0], it[1].findAll{ it.getName().contains("_lddt_")}]} + .set{ch_af_out_lddt} + + RUN_ALPHAFOLD2_PRED.out.af_out_tsv + .map{[it[0], it[1].findAll{ it.getName().contains("_msa.tsv")}]} + .set{ch_af_out_msa} + + RUN_ALPHAFOLD2_PRED.out.af_out_pdb.set{ch_af_out_pdb} } - ch_alphafold_outputs - .map{[it[0].id, it[1].findAll { it.getName().endsWith('.pkl') && it.getName().startsWith('result_model_') } ]} - .set{ch_pred} - - EXTRACT_OUTPUTS(ch_features.mix(ch_pred)) - - - ch_alphafold_outputs - .map{[it[0].id, it[1].findAll { it.getName().endsWith('.pdb') && it.getName().startsWith('ranked_') } ]} - .set{ch_pdb} - - - EXTRACT_OUTPUTS.out.msa_info.join( - EXTRACT_OUTPUTS.out.lddt_info.join( - ch_pdb - ) - ).set{ch_all} - GENERATE_REPORT( - ch_all.map{[it[0], it[1]]}, - ch_all.map{[it[0], it[2]]}, - ch_all.map{[it[0], it[3]]}, + ch_af_out_msa, + ch_af_out_lddt, + ch_af_out_pdb, Channel.fromPath("$projectDir/assets/alphafold_template.html").first(), - Channel.fromPath("$projectDir/assets/generat_plots_2.py").first(), - + Channel.value("ALPHAFOLD2") ) - + + + // // Collate and save software versions // diff --git a/workflows/esmfold.nf b/workflows/esmfold.nf index cc8ccd35..4db38569 100644 --- a/workflows/esmfold.nf +++ b/workflows/esmfold.nf @@ -44,7 +44,7 @@ ch_multiqc_custom_methods_description = params.multiqc_methods_description ? fil // include { RUN_ESMFOLD } from '../modules/local/run_esmfold' include { MULTIFASTA_TO_SINGLEFASTA } from '../modules/local/multifasta_to_singlefasta' - +include { GENERATE_REPORT } from '../modules/local/generat_report' /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ IMPORT NF-CORE MODULES/SUBWORKFLOWS @@ -56,6 +56,7 @@ include { MULTIFASTA_TO_SINGLEFASTA } from '../modules/local/multifasta_to_singl // include { MULTIQC } from '../modules/nf-core/multiqc/main' + // // SUBWORKFLOW: Consisting entirely of nf-core/modules // @@ -111,6 +112,21 @@ workflow ESMFOLD { ch_versions = ch_versions.mix(RUN_ESMFOLD.out.versions) } + RUN_ESMFOLD.out.multiqc + .map{[it[0].id, it[0], it[1]]} + .join( + RUN_ESMFOLD.out.pdb + .map{[it[0].id, it[0], it[1]]} + ).set{ch_all} + + GENERATE_REPORT( + Channel.of([["id":"TEMP"], file("$projectDir/assets/NO_FILE")]), + ch_all.map{[it[1], [it[2]]]}, + ch_all.map{[it[3], [it[4]]]}, + Channel.fromPath("$projectDir/assets/alphafold_template.html").first(), + Channel.value("ESM-FOLD") + ) + // // Collate and save software versions // @@ -142,7 +158,7 @@ workflow ESMFOLD { ch_multiqc_files = ch_multiqc_files.mix(ch_workflow_summary.collectFile(name: 'workflow_summary_mqc.yaml')) ch_multiqc_files = ch_multiqc_files.mix(ch_methods_description.collectFile(name: 'methods_description_mqc.yaml')) ch_multiqc_files = ch_multiqc_files.mix(ch_collated_versions) - ch_multiqc_files = ch_multiqc_files.mix(RUN_ESMFOLD.out.multiqc.collect()) + ch_multiqc_files = ch_multiqc_files.mix(RUN_ESMFOLD.out.multiqc.map{it[1]}.collect()) MULTIQC ( ch_multiqc_files.collect(), @@ -151,6 +167,7 @@ workflow ESMFOLD { ch_multiqc_logo.toList() ) ch_multiqc_report = MULTIQC.out.report.toList() + emit: multiqc_report = ch_multiqc_report // channel: /path/to/multiqc_report.html versions = ch_versions // channel: [ path(versions.yml) ] From 6c4473288d5a61cf64f8f538299eb6e6ba0c2d86 Mon Sep 17 00:00:00 2001 From: ziadbkh Date: Mon, 20 May 2024 15:23:03 +1000 Subject: [PATCH 209/227] fix split msa --- modules/local/run_alphafold2_msa.nf | 4 ++-- workflows/alphafold2.nf | 14 +++++++++----- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/modules/local/run_alphafold2_msa.nf b/modules/local/run_alphafold2_msa.nf index 6c9bfcf8..28f8ceb9 100644 --- a/modules/local/run_alphafold2_msa.nf +++ b/modules/local/run_alphafold2_msa.nf @@ -28,8 +28,8 @@ process RUN_ALPHAFOLD2_MSA { path ('uniprot/*') output: - path ("${fasta.baseName}*") - path ("${fasta.baseName}.features.pkl"), emit: features + tuple val(meta), path ("${fasta.baseName}*") + tuple val(meta), path ("${fasta.baseName}.features.pkl"), emit: features path "versions.yml" , emit: versions when: diff --git a/workflows/alphafold2.nf b/workflows/alphafold2.nf index 2374f34c..0ae9ac8e 100644 --- a/workflows/alphafold2.nf +++ b/workflows/alphafold2.nf @@ -136,11 +136,15 @@ workflow ALPHAFOLD2 { ch_versions = ch_versions.mix(RUN_ALPHAFOLD2_MSA.out.versions) RUN_ALPHAFOLD2_MSA.out.features - .map{[it.getName().split("\\.")[0], [it]]} - .set{ch_features} - + .map{[it[0].id, it[0], it[1]]} + .join( + ch_fasta + .map{[it[0].id, it[0], it[1]]} + ) + .set{ch_af_all} + RUN_ALPHAFOLD2_PRED ( - ch_fasta, + ch_af_all.map{[it[3], it[4]]}, full_dbs, alphafold2_model_preset, ch_alphafold2_params, @@ -153,7 +157,7 @@ workflow ALPHAFOLD2 { ch_uniref90, ch_pdb_seqres, ch_uniprot, - RUN_ALPHAFOLD2_MSA.out.features + ch_af_all.map{it[2]} ) ch_multiqc_rep = RUN_ALPHAFOLD2_PRED.out.multiqc.collect() ch_versions = ch_versions.mix(RUN_ALPHAFOLD2_PRED.out.versions) From 2cf02ba407e61f5157906d7d6c6f2d414b3d13da Mon Sep 17 00:00:00 2001 From: ziadbkh Date: Mon, 20 May 2024 16:14:03 +1000 Subject: [PATCH 210/227] fix split esm-fold nofile channel --- workflows/esmfold.nf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workflows/esmfold.nf b/workflows/esmfold.nf index 4db38569..67fc8439 100644 --- a/workflows/esmfold.nf +++ b/workflows/esmfold.nf @@ -120,7 +120,7 @@ workflow ESMFOLD { ).set{ch_all} GENERATE_REPORT( - Channel.of([["id":"TEMP"], file("$projectDir/assets/NO_FILE")]), + Channel.value([["id":"TEMP"], file("$projectDir/assets/NO_FILE")]), ch_all.map{[it[1], [it[2]]]}, ch_all.map{[it[3], [it[4]]]}, Channel.fromPath("$projectDir/assets/alphafold_template.html").first(), From 83f5c14f37ec02deb9e6d029608d26fdd5661c3d Mon Sep 17 00:00:00 2001 From: ziadbkh Date: Mon, 1 Jul 2024 14:33:22 +1000 Subject: [PATCH 211/227] reports-update --- assets/alphafold_template.html | 17 +++++++++++++++++ bin/generat_plots_2.py | 11 +++++++++++ conf/dbs.config | 2 +- conf/gadi.config | 20 +++++++++++++------- nextflow.config | 1 - nextflow_schema.json | 23 ++++++++++++++++++++++- tower.yml | 2 ++ workflows/colabfold.nf | 4 ++-- workflows/esmfold.nf | 1 + 9 files changed, 69 insertions(+), 12 deletions(-) diff --git a/assets/alphafold_template.html b/assets/alphafold_template.html index 533e2fee..1655e078 100755 --- a/assets/alphafold_template.html +++ b/assets/alphafold_template.html @@ -570,6 +570,23 @@

    Download

    }) } + const loadModel_n = () => { + reps = Object.keys(state.representations); + if (reps.length) { + state.representations = {}; + } else { + reps = [DEFAULT_REPRESENTATION]; + } + + // Load PDB entry + return stage.loadFile( uri(state.model) ).then( (o) => { + state.modelObject = o; + reps.forEach( (r) => addModelRepresentation(r) ); + stage.setSpin(state.spin); + o.autoView(); + setLoading(0); + }) + } // Representations --------------------------------------------------------- const toggleModelRepresentation = (rep) => { diff --git a/bin/generat_plots_2.py b/bin/generat_plots_2.py index 87982a25..bf6bfbba 100755 --- a/bin/generat_plots_2.py +++ b/bin/generat_plots_2.py @@ -207,21 +207,29 @@ def generate_plots(msa_path, plddt_paths, name, out_dir): alphfold_template = open(args.html_template, "r").read() alphfold_template = alphfold_template.replace(f"*sample_name_here*", args.name) +alphfold_template2 = open(args.html_template, "r").read() +alphfold_template2 = alphfold_template2.replace(f"*sample_name_here*", args.name) + + i = 0 for structure in structures: alphfold_template = alphfold_template.replace(f"*_data_ranked_{i}.pdb*", open(structure, "r").read().replace("\n", "\\n")) + alphfold_template2 = alphfold_template2.replace(f"*_data_ranked_{i}.pdb*", structure) i += 1 if True: if not args.msa.endswith("NO_FILE"): with open(f"{args.output_dir}/{args.name + ('_' if args.name else '')}seq_coverage.png", "rb") as in_file: alphfold_template = alphfold_template.replace("seq_coverage.png", f"data:image/png;base64,{base64.b64encode(in_file.read()).decode('utf-8')}") + alphfold_template2 = alphfold_template2.replace("seq_coverage.png", f"{args.name + ('_' if args.name else '')}seq_coverage.png") else: alphfold_template = alphfold_template.replace("seq_coverage.png","") + alphfold_template2 = alphfold_template2.replace("seq_coverage.png","") for i in range(0, len(args.plddt)): with open(f"{args.output_dir}/{args.name + ('_' if args.name else '')}coverage_LDDT_{i}.png", "rb") as in_file: alphfold_template = alphfold_template.replace(f"coverage_LDDT_{i}.png", f"data:image/png;base64,{base64.b64encode(in_file.read()).decode('utf-8')}") + alphfold_template2 = alphfold_template2.replace(f"coverage_LDDT_{i}.png", f"{args.name + ('_' if args.name else '')}coverage_LDDT_{i}.png") """ @@ -235,3 +243,6 @@ def generate_plots(msa_path, plddt_paths, name, out_dir): """ with open(f"{args.output_dir}/{args.name}_alphafold.html", "w") as out_file: out_file.write(alphfold_template) + +with open(f"{args.output_dir}/{args.name}_alphafold2.html", "w") as out_file: + out_file.write(alphfold_template2) \ No newline at end of file diff --git a/conf/dbs.config b/conf/dbs.config index d8a9c126..f29e6c9a 100644 --- a/conf/dbs.config +++ b/conf/dbs.config @@ -54,5 +54,5 @@ params { esm2_t36_3B_UR50D_contact_regression = 'https://dl.fbaipublicfiles.com/fair-esm/regression/esm2_t36_3B_UR50D-contact-regression.pt' // Esmfold paths - esmfold_params_path = "${params.esmfold_db}/*" + //esmfold_params_path = "${params.esmfold_db}/*" } diff --git a/conf/gadi.config b/conf/gadi.config index d35ce0b0..23127eeb 100755 --- a/conf/gadi.config +++ b/conf/gadi.config @@ -12,22 +12,28 @@ if (params.use_gpu) { singularity.runOptions = '--nv' } + singularity.cacheDir = "/g/data/if89/singularity_cache/" singularity.autoMounts = true + params { mode = 'alphafold2' alphafold2_mode = 'split_msa_prediction' input = 'https://raw.githubusercontent.com/nf-core/test-datasets/proteinfold/testdata/samplesheet/v1.0/samplesheet.csv' - alphafold2_db = '/g/data/if89/alphafold2/standard/' + alphafold2_db = '/g/data/if89/protienfold_dbs/alphafold2_dbs/' use_dgxa100 = false - esmfold_params_path = '/g/data/if89/esm-fold/checkpoints' + esmfold_params_path = '/g/data/if89/protienfold_dbs/esmfold_dbs/checkpoints' + colabfold_db = "/g/data/if89/protienfold_dbs/colabfold_dbs" + colabfold_server = 'local' + colabfold_model_preset = 'alphafold2_ptm' + } process { storage = "gdata/if89+scratch/${params.project}+gdata/${params.project}" if (params.use_gpu) { - withName: 'RUN_ALPHAFOLD2_PRED|RUN_ALPHAFOLD2' { + withName: 'RUN_ALPHAFOLD2_PRED|RUN_ALPHAFOLD2|RUN_ESMFOLD' { if (params.use_dgxa100){ queue = "dgxa100" cpus = 16 @@ -39,9 +45,9 @@ process { } } - withName: 'RUN_ESMFOLD' { - queue = "copyq" - cpus = 1 - time = 10.h + withName: 'MMSEQS_COLABFOLDSEARCH' { + memory = 300.GB + time = 16.h } + } \ No newline at end of file diff --git a/nextflow.config b/nextflow.config index b4f72570..6777332d 100644 --- a/nextflow.config +++ b/nextflow.config @@ -13,7 +13,6 @@ params { input = null mode = 'alphafold2' // {alphafold2, colabfold, esmfold} use_gpu = false - // Alphafold2 parameters alphafold2_mode = "standard" max_template_date = "2020-05-14" diff --git a/nextflow_schema.json b/nextflow_schema.json index d2ef75da..bcd9c169 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -29,7 +29,7 @@ "description": "The output directory where the results will be saved. You have to use absolute paths to storage on Cloud infrastructure.", "fa_icon": "fas fa-folder-open" }, - "mode": { + "mode": { "type": "string", "default": "alphafold2", "description": "Specifies the mode in which the pipeline will be run", @@ -194,6 +194,27 @@ } } }, + "gadi_config_options": { + "title": "Institutional config options", + "type": "object", + "fa_icon": "fas fa-university", + "description": "Configurations for running on GADI at NCI.", + "help_text": ".", + "properties": { + "use_dgxa100": { + "type": "boolean", + "default": false, + "description": "If true uses the DGXA 100 GPU nodes", + "fa_icon": "fas fa-battery-full" + }, + "project": { + "type": "string", + "description": "Thge project code on GADI.", + "default": "", + "fa_icon": "fas fa-users-cog" + } + } + }, "institutional_config_options": { "title": "Institutional config options", "type": "object", diff --git a/tower.yml b/tower.yml index c0b95ad4..5e889484 100755 --- a/tower.yml +++ b/tower.yml @@ -5,3 +5,5 @@ reports: display: "Auto-created samplesheet with collated metadata and FASTQ paths" "*_alphafold.html": display: "Predected structures" + "*_alphafold2.html": + display: "Predected structures 2" diff --git a/workflows/colabfold.nf b/workflows/colabfold.nf index aa85d116..a6788faa 100644 --- a/workflows/colabfold.nf +++ b/workflows/colabfold.nf @@ -56,7 +56,7 @@ workflow COLABFOLD { Channel .fromSamplesheet("input") .set { ch_fasta } - + ch_fasta.view() if (params.colabfold_server == 'webserver') { // // MODULE: Run colabfold @@ -91,7 +91,7 @@ workflow COLABFOLD { // // MODULE: Run mmseqs // - if (params.colabfold_model_preset != 'AlphaFold2-ptm') { + if (params.colabfold_model_preset != 'alphafold2_ptm') { MULTIFASTA_TO_CSV( ch_fasta ) diff --git a/workflows/esmfold.nf b/workflows/esmfold.nf index 67fc8439..c6c6d308 100644 --- a/workflows/esmfold.nf +++ b/workflows/esmfold.nf @@ -85,6 +85,7 @@ workflow ESMFOLD { // // Create input channel from input file provided through params.input // + Channel .fromSamplesheet("input") .set { ch_fasta } From 2a0a8241680d97cbcbbf2d97fb937241ec1f2b28 Mon Sep 17 00:00:00 2001 From: ziadbkh Date: Tue, 2 Jul 2024 14:53:17 +1000 Subject: [PATCH 212/227] add plots --- tower.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tower.yml b/tower.yml index 5e889484..b6d2d138 100755 --- a/tower.yml +++ b/tower.yml @@ -7,3 +7,5 @@ reports: display: "Predected structures" "*_alphafold2.html": display: "Predected structures 2" + "*.png": + display: "plots" \ No newline at end of file From 3907a7d53bd685263164bf15a6c505a45a6ca290 Mon Sep 17 00:00:00 2001 From: ziadbkh Date: Fri, 19 Jul 2024 17:01:05 +1000 Subject: [PATCH 213/227] new report --- assets/alphafold_template.html | 1253 ++++++++++------- assets/alphafold_template_v0.html | 705 ++++++++++ bin/generat_plots.py | 341 +++++ ...enerat_plots_2.py => generat_plots_old.py} | 0 conf/test.config | 2 +- main.nf | 10 - modules/local/generat_report.nf | 3 +- modules/local/old_run_alphafold2.nf | 57 + workflows/alphafold2.nf | 2 +- 9 files changed, 1846 insertions(+), 527 deletions(-) create mode 100755 assets/alphafold_template_v0.html create mode 100755 bin/generat_plots.py rename bin/{generat_plots_2.py => generat_plots_old.py} (100%) create mode 100644 modules/local/old_run_alphafold2.nf diff --git a/assets/alphafold_template.html b/assets/alphafold_template.html index 1655e078..1074c1c4 100755 --- a/assets/alphafold_template.html +++ b/assets/alphafold_template.html @@ -1,496 +1,650 @@ - - + - - - - - Alphafold structure prediction - - - - - - + + + + Alphafold structure prediction + + + + + - - - -

    Alphafold structure prediction

    -
    -
    -
    -
    -
    -
    - -
    - -
    - Select a representation to display -
    - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    - -
    -
    -

    - Scroll up/down - to zoom in and out -

    -

    - Click + drag - to rotate the structure -

    -

    - CTRL + click + drag - to move the structure -

    -

    - Click - an atom to bring it into focus -

    -
    - -
    -
    -
    -
    -
    <50
    -
    70
    -
    90+
    -
    -
    - -
    -

    - - Alphafold produces a - - per-residue confidence score (pLDDT) - - between 0 and 100. Some regions below 50 pLDDT may be - unstructured in isolation. - -

    -
    -
    + + + + + + + + +
    +
    +

    + Alphafold structure prediction (*sample_name_here*) +

    +
    +
    + + +
    + +
    + +
    + + + + + +
    + +
    + + + + + +
    + +
    +
    + Navigation +
    +
    +
    + Scroll up/down + to zoom in and out +
    +
    + Click + drag + to rotate the structure +
    +
    + CTRL + click + drag + to move the structure +
    +
    + Click + an atom to bring it into focus +
    +
    +
    + + +
    +
    +
    + Toggle representations +
    +
    + + + + +
    +
    + +
    +
    + Actions +
    +
    + + + +
    +
    + +
    +
    + Download +
    + +
    + +
    -
    -
    -
    -
    - +
    + +
    +
    +
    +
    +
    +
    +
    + + +
    +
    +
    + Sequence Coverage +
    +
    +
    + +
    +
    + +
    +
    + pLDDT +
    +
    + +
    +
    +
    +
    +
    + +
    - + +
    +
    +
    + + +
    +
    +

    + The Australian BioCommons + is supported by + Bioplatforms Australia +

    +

    + Bioplatforms Australia + is enabled by + NCRIS +

    +
    +
    +
    + - diff --git a/assets/alphafold_template_v0.html b/assets/alphafold_template_v0.html new file mode 100755 index 00000000..1655e078 --- /dev/null +++ b/assets/alphafold_template_v0.html @@ -0,0 +1,705 @@ + + + + + + + + + Alphafold structure prediction + + + + + + + + + + + + + + +

    Alphafold structure prediction

    +
    +
    +
    +
    +
    +
    + +
    + +
    + Select a representation to display +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +
    + +
    +
    +

    + Scroll up/down + to zoom in and out +

    +

    + Click + drag + to rotate the structure +

    +

    + CTRL + click + drag + to move the structure +

    +

    + Click + an atom to bring it into focus +

    +
    + +
    +
    +
    +
    +
    <50
    +
    70
    +
    90+
    +
    +
    + +
    +

    + + Alphafold produces a + + per-residue confidence score (pLDDT) + + between 0 and 100. Some regions below 50 pLDDT may be + unstructured in isolation. + +

    +
    +
    +
    +
    + +
    +
    +

    Select model

    +

    The top-ranked structures predicted by Alphafold

    +
    + + + + + + + + + +
    +
    + +
    +

    Toggle representations

    +
    + + + + + + + +
    +
    + +
    +

    Actions

    +
    + + + +
    +
    + +
    +

    Download

    +
    + + + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    + + + + + + diff --git a/bin/generat_plots.py b/bin/generat_plots.py new file mode 100755 index 00000000..cc666ae6 --- /dev/null +++ b/bin/generat_plots.py @@ -0,0 +1,341 @@ +#!/usr/bin/env python + +import os +from matplotlib import pyplot as plt +import argparse +from collections import OrderedDict +import base64 +import os +from collections import OrderedDict +import plotly.graph_objects as go +from plotly.subplots import make_subplots + + +#from Bio import PDB + +def generate_output_images(msa_path, plddt_paths, name, out_dir, in_type): + msa = [] + if not msa_path.endswith("NO_FILE"): + with open(msa_path, 'r') as in_file: + for line in in_file: + msa.append([int(x) for x in line.strip().split()]) + + seqid = [] + for sequence in msa: + matches = [1.0 if first == other else 0.0 for first, other in zip(msa[0], sequence)] + seqid.append(sum(matches) / len(matches)) + + seqid_sort = sorted(range(len(seqid)), key=seqid.__getitem__) + + non_gaps = [] + for sequence in msa: + non_gaps.append([float(num != 21) if num != 21 else float('nan') for num in sequence]) + + sorted_non_gaps = [non_gaps[i] for i in seqid_sort] + final = [] + for sorted_seq, identity in zip(sorted_non_gaps, [seqid[i] for i in seqid_sort]): + final.append([value * identity if not isinstance(value, str) else value for value in sorted_seq]) + + # ################################################################## + plt.figure(figsize=(14, 14), dpi=100) + # ################################################################## + plt.title("Sequence coverage", fontsize=30, pad=36) + plt.imshow(final, + interpolation='nearest', aspect='auto', + cmap="rainbow_r", vmin=0, vmax=1, origin='lower') + + column_counts = [0] * len(msa[0]) + for col in range(len(msa[0])): + for row in msa: + if row[col] != 21: + column_counts[col] += 1 + + plt.plot(column_counts, color='black') + plt.xlim(-0.5, len(msa[0]) - 0.5) + plt.ylim(-0.5, len(msa) - 0.5) + + plt.tick_params(axis='both', which='both', labelsize=18) + + cbar = plt.colorbar() + cbar.set_label("Sequence identity to query", fontsize=24, labelpad=24) + cbar.ax.tick_params(labelsize=18) + plt.xlabel("Positions", fontsize=24, labelpad=24) + plt.ylabel("Sequences", fontsize=24, labelpad=36) + plt.savefig(f"{out_dir}/{name+('_' if name else '')}seq_coverage.png") + + # ################################################################## + + plddt_per_model = OrderedDict() + plddt_paths_srt = plddt_paths + plddt_paths_srt.sort() + for plddt_path in plddt_paths_srt: + with open(plddt_path, 'r') as in_file: + if in_type == "ESM-FOLD": + plddt_per_model[os.path.basename(plddt_path)[:-4]] = [] + in_file.readline() + for line in in_file: + vals = line.strip().split() + #print(vals) + if len(vals) == 5: + plddt_per_model[os.path.basename(plddt_path)[:-4]].append(float(vals[-1].strip())) + else: + plddt_per_model[os.path.basename(plddt_path)[:-4]] = [float(x) for x in in_file.read().strip().split()] + + # plt.figure(figsize=(14, 14), dpi=100) + # plt.title("Predicted LDDT per position") + # for model_name, value_plddt in plddt_per_model.items(): + # plt.plot(value_plddt, label=model_name) + # plt.ylim(0, 100) + # plt.ylabel("Predicted LDDT") + # plt.xlabel("Positions") + # plt.savefig(f"{out_dir}/{name+('_' if name else '')}coverage_LDDT.png") + + # # split into figures + # i = 0 + # for model_name, value_plddt in plddt_per_model.items(): + # plt.figure(figsize=(14, 14), dpi=100) + # plt.title("Predicted LDDT per position") + # plt.plot(value_plddt, label=model_name) + # plt.ylim(0, 100) + # plt.ylabel("Predicted LDDT") + # plt.xlabel("Positions") + # plt.savefig(f"{out_dir}/{name+('_' if name else '')}coverage_LDDT_{i}.png") + # i += 1 + + fig = go.Figure() + for idx, (model_name, value_plddt) in enumerate(plddt_per_model.items()): + rank_label = f"Ranked {idx}" + fig.add_trace(go.Scatter( + x=list(range(len(value_plddt))), + y=value_plddt, + mode='lines', + name=rank_label, + text=[f"Position {i}: {value:.2f}" for i, value in enumerate(value_plddt)], + hoverinfo='text' + )) + fig.update_layout( + title=dict( + text='Predicted LDDT per position', + x=0.5, + xanchor='center' + ), + xaxis=dict( + title='Positions', + showline=True, + linecolor='black', + gridcolor='WhiteSmoke' + ), + yaxis=dict( + title='Predicted LDDT', + range=[0, 100], + minallowed=0, + maxallowed=100, + showline=True, + linecolor='black', + gridcolor='WhiteSmoke' + ), + legend=dict( + yanchor="bottom", + y=0, + xanchor="right", + x=1.3 + ), + plot_bgcolor='white', + width=600, + height=600, + modebar_remove=['toImage', 'zoomIn', 'zoomOut'] + ) + html_content = fig.to_html(full_html=False, config={'displayModeBar': True, 'displaylogo': False, 'scrollZoom': True}) + + with open(f"{out_dir}/{name+('_' if name else '')}coverage_LDDT.html", "w") as out_file: + out_file.write(html_content) + + + ################################################################## + + + ################################################################## + """ + num_models = 5 # columns + num_runs_per_model = math.ceil(len(model_names)/num_models) + fig = plt.figure(figsize=(3 * num_models, 2 * num_runs_per_model), dpi=100) + for n, (model_name, value) in enumerate(pae_plddt_per_model.items()): + plt.subplot(num_runs_per_model, num_models, n + 1) + plt.title(model_name) + plt.imshow(value["pae"], label=model_name, cmap="bwr", vmin=0, vmax=30) + plt.colorbar() + fig.tight_layout() + plt.savefig(f"{out_dir}/{name+('_' if name else '')}PAE.png") + """ + ################################################################## + + +def generate_plots(msa_path, plddt_paths, name, out_dir): + msa = [] + with open(msa_path, 'r') as in_file: + for line in in_file: + msa.append([int(x) for x in line.strip().split()]) + + seqid = [] + for sequence in msa: + matches = [1.0 if first == other else 0.0 for first, other in zip(msa[0], sequence)] + seqid.append(sum(matches) / len(matches)) + + seqid_sort = sorted(range(len(seqid)), key=seqid.__getitem__) + + non_gaps = [] + for sequence in msa: + non_gaps.append([float(num != 21) if num != 21 else float('nan') for num in sequence]) + + sorted_non_gaps = [non_gaps[i] for i in seqid_sort] + final = [] + for sorted_seq, identity in zip(sorted_non_gaps, [seqid[i] for i in seqid_sort]): + final.append([value * identity if not isinstance(value, str) else value for value in sorted_seq]) + + # Plotting Sequence Coverage using Plotly + fig = go.Figure() + fig.add_trace(go.Heatmap( + z=final, + colorscale="Rainbow", + zmin=0, + zmax=1, + )) + fig.update_layout( + title="Sequence coverage", + xaxis_title="Positions", + yaxis_title="Sequences" + ) + # Save as interactive HTML instead of an image + fig.savefig(f"{out_dir}/{name+('_' if name else '')}seq_coverage.png") + """ + #fig.to_html(full_html=False).write_html(f"{out_dir}/{name+('_' if name else '')}seq_coverage.html") + with open (f"{out_dir}/{name+('_' if name else '')}seq_coverage.html", "w") as out_plt: + out_plt.write(fig.to_html(full_html=False)) + """ + # Plotting Predicted LDDT per position using Plotly + plddt_per_model = OrderedDict() + plddt_paths.sort() + for plddt_path in plddt_paths: + with open(plddt_path, 'r') as in_file: + plddt_per_model[os.path.basename(plddt_path)[:-4]] = [float(x) for x in in_file.read().strip().split()] + + i = 0 + for model_name, value_plddt in plddt_per_model.items(): + fig = go.Figure() + fig.add_trace(go.Scatter( + x=list(range(len(value_plddt))), + y=value_plddt, + mode='lines', + name=model_name + )) + fig.update_layout(title="Predicted LDDT per Position") + fig.savefig(f"{out_dir}/{name+('_' if name else '')}coverage_LDDT_{i}.png") + """ + with open (f"{out_dir}/{name+('_' if name else '')}coverage_LDDT_{i}.html", "w") as out_plt: + out_plt.write(fig.to_html(full_html=False).replace("\"", "\\\"")) + """ + i += 1 + +def align_structures(structures): + return structures + """ + parser = PDB.PDBParser(QUIET=True) + structures = [parser.get_structure(f'Structure_{i}', pdb) for i, pdb in enumerate(structures)] + + ref_structure = structures[0] + ref_atoms = [atom for atom in ref_structure.get_atoms()] + + super_imposer = PDB.Superimposer() + aligned_structures = [structures[0]] # Include the reference structure in the list + + for i, structure in enumerate(structures[1:], start=1): + target_atoms = [atom for atom in structure.get_atoms()] + + super_imposer.set_atoms(ref_atoms, target_atoms) + super_imposer.apply(structure.get_atoms()) + + aligned_structure = f'aligned_structure_{i}.pdb' + io = PDB.PDBIO() + io.set_structure(structure) + io.save(aligned_structure) + aligned_structures.append(aligned_structure) + + return aligned_structures + """ + +print("Starting..") +parser = argparse.ArgumentParser() +parser.add_argument('--type', dest='in_type') +parser.add_argument('--msa', dest='msa',required=True) +parser.add_argument('--plddt', dest='plddt',required=True, nargs="+") +parser.add_argument('--pdb', dest='pdb',required=True, nargs="+") +parser.add_argument('--name', dest='name') +parser.add_argument('--output_dir',dest='output_dir') +parser.add_argument('--html_template',dest='html_template') +parser.set_defaults(output_dir='') +parser.set_defaults(in_type='ESM-FOLD') +parser.set_defaults(name='') +args = parser.parse_args() + + +generate_output_images(args.msa, args.plddt, args.name, args.output_dir, args.in_type) + +#generate_plots(args.msa, args.plddt, args.name, args.output_dir) + +print("generating html report...") +structures = args.pdb +structures.sort() +aligned_structures = align_structures(structures) + +## Ziad uncomment this +""" +io = PDB.PDBIO() +ref_structure_path = 'aligned_structure_0.pdb' +io.set_structure(aligned_structures[0]) +io.save(ref_structure_path) +aligned_structures[0] = ref_structure_path +""" +alphfold_template = open(args.html_template, "r").read() +alphfold_template = alphfold_template.replace(f"*sample_name_here*", args.name) + +i = 0 +for structure in aligned_structures: + alphfold_template = alphfold_template.replace(f"*_data_ranked_{i}.pdb*", open(structure, "r").read().replace("\n", "\\n")) + i += 1 + +if True: + if not args.msa.endswith("NO_FILE"): + with open(f"{args.output_dir}/{args.name + ('_' if args.name else '')}seq_coverage.png", "rb") as in_file: + alphfold_template = alphfold_template.replace("seq_coverage.png", f"data:image/png;base64,{base64.b64encode(in_file.read()).decode('utf-8')}") + + # with open(f"{args.output_dir}/{args.name + ('_' if args.name else '')}seq_coverage.html", "r") as in_file: + # seq_cov_html = in_file.read() + # alphfold_template = alphfold_template.replace("
    ", seq_cov_html) + + else: + alphfold_template = alphfold_template.replace("seq_coverage.png","") + + # for i in range(0, len(args.plddt)): + # with open(f"{args.output_dir}/{args.name + ('_' if args.name else '')}coverage_LDDT_{i}.png", "rb") as in_file: + # alphfold_template = alphfold_template.replace(f"coverage_LDDT_{i}.png", f"data:image/png;base64,{base64.b64encode(in_file.read()).decode('utf-8')}") + + # for i in range(0, len(args.plddt)): + # with open(f"{args.output_dir}/{args.name + ('_' if args.name else '')}coverage_LDDT_{i}.html", "r") as in_file: + # lddt_html = in_file.read() + # alphfold_template = alphfold_template.replace("
    ", lddt_html) + + with open(f"{args.output_dir}/{args.name + ('_' if args.name else '')}coverage_LDDT.html", "r") as in_file: + lddt_html = in_file.read() + alphfold_template = alphfold_template.replace("
    ", lddt_html) + +""" +with open(f"{args.output_dir}/{args.name + ('_' if args.name else '')}seq_coverage.html", "r") as in_file: + alphfold_template = alphfold_template.replace(f"seq_coverage.png", f"{in_file.read()}") + +for i in range(0, 5): + with open(f"{args.output_dir}/{args.name + ('_' if args.name else '')}coverage_LDDT_{i}.html", "r") as in_file: + alphfold_template = alphfold_template.replace(f"coverage_LDDT_{i}.png", f"{in_file.read()}") + +""" +with open(f"{args.output_dir}/{args.name}_report.html", "w") as out_file: + out_file.write(alphfold_template) diff --git a/bin/generat_plots_2.py b/bin/generat_plots_old.py similarity index 100% rename from bin/generat_plots_2.py rename to bin/generat_plots_old.py diff --git a/conf/test.config b/conf/test.config index 4001a162..dfdebd88 100644 --- a/conf/test.config +++ b/conf/test.config @@ -10,7 +10,7 @@ ---------------------------------------------------------------------------------------- */ -stubRun = true +//stubRun = true params { config_profile_name = 'Test profile' diff --git a/main.nf b/main.nf index 20d59aff..627c747c 100644 --- a/main.nf +++ b/main.nf @@ -81,17 +81,7 @@ workflow NFCORE_PROTEINFOLD { ch_small_bfd = Channel.fromPath( params.small_bfd_path) ch_bfd = Channel.fromPath( params.bfd_path) - /*ch_params.view() - ch_params.first().view() - ch_bfd.ifEmpty([]).first().view() - ch_small_bfd.ifEmpty([]).first().view() - ch_uniref90.first().view() - ch_pdb_seqres.first().view() - ch_uniprot.first().view() - - - */ ALPHAFOLD2 ( diff --git a/modules/local/generat_report.nf b/modules/local/generat_report.nf index 69875541..a58a6f22 100644 --- a/modules/local/generat_report.nf +++ b/modules/local/generat_report.nf @@ -4,6 +4,7 @@ process GENERATE_REPORT { container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? 'https://depot.galaxyproject.org/singularity/multiqc:1.21--pyhdfd78af_0' : 'biocontainers/multiqc:1.21--pyhdfd78af_0' }" + conda "bioconda::multiqc=1.21" input: tuple val(meta_msa), path(msa) @@ -23,6 +24,6 @@ process GENERATE_REPORT { def args = task.ext.args ?: '' """ - generat_plots_2.py --type ${output_type} --msa ${msa} --plddt ${lddt.join(' ')} --pdb ${pdb.join(' ')} --html_template ${template} --output_dir ./ --name ${meta.id} + generat_plots.py --type ${output_type} --msa ${msa} --plddt ${lddt.join(' ')} --pdb ${pdb.join(' ')} --html_template ${template} --output_dir ./ --name ${meta.id} """ } diff --git a/modules/local/old_run_alphafold2.nf b/modules/local/old_run_alphafold2.nf new file mode 100644 index 00000000..570243d5 --- /dev/null +++ b/modules/local/old_run_alphafold2.nf @@ -0,0 +1,57 @@ +/* + * Run Alphafold2 + */ +process RUN_ALPHAFOLD2 { + tag "$meta.id" + label 'process_medium' + + // Exit if running this module with -profile conda / -profile mamba + if (workflow.profile.tokenize(',').intersect(['conda', 'mamba']).size() >= 1) { + error("Local RUN_ALPHAFOLD2 module does not support Conda. Please use Docker / Singularity / Podman instead.") + } + + container "nf-core/proteinfold_alphafold2_standard:dev" + + input: + tuple val(meta), path(fasta) + val db_preset + val alphafold2_model_preset + path ('params/*') + path ('bfd/*') + path ('small_bfd/*') + path ('mgnify/*') + path ('pdb70/*') + path ('pdb_mmcif/*') + path ('uniref30/*') + path ('uniref90/*') + path ('pdb_seqres/*') + path ('uniprot/*') + + output: + tuple val(meta), path ("${fasta.baseName}*"), emit: af_out + tuple val(meta), path ("${fasta.baseName}/${fasta.baseName}*tsv"), emit: af_out_tsv + tuple val(meta), path ("${fasta.baseName}/ranked*pdb"), emit: af_out_pdb + path "*_mqc.tsv", emit: multiqc + path "versions.yml", emit: versions + + when: + task.ext.when == null || task.ext.when + + script: + + """ + cp -r /mnt/d/01852933ca43cd53eb240fcf350f32/* ./ + + """ + + stub: + """ + touch ./"${fasta.baseName}".alphafold.pdb + touch ./"${fasta.baseName}"_mqc.tsv + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + awk: \$(gawk --version| head -1 | sed 's/GNU Awk //; s/, API:.*//') + END_VERSIONS + """ +} diff --git a/workflows/alphafold2.nf b/workflows/alphafold2.nf index 0ae9ac8e..5870820d 100644 --- a/workflows/alphafold2.nf +++ b/workflows/alphafold2.nf @@ -172,7 +172,7 @@ workflow ALPHAFOLD2 { RUN_ALPHAFOLD2_PRED.out.af_out_pdb.set{ch_af_out_pdb} } - + GENERATE_REPORT( ch_af_out_msa, ch_af_out_lddt, From 00266e1d3689d756c55bbb9ac6389d38cc15fbb2 Mon Sep 17 00:00:00 2001 From: Minh Vu Date: Wed, 24 Jul 2024 11:42:29 +1000 Subject: [PATCH 214/227] updated alphafold template with info on the left & navigation on the right --- ...iocommons-Logo-Horizontal-Reversed-RGB.png | Bin 0 -> 51235 bytes assets/alphafold_template.html | 186 ++++++------------ bin/generat_plots.py | 2 +- 3 files changed, 57 insertions(+), 131 deletions(-) create mode 100644 assets/Australian-Biocommons-Logo-Horizontal-Reversed-RGB.png diff --git a/assets/Australian-Biocommons-Logo-Horizontal-Reversed-RGB.png b/assets/Australian-Biocommons-Logo-Horizontal-Reversed-RGB.png new file mode 100644 index 0000000000000000000000000000000000000000..db7920059a1f913d3c7110c01b56be5d750a350e GIT binary patch literal 51235 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 Ms-=>1)9k - Alphafold structure prediction + Protein structure prediction - + - - - - - - - - - - - - - - - - - - -
    - -
    - -
    - - - - - -
    - -
    - - -
    -
    Program: (*prog_name*)
    -
    ID: (*sample_name*)
    -
    - - - - - - - - - -
    - - - - - - - - - -
    - -
    - - -
    - -
    -
    Navigation
    -
    -
    - Scroll up/down - to zoom in and out -
    -
    - Click + drag - to rotate the structure -
    -
    - CTRL + click + drag - to move the structure -
    -
    - Click - an atom to bring it into focus -
    -
    -
    - - -
    -
    -
    Toggle representations
    -
    - - - - -
    -
    - -
    -
    Actions
    -
    - - - -
    -
    -
    -
    Download
    - -
    - - -
    -
    -
    -
    -
    - -
    -
    -
    -
    -
    -
    -
    - - -
    -
    -
    Sequence Coverage
    -
    -
    - -
    - -
    -
    - -
    -
    pLDDT
    -
    - -
    -
    -
    -
    -
    - - -
    - - -
    -
    -
    - - -
    -
    -

    - The Australian BioCommons - is supported by - Bioplatforms Australia -

    -

    - Bioplatforms Australia - is enabled by - NCRIS -

    -
    -
    -
    - - - - diff --git a/assets/alphafold_template_v0.html b/assets/alphafold_template_v0.html deleted file mode 100755 index 1655e078..00000000 --- a/assets/alphafold_template_v0.html +++ /dev/null @@ -1,705 +0,0 @@ - - - - - - - - - Alphafold structure prediction - - - - - - - - - - - - - - -

    Alphafold structure prediction

    -
    -
    -
    -
    -
    -
    - -
    - -
    - Select a representation to display -
    - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    - -
    -
    -

    - Scroll up/down - to zoom in and out -

    -

    - Click + drag - to rotate the structure -

    -

    - CTRL + click + drag - to move the structure -

    -

    - Click - an atom to bring it into focus -

    -
    - -
    -
    -
    -
    -
    <50
    -
    70
    -
    90+
    -
    -
    - -
    -

    - - Alphafold produces a - - per-residue confidence score (pLDDT) - - between 0 and 100. Some regions below 50 pLDDT may be - unstructured in isolation. - -

    -
    -
    -
    -
    - -
    -
    -

    Select model

    -

    The top-ranked structures predicted by Alphafold

    -
    - - - - - - - - - -
    -
    - -
    -

    Toggle representations

    -
    - - - - - - - -
    -
    - -
    -

    Actions

    -
    - - - -
    -
    - -
    -

    Download

    -
    - - - -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    - -
    -
    -
    - - - - - - diff --git a/assets/proteinfold_template.html b/assets/proteinfold_template.html new file mode 100755 index 00000000..8d6dfc81 --- /dev/null +++ b/assets/proteinfold_template.html @@ -0,0 +1,856 @@ + + + + + + + Protein structure prediction + + + + + + + + + + + + + + + + + + + + + +
    + +
    + +
    + + + + + +
    + +
    + + +
    +
    Program: (*prog_name*)
    +
    ID: (*sample_name*)
    +
    + + + + + + + + + +
    + + + + + + + + + +
    + +
    + + +
    + +
    +
    Navigation
    +
    +
    + Scroll up/down + to zoom in and out +
    +
    + Click + drag + to rotate the structure +
    +
    + CTRL + click + drag + to move the structure +
    +
    + Click + an atom to bring it into focus +
    +
    +
    + + +
    +
    +
    Toggle representations
    +
    + + + + +
    +
    + +
    +
    Actions
    +
    + + + +
    +
    +
    +
    Download
    + +
    + + +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    + + +
    +
    +
    Sequence Coverage
    +
    +
    + +
    + +
    +
    + +
    +
    pLDDT
    +
    + +
    +
    +
    +
    +
    + + +
    + + +
    +
    +
    + + +
    +
    +

    + The Australian BioCommons + is supported by + Bioplatforms Australia +

    +

    + Bioplatforms Australia + is enabled by + NCRIS +

    +
    +
    +
    + + + + diff --git a/bin/generat_plots.py b/bin/generat_plots.py index aed3171f..e9f44f22 100755 --- a/bin/generat_plots.py +++ b/bin/generat_plots.py @@ -297,6 +297,7 @@ def align_structures(structures): """ alphfold_template = open(args.html_template, "r").read() alphfold_template = alphfold_template.replace(f"*sample_name*", args.name) +alphfold_template = alphfold_template.replace(f"*prog_name*", args.in_type) i = 0 for structure in aligned_structures: @@ -337,5 +338,5 @@ def align_structures(structures): alphfold_template = alphfold_template.replace(f"coverage_LDDT_{i}.png", f"{in_file.read()}") """ -with open(f"{args.output_dir}/{args.name}_report.html", "w") as out_file: +with open(f"{args.output_dir}/{args.name}_${args.in_type}_report.html", "w") as out_file: out_file.write(alphfold_template) diff --git a/bin/generat_plots_old.py b/bin/generat_plots_old.py deleted file mode 100755 index bf6bfbba..00000000 --- a/bin/generat_plots_old.py +++ /dev/null @@ -1,248 +0,0 @@ -#!/usr/bin/env python - -import os -from matplotlib import pyplot as plt -import argparse -from collections import OrderedDict -import base64 -import os -from collections import OrderedDict -import plotly.graph_objects as go -from plotly.subplots import make_subplots - -def generate_output_images(msa_path, plddt_paths, name, out_dir, in_type): - msa = [] - if not msa_path.endswith("NO_FILE"): - with open(msa_path, 'r') as in_file: - for line in in_file: - msa.append([int(x) for x in line.strip().split()]) - - seqid = [] - for sequence in msa: - matches = [1.0 if first == other else 0.0 for first, other in zip(msa[0], sequence)] - seqid.append(sum(matches) / len(matches)) - - seqid_sort = sorted(range(len(seqid)), key=seqid.__getitem__) - - non_gaps = [] - for sequence in msa: - non_gaps.append([float(num != 21) if num != 21 else float('nan') for num in sequence]) - - sorted_non_gaps = [non_gaps[i] for i in seqid_sort] - final = [] - for sorted_seq, identity in zip(sorted_non_gaps, [seqid[i] for i in seqid_sort]): - final.append([value * identity if not isinstance(value, str) else value for value in sorted_seq]) - - ################################################################## - plt.figure(figsize=(14, 14), dpi=100) - ################################################################## - plt.title("Sequence coverage") - plt.imshow(final, - interpolation='nearest', aspect='auto', - cmap="rainbow_r", vmin=0, vmax=1, origin='lower') - - column_counts = [0] * len(msa[0]) - for col in range(len(msa[0])): - for row in msa: - if row[col] != 21: - column_counts[col] += 1 - - plt.plot(column_counts, color='black') - plt.xlim(-0.5, len(msa[0]) - 0.5) - plt.ylim(-0.5, len(msa) - 0.5) - - plt.colorbar(label="Sequence identity to query", ) - plt.xlabel("Positions") - plt.ylabel("Sequences") - plt.savefig(f"{out_dir}/{name+('_' if name else '')}seq_coverage.png") - - ################################################################## - - plddt_per_model = OrderedDict() - plddt_paths_srt = plddt_paths - plddt_paths_srt.sort() - for plddt_path in plddt_paths_srt: - with open(plddt_path, 'r') as in_file: - if in_type == "ESM-FOLD": - plddt_per_model[os.path.basename(plddt_path)[:-4]] = [] - in_file.readline() - for line in in_file: - vals = line.strip().split() - #print(vals) - if len(vals) == 5: - plddt_per_model[os.path.basename(plddt_path)[:-4]].append(float(vals[-1].strip())) - else: - plddt_per_model[os.path.basename(plddt_path)[:-4]] = [float(x) for x in in_file.read().strip().split()] - - plt.figure(figsize=(14, 14), dpi=100) - plt.title("Predicted LDDT per position") - for model_name, value_plddt in plddt_per_model.items(): - plt.plot(value_plddt, label=model_name) - plt.ylim(0, 100) - plt.ylabel("Predicted LDDT") - plt.xlabel("Positions") - plt.savefig(f"{out_dir}/{name+('_' if name else '')}coverage_LDDT.png") - - # split into figures - i = 0 - for model_name, value_plddt in plddt_per_model.items(): - plt.figure(figsize=(14, 14), dpi=100) - plt.title("Predicted LDDT per position") - plt.plot(value_plddt, label=model_name) - plt.ylim(0, 100) - plt.ylabel("Predicted LDDT") - plt.xlabel("Positions") - plt.savefig(f"{out_dir}/{name+('_' if name else '')}coverage_LDDT_{i}.png") - i += 1 - - - ################################################################## - - - ################################################################## - """ - num_models = 5 # columns - num_runs_per_model = math.ceil(len(model_names)/num_models) - fig = plt.figure(figsize=(3 * num_models, 2 * num_runs_per_model), dpi=100) - for n, (model_name, value) in enumerate(pae_plddt_per_model.items()): - plt.subplot(num_runs_per_model, num_models, n + 1) - plt.title(model_name) - plt.imshow(value["pae"], label=model_name, cmap="bwr", vmin=0, vmax=30) - plt.colorbar() - fig.tight_layout() - plt.savefig(f"{out_dir}/{name+('_' if name else '')}PAE.png") - """ - ################################################################## - - -def generate_plots(msa_path, plddt_paths, name, out_dir): - msa = [] - with open(msa_path, 'r') as in_file: - for line in in_file: - msa.append([int(x) for x in line.strip().split()]) - - seqid = [] - for sequence in msa: - matches = [1.0 if first == other else 0.0 for first, other in zip(msa[0], sequence)] - seqid.append(sum(matches) / len(matches)) - - seqid_sort = sorted(range(len(seqid)), key=seqid.__getitem__) - - non_gaps = [] - for sequence in msa: - non_gaps.append([float(num != 21) if num != 21 else float('nan') for num in sequence]) - - sorted_non_gaps = [non_gaps[i] for i in seqid_sort] - final = [] - for sorted_seq, identity in zip(sorted_non_gaps, [seqid[i] for i in seqid_sort]): - final.append([value * identity if not isinstance(value, str) else value for value in sorted_seq]) - - # Plotting Sequence Coverage using Plotly - fig = go.Figure() - fig.add_trace(go.Heatmap( - z=final, - colorscale="Rainbow", - zmin=0, - zmax=1, - )) - fig.update_layout( - title="Sequence coverage", - xaxis_title="Positions", - yaxis_title="Sequences" - ) - # Save as interactive HTML instead of an image - fig.savefig(f"{out_dir}/{name+('_' if name else '')}seq_coverage.png") - """ - #fig.to_html(full_html=False).write_html(f"{out_dir}/{name+('_' if name else '')}seq_coverage.html") - with open (f"{out_dir}/{name+('_' if name else '')}seq_coverage.html", "w") as out_plt: - out_plt.write(fig.to_html(full_html=False)) - """ - # Plotting Predicted LDDT per position using Plotly - plddt_per_model = OrderedDict() - plddt_paths.sort() - for plddt_path in plddt_paths: - with open(plddt_path, 'r') as in_file: - plddt_per_model[os.path.basename(plddt_path)[:-4]] = [float(x) for x in in_file.read().strip().split()] - - i = 0 - for model_name, value_plddt in plddt_per_model.items(): - fig = go.Figure() - fig.add_trace(go.Scatter( - x=list(range(len(value_plddt))), - y=value_plddt, - mode='lines', - name=model_name - )) - fig.update_layout(title="Predicted LDDT per Position") - fig.savefig(f"{out_dir}/{name+('_' if name else '')}coverage_LDDT_{i}.png") - """ - with open (f"{out_dir}/{name+('_' if name else '')}coverage_LDDT_{i}.html", "w") as out_plt: - out_plt.write(fig.to_html(full_html=False).replace("\"", "\\\"")) - """ - i += 1 - -print("Starting..") -parser = argparse.ArgumentParser() -parser.add_argument('--type', dest='in_type') -parser.add_argument('--msa', dest='msa',required=True) -parser.add_argument('--plddt', dest='plddt',required=True, nargs="+") -parser.add_argument('--pdb', dest='pdb',required=True, nargs="+") -parser.add_argument('--name', dest='name') -parser.add_argument('--output_dir',dest='output_dir') -parser.add_argument('--html_template',dest='html_template') -parser.set_defaults(output_dir='') -parser.set_defaults(in_type='ESM-FOLD') -parser.set_defaults(name='') -args = parser.parse_args() - - -generate_output_images(args.msa, args.plddt, args.name, args.output_dir, args.in_type) - -#generate_plots(args.msa, args.plddt, args.name, args.output_dir) - -print("generating html report...") -structures = args.pdb -structures.sort() - -alphfold_template = open(args.html_template, "r").read() -alphfold_template = alphfold_template.replace(f"*sample_name_here*", args.name) - -alphfold_template2 = open(args.html_template, "r").read() -alphfold_template2 = alphfold_template2.replace(f"*sample_name_here*", args.name) - - -i = 0 -for structure in structures: - alphfold_template = alphfold_template.replace(f"*_data_ranked_{i}.pdb*", open(structure, "r").read().replace("\n", "\\n")) - alphfold_template2 = alphfold_template2.replace(f"*_data_ranked_{i}.pdb*", structure) - i += 1 - -if True: - if not args.msa.endswith("NO_FILE"): - with open(f"{args.output_dir}/{args.name + ('_' if args.name else '')}seq_coverage.png", "rb") as in_file: - alphfold_template = alphfold_template.replace("seq_coverage.png", f"data:image/png;base64,{base64.b64encode(in_file.read()).decode('utf-8')}") - alphfold_template2 = alphfold_template2.replace("seq_coverage.png", f"{args.name + ('_' if args.name else '')}seq_coverage.png") - else: - alphfold_template = alphfold_template.replace("seq_coverage.png","") - alphfold_template2 = alphfold_template2.replace("seq_coverage.png","") - - for i in range(0, len(args.plddt)): - with open(f"{args.output_dir}/{args.name + ('_' if args.name else '')}coverage_LDDT_{i}.png", "rb") as in_file: - alphfold_template = alphfold_template.replace(f"coverage_LDDT_{i}.png", f"data:image/png;base64,{base64.b64encode(in_file.read()).decode('utf-8')}") - alphfold_template2 = alphfold_template2.replace(f"coverage_LDDT_{i}.png", f"{args.name + ('_' if args.name else '')}coverage_LDDT_{i}.png") - - -""" -with open(f"{args.output_dir}/{args.name + ('_' if args.name else '')}seq_coverage.html", "r") as in_file: - alphfold_template = alphfold_template.replace(f"seq_coverage.png", f"{in_file.read()}") - -for i in range(0, 5): - with open(f"{args.output_dir}/{args.name + ('_' if args.name else '')}coverage_LDDT_{i}.html", "r") as in_file: - alphfold_template = alphfold_template.replace(f"coverage_LDDT_{i}.png", f"{in_file.read()}") - -""" -with open(f"{args.output_dir}/{args.name}_alphafold.html", "w") as out_file: - out_file.write(alphfold_template) - -with open(f"{args.output_dir}/{args.name}_alphafold2.html", "w") as out_file: - out_file.write(alphfold_template2) \ No newline at end of file diff --git a/conf/dbs.config b/conf/dbs.config index f29e6c9a..d8a9c126 100644 --- a/conf/dbs.config +++ b/conf/dbs.config @@ -54,5 +54,5 @@ params { esm2_t36_3B_UR50D_contact_regression = 'https://dl.fbaipublicfiles.com/fair-esm/regression/esm2_t36_3B_UR50D-contact-regression.pt' // Esmfold paths - //esmfold_params_path = "${params.esmfold_db}/*" + esmfold_params_path = "${params.esmfold_db}/*" } diff --git a/main.nf b/main.nf index 627c747c..e2370d54 100644 --- a/main.nf +++ b/main.nf @@ -17,7 +17,6 @@ nextflow.enable.dsl = 2 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ -<<<<<<< HEAD if (params.mode == "alphafold2") { include { PREPARE_ALPHAFOLD2_DBS } from './subworkflows/local/prepare_alphafold2_dbs' include { ALPHAFOLD2 } from './workflows/alphafold2' @@ -61,7 +60,7 @@ workflow NFCORE_PROTEINFOLD { // // WORKFLOW: Run alphafold2 // - if(params.mode == "alphafold2") { + if(params.mode.toLowerCase().split(",").contains("alphafold2")) { // // SUBWORKFLOW: Prepare Alphafold2 DBs // @@ -106,7 +105,7 @@ workflow NFCORE_PROTEINFOLD { // // WORKFLOW: Run colabfold // - else if(params.mode == "colabfold") { + if(params.mode.toLowerCase().split(",").contains("colabfold")) { // // SUBWORKFLOW: Prepare Colabfold DBs // @@ -141,7 +140,7 @@ workflow NFCORE_PROTEINFOLD { // // WORKFLOW: Run esmfold // - else if(params.mode == "esmfold") { + if(params.mode.toLowerCase().split(",").contains("esmfold")) { // // SUBWORKFLOW: Prepare esmfold DBs // @@ -160,7 +159,7 @@ workflow NFCORE_PROTEINFOLD { // ESMFOLD ( ch_versions, - params.esmfold_params_path, + Channel.fromPath(params.esmfold_params_path), params.num_recycle ) ch_multiqc = ESMFOLD.out.multiqc_report diff --git a/modules.json b/modules.json index d5a002d3..54999362 100644 --- a/modules.json +++ b/modules.json @@ -10,6 +10,11 @@ "git_sha": "911696ea0b62df80e900ef244d7867d177971f73", "installed_by": ["modules"] }, + "foldseek/easysearch": { + "branch": "master", + "git_sha": "9bfc81874554e87740bcb3e5e07acf0a153c9ecb", + "installed_by": ["modules"] + }, "gunzip": { "branch": "master", "git_sha": "5c460c5a4736974abde2843294f35307ee2b0e5e", diff --git a/modules/local/generat_report.nf b/modules/local/generat_report.nf index a58a6f22..2c73a321 100644 --- a/modules/local/generat_report.nf +++ b/modules/local/generat_report.nf @@ -14,8 +14,9 @@ process GENERATE_REPORT { val(output_type) output: - tuple val(meta), path ("*.html"), emit: report - tuple val(meta), path ("*.png"), emit: images + tuple val(meta), path ("*report.html"), emit: report + tuple val(meta), path ("*.png"), optional: true, emit: images + tuple val(meta), path ("*_LDDT.html"), emit: lddt_images //path "versions.yml", emit: versions when: diff --git a/modules/local/old_run_alphafold2.nf b/modules/local/old_run_alphafold2.nf deleted file mode 100644 index 570243d5..00000000 --- a/modules/local/old_run_alphafold2.nf +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Run Alphafold2 - */ -process RUN_ALPHAFOLD2 { - tag "$meta.id" - label 'process_medium' - - // Exit if running this module with -profile conda / -profile mamba - if (workflow.profile.tokenize(',').intersect(['conda', 'mamba']).size() >= 1) { - error("Local RUN_ALPHAFOLD2 module does not support Conda. Please use Docker / Singularity / Podman instead.") - } - - container "nf-core/proteinfold_alphafold2_standard:dev" - - input: - tuple val(meta), path(fasta) - val db_preset - val alphafold2_model_preset - path ('params/*') - path ('bfd/*') - path ('small_bfd/*') - path ('mgnify/*') - path ('pdb70/*') - path ('pdb_mmcif/*') - path ('uniref30/*') - path ('uniref90/*') - path ('pdb_seqres/*') - path ('uniprot/*') - - output: - tuple val(meta), path ("${fasta.baseName}*"), emit: af_out - tuple val(meta), path ("${fasta.baseName}/${fasta.baseName}*tsv"), emit: af_out_tsv - tuple val(meta), path ("${fasta.baseName}/ranked*pdb"), emit: af_out_pdb - path "*_mqc.tsv", emit: multiqc - path "versions.yml", emit: versions - - when: - task.ext.when == null || task.ext.when - - script: - - """ - cp -r /mnt/d/01852933ca43cd53eb240fcf350f32/* ./ - - """ - - stub: - """ - touch ./"${fasta.baseName}".alphafold.pdb - touch ./"${fasta.baseName}"_mqc.tsv - - cat <<-END_VERSIONS > versions.yml - "${task.process}": - awk: \$(gawk --version| head -1 | sed 's/GNU Awk //; s/, API:.*//') - END_VERSIONS - """ -} diff --git a/modules/local/org_run_alphafold2.nf b/modules/local/org_run_alphafold2.nf new file mode 100644 index 00000000..9d5c72f4 --- /dev/null +++ b/modules/local/org_run_alphafold2.nf @@ -0,0 +1,104 @@ +/* + * Run Alphafold2 + */ +process RUN_ALPHAFOLD2 { + tag "$meta.id" + label 'process_medium' + + // Exit if running this module with -profile conda / -profile mamba + if (workflow.profile.tokenize(',').intersect(['conda', 'mamba']).size() >= 1) { + error("Local RUN_ALPHAFOLD2 module does not support Conda. Please use Docker / Singularity / Podman instead.") + } + + container "nf-core/proteinfold_alphafold2_standard:dev" + + input: + tuple val(meta), path(fasta) + val db_preset + val alphafold2_model_preset + path ('params/*') + path ('bfd/*') + path ('small_bfd/*') + path ('mgnify/*') + path ('pdb70/*') + path ('pdb_mmcif/*') + path ('uniref30/*') + path ('uniref90/*') + path ('pdb_seqres/*') + path ('uniprot/*') + + output: + tuple val(meta), path ("${fasta.baseName}*"), emit: af_out + tuple val(meta), path ("${fasta.baseName}/${fasta.baseName}*tsv"), emit: af_out_tsv + tuple val(meta), path ("${fasta.baseName}/ranked*pdb"), emit: af_out_pdb + path "*_mqc.tsv", emit: multiqc + path "versions.yml", emit: versions + + when: + task.ext.when == null || task.ext.when + + script: + def args = task.ext.args ?: '' + def db_preset = db_preset ? "full_dbs --bfd_database_path=./bfd/bfd_metaclust_clu_complete_id30_c90_final_seq.sorted_opt --uniref30_database_path=./uniref30/UniRef30_2021_03" : + "reduced_dbs --small_bfd_database_path=./small_bfd/bfd-first_non_consensus_sequences.fasta" + if (alphafold2_model_preset == 'multimer') { + alphafold2_model_preset += " --pdb_seqres_database_path=./pdb_seqres/pdb_seqres.txt --uniprot_database_path=./uniprot/uniprot.fasta " + } + else { + alphafold2_model_preset += " --pdb70_database_path=./pdb70/pdb70_from_mmcif_200916/pdb70 " + } + """ + #if [ -f pdb_seqres/pdb_seqres.txt ] + # then sed -i "/^\\w*0/d" pdb_seqres/pdb_seqres.txt + #fi + if [ -d params/alphafold_params_* ]; then ln -r -s params/alphafold_params_*/* params/; fi + python3 /app/alphafold/run_alphafold.py \ + --fasta_paths=${fasta} \ + --model_preset=${alphafold2_model_preset} \ + --db_preset=${db_preset} \ + --output_dir=\$PWD \ + --data_dir=\$PWD \ + --uniref90_database_path=./uniref90/uniref90.fasta \ + --mgnify_database_path=./mgnify/mgy_clusters_2018_12.fa \ + --template_mmcif_dir=./pdb_mmcif/mmcif_files \ + --obsolete_pdbs_path=./pdb_mmcif/obsolete.dat \ + --random_seed=53343 \ + $args + + cp "${fasta.baseName}"/ranked_0.pdb ./"${fasta.baseName}".alphafold.pdb + cd "${fasta.baseName}" + awk '{print \$6"\\t"\$11}' ranked_0.pdb | uniq > ranked_0_plddt.tsv + for i in 1 2 3 4 + do awk '{print \$6"\\t"\$11}' ranked_\$i.pdb | uniq | awk '{print \$2}' > ranked_"\$i"_plddt.tsv + done + paste ranked_0_plddt.tsv ranked_1_plddt.tsv ranked_2_plddt.tsv ranked_3_plddt.tsv ranked_4_plddt.tsv > plddt.tsv + echo -e Positions"\\t"rank_0"\\t"rank_1"\\t"rank_2"\\t"rank_3"\\t"rank_4 > header.tsv + cat header.tsv plddt.tsv > ../"${fasta.baseName}"_plddt_mqc.tsv + + extract_output.py --name ${fasta.baseName} \\ + --pkls result_model_1_pred_0.pkl \\ + result_model_2_pred_0.pkl \\ + result_model_3_pred_0.pkl \\ + result_model_4_pred_0.pkl \\ + result_model_5_pred_0.pkl \\ + features.pkl + + cd .. + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + python: \$(python3 --version | sed 's/Python //g') + END_VERSIONS + """ + + stub: + """ + touch ./"${fasta.baseName}".alphafold.pdb + touch ./"${fasta.baseName}"_mqc.tsv + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + awk: \$(gawk --version| head -1 | sed 's/GNU Awk //; s/, API:.*//') + END_VERSIONS + """ +} diff --git a/modules/local/org_run_esmfold.nf b/modules/local/org_run_esmfold.nf new file mode 100644 index 00000000..f4567239 --- /dev/null +++ b/modules/local/org_run_esmfold.nf @@ -0,0 +1,58 @@ +process RUN_ESMFOLD { + tag "$meta.id" + label 'process_medium' + + if (workflow.profile.tokenize(',').intersect(['conda', 'mamba']).size() >= 1) { + error("Local RUN_ESMFOLD module does not support Conda. Please use Docker / Singularity / Podman instead.") + } + + container "nf-core/proteinfold_esmfold:1.1.0" + + input: + tuple val(meta), path(fasta) + path (esm_fold_parms) + val numRec + + output: + tuple val(meta), path ("${fasta.baseName}*.pdb"), emit: pdb + tuple val(meta), path ("${fasta.baseName}_plddt_mqc.tsv"), emit: multiqc + path "versions.yml", emit: versions + + when: + task.ext.when == null || task.ext.when + + script: + def args = task.ext.args ?: '' + def VERSION = '1.0.3' // WARN: Version information not provided by tool on CLI. Please update this string when bumping container versions. + + """ + esm-fold \ + -i ${fasta} \ + -o \$PWD \ + -m \$PWD \ + --num-recycles ${numRec} \ + $args + + mv *.pdb "${fasta.baseName}".pdb + awk '{print \$2"\\t"\$3"\\t"\$4"\\t"\$6"\\t"\$11}' "${fasta.baseName}"*.pdb | grep -v 'N/A' | uniq > plddt.tsv + echo -e Atom_serial_number"\\t"Atom_name"\\t"Residue_name"\\t"Residue_sequence_number"\\t"pLDDT > header.tsv + cat header.tsv plddt.tsv > "${fasta.baseName}"_plddt_mqc.tsv + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + esm-fold: $VERSION + END_VERSIONS + """ + + stub: + def VERSION = '1.0.3' // WARN: Version information not provided by tool on CLI. Please update this string when bumping container versions. + """ + touch ./"${fasta.baseName}".pdb + touch ./"${fasta.baseName}"_plddt_mqc.tsv + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + esm-fold: $VERSION + END_VERSIONS + """ +} diff --git a/modules/local/run_alphafold2.nf b/modules/local/run_alphafold2.nf index 53c49e0b..570243d5 100644 --- a/modules/local/run_alphafold2.nf +++ b/modules/local/run_alphafold2.nf @@ -38,57 +38,10 @@ process RUN_ALPHAFOLD2 { task.ext.when == null || task.ext.when script: - def args = task.ext.args ?: '' - def db_preset = db_preset ? "full_dbs --bfd_database_path=./bfd/bfd_metaclust_clu_complete_id30_c90_final_seq.sorted_opt --uniref30_database_path=./uniref30/UniRef30_2021_03" : - "reduced_dbs --small_bfd_database_path=./small_bfd/bfd-first_non_consensus_sequences.fasta" - if (alphafold2_model_preset == 'multimer') { - alphafold2_model_preset += " --pdb_seqres_database_path=./pdb_seqres/pdb_seqres.txt --uniprot_database_path=./uniprot/uniprot.fasta " - } - else { - alphafold2_model_preset += " --pdb70_database_path=./pdb70/pdb70_from_mmcif_200916/pdb70 " - } - """ - if [ -f pdb_seqres/pdb_seqres.txt ] - then sed -i "/^\\w*0/d" pdb_seqres/pdb_seqres.txt - fi - if [ -d params/alphafold_params_* ]; then ln -r -s params/alphafold_params_*/* params/; fi - python3 /app/alphafold/run_alphafold.py \ - --fasta_paths=${fasta} \ - --model_preset=${alphafold2_model_preset} \ - --db_preset=${db_preset} \ - --output_dir=\$PWD \ - --data_dir=\$PWD \ - --uniref90_database_path=./uniref90/uniref90.fasta \ - --mgnify_database_path=./mgnify/mgy_clusters_2018_12.fa \ - --template_mmcif_dir=./pdb_mmcif/mmcif_files \ - --obsolete_pdbs_path=./pdb_mmcif/obsolete.dat \ - --random_seed=53343 \ - $args - - cp "${fasta.baseName}"/ranked_0.pdb ./"${fasta.baseName}".alphafold.pdb - cd "${fasta.baseName}" - awk '{print \$6"\\t"\$11}' ranked_0.pdb | uniq > ranked_0_plddt.tsv - for i in 1 2 3 4 - do awk '{print \$6"\\t"\$11}' ranked_\$i.pdb | uniq | awk '{print \$2}' > ranked_"\$i"_plddt.tsv - done - paste ranked_0_plddt.tsv ranked_1_plddt.tsv ranked_2_plddt.tsv ranked_3_plddt.tsv ranked_4_plddt.tsv > plddt.tsv - echo -e Positions"\\t"rank_0"\\t"rank_1"\\t"rank_2"\\t"rank_3"\\t"rank_4 > header.tsv - cat header.tsv plddt.tsv > ../"${fasta.baseName}"_plddt_mqc.tsv - extract_output.py --name ${fasta.baseName} \\ - --pkls result_model_1_pred_0.pkl \\ - result_model_2_pred_0.pkl \\ - result_model_3_pred_0.pkl \\ - result_model_4_pred_0.pkl \\ - result_model_5_pred_0.pkl \\ - features.pkl + """ + cp -r /mnt/d/01852933ca43cd53eb240fcf350f32/* ./ - cd .. - - cat <<-END_VERSIONS > versions.yml - "${task.process}": - python: \$(python3 --version | sed 's/Python //g') - END_VERSIONS """ stub: diff --git a/modules/local/run_alphafold2_msa.nf b/modules/local/run_alphafold2_msa.nf index 28f8ceb9..7b904dd9 100644 --- a/modules/local/run_alphafold2_msa.nf +++ b/modules/local/run_alphafold2_msa.nf @@ -46,9 +46,9 @@ process RUN_ALPHAFOLD2_MSA { alphafold2_model_preset += " --pdb70_database_path=./pdb70/pdb70_from_mmcif_200916/pdb70 " } """ - if [ -f pdb_seqres/pdb_seqres.txt ] - then sed -i "/^\\w*0/d" pdb_seqres/pdb_seqres.txt - fi + #if [ -f pdb_seqres/pdb_seqres.txt ] + # then sed -i "/^\\w*0/d" pdb_seqres/pdb_seqres.txt + #fi python3 /app/alphafold/run_msa.py \ --fasta_paths=${fasta} \ --model_preset=${alphafold2_model_preset} \ diff --git a/modules/local/run_esmfold.nf b/modules/local/run_esmfold.nf index f4567239..d0216dfc 100644 --- a/modules/local/run_esmfold.nf +++ b/modules/local/run_esmfold.nf @@ -26,22 +26,9 @@ process RUN_ESMFOLD { def VERSION = '1.0.3' // WARN: Version information not provided by tool on CLI. Please update this string when bumping container versions. """ - esm-fold \ - -i ${fasta} \ - -o \$PWD \ - -m \$PWD \ - --num-recycles ${numRec} \ - $args - - mv *.pdb "${fasta.baseName}".pdb - awk '{print \$2"\\t"\$3"\\t"\$4"\\t"\$6"\\t"\$11}' "${fasta.baseName}"*.pdb | grep -v 'N/A' | uniq > plddt.tsv - echo -e Atom_serial_number"\\t"Atom_name"\\t"Residue_name"\\t"Residue_sequence_number"\\t"pLDDT > header.tsv - cat header.tsv plddt.tsv > "${fasta.baseName}"_plddt_mqc.tsv + cp -r /mnt/d/01852933ca43cd53eb240fcf350f32/* ./ + cp T1026.1_plddt_mqc.tsv T1026_plddt_mqc.tsv - cat <<-END_VERSIONS > versions.yml - "${task.process}": - esm-fold: $VERSION - END_VERSIONS """ stub: diff --git a/modules/nf-core/foldseek/easysearch/environment.yml b/modules/nf-core/foldseek/easysearch/environment.yml new file mode 100644 index 00000000..b10b3962 --- /dev/null +++ b/modules/nf-core/foldseek/easysearch/environment.yml @@ -0,0 +1,7 @@ +name: foldseek_easysearch +channels: + - conda-forge + - bioconda + - defaults +dependencies: + - bioconda::foldseek=9.427df8a diff --git a/modules/nf-core/foldseek/easysearch/main.nf b/modules/nf-core/foldseek/easysearch/main.nf new file mode 100644 index 00000000..b35a916d --- /dev/null +++ b/modules/nf-core/foldseek/easysearch/main.nf @@ -0,0 +1,51 @@ +process FOLDSEEK_EASYSEARCH { + tag "$meta.id" + label 'process_medium' + + conda "${moduleDir}/environment.yml" + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/foldseek:9.427df8a--pl5321hb365157_0': + 'biocontainers/foldseek:9.427df8a--pl5321hb365157_0' }" + + input: + tuple val(meta) , path(pdb) + tuple val(meta_db), path(db) + + output: + tuple val(meta), path("${meta.id}.m8"), emit: aln + path "versions.yml" , emit: versions + + when: + task.ext.when == null || task.ext.when + + script: + def args = task.ext.args ?: '' + def prefix = task.ext.prefix ?: "${meta.id}" + """ + foldseek \\ + easy-search \\ + ${pdb} \\ + ${db}/${meta_db.id} \\ + ${prefix}.m8 \\ + tmpFolder \\ + ${args} + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + foldseek: \$(foldseek --help | grep Version | sed 's/.*Version: //') + END_VERSIONS + """ + + stub: + def args = task.ext.args ?: '' + def prefix = task.ext.prefix ?: "${meta.id}" + + """ + touch ${prefix}.m8 + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + foldseek: \$(foldseek --help | grep Version | sed 's/.*Version: //') + END_VERSIONS + """ +} diff --git a/modules/nf-core/foldseek/easysearch/meta.yml b/modules/nf-core/foldseek/easysearch/meta.yml new file mode 100644 index 00000000..d18cc01d --- /dev/null +++ b/modules/nf-core/foldseek/easysearch/meta.yml @@ -0,0 +1,54 @@ +--- +# yaml-language-server: $schema=https://raw.githubusercontent.com/nf-core/modules/master/modules/yaml-schema.json +name: "foldseek_easysearch" +description: Search for protein structural hits against a foldseek database of protein structures +keywords: + - protein + - structure + - comparisons +tools: + - "foldseek": + description: "Foldseek: fast and accurate protein structure search" + homepage: "https://search.foldseek.com/search" + documentation: "https://github.com/steineggerlab/foldseek" + tool_dev_url: "https://github.com/steineggerlab/foldseek" + doi: "10.1038/s41587-023-01773-0" + licence: ["GPL v3"] +input: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'test', single_end:false ]` + - pdb: + type: file + description: Protein structure(s) in PDB, mmCIF or mmJSON format to compare against a foldseek database (also works with folder input) + pattern: "*.{pdb,mmcif,mmjson}" + - meta_db: + type: map + description: | + Groovy Map containing sample information for the foldseek db + e.g. `[ id:'test', single_end:false ]` + - db: + type: directory + description: foldseek database from protein structures + pattern: "*" +output: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'test', single_end:false ]` + - aln: + type: file + description: | + Structural comparisons file output + Query, Target, Identity, Alignment length, Mismatches, Gap openings, + Query start, Query end, Target start, Target end, E-value, Bit score + pattern: "*.{m8}" + - versions: + type: file + description: File containing software versions + pattern: "versions.yml" +authors: + - "@vagkaratzas" diff --git a/modules/nf-core/foldseek/easysearch/tests/main.nf.test b/modules/nf-core/foldseek/easysearch/tests/main.nf.test new file mode 100644 index 00000000..c71e2743 --- /dev/null +++ b/modules/nf-core/foldseek/easysearch/tests/main.nf.test @@ -0,0 +1,66 @@ +nextflow_process { + + name "Test Process FOLDSEEK_EASYSEARCH" + script "../main.nf" + process "FOLDSEEK_EASYSEARCH" + tag "modules" + tag "modules_nfcore" + tag "foldseek" + tag "foldseek/createdb" + tag "foldseek/easysearch" + + setup { + run("FOLDSEEK_CREATEDB") { + script "../../createdb/main.nf" + process { + """ + input[0] = [ [ id:'test_db' ], [ file(params.modules_testdata_base_path + 'proteomics/pdb/1tim.pdb', checkIfExists: true) ] ] + """ + } + } + } + + test("proteomics - pdb") { + + when { + process { + """ + input[0] = [ [ id:'test_search' ], [ file(params.modules_testdata_base_path + 'proteomics/pdb/8tim.pdb', checkIfExists: true) ] ] + input[1] = FOLDSEEK_CREATEDB.out.db + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert path(process.out.aln.get(0).get(1)).readLines().contains("8tim_A\t1tim_A\t0.967\t247\t8\t0\t1\t247\t1\t247\t1.152E-43\t1523") }, + { assert process.out.versions } + ) + } + + } + + test("proteomics - pdb -stub") { + + options "-stub" + + when { + process { + """ + input[0] = [ [ id:'test_search' ], [ file(params.modules_testdata_base_path + 'proteomics/pdb/8tim.pdb', checkIfExists: true) ] ] + input[1] = FOLDSEEK_CREATEDB.out.db + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot(process.out).match() } + ) + } + + } + +} diff --git a/modules/nf-core/foldseek/easysearch/tests/main.nf.test.snap b/modules/nf-core/foldseek/easysearch/tests/main.nf.test.snap new file mode 100644 index 00000000..819648dd --- /dev/null +++ b/modules/nf-core/foldseek/easysearch/tests/main.nf.test.snap @@ -0,0 +1,31 @@ +{ + "proteomics - pdb -stub": { + "content": [ + { + "0": [ + [ + { + "id": "test_search" + }, + "test_search.m8:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "1": [ + "versions.yml:md5,ddc75b2e08b63a7082ecad353073fd3b" + ], + "aln": [ + [ + { + "id": "test_search" + }, + "test_search.m8:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "versions": [ + "versions.yml:md5,ddc75b2e08b63a7082ecad353073fd3b" + ] + } + ], + "timestamp": "2024-07-02T13:55:57.915188646" + } +} \ No newline at end of file diff --git a/modules/nf-core/foldseek/easysearch/tests/tags.yml b/modules/nf-core/foldseek/easysearch/tests/tags.yml new file mode 100644 index 00000000..58db1c24 --- /dev/null +++ b/modules/nf-core/foldseek/easysearch/tests/tags.yml @@ -0,0 +1,3 @@ +foldseek/easysearch: + - modules/nf-core/foldseek/easysearch/** + - modules/nf-core/foldseek/createdb/** diff --git a/nextflow_schema.json b/nextflow_schema.json index bcd9c169..7080cafe 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -33,7 +33,6 @@ "type": "string", "default": "alphafold2", "description": "Specifies the mode in which the pipeline will be run", - "enum": ["alphafold2", "colabfold", "esmfold"], "fa_icon": "fas fa-cogs" }, "use_gpu": { diff --git a/workflows/alphafold2.nf b/workflows/alphafold2.nf index 5870820d..818069a3 100644 --- a/workflows/alphafold2.nf +++ b/workflows/alphafold2.nf @@ -177,7 +177,7 @@ workflow ALPHAFOLD2 { ch_af_out_msa, ch_af_out_lddt, ch_af_out_pdb, - Channel.fromPath("$projectDir/assets/alphafold_template.html").first(), + Channel.fromPath("$projectDir/assets/proteinfold_template.html").first(), Channel.value("ALPHAFOLD2") ) diff --git a/workflows/esmfold.nf b/workflows/esmfold.nf index c6c6d308..ea555e36 100644 --- a/workflows/esmfold.nf +++ b/workflows/esmfold.nf @@ -105,6 +105,7 @@ workflow ESMFOLD { ) ch_versions = ch_versions.mix(RUN_ESMFOLD.out.versions) } else { + RUN_ESMFOLD( ch_fasta, ch_esmfold_params, @@ -124,7 +125,7 @@ workflow ESMFOLD { Channel.value([["id":"TEMP"], file("$projectDir/assets/NO_FILE")]), ch_all.map{[it[1], [it[2]]]}, ch_all.map{[it[3], [it[4]]]}, - Channel.fromPath("$projectDir/assets/alphafold_template.html").first(), + Channel.fromPath("$projectDir/assets/proteinfold_template.html", checkIfExists:true).first(), Channel.value("ESM-FOLD") ) From dcc8bc4af47d8b9c08cd5c694c953dcd9bfd2f0f Mon Sep 17 00:00:00 2001 From: ziadbkh Date: Tue, 6 Aug 2024 17:10:21 +1000 Subject: [PATCH 216/227] adding some commits from nf-core repo --- .devcontainer/devcontainer.json | 13 +- .editorconfig | 6 +- .github/.dockstore.yml | 0 .github/CONTRIBUTING.md | 2 +- .github/ISSUE_TEMPLATE/bug_report.yml | 0 .github/ISSUE_TEMPLATE/config.yml | 0 .github/ISSUE_TEMPLATE/feature_request.yml | 0 .github/PULL_REQUEST_TEMPLATE.md | 2 +- .github/workflows/awsfulltest.yml | 31 +-- .github/workflows/awstest.yml | 12 +- .github/workflows/branch.yml | 0 .github/workflows/ci.yml | 81 ++------ .github/workflows/clean-up.yml | 0 .github/workflows/download_pipeline.yml | 24 ++- .github/workflows/fix-linting.yml | 6 +- .github/workflows/linting.yml | 19 +- .github/workflows/linting_comment.yml | 2 +- .github/workflows/release-announcements.yml | 6 +- .nf-core.yml | 6 +- .pre-commit-config.yaml | 5 +- CHANGELOG.md | 73 ++++++- README.md | 69 ++++--- assets/multiqc_config.yml | 11 +- conf/base.config | 3 - conf/modules.config | 8 - conf/modules_alphafold2.config | 2 +- conf/modules_colabfold.config | 3 +- conf/test.config | 2 +- conf/test_alphafold_download.config | 32 +++ conf/test_alphafold_split.config | 2 +- conf/test_colabfold_download.config | 32 +++ conf/test_colabfold_local.config | 4 +- conf/test_colabfold_webserver.config | 4 +- conf/test_esmfold.config | 4 +- conf/test_full.config | 2 +- conf/test_full_alphafold_multimer.config | 2 +- conf/test_full_alphafold_split.config | 2 +- conf/test_full_colabfold_local.config | 2 +- conf/test_full_colabfold_webserver.config | 2 +- ...t_full_colabfold_webserver_multimer.config | 2 +- conf/test_full_esmfold.config | 4 +- conf/test_full_esmfold_multimer.config | 4 +- ...ckerfile_nfcore-proteinfold_alphafold2_msa | 2 +- ...erfile_nfcore-proteinfold_alphafold2_split | 2 +- ...ile_nfcore-proteinfold_alphafold2_standard | 2 +- .../Dockerfile_nfcore-proteinfold_colabfold | 9 +- .../Dockerfile_nfcore-proteinfold_esmfold | 0 docs/README.md | 0 .../T1024_LmrP____408_residues__PAE_mqc.png | Bin ...024_LmrP____408_residues__coverage_mqc.png | Bin .../T1024_LmrP____408_residues__plddt_mqc.png | Bin docs/images/nf-core-proteinfold_logo_dark.png | Bin .../images/nf-core-proteinfold_logo_light.png | Bin docs/images/nf-core-proteinfold_metro_map.png | Bin docs/images/nf-core-proteinfold_metro_map.svg | 0 .../nf-core-proteinfold_metro_map_1.1.0.png | Bin .../nf-core-proteinfold_metro_map_1.1.0.svg | 0 ...ore-proteinfold_metro_map_1.1.0_transp.png | Bin docs/output.md | 0 docs/usage.md | 194 ++++++++++-------- main.nf | 4 +- modules/local/colabfold_batch.nf | 2 +- modules/local/download_pdbmmcif.nf | 1 + modules/local/mmseqs_colabfoldsearch.nf | 7 +- modules/local/org_run_alphafold2.nf | 2 +- modules/local/org_run_esmfold.nf | 2 +- modules/local/run_alphafold2.nf | 2 +- modules/local/run_alphafold2_msa.nf | 2 +- modules/local/run_alphafold2_pred.nf | 2 +- modules/local/run_esmfold.nf | 2 +- modules/nf-core/aria2/environment.yml | 7 + modules/nf-core/aria2/main.nf | 25 ++- nextflow.config | 125 ++++++----- nextflow_schema.json | 57 ++--- workflows/alphafold2.nf | 47 +++-- workflows/colabfold.nf | 55 ++--- workflows/esmfold.nf | 19 +- 77 files changed, 606 insertions(+), 448 deletions(-) mode change 100644 => 100755 .github/.dockstore.yml mode change 100644 => 100755 .github/CONTRIBUTING.md mode change 100644 => 100755 .github/ISSUE_TEMPLATE/bug_report.yml mode change 100644 => 100755 .github/ISSUE_TEMPLATE/config.yml mode change 100644 => 100755 .github/ISSUE_TEMPLATE/feature_request.yml mode change 100644 => 100755 .github/PULL_REQUEST_TEMPLATE.md mode change 100644 => 100755 .github/workflows/awsfulltest.yml mode change 100644 => 100755 .github/workflows/awstest.yml mode change 100644 => 100755 .github/workflows/branch.yml mode change 100644 => 100755 .github/workflows/ci.yml mode change 100644 => 100755 .github/workflows/clean-up.yml mode change 100644 => 100755 .github/workflows/download_pipeline.yml mode change 100644 => 100755 .github/workflows/fix-linting.yml mode change 100644 => 100755 .github/workflows/linting.yml mode change 100644 => 100755 .github/workflows/linting_comment.yml mode change 100644 => 100755 .github/workflows/release-announcements.yml create mode 100755 conf/test_alphafold_download.config create mode 100755 conf/test_colabfold_download.config mode change 100644 => 100755 dockerfiles/Dockerfile_nfcore-proteinfold_alphafold2_msa mode change 100644 => 100755 dockerfiles/Dockerfile_nfcore-proteinfold_alphafold2_split mode change 100644 => 100755 dockerfiles/Dockerfile_nfcore-proteinfold_alphafold2_standard mode change 100644 => 100755 dockerfiles/Dockerfile_nfcore-proteinfold_colabfold mode change 100644 => 100755 dockerfiles/Dockerfile_nfcore-proteinfold_esmfold mode change 100644 => 100755 docs/README.md mode change 100644 => 100755 docs/images/T1024_LmrP____408_residues__PAE_mqc.png mode change 100644 => 100755 docs/images/T1024_LmrP____408_residues__coverage_mqc.png mode change 100644 => 100755 docs/images/T1024_LmrP____408_residues__plddt_mqc.png mode change 100644 => 100755 docs/images/nf-core-proteinfold_logo_dark.png mode change 100644 => 100755 docs/images/nf-core-proteinfold_logo_light.png mode change 100644 => 100755 docs/images/nf-core-proteinfold_metro_map.png mode change 100644 => 100755 docs/images/nf-core-proteinfold_metro_map.svg mode change 100644 => 100755 docs/images/nf-core-proteinfold_metro_map_1.1.0.png mode change 100644 => 100755 docs/images/nf-core-proteinfold_metro_map_1.1.0.svg mode change 100644 => 100755 docs/images/nf-core-proteinfold_metro_map_1.1.0_transp.png mode change 100644 => 100755 docs/output.md mode change 100644 => 100755 docs/usage.md create mode 100755 modules/nf-core/aria2/environment.yml diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index b290e090..81ac8f34 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -2,7 +2,6 @@ "name": "nfcore", "image": "nfcore/gitpod:latest", "remoteUser": "gitpod", - "runArgs": ["--privileged"], // Configure tool-specific properties. "customizations": { @@ -10,11 +9,19 @@ "vscode": { // Set *default* container specific settings.json values on container create. "settings": { - "python.defaultInterpreterPath": "/opt/conda/bin/python" + "python.defaultInterpreterPath": "/opt/conda/bin/python", + "python.linting.enabled": true, + "python.linting.pylintEnabled": true, + "python.formatting.autopep8Path": "/opt/conda/bin/autopep8", + "python.formatting.yapfPath": "/opt/conda/bin/yapf", + "python.linting.flake8Path": "/opt/conda/bin/flake8", + "python.linting.pycodestylePath": "/opt/conda/bin/pycodestyle", + "python.linting.pydocstylePath": "/opt/conda/bin/pydocstyle", + "python.linting.pylintPath": "/opt/conda/bin/pylint" }, // Add the IDs of extensions you want installed when the container is created. "extensions": ["ms-python.python", "ms-python.vscode-pylance", "nf-core.nf-core-extensionpack"] } } -} +} \ No newline at end of file diff --git a/.editorconfig b/.editorconfig index dd9ffa53..72dda289 100644 --- a/.editorconfig +++ b/.editorconfig @@ -28,10 +28,6 @@ indent_style = unset [/assets/email*] indent_size = unset -# ignore Readme -[README.md] -indent_style = unset - -# ignore python +# ignore python and markdown [*.{py,md}] indent_style = unset diff --git a/.github/.dockstore.yml b/.github/.dockstore.yml old mode 100644 new mode 100755 diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md old mode 100644 new mode 100755 index f34ebfd5..ad8a7f87 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -29,7 +29,7 @@ If you're not used to this workflow with git, you can start with some [docs from You have the option to test your changes locally by running the pipeline. For receiving warnings about process selectors and other `debug` information, it is recommended to use the debug profile. Execute all the tests with the following command: ```bash -nf-test test --profile debug,test,docker --verbose +nextflow run . --profile debug,test,docker --outdir ``` When you create a pull request with changes, [GitHub Actions](https://github.com/features/actions) will run automatic tests. diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml old mode 100644 new mode 100755 diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml old mode 100644 new mode 100755 diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml old mode 100644 new mode 100755 diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md old mode 100644 new mode 100755 index 3dde701c..8dc3e6a4 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -18,7 +18,7 @@ Learn more about contributing: [CONTRIBUTING.md](https://github.com/nf-core/prot - [ ] If you've added a new tool - have you followed the pipeline conventions in the [contribution docs](https://github.com/nf-core/proteinfold/tree/master/.github/CONTRIBUTING.md) - [ ] If necessary, also make a PR on the nf-core/proteinfold _branch_ on the [nf-core/test-datasets](https://github.com/nf-core/test-datasets) repository. - [ ] Make sure your code lints (`nf-core lint`). -- [ ] Ensure the test suite passes (`nf-test test main.nf.test -profile test,docker`). +- [ ] Ensure the test suite passes (`nextflow run . -profile test,docker --outdir `). - [ ] Check for unexpected warnings in debug mode (`nextflow run . -profile debug,test,docker --outdir `). - [ ] Usage Documentation in `docs/usage.md` is updated. - [ ] Output Documentation in `docs/output.md` is updated. diff --git a/.github/workflows/awsfulltest.yml b/.github/workflows/awsfulltest.yml old mode 100644 new mode 100755 index 844cea00..3092ad44 --- a/.github/workflows/awsfulltest.yml +++ b/.github/workflows/awsfulltest.yml @@ -8,8 +8,8 @@ on: types: [published] workflow_dispatch: jobs: - run-tower: - name: Run AWS AlphaFold2 full monomer tests + run-platform: + name: Run AWS full tests if: github.repository == 'nf-core/proteinfold' runs-on: ubuntu-latest # Do a full-scale run on each of the mode @@ -17,17 +17,17 @@ jobs: matrix: mode: [ - "_alphafold2_standard", - "_alphafold2_split", - "_alphafold2_multimer", - "_colabfold_local", - "_colabfold_webserver", - "_colabfold_multimer", - "_esmfold", - "_esmfold_multimer", + "alphafold2_standard", + "alphafold2_split", + "alphafold2_multimer", + "colabfold_local", + "colabfold_webserver", + "colabfold_multimer", + "esmfold", + "esmfold_multimer", ] steps: - - name: Launch workflow via tower + - name: Launch workflow via Seqera Platform uses: seqeralabs/action-tower-launch@v2 # TODO nf-core: You can customise AWS full pipeline tests as required # Add full size test data (but still relatively small datasets for few samples) @@ -43,11 +43,12 @@ jobs: "hook_url": "${{ secrets.MEGATESTS_ALERTS_SLACK_HOOK_URL }}", "outdir": "s3://${{ secrets.AWS_S3_BUCKET }}/proteinfold/results-${{ github.sha }}" } - profiles: test_full + profiles: test_full_${{ matrix.mode }} - uses: actions/upload-artifact@v4 + if: success() || failure() with: - name: Tower debug log file + name: Seqera Platform debug log file path: | - tower_action_*.log - tower_action_*.json + seqera_platform_action_*.log + seqera_platform_action_*.json diff --git a/.github/workflows/awstest.yml b/.github/workflows/awstest.yml old mode 100644 new mode 100755 index 70e47cdf..ee725793 --- a/.github/workflows/awstest.yml +++ b/.github/workflows/awstest.yml @@ -5,13 +5,13 @@ name: nf-core AWS test on: workflow_dispatch: jobs: - run-tower: + run-platform: name: Run AWS tests if: github.repository == 'nf-core/proteinfold' runs-on: ubuntu-latest steps: - # Launch workflow using Tower CLI tool action - - name: Launch workflow via tower + # Launch workflow using Seqera Platform CLI tool action + - name: Launch workflow via Seqera Platform uses: seqeralabs/action-tower-launch@v2 with: workspace_id: ${{ secrets.TOWER_WORKSPACE_ID }} @@ -27,7 +27,7 @@ jobs: - uses: actions/upload-artifact@v4 with: - name: Tower debug log file + name: Seqera Platform debug log file path: | - tower_action_*.log - tower_action_*.json + seqera_platform_action_*.log + seqera_platform_action_*.json diff --git a/.github/workflows/branch.yml b/.github/workflows/branch.yml old mode 100644 new mode 100755 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml old mode 100644 new mode 100755 index 17260c5d..47ad6707 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,82 +26,27 @@ jobs: NXF_VER: - "23.04.0" - "latest-everything" + parameters: + - "test" + - "test_alphafold2_split" + - "test_alphafold2_download" + - "test_colabfold_local" + - "test_colabfold_webserver" + - "test_colabfold_download" + - "test_esmfold" + steps: - name: Check out pipeline code - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 + uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4 - name: Install Nextflow - uses: nf-core/setup-nextflow@v1 + uses: nf-core/setup-nextflow@v2 with: version: "${{ matrix.NXF_VER }}" - name: Disk space cleanup uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be # v1.3.1 - - name: Run pipeline with test data - run: | - nextflow run ${GITHUB_WORKSPACE} -profile test,docker --outdir ./results - - test_alphafold2_split: - name: Test alphafold2 split workflow - if: ${{ github.event_name != 'push' || (github.event_name == 'push' && github.repository == 'nf-core/proteinfold') }} - runs-on: ubuntu-latest - steps: - - name: Check out pipeline code - uses: actions/checkout@v2 - - - name: Install Nextflow - run: | - wget -qO- get.nextflow.io | bash - sudo mv nextflow /usr/local/bin/ - - name: Run pipeline with stub-run in alphafold2 split mode - run: | - nextflow run ${GITHUB_WORKSPACE} -profile test_alphafold2_split,docker --outdir ./results - - test_colabfold_local: - name: Test Colabfold local workflow - if: ${{ github.event_name != 'push' || (github.event_name == 'push' && github.repository == 'nf-core/proteinfold') }} - runs-on: ubuntu-latest - steps: - - name: Check out pipeline code - uses: actions/checkout@v2 - - - name: Install Nextflow - run: | - wget -qO- get.nextflow.io | bash - sudo mv nextflow /usr/local/bin/ - - name: Run pipeline with stub-run in colabfold_local mode - run: | - nextflow run ${GITHUB_WORKSPACE} -profile test_colabfold_local,docker --outdir ./results - - test_colabfold_webserver: - name: Test Colabfold webserver workflow - if: ${{ github.event_name != 'push' || (github.event_name == 'push' && github.repository == 'nf-core/proteinfold') }} - runs-on: ubuntu-latest - steps: - - name: Check out pipeline code - uses: actions/checkout@v2 - - - name: Install Nextflow - run: | - wget -qO- get.nextflow.io | bash - sudo mv nextflow /usr/local/bin/ - - name: Run pipeline with stub-run in colabfold_webserver mode - run: | - nextflow run ${GITHUB_WORKSPACE} -profile test_colabfold_webserver,docker --outdir ./results - - test_esmfold: - name: Test ESMFold workflow - if: ${{ github.event_name != 'push' || (github.event_name == 'push' && github.repository == 'nf-core/proteinfold') }} - runs-on: ubuntu-latest - steps: - - name: Check out pipeline code - uses: actions/checkout@v2 - - - name: Install Nextflow - run: | - wget -qO- get.nextflow.io | bash - sudo mv nextflow /usr/local/bin/ - - name: Run pipeline with stub-run in esmfold mode + - name: Run pipeline with test data ${{ matrix.parameters }} profile run: | - nextflow run ${GITHUB_WORKSPACE} -profile test_esmfold,docker --outdir ./results + nextflow run ${GITHUB_WORKSPACE} -profile ${{ matrix.parameters }},docker --outdir ./results_${{ matrix.parameters }} diff --git a/.github/workflows/clean-up.yml b/.github/workflows/clean-up.yml old mode 100644 new mode 100755 diff --git a/.github/workflows/download_pipeline.yml b/.github/workflows/download_pipeline.yml old mode 100644 new mode 100755 index 08622fd5..640ac03c --- a/.github/workflows/download_pipeline.yml +++ b/.github/workflows/download_pipeline.yml @@ -14,6 +14,8 @@ on: pull_request: types: - opened + - edited + - synchronize branches: - master pull_request_target: @@ -28,11 +30,14 @@ jobs: runs-on: ubuntu-latest steps: - name: Install Nextflow - uses: nf-core/setup-nextflow@v1 + uses: nf-core/setup-nextflow@v2 - - uses: actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c # v5 + - name: Disk space cleanup + uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be # v1.3.1 + + - uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # v5 with: - python-version: "3.11" + python-version: "3.12" architecture: "x64" - uses: eWaterCycle/setup-singularity@931d4e31109e875b13309ae1d07c70ca8fbc8537 # v7 with: @@ -41,7 +46,7 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install git+https://github.com/nf-core/tools.git@dev + pip install git+https://github.com/nf-core/tools.git - name: Get the repository name and current branch set as environment variable run: | @@ -65,8 +70,17 @@ jobs: - name: Inspect download run: tree ./${{ env.REPOTITLE_LOWERCASE }} - - name: Run the downloaded pipeline + - name: Run the downloaded pipeline (stub) + id: stub_run_pipeline + continue-on-error: true env: NXF_SINGULARITY_CACHEDIR: ./ NXF_SINGULARITY_HOME_MOUNT: true run: nextflow run ./${{ env.REPOTITLE_LOWERCASE }}/$( sed 's/\W/_/g' <<< ${{ env.REPO_BRANCH }}) -stub -profile test,singularity --outdir ./results + - name: Run the downloaded pipeline (stub run not supported) + id: run_pipeline + if: ${{ job.steps.stub_run_pipeline.status == failure() }} + env: + NXF_SINGULARITY_CACHEDIR: ./ + NXF_SINGULARITY_HOME_MOUNT: true + run: nextflow run ./${{ env.REPOTITLE_LOWERCASE }}/$( sed 's/\W/_/g' <<< ${{ env.REPO_BRANCH }}) -profile test,singularity --outdir ./results diff --git a/.github/workflows/fix-linting.yml b/.github/workflows/fix-linting.yml old mode 100644 new mode 100755 index f459fc21..ddaa085a --- a/.github/workflows/fix-linting.yml +++ b/.github/workflows/fix-linting.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest steps: # Use the @nf-core-bot token to check out so we can push later - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4 with: token: ${{ secrets.nf_core_bot_auth_token }} @@ -32,9 +32,9 @@ jobs: GITHUB_TOKEN: ${{ secrets.nf_core_bot_auth_token }} # Install and run pre-commit - - uses: actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c # v5 + - uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # v5 with: - python-version: 3.11 + python-version: "3.12" - name: Install pre-commit run: pip install pre-commit diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml old mode 100644 new mode 100755 index 073e1876..1fcafe88 --- a/.github/workflows/linting.yml +++ b/.github/workflows/linting.yml @@ -14,13 +14,12 @@ jobs: pre-commit: runs-on: ubuntu-latest steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4 - - name: Set up Python 3.11 - uses: actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c # v5 + - name: Set up Python 3.12 + uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # v5 with: - python-version: 3.11 - cache: "pip" + python-version: "3.12" - name: Install pre-commit run: pip install pre-commit @@ -32,14 +31,14 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out pipeline code - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 + uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4 - name: Install Nextflow - uses: nf-core/setup-nextflow@v1 + uses: nf-core/setup-nextflow@v2 - - uses: actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c # v5 + - uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # v5 with: - python-version: "3.11" + python-version: "3.12" architecture: "x64" - name: Install dependencies @@ -60,7 +59,7 @@ jobs: - name: Upload linting log file artifact if: ${{ always() }} - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4 + uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4 with: name: linting-logs path: | diff --git a/.github/workflows/linting_comment.yml b/.github/workflows/linting_comment.yml old mode 100644 new mode 100755 index b706875f..40acc23f --- a/.github/workflows/linting_comment.yml +++ b/.github/workflows/linting_comment.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Download lint results - uses: dawidd6/action-download-artifact@f6b0bace624032e30a85a8fd9c1a7f8f611f5737 # v3 + uses: dawidd6/action-download-artifact@09f2f74827fd3a8607589e5ad7f9398816f540fe # v3 with: workflow: linting.yml workflow_conclusion: completed diff --git a/.github/workflows/release-announcements.yml b/.github/workflows/release-announcements.yml old mode 100644 new mode 100755 index d468aeaa..03ecfcf7 --- a/.github/workflows/release-announcements.yml +++ b/.github/workflows/release-announcements.yml @@ -12,7 +12,7 @@ jobs: - name: get topics and convert to hashtags id: get_topics run: | - curl -s https://nf-co.re/pipelines.json | jq -r '.remote_workflows[] | select(.full_name == "${{ github.repository }}") | .topics[]' | awk '{print "#"$0}' | tr '\n' ' ' >> $GITHUB_OUTPUT + echo "topics=$(curl -s https://nf-co.re/pipelines.json | jq -r '.remote_workflows[] | select(.full_name == "${{ github.repository }}") | .topics[]' | awk '{print "#"$0}' | tr '\n' ' ')" >> $GITHUB_OUTPUT - uses: rzr/fediverse-action@master with: @@ -25,13 +25,13 @@ jobs: Please see the changelog: ${{ github.event.release.html_url }} - ${{ steps.get_topics.outputs.GITHUB_OUTPUT }} #nfcore #openscience #nextflow #bioinformatics + ${{ steps.get_topics.outputs.topics }} #nfcore #openscience #nextflow #bioinformatics send-tweet: runs-on: ubuntu-latest steps: - - uses: actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c # v5 + - uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # v5 with: python-version: "3.10" - name: Install dependencies diff --git a/.nf-core.yml b/.nf-core.yml index cfe39173..39feb094 100644 --- a/.nf-core.yml +++ b/.nf-core.yml @@ -1,6 +1,6 @@ repository_type: pipeline +nf_core_version: "2.14.1" lint: files_unchanged: - - .github/ISSUE_TEMPLATE/bug_report.yml - - pyproject.toml - multiqc_config: false + - .github/CONTRIBUTING.md + multiqc_config: false \ No newline at end of file diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index af57081f..10207fa6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -3,8 +3,11 @@ repos: rev: "v3.1.0" hooks: - id: prettier + additional_dependencies: + - prettier@3.2.5 + - repo: https://github.com/editorconfig-checker/editorconfig-checker.python rev: "2.7.3" hooks: - id: editorconfig-checker - alias: ec + alias: ec \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index dcb7d501..f5b09ee6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,30 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## 1.1.0dev - [date] +## [[1.1.1](https://github.com/nf-core/proteinfold/releases/tag/1.1.1)] - 2025-07-30 + +- Minor patch release to fix multiqc report. + +### Enhancements & fixes + +## [[1.1.0](https://github.com/nf-core/proteinfold/releases/tag/1.1.0)] - 2025-06-25 + +### Credits + +Special thanks to the following for their contributions to the release: + +- [Adam Talbot](https://github.com/adamrtalbot) +- [Athanasios Baltzis](https://github.com/athbaltzis) +- [Björn Langer](https://github.com/bjlang) +- [Igor Trujnara](https://github.com/itrujnara) +- [Matthias Hörtenhuber](https://github.com/mashehu) +- [Maxime Garcia](https://github.com/maxulysse) +- [Júlia Mir Pedrol](https://github.com/mirpedrol) +- [Ziad Al-Bkhetan](https://github.com/ziadbkh) + +Thank you to everyone else that has contributed by reporting bugs, enhancements or in any other way, shape or form. + +## [[1.1.0](https://github.com/nf-core/proteinfold/releases/tag/1.1.0)] - 2025-06-21 ### Enhancements & fixes @@ -28,11 +51,49 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [[PR #132](https://github.com/nf-core/proteinfold/pull/132)] - Remove `lib/` directory. - [[#135](https://github.com/nf-core/proteinfold/issues/135)] - Reduce Alphafold Docker images sizes. - [[#115](https://github.com/nf-core/proteinfold/issues/115)] - Throw message error when profile conda is used. -<<<<<<< HEAD -- [[#144](https://github.com/nf-core/proteinfold/issues/144)] - Force value channels when providing dbs (downloaded) in `main.nf` to enable the processing of multiple samples. -======= -- [[#131](https://github.com/nf-core/proteinfold/issues/131)] - Add esmfold small tests. ->>>>>>> Include esmfold small test +- [[#147](https://github.com/nf-core/proteinfold/issues/147)] - Update modules to last version. +- [[#145](https://github.com/nf-core/proteinfold/issues/145)] - Implement test to check the processes/subworkflows triggered when downloading the databases. +- [[#130](https://github.com/nf-core/proteinfold/issues/130)] - Add `--skip_multiqc` parameter. +- [[PR #154](https://github.com/nf-core/proteinfold/pull/154)] - Update pipeline template to [nf-core/tools 2.14.1](https://github.com/nf-core/tools/releases/tag/2.14.1). +- [[#148](https://github.com/nf-core/proteinfold/issues/148)] - Update Colabfold DBs. +- [[PR #159](https://github.com/nf-core/proteinfold/pull/159)] - Update `mgnify` paths to new available version. +- [[PR ##163](https://github.com/nf-core/proteinfold/pull/163)] - Fix full test CI. +- [[#150]](https://github.com/nf-core/proteinfold/issues/150)] - Add thanks to the AWS Open Data Sponsorship program in `README.md`. +- [[PR ##166](https://github.com/nf-core/proteinfold/pull/166)] - Create 2 different parameters for Colabfold and ESMfold number of recycles. + +### Parameters + +| Old parameter | New parameter | +| --------------------- | ---------------------------------------- | +| `--uniclust30` | | +| `--bfd` | `--bfd_link` | +| `--small_bfd` | `--small_bfd_link` | +| `--alphafold2_params` | `--alphafold2_params_link` | +| `--mgnify` | `--mgnify_link` | +| `--pdb70` | `--pdb70_link` | +| `--pdb_mmcif` | `--pdb_mmcif_link` | +| `--pdb_obsolete` | `--pdb_obsolete_link` | +| `--uniref90` | `--uniref90_link` | +| `--pdb_seqres` | `--pdb_seqres_link` | +| `--uniprot_sprot` | `--uniprot_sprot_link` | +| `--uniprot_trembl` | `--uniprot_trembl_link` | +| `--uniclust30_path` | `--uniref30_alphafold2_path` | +| `--uniref30` | `--uniref30_colabfold_link` | +| `--uniref30_path` | `--uniref30_colabfold_path` | +| `--num_recycle` | `--num_recycles_colabfold` | +| | `--num_recycles_esmfold` | +| | `--uniref30_alphafold2_link` | +| | `--esmfold_db` | +| | `--esmfold_model_preset` | +| | `--esmfold_3B_v1` | +| | `--esm2_t36_3B_UR50D` | +| | `--esm2_t36_3B_UR50D_contact_regression` | +| | `--esmfold_params_path` | +| | `--skip_multiqc` | + +> **NB:** Parameter has been **updated** if both old and new parameter information is present. +> **NB:** Parameter has been **added** if just the new parameter information is present. +> **NB:** Parameter has been **removed** if parameter information isn't present. ## 1.0.0 - White Silver Reebok diff --git a/README.md b/README.md index b79c916f..91086e9f 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ [![run with conda](http://img.shields.io/badge/run%20with-conda-3EB049?labelColor=000000&logo=anaconda)](https://docs.conda.io/en/latest/) [![run with docker](https://img.shields.io/badge/run%20with-docker-0db7ed?labelColor=000000&logo=docker)](https://www.docker.com/) [![run with singularity](https://img.shields.io/badge/run%20with-singularity-1d355c.svg?labelColor=000000)](https://sylabs.io/docs/) -[![Launch on Seqera Platform](https://img.shields.io/badge/Launch%20%F0%9F%9A%80-Seqera%20Platform-%234256e7)](https://tower.nf/launch?pipeline=https://github.com/nf-core/proteinfold) +[![Launch on Seqera Platform](https://img.shields.io/badge/Launch%20%F0%9F%9A%80-Seqera%20Platform-%234256e7)](https://cloud.seqera.io/launch?pipeline=https://github.com/nf-core/proteinfold) [![Get help on Slack](http://img.shields.io/badge/slack-nf--core%20%23proteinfold-4A154B?labelColor=000000&logo=slack)](https://nfcore.slack.com/channels/proteinfold)[![Follow on Twitter](http://img.shields.io/badge/twitter-%40nf__core-1DA1F2?labelColor=000000&logo=twitter)](https://twitter.com/nf_core)[![Follow on Mastodon](https://img.shields.io/badge/mastodon-nf__core-6364ff?labelColor=FFFFFF&logo=mastodon)](https://mstdn.science/@nf_core)[![Watch on YouTube](http://img.shields.io/badge/youtube-nf--core-FF0000?labelColor=000000&logo=youtube)](https://www.youtube.com/c/nf-core) @@ -37,7 +37,7 @@ On release, automated continuous integration tests run the pipeline on a full-si 1. Choice of protein structure prediction method: - i. [AlphaFold2](https://github.com/deepmind/alphafold) + i. [AlphaFold2](https://github.com/deepmind/alphafold) - Regular AlphaFold2 (MSA computation and model inference in the same process) ii. [AlphaFold2 split](https://github.com/luisas/alphafold_split) - AlphaFold2 MSA computation and model inference in separate processes @@ -45,7 +45,7 @@ On release, automated continuous integration tests run the pipeline on a full-si iv. [ColabFold](https://github.com/sokrypton/ColabFold) - MMseqs2 local search followed by ColabFold - v. [ESMFold](https://github.com/facebookresearch/esm) + v. [ESMFold](https://github.com/facebookresearch/esm) - Regular ESM ## Usage @@ -57,36 +57,55 @@ On release, automated continuous integration tests run the pipeline on a full-si First, prepare a samplesheet with your input data that looks as follows: -`samplesheet.csv`: +The pipeline takes care of downloading the databases and parameters required by AlphaFold2, Colabfold or ESMFold. In case you have already downloaded the required files, you can skip this step by providing the path to the databases using the corresponding parameter [`--alphafold2_db`], [`--colabfold_db`] or [`--esmfold_db`]. Please refer to the [usage documentation](https://nf-co.re/proteinfold/usage) to check the directory structure you need to provide for each of the databases. -```csv -sample,fastq_1,fastq_2 -CONTROL_REP1,AEG588A1_S1_L002_R1_001.fastq.gz,AEG588A1_S1_L002_R2_001.fastq.gz -``` +- The typical command to run AlphaFold2 mode is shown below: Each row represents a fastq file (single-end) or a pair of fastq files (paired end). ---> +- Here is the command to run AlphaFold2 splitting the MSA from the prediction execution: Now, you can run the pipeline using: - +- Below, the command to run colabfold_local mode: -```bash -nextflow run nf-core/proteinfold \ - -profile \ - --input samplesheet.csv \ - --outdir -``` + ```console + nextflow run nf-core/proteinfold \ + --input samplesheet.csv \ + --outdir \ + --mode colabfold \ + --colabfold_server local \ + --colabfold_db \ + --num_recycles_colabfold 3 \ + --use_amber \ + --colabfold_model_preset "AlphaFold2-ptm" \ + --use_gpu \ + --db_load_mode 0 + -profile + ``` -> [!WARNING] -> Please provide pipeline parameters via the CLI or Nextflow `-params-file` option. Custom config files including those provided by the `-c` Nextflow option can be used to provide any configuration _**except for parameters**_; -> see [docs](https://nf-co.re/usage/configuration#custom-configuration-files). +- The typical command to run colabfold_webserver mode would be: -For more details and further functionality, please refer to the [usage documentation](https://nf-co.re/proteinfold/usage) and the [parameter documentation](https://nf-co.re/proteinfold/parameters). + ```console + nextflow run nf-core/proteinfold \ + --input samplesheet.csv \ + --outdir \ + --mode colabfold \ + --colabfold_server webserver \ + --host_url \ + --colabfold_db \ + --num_recycles_colabfold 3 \ + --use_amber \ + --colabfold_model_preset "AlphaFold2-ptm" \ + --use_gpu \ + -profile + ``` + + [!WARNING] + + > If you aim to carry out a large amount of predictions using the colabfold_webserver mode, please setup and use your own custom MMSeqs2 API Server. You can find instructions [here](https://github.com/sokrypton/ColabFold/tree/main/MsaServer). -## Pipeline output -- Typical command to run esmfold mode: +- The esmfold mode can be run using the command below: ```console nextflow run nf-core/proteinfold \ @@ -95,7 +114,7 @@ For more details and further functionality, please refer to the [usage documenta --mode esmfold \ --esmfold_model_preset \ --esmfold_db \ - --num_recycles 4 \ + --num_recycles_esmfold 4 \ --use_gpu \ -profile ``` @@ -117,10 +136,10 @@ For more details about the output files and reports, please refer to the nf-core/proteinfold was originally written by Athanasios Baltzis ([@athbaltzis](https://github.com/athbaltzis)), Jose Espinosa-Carrasco ([@JoseEspinosa](https://github.com/JoseEspinosa)), Luisa Santus ([@luisas](https://github.com/luisas)) and Leila Mansouri ([@l-mansouri](https://github.com/l-mansouri)) from [The Comparative Bioinformatics Group](https://www.crg.eu/en/cedric_notredame) at [The Centre for Genomic Regulation, Spain](https://www.crg.eu/) under the umbrella of the [BovReg project](https://www.bovreg.eu/) and Harshil Patel ([@drpatelh](https://github.com/drpatelh)) from [Seqera Labs, Spain](https://seqera.io/). -We thank the following people for their extensive assistance in the development of this pipeline: - Many thanks to others who have helped out and contributed along the way too, including (but not limited to): Norman Goodacre and Waleed Osman from Interline Therapeutics ([@interlinetx](https://github.com/interlinetx)), Martin Steinegger ([@martin-steinegger](https://github.com/martin-steinegger)) and Raoul J.P. Bonnal ([@rjpbonnal](https://github.com/rjpbonnal)) +We would also like to thanks to the AWS Open Data Sponsorship Program for generously providing the resources necessary to host the data utilized in the testing, development, and deployment of nf-core proteinfold. + ## Contributions and Support If you would like to contribute to this pipeline, please see the [contributing guidelines](.github/CONTRIBUTING.md). diff --git a/assets/multiqc_config.yml b/assets/multiqc_config.yml index c28872f6..3b58e3d0 100644 --- a/assets/multiqc_config.yml +++ b/assets/multiqc_config.yml @@ -1,7 +1,7 @@ report_comment: > - This report has been generated by the nf-core/proteinfold + This report has been generated by the nf-core/proteinfold analysis pipeline. For information about how to interpret these results, please see the - documentation. + documentation. report_section_order: "nf-core-proteinfold-methods-description": order: -1000 @@ -12,4 +12,11 @@ report_section_order: export_plots: true +# Run only these modules +run_modules: + - custom_content + - run_alphafold2 + - run_alphafold2_pred + - colabfold_batch + disable_version_detection: true diff --git a/conf/base.config b/conf/base.config index f13f56b2..69ad41e9 100644 --- a/conf/base.config +++ b/conf/base.config @@ -59,7 +59,4 @@ process { errorStrategy = 'retry' maxRetries = 2 } - withName:CUSTOM_DUMPSOFTWAREVERSIONS { - cache = false - } } diff --git a/conf/modules.config b/conf/modules.config index cd1ee8d2..f68878b8 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -20,14 +20,6 @@ process { mode: params.publish_dir_mode, saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] - - withName: 'CUSTOM_DUMPSOFTWAREVERSIONS' { - publishDir = [ - path: { "${params.outdir}/pipeline_info" }, - mode: params.publish_dir_mode, - pattern: '*_versions.yml' - ] - } } // diff --git a/conf/modules_alphafold2.config b/conf/modules_alphafold2.config index 9a266160..4aae2d30 100644 --- a/conf/modules_alphafold2.config +++ b/conf/modules_alphafold2.config @@ -15,7 +15,7 @@ // process { - withName: 'GUNZIP|COMBINE_UNIPROT|DOWNLOAD_PDBMMCIF' { + withName: 'GUNZIP|COMBINE_UNIPROT|DOWNLOAD_PDBMMCIF|ARIA2_PDB_SEQRES' { publishDir = [ path: {"${params.outdir}/DBs/${params.mode}/${params.alphafold2_mode}"}, mode: 'symlink', diff --git a/conf/modules_colabfold.config b/conf/modules_colabfold.config index 3a6d2e23..a7a719b0 100644 --- a/conf/modules_colabfold.config +++ b/conf/modules_colabfold.config @@ -37,7 +37,8 @@ if (params.colabfold_server == 'local') { ] } withName: 'MMSEQS_CREATEINDEX' { - ext.args = '--remove-tmp-files 1' + ext.args = '--remove-tmp-files 1' + ext.args2 = '*_seq.tsv' publishDir = [ enabled: false ] diff --git a/conf/test.config b/conf/test.config index dfdebd88..b8996f1a 100644 --- a/conf/test.config +++ b/conf/test.config @@ -24,7 +24,7 @@ params { // Input data to test alphafold2 analysis mode = 'alphafold2' alphafold2_mode = 'standard' - input = 'https://raw.githubusercontent.com/nf-core/test-datasets/proteinfold/testdata/samplesheet/v1.0/samplesheet.csv' + input = params.pipelines_testdata_base_path + '/proteinfold/testdata/samplesheet/v1.0/samplesheet.csv' alphafold2_db = "${projectDir}/assets/dummy_db_dir" } diff --git a/conf/test_alphafold_download.config b/conf/test_alphafold_download.config new file mode 100755 index 00000000..24e7e5c8 --- /dev/null +++ b/conf/test_alphafold_download.config @@ -0,0 +1,32 @@ +/* +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Nextflow config file for running minimal tests +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Defines input files and everything required to run a fast and simple pipeline test. + Use as follows: + nextflow run nf-core/proteinfold -profile test_alphafold2_download, --outdir +---------------------------------------------------------------------------------------- +*/ + +stubRun = true + +params { + config_profile_name = 'Test profile' + config_profile_description = 'Minimal test dataset to check pipeline function' + + // Limit resources so that this can run on GitHub Actions + max_cpus = 2 + max_memory = '6.GB' + max_time = '6.h' + + // Input data to test alphafold2 analysis + mode = 'alphafold2' + alphafold2_mode = 'standard' + input = params.pipelines_testdata_base_path + 'proteinfold/testdata/samplesheet/v1.0/samplesheet.csv' +} + +process { + withName: 'ARIA2|UNTAR|RUN_ALPHAFOLD2' { + container = 'biocontainers/gawk:5.1.0' + } +} \ No newline at end of file diff --git a/conf/test_alphafold_split.config b/conf/test_alphafold_split.config index 1bc651f6..295ffd67 100644 --- a/conf/test_alphafold_split.config +++ b/conf/test_alphafold_split.config @@ -24,7 +24,7 @@ params { // Input data to test alphafold2 splitting MSA from prediction analysis mode = 'alphafold2' alphafold2_mode = 'split_msa_prediction' - input = 'https://raw.githubusercontent.com/nf-core/test-datasets/proteinfold/testdata/samplesheet/v1.0/samplesheet.csv' + input = params.pipelines_testdata_base_path + 'proteinfold/testdata/samplesheet/v1.0/samplesheet.csv' alphafold2_db = "${projectDir}/assets/dummy_db_dir" } diff --git a/conf/test_colabfold_download.config b/conf/test_colabfold_download.config new file mode 100755 index 00000000..7a641118 --- /dev/null +++ b/conf/test_colabfold_download.config @@ -0,0 +1,32 @@ +/* +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Nextflow config file for running minimal tests +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Defines input files and everything required to run a fast and simple pipeline test. + Use as follows: + nextflow run nf-core/proteinfold -profile test_colabfold_download, --outdir +---------------------------------------------------------------------------------------- +*/ + +stubRun = true + +params { + config_profile_name = 'Test profile' + config_profile_description = 'Minimal test dataset to check pipeline function' + + // Limit resources so that this can run on GitHub Actions + max_cpus = 2 + max_memory = '6.GB' + max_time = '6.h' + + // Input data to test colabfold analysis + mode = 'colabfold' + colabfold_server = 'webserver' + input = params.pipelines_testdata_base_path + 'proteinfold/testdata/samplesheet/v1.0/samplesheet.csv' +} + +process { + withName: 'ARIA2|UNTAR|COLABFOLD_BATCH' { + container = 'biocontainers/gawk:5.1.0' + } +} \ No newline at end of file diff --git a/conf/test_colabfold_local.config b/conf/test_colabfold_local.config index 43c04b4c..b401c0aa 100644 --- a/conf/test_colabfold_local.config +++ b/conf/test_colabfold_local.config @@ -4,7 +4,7 @@ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Defines input files and everything required to run a fast and simple pipeline test. Use as follows: - nextflow run nf-core/proteinfold -profile test, --outdir + nextflow run nf-core/proteinfold -profile test_colabfold_local, --outdir ---------------------------------------------------------------------------------------- */ @@ -23,7 +23,7 @@ params { mode = 'colabfold' colabfold_server = 'local' colabfold_db = "${projectDir}/assets/dummy_db_dir" - input = 'https://raw.githubusercontent.com/nf-core/test-datasets/proteinfold/testdata/samplesheet/v1.0/samplesheet.csv' + input = params.pipelines_testdata_base_path + 'proteinfold/testdata/samplesheet/v1.0/samplesheet.csv' } process { diff --git a/conf/test_colabfold_webserver.config b/conf/test_colabfold_webserver.config index 99ebf182..3cd74de7 100644 --- a/conf/test_colabfold_webserver.config +++ b/conf/test_colabfold_webserver.config @@ -4,7 +4,7 @@ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Defines input files and everything required to run a fast and simple pipeline test. Use as follows: - nextflow run nf-core/proteinfold -profile test, --outdir + nextflow run nf-core/proteinfold -profile test_colabfold_webserver, --outdir ---------------------------------------------------------------------------------------- */ @@ -23,7 +23,7 @@ params { mode = 'colabfold' colabfold_server = 'webserver' colabfold_db = "${projectDir}/assets/dummy_db_dir" - input = 'https://raw.githubusercontent.com/nf-core/test-datasets/proteinfold/testdata/samplesheet/v1.0/samplesheet.csv' + input = params.pipelines_testdata_base_path + 'proteinfold/testdata/samplesheet/v1.0/samplesheet.csv' } process { diff --git a/conf/test_esmfold.config b/conf/test_esmfold.config index 38202ac8..ad984742 100644 --- a/conf/test_esmfold.config +++ b/conf/test_esmfold.config @@ -4,7 +4,7 @@ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Defines input files and everything required to run a fast and simple pipeline test. Use as follows: - nextflow run nf-core/proteinfold -profile test, --outdir + nextflow run nf-core/proteinfold -profile test_esmfold, --outdir ---------------------------------------------------------------------------------------- */ @@ -22,7 +22,7 @@ params { // Input data to test esmfold mode = 'esmfold' esmfold_db = "${projectDir}/assets/dummy_db_dir" - input = 'https://raw.githubusercontent.com/nf-core/test-datasets/proteinfold/testdata/samplesheet/v1.0/samplesheet.csv' + input = params.pipelines_testdata_base_path + 'proteinfold/testdata/samplesheet/v1.0/samplesheet.csv' } process { diff --git a/conf/test_full.config b/conf/test_full.config index 2c8a4fae..18233938 100644 --- a/conf/test_full.config +++ b/conf/test_full.config @@ -17,6 +17,6 @@ params { // Input data for full test of alphafold standard mode mode = 'alphafold2' alphafold2_mode = 'standard' - input = 'https://raw.githubusercontent.com/nf-core/test-datasets/proteinfold/testdata/samplesheet/v1.0/samplesheet.csv' + input = params.pipelines_testdata_base_path + 'proteinfold/testdata/samplesheet/v1.0/samplesheet.csv' alphafold2_db = 's3://proteinfold-dataset/test-data/db/alphafold_mini' } diff --git a/conf/test_full_alphafold_multimer.config b/conf/test_full_alphafold_multimer.config index 2f8b0627..62e81966 100644 --- a/conf/test_full_alphafold_multimer.config +++ b/conf/test_full_alphafold_multimer.config @@ -18,6 +18,6 @@ params { mode = 'alphafold2' alphafold2_mode = 'standard' alphafold2_model_preset = 'multimer' - input = 'https://raw.githubusercontent.com/nf-core/test-datasets/proteinfold/testdata/samplesheet/v1.0/samplesheet_multimer.csv' + input = params.pipelines_testdata_base_path + 'proteinfold/testdata/samplesheet/v1.0/samplesheet_multimer.csv' alphafold2_db = 's3://proteinfold-dataset/test-data/db/alphafold_mini' } diff --git a/conf/test_full_alphafold_split.config b/conf/test_full_alphafold_split.config index 9cb378c2..90df73f2 100644 --- a/conf/test_full_alphafold_split.config +++ b/conf/test_full_alphafold_split.config @@ -17,6 +17,6 @@ params { // Input data to test colabfold with a local server analysis mode = 'alphafold2' alphafold2_mode = 'split_msa_prediction' - input = 'https://raw.githubusercontent.com/nf-core/test-datasets/proteinfold/testdata/samplesheet/v1.0/samplesheet.csv' + input = params.pipelines_testdata_base_path + 'proteinfold/testdata/samplesheet/v1.0/samplesheet.csv' alphafold2_db = 's3://proteinfold-dataset/test-data/db/alphafold_mini' } diff --git a/conf/test_full_colabfold_local.config b/conf/test_full_colabfold_local.config index 90f1c811..ad91f5e0 100644 --- a/conf/test_full_colabfold_local.config +++ b/conf/test_full_colabfold_local.config @@ -19,7 +19,7 @@ params { mode = 'colabfold' colabfold_server = 'local' colabfold_model_preset = 'alphafold2_ptm' - input = 'https://raw.githubusercontent.com/nf-core/test-datasets/proteinfold/testdata/samplesheet/v1.0/samplesheet.csv' + input = params.pipelines_testdata_base_path + 'proteinfold/testdata/samplesheet/v1.0/samplesheet.csv' colabfold_db = 's3://proteinfold-dataset/test-data/db/colabfold_mini' } process { diff --git a/conf/test_full_colabfold_webserver.config b/conf/test_full_colabfold_webserver.config index a9db381a..7e296189 100644 --- a/conf/test_full_colabfold_webserver.config +++ b/conf/test_full_colabfold_webserver.config @@ -18,6 +18,6 @@ params { mode = 'colabfold' colabfold_server = 'webserver' colabfold_model_preset = 'alphafold2_ptm' - input = 'https://raw.githubusercontent.com/nf-core/test-datasets/proteinfold/testdata/samplesheet/v1.0/samplesheet.csv' + input = params.pipelines_testdata_base_path + 'proteinfold/testdata/samplesheet/v1.0/samplesheet.csv' colabfold_db = 's3://proteinfold-dataset/test-data/db/colabfold_mini' } diff --git a/conf/test_full_colabfold_webserver_multimer.config b/conf/test_full_colabfold_webserver_multimer.config index 612a1221..c8adca61 100644 --- a/conf/test_full_colabfold_webserver_multimer.config +++ b/conf/test_full_colabfold_webserver_multimer.config @@ -18,6 +18,6 @@ params { mode = 'colabfold' colabfold_server = 'webserver' colabfold_model_preset = 'alphafold2_multimer_v3' - input = 'https://raw.githubusercontent.com/nf-core/test-datasets/proteinfold/testdata/samplesheet/v1.0/samplesheet_multimer.csv' + input = params.pipelines_testdata_base_path + 'proteinfold/testdata/samplesheet/v1.0/samplesheet_multimer.csv' colabfold_db = 's3://proteinfold-dataset/test-data/db/colabfold_mini' } diff --git a/conf/test_full_esmfold.config b/conf/test_full_esmfold.config index 64bb7628..136e7414 100644 --- a/conf/test_full_esmfold.config +++ b/conf/test_full_esmfold.config @@ -5,7 +5,7 @@ Defines input files and everything required to run a full size pipeline test. Use as follows: - nextflow run nf-core/proteinfold -profile test_full_colabfold_webserver, --outdir + nextflow run nf-core/proteinfold -profile test_full_esmfold, --outdir ---------------------------------------------------------------------------------------- */ @@ -17,6 +17,6 @@ params { // Input data for full test of esmfold monomer mode = 'esmfold' esmfold_model_preset = 'monomer' - input = 'https://raw.githubusercontent.com/nf-core/test-datasets/proteinfold/testdata/samplesheet/v1.0/samplesheet.csv' + input = params.pipelines_testdata_base_path + 'proteinfold/testdata/samplesheet/v1.0/samplesheet.csv' //esmfold_db = 's3://proteinfold-dataset/test-data/db/esmfold' } diff --git a/conf/test_full_esmfold_multimer.config b/conf/test_full_esmfold_multimer.config index 36445443..498ae002 100644 --- a/conf/test_full_esmfold_multimer.config +++ b/conf/test_full_esmfold_multimer.config @@ -5,7 +5,7 @@ Defines input files and everything required to run a full size pipeline test. Use as follows: - nextflow run nf-core/proteinfold -profile test_full_colabfold_webserver, --outdir + nextflow run nf-core/proteinfold -profile test_full_esmfold_multimer, --outdir ---------------------------------------------------------------------------------------- */ @@ -17,6 +17,6 @@ params { // Input data for full test of esmfold multimer mode = 'esmfold' esmfold_model_preset = 'multimer' - input = 'https://raw.githubusercontent.com/nf-core/test-datasets/proteinfold/testdata/samplesheet/v1.0/samplesheet_multimer.csv' + input = params.pipelines_testdata_base_path + 'proteinfold/testdata/samplesheet/v1.0/samplesheet_multimer.csv' esmfold_db = 's3://proteinfold-dataset/test-data/db/esmfold' } diff --git a/dockerfiles/Dockerfile_nfcore-proteinfold_alphafold2_msa b/dockerfiles/Dockerfile_nfcore-proteinfold_alphafold2_msa old mode 100644 new mode 100755 index af184d3a..64baaa38 --- a/dockerfiles/Dockerfile_nfcore-proteinfold_alphafold2_msa +++ b/dockerfiles/Dockerfile_nfcore-proteinfold_alphafold2_msa @@ -9,7 +9,7 @@ LABEL authors="Luisa Santus, Athanasios Baltzis, Jose Espinosa-Carrasco, Leila M SHELL ["/bin/bash", "-o", "pipefail", "-c"] # Add env variables -ENV LD_LIBRARY_PATH="/conda/lib:/usr/local/cuda-11.1/lib64:$LD_LIBRARY_PATH" +ENV LD_LIBRARY_PATH="/conda/lib:/usr/local/cuda-11.4/lib64:$LD_LIBRARY_PATH" ENV PATH="/conda/bin:$PATH" RUN apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A4B469963BF863CC diff --git a/dockerfiles/Dockerfile_nfcore-proteinfold_alphafold2_split b/dockerfiles/Dockerfile_nfcore-proteinfold_alphafold2_split old mode 100644 new mode 100755 index 2c7fd102..4f4c89b4 --- a/dockerfiles/Dockerfile_nfcore-proteinfold_alphafold2_split +++ b/dockerfiles/Dockerfile_nfcore-proteinfold_alphafold2_split @@ -9,7 +9,7 @@ LABEL authors="Athanasios Baltzis, Jose Espinosa-Carrasco, Leila Mansouri" \ SHELL ["/bin/bash", "-o", "pipefail", "-c"] # Add env variables -ENV LD_LIBRARY_PATH="/conda/lib:/usr/local/cuda-11.1/lib64:$LD_LIBRARY_PATH" +ENV LD_LIBRARY_PATH="/conda/lib:/usr/local/cuda-11.4/lib64:$LD_LIBRARY_PATH" ENV PATH="/conda/bin:$PATH" RUN apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A4B469963BF863CC diff --git a/dockerfiles/Dockerfile_nfcore-proteinfold_alphafold2_standard b/dockerfiles/Dockerfile_nfcore-proteinfold_alphafold2_standard old mode 100644 new mode 100755 index e7b49f5a..774d89f6 --- a/dockerfiles/Dockerfile_nfcore-proteinfold_alphafold2_standard +++ b/dockerfiles/Dockerfile_nfcore-proteinfold_alphafold2_standard @@ -9,7 +9,7 @@ LABEL authors="Athanasios Baltzis, Jose Espinosa-Carrasco, Leila Mansouri" \ SHELL ["/bin/bash", "-o", "pipefail", "-c"] # Add env variables -ENV LD_LIBRARY_PATH="/conda/lib:/usr/local/cuda-11.1/lib64:$LD_LIBRARY_PATH" +ENV LD_LIBRARY_PATH="/conda/lib:/usr/local/cuda-11.4/lib64:$LD_LIBRARY_PATH" ENV PATH="/conda/bin:$PATH" RUN apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A4B469963BF863CC diff --git a/dockerfiles/Dockerfile_nfcore-proteinfold_colabfold b/dockerfiles/Dockerfile_nfcore-proteinfold_colabfold old mode 100644 new mode 100755 index 6e639b50..2ac1f851 --- a/dockerfiles/Dockerfile_nfcore-proteinfold_colabfold +++ b/dockerfiles/Dockerfile_nfcore-proteinfold_colabfold @@ -1,5 +1,6 @@ -FROM nvidia/cuda:11.4.2-cudnn8-runtime-ubuntu18.04 -LABEL authors="Athanasios Baltzis, Leila Mansouri" \ +FROM nvidia/cuda:11.4.3-cudnn8-runtime-ubuntu18.04 + +LABEL authors="Athanasios Baltzis, Jose Espinosa-Carrasco, Leila Mansouri" \ title="nfcore/proteinfold_colabfold" \ Version="1.1.0" \ description="Docker image containing all software requirements to run the COLABFOLD_BATCH module using the nf-core/proteinfold pipeline" @@ -24,7 +25,7 @@ RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y \ && rm -rf /var/lib/apt/lists/* RUN cd / \ - && wget https://raw.githubusercontent.com/YoshitakaMo/localcolabfold/fb0598ebbd8227096a052d3684a549ddf33d06bb/install_colabbatch_linux.sh \ + && wget https://raw.githubusercontent.com/YoshitakaMo/localcolabfold/82a3635/install_colabbatch_linux.sh \ && sed -i "/colabfold.download/d" install_colabbatch_linux.sh \ && sed -i "s|cudatoolkit==.*\sopenmm|cudatoolkit==11.1.1 openmm|g" install_colabbatch_linux.sh \ && bash install_colabbatch_linux.sh @@ -33,4 +34,4 @@ RUN /localcolabfold/colabfold-conda/bin/python3.10 -m pip install tensorflow-cpu #Silence download of the AlphaFold2 params RUN sed -i "s|download_alphafold_params(|#download_alphafold_params(|g" /localcolabfold/colabfold-conda/lib/python3.10/site-packages/colabfold/batch.py - +RUN sed -i "s|if args\.num_models|#if args\.num_models|g" /localcolabfold/colabfold-conda/lib/python3.10/site-packages/colabfold/batch.py diff --git a/dockerfiles/Dockerfile_nfcore-proteinfold_esmfold b/dockerfiles/Dockerfile_nfcore-proteinfold_esmfold old mode 100644 new mode 100755 diff --git a/docs/README.md b/docs/README.md old mode 100644 new mode 100755 diff --git a/docs/images/T1024_LmrP____408_residues__PAE_mqc.png b/docs/images/T1024_LmrP____408_residues__PAE_mqc.png old mode 100644 new mode 100755 diff --git a/docs/images/T1024_LmrP____408_residues__coverage_mqc.png b/docs/images/T1024_LmrP____408_residues__coverage_mqc.png old mode 100644 new mode 100755 diff --git a/docs/images/T1024_LmrP____408_residues__plddt_mqc.png b/docs/images/T1024_LmrP____408_residues__plddt_mqc.png old mode 100644 new mode 100755 diff --git a/docs/images/nf-core-proteinfold_logo_dark.png b/docs/images/nf-core-proteinfold_logo_dark.png old mode 100644 new mode 100755 diff --git a/docs/images/nf-core-proteinfold_logo_light.png b/docs/images/nf-core-proteinfold_logo_light.png old mode 100644 new mode 100755 diff --git a/docs/images/nf-core-proteinfold_metro_map.png b/docs/images/nf-core-proteinfold_metro_map.png old mode 100644 new mode 100755 diff --git a/docs/images/nf-core-proteinfold_metro_map.svg b/docs/images/nf-core-proteinfold_metro_map.svg old mode 100644 new mode 100755 diff --git a/docs/images/nf-core-proteinfold_metro_map_1.1.0.png b/docs/images/nf-core-proteinfold_metro_map_1.1.0.png old mode 100644 new mode 100755 diff --git a/docs/images/nf-core-proteinfold_metro_map_1.1.0.svg b/docs/images/nf-core-proteinfold_metro_map_1.1.0.svg old mode 100644 new mode 100755 diff --git a/docs/images/nf-core-proteinfold_metro_map_1.1.0_transp.png b/docs/images/nf-core-proteinfold_metro_map_1.1.0_transp.png old mode 100644 new mode 100755 diff --git a/docs/output.md b/docs/output.md old mode 100644 new mode 100755 diff --git a/docs/usage.md b/docs/usage.md old mode 100644 new mode 100755 index 421f8bf7..fbf7908b --- a/docs/usage.md +++ b/docs/usage.md @@ -18,9 +18,7 @@ You will need to create a samplesheet with information about the sequences you w ### Full samplesheet -The samplesheet can have as many columns as you desire, however, there is a strict requirement for the first 2 columns to match those defined in the table below. - -A final samplesheet file may look something like the one below. This is for 2 sequences. +A sample of the final samplesheet file for two sequences is shown below: ```csv title="samplesheet.csv" sample,fastq_1,fastq_2 @@ -29,6 +27,8 @@ CONTROL_REP1,AEG588A1_S1_L003_R1_001.fastq.gz,AEG588A1_S1_L003_R2_001.fastq.gz CONTROL_REP1,AEG588A1_S1_L004_R1_001.fastq.gz,AEG588A1_S1_L004_R2_001.fastq.gz ``` +The samplesheet can have as many columns as you desire, however, there is a strict requirement for the first 2 columns to match those defined in the table below: + | Column | Description | | ---------- | --------------------------------------------------------------------------------------------------- | | `sequence` | Custom sequence name. Spaces in sequence names are automatically converted to underscores (`_`). | @@ -38,9 +38,11 @@ An [example samplesheet](../assets/samplesheet.csv) has been provided with the p ## Running the pipeline -The typical commands for running the pipeline on AlphaFold2, Colabfold and ESMFold modes are as follows: +The typical commands for running the pipeline on AlphaFold2, Colabfold and ESMFold modes are shown below. -```csv title="samplesheet.csv" +AlphaFold2 regular can be run using this command: + +```bash nextflow run nf-core/proteinfold \ --input samplesheet.csv \ --outdir \ @@ -49,10 +51,12 @@ nextflow run nf-core/proteinfold \ --full_dbs \ --alphafold2_model_preset monomer \ --use_gpu \ - -profile + -profile ``` -```console +To run the AlphaFold2 that splits the MSA calculation from the model inference, you can use the `--alphafold2_mode split_msa_prediction` parameter, as shown below: + +```bash nextflow run nf-core/proteinfold \ --input samplesheet.csv \ --outdir \ @@ -62,36 +66,43 @@ nextflow run nf-core/proteinfold \ --full_dbs \ --alphafold2_model_preset monomer \ --use_gpu \ - -profile + -profile ``` -If you specify the `--alphafold2_db ` parameter, the directory structure of your path should be like this: +To provide the predownloaded AlphaFold2 databases and parameters you can specify the `--alphafold2_db ` parameter and the directory structure of your path should be like this: -``` -├── mgnify -│   └── mgy_clusters_2018_12.fa -├── alphafold_params_2022-03-02 +
    +Directory structure +```console +├── alphafold_params_2022-12-06 │   ├── LICENSE │   ├── params_model_1_multimer.npz │   ├── params_model_1_multimer_v2.npz +│   ├── params_model_1_multimer_v3.npz │   ├── params_model_1.npz │   ├── params_model_1_ptm.npz │   ├── params_model_2_multimer.npz │   ├── params_model_2_multimer_v2.npz +│   ├── params_model_2_multimer_v3.npz │   ├── params_model_2.npz │   ├── params_model_2_ptm.npz │   ├── params_model_3_multimer.npz │   ├── params_model_3_multimer_v2.npz +│   ├── params_model_3_multimer_v3.npz │   ├── params_model_3.npz │   ├── params_model_3_ptm.npz │   ├── params_model_4_multimer.npz │   ├── params_model_4_multimer_v2.npz +│   ├── params_model_4_multimer_v3.npz │   ├── params_model_4.npz │   ├── params_model_4_ptm.npz │   ├── params_model_5_multimer.npz │   ├── params_model_5_multimer_v2.npz +│   ├── params_model_5_multimer_v3.npz │   ├── params_model_5.npz │   └── params_model_5_ptm.npz +├── mgnify +│   └── mgy_clusters_2022_05.fa ├── pdb70 │   └── pdb70_from_mmcif_200916 │   ├── md5sum @@ -201,43 +212,40 @@ If you specify the `--alphafold2_db ` parameter, the directory structure of your │   └── pdb_seqres.txt ├── small_bfd │   └── bfd-first_non_consensus_sequences.fasta -├── uniclust30 -│   └── uniclust30_2018_08 -│   ├── uniclust30_2018_08_a3m_db -> uniclust30_2018_08_a3m.ffdata -│   ├── uniclust30_2018_08_a3m_db.index -│   ├── uniclust30_2018_08_a3m.ffdata -│   ├── uniclust30_2018_08_a3m.ffindex -│   ├── uniclust30_2018_08.cs219 -│   ├── uniclust30_2018_08_cs219.ffdata -│   ├── uniclust30_2018_08_cs219.ffindex -│   ├── uniclust30_2018_08.cs219.sizes -│   ├── uniclust30_2018_08_hhm_db -> uniclust30_2018_08_hhm.ffdata -│   ├── uniclust30_2018_08_hhm_db.index -│   ├── uniclust30_2018_08_hhm.ffdata -│   ├── uniclust30_2018_08_hhm.ffindex -│   └── uniclust30_2018_08_md5sum ├── uniprot │   └── uniprot.fasta +├── uniref30 +│   ├── UniRef30_2021_03_a3m.ffdata +│   ├── UniRef30_2021_03_a3m.ffindex +│   ├── UniRef30_2021_03_cs219.ffdata +│   ├── UniRef30_2021_03_cs219.ffindex +| ├── UniRef30_2021_03_hhm.ffdata +│   └── UniRef30_2021_03_hhm.ffindex └── uniref90 └── uniref90.fasta ``` +
    -```console +Colabfold mode using use your own custom MMSeqs2 API server (`--colabfold_server local`) can be run using the following command: + +```bash nextflow run nf-core/proteinfold \ --input samplesheet.csv \ --outdir \ --mode colabfold \ --colabfold_server local \ --colabfold_db \ - --num_recycle 3 \ + --num_recycles_colabfold 3 \ --use_amber \ --colabfold_model_preset "AlphaFold2-ptm" \ --use_gpu \ - --db_load_mode 0 - -profile + --db_load_mode 0 \ + -profile ``` -```console +The command to run run Colabfold, using the Colabfold webserver is shown below: + +```bash nextflow run nf-core/proteinfold \ --input samplesheet.csv \ --outdir \ @@ -245,16 +253,18 @@ nextflow run nf-core/proteinfold \ --colabfold_server webserver \ --host_url \ --colabfold_db \ - --num_recycle 3 \ + --num_recycles_colabfold 3 \ --use_amber \ --colabfold_model_preset "AlphaFold2-ptm" \ --use_gpu \ - -profile + -profile ``` -If you specify the `--colabfold_db ` parameter, the directory structure of your path should be like this: +If you specify the `--colabfold_db ` parameter, the directory structure of your path should be like this: -``` +
    +Directory structure +```console ├── colabfold_envdb_202108 │   ├── colabfold_envdb_202108_db.0 │   ├── colabfold_envdb_202108_db.1 @@ -332,60 +342,65 @@ If you specify the `--colabfold_db ` parameter, the directory structure of your │   │   ├── params_model_4_ptm.npz │   │   ├── params_model_5.npz │   │   └── params_model_5_ptm.npz -│   └── alphafold_params_colab_2022-03-02 +│   └── alphafold_params_colab_2022-12-06 │   ├── LICENSE -│   ├── params_model_1_multimer_v2.npz +│   ├── params_model_1_multimer_v3.npz │   ├── params_model_1.npz -│   ├── params_model_2_multimer_v2.npz +│   ├── params_model_2_multimer_v3.npz │   ├── params_model_2.npz │   ├── params_model_2_ptm.npz -│   ├── params_model_3_multimer_v2.npz +│   ├── params_model_3_multimer_v3.npz │   ├── params_model_3.npz -│   ├── params_model_4_multimer_v2.npz +│   ├── params_model_4_multimer_v3.npz │   ├── params_model_4.npz -│   ├── params_model_5_multimer_v2.npz +│   ├── params_model_5_multimer_v3.npz │   └── params_model_5.npz -└── uniref30_2202 - ├── uniref30_2202_db.0 - ├── uniref30_2202_db.1 - ├── uniref30_2202_db.2 - ├── uniref30_2202_db.3 - ├── uniref30_2202_db.4 - ├── uniref30_2202_db.5 - ├── uniref30_2202_db.6 - ├── uniref30_2202_db.7 - ├── uniref30_2202_db_aln.0 - ├── uniref30_2202_db_aln.1 - ├── uniref30_2202_db_aln.2 - ├── uniref30_2202_db_aln.3 - ├── uniref30_2202_db_aln.4 - ├── uniref30_2202_db_aln.5 - ├── uniref30_2202_db_aln.6 - ├── uniref30_2202_db_aln.7 - ├── uniref30_2202_db_aln.dbtype - ├── uniref30_2202_db_aln.index - ├── uniref30_2202_db.dbtype - ├── uniref30_2202_db_h - ├── uniref30_2202_db_h.dbtype - ├── uniref30_2202_db_h.index - ├── uniref30_2202_db.idx - ├── uniref30_2202_db.idx.dbtype - ├── uniref30_2202_db.idx.index - ├── uniref30_2202_db.index - ├── uniref30_2202_db_seq.0 - ├── uniref30_2202_db_seq.1 - ├── uniref30_2202_db_seq.2 - ├── uniref30_2202_db_seq.3 - ├── uniref30_2202_db_seq.4 - ├── uniref30_2202_db_seq.5 - ├── uniref30_2202_db_seq.6 - ├── uniref30_2202_db_seq.7 - ├── uniref30_2202_db_seq.dbtype - ├── uniref30_2202_db_seq_h -> uniref30_2202_db_h - ├── uniref30_2202_db_seq_h.dbtype -> uniref30_2202_db_h.dbtype - ├── uniref30_2202_db_seq_h.index -> uniref30_2202_db_h.index - └── uniref30_2202_db_seq.index +└── uniref30_2302 + ├── uniref30_2302_aln.tsv + ├── uniref30_2302_db.0 + ├── uniref30_2302_db.1 + ├── uniref30_2302_db.2 + ├── uniref30_2302_db.3 + ├── uniref30_2302_db.4 + ├── uniref30_2302_db.5 + ├── uniref30_2302_db.6 + ├── uniref30_2302_db.7 + ├── uniref30_2302_db_aln.0 + ├── uniref30_2302_db_aln.1 + ├── uniref30_2302_db_aln.2 + ├── uniref30_2302_db_aln.3 + ... + ├── uniref30_2302_db_aln.97 + ├── uniref30_2302_db_aln.98 + ├── uniref30_2302_db_aln.99 + ├── uniref30_2302_db_aln.dbtype + ├── uniref30_2302_db_aln.index + ├── uniref30_2302_db.dbtype + ├── uniref30_2302_db_h + ├── uniref30_2302_db_h.dbtype + ├── uniref30_2302_db_h.index + ├── uniref30_2302_db.idx + ├── uniref30_2302_db.idx.dbtype + ├── uniref30_2302_db.idx.index + ├── uniref30_2302_db.idx_mapping + ├── uniref30_2302_db.idx_taxonomy + ├── uniref30_2302_db.index + ├── uniref30_2302_db_mapping + ├── uniref30_2302_db_seq.0 + ├── uniref30_2302_db_seq.1 + ├── uniref30_2302_db_seq.2 + ├── uniref30_2302_db_seq.3 + ... + ├── uniref30_2302_db_seq.97 + ├── uniref30_2302_db_seq.98 + ├── uniref30_2302_db_seq.99 + ├── uniref30_2302_db_seq.dbtype + ├── uniref30_2302_db_seq_h -> uniref30_2302_db_h + ├── uniref30_2302_db_seq_h.dbtype -> uniref30_2302_db_h.dbtype + ├── uniref30_2302_db_seq_h.index -> uniref30_2302_db_h.index + └── uniref30_2302_db_seq.index ``` +
    ```console nextflow run nf-core/proteinfold \ @@ -393,14 +408,19 @@ nextflow run nf-core/proteinfold \ --outdir \ --mode esmfold --esmfold_db \ - --num_recycles 4 \ + --num_recycles_esmfold 4 \ --esmfold_model_preset \ --use_gpu \ -profile ``` -```bash -nextflow run nf-core/proteinfold --input ./samplesheet.csv --outdir ./results --genome GRCh37 -profile docker +If you specify the `--esmfold_db ` parameter, the directory structure of your path should be like this: + +```console +└── checkpoints + ├── esm2_t36_3B_UR50D-contact-regression.pt + ├── esm2_t36_3B_UR50D.pt + └── esmfold_3B_v1.pt ``` This will launch the pipeline with the `docker` configuration profile. See below for more information about profiles. @@ -499,6 +519,8 @@ If `-profile` is not specified, the pipeline will run locally and expect all sof - A generic configuration profile to be used with [Charliecloud](https://hpc.github.io/charliecloud/) - `apptainer` - A generic configuration profile to be used with [Apptainer](https://apptainer.org/) +- `wave` + - A generic configuration profile to enable [Wave](https://seqera.io/wave/) containers. Use together with one of the above (requires Nextflow ` 24.03.0-edge` or later). - `conda` - A generic configuration profile to be used with [Conda](https://conda.io/docs/). Please only use Conda as a last resort i.e. when it's not possible to run the pipeline with Docker, Singularity, Podman, Shifter, Charliecloud, or Apptainer. diff --git a/main.nf b/main.nf index e2370d54..80cca81d 100644 --- a/main.nf +++ b/main.nf @@ -131,7 +131,7 @@ workflow NFCORE_PROTEINFOLD { PREPARE_COLABFOLD_DBS.out.params.first(), PREPARE_COLABFOLD_DBS.out.colabfold_db.first(), PREPARE_COLABFOLD_DBS.out.uniref30.first(), - params.num_recycle + params.num_recycles_colabfold ) ch_multiqc = COLABFOLD.out.multiqc_report ch_versions = ch_versions.mix(COLABFOLD.out.versions) @@ -160,7 +160,7 @@ workflow NFCORE_PROTEINFOLD { ESMFOLD ( ch_versions, Channel.fromPath(params.esmfold_params_path), - params.num_recycle + params.num_recycles_esmfold ) ch_multiqc = ESMFOLD.out.multiqc_report ch_versions = ch_versions.mix(ESMFOLD.out.versions) diff --git a/modules/local/colabfold_batch.nf b/modules/local/colabfold_batch.nf index 28f26274..5dab51fb 100644 --- a/modules/local/colabfold_batch.nf +++ b/modules/local/colabfold_batch.nf @@ -7,7 +7,7 @@ process COLABFOLD_BATCH { error("Local COLABFOLD_BATCH module does not support Conda. Please use Docker / Singularity / Podman instead.") } - container "nf-core/proteinfold_colabfold:1.1.0" + container "nf-core/proteinfold_colabfold:1.1.1" input: tuple val(meta), path(fasta) diff --git a/modules/local/download_pdbmmcif.nf b/modules/local/download_pdbmmcif.nf index fef63755..98ef831e 100644 --- a/modules/local/download_pdbmmcif.nf +++ b/modules/local/download_pdbmmcif.nf @@ -2,6 +2,7 @@ * Download PDB MMCIF database */ process DOWNLOAD_PDBMMCIF { + tag "${source_url_pdb_mmcif}--${source_url_pdb_obsolete}" label 'process_low' label 'error_retry' diff --git a/modules/local/mmseqs_colabfoldsearch.nf b/modules/local/mmseqs_colabfoldsearch.nf index 978a627e..b879c8cd 100644 --- a/modules/local/mmseqs_colabfoldsearch.nf +++ b/modules/local/mmseqs_colabfoldsearch.nf @@ -7,7 +7,7 @@ process MMSEQS_COLABFOLDSEARCH { error("Local MMSEQS_COLABFOLDSEARCH module does not support Conda. Please use Docker / Singularity / Podman instead.") } - container "nf-core/proteinfold_colabfold:1.1.0" + container "nf-core/proteinfold_colabfold:1.1.1" input: tuple val(meta), path(fasta) @@ -16,7 +16,7 @@ process MMSEQS_COLABFOLDSEARCH { path uniref30 output: - tuple val(meta), path("${meta.id}.a3m"), emit: a3m + tuple val(meta), path("**.a3m"), emit: a3m path "versions.yml", emit: versions when: @@ -47,7 +47,8 @@ process MMSEQS_COLABFOLDSEARCH { stub: def VERSION = '1.5.2' // WARN: Version information not provided by tool on CLI. Please update this string when bumping container versions. """ - touch ${meta.id}.a3m + mkdir results + touch results/${meta.id}.a3m cat <<-END_VERSIONS > versions.yml "${task.process}": diff --git a/modules/local/org_run_alphafold2.nf b/modules/local/org_run_alphafold2.nf index 9d5c72f4..c46c2246 100644 --- a/modules/local/org_run_alphafold2.nf +++ b/modules/local/org_run_alphafold2.nf @@ -10,7 +10,7 @@ process RUN_ALPHAFOLD2 { error("Local RUN_ALPHAFOLD2 module does not support Conda. Please use Docker / Singularity / Podman instead.") } - container "nf-core/proteinfold_alphafold2_standard:dev" + container "nf-core/proteinfold_alphafold2_standard:1.1.1" input: tuple val(meta), path(fasta) diff --git a/modules/local/org_run_esmfold.nf b/modules/local/org_run_esmfold.nf index f4567239..c20a22c0 100644 --- a/modules/local/org_run_esmfold.nf +++ b/modules/local/org_run_esmfold.nf @@ -6,7 +6,7 @@ process RUN_ESMFOLD { error("Local RUN_ESMFOLD module does not support Conda. Please use Docker / Singularity / Podman instead.") } - container "nf-core/proteinfold_esmfold:1.1.0" + container "nf-core/proteinfold_esmfold:1.1.1" input: tuple val(meta), path(fasta) diff --git a/modules/local/run_alphafold2.nf b/modules/local/run_alphafold2.nf index 570243d5..31bdac8c 100644 --- a/modules/local/run_alphafold2.nf +++ b/modules/local/run_alphafold2.nf @@ -10,7 +10,7 @@ process RUN_ALPHAFOLD2 { error("Local RUN_ALPHAFOLD2 module does not support Conda. Please use Docker / Singularity / Podman instead.") } - container "nf-core/proteinfold_alphafold2_standard:dev" + container "nf-core/proteinfold_alphafold2_standard:1.1.1" input: tuple val(meta), path(fasta) diff --git a/modules/local/run_alphafold2_msa.nf b/modules/local/run_alphafold2_msa.nf index 7b904dd9..991387b4 100644 --- a/modules/local/run_alphafold2_msa.nf +++ b/modules/local/run_alphafold2_msa.nf @@ -10,7 +10,7 @@ process RUN_ALPHAFOLD2_MSA { error("Local RUN_ALPHAFOLD2_MSA module does not support Conda. Please use Docker / Singularity / Podman instead.") } - container "nf-core/proteinfold_alphafold2_msa:dev" + container "nf-core/proteinfold_alphafold2_msa:1.1.1" input: tuple val(meta), path(fasta) diff --git a/modules/local/run_alphafold2_pred.nf b/modules/local/run_alphafold2_pred.nf index 88b4f878..66ba22de 100644 --- a/modules/local/run_alphafold2_pred.nf +++ b/modules/local/run_alphafold2_pred.nf @@ -10,7 +10,7 @@ process RUN_ALPHAFOLD2_PRED { error("Local RUN_ALPHAFOLD2_PRED module does not support Conda. Please use Docker / Singularity / Podman instead.") } - container "nf-core/proteinfold_alphafold2_split:dev" + container "nf-core/proteinfold_alphafold2_split:1.1.1" input: tuple val(meta), path(fasta) diff --git a/modules/local/run_esmfold.nf b/modules/local/run_esmfold.nf index d0216dfc..490f16f7 100644 --- a/modules/local/run_esmfold.nf +++ b/modules/local/run_esmfold.nf @@ -6,7 +6,7 @@ process RUN_ESMFOLD { error("Local RUN_ESMFOLD module does not support Conda. Please use Docker / Singularity / Podman instead.") } - container "nf-core/proteinfold_esmfold:1.1.0" + container "nf-core/proteinfold_esmfold:1.1.1" input: tuple val(meta), path(fasta) diff --git a/modules/nf-core/aria2/environment.yml b/modules/nf-core/aria2/environment.yml new file mode 100755 index 00000000..1095bd44 --- /dev/null +++ b/modules/nf-core/aria2/environment.yml @@ -0,0 +1,7 @@ +name: aria2 +channels: + - conda-forge + - bioconda + - defaults +dependencies: + - conda-forge::aria2=1.36.0 \ No newline at end of file diff --git a/modules/nf-core/aria2/main.nf b/modules/nf-core/aria2/main.nf index af595a9f..972ee036 100644 --- a/modules/nf-core/aria2/main.nf +++ b/modules/nf-core/aria2/main.nf @@ -1,19 +1,18 @@ - process ARIA2 { tag "$source_url" label 'process_single' - conda "conda-forge::aria2=1.36.0" + conda "${moduleDir}/environment.yml" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? 'https://depot.galaxyproject.org/singularity/aria2:1.36.0' : 'biocontainers/aria2:1.36.0' }" input: - val source_url + tuple val(meta), val(source_url) output: - path ("$downloaded_file"), emit: downloaded_file - path "versions.yml" , emit: versions + tuple val(meta), path("$downloaded_file"), emit: downloaded_file + path "versions.yml" , emit: versions when: task.ext.when == null || task.ext.when @@ -23,8 +22,6 @@ process ARIA2 { downloaded_file = source_url.split("/")[-1] """ - set -e - aria2c \\ --check-certificate=false \\ $args \\ @@ -35,4 +32,16 @@ process ARIA2 { aria2: \$(echo \$(aria2c --version 2>&1) | grep 'aria2 version' | cut -f3 -d ' ') END_VERSIONS """ -} + + stub: + downloaded_file = source_url.split("/")[-1] + + """ + touch ${downloaded_file} + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + aria2: \$(echo \$(aria2c --version 2>&1) | grep 'aria2 version' | cut -f3 -d ' ') + END_VERSIONS + """ +} \ No newline at end of file diff --git a/nextflow.config b/nextflow.config index 6777332d..6697b3bd 100644 --- a/nextflow.config +++ b/nextflow.config @@ -49,7 +49,7 @@ params { // Colabfold parameters colabfold_server = "webserver" colabfold_model_preset = "alphafold2_ptm" // {'auto', 'alphafold2', 'alphafold2_ptm', 'alphafold2_multimer_v1', 'alphafold2_multimer_v2', 'alphafold2_multimer_v3'} - num_recycle = 3 + num_recycles_colabfold = 3 use_amber = true colabfold_db = null db_load_mode = 0 @@ -68,7 +68,7 @@ params { // Esmfold parameters esmfold_db = null esmfold_model_preset = "monomer" - num_recycles = 4 + num_recycles_esmfold = 4 // Esmfold links esmfold_3B_v1 = null @@ -77,6 +77,9 @@ params { // Esmfold paths esmfold_params_path = null + + // Process skipping options + skip_multiqc = false // MultiQC options multiqc_config = null @@ -95,7 +98,8 @@ params { hook_url = null help = false version = false - + pipelines_testdata_base_path = 'https://raw.githubusercontent.com/nf-core/test-datasets/' + // Config options config_profile_name = null config_profile_description = null @@ -138,30 +142,30 @@ try { profiles { debug { - dumpHashes = true - process.beforeScript = 'echo $HOSTNAME' - cleanup = false + dumpHashes = true + process.beforeScript = 'echo $HOSTNAME' + cleanup = false nextflow.enable.configProcessNamesValidation = true } conda { - conda.enabled = true - docker.enabled = false - singularity.enabled = false - podman.enabled = false - shifter.enabled = false - charliecloud.enabled = false - channels = ['conda-forge', 'bioconda', 'defaults'] - apptainer.enabled = false + conda.enabled = true + docker.enabled = false + singularity.enabled = false + podman.enabled = false + shifter.enabled = false + charliecloud.enabled = false + conda.channels = ['conda-forge', 'bioconda', 'defaults'] + apptainer.enabled = false } mamba { - conda.enabled = true - conda.useMamba = true - docker.enabled = false - singularity.enabled = false - podman.enabled = false - shifter.enabled = false - charliecloud.enabled = false - apptainer.enabled = false + conda.enabled = true + conda.useMamba = true + docker.enabled = false + singularity.enabled = false + podman.enabled = false + shifter.enabled = false + charliecloud.enabled = false + apptainer.enabled = false } docker { docker.enabled = true @@ -193,51 +197,60 @@ profiles { apptainer.enabled = false } podman { - podman.enabled = true - conda.enabled = false - docker.enabled = false - singularity.enabled = false - shifter.enabled = false - charliecloud.enabled = false - apptainer.enabled = false + podman.enabled = true + conda.enabled = false + docker.enabled = false + singularity.enabled = false + shifter.enabled = false + charliecloud.enabled = false + apptainer.enabled = false } shifter { - shifter.enabled = true - conda.enabled = false - docker.enabled = false - singularity.enabled = false - podman.enabled = false - charliecloud.enabled = false - apptainer.enabled = false + shifter.enabled = true + conda.enabled = false + docker.enabled = false + singularity.enabled = false + podman.enabled = false + charliecloud.enabled = false + apptainer.enabled = false } charliecloud { - charliecloud.enabled = true - conda.enabled = false - docker.enabled = false - singularity.enabled = false - podman.enabled = false - shifter.enabled = false - apptainer.enabled = false + charliecloud.enabled = true + conda.enabled = false + docker.enabled = false + singularity.enabled = false + podman.enabled = false + shifter.enabled = false + apptainer.enabled = false } apptainer { - apptainer.enabled = true - apptainer.autoMounts = true - conda.enabled = false - docker.enabled = false - singularity.enabled = false - podman.enabled = false - shifter.enabled = false - charliecloud.enabled = false + apptainer.enabled = true + apptainer.autoMounts = true + conda.enabled = false + docker.enabled = false + singularity.enabled = false + podman.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + wave { + apptainer.ociAutoPull = true + singularity.ociAutoPull = true + wave.enabled = true + wave.freeze = true + wave.strategy = 'conda,container' } gitpod { - executor.name = 'local' - executor.cpus = 4 - executor.memory = 8.GB + executor.name = 'local' + executor.cpus = 4 + executor.memory = 8.GB } test { includeConfig 'conf/test.config' } test_alphafold2_split { includeConfig 'conf/test_alphafold_split.config' } + test_alphafold2_download { includeConfig 'conf/test_alphafold_download.config' } test_colabfold_local { includeConfig 'conf/test_colabfold_local.config' } test_colabfold_webserver { includeConfig 'conf/test_colabfold_webserver.config' } + test_colabfold_download { includeConfig 'conf/test_colabfold_download.config' } test_esmfold { includeConfig 'conf/test_esmfold.config' } test_full { includeConfig 'conf/test_full.config' } test_full_alphafold2_standard { includeConfig 'conf/test_full.config' } @@ -308,8 +321,8 @@ manifest { homePage = 'https://github.com/nf-core/proteinfold' description = """Protein 3D structure prediction pipeline""" mainScript = 'main.nf' - nextflowVersion = '!>=22.10.1' - version = '1.1.0dev' + nextflowVersion = '!>=23.04.0' + version = '1.1.1' doi = '10.5281/zenodo.7629996' } diff --git a/nextflow_schema.json b/nextflow_schema.json index 7080cafe..df0bbfe3 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -29,10 +29,11 @@ "description": "The output directory where the results will be saved. You have to use absolute paths to storage on Cloud infrastructure.", "fa_icon": "fas fa-folder-open" }, - "mode": { + "mode": { "type": "string", "default": "alphafold2", "description": "Specifies the mode in which the pipeline will be run", + "enum": ["alphafold2", "colabfold", "esmfold"], "fa_icon": "fas fa-cogs" }, "use_gpu": { @@ -129,10 +130,10 @@ ], "fa_icon": "fas fa-stream" }, - "num_recycle": { + "num_recycles_colabfold": { "type": "integer", "default": 3, - "description": "Number of recycles", + "description": "Number of recycles for Colabfold", "fa_icon": "fas fa-recycle" }, "use_amber": { @@ -179,7 +180,7 @@ "description": "Specifies the PARAMS path used by 'esmfold' mode", "fa_icon": "fas fa-folder-open" }, - "num_recycles": { + "num_recycles_esmfold": { "type": "integer", "default": 4, "description": "Specifies the number of recycles used by Esmfold", @@ -193,24 +194,16 @@ } } }, - "gadi_config_options": { - "title": "Institutional config options", + "process_skipping_options": { + "title": "Process skipping options", "type": "object", - "fa_icon": "fas fa-university", - "description": "Configurations for running on GADI at NCI.", - "help_text": ".", + "fa_icon": "fas fa-fast-forward", + "description": "Options to skip various steps within the workflow.", "properties": { - "use_dgxa100": { + "skip_multiqc": { "type": "boolean", - "default": false, - "description": "If true uses the DGXA 100 GPU nodes", - "fa_icon": "fas fa-battery-full" - }, - "project": { - "type": "string", - "description": "Thge project code on GADI.", - "default": "", - "fa_icon": "fas fa-users-cog" + "description": "Skip MultiQC.", + "fa_icon": "fas fa-fast-forward" } } }, @@ -323,7 +316,7 @@ }, "mgnify_link": { "type": "string", - "default": "https://storage.googleapis.com/alphafold-databases/casp14_versions/mgy_clusters_2018_12.fa.gz", + "default": "https://storage.googleapis.com/alphafold-databases/v2.3/mgy_clusters_2022_05.fa.gz", "description": "Link to the MGnify database", "fa_icon": "fas fa-link" }, @@ -341,8 +334,8 @@ }, "pdb_obsolete_link": { "type": "string", - "default": "ftp://ftp.wwpdb.org/pub/pdb/data/status/obsolete.dat", - "description": "Link to the PDV obsolete database", + "default": "https://files.wwpdb.org/pub/pdb/data/status/obsolete.dat", + "description": "Link to the PDB obsolete database", "fa_icon": "fas fa-link" }, "uniref30_alphafold2_link": { @@ -353,25 +346,25 @@ }, "uniref90_link": { "type": "string", - "default": "ftp://ftp.uniprot.org/pub/databases/uniprot/uniref/uniref90/uniref90.fasta.gz", + "default": "https://ftp.ebi.ac.uk/pub/databases/uniprot/uniref/uniref90/uniref90.fasta.gz", "description": "Link to the UniRef90 database", "fa_icon": "fas fa-link" }, "pdb_seqres_link": { "type": "string", - "default": "ftp://ftp.wwpdb.org/pub/pdb/derived_data/pdb_seqres.txt", + "default": "https://files.wwpdb.org/pub/pdb/derived_data/pdb_seqres.txt", "description": "Link to the PDB SEQRES database", "fa_icon": "fas fa-link" }, "uniprot_sprot_link": { "type": "string", - "default": "ftp://ftp.ebi.ac.uk/pub/databases/uniprot/current_release/knowledgebase/complete/uniprot_sprot.fasta.gz", + "default": "https://ftp.ebi.ac.uk/pub/databases/uniprot/current_release/knowledgebase/complete/uniprot_sprot.fasta.gz", "description": "Link to the SwissProt UniProt database", "fa_icon": "fas fa-link" }, "uniprot_trembl_link": { "type": "string", - "default": "ftp://ftp.ebi.ac.uk/pub/databases/uniprot/current_release/knowledgebase/complete/uniprot_trembl.fasta.gz", + "default": "https://ftp.ebi.ac.uk/pub/databases/uniprot/current_release/knowledgebase/complete/uniprot_trembl.fasta.gz", "description": "Link to the TrEMBL UniProt database", "fa_icon": "fas fa-link" } @@ -449,7 +442,7 @@ }, "uniref30_colabfold_link": { "type": "string", - "default": "https://wwwuser.gwdg.de/~compbiol/colabfold/uniref30_2202.tar.gz", + "default": "https://wwwuser.gwdg.de/~compbiol/colabfold/uniref30_2302.tar.gz", "description": "Link to the UniRef30 database", "fa_icon": "fas fa-link" }, @@ -643,6 +636,13 @@ "description": "Validation of parameters in lenient more.", "hidden": true, "help_text": "Allows string values that are parseable as numbers or booleans. For further information see [JSONSchema docs](https://github.com/everit-org/json-schema#lenient-mode)." + }, + "pipelines_testdata_base_path": { + "type": "string", + "fa_icon": "far fa-check-circle", + "description": "Base URL or local path to location of pipeline test dataset files", + "default": "https://raw.githubusercontent.com/nf-core/test-datasets/", + "hidden": true } } } @@ -660,6 +660,9 @@ { "$ref": "#/definitions/esmfold_options" }, + { + "$ref": "#/definitions/process_skipping_options" + }, { "$ref": "#/definitions/institutional_config_options" }, diff --git a/workflows/alphafold2.nf b/workflows/alphafold2.nf index 818069a3..3c84dce8 100644 --- a/workflows/alphafold2.nf +++ b/workflows/alphafold2.nf @@ -193,28 +193,31 @@ workflow ALPHAFOLD2 { // // MODULE: MultiQC // - ch_multiqc_report = Channel.empty() - ch_multiqc_config = Channel.fromPath("$projectDir/assets/multiqc_config.yml", checkIfExists: true) - ch_multiqc_custom_config = params.multiqc_config ? Channel.fromPath( params.multiqc_config ) : Channel.empty() - ch_multiqc_logo = params.multiqc_logo ? Channel.fromPath( params.multiqc_logo ) : Channel.empty() - summary_params = paramsSummaryMap(workflow, parameters_schema: "nextflow_schema.json") - ch_workflow_summary = Channel.value(paramsSummaryMultiqc(summary_params)) - ch_multiqc_custom_methods_description = params.multiqc_methods_description ? file(params.multiqc_methods_description, checkIfExists: true) : file("$projectDir/assets/methods_description_template.yml", checkIfExists: true) - ch_methods_description = Channel.value(methodsDescriptionText(ch_multiqc_custom_methods_description)) - - ch_multiqc_files = Channel.empty() - ch_multiqc_files = ch_multiqc_files.mix(ch_workflow_summary.collectFile(name: 'workflow_summary_mqc.yaml')) - ch_multiqc_files = ch_multiqc_files.mix(ch_methods_description.collectFile(name: 'methods_description_mqc.yaml')) - ch_multiqc_files = ch_multiqc_files.mix(ch_collated_versions) - ch_multiqc_files = ch_multiqc_files.mix(ch_multiqc_rep) - - MULTIQC ( - ch_multiqc_files.collect(), - ch_multiqc_config.toList(), - ch_multiqc_custom_config.toList(), - ch_multiqc_logo.toList() - ) - ch_multiqc_report = MULTIQC.out.report.toList() + ch_multiqc_report = Channel.empty() + if (!params.skip_multiqc) { + ch_multiqc_report = Channel.empty() + ch_multiqc_config = Channel.fromPath("$projectDir/assets/multiqc_config.yml", checkIfExists: true) + ch_multiqc_custom_config = params.multiqc_config ? Channel.fromPath( params.multiqc_config ) : Channel.empty() + ch_multiqc_logo = params.multiqc_logo ? Channel.fromPath( params.multiqc_logo ) : Channel.empty() + summary_params = paramsSummaryMap(workflow, parameters_schema: "nextflow_schema.json") + ch_workflow_summary = Channel.value(paramsSummaryMultiqc(summary_params)) + ch_multiqc_custom_methods_description = params.multiqc_methods_description ? file(params.multiqc_methods_description, checkIfExists: true) : file("$projectDir/assets/methods_description_template.yml", checkIfExists: true) + ch_methods_description = Channel.value(methodsDescriptionText(ch_multiqc_custom_methods_description)) + + ch_multiqc_files = Channel.empty() + ch_multiqc_files = ch_multiqc_files.mix(ch_workflow_summary.collectFile(name: 'workflow_summary_mqc.yaml')) + ch_multiqc_files = ch_multiqc_files.mix(ch_methods_description.collectFile(name: 'methods_description_mqc.yaml')) + ch_multiqc_files = ch_multiqc_files.mix(ch_collated_versions) + ch_multiqc_files = ch_multiqc_files.mix(ch_multiqc_rep) + + MULTIQC ( + ch_multiqc_files.collect(), + ch_multiqc_config.toList(), + ch_multiqc_custom_config.toList(), + ch_multiqc_logo.toList() + ) + ch_multiqc_report = MULTIQC.out.report.toList() + } emit: multiqc_report = ch_multiqc_report // channel: /path/to/multiqc_report.html diff --git a/workflows/colabfold.nf b/workflows/colabfold.nf index a6788faa..73adb91d 100644 --- a/workflows/colabfold.nf +++ b/workflows/colabfold.nf @@ -45,7 +45,7 @@ workflow COLABFOLD { ch_colabfold_params // channel: path(colabfold_params) ch_colabfold_db // channel: path(colabfold_db) ch_uniref30 // channel: path(uniref30) - num_recycle // int: Number of recycles for esmfold + num_recycles // int: Number of recycles for esmfold main: ch_multiqc_files = Channel.empty() @@ -72,7 +72,7 @@ workflow COLABFOLD { ch_colabfold_params, [], [], - num_recycle + num_recycles ) ch_versions = ch_versions.mix(COLABFOLD_BATCH.out.versions) } else { @@ -82,7 +82,7 @@ workflow COLABFOLD { ch_colabfold_params, [], [], - num_recycle + num_recycles ) ch_versions = ch_versions.mix(COLABFOLD_BATCH.out.versions) } @@ -123,7 +123,7 @@ workflow COLABFOLD { ch_colabfold_params, ch_colabfold_db, ch_uniref30, - num_recycle + num_recycles ) ch_versions = ch_versions.mix(COLABFOLD_BATCH.out.versions) } @@ -138,28 +138,31 @@ workflow COLABFOLD { // // MODULE: MultiQC // - ch_multiqc_report = Channel.empty() - ch_multiqc_config = Channel.fromPath("$projectDir/assets/multiqc_config.yml", checkIfExists: true) - ch_multiqc_custom_config = params.multiqc_config ? Channel.fromPath( params.multiqc_config ) : Channel.empty() - ch_multiqc_logo = params.multiqc_logo ? Channel.fromPath( params.multiqc_logo ) : Channel.empty() - summary_params = paramsSummaryMap(workflow, parameters_schema: "nextflow_schema.json") - ch_workflow_summary = Channel.value(paramsSummaryMultiqc(summary_params)) - ch_multiqc_custom_methods_description = params.multiqc_methods_description ? file(params.multiqc_methods_description, checkIfExists: true) : file("$projectDir/assets/methods_description_template.yml", checkIfExists: true) - ch_methods_description = Channel.value(methodsDescriptionText(ch_multiqc_custom_methods_description)) - - ch_multiqc_files = Channel.empty() - ch_multiqc_files = ch_multiqc_files.mix(ch_workflow_summary.collectFile(name: 'workflow_summary_mqc.yaml')) - ch_multiqc_files = ch_multiqc_files.mix(ch_methods_description.collectFile(name: 'methods_description_mqc.yaml')) - ch_multiqc_files = ch_multiqc_files.mix(ch_collated_versions) - ch_multiqc_files = ch_multiqc_files.mix(COLABFOLD_BATCH.out.multiqc.collect()) - - MULTIQC ( - ch_multiqc_files.collect(), - ch_multiqc_config.toList(), - ch_multiqc_custom_config.toList(), - ch_multiqc_logo.toList() - ) - ch_multiqc_report = MULTIQC.out.report.toList() + ch_multiqc_report = Channel.empty() + if (!params.skip_multiqc) { + ch_multiqc_report = Channel.empty() + ch_multiqc_config = Channel.fromPath("$projectDir/assets/multiqc_config.yml", checkIfExists: true) + ch_multiqc_custom_config = params.multiqc_config ? Channel.fromPath( params.multiqc_config ) : Channel.empty() + ch_multiqc_logo = params.multiqc_logo ? Channel.fromPath( params.multiqc_logo ) : Channel.empty() + summary_params = paramsSummaryMap(workflow, parameters_schema: "nextflow_schema.json") + ch_workflow_summary = Channel.value(paramsSummaryMultiqc(summary_params)) + ch_multiqc_custom_methods_description = params.multiqc_methods_description ? file(params.multiqc_methods_description, checkIfExists: true) : file("$projectDir/assets/methods_description_template.yml", checkIfExists: true) + ch_methods_description = Channel.value(methodsDescriptionText(ch_multiqc_custom_methods_description)) + + ch_multiqc_files = Channel.empty() + ch_multiqc_files = ch_multiqc_files.mix(ch_workflow_summary.collectFile(name: 'workflow_summary_mqc.yaml')) + ch_multiqc_files = ch_multiqc_files.mix(ch_methods_description.collectFile(name: 'methods_description_mqc.yaml')) + ch_multiqc_files = ch_multiqc_files.mix(ch_collated_versions) + ch_multiqc_files = ch_multiqc_files.mix(COLABFOLD_BATCH.out.multiqc.collect()) + + MULTIQC ( + ch_multiqc_files.collect(), + ch_multiqc_config.toList(), + ch_multiqc_custom_config.toList(), + ch_multiqc_logo.toList() + ) + ch_multiqc_report = MULTIQC.out.report.toList() + } emit: multiqc_report = ch_multiqc_report // channel: /path/to/multiqc_report.html diff --git a/workflows/esmfold.nf b/workflows/esmfold.nf index ea555e36..7943cb59 100644 --- a/workflows/esmfold.nf +++ b/workflows/esmfold.nf @@ -77,7 +77,7 @@ workflow ESMFOLD { take: ch_versions // channel: [ path(versions.yml) ] ch_esmfold_params // directory: /path/to/esmfold/params/ - ch_num_recycle // int: Number of recycles for esmfold + ch_num_recycles // int: Number of recycles for esmfold main: ch_multiqc_files = Channel.empty() @@ -101,7 +101,7 @@ workflow ESMFOLD { RUN_ESMFOLD( MULTIFASTA_TO_SINGLEFASTA.out.input_fasta, ch_esmfold_params, - ch_num_recycle + ch_num_recycles ) ch_versions = ch_versions.mix(RUN_ESMFOLD.out.versions) } else { @@ -109,7 +109,7 @@ workflow ESMFOLD { RUN_ESMFOLD( ch_fasta, ch_esmfold_params, - ch_num_recycle + ch_num_recycles ) ch_versions = ch_versions.mix(RUN_ESMFOLD.out.versions) } @@ -139,22 +139,11 @@ workflow ESMFOLD { // // MODULE: MultiQC // -<<<<<<< HEAD workflow_summary = WorkflowEsmfold.paramsSummaryMultiqc(workflow, summary_params) ch_workflow_summary = Channel.value(workflow_summary) methods_description = WorkflowProteinfold.methodsDescriptionText(workflow, ch_multiqc_custom_methods_description, params) ch_methods_description = Channel.value(methods_description) -======= - ch_multiqc_report = Channel.empty() - ch_multiqc_config = Channel.fromPath("$projectDir/assets/multiqc_config.yml", checkIfExists: true) - ch_multiqc_custom_config = params.multiqc_config ? Channel.fromPath( params.multiqc_config ) : Channel.empty() - ch_multiqc_logo = params.multiqc_logo ? Channel.fromPath( params.multiqc_logo ) : Channel.empty() - summary_params = paramsSummaryMap(workflow, parameters_schema: "nextflow_schema.json") - ch_workflow_summary = Channel.value(paramsSummaryMultiqc(summary_params)) - ch_multiqc_methods_description = params.multiqc_methods_description ? file(params.multiqc_methods_description, checkIfExists: true) : file("$projectDir/assets/methods_description_template.yml", checkIfExists: true) - ch_methods_description = Channel.value(methodsDescriptionText(ch_multiqc_methods_description)) ->>>>>>> Clean esmfold ch_multiqc_files = Channel.empty() ch_multiqc_files = ch_multiqc_files.mix(ch_workflow_summary.collectFile(name: 'workflow_summary_mqc.yaml')) @@ -169,7 +158,7 @@ workflow ESMFOLD { ch_multiqc_logo.toList() ) ch_multiqc_report = MULTIQC.out.report.toList() - + emit: multiqc_report = ch_multiqc_report // channel: /path/to/multiqc_report.html versions = ch_versions // channel: [ path(versions.yml) ] From 0c72da3018a0dc2972d25818754a9a40a87a4a70 Mon Sep 17 00:00:00 2001 From: ziadbkh Date: Thu, 8 Aug 2024 10:14:04 +1000 Subject: [PATCH 217/227] update --- nextflow_schema.json | 1 - workflows/esmfold.nf | 4 ---- 2 files changed, 5 deletions(-) diff --git a/nextflow_schema.json b/nextflow_schema.json index df0bbfe3..74021ddd 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -33,7 +33,6 @@ "type": "string", "default": "alphafold2", "description": "Specifies the mode in which the pipeline will be run", - "enum": ["alphafold2", "colabfold", "esmfold"], "fa_icon": "fas fa-cogs" }, "use_gpu": { diff --git a/workflows/esmfold.nf b/workflows/esmfold.nf index 7943cb59..5ffed105 100644 --- a/workflows/esmfold.nf +++ b/workflows/esmfold.nf @@ -1,13 +1,11 @@ /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -<<<<<<< HEAD PRINT PARAMS SUMMARY ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ include { paramsSummaryLog; paramsSummaryMap; fromSamplesheet } from 'plugin/nf-validation' -<<<<<<< HEAD:workflows/esmfold.nf // Validate input parameters WorkflowEsmfold.initialise(params, log) @@ -33,8 +31,6 @@ ch_multiqc_custom_methods_description = params.multiqc_methods_description ? fil /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -======= ->>>>>>> Clean esmfold IMPORT LOCAL MODULES/SUBWORKFLOWS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ From fd5657ca6b1393c4ce4102029aad4b41688bc2d6 Mon Sep 17 00:00:00 2001 From: ziadbkh Date: Thu, 8 Aug 2024 14:54:23 +1000 Subject: [PATCH 218/227] fix readme --- README.md | 101 ++++++++++-------------------------------------------- 1 file changed, 18 insertions(+), 83 deletions(-) diff --git a/README.md b/README.md index 5d81cb26..63a92f26 100644 --- a/README.md +++ b/README.md @@ -19,17 +19,7 @@ ## Introduction -<<<<<<< HEAD -**nf-core/proteinfold** is a bioinformatics pipeline that ... - - -======= **nf-core/proteinfold** is a bioinformatics best-practice analysis pipeline for Protein 3D structure prediction. ->>>>>>> 2c7b861f6e1883406260f1f0f45a0543bcc76927 The pipeline is built using [Nextflow](https://www.nextflow.io), a workflow tool to run tasks across multiple compute infrastructures in a very portable manner. It uses Docker/Singularity containers making installation trivial and results highly reproducible. The [Nextflow DSL2](https://www.nextflow.io/docs/latest/dsl2.html) implementation of this pipeline uses one container per process which makes it much easier to maintain and update software dependencies. Where possible, these processes have been submitted to and installed from [nf-core/modules](https://github.com/nf-core/modules) in order to make them available to all nf-core pipelines, and to everyone within the Nextflow community! @@ -50,26 +40,6 @@ On release, automated continuous integration tests run the pipeline on a full-si iv. [ColabFold](https://github.com/sokrypton/ColabFold) - MMseqs2 local search followed by ColabFold v. [ESMFold](https://github.com/facebookresearch/esm) - Regular ESM -<<<<<<< HEAD - -## Usage - -> [!NOTE] -> If you are new to Nextflow and nf-core, please refer to [this page](https://nf-co.re/docs/usage/installation) on how to set-up Nextflow. Make sure to [test your setup](https://nf-co.re/docs/usage/introduction#how-to-run-a-pipeline) with `-profile test` before running the workflow on actual data. - - - - - -======= If you use nf-core/proteinfold for your analysis, please cite it using the following doi: [10.5281/zenodo.7437038](https://doi.org/10.5281/zenodo.7437038) ->>>>>>> 2c7b861f6e1883406260f1f0f45a0543bcc76927 An extensive list of references for the tools used by the pipeline can be found in the [`CITATIONS.md`](CITATIONS.md) file. From f4b652a52a673925bb655b97afdf94aefee51651 Mon Sep 17 00:00:00 2001 From: ziadbkh Date: Thu, 8 Aug 2024 16:04:58 +1000 Subject: [PATCH 219/227] minor fixes --- main.nf | 4 ++-- nextflow.config | 45 ++++++++++++++++------------------------- nextflow_schema.json | 4 ---- workflows/alphafold2.nf | 2 +- workflows/esmfold.nf | 2 +- 5 files changed, 21 insertions(+), 36 deletions(-) diff --git a/main.nf b/main.nf index fd58a34f..07d06410 100644 --- a/main.nf +++ b/main.nf @@ -35,8 +35,8 @@ include { getColabfoldAlphafold2ParamsPath } from './subworkflows/local/utils_nf ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ -params.colabfold_alphafold2_params = WorkflowMain.getColabfoldAlphafold2Params(params) -params.colabfold_alphafold2_params_path = WorkflowMain.getColabfoldAlphafold2ParamsPath(params) +params.colabfold_alphafold2_params_link = getColabfoldAlphafold2Params() +params.colabfold_alphafold2_params_path = getColabfoldAlphafold2ParamsPath() /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/nextflow.config b/nextflow.config index 112d9db9..f3f7f1ac 100644 --- a/nextflow.config +++ b/nextflow.config @@ -13,6 +13,7 @@ params { input = null mode = 'alphafold2' // {alphafold2, colabfold, esmfold} use_gpu = false + // Alphafold2 parameters alphafold2_mode = "standard" max_template_date = "2020-05-14" @@ -77,7 +78,7 @@ params { // Esmfold paths esmfold_params_path = null - + // Process skipping options skip_multiqc = false @@ -89,17 +90,17 @@ params { multiqc_methods_description = null // Boilerplate options - outdir = null - publish_dir_mode = 'copy' - email = null - email_on_fail = null - plaintext_email = false - monochrome_logs = false - hook_url = null - help = false - version = false + outdir = null + publish_dir_mode = 'copy' + email = null + email_on_fail = null + plaintext_email = false + monochrome_logs = false + hook_url = null + help = false + version = false pipelines_testdata_base_path = 'https://raw.githubusercontent.com/nf-core/test-datasets/' - + // Config options config_profile_name = null config_profile_description = null @@ -117,11 +118,7 @@ params { // Schema validation default options validationFailUnrecognisedParams = false validationLenientMode = false -<<<<<<< HEAD - validationSchemaIgnoreParams = 'genomes,igenomes_base' -======= validationSchemaIgnoreParams = '' ->>>>>>> 2c7b861f6e1883406260f1f0f45a0543bcc76927 validationShowHiddenParams = false validate_params = true @@ -143,7 +140,6 @@ try { } catch (Exception e) { System.err.println("WARNING: Could not load nf-core/config/proteinfold profiles: ${params.custom_config_base}/pipeline/proteinfold.config") } - profiles { debug { dumpHashes = true @@ -185,13 +181,13 @@ profiles { shifter.enabled = false charliecloud.enabled = false apptainer.enabled = false -<<<<<<< HEAD - docker.runOptions = '-u $(id -u):$(id -g)' -======= ->>>>>>> 2c7b861f6e1883406260f1f0f45a0543bcc76927 } arm { - docker.runOptions = '-u $(id -u):$(id -g) --platform=linux/amd64' + if (params.use_gpu) { + docker.runOptions = '-u $(id -u):$(id -g) --platform=linux/amd64 --gpus all' + } else { + docker.runOptions = '-u $(id -u):$(id -g) --platform=linux/amd64' + } } singularity { singularity.enabled = true @@ -269,10 +265,6 @@ profiles { test_full_colabfold_multimer { includeConfig 'conf/test_full_colabfold_webserver_multimer.config' } test_full_esmfold { includeConfig 'conf/test_full_esmfold.config' } test_full_esmfold_multimer { includeConfig 'conf/test_full_esmfold_multimer.config' } - gadi { - includeConfig 'https://raw.githubusercontent.com/nf-core/configs/master/conf/nci_gadi.config' - includeConfig 'conf/gadi.config' - } } // Set default registry for Apptainer, Docker, Podman and Singularity independent of -profile @@ -381,6 +373,3 @@ def check_max(obj, type) { } } } - - - diff --git a/nextflow_schema.json b/nextflow_schema.json index e467b10e..74021ddd 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -33,10 +33,6 @@ "type": "string", "default": "alphafold2", "description": "Specifies the mode in which the pipeline will be run", -<<<<<<< HEAD -======= - "enum": ["alphafold2", "colabfold", "esmfold"], ->>>>>>> 2c7b861f6e1883406260f1f0f45a0543bcc76927 "fa_icon": "fas fa-cogs" }, "use_gpu": { diff --git a/workflows/alphafold2.nf b/workflows/alphafold2.nf index 3c84dce8..a8826afa 100644 --- a/workflows/alphafold2.nf +++ b/workflows/alphafold2.nf @@ -159,7 +159,7 @@ workflow ALPHAFOLD2 { ch_uniprot, ch_af_all.map{it[2]} ) - ch_multiqc_rep = RUN_ALPHAFOLD2_PRED.out.multiqc.collect() + ch_multiqc_rep = RUN_ALPHAFOLD2_PRED.out.multiqc.map{1}.collect() ch_versions = ch_versions.mix(RUN_ALPHAFOLD2_PRED.out.versions) RUN_ALPHAFOLD2_PRED.out.af_out_tsv .map{[it[0], it[1].findAll{ it.getName().contains("_lddt_")}]} diff --git a/workflows/esmfold.nf b/workflows/esmfold.nf index 68e5db77..99759767 100644 --- a/workflows/esmfold.nf +++ b/workflows/esmfold.nf @@ -119,7 +119,7 @@ workflow ESMFOLD { ch_multiqc_files = ch_multiqc_files.mix(ch_workflow_summary.collectFile(name: 'workflow_summary_mqc.yaml')) ch_multiqc_files = ch_multiqc_files.mix(ch_methods_description.collectFile(name: 'methods_description_mqc.yaml')) ch_multiqc_files = ch_multiqc_files.mix(ch_collated_versions) - ch_multiqc_files = ch_multiqc_files.mix(RUN_ESMFOLD.out.multiqc.collect()) + ch_multiqc_files = ch_multiqc_files.mix(RUN_ESMFOLD.out.multiqc.map{it[1]}.collect()) MULTIQC ( ch_multiqc_files.collect(), From c3503877e6844e6a845747e6be309358266a9175 Mon Sep 17 00:00:00 2001 From: Minh Vu Date: Thu, 22 Aug 2024 16:29:12 +1000 Subject: [PATCH 220/227] update & tempalte & reduce report size --- assets/alphafold_template.html | 171 ++++++++++++++++++--------------- bin/generat_plots.py | 2 +- 2 files changed, 93 insertions(+), 80 deletions(-) diff --git a/assets/alphafold_template.html b/assets/alphafold_template.html index fb6598d2..e96afe36 100755 --- a/assets/alphafold_template.html +++ b/assets/alphafold_template.html @@ -7,6 +7,7 @@ Protein structure prediction + - - - - - - - - - - - - - - - - - - -
    - -
    - -
    - - - - - -
    - -
    - - -
    -
    -
    -
    -
    -
    <50
    -
    70
    -
    90+
    -
    -
    -
    - -
    -

    - Alphafold produces a - - per-residue confidence score (pLDDT) - - between 0 and 100. Some regions below 50 pLDDT may be unstructured in isolation. -

    -
    -
    - - - - - - -
    - - - - - - - - - -
    - -
    - - -
    -
    -
    Information
    - -
    -
    Program: (*prog_name*)
    -
    ID: (*sample_name*)
    -
    - -
    -
    Navigation
    - - -
    -
    - Scroll up/down - to zoom in and out -
    -
    - Click + drag - to rotate the structure -
    -
    - CTRL + click + drag - to move the structure -
    -
    - Click - an atom to bring it into focus -
    -
    -
    -
    - - -
    -
    -
    Toggle representations
    -
    - - - - -
    -
    - -
    -
    -
    -
    Actions
    -
    - - - -
    -
    -
    -
    Download
    - -
    - - -
    -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    -
    -
    -
    - - -
    -
    -
    Sequence Coverage
    -
    -
    - -
    - -
    -
    - -
    -
    pLDDT
    -
    - -
    -
    -
    -
    -
    - - -
    - - -
    -
    -
    - - -
    -
    -

    - The Australian BioCommons - is supported by - Bioplatforms Australia -

    -

    - Bioplatforms Australia - is enabled by - NCRIS -

    -
    -
    -
    - - - - diff --git a/assets/proteinfold_template.html b/assets/proteinfold_template.html index 8d6dfc81..1f5f40e1 100755 --- a/assets/proteinfold_template.html +++ b/assets/proteinfold_template.html @@ -71,7 +71,10 @@ - +
    Protein structure prediction
    @@ -114,11 +117,11 @@

    Structure p --> -
    +
    -
    +
    Structure p
    - -
    -
    Program: (*prog_name*)
    -
    ID: (*sample_name*)
    -
    - -